Skip to content

Commit

Permalink
FRAPI improvements: context getters, full removal of fallback consume…
Browse files Browse the repository at this point in the history
…rs, small enhancements (#3287)

* Add cull check and item transformation mode getter to FRAPI

* Terminally deprecate `fallbackConsumer` and `bakedModelConsumer`

* Fix uvs in octagonal column test mod

* Review comments

(cherry picked from commit 39a511b)
  • Loading branch information
Technici4n authored and modmuss50 committed Sep 18, 2023
1 parent bbfadae commit 2034447
Show file tree
Hide file tree
Showing 11 changed files with 255 additions and 134 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import net.minecraft.world.BlockRenderView;

import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.fabricmc.fabric.impl.renderer.VanillaModelEncoder;

/**
* Interface for baked models that output meshes with enhanced rendering features.
Expand Down Expand Up @@ -92,9 +93,8 @@ default boolean isVanillaAdapter() {
* Will not be thread-safe. Do not cache or retain a reference.
* @param context Accepts model output.
*/
@SuppressWarnings("deprecation")
default void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
context.bakedModelConsumer().accept((BakedModel) this, state);
VanillaModelEncoder.emitBlockQuads((BakedModel) this, state, randomSupplier, context, context.getEmitter());
}

/**
Expand Down Expand Up @@ -124,9 +124,7 @@ default void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPo
* logic here, instead of returning every possible shape from {@link BakedModel#getOverrides}
* as vanilla baked models.
*/
@SuppressWarnings("deprecation")
default void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) {
// Pass null state to enforce item quads in block render contexts
context.bakedModelConsumer().accept((BakedModel) this, null);
VanillaModelEncoder.emitItemQuads((BakedModel) this, null, randomSupplier, context);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,16 @@

import net.minecraft.block.BlockState;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.json.ModelTransformationMode;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.BlockRenderView;

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.mesh.QuadView;
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;

/**
Expand All @@ -54,6 +57,15 @@ public interface RenderContext {
*/
QuadEmitter getEmitter();

/**
* Returns whether this context currently has at least one transform.
*
* @apiNote The default implementation will be removed in the next breaking release.
*/
default boolean hasTransform() {
return true;
}

/**
* Causes all models/quads/meshes sent to this consumer to be transformed by the provided
* {@link QuadTransform} that edits each quad before buffering. Quads in the mesh will
Expand All @@ -67,6 +79,8 @@ public interface RenderContext {
*
* <p>Meshes are never mutated by the transformer - only buffered quads. This ensures thread-safe
* use of meshes/models across multiple chunk builders.
*
* <p>Using the {@linkplain #getEmitter() quad emitter of this context} from the inside of a quad transform is not supported.
*/
void pushTransform(QuadTransform transform);

Expand All @@ -76,6 +90,36 @@ public interface RenderContext {
*/
void popTransform();

/**
* Returns {@code true} if the given face will be culled away.
*
* <p>This function can be used to skip complex transformations of quads that will be culled anyway.
* The cull face of a quad is determined by {@link QuadView#cullFace()}.
* Note that if {@linkplain #hasTransform() there is a transform}, no computation should be skipped,
* because the cull face might be changed by the transform,
* or the transform might wish to receive culled faces too.
*
* <p>This function can only be used on a block render context (i.e. in {@link FabricBakedModel#emitBlockQuads}).
* Calling it on another context (e.g. in {@link FabricBakedModel#emitItemQuads}) will throw an exception.
*
* @apiNote The default implementation will be removed in the next breaking release.
*/
default boolean isFaceCulled(@Nullable Direction face) {
return false;
}

/**
* Returns the current transformation mode.
*
* <p>This function can only be used on an item render context (i.e. in {@link FabricBakedModel#emitItemQuads}).
* Calling it on another context (e.g. in {@link FabricBakedModel#emitBlockQuads}) will throw an exception.
*
* @apiNote The default implementation will be removed in the next breaking release.
*/
default ModelTransformationMode itemTransformationMode() {
return ModelTransformationMode.NONE;
}

@FunctionalInterface
interface QuadTransform {
/**
Expand All @@ -89,27 +133,29 @@ interface QuadTransform {
* @deprecated Use {@link Mesh#outputTo(QuadEmitter)} instead.
*/
@Deprecated
Consumer<Mesh> meshConsumer();
default Consumer<Mesh> meshConsumer() {
return mesh -> mesh.outputTo(getEmitter());
}

/**
* @deprecated Use {@link FabricBakedModel#emitBlockQuads(BlockRenderView, BlockState, BlockPos, Supplier, RenderContext) emitBlockQuads}
* or {@link FabricBakedModel#emitItemQuads(ItemStack, Supplier, RenderContext) emitItemQuads} on the baked model
* that you want to consume instead.
*/
@Deprecated
@Deprecated(forRemoval = true)
BakedModelConsumer bakedModelConsumer();

/**
* @deprecated Use {@link FabricBakedModel#emitBlockQuads(BlockRenderView, BlockState, BlockPos, Supplier, RenderContext) emitBlockQuads}
* or {@link FabricBakedModel#emitItemQuads(ItemStack, Supplier, RenderContext) emitItemQuads} on the baked model
* that you want to consume instead.
*/
@Deprecated
@Deprecated(forRemoval = true)
default Consumer<BakedModel> fallbackConsumer() {
return bakedModelConsumer();
}

@Deprecated
@Deprecated(forRemoval = true)
interface BakedModelConsumer extends Consumer<BakedModel> {
/**
* Render a baked model by processing its {@linkplain BakedModel#getQuads} using the rendered block state.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* 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.renderer;

import java.util.List;
import java.util.function.Supplier;

import org.jetbrains.annotations.Nullable;

import net.minecraft.block.BlockState;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.random.Random;

import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
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.util.TriState;

/**
* Routines for adaptation of vanilla {@link BakedModel}s to FRAPI pipelines.
* Even though Indigo calls them directly, they are not for use by third party renderers, and might change at any time.
*/
public class VanillaModelEncoder {
private static final Renderer RENDERER = RendererAccess.INSTANCE.getRenderer();
private static final RenderMaterial MATERIAL_STANDARD = RENDERER.materialFinder().find();
private static final RenderMaterial MATERIAL_NO_AO = RENDERER.materialFinder().ambientOcclusion(TriState.FALSE).find();

// Separate QuadEmitter parameter so that Indigo can pass its own emitter that handles vanilla quads differently.
public static void emitBlockQuads(BakedModel model, @Nullable BlockState state, Supplier<Random> randomSupplier, RenderContext context, QuadEmitter emitter) {
final RenderMaterial defaultMaterial = model.useAmbientOcclusion() ? MATERIAL_STANDARD : MATERIAL_NO_AO;

for (int i = 0; i <= ModelHelper.NULL_FACE_ID; i++) {
final Direction cullFace = ModelHelper.faceFromIndex(i);

if (!context.hasTransform() && context.isFaceCulled(cullFace)) {
// Skip entire quad list if possible.
continue;
}

final List<BakedQuad> quads = model.getQuads(state, cullFace, randomSupplier.get());
final int count = quads.size();

for (int j = 0; j < count; j++) {
final BakedQuad q = quads.get(j);
emitter.fromVanilla(q, defaultMaterial, cullFace);
emitter.emit();
}
}
}

public static void emitItemQuads(BakedModel model, @Nullable BlockState state, Supplier<Random> randomSupplier, RenderContext context) {
QuadEmitter emitter = context.getEmitter();

for (int i = 0; i <= ModelHelper.NULL_FACE_ID; i++) {
final Direction cullFace = ModelHelper.faceFromIndex(i);
final List<BakedQuad> quads = model.getQuads(state, cullFace, randomSupplier.get());
final int count = quads.size();

for (int j = 0; j < count; j++) {
final BakedQuad q = quads.get(j);
emitter.fromVanilla(q, MATERIAL_STANDARD, cullFace);
emitter.emit();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import net.minecraft.client.texture.SpriteAtlasTexture;
import net.minecraft.client.util.SpriteIdentifier;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.Direction;

import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
Expand Down Expand Up @@ -79,6 +80,7 @@ public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> textureGe
emitter.pos(1, 0.5f, 1, 0.5f);
emitter.pos(2, 1, 1, A);
emitter.pos(3, B, 1, 0);
emitter.cullFace(Direction.UP);
emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
emitter.color(-1, -1, -1, -1);
emitter.emit();
Expand All @@ -87,6 +89,7 @@ public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> textureGe
emitter.pos(1, 0, 1, B);
emitter.pos(2, 0.5f, 1, 0.5f);
emitter.pos(3, A, 1, 0);
emitter.cullFace(Direction.UP);
emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
emitter.color(-1, -1, -1, -1);
emitter.emit();
Expand All @@ -95,6 +98,7 @@ public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> textureGe
emitter.pos(1, A, 1, 1);
emitter.pos(2, B, 1, 1);
emitter.pos(3, 0.5f, 1, 0.5f);
emitter.cullFace(Direction.UP);
emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
emitter.color(-1, -1, -1, -1);
emitter.emit();
Expand All @@ -103,6 +107,7 @@ public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> textureGe
emitter.pos(1, B, 1, 1);
emitter.pos(2, 1, 1, B);
emitter.pos(3, 1, 1, A);
emitter.cullFace(Direction.UP);
emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
emitter.color(-1, -1, -1, -1);
emitter.emit();
Expand All @@ -113,6 +118,7 @@ public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> textureGe
emitter.pos(1, 0.5f, 0, 0.5f);
emitter.pos(2, 1, 0, B);
emitter.pos(3, B, 0, 1);
emitter.cullFace(Direction.DOWN);
emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
emitter.color(-1, -1, -1, -1);
emitter.emit();
Expand All @@ -121,6 +127,7 @@ public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> textureGe
emitter.pos(1, 0, 0, A);
emitter.pos(2, 0.5f, 0, 0.5f);
emitter.pos(3, A, 0, 1);
emitter.cullFace(Direction.DOWN);
emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
emitter.color(-1, -1, -1, -1);
emitter.emit();
Expand All @@ -129,6 +136,7 @@ public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> textureGe
emitter.pos(1, A, 0, 0);
emitter.pos(2, B, 0, 0);
emitter.pos(3, 0.5f, 0, 0.5f);
emitter.cullFace(Direction.DOWN);
emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
emitter.color(-1, -1, -1, -1);
emitter.emit();
Expand All @@ -137,6 +145,7 @@ public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> textureGe
emitter.pos(1, B, 0, 0);
emitter.pos(2, 1, 0, A);
emitter.pos(3, 1, 0, B);
emitter.cullFace(Direction.DOWN);
emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
emitter.color(-1, -1, -1, -1);
emitter.emit();
Expand All @@ -146,6 +155,7 @@ public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> textureGe
emitter.pos(1, B, 0, 0);
emitter.pos(2, A, 0, 0);
emitter.pos(3, A, 1, 0);
emitter.cullFace(Direction.NORTH);
emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
emitter.material(glintMaterial);
emitter.color(-1, -1, -1, -1);
Expand All @@ -156,7 +166,7 @@ public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> textureGe
emitter.pos(1, A, 0, 0);
emitter.pos(2, 0, 0, A);
emitter.pos(3, 0, 1, A);
emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
cornerSprite(emitter, whiteConcreteSprite);
emitter.material(glintMaterial);
emitter.color(-1, -1, -1, -1);
emitter.emit();
Expand All @@ -166,6 +176,7 @@ public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> textureGe
emitter.pos(1, 0, 0, A);
emitter.pos(2, 0, 0, B);
emitter.pos(3, 0, 1, B);
emitter.cullFace(Direction.WEST);
emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
emitter.material(glintMaterial);
emitter.color(-1, -1, -1, -1);
Expand All @@ -176,7 +187,7 @@ public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> textureGe
emitter.pos(1, 0, 0, B);
emitter.pos(2, A, 0, 1);
emitter.pos(3, A, 1, 1);
emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
cornerSprite(emitter, whiteConcreteSprite);
emitter.material(glintMaterial);
emitter.color(-1, -1, -1, -1);
emitter.emit();
Expand All @@ -186,6 +197,7 @@ public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> textureGe
emitter.pos(1, A, 0, 1);
emitter.pos(2, B, 0, 1);
emitter.pos(3, B, 1, 1);
emitter.cullFace(Direction.SOUTH);
emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
emitter.material(glintMaterial);
emitter.color(-1, -1, -1, -1);
Expand All @@ -196,7 +208,7 @@ public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> textureGe
emitter.pos(1, B, 0, 1);
emitter.pos(2, 1, 0, B);
emitter.pos(3, 1, 1, B);
emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
cornerSprite(emitter, whiteConcreteSprite);
emitter.material(glintMaterial);
emitter.color(-1, -1, -1, -1);
emitter.emit();
Expand All @@ -206,6 +218,7 @@ public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> textureGe
emitter.pos(1, 1, 0, B);
emitter.pos(2, 1, 0, A);
emitter.pos(3, 1, 1, A);
emitter.cullFace(Direction.EAST);
emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
emitter.material(glintMaterial);
emitter.color(-1, -1, -1, -1);
Expand All @@ -216,11 +229,21 @@ public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> textureGe
emitter.pos(1, 1, 0, A);
emitter.pos(2, B, 0, 0);
emitter.pos(3, B, 1, 0);
emitter.spriteBake(whiteConcreteSprite, MutableQuadView.BAKE_LOCK_UV);
cornerSprite(emitter, whiteConcreteSprite);
emitter.material(glintMaterial);
emitter.color(-1, -1, -1, -1);
emitter.emit();

return new SingleMeshBakedModel(builder.build(), whiteConcreteSprite);
}

private static void cornerSprite(QuadEmitter emitter, Sprite sprite) {
// Assign uvs for a corner face in such a way that the texture is not stretched, using coordinates in [0, 1].
emitter.uv(0, A, 0);
emitter.uv(1, A, 1);
emitter.uv(2, B, 1);
emitter.uv(3, B, 0);
// Map [0, 1] coordinates to sprite atlas coordinates. spriteBake assumes [0, 16] unless we pass the BAKE_NORMALIZED flag.
emitter.spriteBake(sprite, MutableQuadView.BAKE_NORMALIZED);
}
}
Loading

0 comments on commit 2034447

Please sign in to comment.