Add ShadeMode to FRAPI (#3937)

* Add ShadeMode to the API

- Update material documentation
- Use ShadeMode in VanillaModelEncoder

* Add ShadeMode support to Indigo

- Rewrite header packing constants in EncodingFormat to match material packing constants
- Pass buffer Function to TerrainRenderContext instead of allocators and buffer map
- Restore functionality of ChunkRenderInfo#release
- Set captured terrain context to null after releasing it
- Bump mixin compatibility level to Java 21
- Remove unused AWs

* Add test for ShadeMode

- Fix RiverstoneUnbakedModel not calling setParents on models that it will bake

* Clarify documentation of inverted material properties
This commit is contained in:
PepperCode1 2024-08-04 06:52:02 -06:00 committed by GitHub
parent 6c1df360ce
commit c705a49cc5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 288 additions and 217 deletions

View file

@ -17,13 +17,15 @@
package net.fabricmc.fabric.api.renderer.v1.material; package net.fabricmc.fabric.api.renderer.v1.material;
import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.RenderLayers;
/** /**
* Defines how sprite pixels will be blended with the scene. * Controls how sprite pixels will be blended with the scene.
*/ */
public enum BlendMode { public enum BlendMode {
/** /**
* Emulate blending behavior of {@code BlockRenderLayer} associated with the block. * Emulate blending behavior of the {@link RenderLayer} associated with the block state through
* {@link RenderLayers}.
*/ */
DEFAULT(null), DEFAULT(null),

View file

@ -17,6 +17,7 @@
package net.fabricmc.fabric.api.renderer.v1.material; package net.fabricmc.fabric.api.renderer.v1.material;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BakedModel;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
@ -34,33 +35,42 @@ import net.fabricmc.fabric.api.util.TriState;
*/ */
public interface MaterialFinder extends MaterialView { public interface MaterialFinder extends MaterialView {
/** /**
* Defines how sprite pixels will be blended with the scene. * Controls how sprite pixels should be blended with the scene.
* *
* <p>See {@link BlendMode} for more information. * <p>The default value is {@link BlendMode#DEFAULT}.
*
* @see BlendMode
*/ */
MaterialFinder blendMode(BlendMode blendMode); MaterialFinder blendMode(BlendMode blendMode);
/** /**
* Vertex color(s) will be modified for quad color index unless disabled. * Controls whether vertex colors should be modified for quad coloring. This property
* is inverted, so a value of {@code false} means that quad coloring will be applied.
*
* <p>The default value is {@code false}.
*/ */
MaterialFinder disableColorIndex(boolean disable); MaterialFinder disableColorIndex(boolean disable);
/** /**
* When true, sprite texture and color will be rendered at full brightness. * When true, sprite texture and color will be rendered at full brightness.
* Lightmap values provided via {@link QuadEmitter#lightmap(int)} will be ignored. * Lightmap values provided via {@link QuadEmitter#lightmap(int)} will be ignored.
* False by default
* *
* <p>This is the preferred method for emissive lighting effects. Some renderers * <p>This is the preferred method for emissive lighting effects. Some renderers
* with advanced lighting models may not use block lightmaps and this method will * with advanced lighting pipelines may not use block lightmaps and this method will
* allow per-sprite emissive lighting in future extensions that support overlay sprites. * allow per-sprite emissive lighting in future extensions that support overlay sprites.
* *
* <p>Note that color will still be modified by diffuse shading and ambient occlusion, * <p>Note that color will still be modified by diffuse shading and ambient occlusion,
* unless disabled via {@link #disableDiffuse(boolean)} and {@link #ambientOcclusion(TriState)}. * unless disabled via {@link #disableDiffuse(boolean)} and {@link #ambientOcclusion(TriState)}.
*
* <p>The default value is {@code false}.
*/ */
MaterialFinder emissive(boolean isEmissive); MaterialFinder emissive(boolean isEmissive);
/** /**
* Vertex color(s) will be modified for diffuse shading unless disabled. * Controls whether vertex colors should be modified for diffuse shading. This property
* is inverted, so a value of {@code false} means that diffuse shading will be applied.
*
* <p>The default value is {@code false}.
* *
* <p>This property is guaranteed to be respected in block contexts. Some renderers may also respect it in item * <p>This property is guaranteed to be respected in block contexts. Some renderers may also respect it in item
* contexts, but this is not guaranteed. * contexts, but this is not guaranteed.
@ -68,11 +78,15 @@ public interface MaterialFinder extends MaterialView {
MaterialFinder disableDiffuse(boolean disable); MaterialFinder disableDiffuse(boolean disable);
/** /**
* Controls whether vertex color(s) will be modified for ambient occlusion. * Controls whether vertex colors should be modified for ambient occlusion.
* *
* <p>By default, ambient occlusion will be used if {@link BakedModel#useAmbientOcclusion() the model uses ambient occlusion} * <p>If set to {@link TriState#DEFAULT}, ambient occlusion will be used if
* and the block state has {@link BlockState#getLuminance() a luminance} of 0. * {@linkplain BakedModel#useAmbientOcclusion() the model uses ambient occlusion} and the block state has
* Set to {@link TriState#TRUE} or {@link TriState#FALSE} to override this behavior. * {@linkplain BlockState#getLuminance() a luminance} of 0. Set to {@link TriState#TRUE} or {@link TriState#FALSE}
* to override this behavior. {@link TriState#TRUE} will not have an effect if
* {@linkplain MinecraftClient#isAmbientOcclusionEnabled() ambient occlusion is disabled globally}.
*
* <p>The default value is {@link TriState#DEFAULT}.
* *
* <p>This property is respected only in block contexts. It will not have an effect in other contexts. * <p>This property is respected only in block contexts. It will not have an effect in other contexts.
*/ */
@ -81,14 +95,33 @@ public interface MaterialFinder extends MaterialView {
/** /**
* Controls whether glint should be applied. * Controls whether glint should be applied.
* *
* <p>By default, glint will be applied in item contexts if {@link ItemStack#hasGlint() the item stack has glint}. * <p>If set to {@link TriState#DEFAULT}, glint will be applied in item contexts if
* Set to {@link TriState#TRUE} or {@link TriState#FALSE} to override this behavior. * {@linkplain ItemStack#hasGlint() the item stack has glint}. Set to {@link TriState#TRUE} or
* {@link TriState#FALSE} to override this behavior.
*
* <p>The default value is {@link TriState#DEFAULT}.
* *
* <p>This property is guaranteed to be respected in item contexts. Some renderers may also respect it in block * <p>This property is guaranteed to be respected in item contexts. Some renderers may also respect it in block
* contexts, but this is not guaranteed. * contexts, but this is not guaranteed.
*/ */
MaterialFinder glint(TriState mode); MaterialFinder glint(TriState mode);
/**
* A hint to the renderer about how the quad is intended to be shaded, for example through ambient occlusion and
* diffuse shading. The renderer is free to ignore this hint.
*
* <p>The default value is {@link ShadeMode#ENHANCED}.
*
* <p>This property is respected only in block contexts. It will not have an effect in other contexts.
*
* @see ShadeMode
*
* @apiNote The default implementation will be removed in the next breaking release.
*/
default MaterialFinder shadeMode(ShadeMode mode) {
return this;
}
/** /**
* Copies all properties from the given {@link MaterialView} to this material finder. * Copies all properties from the given {@link MaterialView} to this material finder.
*/ */

View file

@ -54,4 +54,13 @@ public interface MaterialView {
* @see MaterialFinder#glint(TriState) * @see MaterialFinder#glint(TriState)
*/ */
TriState glint(); TriState glint();
/**
* @see MaterialFinder#shadeMode(ShadeMode)
*
* @apiNote The default implementation will be removed in the next breaking release.
*/
default ShadeMode shadeMode() {
return ShadeMode.ENHANCED;
}
} }

View file

@ -0,0 +1,36 @@
/*
* 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.api.renderer.v1.material;
/**
* A hint to the renderer about how the quad is intended to be shaded, for example through ambient occlusion and
* diffuse shading. The renderer is free to ignore this hint.
*/
public enum ShadeMode {
/**
* Conveys the intent that shading should be generally consistent, lack edge cases, and produce visually pleasing
* results, even for quads that are not used by vanilla or are not possible to create through resource packs in
* vanilla.
*/
ENHANCED,
/**
* Conveys the intent that shading should mimic vanilla results, potentially to preserve certain visuals produced
* by resource packs that modify models.
*/
VANILLA;
}

View file

@ -94,7 +94,7 @@ public interface FabricBakedModel {
* @param context Accepts model output. * @param context Accepts model output.
*/ */
default void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) { default void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
VanillaModelEncoder.emitBlockQuads((BakedModel) this, state, randomSupplier, context, context.getEmitter()); VanillaModelEncoder.emitBlockQuads((BakedModel) this, state, randomSupplier, context);
} }
/** /**

View file

@ -20,6 +20,7 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import org.jetbrains.annotations.Nullable;
import org.joml.Vector3f; import org.joml.Vector3f;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
@ -46,7 +47,7 @@ public final class ModelHelper {
* Null is returned as {@link #NULL_FACE_ID}. * Null is returned as {@link #NULL_FACE_ID}.
* Use {@link #faceFromIndex(int)} to retrieve encoded face. * Use {@link #faceFromIndex(int)} to retrieve encoded face.
*/ */
public static int toFaceIndex(Direction face) { public static int toFaceIndex(@Nullable Direction face) {
return face == null ? NULL_FACE_ID : face.getId(); return face == null ? NULL_FACE_ID : face.getId();
} }
@ -57,6 +58,7 @@ public final class ModelHelper {
* optionally including the null face. (Use &lt; or &lt;= {@link #NULL_FACE_ID} * optionally including the null face. (Use &lt; or &lt;= {@link #NULL_FACE_ID}
* to exclude or include the null value, respectively.) * to exclude or include the null value, respectively.)
*/ */
@Nullable
public static Direction faceFromIndex(int faceIndex) { public static Direction faceFromIndex(int faceIndex) {
return FACES[faceIndex]; return FACES[faceIndex];
} }

View file

@ -30,6 +30,7 @@ import net.minecraft.util.math.random.Random;
import net.fabricmc.fabric.api.renderer.v1.Renderer; import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.RendererAccess; import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial; import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
import net.fabricmc.fabric.api.renderer.v1.material.ShadeMode;
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.ModelHelper; 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;
@ -41,12 +42,12 @@ import net.fabricmc.fabric.api.util.TriState;
*/ */
public class VanillaModelEncoder { public class VanillaModelEncoder {
private static final Renderer RENDERER = RendererAccess.INSTANCE.getRenderer(); private static final Renderer RENDERER = RendererAccess.INSTANCE.getRenderer();
private static final RenderMaterial MATERIAL_STANDARD = RENDERER.materialFinder().find(); private static final RenderMaterial STANDARD_MATERIAL = RENDERER.materialFinder().shadeMode(ShadeMode.VANILLA).find();
private static final RenderMaterial MATERIAL_NO_AO = RENDERER.materialFinder().ambientOcclusion(TriState.FALSE).find(); private static final RenderMaterial NO_AO_MATERIAL = RENDERER.materialFinder().shadeMode(ShadeMode.VANILLA).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) {
public static void emitBlockQuads(BakedModel model, @Nullable BlockState state, Supplier<Random> randomSupplier, RenderContext context, QuadEmitter emitter) { QuadEmitter emitter = context.getEmitter();
final RenderMaterial defaultMaterial = model.useAmbientOcclusion() ? MATERIAL_STANDARD : MATERIAL_NO_AO; final RenderMaterial defaultMaterial = model.useAmbientOcclusion() ? STANDARD_MATERIAL : NO_AO_MATERIAL;
for (int i = 0; i <= ModelHelper.NULL_FACE_ID; i++) { for (int i = 0; i <= ModelHelper.NULL_FACE_ID; i++) {
final Direction cullFace = ModelHelper.faceFromIndex(i); final Direction cullFace = ModelHelper.faceFromIndex(i);
@ -77,7 +78,7 @@ public class VanillaModelEncoder {
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);
emitter.fromVanilla(q, MATERIAL_STANDARD, cullFace); emitter.fromVanilla(q, STANDARD_MATERIAL, cullFace);
emitter.emit(); emitter.emit();
} }
} }

View file

@ -1,7 +1,7 @@
{ {
"required": true, "required": true,
"package": "net.fabricmc.fabric.mixin.renderer", "package": "net.fabricmc.fabric.mixin.renderer",
"compatibilityLevel": "JAVA_17", "compatibilityLevel": "JAVA_21",
"client": [ "client": [
"client.BakedModelMixin", "client.BakedModelMixin",
"client.MultipartBakedModelMixin", "client.MultipartBakedModelMixin",

View file

@ -0,0 +1,36 @@
/*
* 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.test.renderer;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.state.StateManager;
import net.minecraft.state.property.BooleanProperty;
public class OctagonalColumnBlock extends Block {
public static final BooleanProperty VANILLA_SHADE_MODE = BooleanProperty.of("vanilla_shade_mode");
public OctagonalColumnBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(VANILLA_SHADE_MODE, false));
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
builder.add(VANILLA_SHADE_MODE);
}
}

View file

@ -32,7 +32,7 @@ public final class Registration {
public static final FrameBlock FRAME_MULTIPART_BLOCK = register("frame_multipart", new FrameBlock(AbstractBlock.Settings.copy(Blocks.IRON_BLOCK).nonOpaque())); public static final FrameBlock FRAME_MULTIPART_BLOCK = register("frame_multipart", new FrameBlock(AbstractBlock.Settings.copy(Blocks.IRON_BLOCK).nonOpaque()));
public static final FrameBlock FRAME_VARIANT_BLOCK = register("frame_variant", new FrameBlock(AbstractBlock.Settings.copy(Blocks.IRON_BLOCK).nonOpaque())); public static final FrameBlock FRAME_VARIANT_BLOCK = register("frame_variant", new FrameBlock(AbstractBlock.Settings.copy(Blocks.IRON_BLOCK).nonOpaque()));
public static final Block PILLAR_BLOCK = register("pillar", new Block(AbstractBlock.Settings.create())); public static final Block PILLAR_BLOCK = register("pillar", new Block(AbstractBlock.Settings.create()));
public static final Block OCTAGONAL_COLUMN_BLOCK = register("octagonal_column", new Block(AbstractBlock.Settings.create().nonOpaque().strength(1.8F))); public static final Block OCTAGONAL_COLUMN_BLOCK = register("octagonal_column", new OctagonalColumnBlock(AbstractBlock.Settings.create().nonOpaque().strength(1.8F)));
public static final Block RIVERSTONE_BLOCK = register("riverstone", new Block(AbstractBlock.Settings.copy(Blocks.STONE))); public static final Block RIVERSTONE_BLOCK = register("riverstone", new Block(AbstractBlock.Settings.copy(Blocks.STONE)));
public static final FrameBlock[] FRAME_BLOCKS = new FrameBlock[] { public static final FrameBlock[] FRAME_BLOCKS = new FrameBlock[] {

View file

@ -24,6 +24,7 @@ import net.minecraft.client.render.model.UnbakedModel;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.fabricmc.fabric.api.client.model.loading.v1.ModelResolver; import net.fabricmc.fabric.api.client.model.loading.v1.ModelResolver;
import net.fabricmc.fabric.api.renderer.v1.material.ShadeMode;
import net.fabricmc.fabric.test.renderer.RendererTest; import net.fabricmc.fabric.test.renderer.RendererTest;
public class ModelResolverImpl implements ModelResolver { public class ModelResolverImpl implements ModelResolver {
@ -44,6 +45,10 @@ public class ModelResolverImpl implements ModelResolver {
RendererTest.id("item/octagonal_column") RendererTest.id("item/octagonal_column")
); );
private static final Set<Identifier> OCTAGONAL_COLUMN_VANILLA_MODEL_LOCATIONS = Set.of(
RendererTest.id("block/octagonal_column_vanilla")
);
private static final Set<Identifier> RIVERSTONE_MODEL_LOCATIONS = Set.of( private static final Set<Identifier> RIVERSTONE_MODEL_LOCATIONS = Set.of(
RendererTest.id("block/riverstone"), RendererTest.id("block/riverstone"),
RendererTest.id("item/riverstone") RendererTest.id("item/riverstone")
@ -63,7 +68,11 @@ public class ModelResolverImpl implements ModelResolver {
} }
if (OCTAGONAL_COLUMN_MODEL_LOCATIONS.contains(id)) { if (OCTAGONAL_COLUMN_MODEL_LOCATIONS.contains(id)) {
return new OctagonalColumnUnbakedModel(); return new OctagonalColumnUnbakedModel(ShadeMode.ENHANCED);
}
if (OCTAGONAL_COLUMN_VANILLA_MODEL_LOCATIONS.contains(id)) {
return new OctagonalColumnUnbakedModel(ShadeMode.VANILLA);
} }
if (RIVERSTONE_MODEL_LOCATIONS.contains(id)) { if (RIVERSTONE_MODEL_LOCATIONS.contains(id)) {

View file

@ -36,6 +36,7 @@ import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.RendererAccess; import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
import net.fabricmc.fabric.api.renderer.v1.material.MaterialFinder; import net.fabricmc.fabric.api.renderer.v1.material.MaterialFinder;
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial; import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
import net.fabricmc.fabric.api.renderer.v1.material.ShadeMode;
import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder; import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder;
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.QuadEmitter; import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
@ -49,6 +50,12 @@ public class OctagonalColumnUnbakedModel implements UnbakedModel {
private static final float A = (float) (1 - Math.sqrt(2) / 2); private static final float A = (float) (1 - Math.sqrt(2) / 2);
private static final float B = (float) (Math.sqrt(2) / 2); private static final float B = (float) (Math.sqrt(2) / 2);
private final ShadeMode shadeMode;
public OctagonalColumnUnbakedModel(ShadeMode shadeMode) {
this.shadeMode = shadeMode;
}
@Override @Override
public Collection<Identifier> getModelDependencies() { public Collection<Identifier> getModelDependencies() {
return Collections.emptySet(); return Collections.emptySet();
@ -58,8 +65,8 @@ public class OctagonalColumnUnbakedModel implements UnbakedModel {
public void setParents(Function<Identifier, UnbakedModel> modelLoader) { public void setParents(Function<Identifier, UnbakedModel> modelLoader) {
} }
@Nullable
@Override @Override
@Nullable
public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> textureGetter, ModelBakeSettings rotationContainer) { public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> textureGetter, ModelBakeSettings rotationContainer) {
if (!RendererAccess.INSTANCE.hasRenderer()) { if (!RendererAccess.INSTANCE.hasRenderer()) {
return null; return null;
@ -69,7 +76,7 @@ public class OctagonalColumnUnbakedModel implements UnbakedModel {
Renderer renderer = RendererAccess.INSTANCE.getRenderer(); Renderer renderer = RendererAccess.INSTANCE.getRenderer();
MaterialFinder finder = renderer.materialFinder(); MaterialFinder finder = renderer.materialFinder();
RenderMaterial glintMaterial = finder.glint(TriState.TRUE).find(); RenderMaterial glintMaterial = finder.glint(TriState.TRUE).shadeMode(shadeMode).find();
MeshBuilder builder = renderer.meshBuilder(); MeshBuilder builder = renderer.meshBuilder();
QuadEmitter emitter = builder.getEmitter(); QuadEmitter emitter = builder.getEmitter();

View file

@ -41,6 +41,8 @@ public class RiverstoneUnbakedModel implements UnbakedModel {
@Override @Override
public void setParents(Function<Identifier, UnbakedModel> modelLoader) { public void setParents(Function<Identifier, UnbakedModel> modelLoader) {
modelLoader.apply(STONE_MODEL_ID).setParents(modelLoader);
modelLoader.apply(GOLD_BLOCK_MODEL_ID).setParents(modelLoader);
} }
@Nullable @Nullable

View file

@ -1,5 +1,6 @@
{ {
"variants": { "variants": {
"": { "model": "fabric-renderer-api-v1-testmod:block/octagonal_column" } "vanilla_shade_mode=false": { "model": "fabric-renderer-api-v1-testmod:block/octagonal_column" },
"vanilla_shade_mode=true": { "model": "fabric-renderer-api-v1-testmod:block/octagonal_column_vanilla" }
} }
} }

View file

@ -25,6 +25,7 @@ import net.fabricmc.fabric.api.renderer.v1.material.MaterialFinder;
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial; import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder; import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder;
import net.fabricmc.fabric.impl.client.indigo.renderer.material.MaterialFinderImpl; import net.fabricmc.fabric.impl.client.indigo.renderer.material.MaterialFinderImpl;
import net.fabricmc.fabric.impl.client.indigo.renderer.material.RenderMaterialImpl;
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MeshBuilderImpl; import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MeshBuilderImpl;
/** /**
@ -64,7 +65,7 @@ public class IndigoRenderer implements Renderer {
if (materialMap.containsKey(id)) return false; if (materialMap.containsKey(id)) return false;
// cast to prevent acceptance of impostor implementations // cast to prevent acceptance of impostor implementations
materialMap.put(id, material); materialMap.put(id, (RenderMaterialImpl) material);
return true; return true;
} }
} }

View file

@ -45,7 +45,6 @@ import net.minecraft.world.LightType;
import net.fabricmc.fabric.impl.client.indigo.Indigo; import net.fabricmc.fabric.impl.client.indigo.Indigo;
import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoFace.WeightFunction; import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoFace.WeightFunction;
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat; import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat;
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MutableQuadViewImpl;
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.QuadViewImpl; import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.QuadViewImpl;
import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderInfo; import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderInfo;
@ -105,41 +104,23 @@ public abstract class AoCalculator {
completionFlags = 0; completionFlags = 0;
} }
public void compute(MutableQuadViewImpl quad, boolean isVanilla) { public void compute(QuadViewImpl quad, boolean vanillaShade) {
final AoConfig config = Indigo.AMBIENT_OCCLUSION_MODE; final AoConfig config = Indigo.AMBIENT_OCCLUSION_MODE;
final boolean shouldCompare;
switch (config) { switch (config) {
case VANILLA: case VANILLA -> calcVanilla(quad);
calcVanilla(quad); case EMULATE -> calcFastVanilla(quad);
case HYBRID -> {
// no point in comparing vanilla with itself if (vanillaShade) {
shouldCompare = false;
break;
case EMULATE:
calcFastVanilla(quad);
shouldCompare = Indigo.DEBUG_COMPARE_LIGHTING && isVanilla;
break;
case HYBRID:
default:
if (isVanilla) {
shouldCompare = Indigo.DEBUG_COMPARE_LIGHTING;
calcFastVanilla(quad); calcFastVanilla(quad);
} else { } else {
shouldCompare = false;
calcEnhanced(quad); calcEnhanced(quad);
} }
}
break; case ENHANCED -> calcEnhanced(quad);
case ENHANCED:
shouldCompare = false;
calcEnhanced(quad);
} }
if (shouldCompare) { if (Indigo.DEBUG_COMPARE_LIGHTING && vanillaShade && (config == AoConfig.EMULATE || config == AoConfig.HYBRID)) {
float[] vanillaAo = new float[4]; float[] vanillaAo = new float[4];
int[] vanillaLight = new int[4]; int[] vanillaLight = new int[4];
calcVanilla(quad, vanillaAo, vanillaLight); calcVanilla(quad, vanillaAo, vanillaLight);
@ -158,7 +139,7 @@ public abstract class AoCalculator {
} }
} }
private void calcVanilla(MutableQuadViewImpl quad) { private void calcVanilla(QuadViewImpl quad) {
calcVanilla(quad, ao, light); calcVanilla(quad, ao, light);
} }
@ -169,7 +150,7 @@ public abstract class AoCalculator {
private final BitSet vanillaAoControlBits = new BitSet(3); private final BitSet vanillaAoControlBits = new BitSet(3);
private final int[] vertexData = new int[EncodingFormat.QUAD_STRIDE]; private final int[] vertexData = new int[EncodingFormat.QUAD_STRIDE];
private void calcVanilla(MutableQuadViewImpl quad, float[] aoDest, int[] lightDest) { private void calcVanilla(QuadViewImpl quad, float[] aoDest, int[] lightDest) {
vanillaAoControlBits.clear(); vanillaAoControlBits.clear();
final Direction lightFace = quad.lightFace(); final Direction lightFace = quad.lightFace();
quad.toVanilla(vertexData, 0); quad.toVanilla(vertexData, 0);
@ -181,7 +162,7 @@ public abstract class AoCalculator {
System.arraycopy(vanillaCalc.light, 0, lightDest, 0, 4); System.arraycopy(vanillaCalc.light, 0, lightDest, 0, 4);
} }
private void calcFastVanilla(MutableQuadViewImpl quad) { private void calcFastVanilla(QuadViewImpl quad) {
int flags = quad.geometryFlags(); int flags = quad.geometryFlags();
// force to block face if shape is full cube - matches vanilla logic // force to block face if shape is full cube - matches vanilla logic
@ -196,7 +177,7 @@ public abstract class AoCalculator {
} }
} }
private void calcEnhanced(MutableQuadViewImpl quad) { private void calcEnhanced(QuadViewImpl quad) {
switch (quad.geometryFlags()) { switch (quad.geometryFlags()) {
case AXIS_ALIGNED_FLAG | CUBIC_FLAG | LIGHT_FACE_FLAG: case AXIS_ALIGNED_FLAG | CUBIC_FLAG | LIGHT_FACE_FLAG:
case AXIS_ALIGNED_FLAG | LIGHT_FACE_FLAG: case AXIS_ALIGNED_FLAG | LIGHT_FACE_FLAG:
@ -271,7 +252,7 @@ public abstract class AoCalculator {
/** used exclusively in irregular face to avoid new heap allocations each call. */ /** used exclusively in irregular face to avoid new heap allocations each call. */
private final Vector3f vertexNormal = new Vector3f(); private final Vector3f vertexNormal = new Vector3f();
private void irregularFace(MutableQuadViewImpl quad, boolean shade) { private void irregularFace(QuadViewImpl quad, boolean shade) {
final Vector3f faceNorm = quad.faceNormal(); final Vector3f faceNorm = quad.faceNormal();
Vector3f normal; Vector3f normal;
final float[] w = this.w; final float[] w = this.w;

View file

@ -22,6 +22,7 @@ import net.fabricmc.fabric.api.renderer.v1.material.BlendMode;
import net.fabricmc.fabric.api.renderer.v1.material.MaterialFinder; import net.fabricmc.fabric.api.renderer.v1.material.MaterialFinder;
import net.fabricmc.fabric.api.renderer.v1.material.MaterialView; import net.fabricmc.fabric.api.renderer.v1.material.MaterialView;
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial; import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
import net.fabricmc.fabric.api.renderer.v1.material.ShadeMode;
import net.fabricmc.fabric.api.util.TriState; import net.fabricmc.fabric.api.util.TriState;
public class MaterialFinderImpl extends MaterialViewImpl implements MaterialFinder { public class MaterialFinderImpl extends MaterialViewImpl implements MaterialFinder {
@ -84,6 +85,14 @@ public class MaterialFinderImpl extends MaterialViewImpl implements MaterialFind
return this; return this;
} }
@Override
public MaterialFinder shadeMode(ShadeMode mode) {
Objects.requireNonNull(mode, "ShadeMode may not be null");
bits = (bits & ~SHADE_MODE_MASK) | (mode.ordinal() << SHADE_MODE_BIT_OFFSET);
return this;
}
@Override @Override
public MaterialFinder copyFrom(MaterialView material) { public MaterialFinder copyFrom(MaterialView material) {
bits = ((MaterialViewImpl) material).bits; bits = ((MaterialViewImpl) material).bits;

View file

@ -16,10 +16,13 @@
package net.fabricmc.fabric.impl.client.indigo.renderer.material; package net.fabricmc.fabric.impl.client.indigo.renderer.material;
import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat.bitMask;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
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.material.MaterialView; import net.fabricmc.fabric.api.renderer.v1.material.MaterialView;
import net.fabricmc.fabric.api.renderer.v1.material.ShadeMode;
import net.fabricmc.fabric.api.util.TriState; import net.fabricmc.fabric.api.util.TriState;
/** /**
@ -33,6 +36,8 @@ public class MaterialViewImpl implements MaterialView {
private static final int BLEND_MODE_COUNT = BLEND_MODES.length; private static final int BLEND_MODE_COUNT = BLEND_MODES.length;
private static final TriState[] TRI_STATES = TriState.values(); private static final TriState[] TRI_STATES = TriState.values();
private static final int TRI_STATE_COUNT = TRI_STATES.length; private static final int TRI_STATE_COUNT = TRI_STATES.length;
private static final ShadeMode[] SHADE_MODES = ShadeMode.values();
private static final int SHADE_MODE_COUNT = SHADE_MODES.length;
protected static final int BLEND_MODE_BIT_LENGTH = MathHelper.ceilLog2(BLEND_MODE_COUNT); protected static final int BLEND_MODE_BIT_LENGTH = MathHelper.ceilLog2(BLEND_MODE_COUNT);
protected static final int COLOR_DISABLE_BIT_LENGTH = 1; protected static final int COLOR_DISABLE_BIT_LENGTH = 1;
@ -40,6 +45,7 @@ public class MaterialViewImpl implements MaterialView {
protected static final int DIFFUSE_BIT_LENGTH = 1; protected static final int DIFFUSE_BIT_LENGTH = 1;
protected static final int AO_BIT_LENGTH = MathHelper.ceilLog2(TRI_STATE_COUNT); protected static final int AO_BIT_LENGTH = MathHelper.ceilLog2(TRI_STATE_COUNT);
protected static final int GLINT_BIT_LENGTH = MathHelper.ceilLog2(TRI_STATE_COUNT); protected static final int GLINT_BIT_LENGTH = MathHelper.ceilLog2(TRI_STATE_COUNT);
protected static final int SHADE_MODE_BIT_LENGTH = MathHelper.ceilLog2(SHADE_MODE_COUNT);
protected static final int BLEND_MODE_BIT_OFFSET = 0; protected static final int BLEND_MODE_BIT_OFFSET = 0;
protected static final int COLOR_DISABLE_BIT_OFFSET = BLEND_MODE_BIT_OFFSET + BLEND_MODE_BIT_LENGTH; protected static final int COLOR_DISABLE_BIT_OFFSET = BLEND_MODE_BIT_OFFSET + BLEND_MODE_BIT_LENGTH;
@ -47,7 +53,8 @@ public class MaterialViewImpl implements MaterialView {
protected static final int DIFFUSE_BIT_OFFSET = EMISSIVE_BIT_OFFSET + EMISSIVE_BIT_LENGTH; protected static final int DIFFUSE_BIT_OFFSET = EMISSIVE_BIT_OFFSET + EMISSIVE_BIT_LENGTH;
protected static final int AO_BIT_OFFSET = DIFFUSE_BIT_OFFSET + DIFFUSE_BIT_LENGTH; protected static final int AO_BIT_OFFSET = DIFFUSE_BIT_OFFSET + DIFFUSE_BIT_LENGTH;
protected static final int GLINT_BIT_OFFSET = AO_BIT_OFFSET + AO_BIT_LENGTH; protected static final int GLINT_BIT_OFFSET = AO_BIT_OFFSET + AO_BIT_LENGTH;
protected static final int TOTAL_BIT_LENGTH = GLINT_BIT_OFFSET + GLINT_BIT_LENGTH; protected static final int SHADE_MODE_BIT_OFFSET = GLINT_BIT_OFFSET + GLINT_BIT_LENGTH;
public static final int TOTAL_BIT_LENGTH = SHADE_MODE_BIT_OFFSET + SHADE_MODE_BIT_LENGTH;
protected static final int BLEND_MODE_MASK = bitMask(BLEND_MODE_BIT_LENGTH, BLEND_MODE_BIT_OFFSET); protected static final int BLEND_MODE_MASK = bitMask(BLEND_MODE_BIT_LENGTH, BLEND_MODE_BIT_OFFSET);
protected static final int COLOR_DISABLE_FLAG = bitMask(COLOR_DISABLE_BIT_LENGTH, COLOR_DISABLE_BIT_OFFSET); protected static final int COLOR_DISABLE_FLAG = bitMask(COLOR_DISABLE_BIT_LENGTH, COLOR_DISABLE_BIT_OFFSET);
@ -55,19 +62,18 @@ public class MaterialViewImpl implements MaterialView {
protected static final int DIFFUSE_FLAG = bitMask(DIFFUSE_BIT_LENGTH, DIFFUSE_BIT_OFFSET); protected static final int DIFFUSE_FLAG = bitMask(DIFFUSE_BIT_LENGTH, DIFFUSE_BIT_OFFSET);
protected static final int AO_MASK = bitMask(AO_BIT_LENGTH, AO_BIT_OFFSET); protected static final int AO_MASK = bitMask(AO_BIT_LENGTH, AO_BIT_OFFSET);
protected static final int GLINT_MASK = bitMask(GLINT_BIT_LENGTH, GLINT_BIT_OFFSET); protected static final int GLINT_MASK = bitMask(GLINT_BIT_LENGTH, GLINT_BIT_OFFSET);
protected static final int SHADE_MODE_MASK = bitMask(SHADE_MODE_BIT_LENGTH, SHADE_MODE_BIT_OFFSET);
protected static int bitMask(int bitLength, int bitOffset) {
return ((1 << bitLength) - 1) << bitOffset;
}
protected static boolean areBitsValid(int bits) { protected static boolean areBitsValid(int bits) {
int blendMode = (bits & BLEND_MODE_MASK) >>> BLEND_MODE_BIT_OFFSET; int blendMode = (bits & BLEND_MODE_MASK) >>> BLEND_MODE_BIT_OFFSET;
int ao = (bits & AO_MASK) >>> AO_BIT_OFFSET; int ao = (bits & AO_MASK) >>> AO_BIT_OFFSET;
int glint = (bits & GLINT_MASK) >>> GLINT_BIT_OFFSET; int glint = (bits & GLINT_MASK) >>> GLINT_BIT_OFFSET;
int shadeMode = (bits & SHADE_MODE_MASK) >>> SHADE_MODE_BIT_OFFSET;
return blendMode < BLEND_MODE_COUNT return blendMode < BLEND_MODE_COUNT
&& ao < TRI_STATE_COUNT && ao < TRI_STATE_COUNT
&& glint < TRI_STATE_COUNT; && glint < TRI_STATE_COUNT
&& shadeMode < SHADE_MODE_COUNT;
} }
protected int bits; protected int bits;
@ -105,4 +111,9 @@ public class MaterialViewImpl implements MaterialView {
public TriState glint() { public TriState glint() {
return TRI_STATES[(bits & GLINT_MASK) >>> GLINT_BIT_OFFSET]; return TRI_STATES[(bits & GLINT_MASK) >>> GLINT_BIT_OFFSET];
} }
@Override
public ShadeMode shadeMode() {
return SHADE_MODES[(bits & SHADE_MODE_MASK) >>> SHADE_MODE_BIT_OFFSET];
}
} }

View file

@ -17,6 +17,7 @@
package net.fabricmc.fabric.impl.client.indigo.renderer.mesh; package net.fabricmc.fabric.impl.client.indigo.renderer.mesh;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import org.jetbrains.annotations.Nullable;
import net.minecraft.client.render.VertexFormat; import net.minecraft.client.render.VertexFormat;
import net.minecraft.client.render.VertexFormats; import net.minecraft.client.render.VertexFormats;
@ -26,6 +27,7 @@ import net.minecraft.util.math.MathHelper;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadView; import net.fabricmc.fabric.api.renderer.v1.mesh.QuadView;
import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper; import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper;
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.GeometryHelper; import net.fabricmc.fabric.impl.client.indigo.renderer.helper.GeometryHelper;
import net.fabricmc.fabric.impl.client.indigo.renderer.material.MaterialViewImpl;
import net.fabricmc.fabric.impl.client.indigo.renderer.material.RenderMaterialImpl; import net.fabricmc.fabric.impl.client.indigo.renderer.material.RenderMaterialImpl;
/** /**
@ -78,66 +80,75 @@ public abstract class EncodingFormat {
/** used for quick clearing of quad buffers. */ /** used for quick clearing of quad buffers. */
static final int[] EMPTY = new int[TOTAL_STRIDE]; static final int[] EMPTY = new int[TOTAL_STRIDE];
private static final int DIRECTION_MASK = MathHelper.smallestEncompassingPowerOfTwo(ModelHelper.NULL_FACE_ID) - 1; private static final int DIRECTION_COUNT = Direction.values().length;
private static final int DIRECTION_BIT_COUNT = Integer.bitCount(DIRECTION_MASK); private static final int NULLABLE_DIRECTION_COUNT = DIRECTION_COUNT + 1;
private static final int CULL_SHIFT = 0;
private static final int CULL_INVERSE_MASK = ~(DIRECTION_MASK << CULL_SHIFT); private static final int CULL_BIT_LENGTH = MathHelper.ceilLog2(NULLABLE_DIRECTION_COUNT);
private static final int LIGHT_SHIFT = CULL_SHIFT + DIRECTION_BIT_COUNT; private static final int LIGHT_BIT_LENGTH = MathHelper.ceilLog2(DIRECTION_COUNT);
private static final int LIGHT_INVERSE_MASK = ~(DIRECTION_MASK << LIGHT_SHIFT); private static final int NORMALS_BIT_LENGTH = 4;
private static final int NORMALS_SHIFT = LIGHT_SHIFT + DIRECTION_BIT_COUNT; private static final int GEOMETRY_BIT_LENGTH = GeometryHelper.FLAG_BIT_COUNT;
private static final int NORMALS_COUNT = 4; private static final int MATERIAL_BIT_LENGTH = MaterialViewImpl.TOTAL_BIT_LENGTH;
private static final int NORMALS_MASK = (1 << NORMALS_COUNT) - 1;
private static final int NORMALS_INVERSE_MASK = ~(NORMALS_MASK << NORMALS_SHIFT); private static final int CULL_BIT_OFFSET = 0;
private static final int GEOMETRY_SHIFT = NORMALS_SHIFT + NORMALS_COUNT; private static final int LIGHT_BIT_OFFSET = CULL_BIT_OFFSET + CULL_BIT_LENGTH;
private static final int GEOMETRY_MASK = (1 << GeometryHelper.FLAG_BIT_COUNT) - 1; private static final int NORMALS_BIT_OFFSET = LIGHT_BIT_OFFSET + LIGHT_BIT_LENGTH;
private static final int GEOMETRY_INVERSE_MASK = ~(GEOMETRY_MASK << GEOMETRY_SHIFT); private static final int GEOMETRY_BIT_OFFSET = NORMALS_BIT_OFFSET + NORMALS_BIT_LENGTH;
private static final int MATERIAL_SHIFT = GEOMETRY_SHIFT + GeometryHelper.FLAG_BIT_COUNT; private static final int MATERIAL_BIT_OFFSET = GEOMETRY_BIT_OFFSET + GEOMETRY_BIT_LENGTH;
private static final int MATERIAL_MASK = MathHelper.smallestEncompassingPowerOfTwo(RenderMaterialImpl.VALUE_COUNT) - 1; private static final int TOTAL_BIT_LENGTH = MATERIAL_BIT_OFFSET + MATERIAL_BIT_LENGTH;
private static final int MATERIAL_BIT_COUNT = Integer.bitCount(MATERIAL_MASK);
private static final int MATERIAL_INVERSE_MASK = ~(MATERIAL_MASK << MATERIAL_SHIFT); private static final int CULL_MASK = bitMask(CULL_BIT_LENGTH, CULL_BIT_OFFSET);
private static final int LIGHT_MASK = bitMask(LIGHT_BIT_LENGTH, LIGHT_BIT_OFFSET);
private static final int NORMALS_MASK = bitMask(NORMALS_BIT_LENGTH, NORMALS_BIT_OFFSET);
private static final int GEOMETRY_MASK = bitMask(GEOMETRY_BIT_LENGTH, GEOMETRY_BIT_OFFSET);
private static final int MATERIAL_MASK = bitMask(MATERIAL_BIT_LENGTH, MATERIAL_BIT_OFFSET);
static { static {
Preconditions.checkArgument(MATERIAL_SHIFT + MATERIAL_BIT_COUNT <= 32, "Indigo header encoding bit count (%s) exceeds integer bit length)", TOTAL_STRIDE); Preconditions.checkArgument(TOTAL_BIT_LENGTH <= 32, "Indigo header encoding bit count (%s) exceeds integer bit length)", TOTAL_STRIDE);
} }
public static int bitMask(int bitLength, int bitOffset) {
return ((1 << bitLength) - 1) << bitOffset;
}
@Nullable
static Direction cullFace(int bits) { static Direction cullFace(int bits) {
return ModelHelper.faceFromIndex((bits >>> CULL_SHIFT) & DIRECTION_MASK); return ModelHelper.faceFromIndex((bits & CULL_MASK) >>> CULL_BIT_OFFSET);
} }
static int cullFace(int bits, Direction face) { static int cullFace(int bits, @Nullable Direction face) {
return (bits & CULL_INVERSE_MASK) | (ModelHelper.toFaceIndex(face) << CULL_SHIFT); return (bits & ~CULL_MASK) | (ModelHelper.toFaceIndex(face) << CULL_BIT_OFFSET);
} }
static Direction lightFace(int bits) { static Direction lightFace(int bits) {
return ModelHelper.faceFromIndex((bits >>> LIGHT_SHIFT) & DIRECTION_MASK); return ModelHelper.faceFromIndex((bits & LIGHT_MASK) >>> LIGHT_BIT_OFFSET);
} }
static int lightFace(int bits, Direction face) { static int lightFace(int bits, Direction face) {
return (bits & LIGHT_INVERSE_MASK) | (ModelHelper.toFaceIndex(face) << LIGHT_SHIFT); return (bits & ~LIGHT_MASK) | (ModelHelper.toFaceIndex(face) << LIGHT_BIT_OFFSET);
} }
/** indicate if vertex normal has been set - bits correspond to vertex ordinals. */ /** indicate if vertex normal has been set - bits correspond to vertex ordinals. */
static int normalFlags(int bits) { static int normalFlags(int bits) {
return (bits >>> NORMALS_SHIFT) & NORMALS_MASK; return (bits & NORMALS_MASK) >>> NORMALS_BIT_OFFSET;
} }
static int normalFlags(int bits, int normalFlags) { static int normalFlags(int bits, int normalFlags) {
return (bits & NORMALS_INVERSE_MASK) | ((normalFlags & NORMALS_MASK) << NORMALS_SHIFT); return (bits & ~NORMALS_MASK) | ((normalFlags << NORMALS_BIT_OFFSET) & NORMALS_MASK);
} }
static int geometryFlags(int bits) { static int geometryFlags(int bits) {
return (bits >>> GEOMETRY_SHIFT) & GEOMETRY_MASK; return (bits & GEOMETRY_MASK) >>> GEOMETRY_BIT_OFFSET;
} }
static int geometryFlags(int bits, int geometryFlags) { static int geometryFlags(int bits, int geometryFlags) {
return (bits & GEOMETRY_INVERSE_MASK) | ((geometryFlags & GEOMETRY_MASK) << GEOMETRY_SHIFT); return (bits & ~GEOMETRY_MASK) | ((geometryFlags << GEOMETRY_BIT_OFFSET) & GEOMETRY_MASK);
} }
static RenderMaterialImpl material(int bits) { static RenderMaterialImpl material(int bits) {
return RenderMaterialImpl.byIndex((bits >>> MATERIAL_SHIFT) & MATERIAL_MASK); return RenderMaterialImpl.byIndex((bits & MATERIAL_MASK) >>> MATERIAL_BIT_OFFSET);
} }
static int material(int bits, RenderMaterialImpl material) { static int material(int bits, RenderMaterialImpl material) {
return (bits & MATERIAL_INVERSE_MASK) | (material.index() << MATERIAL_SHIFT); return (bits & ~MATERIAL_MASK) | (material.index() << MATERIAL_BIT_OFFSET);
} }
} }

View file

@ -33,6 +33,7 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial; import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
import net.fabricmc.fabric.api.renderer.v1.material.ShadeMode;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
import net.fabricmc.fabric.api.util.TriState; import net.fabricmc.fabric.api.util.TriState;
import net.fabricmc.fabric.impl.client.indigo.Indigo; import net.fabricmc.fabric.impl.client.indigo.Indigo;
@ -55,18 +56,7 @@ public abstract class AbstractBlockRenderContext extends AbstractRenderContext {
@Override @Override
public void emitDirectly() { public void emitDirectly() {
renderQuad(this, false); renderQuad(this);
}
};
private final MutableQuadViewImpl vanillaModelEditorQuad = new MutableQuadViewImpl() {
{
data = new int[EncodingFormat.TOTAL_STRIDE];
clear();
}
@Override
public void emitDirectly() {
renderQuad(this, true);
} }
}; };
@ -88,11 +78,6 @@ public abstract class AbstractBlockRenderContext extends AbstractRenderContext {
return editorQuad; return editorQuad;
} }
public QuadEmitter getVanillaModelEmitter() {
// Do not clear the editorQuad since it is not accessible to API users.
return vanillaModelEditorQuad;
}
@Override @Override
public boolean isFaceCulled(@Nullable Direction face) { public boolean isFaceCulled(@Nullable Direction face) {
return !blockInfo.shouldDrawFace(face); return !blockInfo.shouldDrawFace(face);
@ -108,7 +93,7 @@ public abstract class AbstractBlockRenderContext extends AbstractRenderContext {
return vanillaModelConsumer; return vanillaModelConsumer;
} }
private void renderQuad(MutableQuadViewImpl quad, boolean isVanilla) { private void renderQuad(MutableQuadViewImpl quad) {
if (!transform(quad)) { if (!transform(quad)) {
return; return;
} }
@ -122,10 +107,11 @@ public abstract class AbstractBlockRenderContext extends AbstractRenderContext {
final TriState aoMode = mat.ambientOcclusion(); final TriState aoMode = mat.ambientOcclusion();
final boolean ao = blockInfo.useAo && (aoMode == TriState.TRUE || (aoMode == TriState.DEFAULT && blockInfo.defaultAo)); final boolean ao = blockInfo.useAo && (aoMode == TriState.TRUE || (aoMode == TriState.DEFAULT && blockInfo.defaultAo));
final boolean emissive = mat.emissive(); final boolean emissive = mat.emissive();
final boolean vanillaShade = mat.shadeMode() == ShadeMode.VANILLA;
final VertexConsumer vertexConsumer = getVertexConsumer(blockInfo.effectiveRenderLayer(mat.blendMode())); final VertexConsumer vertexConsumer = getVertexConsumer(blockInfo.effectiveRenderLayer(mat.blendMode()));
colorizeQuad(quad, colorIndex); colorizeQuad(quad, colorIndex);
shadeQuad(quad, isVanilla, ao, emissive); shadeQuad(quad, ao, emissive, vanillaShade);
bufferQuad(quad, vertexConsumer); bufferQuad(quad, vertexConsumer);
} }
@ -140,10 +126,10 @@ public abstract class AbstractBlockRenderContext extends AbstractRenderContext {
} }
} }
private void shadeQuad(MutableQuadViewImpl quad, boolean isVanilla, boolean ao, boolean emissive) { private void shadeQuad(MutableQuadViewImpl quad, boolean ao, boolean emissive, boolean vanillaShade) {
// 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
if (ao) { if (ao) {
aoCalc.compute(quad, isVanilla); aoCalc.compute(quad, vanillaShade);
if (emissive) { if (emissive) {
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
@ -157,7 +143,7 @@ public abstract class AbstractBlockRenderContext extends AbstractRenderContext {
} }
} }
} else { } else {
shadeFlatQuad(quad, isVanilla); shadeFlatQuad(quad, vanillaShade);
if (emissive) { if (emissive) {
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
@ -177,11 +163,11 @@ public abstract class AbstractBlockRenderContext extends AbstractRenderContext {
* Starting in 1.16 flat shading uses dimension-specific diffuse factors that can be < 1.0 * Starting in 1.16 flat shading uses dimension-specific diffuse factors that can be < 1.0
* even for un-shaded quads. These are also applied with AO shading but that is done in AO calculator. * even for un-shaded quads. These are also applied with AO shading but that is done in AO calculator.
*/ */
private void shadeFlatQuad(MutableQuadViewImpl quad, boolean isVanilla) { private void shadeFlatQuad(MutableQuadViewImpl quad, boolean vanillaShade) {
final boolean hasShade = quad.hasShade(); final boolean hasShade = quad.hasShade();
// Check the AO mode to match how shade is applied during smooth lighting // Check the AO mode to match how shade is applied during smooth lighting
if ((Indigo.AMBIENT_OCCLUSION_MODE == AoConfig.HYBRID && !isVanilla) || Indigo.AMBIENT_OCCLUSION_MODE == AoConfig.ENHANCED) { if ((Indigo.AMBIENT_OCCLUSION_MODE == AoConfig.HYBRID && !vanillaShade) || Indigo.AMBIENT_OCCLUSION_MODE == AoConfig.ENHANCED) {
if (quad.hasAllVertexNormals()) { if (quad.hasAllVertexNormals()) {
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
float shade = normalShade(quad.normalX(i), quad.normalY(i), quad.normalZ(i), hasShade); float shade = normalShade(quad.normalX(i), quad.normalY(i), quad.normalZ(i), hasShade);
@ -305,7 +291,7 @@ public abstract class AbstractBlockRenderContext extends AbstractRenderContext {
@Override @Override
public void accept(BakedModel model, @Nullable BlockState state) { public void accept(BakedModel model, @Nullable BlockState state) {
VanillaModelEncoder.emitBlockQuads(model, state, blockInfo.randomSupplier, AbstractBlockRenderContext.this, vanillaModelEditorQuad); VanillaModelEncoder.emitBlockQuads(model, state, blockInfo.randomSupplier, AbstractBlockRenderContext.this);
} }
} }
} }

View file

@ -16,7 +16,7 @@
package net.fabricmc.fabric.impl.client.indigo.renderer.render; package net.fabricmc.fabric.impl.client.indigo.renderer.render;
import java.util.Map; import java.util.function.Function;
import it.unimi.dsi.fastutil.longs.Long2FloatOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2FloatOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
@ -24,12 +24,8 @@ import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.client.render.BufferBuilder; import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexFormat;
import net.minecraft.client.render.VertexFormats;
import net.minecraft.client.render.WorldRenderer; import net.minecraft.client.render.WorldRenderer;
import net.minecraft.client.render.chunk.BlockBufferAllocatorStorage;
import net.minecraft.client.render.chunk.ChunkRendererRegion; import net.minecraft.client.render.chunk.ChunkRendererRegion;
import net.minecraft.client.util.BufferAllocator;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.BlockRenderView; import net.minecraft.world.BlockRenderView;
@ -69,9 +65,7 @@ public class ChunkRenderInfo {
private final Long2IntOpenHashMap brightnessCache; private final Long2IntOpenHashMap brightnessCache;
private final Long2FloatOpenHashMap aoLevelCache; private final Long2FloatOpenHashMap aoLevelCache;
private final BlockPos.Mutable chunkOrigin = new BlockPos.Mutable(); private Function<RenderLayer, BufferBuilder> bufferFunc;
BlockBufferAllocatorStorage builders;
Map<RenderLayer, BufferBuilder> buffers;
BlockRenderView blockView; BlockRenderView blockView;
ChunkRenderInfo() { ChunkRenderInfo() {
@ -81,31 +75,21 @@ public class ChunkRenderInfo {
aoLevelCache.defaultReturnValue(Float.MAX_VALUE); aoLevelCache.defaultReturnValue(Float.MAX_VALUE);
} }
void prepare(ChunkRendererRegion blockView, BlockPos chunkOrigin, BlockBufferAllocatorStorage builders, Map<RenderLayer, BufferBuilder> buffers) { void prepare(ChunkRendererRegion blockView, Function<RenderLayer, BufferBuilder> bufferFunc) {
this.blockView = blockView; this.blockView = blockView;
this.chunkOrigin.set(chunkOrigin); this.bufferFunc = bufferFunc;
this.builders = builders;
this.buffers = buffers;
brightnessCache.clear(); brightnessCache.clear();
aoLevelCache.clear(); aoLevelCache.clear();
} }
void release() { void release() {
blockView = null;
bufferFunc = null;
} }
/** Lazily retrieves output buffer for given layer, initializing as needed. */ BufferBuilder getBuffer(RenderLayer layer) {
public BufferBuilder getInitializedBuffer(RenderLayer renderLayer) { return bufferFunc.apply(layer);
// TODO 24w21b - possibly AW class_9810#method_60903 which does the same thing?
BufferBuilder builder = buffers.get(renderLayer);
if (builder == null) {
BufferAllocator byteBuilder = builders.get(renderLayer);
builder = new BufferBuilder(byteBuilder, VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_COLOR_TEXTURE_LIGHT_NORMAL);
buffers.put(renderLayer, builder);
}
return builder;
} }
/** /**

View file

@ -16,14 +16,13 @@
package net.fabricmc.fabric.impl.client.indigo.renderer.render; package net.fabricmc.fabric.impl.client.indigo.renderer.render;
import java.util.Map; import java.util.function.Function;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.client.render.BufferBuilder; import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.OverlayTexture; import net.minecraft.client.render.OverlayTexture;
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.chunk.BlockBufferAllocatorStorage;
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.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
@ -69,12 +68,12 @@ public class TerrainRenderContext extends AbstractBlockRenderContext {
@Override @Override
protected VertexConsumer getVertexConsumer(RenderLayer layer) { protected VertexConsumer getVertexConsumer(RenderLayer layer) {
return chunkInfo.getInitializedBuffer(layer); return chunkInfo.getBuffer(layer);
} }
public void prepare(ChunkRendererRegion blockView, BlockPos chunkOrigin, BlockBufferAllocatorStorage builders, Map<RenderLayer, BufferBuilder> builderMap) { public void prepare(ChunkRendererRegion blockView, Function<RenderLayer, BufferBuilder> bufferFunc) {
chunkInfo.prepare(blockView, bufferFunc);
blockInfo.prepareForWorld(blockView, true); blockInfo.prepareForWorld(blockView, true);
chunkInfo.prepare(blockView, chunkOrigin, builders, builderMap);
} }
public void release() { public void release() {

View file

@ -1,43 +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.mixin.client.indigo.renderer;
import java.util.function.Supplier;
import org.spongepowered.asm.mixin.Mixin;
import net.minecraft.block.BlockState;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.random.Random;
import net.minecraft.world.BlockRenderView;
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.render.AbstractBlockRenderContext;
import net.fabricmc.fabric.impl.renderer.VanillaModelEncoder;
@Mixin(BakedModel.class)
public interface BakedModelMixin extends FabricBakedModel {
/**
* Override the fallback path to shade vanilla quads differently.
*/
@Override
default void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
VanillaModelEncoder.emitBlockQuads((BakedModel) this, state, randomSupplier, context, ((AbstractBlockRenderContext) context).getVanillaModelEmitter());
}
}

View file

@ -17,11 +17,11 @@
package net.fabricmc.fabric.mixin.client.indigo.renderer; package net.fabricmc.fabric.mixin.client.indigo.renderer;
import java.util.Map; import java.util.Map;
import java.util.Set;
import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.Local;
import com.mojang.blaze3d.systems.VertexSorter; import com.mojang.blaze3d.systems.VertexSorter;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
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.Redirect; import org.spongepowered.asm.mixin.injection.Redirect;
@ -30,7 +30,6 @@ import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
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.client.render.BufferBuilder; import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumer;
@ -67,17 +66,19 @@ import net.fabricmc.fabric.impl.client.indigo.renderer.render.TerrainRenderConte
*/ */
@Mixin(SectionBuilder.class) @Mixin(SectionBuilder.class)
public abstract class SectionBuilderMixin { public abstract class SectionBuilderMixin {
@Shadow
abstract BufferBuilder beginBufferBuilding(Map<RenderLayer, BufferBuilder> builders, BlockBufferAllocatorStorage allocatorStorage, RenderLayer layer);
@Inject(method = "build", @Inject(method = "build",
at = @At(value = "INVOKE", target = "Lnet/minecraft/util/math/BlockPos;iterate(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/BlockPos;)Ljava/lang/Iterable;"), at = @At(value = "INVOKE", target = "Lnet/minecraft/util/math/BlockPos;iterate(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/BlockPos;)Ljava/lang/Iterable;"),
locals = LocalCapture.CAPTURE_FAILHARD) locals = LocalCapture.CAPTURE_FAILHARD)
private void hookChunkBuild(ChunkSectionPos sectionPos, ChunkRendererRegion region, VertexSorter sorter, private void hookBuild(ChunkSectionPos sectionPos, ChunkRendererRegion region, VertexSorter sorter,
BlockBufferAllocatorStorage builder, BlockBufferAllocatorStorage allocators,
CallbackInfoReturnable<SectionBuilder.RenderData> ci, CallbackInfoReturnable<SectionBuilder.RenderData> cir,
@Local(ordinal = 0) Map<RenderLayer, BufferBuilder> builderMap) { @Local(ordinal = 0) Map<RenderLayer, BufferBuilder> builderMap) {
// hook just before iterating over the render chunk's chunks blocks, captures the buffer builder map // hook just before iterating over the render chunk's blocks to capture the buffer builder map
TerrainRenderContext renderer = TerrainRenderContext.POOL.get(); TerrainRenderContext renderer = TerrainRenderContext.POOL.get();
renderer.prepare(region, sectionPos.getMinPos(), builder, builderMap); renderer.prepare(region, layer -> beginBufferBuilding(builderMap, allocators, layer));
((AccessChunkRendererRegion) region).fabric_setRenderer(renderer); ((AccessChunkRendererRegion) region).fabric_setRenderer(renderer);
} }
@ -99,7 +100,7 @@ public abstract class SectionBuilderMixin {
*/ */
@Redirect(method = "build", require = 1, at = @At(value = "INVOKE", @Redirect(method = "build", require = 1, at = @At(value = "INVOKE",
target = "Lnet/minecraft/client/render/block/BlockRenderManager;renderBlock(Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/BlockRenderView;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumer;ZLnet/minecraft/util/math/random/Random;)V")) target = "Lnet/minecraft/client/render/block/BlockRenderManager;renderBlock(Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/BlockRenderView;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumer;ZLnet/minecraft/util/math/random/Random;)V"))
private void hookChunkBuildTessellate(BlockRenderManager renderManager, BlockState blockState, BlockPos blockPos, BlockRenderView blockView, MatrixStack matrix, VertexConsumer bufferBuilder, boolean checkSides, Random random) { private void hookBuildRenderBlock(BlockRenderManager renderManager, BlockState blockState, BlockPos blockPos, BlockRenderView blockView, MatrixStack matrix, VertexConsumer bufferBuilder, boolean checkSides, Random random) {
if (blockState.getRenderType() == BlockRenderType.MODEL) { if (blockState.getRenderType() == BlockRenderType.MODEL) {
final BakedModel model = renderManager.getModel(blockState); final BakedModel model = renderManager.getModel(blockState);
@ -115,11 +116,9 @@ public abstract class SectionBuilderMixin {
/** /**
* Release all references. Probably not necessary but would be $#%! to debug if it is. * Release all references. Probably not necessary but would be $#%! to debug if it is.
*/ */
@Inject(method = "build", @Inject(method = "build", at = @At(value = "RETURN"))
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/block/BlockModelRenderer;disableBrightnessCache()V")) private void hookBuildReturn(ChunkSectionPos sectionPos, ChunkRendererRegion renderRegion, VertexSorter vertexSorter, BlockBufferAllocatorStorage allocatorStorage, CallbackInfoReturnable<SectionBuilder.RenderData> cir) {
private void hookRebuildChunkReturn(CallbackInfoReturnable<Set<BlockEntity>> ci) { ((AccessChunkRendererRegion) renderRegion).fabric_getRenderer().release();
// hook after iterating over the render chunk's chunks blocks, must be called if and only if hookChunkBuild happened ((AccessChunkRendererRegion) renderRegion).fabric_setRenderer(null);
TerrainRenderContext.POOL.get().release();
} }
} }

View file

@ -1,11 +1,7 @@
accessWidener v2 named accessWidener v2 named
accessible class net/minecraft/client/render/chunk/ChunkBuilder$BuiltChunk$RebuildTask
accessible class net/minecraft/client/render/block/BlockModelRenderer$AmbientOcclusionCalculator accessible class net/minecraft/client/render/block/BlockModelRenderer$AmbientOcclusionCalculator
accessible field net/minecraft/client/render/block/BlockModelRenderer$AmbientOcclusionCalculator brightness [F accessible field net/minecraft/client/render/block/BlockModelRenderer$AmbientOcclusionCalculator brightness [F
accessible field net/minecraft/client/render/block/BlockModelRenderer$AmbientOcclusionCalculator light [I accessible field net/minecraft/client/render/block/BlockModelRenderer$AmbientOcclusionCalculator light [I
accessible method net/minecraft/client/render/block/BlockModelRenderer getQuadDimensions (Lnet/minecraft/world/BlockRenderView;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;[ILnet/minecraft/util/math/Direction;[FLjava/util/BitSet;)V accessible method net/minecraft/client/render/block/BlockModelRenderer getQuadDimensions (Lnet/minecraft/world/BlockRenderView;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;[ILnet/minecraft/util/math/Direction;[FLjava/util/BitSet;)V
accessible method net/minecraft/client/render/item/ItemRenderer renderBakedItemModel (Lnet/minecraft/client/render/model/BakedModel;Lnet/minecraft/item/ItemStack;IILnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumer;)V

View file

@ -1,12 +1,11 @@
{ {
"required": true, "required": true,
"package": "net.fabricmc.fabric.mixin.client.indigo.renderer", "package": "net.fabricmc.fabric.mixin.client.indigo.renderer",
"compatibilityLevel": "JAVA_17", "compatibilityLevel": "JAVA_21",
"plugin": "net.fabricmc.fabric.impl.client.indigo.IndigoMixinConfigPlugin", "plugin": "net.fabricmc.fabric.impl.client.indigo.IndigoMixinConfigPlugin",
"mixins": [ "mixins": [
], ],
"client": [ "client": [
"BakedModelMixin",
"BlockModelRendererMixin", "BlockModelRendererMixin",
"ChunkRendererRegionMixin", "ChunkRendererRegionMixin",
"ItemRendererAccessor", "ItemRendererAccessor",