Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
33e1e9a
begin porting
sylv256 Feb 15, 2026
27e9b00
refactor!: `RenderType` in `QuadView` & update `EncodingFormat`
sylv256 Feb 16, 2026
adffa1a
refactor!: `FabricMaterialBaker` :3
sylv256 Feb 19, 2026
639fbe3
fix: everything
sylv256 Feb 19, 2026
04d1ff6
Merge remote-tracking branch 'origin/26.1' into rendering-1
sylv256 Feb 19, 2026
9d916ea
fix: some style and mixinextras being mad at me
sylv256 Feb 19, 2026
959487b
fix!: minor issues
sylv256 Feb 20, 2026
da01daf
fix: the `BakedQuadOutput` crisis of 2026
sylv256 Feb 21, 2026
fca1cb7
refactor!: remove `ItemRenderTypeGetter`
sylv256 Feb 21, 2026
35d8de6
docs: fix `ModelModifier` `ModelBaker#sprites` -> `#materials`
sylv256 Feb 21, 2026
37642a7
docs: `MutableQuadView` missing reference
sylv256 Feb 21, 2026
4470e79
fix: forgot to set `itemRenderType` to null by default
sylv256 Feb 21, 2026
df13e0a
fix: set `chunkLayer` and `itemRenderType` in `#fromBakedQuad`
sylv256 Feb 21, 2026
37ad034
fix: nullability issues in `EncodingFormat` w/ `itemRenderType`
sylv256 Feb 21, 2026
4c00b49
fix: nullability issues in `EncodingFormat` w/ `itemRenderType` pt 2
sylv256 Feb 21, 2026
6df8101
fix: recalculate transparency instead of storing in QVs
sylv256 Feb 21, 2026
5cdecb7
fix: get or find chunk layer (not item render types)
sylv256 Feb 22, 2026
440abec
Fix testmod and use getOrResolveItemRenderType in ItemRenderContext
PepperCode1 Feb 22, 2026
e723894
Fixes and improvements
PepperCode1 Feb 22, 2026
7f28d9c
Some doc fixes
PepperCode1 Feb 22, 2026
a35cea9
style: chain `QuadEmitter` calls
sylv256 Feb 22, 2026
8a71c9c
docs: prefer using this method 😇 -> use this method 😡
sylv256 Feb 23, 2026
37ebbae
docs(javadoc): omg pls build successfully thx
sylv256 Feb 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ interface Context {

/**
* The baker being used to bake this model. It can be used to
* {@linkplain ModelBaker#getModel get resolved models} and {@linkplain ModelBaker#sprites get sprites}. Note
* {@linkplain ModelBaker#getModel get resolved models} and {@linkplain ModelBaker#materials get materials}. Note
* that retrieving a model which was not previously
* {@linkplain ResolvableModel.Resolver#markDependency discovered} will log a warning and return the missing
* model.
Expand Down Expand Up @@ -182,7 +182,7 @@ interface Context {

/**
* The baker being used to bake this model. It can be used to
* {@linkplain ModelBaker#getModel get resolved models} and {@linkplain ModelBaker#sprites get sprites}. Note
* {@linkplain ModelBaker#getModel get resolved models} and {@linkplain ModelBaker#materials get materials}. Note
* that retrieving a model which was not previously
* {@linkplain ResolvableModel.Resolver#markDependency discovered} will log a warning and return the missing
* model.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,40 @@

import java.util.function.BiFunction;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.mojang.logging.LogUtils;
import org.apache.commons.lang3.mutable.MutableBoolean;
import org.apache.commons.lang3.mutable.MutableObject;
import org.slf4j.Logger;

import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.BlockModelPart;
import net.minecraft.client.renderer.block.model.BlockStateModel;
import net.minecraft.client.renderer.block.model.Material;
import net.minecraft.client.renderer.block.model.SimpleModelWrapper;
import net.minecraft.client.renderer.block.model.SingleVariant;
import net.minecraft.client.renderer.block.model.TextureSlots;
import net.minecraft.client.renderer.texture.TextureAtlas;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.model.BlockModelRotation;
import net.minecraft.client.resources.model.ModelBaker;
import net.minecraft.client.resources.model.ModelState;
import net.minecraft.client.resources.model.QuadCollection;
import net.minecraft.client.resources.model.ResolvedModel;
import net.minecraft.resources.Identifier;

import net.fabricmc.fabric.api.client.renderer.v1.mesh.QuadAtlas;
import net.fabricmc.fabric.api.client.renderer.v1.model.MeshQuadCollection;

/**
* A {@link UnbakedExtraModel} that loads a single model.
*
* @param <T> The type of the baked model, for instance {@link BlockStateModel}.
*/
public final class SimpleUnbakedExtraModel<T> implements UnbakedExtraModel<T> {
private static final Logger LOGGER = LogUtils.getLogger();

private final Identifier model;
private final BiFunction<ResolvedModel, ModelBaker, T> bake;

Expand Down Expand Up @@ -69,19 +87,68 @@ public static SimpleUnbakedExtraModel<BlockStateModel> blockStateModel(Identifie
/**
* Create a {@link SimpleUnbakedExtraModel} for a {@link BlockStateModel}.
*
* @param model The location of the model to load.
* @param settings The settings to bake the geometry with.
* @param model The location of the model to load.
* @param state The state to bake the geometry with.
* @return The unbaked extra model.
*/
public static SimpleUnbakedExtraModel<BlockStateModel> blockStateModel(Identifier model, ModelState settings) {
return new SimpleUnbakedExtraModel<>(model, (baked, baker) -> {
TextureSlots textures = baked.getTopTextureSlots();
return new SingleVariant(new SimpleModelWrapper(
baked.bakeTopGeometry(textures, baker, settings),
baked.getTopAmbientOcclusion(),
baked.resolveParticleSprite(textures, baker)
));
});
public static SimpleUnbakedExtraModel<BlockStateModel> blockStateModel(Identifier model, ModelState state) {
return new SimpleUnbakedExtraModel<>(model, (baked, baker) -> new SingleVariant(bakeResolved(baker, baked, state)));
}

// TODO: expose this as a public utility
// Mirror of SimpleModelWrapper#bake (with FRAPI's mixin) that accepts a ResolvedModel instead of an Identifier
private static BlockModelPart bakeResolved(final ModelBaker modelBakery, final ResolvedModel model, final ModelState state) {
TextureSlots textureSlots = model.getTopTextureSlots();
boolean hasAmbientOcclusion = model.getTopAmbientOcclusion();
Material.Baked particleMaterial = model.resolveParticleMaterial(textureSlots, modelBakery);
QuadCollection geometry = model.bakeTopGeometry(textureSlots, modelBakery, state);
boolean hasTranslucency = false;
Multimap<Identifier, Identifier> forbiddenSprites = null;

if (geometry instanceof MeshQuadCollection meshQuadCollection) {
MutableBoolean hasTranslucencyRef = new MutableBoolean(hasTranslucency);
MutableObject<Multimap<Identifier, Identifier>> forbiddenSpritesRef = new MutableObject<>(forbiddenSprites);

meshQuadCollection.getMesh().forEach(quad -> {
if (quad.atlas() != QuadAtlas.BLOCK) {
Multimap<Identifier, Identifier> forbiddenSprites1 = forbiddenSpritesRef.get();

if (forbiddenSprites1 == null) {
forbiddenSprites1 = HashMultimap.create();
forbiddenSpritesRef.setValue(forbiddenSprites1);
}

TextureAtlasSprite sprite = modelBakery.materials().spriteFinder(quad.atlas()).find(quad);
forbiddenSprites1.put(sprite.atlasLocation(), sprite.contents().name());
}

hasTranslucencyRef.setValue(hasTranslucencyRef.booleanValue() | quad.chunkLayer().translucent());
});

hasTranslucency = hasTranslucencyRef.booleanValue();
forbiddenSprites = forbiddenSpritesRef.get();
}

for (BakedQuad bakedQuad : geometry.getAll()) {
TextureAtlasSprite sprite = bakedQuad.spriteInfo().sprite();

if (!sprite.atlasLocation().equals(TextureAtlas.LOCATION_BLOCKS)) {
if (forbiddenSprites == null) {
forbiddenSprites = HashMultimap.create();
}

forbiddenSprites.put(sprite.atlasLocation(), sprite.contents().name());
}

hasTranslucency |= bakedQuad.spriteInfo().layer().translucent();
}

if (forbiddenSprites != null) {
LOGGER.warn("Rejecting block model {}, since it contains sprites from outside of supported atlas: {}", model.debugName(), forbiddenSprites);
return modelBakery.missingBlockModelPart();
} else {
return new SimpleModelWrapper(geometry, hasAmbientOcclusion, particleMaterial, hasTranslucency);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

import net.minecraft.client.renderer.block.model.BlockModelPart;
import net.minecraft.client.renderer.block.model.BlockStateModel;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.block.model.Material;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.util.RandomSource;
Expand Down Expand Up @@ -57,8 +57,8 @@ public List<BlockModelPart> collectParts(RandomSource random) {
}

@Override
public TextureAtlasSprite particleIcon() {
return wrapped.particleIcon();
public Material.Baked particleMaterial() {
return wrapped.particleMaterial();
}

@Override
Expand All @@ -73,7 +73,12 @@ public Object createGeometryKey(BlockAndTintGetter level, BlockPos pos, BlockSta
}

@Override
public TextureAtlasSprite particleIcon(BlockAndTintGetter level, BlockPos pos, BlockState state) {
return wrapped.particleIcon(level, pos, state);
public Material.Baked particleMaterial(BlockAndTintGetter level, BlockPos pos, BlockState state) {
return wrapped.particleMaterial(level, pos, state);
}

@Override
public boolean hasTranslucency() {
return wrapped.hasTranslucency();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

import net.minecraft.client.renderer.block.model.BlockModelPart;
import net.minecraft.client.renderer.block.model.BlockStateModel;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.block.model.Material;
import net.minecraft.client.resources.model.ModelBaker;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
Expand All @@ -46,10 +46,22 @@ public class CompositeBlockStateModelImpl implements CompositeBlockStateModel {
private final BlockStateModel[] models;
@UnmodifiableView
private final List<BlockStateModel> modelsView;
private final boolean hasTranslucency;

public CompositeBlockStateModelImpl(BlockStateModel[] models) {
this.models = models;
modelsView = Arrays.asList(models);

boolean hasTranslucency = false;

for (BlockStateModel model : this.models) {
if (model.hasTranslucency()) {
hasTranslucency = true;
break;
}
}

this.hasTranslucency = hasTranslucency;
}

public static CompositeBlockStateModelImpl of(List<BlockStateModel> models) {
Expand Down Expand Up @@ -118,13 +130,18 @@ record Key(List<Object> subkeys) {
}

@Override
public TextureAtlasSprite particleIcon() {
return models[0].particleIcon();
public Material.Baked particleMaterial() {
return models[0].particleMaterial();
}

@Override
public boolean hasTranslucency() {
return this.hasTranslucency;
}

@Override
public TextureAtlasSprite particleIcon(BlockAndTintGetter level, BlockPos pos, BlockState state) {
return models[0].particleIcon(level, pos, state);
public Material.Baked particleMaterial(BlockAndTintGetter level, BlockPos pos, BlockState state) {
return models[0].particleMaterial(level, pos, state);
}

public record Unbaked(@Unmodifiable List<BlockStateModel.Unbaked> models) implements CompositeBlockStateModel.Unbaked {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public void submit(PoseStack poseStack, SubmitNodeCollector nodeCollector, int l
poseStack.translate(-0.5F, 0.75F + aboveHead, -0.5F);
// FIXME 1.21.9
// FabricBlockModelRenderer.render(matrices.peek(), RenderLayerHelper.entityDelegate(bufferSource), model, 1, 1, 1, light, OverlayTexture.DEFAULT_UV, EmptyBlockRenderView.INSTANCE, BlockPos.ORIGIN, Blocks.AIR.getDefaultState());
nodeCollector.order(0).submitBlockModel(poseStack, Sheets.cutoutBlockSheet(), model, 1, 1, 1, light, OverlayTexture.NO_OVERLAY, 0);
nodeCollector.order(0).submitBlockModel(poseStack, Sheets.cutoutBlockSheet(), model, -1, light, OverlayTexture.NO_OVERLAY, 0);
poseStack.popPose();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.jspecify.annotations.Nullable;

import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.block.BakedQuadOutput;
import net.minecraft.client.renderer.block.BlockRenderDispatcher;
import net.minecraft.client.renderer.block.ModelBlockRenderer;
import net.minecraft.client.renderer.block.model.BlockStateModel;
Expand All @@ -43,7 +44,6 @@
import net.fabricmc.fabric.api.client.renderer.v1.render.FabricBlockRenderDispatcher;
import net.fabricmc.fabric.api.client.renderer.v1.render.FabricLayerRenderState;
import net.fabricmc.fabric.api.client.renderer.v1.render.FabricModelBlockRenderer;
import net.fabricmc.fabric.api.client.renderer.v1.render.ItemRenderTypeGetter;
import net.fabricmc.fabric.impl.client.renderer.RendererManager;

/**
Expand All @@ -55,15 +55,15 @@
* {@link SectionCompiler} in vanilla; this code is not patched automatically. Renderers must also ensure that the
* following vanilla methods support {@link BlockStateModel#emitQuads}; these methods are not patched automatically.
*
* <ul><li>{@link ModelBlockRenderer#renderModel(PoseStack.Pose, VertexConsumer, BlockStateModel, float, float, float, int, int)}
*
* <li>{@link BlockRenderDispatcher#renderBreakingTexture(BlockState, BlockPos, BlockAndTintGetter, PoseStack, VertexConsumer)}
*
* <li>{@link BlockRenderDispatcher#renderSingleBlock(BlockState, PoseStack, MultiBufferSource, int, int)}</ul>
* <ul><li>{@link BlockRenderDispatcher#renderSingleBlock(BlockState, PoseStack, MultiBufferSource, int, int)}</ul>
*
* <p>All other places in vanilla code that invoke {@link BlockStateModel#collectParts(RandomSource, List)},
* {@link BlockStateModel#collectParts(RandomSource)}, or
* {@link ModelBlockRenderer#renderModel(PoseStack.Pose, VertexConsumer, BlockStateModel, float, float, float, int, int)}
* {@link BlockStateModel#collectParts(RandomSource)},
* {@link BlockRenderDispatcher#renderBreakingTexture(BlockState, BlockPos, BlockAndTintGetter, PoseStack, BakedQuadOutput)},
* {@link BlockRenderDispatcher#renderBatched(BlockState, BlockPos, BlockAndTintGetter, PoseStack, BakedQuadOutput, boolean, List)},
* {@link ModelBlockRenderer#tesselateBlock(BlockAndTintGetter, List, BlockState, BlockPos, PoseStack, BakedQuadOutput, boolean, int)},
* or
* {@link ModelBlockRenderer#renderModel(PoseStack.Pose, BakedQuadOutput, BlockStateModel, int, int, int)}
* are, where appropriate, patched automatically to invoke the corresponding method above or the corresponding method in
* {@link FabricModelBlockRenderer} or {@link FabricBlockRenderDispatcher}.
*/
Expand Down Expand Up @@ -96,26 +96,26 @@ static Renderer get() {
void tesselateBlock(ModelBlockRenderer blockRenderer, BlockAndTintGetter level, BlockStateModel model, BlockState state, BlockPos pos, PoseStack poseStack, BlockMultiBufferSource bufferSource, @Nullable Predicate<ChunkSectionLayer> layerFilter, boolean cull, long seed, int overlay);

/**
* @see FabricModelBlockRenderer#renderModel(PoseStack.Pose, BlockMultiBufferSource, Predicate, BlockStateModel, float, float, float, int, int, BlockAndTintGetter, BlockPos, BlockState)
* @see FabricModelBlockRenderer#renderModel(PoseStack.Pose, BlockMultiBufferSource, Predicate, BlockStateModel, int, int, int, BlockAndTintGetter, BlockPos, BlockState)
*/
@ApiStatus.OverrideOnly
void renderModel(PoseStack.Pose pose, BlockMultiBufferSource bufferSource, @Nullable Predicate<ChunkSectionLayer> layerFilter, BlockStateModel model, float red, float green, float blue, int light, int overlay, BlockAndTintGetter level, BlockPos pos, BlockState state);
void renderModel(PoseStack.Pose pose, BlockMultiBufferSource bufferSource, @Nullable Predicate<ChunkSectionLayer> layerFilter, BlockStateModel model, int tintColor, int light, int overlay, BlockAndTintGetter level, BlockPos pos, BlockState state);

/**
* @see FabricBlockRenderDispatcher#renderSingleBlock(BlockState, PoseStack, MultiBufferSource, Predicate, int, int, BlockAndTintGetter, BlockPos)
* @see FabricBlockRenderDispatcher#renderBreakingTexture(BlockState, BlockPos, BlockAndTintGetter, PoseStack, VertexConsumer)
*/
@ApiStatus.OverrideOnly
void renderSingleBlock(BlockRenderDispatcher renderDispatcher, BlockState state, PoseStack poseStack, MultiBufferSource bufferSource, @Nullable Predicate<ChunkSectionLayer> layerFilter, int light, int overlay, BlockAndTintGetter level, BlockPos pos);
void renderBreakingTexture(BlockRenderDispatcher renderDispatcher, BlockState state, BlockPos pos, BlockAndTintGetter level, PoseStack poseStack, VertexConsumer vertexConsumer);

/**
* @see FabricLayerRenderState#emitter()
* @see FabricBlockRenderDispatcher#renderSingleBlock(BlockState, PoseStack, MultiBufferSource, Predicate, int, int, BlockAndTintGetter, BlockPos)
*/
@ApiStatus.OverrideOnly
QuadEmitter getLayerRenderStateEmitter(ItemStackRenderState.LayerRenderState layer);
void renderSingleBlock(BlockRenderDispatcher renderDispatcher, BlockState state, PoseStack poseStack, MultiBufferSource bufferSource, @Nullable Predicate<ChunkSectionLayer> layerFilter, int light, int overlay, BlockAndTintGetter level, BlockPos pos);

/**
* @see FabricLayerRenderState#setRenderTypeGetter(ItemRenderTypeGetter)
* @see FabricLayerRenderState#emitter()
*/
@ApiStatus.OverrideOnly
void setLayerRenderTypeGetter(ItemStackRenderState.LayerRenderState layer, ItemRenderTypeGetter renderTypeGetter);
QuadEmitter getLayerRenderStateEmitter(ItemStackRenderState.LayerRenderState layer);
}
Loading
Loading