Modernize Indigo (#2110)

* Modernize Indigo

- Restructure ItemRenderContext to be more efficient and consistent with block rendering
- Remove unnecessary code including CompatibilityHelper
- Add Unique annotations to fields added by Indigo
- Fix typos
- Organize imports

* Remove unused import

* Fix bugs

- Fix enchantment glint not rendering on item models
- Fix QuadView#copyTo not copying enough data
- Make ItemRenderContext vertex consumer calculation mirror vanilla and be more efficient

* Clear the target quad's material

* Tweaks

- Retain material during copyTo instead of clearing it
- Standardize mixins

* Direct return

- Return from ItemRenderContext#quadVertexConsumer instead of assigning value to variable
This commit is contained in:
PepperCode1 2022-04-17 11:23:55 -07:00 committed by modmuss50
parent bd8a4a1845
commit 7faf0d8813
16 changed files with 318 additions and 316 deletions

View file

@ -33,9 +33,10 @@ import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingForma
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import net.minecraft.util.math.Vec3f;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3f;
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView; import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadView; import net.fabricmc.fabric.api.renderer.v1.mesh.QuadView;
import net.fabricmc.fabric.impl.client.indigo.renderer.RenderMaterialImpl; import net.fabricmc.fabric.impl.client.indigo.renderer.RenderMaterialImpl;
@ -163,7 +164,9 @@ public class QuadViewImpl implements QuadView {
final MutableQuadViewImpl quad = (MutableQuadViewImpl) target; final MutableQuadViewImpl quad = (MutableQuadViewImpl) target;
// copy everything except the material // copy everything except the material
System.arraycopy(data, baseIndex + 1, quad.data, quad.baseIndex + 1, EncodingFormat.TOTAL_STRIDE - 1); RenderMaterial material = quad.material();
System.arraycopy(data, baseIndex, quad.data, quad.baseIndex, EncodingFormat.TOTAL_STRIDE);
quad.material(material);
quad.faceNormal.set(faceNormal.getX(), faceNormal.getY(), faceNormal.getZ()); quad.faceNormal.set(faceNormal.getX(), faceNormal.getY(), faceNormal.getZ());
quad.nominalFace = this.nominalFace; quad.nominalFace = this.nominalFace;
quad.isGeometryInvalid = false; quad.isGeometryInvalid = false;

View file

@ -34,7 +34,7 @@ import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MeshImpl;
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MutableQuadViewImpl; import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MutableQuadViewImpl;
/** /**
* Consumer for pre-baked meshes. Works by copying the mesh data to a * Consumer for pre-baked meshes. Works by copying the mesh data to an
* "editor" quad held in the instance, where all transformations are applied before buffering. * "editor" quad held in the instance, where all transformations are applied before buffering.
*/ */
public abstract class AbstractMeshConsumer extends AbstractQuadRenderer implements Consumer<Mesh> { public abstract class AbstractMeshConsumer extends AbstractQuadRenderer implements Consumer<Mesh> {
@ -46,7 +46,7 @@ public abstract class AbstractMeshConsumer extends AbstractQuadRenderer implemen
* Where we handle all pre-buffer coloring, lighting, transformation, etc. * Where we handle all pre-buffer coloring, lighting, transformation, etc.
* Reused for all mesh quads. Fixed baking array sized to hold largest possible mesh quad. * Reused for all mesh quads. Fixed baking array sized to hold largest possible mesh quad.
*/ */
private class Maker extends MutableQuadViewImpl implements QuadEmitter { private class Maker extends MutableQuadViewImpl {
{ {
data = new int[EncodingFormat.TOTAL_STRIDE]; data = new int[EncodingFormat.TOTAL_STRIDE];
material(IndigoRenderer.MATERIAL_STANDARD); material(IndigoRenderer.MATERIAL_STANDARD);
@ -84,44 +84,44 @@ public abstract class AbstractMeshConsumer extends AbstractQuadRenderer implemen
return editorQuad; return editorQuad;
} }
private void renderQuad(MutableQuadViewImpl q) { private void renderQuad(MutableQuadViewImpl quad) {
if (!transform.transform(editorQuad)) { if (!transform.transform(quad)) {
return; return;
} }
if (!blockInfo.shouldDrawFace(q.cullFace())) { if (!blockInfo.shouldDrawFace(quad.cullFace())) {
return; return;
} }
final RenderMaterialImpl.Value mat = q.material(); final RenderMaterialImpl.Value mat = quad.material();
if (!mat.disableAo(0) && MinecraftClient.isAmbientOcclusionEnabled()) { if (!mat.disableAo(0) && MinecraftClient.isAmbientOcclusionEnabled()) {
// needs to happen before offsets are applied // needs to happen before offsets are applied
aoCalc.compute(q, false); aoCalc.compute(quad, false);
} }
tesselateQuad(q, mat, 0); tessellateQuad(quad, mat, 0);
} }
/** /**
* Determines color index and render layer, then routes to appropriate * Determines color index and render layer, then routes to appropriate
* tesselate routine based on material properties. * tessellate routine based on material properties.
*/ */
private void tesselateQuad(MutableQuadViewImpl quad, RenderMaterialImpl.Value mat, int textureIndex) { private void tessellateQuad(MutableQuadViewImpl quad, RenderMaterialImpl.Value mat, int textureIndex) {
final int colorIndex = mat.disableColorIndex(textureIndex) ? -1 : quad.colorIndex(); final int colorIndex = mat.disableColorIndex(textureIndex) ? -1 : quad.colorIndex();
final RenderLayer renderLayer = blockInfo.effectiveRenderLayer(mat.blendMode(textureIndex)); final RenderLayer renderLayer = blockInfo.effectiveRenderLayer(mat.blendMode(textureIndex));
if (blockInfo.defaultAo && !mat.disableAo(textureIndex)) { if (blockInfo.defaultAo && !mat.disableAo(textureIndex)) {
if (mat.emissive(textureIndex)) { if (mat.emissive(textureIndex)) {
tesselateSmoothEmissive(quad, renderLayer, colorIndex); tessellateSmoothEmissive(quad, renderLayer, colorIndex);
} else { } else {
tesselateSmooth(quad, renderLayer, colorIndex); tessellateSmooth(quad, renderLayer, colorIndex);
} }
} else { } else {
if (mat.emissive(textureIndex)) { if (mat.emissive(textureIndex)) {
tesselateFlatEmissive(quad, renderLayer, colorIndex); tessellateFlatEmissive(quad, renderLayer, colorIndex);
} else { } else {
tesselateFlat(quad, renderLayer, colorIndex); tessellateFlat(quad, renderLayer, colorIndex);
} }
} }
} }

View file

@ -22,14 +22,15 @@ import java.util.function.Function;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.client.render.LightmapTextureManager;
import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.WorldRenderer; import net.minecraft.client.render.WorldRenderer;
import net.minecraft.util.math.Vec3f;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Matrix3f; import net.minecraft.util.math.Matrix3f;
import net.minecraft.util.math.Matrix4f; import net.minecraft.util.math.Matrix4f;
import net.minecraft.util.math.Vec3f;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext.QuadTransform; import net.fabricmc.fabric.api.renderer.v1.render.RenderContext.QuadTransform;
import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator; import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator;
@ -42,8 +43,6 @@ import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MutableQuadViewImpl;
* Has most of the actual buffer-time lighting and coloring logic. * Has most of the actual buffer-time lighting and coloring logic.
*/ */
public abstract class AbstractQuadRenderer { public abstract class AbstractQuadRenderer {
static final int FULL_BRIGHTNESS = 0xF000F0;
protected final Function<RenderLayer, VertexConsumer> bufferFunc; protected final Function<RenderLayer, VertexConsumer> bufferFunc;
protected final BlockRenderInfo blockInfo; protected final BlockRenderInfo blockInfo;
protected final AoCalculator aoCalc; protected final AoCalculator aoCalc;
@ -115,7 +114,7 @@ public abstract class AbstractQuadRenderer {
// routines below have a bit of copy-paste code reuse to avoid conditional execution inside a hot loop // routines below have a bit of copy-paste code reuse to avoid conditional execution inside a hot loop
/** for non-emissive mesh quads and all fallback quads with smooth lighting. */ /** for non-emissive mesh quads and all fallback quads with smooth lighting. */
protected void tesselateSmooth(MutableQuadViewImpl q, RenderLayer renderLayer, int blockColorIndex) { protected void tessellateSmooth(MutableQuadViewImpl q, RenderLayer renderLayer, int blockColorIndex) {
colorizeQuad(q, blockColorIndex); colorizeQuad(q, blockColorIndex);
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
@ -127,19 +126,19 @@ public abstract class AbstractQuadRenderer {
} }
/** for emissive mesh quads with smooth lighting. */ /** for emissive mesh quads with smooth lighting. */
protected void tesselateSmoothEmissive(MutableQuadViewImpl q, RenderLayer renderLayer, int blockColorIndex) { protected void tessellateSmoothEmissive(MutableQuadViewImpl q, RenderLayer renderLayer, int blockColorIndex) {
colorizeQuad(q, blockColorIndex); colorizeQuad(q, blockColorIndex);
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
q.spriteColor(i, 0, ColorHelper.multiplyRGB(q.spriteColor(i, 0), aoCalc.ao[i])); q.spriteColor(i, 0, ColorHelper.multiplyRGB(q.spriteColor(i, 0), aoCalc.ao[i]));
q.lightmap(i, FULL_BRIGHTNESS); q.lightmap(i, LightmapTextureManager.MAX_LIGHT_COORDINATE);
} }
bufferQuad(q, renderLayer); bufferQuad(q, renderLayer);
} }
/** for non-emissive mesh quads and all fallback quads with flat lighting. */ /** for non-emissive mesh quads and all fallback quads with flat lighting. */
protected void tesselateFlat(MutableQuadViewImpl quad, RenderLayer renderLayer, int blockColorIndex) { protected void tessellateFlat(MutableQuadViewImpl quad, RenderLayer renderLayer, int blockColorIndex) {
colorizeQuad(quad, blockColorIndex); colorizeQuad(quad, blockColorIndex);
shadeFlatQuad(quad); shadeFlatQuad(quad);
@ -153,12 +152,12 @@ public abstract class AbstractQuadRenderer {
} }
/** for emissive mesh quads with flat lighting. */ /** for emissive mesh quads with flat lighting. */
protected void tesselateFlatEmissive(MutableQuadViewImpl quad, RenderLayer renderLayer, int blockColorIndex) { protected void tessellateFlatEmissive(MutableQuadViewImpl quad, RenderLayer renderLayer, int blockColorIndex) {
colorizeQuad(quad, blockColorIndex); colorizeQuad(quad, blockColorIndex);
shadeFlatQuad(quad); shadeFlatQuad(quad);
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
quad.lightmap(i, FULL_BRIGHTNESS); quad.lightmap(i, LightmapTextureManager.MAX_LIGHT_COORDINATE);
} }
bufferQuad(quad, renderLayer); bufferQuad(quad, renderLayer);

View file

@ -18,19 +18,17 @@ package net.fabricmc.fabric.impl.client.indigo.renderer.render;
import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.minecraft.util.math.Matrix4f;
import net.minecraft.util.math.Matrix3f; import net.minecraft.util.math.Matrix3f;
import net.minecraft.util.math.Matrix4f;
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView; import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext; import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
abstract class AbstractRenderContext implements RenderContext { abstract class AbstractRenderContext implements RenderContext {
private final ObjectArrayList<QuadTransform> transformStack = new ObjectArrayList<>();
private static final QuadTransform NO_TRANSFORM = (q) -> true; private static final QuadTransform NO_TRANSFORM = (q) -> true;
protected Matrix4f matrix;
protected Matrix3f normalMatrix;
protected int overlay;
private QuadTransform activeTransform = NO_TRANSFORM;
private final ObjectArrayList<QuadTransform> transformStack = new ObjectArrayList<>();
private final QuadTransform stackTransform = (q) -> { private final QuadTransform stackTransform = (q) -> {
int i = transformStack.size() - 1; int i = transformStack.size() - 1;
@ -43,7 +41,9 @@ abstract class AbstractRenderContext implements RenderContext {
return true; return true;
}; };
private QuadTransform activeTransform = NO_TRANSFORM; protected Matrix4f matrix;
protected Matrix3f normalMatrix;
protected int overlay;
protected final boolean transform(MutableQuadView q) { protected final boolean transform(MutableQuadView q) {
return activeTransform.transform(q); return activeTransform.transform(q);

View file

@ -18,37 +18,36 @@ package net.fabricmc.fabric.impl.client.indigo.renderer.render;
import java.util.Random; import java.util.Random;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.client.render.LightmapTextureManager;
import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.WorldRenderer; import net.minecraft.client.render.WorldRenderer;
import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BakedModel;
import net.minecraft.util.math.Matrix4f;
import net.minecraft.util.math.Matrix3f;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Matrix3f;
import net.minecraft.util.math.Matrix4f;
import net.minecraft.world.BlockRenderView; import net.minecraft.world.BlockRenderView;
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh; import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel; import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator; import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator;
import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoLuminanceFix; import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoLuminanceFix;
/** /**
* Context for non-terrain block rendering. * Context for non-terrain block rendering.
*/ */
public class BlockRenderContext extends AbstractRenderContext implements RenderContext { public class BlockRenderContext extends AbstractRenderContext {
private final BlockRenderInfo blockInfo = new BlockRenderInfo(); private final BlockRenderInfo blockInfo = new BlockRenderInfo();
private final AoCalculator aoCalc = new AoCalculator(blockInfo, this::brightness, this::aoLevel); private final AoCalculator aoCalc = new AoCalculator(blockInfo, this::brightness, this::aoLevel);
private final MeshConsumer meshConsumer = new MeshConsumer(blockInfo, this::outputBuffer, aoCalc, this::transform);
private VertexConsumer bufferBuilder; private VertexConsumer bufferBuilder;
private boolean didOutput = false; private boolean didOutput = false;
// These are kept as fields to avoid avoid the heap allocation for a supplier. // 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. // BlockModelRenderer allows the caller to supply both the random object and seed.
private Random random; private Random random;
private long seed; private long seed;
@ -57,16 +56,7 @@ public class BlockRenderContext extends AbstractRenderContext implements RenderC
return random; return random;
}; };
/** private final AbstractMeshConsumer meshConsumer = new AbstractMeshConsumer(blockInfo, this::outputBuffer, aoCalc, this::transform) {
* Reuse the fallback consumer from the render context used during chunk rebuild to make it properly
* apply the current transforms to vanilla models.
*/
private final TerrainFallbackConsumer fallbackConsumer = new TerrainFallbackConsumer(blockInfo, this::outputBuffer, aoCalc, this::transform) {
@Override
protected int overlay() {
return overlay;
}
@Override @Override
protected Matrix4f matrix() { protected Matrix4f matrix() {
return matrix; return matrix;
@ -76,11 +66,37 @@ public class BlockRenderContext extends AbstractRenderContext implements RenderC
protected Matrix3f normalMatrix() { protected Matrix3f normalMatrix() {
return normalMatrix; return normalMatrix;
} }
@Override
protected int overlay() {
return overlay;
}
};
/**
* Reuse the fallback consumer from the render context used during chunk rebuild to make it properly
* apply the current transforms to vanilla models.
*/
private final TerrainFallbackConsumer fallbackConsumer = new TerrainFallbackConsumer(blockInfo, this::outputBuffer, aoCalc, this::transform) {
@Override
protected Matrix4f matrix() {
return matrix;
}
@Override
protected Matrix3f normalMatrix() {
return normalMatrix;
}
@Override
protected int overlay() {
return overlay;
}
}; };
private int brightness(BlockPos pos) { private int brightness(BlockPos pos) {
if (blockInfo.blockView == null) { if (blockInfo.blockView == null) {
return 15 << 20 | 15 << 4; return LightmapTextureManager.MAX_LIGHT_COORDINATE;
} }
return WorldRenderer.getLightmapCoordinates(blockInfo.blockView, blockInfo.blockView.getBlockState(pos), pos); return WorldRenderer.getLightmapCoordinates(blockInfo.blockView, blockInfo.blockView.getBlockState(pos), pos);
@ -119,27 +135,6 @@ public class BlockRenderContext extends AbstractRenderContext implements RenderC
return didOutput; return didOutput;
} }
private class MeshConsumer extends AbstractMeshConsumer {
MeshConsumer(BlockRenderInfo blockInfo, Function<RenderLayer, VertexConsumer> bufferFunc, AoCalculator aoCalc, QuadTransform transform) {
super(blockInfo, bufferFunc, aoCalc, transform);
}
@Override
protected Matrix4f matrix() {
return matrix;
}
@Override
protected Matrix3f normalMatrix() {
return normalMatrix;
}
@Override
protected int overlay() {
return overlay;
}
}
@Override @Override
public Consumer<Mesh> meshConsumer() { public Consumer<Mesh> meshConsumer() {
return meshConsumer; return meshConsumer;

View file

@ -25,8 +25,8 @@ import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.WorldRenderer; import net.minecraft.client.render.WorldRenderer;
import net.minecraft.client.render.chunk.BlockBufferBuilderStorage; import net.minecraft.client.render.chunk.BlockBufferBuilderStorage;
import net.minecraft.client.render.chunk.ChunkBuilder.ChunkData;
import net.minecraft.client.render.chunk.ChunkBuilder.BuiltChunk; 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.chunk.ChunkRendererRegion;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.BlockRenderView; import net.minecraft.world.BlockRenderView;

View file

@ -1,44 +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.fabricmc.fabric.impl.client.indigo.Indigo;
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat;
/**
* Controls 1x warning for vanilla quad vertex format when running in compatibility mode.
*/
public abstract class CompatibilityHelper {
private CompatibilityHelper() { }
private static boolean logCompatibilityWarning = true;
private static boolean isCompatible(int[] vertexData) {
final boolean result = vertexData.length == EncodingFormat.QUAD_STRIDE;
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;
}
public static boolean canRender(int[] vertexData) {
return !Indigo.ENSURE_VERTEX_FORMAT_COMPATIBILITY || isCompatible(vertexData);
}
}

View file

@ -22,7 +22,9 @@ import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.color.item.ItemColors; import net.minecraft.client.color.item.ItemColors;
import net.minecraft.client.render.LightmapTextureManager;
import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.RenderLayers; import net.minecraft.client.render.RenderLayers;
import net.minecraft.client.render.TexturedRenderLayers; import net.minecraft.client.render.TexturedRenderLayers;
@ -31,20 +33,19 @@ import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.item.ItemRenderer; import net.minecraft.client.render.item.ItemRenderer;
import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.BakedQuad; import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.client.render.model.json.ModelTransformation;
import net.minecraft.client.render.model.json.ModelTransformation.Mode; import net.minecraft.client.render.model.json.ModelTransformation.Mode;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.math.Vec3f; import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Matrix4f; import net.minecraft.util.math.Vec3f;
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode; import net.fabricmc.fabric.api.renderer.v1.material.BlendMode;
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh; import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel; import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper; import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.fabricmc.fabric.impl.client.indigo.renderer.IndigoRenderer; 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;
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.ColorHelper; import net.fabricmc.fabric.impl.client.indigo.renderer.helper.ColorHelper;
@ -54,10 +55,8 @@ import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MutableQuadViewImpl;
/** /**
* The render context used for item rendering. * The render context used for item rendering.
* Does not implement emissive lighting for sake
* of simplicity in the default renderer.
*/ */
public class ItemRenderContext extends AbstractRenderContext implements RenderContext { public class ItemRenderContext extends AbstractRenderContext {
/** Value vanilla uses for item rendering. The only sensible choice, of course. */ /** Value vanilla uses for item rendering. The only sensible choice, of course. */
private static final long ITEM_RANDOM_SEED = 42L; private static final long ITEM_RANDOM_SEED = 42L;
@ -69,127 +68,82 @@ public class ItemRenderContext extends AbstractRenderContext implements RenderCo
private final ItemColors colorMap; private final ItemColors colorMap;
private final Random random = new Random(); private final Random random = new Random();
private final Consumer<BakedModel> fallbackConsumer;
private final Vec3f normalVec = new Vec3f(); private final Vec3f normalVec = new Vec3f();
private MatrixStack matrixStack;
private Matrix4f matrix;
private VertexConsumerProvider vertexConsumerProvider;
private VertexConsumer modelVertexConsumer;
private BlendMode quadBlendMode;
private VertexConsumer quadVertexConsumer;
private Mode transformMode;
private int lightmap;
private int overlay;
private ItemStack itemStack;
private VanillaQuadHandler vanillaHandler;
private final Supplier<Random> randomSupplier = () -> { private final Supplier<Random> randomSupplier = () -> {
final Random result = random; random.setSeed(ITEM_RANDOM_SEED);
result.setSeed(ITEM_RANDOM_SEED);
return random; return random;
}; };
private final int[] quadData = new int[EncodingFormat.TOTAL_STRIDE]; private final Maker editorQuad = new Maker();
private final MeshConsumer meshConsumer = new MeshConsumer();
private final FallbackConsumer fallbackConsumer = new FallbackConsumer();
private ItemStack itemStack;
private Mode transformMode;
private MatrixStack matrixStack;
private VertexConsumerProvider vertexConsumerProvider;
private int lightmap;
private VanillaQuadHandler vanillaHandler;
private boolean isDefaultTranslucent;
private boolean isTranslucentDirect;
private VertexConsumer translucentVertexConsumer;
private VertexConsumer cutoutVertexConsumer;
private VertexConsumer modelVertexConsumer;
public ItemRenderContext(ItemColors colorMap) { public ItemRenderContext(ItemColors colorMap) {
this.colorMap = colorMap; this.colorMap = colorMap;
fallbackConsumer = this::fallbackConsumer;
} }
public void renderModel(ItemStack itemStack, Mode transformMode, boolean invert, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int lightmap, int overlay, FabricBakedModel model, VanillaQuadHandler vanillaHandler) { public void renderModel(ItemStack itemStack, Mode transformMode, boolean invert, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int lightmap, int overlay, BakedModel model, VanillaQuadHandler vanillaHandler) {
this.itemStack = itemStack;
this.transformMode = transformMode;
this.matrixStack = matrixStack;
this.vertexConsumerProvider = vertexConsumerProvider;
this.lightmap = lightmap; this.lightmap = lightmap;
this.overlay = overlay; this.overlay = overlay;
this.itemStack = itemStack;
this.vertexConsumerProvider = vertexConsumerProvider;
this.matrixStack = matrixStack;
this.transformMode = transformMode;
this.vanillaHandler = vanillaHandler; this.vanillaHandler = vanillaHandler;
quadBlendMode = BlendMode.DEFAULT; computeOutputInfo();
modelVertexConsumer = selectVertexConsumer(RenderLayers.getItemLayer(itemStack, transformMode != ModelTransformation.Mode.GROUND));
matrixStack.push(); matrixStack.push();
((BakedModel) model).getTransformation().getTransformation(transformMode).apply(invert, matrixStack); model.getTransformation().getTransformation(transformMode).apply(invert, matrixStack);
matrixStack.translate(-0.5D, -0.5D, -0.5D); matrixStack.translate(-0.5D, -0.5D, -0.5D);
matrix = matrixStack.peek().getPositionMatrix(); matrix = matrixStack.peek().getPositionMatrix();
normalMatrix = matrixStack.peek().getNormalMatrix(); normalMatrix = matrixStack.peek().getNormalMatrix();
model.emitItemQuads(itemStack, randomSupplier, this); ((FabricBakedModel) model).emitItemQuads(itemStack, randomSupplier, this);
matrixStack.pop(); matrixStack.pop();
this.matrixStack = null;
this.itemStack = null; this.itemStack = null;
this.matrixStack = null;
this.vanillaHandler = null; this.vanillaHandler = null;
translucentVertexConsumer = null;
cutoutVertexConsumer = null;
modelVertexConsumer = null; modelVertexConsumer = null;
} }
/** private void computeOutputInfo() {
* Use non-culling translucent material in GUI to match vanilla behavior. If the item isDefaultTranslucent = true;
* is enchanted then also select a dual-output vertex consumer. For models with layered isTranslucentDirect = true;
* coplanar polygons this means we will render the glint more than once. Indigo doesn't
* support sprite layers, so this can't be helped in this implementation.
*/
private VertexConsumer selectVertexConsumer(RenderLayer layerIn) {
final RenderLayer layer = transformMode == ModelTransformation.Mode.GUI ? TexturedRenderLayers.getEntityTranslucentCull() : layerIn;
return ItemRenderer.getArmorGlintConsumer(vertexConsumerProvider, layer, true, itemStack.hasGlint());
}
private class Maker extends MutableQuadViewImpl implements QuadEmitter { Item item = itemStack.getItem();
{
data = quadData; if (item instanceof BlockItem blockItem) {
clear(); BlockState state = blockItem.getBlock().getDefaultState();
RenderLayer renderLayer = RenderLayers.getBlockLayer(state);
if (renderLayer != RenderLayer.getTranslucent()) {
isDefaultTranslucent = false;
}
if (transformMode != Mode.GUI && !transformMode.isFirstPerson()) {
isTranslucentDirect = false;
}
} }
@Override modelVertexConsumer = quadVertexConsumer(BlendMode.DEFAULT);
public Maker emit() {
computeGeometry();
renderQuad();
clear();
return this;
}
}
private final Maker editorQuad = new Maker();
private final Consumer<Mesh> meshConsumer = (mesh) -> {
final MeshImpl m = (MeshImpl) mesh;
final int[] data = m.data();
final int limit = data.length;
int index = 0;
while (index < limit) {
System.arraycopy(data, index, editorQuad.data(), 0, EncodingFormat.TOTAL_STRIDE);
editorQuad.load();
index += EncodingFormat.TOTAL_STRIDE;
renderQuad();
}
};
private int indexColor() {
final int colorIndex = editorQuad.colorIndex();
return colorIndex == -1 ? -1 : (colorMap.getColor(itemStack, colorIndex) | 0xFF000000);
}
private void renderQuad() {
final MutableQuadViewImpl quad = editorQuad;
if (!transform(editorQuad)) {
return;
}
final RenderMaterialImpl.Value mat = quad.material();
final int quadColor = mat.disableColorIndex(0) ? -1 : indexColor();
final int lightmap = mat.emissive(0) ? AbstractQuadRenderer.FULL_BRIGHTNESS : this.lightmap;
for (int i = 0; i < 4; i++) {
int c = quad.spriteColor(i, 0);
c = ColorHelper.multiplyColor(quadColor, c);
quad.spriteColor(i, 0, ColorHelper.swapRedBlueIfNeeded(c));
quad.lightmap(i, ColorHelper.maxBrightness(quad.lightmap(i), lightmap));
}
AbstractQuadRenderer.bufferQuad(quadVertexConsumer(mat.blendMode(0)), quad, matrix, overlay, normalMatrix, normalVec);
} }
/** /**
@ -198,25 +152,157 @@ public class ItemRenderContext extends AbstractRenderContext implements RenderCo
* translucent are mapped to cutout. * translucent are mapped to cutout.
*/ */
private VertexConsumer quadVertexConsumer(BlendMode blendMode) { private VertexConsumer quadVertexConsumer(BlendMode blendMode) {
boolean translucent;
if (blendMode == BlendMode.DEFAULT) { if (blendMode == BlendMode.DEFAULT) {
return modelVertexConsumer; translucent = isDefaultTranslucent;
}
if (blendMode != BlendMode.TRANSLUCENT) {
blendMode = BlendMode.CUTOUT;
}
if (blendMode == quadBlendMode) {
return quadVertexConsumer;
} else if (blendMode == BlendMode.TRANSLUCENT) {
quadVertexConsumer = selectVertexConsumer(TexturedRenderLayers.getEntityTranslucentCull());
quadBlendMode = BlendMode.TRANSLUCENT;
} else { } else {
quadVertexConsumer = selectVertexConsumer(TexturedRenderLayers.getEntityCutout()); translucent = blendMode == BlendMode.TRANSLUCENT;
quadBlendMode = BlendMode.CUTOUT;
} }
return quadVertexConsumer; if (translucent) {
if (translucentVertexConsumer == null) {
if (isTranslucentDirect) {
translucentVertexConsumer = ItemRenderer.getDirectItemGlintConsumer(vertexConsumerProvider, TexturedRenderLayers.getEntityTranslucentCull(), true, itemStack.hasGlint());
} else if (MinecraftClient.isFabulousGraphicsOrBetter()) {
translucentVertexConsumer = ItemRenderer.getItemGlintConsumer(vertexConsumerProvider, TexturedRenderLayers.getItemEntityTranslucentCull(), true, itemStack.hasGlint());
} else {
translucentVertexConsumer = ItemRenderer.getItemGlintConsumer(vertexConsumerProvider, TexturedRenderLayers.getEntityTranslucentCull(), true, itemStack.hasGlint());
}
}
return translucentVertexConsumer;
} else {
if (cutoutVertexConsumer == null) {
cutoutVertexConsumer = ItemRenderer.getDirectItemGlintConsumer(vertexConsumerProvider, TexturedRenderLayers.getEntityCutout(), true, itemStack.hasGlint());
}
return cutoutVertexConsumer;
}
}
private void bufferQuad(MutableQuadViewImpl quad, BlendMode blendMode) {
AbstractQuadRenderer.bufferQuad(quadVertexConsumer(blendMode), quad, matrix, overlay, normalMatrix, normalVec);
}
private void colorizeQuad(MutableQuadViewImpl q, int colorIndex) {
if (colorIndex == -1) {
for (int i = 0; i < 4; i++) {
q.spriteColor(i, 0, ColorHelper.swapRedBlueIfNeeded(q.spriteColor(i, 0)));
}
} else {
final int itemColor = 0xFF000000 | colorMap.getColor(itemStack, colorIndex);
for (int i = 0; i < 4; i++) {
q.spriteColor(i, 0, ColorHelper.swapRedBlueIfNeeded(ColorHelper.multiplyColor(itemColor, q.spriteColor(i, 0))));
}
}
}
private void renderQuad(MutableQuadViewImpl quad, BlendMode blendMode, int colorIndex) {
colorizeQuad(quad, colorIndex);
final int lightmap = this.lightmap;
for (int i = 0; i < 4; i++) {
quad.lightmap(i, ColorHelper.maxBrightness(quad.lightmap(i), lightmap));
}
bufferQuad(quad, blendMode);
}
private void renderQuadEmissive(MutableQuadViewImpl quad, BlendMode blendMode, int colorIndex) {
colorizeQuad(quad, colorIndex);
for (int i = 0; i < 4; i++) {
quad.lightmap(i, LightmapTextureManager.MAX_LIGHT_COORDINATE);
}
bufferQuad(quad, blendMode);
}
private void renderMeshQuad(MutableQuadViewImpl quad) {
if (!transform(quad)) {
return;
}
final RenderMaterialImpl.Value mat = quad.material();
final int colorIndex = mat.disableColorIndex(0) ? -1 : quad.colorIndex();
final BlendMode blendMode = mat.blendMode(0);
if (mat.emissive(0)) {
renderQuadEmissive(quad, blendMode, colorIndex);
} else {
renderQuad(quad, blendMode, colorIndex);
}
}
private class Maker extends MutableQuadViewImpl implements QuadEmitter {
{
data = new int[EncodingFormat.TOTAL_STRIDE];
clear();
}
@Override
public Maker emit() {
computeGeometry();
renderMeshQuad(this);
clear();
return this;
}
}
private class MeshConsumer implements Consumer<Mesh> {
@Override
public void accept(Mesh mesh) {
final MeshImpl m = (MeshImpl) mesh;
final int[] data = m.data();
final int limit = data.length;
int index = 0;
while (index < limit) {
System.arraycopy(data, index, editorQuad.data(), 0, EncodingFormat.TOTAL_STRIDE);
editorQuad.load();
index += EncodingFormat.TOTAL_STRIDE;
renderMeshQuad(editorQuad);
}
}
}
private class FallbackConsumer implements Consumer<BakedModel> {
@Override
public void accept(BakedModel model) {
if (hasTransform()) {
// if there's a transform in effect, convert to mesh-based quads so that we can apply it
for (int i = 0; i <= ModelHelper.NULL_FACE_ID; i++) {
final Direction cullFace = ModelHelper.faceFromIndex(i);
random.setSeed(ITEM_RANDOM_SEED);
final List<BakedQuad> quads = model.getQuads(null, cullFace, random);
final int count = quads.size();
if (count != 0) {
for (int j = 0; j < count; j++) {
final BakedQuad q = quads.get(j);
renderQuadWithTransform(q, cullFace);
}
}
}
} else {
vanillaHandler.accept(model, itemStack, lightmap, overlay, matrixStack, modelVertexConsumer);
}
}
private void renderQuadWithTransform(BakedQuad quad, Direction cullFace) {
final Maker editorQuad = ItemRenderContext.this.editorQuad;
editorQuad.fromVanilla(quad, IndigoRenderer.MATERIAL_STANDARD, cullFace);
if (!transform(editorQuad)) {
return;
}
renderQuad(editorQuad, BlendMode.DEFAULT, editorQuad.colorIndex());
}
} }
@Override @Override
@ -224,32 +310,6 @@ public class ItemRenderContext extends AbstractRenderContext implements RenderCo
return meshConsumer; return meshConsumer;
} }
private void fallbackConsumer(BakedModel model) {
if (hasTransform()) {
// if there's a transform in effect, convert to mesh-based quads so that we can apply it
for (int i = 0; i <= ModelHelper.NULL_FACE_ID; i++) {
random.setSeed(ITEM_RANDOM_SEED);
final Direction cullFace = ModelHelper.faceFromIndex(i);
renderFallbackWithTransform(model.getQuads((BlockState) null, cullFace, random), cullFace);
}
} else {
vanillaHandler.accept(model, itemStack, lightmap, overlay, matrixStack, modelVertexConsumer);
}
}
private void renderFallbackWithTransform(List<BakedQuad> quads, Direction cullFace) {
if (quads.isEmpty()) {
return;
}
final Maker editorQuad = this.editorQuad;
for (final BakedQuad q : quads) {
editorQuad.fromVanilla(q, IndigoRenderer.MATERIAL_STANDARD, cullFace);
renderQuad();
}
}
@Override @Override
public Consumer<BakedModel> fallbackConsumer() { public Consumer<BakedModel> fallbackConsumer() {
return fallbackConsumer; return fallbackConsumer;

View file

@ -58,10 +58,8 @@ import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MutableQuadViewImpl;
* manipulating the data via NIO. * manipulating the data via NIO.
*/ */
public abstract class TerrainFallbackConsumer extends AbstractQuadRenderer implements Consumer<BakedModel> { public abstract class TerrainFallbackConsumer extends AbstractQuadRenderer implements Consumer<BakedModel> {
private static Value MATERIAL_FLAT = (Value) IndigoRenderer.INSTANCE.materialFinder().disableAo(0, true).find(); private static final Value MATERIAL_FLAT = (Value) IndigoRenderer.INSTANCE.materialFinder().disableAo(0, true).find();
private static Value MATERIAL_SHADED = (Value) IndigoRenderer.INSTANCE.materialFinder().find(); private static final Value MATERIAL_SHADED = (Value) IndigoRenderer.INSTANCE.materialFinder().find();
private final int[] editorBuffer = new int[EncodingFormat.TOTAL_STRIDE];
TerrainFallbackConsumer(BlockRenderInfo blockInfo, Function<RenderLayer, VertexConsumer> bufferFunc, AoCalculator aoCalc, QuadTransform transform) { TerrainFallbackConsumer(BlockRenderInfo blockInfo, Function<RenderLayer, VertexConsumer> bufferFunc, AoCalculator aoCalc, QuadTransform transform) {
super(blockInfo, bufferFunc, aoCalc, transform); super(blockInfo, bufferFunc, aoCalc, transform);
@ -69,7 +67,7 @@ public abstract class TerrainFallbackConsumer extends AbstractQuadRenderer imple
private final MutableQuadViewImpl editorQuad = new MutableQuadViewImpl() { private final MutableQuadViewImpl editorQuad = new MutableQuadViewImpl() {
{ {
data = editorBuffer; data = new int[EncodingFormat.TOTAL_STRIDE];
material(MATERIAL_SHADED); material(MATERIAL_SHADED);
} }
@ -86,36 +84,21 @@ public abstract class TerrainFallbackConsumer extends AbstractQuadRenderer imple
final Value defaultMaterial = blockInfo.defaultAo && model.useAmbientOcclusion() ? MATERIAL_SHADED : MATERIAL_FLAT; final Value defaultMaterial = blockInfo.defaultAo && model.useAmbientOcclusion() ? MATERIAL_SHADED : MATERIAL_FLAT;
final BlockState blockState = blockInfo.blockState; final BlockState blockState = blockInfo.blockState;
for (int i = 0; i < 6; i++) { for (int i = 0; i <= ModelHelper.NULL_FACE_ID; i++) {
final Direction face = ModelHelper.faceFromIndex(i); final Direction cullFace = ModelHelper.faceFromIndex(i);
final List<BakedQuad> quads = model.getQuads(blockState, face, random.get()); final List<BakedQuad> quads = model.getQuads(blockState, cullFace, random.get());
final int count = quads.size(); final int count = quads.size();
if (count != 0) { if (count != 0) {
for (int j = 0; j < count; j++) { for (int j = 0; j < count; j++) {
final BakedQuad q = quads.get(j); final BakedQuad q = quads.get(j);
renderQuad(q, face, defaultMaterial); renderQuad(q, cullFace, defaultMaterial);
} }
} }
} }
final List<BakedQuad> quads = model.getQuads(blockState, null, random.get());
final int count = quads.size();
if (count != 0) {
for (int j = 0; j < count; j++) {
final BakedQuad q = quads.get(j);
renderQuad(q, null, defaultMaterial);
}
}
} }
private void renderQuad(BakedQuad quad, Direction cullFace, Value defaultMaterial) { private void renderQuad(BakedQuad quad, Direction cullFace, Value defaultMaterial) {
// TODO: should remove in 1.17 cycle, was for OF compat only
if (!CompatibilityHelper.canRender(quad.getVertexData())) {
return;
}
final MutableQuadViewImpl editorQuad = this.editorQuad; final MutableQuadViewImpl editorQuad = this.editorQuad;
editorQuad.fromVanilla(quad, defaultMaterial, cullFace); editorQuad.fromVanilla(quad, defaultMaterial, cullFace);
@ -132,16 +115,16 @@ public abstract class TerrainFallbackConsumer extends AbstractQuadRenderer imple
if (!editorQuad.material().disableAo(0)) { if (!editorQuad.material().disableAo(0)) {
// needs to happen before offsets are applied // needs to happen before offsets are applied
aoCalc.compute(editorQuad, true); aoCalc.compute(editorQuad, true);
tesselateSmooth(editorQuad, blockInfo.defaultLayer, editorQuad.colorIndex()); tessellateSmooth(editorQuad, blockInfo.defaultLayer, editorQuad.colorIndex());
} else { } else {
// Recomputing whether the quad has a light face is only needed if it doesn't also have a cull face, // Recomputing whether the quad has a light face is only needed if it doesn't also have a cull face,
// as in those cases, the cull face will always be used to offset the light sampling position // as in those cases, the cull face will always be used to offset the light sampling position
if (cullFace == null) { if (cullFace == null) {
// Can't rely on lazy computation in tesselateFlat() because needs to happen before offsets are applied // Can't rely on lazy computation in tessellateFlat() because needs to happen before offsets are applied
editorQuad.geometryFlags(); editorQuad.geometryFlags();
} }
tesselateFlat(editorQuad, blockInfo.defaultLayer, editorQuad.colorIndex()); tessellateFlat(editorQuad, blockInfo.defaultLayer, editorQuad.colorIndex());
} }
} }
} }

View file

@ -20,17 +20,17 @@ import java.util.function.Consumer;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.client.render.chunk.BlockBufferBuilderStorage; import net.minecraft.client.render.chunk.BlockBufferBuilderStorage;
import net.minecraft.client.render.chunk.ChunkBuilder.ChunkData;
import net.minecraft.client.render.chunk.ChunkBuilder.BuiltChunk; 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.chunk.ChunkRendererRegion;
import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BakedModel;
import net.minecraft.util.math.Matrix4f; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.crash.CrashException; import net.minecraft.util.crash.CrashException;
import net.minecraft.util.crash.CrashReport; import net.minecraft.util.crash.CrashReport;
import net.minecraft.util.crash.CrashReportSection; import net.minecraft.util.crash.CrashReportSection;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Matrix3f; import net.minecraft.util.math.Matrix3f;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.math.Matrix4f;
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh; import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
@ -43,8 +43,9 @@ import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator;
* Dispatches calls from models during chunk rebuild to the appropriate consumer, * Dispatches calls from models during chunk rebuild to the appropriate consumer,
* and holds/manages all of the state needed by them. * and holds/manages all of the state needed by them.
*/ */
public class TerrainRenderContext extends AbstractRenderContext implements RenderContext { public class TerrainRenderContext extends AbstractRenderContext {
public static final ThreadLocal<TerrainRenderContext> POOL = ThreadLocal.withInitial(TerrainRenderContext::new); public static final ThreadLocal<TerrainRenderContext> POOL = ThreadLocal.withInitial(TerrainRenderContext::new);
private final TerrainBlockRenderInfo blockInfo = new TerrainBlockRenderInfo(); private final TerrainBlockRenderInfo blockInfo = new TerrainBlockRenderInfo();
private final ChunkRenderInfo chunkInfo = new ChunkRenderInfo(); private final ChunkRenderInfo chunkInfo = new ChunkRenderInfo();
private final AoCalculator aoCalc = new AoCalculator(blockInfo, chunkInfo::cachedBrightness, chunkInfo::cachedAoLevel); private final AoCalculator aoCalc = new AoCalculator(blockInfo, chunkInfo::cachedBrightness, chunkInfo::cachedAoLevel);
@ -83,10 +84,9 @@ public class TerrainRenderContext extends AbstractRenderContext implements Rende
} }
}; };
public TerrainRenderContext prepare(ChunkRendererRegion blockView, BuiltChunk chunkRenderer, ChunkData chunkData, BlockBufferBuilderStorage builders) { public void prepare(ChunkRendererRegion blockView, BuiltChunk chunkRenderer, ChunkData chunkData, BlockBufferBuilderStorage builders) {
blockInfo.setBlockView(blockView); blockInfo.setBlockView(blockView);
chunkInfo.prepare(blockView, chunkRenderer, chunkData, builders); chunkInfo.prepare(blockView, chunkRenderer, chunkData, builders);
return this;
} }
public void release() { public void release() {
@ -95,7 +95,7 @@ public class TerrainRenderContext extends AbstractRenderContext implements Rende
} }
/** Called from chunk renderer hook. */ /** Called from chunk renderer hook. */
public boolean tesselateBlock(BlockState blockState, BlockPos blockPos, final BakedModel model, MatrixStack matrixStack) { public boolean tessellateBlock(BlockState blockState, BlockPos blockPos, final BakedModel model, MatrixStack matrixStack) {
this.matrix = matrixStack.peek().getPositionMatrix(); this.matrix = matrixStack.peek().getPositionMatrix();
this.normalMatrix = matrixStack.peek().getNormalMatrix(); this.normalMatrix = matrixStack.peek().getNormalMatrix();
@ -103,11 +103,11 @@ public class TerrainRenderContext extends AbstractRenderContext implements Rende
aoCalc.clear(); aoCalc.clear();
blockInfo.prepareForBlock(blockState, blockPos, model.useAmbientOcclusion()); blockInfo.prepareForBlock(blockState, blockPos, model.useAmbientOcclusion());
((FabricBakedModel) model).emitBlockQuads(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, blockInfo.randomSupplier, this); ((FabricBakedModel) model).emitBlockQuads(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, blockInfo.randomSupplier, this);
} catch (Throwable var9) { } catch (Throwable throwable) {
CrashReport crashReport_1 = CrashReport.create(var9, "Tesselating block in world - Indigo Renderer"); CrashReport crashReport = CrashReport.create(throwable, "Tessellating block in world - Indigo Renderer");
CrashReportSection crashReportElement_1 = crashReport_1.addElement("Block being tesselated"); CrashReportSection crashReportSection = crashReport.addElement("Block being tessellated");
CrashReportSection.addBlockInfo(crashReportElement_1, chunkInfo.blockView, blockPos, blockState); CrashReportSection.addBlockInfo(crashReportSection, chunkInfo.blockView, blockPos, blockState);
throw new CrashException(crashReport_1); throw new CrashException(crashReport);
} }
// false because we've already marked the chunk as populated - caller doesn't need to // false because we've already marked the chunk as populated - caller doesn't need to

View file

@ -30,8 +30,10 @@ import net.fabricmc.fabric.impl.client.indigo.renderer.accessor.AccessAmbientOcc
@Mixin(targets = "net.minecraft.client.render.block.BlockModelRenderer$AmbientOcclusionCalculator") @Mixin(targets = "net.minecraft.client.render.block.BlockModelRenderer$AmbientOcclusionCalculator")
public abstract class MixinAmbientOcclusionCalculator implements AccessAmbientOcclusionCalculator { public abstract class MixinAmbientOcclusionCalculator implements AccessAmbientOcclusionCalculator {
@Shadow private float[] brightness; @Shadow
@Shadow private int[] light; private float[] brightness;
@Shadow
private int[] light;
@Shadow @Shadow
public abstract void apply(BlockRenderView blockRenderView, BlockState blockState, BlockPos pos, Direction face, float[] aoData, BitSet controlBits, boolean shade); public abstract void apply(BlockRenderView blockRenderView, BlockState blockState, BlockPos pos, Direction face, float[] aoData, BitSet controlBits, boolean shade);

View file

@ -21,6 +21,7 @@ import java.util.Random;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@ -42,15 +43,16 @@ import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderContext
@Mixin(BlockModelRenderer.class) @Mixin(BlockModelRenderer.class)
public abstract class MixinBlockModelRenderer implements AccessBlockModelRenderer { public abstract class MixinBlockModelRenderer implements AccessBlockModelRenderer {
@Unique
private final ThreadLocal<BlockRenderContext> fabric_contexts = ThreadLocal.withInitial(BlockRenderContext::new);
@Shadow @Shadow
protected abstract void getQuadDimensions(BlockRenderView blockView, BlockState blockState, BlockPos blockPos, int[] vertexData, Direction face, float[] aoData, BitSet controlBits); protected abstract void getQuadDimensions(BlockRenderView blockView, BlockState blockState, BlockPos blockPos, int[] vertexData, Direction face, float[] aoData, BitSet controlBits);
private final ThreadLocal<BlockRenderContext> CONTEXTS = ThreadLocal.withInitial(BlockRenderContext::new);
@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) @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 checkSides, Random rand, long seed, int overlay, CallbackInfoReturnable<Boolean> ci) {
if (!((FabricBakedModel) model).isVanillaAdapter()) { if (!((FabricBakedModel) model).isVanillaAdapter()) {
BlockRenderContext context = CONTEXTS.get(); BlockRenderContext context = fabric_contexts.get();
// Note that we do not support face-culling here (so checkSides is ignored) // 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)); ci.setReturnValue(context.render(blockView, model, state, pos, matrix, buffer, rand, seed, overlay));
} }

View file

@ -29,7 +29,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import net.minecraft.block.BlockRenderType; import net.minecraft.block.BlockRenderType;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntity;
import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.block.BlockRenderManager; import net.minecraft.client.render.block.BlockRenderManager;
import net.minecraft.client.render.chunk.BlockBufferBuilderStorage; import net.minecraft.client.render.chunk.BlockBufferBuilderStorage;
@ -64,14 +63,14 @@ import net.fabricmc.fabric.impl.client.indigo.renderer.render.TerrainRenderConte
* (Though they can use these as an example if they wish.) * (Though they can use these as an example if they wish.)
*/ */
@Mixin(targets = "net.minecraft.client.render.chunk.ChunkBuilder$BuiltChunk$RebuildTask") @Mixin(targets = "net.minecraft.client.render.chunk.ChunkBuilder$BuiltChunk$RebuildTask")
public class MixinChunkRebuildTask { public abstract class MixinChunkRebuildTask {
@Shadow @Shadow
protected ChunkRendererRegion region; protected ChunkRendererRegion region;
@Shadow @Shadow
protected BuiltChunk field_20839; protected BuiltChunk field_20839;
@Inject(at = @At("HEAD"), method = "Lnet/minecraft/client/render/chunk/ChunkBuilder$BuiltChunk$RebuildTask;render(FFFLnet/minecraft/client/render/chunk/ChunkBuilder$ChunkData;Lnet/minecraft/client/render/chunk/BlockBufferBuilderStorage;)Ljava/util/Set;") @Inject(at = @At("HEAD"), method = "Lnet/minecraft/client/render/chunk/ChunkBuilder$BuiltChunk$RebuildTask;render(FFFLnet/minecraft/client/render/chunk/ChunkBuilder$ChunkData;Lnet/minecraft/client/render/chunk/BlockBufferBuilderStorage;)Ljava/util/Set;")
private void hookChunkBuild(float float_1, float float_2, float float_3, ChunkBuilder.ChunkData renderData, BlockBufferBuilderStorage builder, CallbackInfoReturnable<Set<BlockEntity>> ci) { private void hookChunkBuild(float cameraX, float cameraY, float cameraZ, ChunkBuilder.ChunkData renderData, BlockBufferBuilderStorage builder, CallbackInfoReturnable<Set<BlockEntity>> ci) {
ChunkRendererRegion region = this.region; ChunkRendererRegion region = this.region;
if (region != null) { if (region != null) {
@ -85,14 +84,14 @@ public class MixinChunkRebuildTask {
* This is the hook that actually implements the rendering API for terrain rendering. * This is the hook that actually implements the rendering API for terrain rendering.
* *
* <p>It's unusual to have a @Redirect in a Fabric library, but in this case * <p>It's unusual to have a @Redirect in a Fabric library, but in this case
* it is our explicit intention that {@link BlockRenderManager#tesselateBlock(BlockState, BlockPos, BlockRenderView, BufferBuilder, Random)} * it is our explicit intention that {@link BlockRenderManager#renderBlock(BlockState, BlockPos, BlockRenderView, MatrixStack, VertexConsumer, boolean, Random)}
* does not execute for models that will be rendered by our renderer. * does not execute for models that will be rendered by our renderer.
* *
* <p>Any mod that wants to redirect this specific call is likely also a renderer, in which case this * <p>Any mod that wants to redirect this specific call is likely also a renderer, in which case this
* renderer should not be present, or the mod should probably instead be relying on the renderer API * renderer should not be present, or the mod should probably instead be relying on the renderer API
* which was specifically created to provide for enhanced terrain rendering. * which was specifically created to provide for enhanced terrain rendering.
* *
* <p>Note also that {@link BlockRenderManager#tesselateBlock(BlockState, BlockPos, BlockRenderView, BufferBuilder, Random)} * <p>Note also that {@link BlockRenderManager#renderBlock(BlockState, BlockPos, BlockRenderView, MatrixStack, VertexConsumer, boolean, Random)}
* IS called if the block render type is something other than {@link BlockRenderType#MODEL}. * IS called if the block render type is something other than {@link BlockRenderType#MODEL}.
* Normally this does nothing but will allow mods to create rendering hooks that are * Normally this does nothing but will allow mods to create rendering hooks that are
* driven off of render type. (Not recommended or encouraged, but also not prevented.) * driven off of render type. (Not recommended or encouraged, but also not prevented.)
@ -106,7 +105,7 @@ public class MixinChunkRebuildTask {
if (Indigo.ALWAYS_TESSELATE_INDIGO || !((FabricBakedModel) model).isVanillaAdapter()) { if (Indigo.ALWAYS_TESSELATE_INDIGO || !((FabricBakedModel) model).isVanillaAdapter()) {
Vec3d vec3d = blockState.getModelOffset(blockView, blockPos); Vec3d vec3d = blockState.getModelOffset(blockView, blockPos);
matrix.translate(vec3d.x, vec3d.y, vec3d.z); matrix.translate(vec3d.x, vec3d.y, vec3d.z);
return ((AccessChunkRendererRegion) blockView).fabric_getRenderer().tesselateBlock(blockState, blockPos, model, matrix); return ((AccessChunkRendererRegion) blockView).fabric_getRenderer().tessellateBlock(blockState, blockPos, model, matrix);
} }
} }

View file

@ -27,7 +27,7 @@ import net.minecraft.client.render.chunk.ChunkBuilder.ChunkData;
import net.fabricmc.fabric.impl.client.indigo.renderer.accessor.AccessChunkRendererData; import net.fabricmc.fabric.impl.client.indigo.renderer.accessor.AccessChunkRendererData;
@Mixin(ChunkData.class) @Mixin(ChunkData.class)
public class MixinChunkRenderData implements AccessChunkRendererData { public abstract class MixinChunkRenderData implements AccessChunkRendererData {
@Shadow @Shadow
private Set<RenderLayer> initializedLayers; private Set<RenderLayer> initializedLayers;
@Shadow @Shadow

View file

@ -17,6 +17,7 @@
package net.fabricmc.fabric.mixin.client.indigo.renderer; package net.fabricmc.fabric.mixin.client.indigo.renderer;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import net.minecraft.client.render.chunk.ChunkRendererRegion; import net.minecraft.client.render.chunk.ChunkRendererRegion;
@ -25,6 +26,7 @@ import net.fabricmc.fabric.impl.client.indigo.renderer.render.TerrainRenderConte
@Mixin(ChunkRendererRegion.class) @Mixin(ChunkRendererRegion.class)
public abstract class MixinChunkRendererRegion implements AccessChunkRendererRegion { public abstract class MixinChunkRendererRegion implements AccessChunkRendererRegion {
@Unique
private TerrainRenderContext fabric_renderer; private TerrainRenderContext fabric_renderer;
@Override @Override

View file

@ -18,6 +18,7 @@ package net.fabricmc.fabric.mixin.client.indigo.renderer;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@ -32,29 +33,29 @@ import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel; import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
import net.fabricmc.fabric.impl.client.indigo.renderer.render.ItemRenderContext;
import net.fabricmc.fabric.impl.client.indigo.renderer.render.ItemRenderContext.VanillaQuadHandler;
import net.fabricmc.fabric.impl.client.indigo.renderer.accessor.AccessItemRenderer; import net.fabricmc.fabric.impl.client.indigo.renderer.accessor.AccessItemRenderer;
import net.fabricmc.fabric.impl.client.indigo.renderer.render.IndigoQuadHandler; import net.fabricmc.fabric.impl.client.indigo.renderer.render.IndigoQuadHandler;
import net.fabricmc.fabric.impl.client.indigo.renderer.render.ItemRenderContext;
import net.fabricmc.fabric.impl.client.indigo.renderer.render.ItemRenderContext.VanillaQuadHandler;
@Mixin(ItemRenderer.class) @Mixin(ItemRenderer.class)
public abstract class MixinItemRenderer implements AccessItemRenderer { public abstract class MixinItemRenderer implements AccessItemRenderer {
@Shadow
protected abstract void renderBakedItemModel(BakedModel model, ItemStack stack, int light, int overlay, MatrixStack matrixStack, VertexConsumer buffer);
@Shadow @Shadow
protected ItemColors colors; protected ItemColors colors;
private final VanillaQuadHandler vanillaHandler = new IndigoQuadHandler(this); @Unique
private final ThreadLocal<ItemRenderContext> fabric_contexts = ThreadLocal.withInitial(() -> new ItemRenderContext(colors));
private final ThreadLocal<ItemRenderContext> CONTEXTS = ThreadLocal.withInitial(() -> new ItemRenderContext(colors)); @Unique
private final VanillaQuadHandler fabric_vanillaHandler = new IndigoQuadHandler(this);
@Shadow
protected abstract void renderBakedItemModel(BakedModel model, ItemStack stack, int light, int overlay, MatrixStack matrixStack, VertexConsumer buffer);
@Inject(at = @At("HEAD"), method = "renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/render/model/json/ModelTransformation$Mode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;)V", cancellable = true) @Inject(at = @At("HEAD"), method = "renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/render/model/json/ModelTransformation$Mode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;)V", cancellable = true)
public void hook_method_23179(ItemStack stack, ModelTransformation.Mode transformMode, boolean invert, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int light, int overlay, BakedModel model, CallbackInfo ci) { public void hook_renderItem(ItemStack stack, ModelTransformation.Mode transformMode, boolean invert, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int light, int overlay, BakedModel model, CallbackInfo ci) {
final FabricBakedModel fabricModel = (FabricBakedModel) model; if (!stack.isEmpty() && !((FabricBakedModel) model).isVanillaAdapter()) {
fabric_contexts.get().renderModel(stack, transformMode, invert, matrixStack, vertexConsumerProvider, light, overlay, model, fabric_vanillaHandler);
if (!(stack.isEmpty() || fabricModel.isVanillaAdapter())) {
CONTEXTS.get().renderModel(stack, transformMode, invert, matrixStack, vertexConsumerProvider, light, overlay, fabricModel, vanillaHandler);
ci.cancel(); ci.cancel();
} }
} }