Indigo and Renderer API fixes related to fallback consumers ()

* Fix : Indigo fallback consumer does not respect BlendMode or emissivity

* Change renderer testmod to test material change

* Remove presumably unneeded `quad.geometryFlags()` call

* Also test emissivity

* Call emitBlockQuads in the testmod

* Allow passing the block state explicitly to the fallback consumer. Fixes 

* Expand testmod to also test item models

* Also fix fallback consumer ignoring material for items

* Slight changes

* Introduce new interface for the expanded fallback consumer

* Add javadoc to ModelHelper

(cherry picked from commit 9f179aa14c)

(cherry picked from commit 2e5408b563)
This commit is contained in:
Technici4n 2023-01-02 13:05:49 +00:00 committed by modmuss50
parent 88087ab966
commit 6bd39c990e
12 changed files with 220 additions and 113 deletions
fabric-renderer-api-v1/src
main/java/net/fabricmc/fabric
api/renderer/v1
mixin/renderer/client
testmod/java/net/fabricmc/fabric/test/renderer/simple/client
fabric-renderer-indigo/src/main/java/net/fabricmc/fabric/impl/client/indigo/renderer/render

View file

@ -36,6 +36,8 @@ import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
* Can also be used to generate or customize outputs based on world state instead of
* or in addition to block state when render chunks are rebuilt.
*
* <p>Implementors should have a look at {@link ModelHelper} as it contains many useful functions.
*
* <p>Note for {@link Renderer} implementors: Fabric causes BakedModel to extend this
* interface with {@link #isVanillaAdapter()} == true and to produce standard vertex data.
* This means any BakedModel instance can be safely cast to this interface without an instanceof check.
@ -82,7 +84,7 @@ public interface FabricBakedModel {
* parameter is normally initialized with the same seed prior to each face layer.
* Model authors should note this method is called only once per block, and call the provided
* Random supplier multiple times if re-seeding is necessary. For wrapped vanilla baked models,
* it will probably be easier to use {@link RenderContext#fallbackConsumer} which handles
* it will probably be easier to use {@link RenderContext#bakedModelConsumer()} which handles
* re-seeding per face automatically.
*
* @param blockView Access to world state. Using {@link net.fabricmc.fabric.api.rendering.data.v1.RenderAttachedBlockView#getBlockEntityRenderAttachment(BlockPos)} to

View file

@ -18,6 +18,9 @@ package net.fabricmc.fabric.api.renderer.v1.render;
import java.util.function.Consumer;
import org.jetbrains.annotations.Nullable;
import net.minecraft.block.BlockState;
import net.minecraft.client.render.model.BakedModel;
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
@ -39,11 +42,64 @@ public interface RenderContext {
Consumer<Mesh> meshConsumer();
/**
* Fallback consumer that can process a vanilla {@link BakedModel}.
* Fabric causes vanilla baked models to send themselves
* via this interface. Can also be used by compound models that contain a mix
* of vanilla baked models, packaged quads and/or dynamic elements.
*/
Consumer<BakedModel> fallbackConsumer();
default BakedModelConsumer bakedModelConsumer() {
// Default implementation is provided for compat with older renderer implementations,
// but they should always override this function.
Consumer<BakedModel> fallback = fallbackConsumer();
return new BakedModelConsumer() {
@Override
public void accept(BakedModel model) {
fallback.accept(model);
}
@Override
public void accept(BakedModel model, @Nullable BlockState state) {
fallback.accept(model);
}
};
}
interface BakedModelConsumer extends Consumer<BakedModel> {
/**
* Render a baked model by processing its {@linkplain BakedModel#getQuads} using the rendered block state.
*
* <p>For block contexts, this will pass the block state being rendered to {@link BakedModel#getQuads}.
* For item contexts, this will pass a {@code null} block state to {@link BakedModel#getQuads}.
* {@link #accept(BakedModel, BlockState)} can be used instead to pass the block state explicitly.
*/
@Override
void accept(BakedModel model);
/**
* Render a baked model by processing its {@linkplain BakedModel#getQuads} with an explicit block state.
*
* <p>This overload allows passing the block state (or {@code null} to query the item quads).
* This is useful when a model is being wrapped, and expects a different
* block state than the one of the block being rendered.
*
* <p>For item render contexts, you can use this function if you want to render the model with a specific block state.
* Otherwise, use {@linkplain #accept(BakedModel)} the other overload} to render the usual item quads.
*/
void accept(BakedModel model, @Nullable BlockState state);
}
/**
* Fabric causes vanilla baked models to send themselves
* via this interface. Can also be used by compound models that contain a mix
* of vanilla baked models, packaged quads and/or dynamic elements.
*
* @deprecated Prefer using the more flexible {@link #bakedModelConsumer}.
*/
@Deprecated
default Consumer<BakedModel> fallbackConsumer() {
// This default implementation relies on implementors overriding bakedModelConsumer().
return bakedModelConsumer();
}
/**
* Returns a {@link QuadEmitter} instance that emits directly to the render buffer.

View file

@ -42,11 +42,12 @@ public interface MixinBakedModel extends FabricBakedModel {
@Override
default void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
context.fallbackConsumer().accept((BakedModel) this);
context.bakedModelConsumer().accept((BakedModel) this, state);
}
@Override
default void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) {
context.fallbackConsumer().accept((BakedModel) this);
// Pass null state to enforce item quads in block render contexts
context.bakedModelConsumer().accept((BakedModel) this, null);
}
}

View file

@ -25,6 +25,7 @@ import org.jetbrains.annotations.Nullable;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.BakedQuad;
@ -36,20 +37,29 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.BlockRenderView;
import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode;
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
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.ModelHelper;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.fabricmc.fabric.api.rendering.data.v1.RenderAttachedBlockView;
final class FrameBakedModel implements BakedModel, FabricBakedModel {
private final Mesh frameMesh;
private final Sprite frameSprite;
private final RenderMaterial translucentMaterial;
private final RenderMaterial translucentEmissiveMaterial;
FrameBakedModel(Mesh frameMesh, Sprite frameSprite) {
this.frameMesh = frameMesh;
this.frameSprite = frameSprite;
Renderer renderer = RendererAccess.INSTANCE.getRenderer();
this.translucentMaterial = renderer.materialFinder().blendMode(0, BlendMode.TRANSLUCENT).find();
this.translucentEmissiveMaterial = renderer.materialFinder().blendMode(0, BlendMode.TRANSLUCENT).emissive(0, true).find();
}
@Override
@ -69,7 +79,7 @@ final class FrameBakedModel implements BakedModel, FabricBakedModel {
@Override
public boolean isSideLit() {
return false;
return true; // we want the block to be lit from the side when rendered as an item
}
@Override
@ -84,7 +94,7 @@ final class FrameBakedModel implements BakedModel, FabricBakedModel {
@Override
public ModelTransformation getTransformation() {
return ModelTransformation.NONE;
return ModelHelper.MODEL_TRANSFORM_BLOCK;
}
@Override
@ -112,27 +122,71 @@ final class FrameBakedModel implements BakedModel, FabricBakedModel {
return; // No inner block to render
}
Sprite sprite = MinecraftClient.getInstance().getBlockRenderManager().getModels().getModelManager().getBlockModels().getModelParticleSprite(data.getDefaultState());
QuadEmitter emitter = context.getEmitter();
BlockState innerState = data.getDefaultState();
// We can emit our quads outside of the mesh as the block being put in the frame is very much dynamic.
// Emit the quads for each face of the block inside the frame
for (Direction direction : Direction.values()) {
// Add a face, with an inset to give the appearance of the block being in a frame.
emitter.square(direction, 0.1F, 0.1F, 0.9F, 0.9F, 0.1F)
// Set the sprite of the fact, use whole texture via BAKE_LOCK_UV
.spriteBake(0, sprite, MutableQuadView.BAKE_LOCK_UV)
// Allow textures
// TODO: the magic values here are not documented at all and probably should be
.spriteColor(0, -1, -1, -1, -1)
// Emit the quad
.emit();
}
// Now, we emit a transparent scaled-down version of the inner model
// Try both emissive and non-emissive versions of the translucent material
RenderMaterial material = pos.getX() % 2 == 0 ? translucentMaterial : translucentEmissiveMaterial;
emitInnerQuads(context, material, () -> {
// Use emitBlockQuads to allow for Renderer API features
((FabricBakedModel) MinecraftClient.getInstance().getBlockRenderManager().getModel(innerState)).emitBlockQuads(blockView, innerState, pos, randomSupplier, context);
});
}
@Override
public void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) {
// TODO: Implement an item test.
// For now we will just leave this as I have not added a block item yet
// Emit our frame mesh
context.meshConsumer().accept(this.frameMesh);
// Emit a scaled-down fence for testing, trying both materials again.
RenderMaterial material = stack.hasCustomName() ? translucentEmissiveMaterial : translucentMaterial;
BlockState innerState = Blocks.OAK_FENCE.getDefaultState();
emitInnerQuads(context, material, () -> {
// Need to use the fallback consumer directly:
// - we can't use emitBlockQuads because we don't have a blockView
// - we can't use emitItemQuads because multipart models don't have item quads
context.bakedModelConsumer().accept(MinecraftClient.getInstance().getBlockRenderManager().getModel(innerState), innerState);
});
}
/**
* Emit a scaled-down version of the inner model.
*/
private void emitInnerQuads(RenderContext context, RenderMaterial material, Runnable innerModelEmitter) {
// Let's push a transform to scale the model down and make it transparent
context.pushTransform(quad -> {
// Scale model down
for (int vertex = 0; vertex < 4; ++vertex) {
float x = quad.x(vertex) * 0.8f + 0.1f;
float y = quad.y(vertex) * 0.8f + 0.1f;
float z = quad.z(vertex) * 0.8f + 0.1f;
quad.pos(vertex, x, y, z);
}
// Make the quad partially transparent
// Change material to translucent
quad.material(material);
// Change vertex colors to be partially transparent
for (int vertex = 0; vertex < 4; ++vertex) {
int color = quad.spriteColor(vertex, 0);
int alpha = (color >> 24) & 0xFF;
alpha = alpha * 3 / 4;
color = (color & 0xFFFFFF) | (alpha << 24);
quad.spriteColor(vertex, 0, color);
}
// Return true because we want the quad to be rendered
return true;
});
// Emit the inner block model
innerModelEmitter.run();
// Let's not forget to pop the transform!
context.popTransform();
}
}

View file

@ -16,6 +16,9 @@
package net.fabricmc.fabric.test.renderer.simple.client;
import java.util.HashSet;
import java.util.Set;
import org.jetbrains.annotations.Nullable;
import net.minecraft.client.render.model.UnbakedModel;
@ -28,12 +31,12 @@ import net.fabricmc.fabric.api.client.model.ModelResourceProvider;
* Provides the unbaked model for use with the frame block.
*/
final class FrameModelResourceProvider implements ModelResourceProvider {
private static final Identifier FRAME_MODEL_ID = new Identifier("fabric-renderer-api-v1-testmod", "block/frame");
static final Set<Identifier> FRAME_MODELS = new HashSet<>();
@Nullable
@Override
public UnbakedModel loadModelResource(Identifier resourceId, ModelProviderContext context) {
if (resourceId.equals(FRAME_MODEL_ID)) {
if (FRAME_MODELS.contains(resourceId)) {
return new FrameUnbakedModel();
}

View file

@ -16,7 +16,10 @@
package net.fabricmc.fabric.test.renderer.simple.client;
import static net.fabricmc.fabric.test.renderer.simple.RendererTest.id;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.util.registry.Registry;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap;
@ -31,7 +34,14 @@ public final class RendererClientTest implements ClientModInitializer {
ModelLoadingRegistry.INSTANCE.registerVariantProvider(manager -> new PillarModelVariantProvider());
for (FrameBlock frameBlock : RendererTest.FRAMES) {
// We don't specify a material for the frame mesh,
// so it will use the default material, i.e. the one from BlockRenderLayerMap.
BlockRenderLayerMap.INSTANCE.putBlock(frameBlock, RenderLayer.getCutoutMipped());
String itemPath = Registry.ITEM.getId(frameBlock.asItem()).getPath();
FrameModelResourceProvider.FRAME_MODELS.add(id("item/" + itemPath));
}
FrameModelResourceProvider.FRAME_MODELS.add(id("block/frame"));
}
}

View file

@ -26,7 +26,6 @@ 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.render.RenderContext.QuadTransform;
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.aocalc.AoCalculator;
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat;
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MeshImpl;
@ -55,7 +54,7 @@ public abstract class AbstractMeshConsumer extends AbstractQuadRenderer implemen
@Override
public Maker emit() {
computeGeometry();
renderQuad(this);
renderQuad(this, false);
clear();
return this;
}
@ -74,7 +73,7 @@ public abstract class AbstractMeshConsumer extends AbstractQuadRenderer implemen
System.arraycopy(data, index, editorQuad.data(), 0, EncodingFormat.TOTAL_STRIDE);
editorQuad.load();
index += EncodingFormat.TOTAL_STRIDE;
renderQuad(editorQuad);
renderQuad(editorQuad, false);
}
}
@ -82,43 +81,4 @@ public abstract class AbstractMeshConsumer extends AbstractQuadRenderer implemen
editorQuad.clear();
return editorQuad;
}
private void renderQuad(MutableQuadViewImpl quad) {
if (!transform.transform(quad)) {
return;
}
if (!blockInfo.shouldDrawFace(quad.cullFace())) {
return;
}
tessellateQuad(quad, 0);
}
/**
* Determines color index and render layer, then routes to appropriate
* tessellate routine based on material properties.
*/
private void tessellateQuad(MutableQuadViewImpl quad, int textureIndex) {
final RenderMaterialImpl.Value mat = quad.material();
final int colorIndex = mat.disableColorIndex(textureIndex) ? -1 : quad.colorIndex();
final RenderLayer renderLayer = blockInfo.effectiveRenderLayer(mat.blendMode(textureIndex));
if (blockInfo.defaultAo && !mat.disableAo(textureIndex)) {
// needs to happen before offsets are applied
aoCalc.compute(quad, false);
if (mat.emissive(textureIndex)) {
tessellateSmoothEmissive(quad, renderLayer, colorIndex);
} else {
tessellateSmooth(quad, renderLayer, colorIndex);
}
} else {
if (mat.emissive(textureIndex)) {
tessellateFlatEmissive(quad, renderLayer, colorIndex);
} else {
tessellateFlat(quad, renderLayer, colorIndex);
}
}
}
}

View file

@ -33,6 +33,7 @@ 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.impl.client.indigo.renderer.RenderMaterialImpl;
import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator;
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.ColorHelper;
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.GeometryHelper;
@ -62,6 +63,45 @@ public abstract class AbstractQuadRenderer {
this.transform = transform;
}
protected void renderQuad(MutableQuadViewImpl quad, boolean isVanilla) {
if (!transform.transform(quad)) {
return;
}
if (!blockInfo.shouldDrawFace(quad.cullFace())) {
return;
}
tessellateQuad(quad, 0, isVanilla);
}
/**
* Determines color index and render layer, then routes to appropriate
* tessellate routine based on material properties.
*/
private void tessellateQuad(MutableQuadViewImpl quad, int textureIndex, boolean isVanilla) {
final RenderMaterialImpl.Value mat = quad.material();
final int colorIndex = mat.disableColorIndex(textureIndex) ? -1 : quad.colorIndex();
final RenderLayer renderLayer = blockInfo.effectiveRenderLayer(mat.blendMode(textureIndex));
if (blockInfo.defaultAo && !mat.disableAo(textureIndex)) {
// needs to happen before offsets are applied
aoCalc.compute(quad, isVanilla);
if (mat.emissive(textureIndex)) {
tessellateSmoothEmissive(quad, renderLayer, colorIndex);
} else {
tessellateSmooth(quad, renderLayer, colorIndex);
}
} else {
if (mat.emissive(textureIndex)) {
tessellateFlatEmissive(quad, renderLayer, colorIndex);
} else {
tessellateFlat(quad, renderLayer, colorIndex);
}
}
}
/** handles block color and red-blue swizzle, common to all renders. */
private void colorizeQuad(MutableQuadViewImpl q, int blockColorIndex) {
if (blockColorIndex == -1) {

View file

@ -141,7 +141,7 @@ public class BlockRenderContext extends AbstractRenderContext {
}
@Override
public Consumer<BakedModel> fallbackConsumer() {
public BakedModelConsumer bakedModelConsumer() {
return fallbackConsumer;
}

View file

@ -21,6 +21,8 @@ import java.util.Random;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
import net.minecraft.block.BlockState;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.color.item.ItemColors;
@ -265,21 +267,27 @@ public class ItemRenderContext extends AbstractRenderContext {
}
}
private class FallbackConsumer implements Consumer<BakedModel> {
private class FallbackConsumer implements BakedModelConsumer {
@Override
public void accept(BakedModel model) {
accept(model, null);
}
@Override
public void accept(BakedModel model, @Nullable BlockState state) {
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 List<BakedQuad> quads = model.getQuads(state, 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);
editorQuad.fromVanilla(q, IndigoRenderer.MATERIAL_STANDARD, cullFace);
renderMeshQuad(editorQuad);
}
}
}
@ -287,17 +295,6 @@ public class ItemRenderContext extends AbstractRenderContext {
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
@ -306,7 +303,7 @@ public class ItemRenderContext extends AbstractRenderContext {
}
@Override
public Consumer<BakedModel> fallbackConsumer() {
public BakedModelConsumer bakedModelConsumer() {
return fallbackConsumer;
}

View file

@ -22,6 +22,8 @@ import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
import net.minecraft.block.BlockState;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumer;
@ -31,6 +33,7 @@ import net.minecraft.util.math.Direction;
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;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext.QuadTransform;
import net.fabricmc.fabric.impl.client.indigo.renderer.IndigoRenderer;
import net.fabricmc.fabric.impl.client.indigo.renderer.RenderMaterialImpl.Value;
@ -57,7 +60,7 @@ import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MutableQuadViewImpl;
* vertex data is sent to the byte buffer. Generally POJO array access will be faster than
* manipulating the data via NIO.
*/
public abstract class TerrainFallbackConsumer extends AbstractQuadRenderer implements Consumer<BakedModel> {
public abstract class TerrainFallbackConsumer extends AbstractQuadRenderer implements RenderContext.BakedModelConsumer {
private static final Value MATERIAL_FLAT = (Value) IndigoRenderer.INSTANCE.materialFinder().disableAo(0, true).find();
private static final Value MATERIAL_SHADED = (Value) IndigoRenderer.INSTANCE.materialFinder().find();
@ -79,10 +82,14 @@ public abstract class TerrainFallbackConsumer extends AbstractQuadRenderer imple
};
@Override
public void accept(BakedModel model) {
public void accept(BakedModel bakedModel) {
accept(bakedModel, blockInfo.blockState);
}
@Override
public void accept(BakedModel model, @Nullable BlockState blockState) {
final Supplier<Random> random = blockInfo.randomSupplier;
final Value defaultMaterial = blockInfo.defaultAo && model.useAmbientOcclusion() ? MATERIAL_SHADED : MATERIAL_FLAT;
final BlockState blockState = blockInfo.blockState;
for (int i = 0; i <= ModelHelper.NULL_FACE_ID; i++) {
final Direction cullFace = ModelHelper.faceFromIndex(i);
@ -102,29 +109,6 @@ public abstract class TerrainFallbackConsumer extends AbstractQuadRenderer imple
final MutableQuadViewImpl editorQuad = this.editorQuad;
editorQuad.fromVanilla(quad, defaultMaterial, cullFace);
if (!transform.transform(editorQuad)) {
return;
}
cullFace = editorQuad.cullFace();
if (cullFace != null && !blockInfo.shouldDrawFace(cullFace)) {
return;
}
if (!editorQuad.material().disableAo(0)) {
// needs to happen before offsets are applied
aoCalc.compute(editorQuad, true);
tessellateSmooth(editorQuad, blockInfo.defaultLayer, editorQuad.colorIndex());
} else {
// 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
if (cullFace == null) {
// Can't rely on lazy computation in tessellateFlat() because needs to happen before offsets are applied
editorQuad.geometryFlags();
}
tessellateFlat(editorQuad, blockInfo.defaultLayer, editorQuad.colorIndex());
}
renderQuad(editorQuad, true);
}
}

View file

@ -120,7 +120,7 @@ public class TerrainRenderContext extends AbstractRenderContext {
}
@Override
public Consumer<BakedModel> fallbackConsumer() {
public BakedModelConsumer bakedModelConsumer() {
return fallbackConsumer;
}