Finalize 1.21.5 FRAPI port and other improvements (#4650)

* Allow using sprite finder during model baking

- Allow retrieving sprite finders from ErrorCollectingSpriteGetter, SpriteLoader.StitchResult, and SpriteAtlasManager.AtlasPreparation via new interface injections
- Deprecate SpriteFinder.get in favor of interface injected method for consistency with other methods
- Ensure lazily initialization of sprite finders is completely thread safe
- Provide a default implementation for MutableQuadView/QuadEmitter.spriteBake
- Improve some documentation

* Add ModelBakeSettingsHelper

- Respect passed ModelBakeSettings in testmod custom Geometry implementations
- Add axis block state property to testmod octagonal column block and rotate model to test ModelBakeSettings application

* Make AoCalculator.vanillaFullFace work regardless of vertex order

- Use vertex map for cubic faces during enhanced light as well now that it works regardless of vertex order and is faster than using weights
- Fix typos in AoFaceData

* Finalization

- Remove Experimental annotations
- Deprecate Renderer.emitBlockModelPartQuads for removal
- Fix GeometryBakedModel.emitQuads not applying instance AO boolean
- Add RenderLayerHelper.movingDelegate/entityDelegate and use them where appropriate
- Clarify some javadoc
This commit is contained in:
PepperCode1 2025-05-31 12:19:20 -07:00 committed by GitHub
parent 39f4686e1f
commit 5281b90756
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
46 changed files with 952 additions and 189 deletions

View file

@ -53,7 +53,7 @@ public class BakedModelFeatureRenderer<S extends LivingEntityRenderState, M exte
matrices.scale(-0.75F, -0.75F, 0.75F);
float aboveHead = (float) (Math.sin(state.age * 0.08F)) * 0.5F + 0.5F;
matrices.translate(-0.5F, 0.75F + aboveHead, -0.5F);
FabricBlockModelRenderer.render(matrices.peek(), layer -> vertexConsumers.getBuffer(RenderLayerHelper.getEntityBlockLayer(layer)), model, 1, 1, 1, light, OverlayTexture.DEFAULT_UV, EmptyBlockRenderView.INSTANCE, BlockPos.ORIGIN, Blocks.AIR.getDefaultState());
FabricBlockModelRenderer.render(matrices.peek(), RenderLayerHelper.entityDelegate(vertexConsumers), model, 1, 1, 1, light, OverlayTexture.DEFAULT_UV, EmptyBlockRenderView.INSTANCE, BlockPos.ORIGIN, Blocks.AIR.getDefaultState());
matrices.pop();
}
}

View file

@ -146,7 +146,7 @@ public interface Renderer {
/**
* @see FabricBlockModelPart#emitQuads(QuadEmitter, Predicate)
*/
@ApiStatus.Experimental
@Deprecated(forRemoval = true)
@ApiStatus.OverrideOnly
default void emitBlockModelPartQuads(BlockModelPart modelPart, QuadEmitter emitter, Predicate<@Nullable Direction> cullTest) {
VanillaBlockModelPartEncoder.emitQuads(modelPart, emitter, cullTest);

View file

@ -29,6 +29,7 @@ import net.minecraft.util.math.Direction;
import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.material.MaterialFinder;
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
import net.fabricmc.fabric.impl.renderer.QuadSpriteBaker;
/**
* A mutable {@link QuadView} instance. The base interface for
@ -41,46 +42,47 @@ import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
*/
public interface MutableQuadView extends QuadView {
/**
* Causes texture to appear with no rotation.
* When enabled, causes texture to appear with no rotation. This is the default and does not have to be specified
* explicitly. Can be overridden by other rotation flags.
* Pass in bakeFlags parameter to {@link #spriteBake(Sprite, int)}.
*/
int BAKE_ROTATE_NONE = 0;
/**
* Causes texture to appear rotated 90 deg. clockwise relative to nominal face.
* When enabled, causes texture to appear rotated 90 degrees clockwise.
* Pass in bakeFlags parameter to {@link #spriteBake(Sprite, int)}.
*/
int BAKE_ROTATE_90 = 1;
/**
* Causes texture to appear rotated 180 deg. relative to nominal face.
* When enabled, causes texture to appear rotated 180 degrees.
* Pass in bakeFlags parameter to {@link #spriteBake(Sprite, int)}.
*/
int BAKE_ROTATE_180 = 2;
/**
* Causes texture to appear rotated 270 deg. clockwise relative to nominal face.
* When enabled, causes texture to appear rotated 270 degrees clockwise.
* Pass in bakeFlags parameter to {@link #spriteBake(Sprite, int)}.
*/
int BAKE_ROTATE_270 = 3;
/**
* When enabled, texture coordinate are assigned based on vertex position.
* Any existing UV coordinates will be replaced.
* When enabled, texture coordinates are assigned based on vertex positions and the
* {@linkplain #nominalFace() nominal face}.
* Any existing UV coordinates will be replaced and the {@link #BAKE_NORMALIZED} flag will be ignored.
* Pass in bakeFlags parameter to {@link #spriteBake(Sprite, int)}.
*
* <p>UV lock always derives texture coordinates based on nominal face, even
* when the quad is not co-planar with that face, and the result is
* the same as if the quad were projected onto the nominal face, which
* is usually the desired result.
* <p>UV lock derives texture coordinates based on {@linkplain #nominalFace() nominal face} by projecting the quad
* onto it, even when the quad is not co-planar with it. This flag is ignored if the normal face is {@code null}.
*/
int BAKE_LOCK_UV = 4;
/**
* When set, U texture coordinates for the given sprite are
* When enabled, U texture coordinates for the given sprite are
* flipped as part of baking. Can be useful for some randomization
* and texture mapping scenarios. Results are different from what
* can be obtained via rotation and both can be applied.
* can be obtained via rotation and both can be applied. Any
* rotation is applied before this flag.
* Pass in bakeFlags parameter to {@link #spriteBake(Sprite, int)}.
*/
int BAKE_FLIP_U = 8;
@ -168,7 +170,10 @@ public interface MutableQuadView extends QuadView {
* Can handle UV locking, rotation, interpolation, etc. Control this behavior
* by passing additive combinations of the BAKE_ flags defined in this interface.
*/
MutableQuadView spriteBake(Sprite sprite, int bakeFlags);
default MutableQuadView spriteBake(Sprite sprite, int bakeFlags) {
QuadSpriteBaker.bakeSprite(this, sprite, bakeFlags);
return this;
}
/**
* Accept vanilla lightmap values. Input values will override lightmap values

View file

@ -78,7 +78,10 @@ public interface QuadEmitter extends MutableQuadView {
}
@Override
QuadEmitter spriteBake(Sprite sprite, int bakeFlags);
default QuadEmitter spriteBake(Sprite sprite, int bakeFlags) {
MutableQuadView.super.spriteBake(sprite, bakeFlags);
return this;
}
default QuadEmitter uvUnitSquare() {
uv(0, 0, 0);

View file

@ -18,7 +18,6 @@ package net.fabricmc.fabric.api.renderer.v1.model;
import java.util.function.Predicate;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import net.minecraft.block.BlockState;
@ -32,15 +31,15 @@ import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
/**
* Note: This interface is automatically implemented on all block model parts via Mixin and interface injection.
* Note: This interface is automatically implemented on {@link BlockModelPart} via Mixin and interface injection.
*/
@ApiStatus.Experimental
public interface FabricBlockModelPart {
/**
* Produces this model part's geometry. <b>This method must be called instead of
* {@link BlockModelPart#getQuads(Direction)}; the vanilla method should be considered deprecated as it may not
* produce accurate results.</b> However, it is acceptable for a custom model part to only implement the vanilla
* method as the default implementation of this method will delegate to the vanilla method.
* {@link BlockModelPart#getQuads(Direction)} and {@link BlockModelPart#useAmbientOcclusion()}; the vanilla methods
* should be considered deprecated as they may not produce accurate results.</b> However, it is acceptable for a
* custom model part to only implement the vanilla methods as the default implementation of this method will
* delegate to the vanilla methods.
*
* <p>This method mainly exists for convenience when interacting with parts implemented and produced by vanilla
* code. Custom models should generally override

View file

@ -41,7 +41,7 @@ import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
*
* <p>Implementors should have a look at {@link ModelHelper} as it contains many useful functions.
*
* <p>Note: This interface is automatically implemented on all block state models via Mixin and interface injection.
* <p>Note: This interface is automatically implemented on {@link BlockStateModel} via Mixin and interface injection.
*/
public interface FabricBlockStateModel {
/**

View file

@ -18,21 +18,24 @@ package net.fabricmc.fabric.api.renderer.v1.model;
import java.util.List;
import org.jetbrains.annotations.ApiStatus;
import net.minecraft.client.render.model.BakedGeometry;
import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.client.render.model.Baker;
import net.minecraft.client.render.model.Geometry;
import net.minecraft.client.render.model.ModelBakeSettings;
import net.minecraft.client.render.model.ModelTextures;
import net.minecraft.client.render.model.SimpleModel;
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
/**
* A special {@link BakedGeometry} which hides a {@link Mesh} instead of using {@link BakedQuad}s. Instances of this
* class always return empty lists from inherited methods.
* A special {@link BakedGeometry} which hides a {@link Mesh} instead of using {@link BakedQuad}s. Useful for custom
* implementations of {@link Geometry#bake(ModelTextures, Baker, ModelBakeSettings, SimpleModel)} that want to return a
* mesh. Instances of this class always return empty lists from inherited methods.
*
* <p>Any code that interacts with {@link BakedGeometry} should first check {@code instanceof MeshBakedGeometry} and use
* {@link #getMesh()} if {@code true} or the vanilla methods otherwise.
*/
@ApiStatus.Experimental
public final class MeshBakedGeometry extends BakedGeometry {
private final Mesh mesh;

View file

@ -0,0 +1,234 @@
/*
* 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.model;
import java.util.EnumMap;
import java.util.Map;
import org.joml.Matrix3f;
import org.joml.Matrix4f;
import org.joml.Matrix4fc;
import org.joml.Vector3f;
import org.joml.Vector4f;
import net.minecraft.client.render.model.Geometry;
import net.minecraft.client.render.model.ModelBakeSettings;
import net.minecraft.client.render.model.ModelRotation;
import net.minecraft.client.texture.Sprite;
import net.minecraft.util.math.AffineTransformation;
import net.minecraft.util.math.AffineTransformations;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.MatrixUtil;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadTransform;
/**
* Utilities to make it easier to work with {@link ModelBakeSettings}.
*/
public final class ModelBakeSettingsHelper {
private static final Direction[] DIRECTIONS = Direction.values();
private ModelBakeSettingsHelper() {
}
/**
* Creates a new {@link ModelBakeSettings} using the given transformation and enables UV lock if specified. Works
* exactly like {@link ModelRotation}, but allows an arbitrary transformation. Instances should be retained and
* reused, especially if UV lock is enabled, to avoid redoing costly computations.
*/
public static ModelBakeSettings of(AffineTransformation transformation, boolean uvLock) {
Matrix4fc matrix = transformation.getMatrix();
if (MatrixUtil.isIdentity(matrix)) {
return ModelRotation.X0_Y0;
}
if (!uvLock) {
return new ModelBakeSettings() {
@Override
public AffineTransformation getRotation() {
return transformation;
}
};
}
Map<Direction, Matrix4fc> faceTransformations = new EnumMap<>(Direction.class);
Map<Direction, Matrix4fc> inverseFaceTransformations = new EnumMap<>(Direction.class);
for (Direction face : DIRECTIONS) {
Matrix4fc faceTransformation = AffineTransformations.getTransformed(transformation, face).getMatrix();
faceTransformations.put(face, faceTransformation);
inverseFaceTransformations.put(face, faceTransformation.invert(new Matrix4f()));
}
return new ModelBakeSettings() {
@Override
public AffineTransformation getRotation() {
return transformation;
}
@Override
public Matrix4fc forward(Direction face) {
return faceTransformations.get(face);
}
@Override
public Matrix4fc reverse(Direction face) {
return inverseFaceTransformations.get(face);
}
};
}
/**
* Creates a new {@link ModelBakeSettings} that is the product of the two given settings. Settings are represented
* by matrices, so this method follows the rules of matrix multiplication, namely that applying the resulting
* settings is (mostly) equivalent to applying the right settings and then the left settings. The only exception
* during standard application is cull face transformation, as the result must be clamped. Thus, applying a single
* premultiplied transformation generally yields better results than multiple applications.
*/
public static ModelBakeSettings multiply(ModelBakeSettings left, ModelBakeSettings right) {
// Assumes face transformations are identity if main transformation is identity
if (MatrixUtil.isIdentity(left.getRotation().getMatrix())) {
return right;
} else if (MatrixUtil.isIdentity(right.getRotation().getMatrix())) {
return left;
}
AffineTransformation transformation = left.getRotation().multiply(right.getRotation());
boolean leftHasFaceTransformations = false;
boolean rightHasFaceTransformations = false;
// Assumes inverse face transformations are exactly inverse of regular face transformations
for (Direction face : DIRECTIONS) {
if (!leftHasFaceTransformations && !MatrixUtil.isIdentity(left.forward(face))) {
leftHasFaceTransformations = true;
}
if (!rightHasFaceTransformations && !MatrixUtil.isIdentity(right.forward(face))) {
rightHasFaceTransformations = true;
}
}
if (leftHasFaceTransformations & rightHasFaceTransformations) {
Map<Direction, Matrix4fc> faceTransformations = new EnumMap<>(Direction.class);
Map<Direction, Matrix4fc> inverseFaceTransformations = new EnumMap<>(Direction.class);
for (Direction face : DIRECTIONS) {
faceTransformations.put(face, left.forward(face).mul(right.forward(face), new Matrix4f()));
inverseFaceTransformations.put(face, right.reverse(face).mul(left.reverse(face), new Matrix4f()));
}
return new ModelBakeSettings() {
@Override
public AffineTransformation getRotation() {
return transformation;
}
@Override
public Matrix4fc forward(Direction face) {
return faceTransformations.get(face);
}
@Override
public Matrix4fc reverse(Direction face) {
return inverseFaceTransformations.get(face);
}
};
}
ModelBakeSettings faceTransformDelegate = leftHasFaceTransformations ? left : right;
return new ModelBakeSettings() {
@Override
public AffineTransformation getRotation() {
return transformation;
}
@Override
public Matrix4fc forward(Direction face) {
return faceTransformDelegate.forward(face);
}
@Override
public Matrix4fc reverse(Direction face) {
return faceTransformDelegate.reverse(face);
}
};
}
/**
* Creates a new {@link QuadTransform} that applies the given transformation. The sprite finder is used to look up
* the current sprite to correctly apply UV lock, if present in the transformation.
*
* <p>This method is most useful when creating custom implementations of {@link Geometry}, which receive a
* {@link ModelBakeSettings}.
*/
public static QuadTransform asQuadTransform(ModelBakeSettings settings, SpriteFinder spriteFinder) {
Matrix4fc matrix = settings.getRotation().getMatrix();
// Assumes face transformations are identity if main transformation is identity
if (MatrixUtil.isIdentity(matrix)) {
return q -> true;
}
Matrix3f normalMatrix = matrix.normal(new Matrix3f());
Vector4f vec4 = new Vector4f();
Vector3f vec3 = new Vector3f();
return quad -> {
Direction lightFace = quad.lightFace();
Matrix4fc reverseMatrix = settings.reverse(lightFace);
if (!MatrixUtil.isIdentity(reverseMatrix)) {
Sprite sprite = spriteFinder.find(quad);
for (int vertexIndex = 0; vertexIndex < 4; vertexIndex++) {
float frameU = sprite.getFrameFromU(quad.u(vertexIndex));
float frameV = sprite.getFrameFromV(quad.v(vertexIndex));
vec3.set(frameU - 0.5f, frameV - 0.5f, 0.0f);
reverseMatrix.transformPosition(vec3);
frameU = vec3.x + 0.5f;
frameV = vec3.y + 0.5f;
quad.uv(vertexIndex, sprite.getFrameU(frameU), sprite.getFrameV(frameV));
}
}
for (int vertexIndex = 0; vertexIndex < 4; vertexIndex++) {
vec4.set(quad.x(vertexIndex) - 0.5f, quad.y(vertexIndex) - 0.5f, quad.z(vertexIndex) - 0.5f, 1.0f);
vec4.mul(matrix);
quad.pos(vertexIndex, vec4.x + 0.5f, vec4.y + 0.5f, vec4.z + 0.5f);
if (quad.hasNormal(vertexIndex)) {
quad.copyNormal(vertexIndex, vec3);
vec3.mul(normalMatrix);
vec3.normalize();
quad.normal(vertexIndex, vec3);
}
}
Direction cullFace = quad.cullFace();
if (cullFace != null) {
quad.cullFace(Direction.transform(matrix, cullFace));
}
return true;
};
}
}

View file

@ -71,7 +71,7 @@ public final class ModelHelper {
* <p>Retrieves sprites from the block texture atlas via {@link SpriteFinder}.
*/
public static List<BakedQuad>[] toQuadLists(Mesh mesh) {
SpriteFinder finder = SpriteFinder.get(MinecraftClient.getInstance().getBakedModelManager().getAtlas(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE));
SpriteFinder finder = MinecraftClient.getInstance().getBakedModelManager().getAtlas(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE).spriteFinder();
@SuppressWarnings("unchecked")
final ImmutableList.Builder<BakedQuad>[] builders = new ImmutableList.Builder[7];
@ -80,12 +80,10 @@ public final class ModelHelper {
builders[i] = ImmutableList.builder();
}
if (mesh != null) {
mesh.forEach(q -> {
Direction cullFace = q.cullFace();
builders[cullFace == null ? NULL_FACE_ID : cullFace.getIndex()].add(q.toBakedQuad(finder.find(q)));
});
}
mesh.forEach(q -> {
Direction cullFace = q.cullFace();
builders[cullFace == null ? NULL_FACE_ID : cullFace.getIndex()].add(q.toBakedQuad(finder.find(q)));
});
@SuppressWarnings("unchecked")
List<BakedQuad>[] result = new List[7];

View file

@ -16,35 +16,42 @@
package net.fabricmc.fabric.api.renderer.v1.model;
import java.util.function.Predicate;
import org.jetbrains.annotations.ApiStatus;
import net.minecraft.block.BlockState;
import net.minecraft.client.render.model.ErrorCollectingSpriteGetter;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.texture.SpriteAtlasTexture;
import net.minecraft.util.Identifier;
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.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.impl.renderer.SpriteFinderImpl;
import net.fabricmc.fabric.api.renderer.v1.sprite.FabricAtlasPreparation;
import net.fabricmc.fabric.api.renderer.v1.sprite.FabricErrorCollectingSpriteGetter;
import net.fabricmc.fabric.api.renderer.v1.sprite.FabricSpriteAtlasTexture;
import net.fabricmc.fabric.api.renderer.v1.sprite.FabricStitchResult;
/**
* Indexes a texture atlas to allow fast lookup of Sprites from
* baked vertex coordinates. Main use is for {@link Mesh}-based models
* to generate vanilla quads on demand without tracking and retaining
* the sprites that were baked into the mesh. In other words, this class
* supplies the sprite parameter for {@link QuadView#toBakedQuad(Sprite)}.
* Indexes a texture atlas to allow fast lookup of {@link Sprite}s from baked texture coordinates.
*
* <p>Example use cases include interpolating the textures of a submodel's quads in
* {@link FabricBlockStateModel#emitQuads(QuadEmitter, BlockRenderView, BlockPos, BlockState, Random, Predicate)} or
* finding the sprite for use in {@link QuadView#toBakedQuad(Sprite)}.
*
* <p>A sprite finder can be retrieved from various vanilla objects. Always use
* {@link FabricErrorCollectingSpriteGetter#spriteFinder(Identifier)}, {@link FabricStitchResult#spriteFinder()}, or
* {@link FabricAtlasPreparation#spriteFinder()} whenever an applicable instance is available. For example, model
* baking is supplied with a {@link ErrorCollectingSpriteGetter}, so it should be used to retrieve the sprite finder.
* In most other cases, it is safe to use {@link FabricSpriteAtlasTexture#spriteFinder()}.
*/
@ApiStatus.NonExtendable
public interface SpriteFinder {
/**
* Retrieves or creates the finder for the given atlas.
* Instances should not be retained as fields, or they must be
* refreshed whenever there is a resource reload or other event
* that causes atlas textures to be re-stitched.
*/
static SpriteFinder get(SpriteAtlasTexture atlas) {
return SpriteFinderImpl.get(atlas);
}
/**
* Finds the atlas sprite containing the vertex centroid of the quad.
* Vertex centroid is essentially the mean u,v coordinate - the intent being
@ -69,6 +76,14 @@ public interface SpriteFinder {
*/
Sprite find(float u, float v);
/**
* @deprecated Use {@link FabricSpriteAtlasTexture#spriteFinder()} instead.
*/
@Deprecated
static SpriteFinder get(SpriteAtlasTexture atlas) {
return atlas.spriteFinder();
}
/**
* @deprecated Use {@link #find(QuadView)} instead.
*/

View file

@ -36,7 +36,7 @@ import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
/**
* Note: This interface is automatically implemented on all block model renderers via Mixin and interface injection.
* Note: This interface is automatically implemented on {@link BlockModelRenderer} via Mixin and interface injection.
*/
public interface FabricBlockModelRenderer {
/**

View file

@ -33,7 +33,7 @@ import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
/**
* Note: This interface is automatically implemented on all block render managers via Mixin and interface injection.
* Note: This interface is automatically implemented on {@link BlockRenderManager} via Mixin and interface injection.
*/
public interface FabricBlockRenderManager {
/**

View file

@ -25,7 +25,8 @@ import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
/**
* Note: This interface is automatically implemented on all layer render states via Mixin and interface injection.
* Note: This interface is automatically implemented on {@link ItemRenderState.LayerRenderState} via Mixin and interface
* injection.
*/
public interface FabricLayerRenderState {
/**

View file

@ -20,6 +20,7 @@ import net.minecraft.block.BlockState;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.RenderLayers;
import net.minecraft.client.render.TexturedRenderLayers;
import net.minecraft.client.render.VertexConsumerProvider;
public final class RenderLayerHelper {
private RenderLayerHelper() {
@ -40,4 +41,20 @@ public final class RenderLayerHelper {
public static RenderLayer getEntityBlockLayer(RenderLayer chunkRenderLayer) {
return chunkRenderLayer == RenderLayer.getTranslucent() ? TexturedRenderLayers.getItemEntityTranslucentCull() : TexturedRenderLayers.getEntityCutout();
}
/**
* Wraps the given provider, converting {@linkplain RenderLayer#getBlockLayers() block layers} to render layers
* using {@link #getMovingBlockLayer(RenderLayer)}.
*/
public static VertexConsumerProvider movingDelegate(VertexConsumerProvider vertexConsumers) {
return chunkRenderLayer -> vertexConsumers.getBuffer(RenderLayerHelper.getMovingBlockLayer(chunkRenderLayer));
}
/**
* Wraps the given provider, converting {@linkplain RenderLayer#getBlockLayers() block layers} to render layers
* using {@link #getEntityBlockLayer(RenderLayer)}.
*/
public static VertexConsumerProvider entityDelegate(VertexConsumerProvider vertexConsumers) {
return chunkRenderLayer -> vertexConsumers.getBuffer(RenderLayerHelper.getEntityBlockLayer(chunkRenderLayer));
}
}

View file

@ -0,0 +1,33 @@
/*
* 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.sprite;
import net.minecraft.client.render.model.SpriteAtlasManager;
import net.fabricmc.fabric.api.renderer.v1.model.SpriteFinder;
/**
* Note: This interface is automatically implemented on {@link SpriteAtlasManager.AtlasPreparation} via Mixin and interface injection.
*/
public interface FabricAtlasPreparation {
/**
* {@return the sprite finder for this atlas preparation}
*/
default SpriteFinder spriteFinder() {
throw new UnsupportedOperationException();
}
}

View file

@ -0,0 +1,34 @@
/*
* 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.sprite;
import net.minecraft.client.render.model.ErrorCollectingSpriteGetter;
import net.minecraft.util.Identifier;
import net.fabricmc.fabric.api.renderer.v1.model.SpriteFinder;
/**
* Note: This interface is automatically implemented on {@link ErrorCollectingSpriteGetter} via Mixin and interface injection.
*/
public interface FabricErrorCollectingSpriteGetter {
/**
* {@return the sprite finder for the given atlas ID}
*/
default SpriteFinder spriteFinder(Identifier atlasId) {
throw new UnsupportedOperationException();
}
}

View file

@ -0,0 +1,44 @@
/*
* 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.sprite;
import net.minecraft.client.texture.SpriteAtlasTexture;
import net.minecraft.client.texture.SpriteLoader;
import net.minecraft.util.Identifier;
import net.fabricmc.fabric.api.renderer.v1.model.SpriteFinder;
/**
* Note: This interface is automatically implemented on {@link SpriteAtlasTexture} via Mixin and interface injection.
*/
public interface FabricSpriteAtlasTexture {
/**
* Retrieves the sprite finder for this atlas. The returned instance is only valid until the next call to
* {@link SpriteAtlasTexture#upload(SpriteLoader.StitchResult)}, and thus should not be persisted across resource
* reloads.
*
* <p><b>This method should not be used during a resource reload</b> as this atlas will only be populated with new
* sprites towards the end of the resource reload. In this case, use
* {@link FabricErrorCollectingSpriteGetter#spriteFinder(Identifier)}, {@link FabricStitchResult#spriteFinder()},
* or {@link FabricAtlasPreparation#spriteFinder()} instead.
*
* @return the sprite finder for this atlas
*/
default SpriteFinder spriteFinder() {
throw new UnsupportedOperationException();
}
}

View file

@ -0,0 +1,33 @@
/*
* 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.sprite;
import net.minecraft.client.texture.SpriteLoader;
import net.fabricmc.fabric.api.renderer.v1.model.SpriteFinder;
/**
* Note: This interface is automatically implemented on {@link SpriteLoader.StitchResult} via Mixin and interface injection.
*/
public interface FabricStitchResult {
/**
* {@return the sprite finder for this stitch result}
*/
default SpriteFinder spriteFinder() {
throw new UnsupportedOperationException();
}
}

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package net.fabricmc.fabric.impl.client.indigo.renderer.helper;
package net.fabricmc.fabric.impl.renderer;
import net.minecraft.client.texture.Sprite;
import net.minecraft.util.math.Direction;
@ -23,11 +23,10 @@ import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
/**
* Handles most texture-baking use cases for model loaders and model libraries
* via {@link #bakeSprite(MutableQuadView, Sprite, int)}. Also used by the API
* itself to implement automatic block-breaking models for enhanced models.
* via {@link #bakeSprite(MutableQuadView, Sprite, int)}.
*/
public final class TextureHelper {
private TextureHelper() { }
public final class QuadSpriteBaker {
private QuadSpriteBaker() { }
private static final float NORMALIZER = 1f / 16f;
@ -101,11 +100,11 @@ public final class TextureHelper {
private static final VertexModifier[] UVLOCKERS = new VertexModifier[6];
static {
UVLOCKERS[Direction.EAST.getIndex()] = (q, i) -> q.uv(i, 1 - q.z(i), 1 - q.y(i));
UVLOCKERS[Direction.WEST.getIndex()] = (q, i) -> q.uv(i, q.z(i), 1 - q.y(i));
UVLOCKERS[Direction.NORTH.getIndex()] = (q, i) -> q.uv(i, 1 - q.x(i), 1 - q.y(i));
UVLOCKERS[Direction.SOUTH.getIndex()] = (q, i) -> q.uv(i, q.x(i), 1 - q.y(i));
UVLOCKERS[Direction.DOWN.getIndex()] = (q, i) -> q.uv(i, q.x(i), 1 - q.z(i));
UVLOCKERS[Direction.UP.getIndex()] = (q, i) -> q.uv(i, q.x(i), q.z(i));
UVLOCKERS[Direction.NORTH.getIndex()] = (q, i) -> q.uv(i, 1 - q.x(i), 1 - q.y(i));
UVLOCKERS[Direction.SOUTH.getIndex()] = (q, i) -> q.uv(i, q.x(i), 1 - q.y(i));
UVLOCKERS[Direction.WEST.getIndex()] = (q, i) -> q.uv(i, q.z(i), 1 - q.y(i));
UVLOCKERS[Direction.EAST.getIndex()] = (q, i) -> q.uv(i, 1 - q.z(i), 1 - q.y(i));
}
}

View file

@ -17,14 +17,12 @@
package net.fabricmc.fabric.impl.renderer;
import java.util.Map;
import java.util.function.Consumer;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.minecraft.client.texture.MissingSprite;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.texture.SpriteAtlasTexture;
import net.minecraft.util.Identifier;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadView;
@ -43,12 +41,12 @@ public class SpriteFinderImpl implements SpriteFinder {
private static final Logger LOGGER = LoggerFactory.getLogger(SpriteFinderImpl.class);
private final Node root;
private final SpriteAtlasTexture spriteAtlasTexture;
private final Sprite missingSprite;
private int badSpriteCount = 0;
public SpriteFinderImpl(Map<Identifier, Sprite> sprites, SpriteAtlasTexture spriteAtlasTexture) {
public SpriteFinderImpl(Map<Identifier, Sprite> sprites, Sprite missingSprite) {
root = new Node(0.5f, 0.5f, 0.25f);
this.spriteAtlasTexture = spriteAtlasTexture;
this.missingSprite = missingSprite;
sprites.values().forEach(root::add);
}
@ -74,9 +72,14 @@ public class SpriteFinderImpl implements SpriteFinder {
final float midU;
final float midV;
final float cellRadius;
@Nullable
Object lowLow = null;
@Nullable
Object lowHigh = null;
@Nullable
Object highLow = null;
@Nullable
Object highHigh = null;
Node(float midU, float midV, float radius) {
@ -105,36 +108,37 @@ public class SpriteFinderImpl implements SpriteFinder {
final boolean highV = sprite.getMaxV() > midV + EPS;
if (lowU && lowV) {
addInner(sprite, lowLow, -1, -1, q -> lowLow = q);
lowLow = addInner(sprite, lowLow, -1, -1);
}
if (lowU && highV) {
addInner(sprite, lowHigh, -1, 1, q -> lowHigh = q);
lowHigh = addInner(sprite, lowHigh, -1, 1);
}
if (highU && lowV) {
addInner(sprite, highLow, 1, -1, q -> highLow = q);
highLow = addInner(sprite, highLow, 1, -1);
}
if (highU && highV) {
addInner(sprite, highHigh, 1, 1, q -> highHigh = q);
highHigh = addInner(sprite, highHigh, 1, 1);
}
}
private void addInner(Sprite sprite, Object quadrant, int uStep, int vStep, Consumer<Object> setter) {
private Object addInner(Sprite sprite, @Nullable Object quadrant, int uStep, int vStep) {
if (quadrant == null) {
setter.accept(sprite);
} else if (quadrant instanceof Node) {
((Node) quadrant).add(sprite);
return sprite;
} else if (quadrant instanceof Node node) {
node.add(sprite);
return quadrant;
} else {
Node n = new Node(midU + cellRadius * uStep, midV + cellRadius * vStep, cellRadius * 0.5f);
if (quadrant instanceof Sprite) {
n.add((Sprite) quadrant);
if (quadrant instanceof Sprite prevSprite) {
n.add(prevSprite);
}
n.add(sprite);
setter.accept(n);
return n;
}
}
@ -146,22 +150,14 @@ public class SpriteFinderImpl implements SpriteFinder {
}
}
private Sprite findInner(Object quadrant, float u, float v) {
if (quadrant instanceof Sprite) {
return (Sprite) quadrant;
} else if (quadrant instanceof Node) {
return ((Node) quadrant).find(u, v);
private Sprite findInner(@Nullable Object quadrant, float u, float v) {
if (quadrant instanceof Node node) {
return node.find(u, v);
} else if (quadrant instanceof Sprite sprite) {
return sprite;
} else {
return spriteAtlasTexture.getSprite(MissingSprite.getMissingSpriteId());
return missingSprite;
}
}
}
public static SpriteFinderImpl get(SpriteAtlasTexture atlas) {
return ((SpriteFinderAccess) atlas).fabric_spriteFinder();
}
public interface SpriteFinderAccess {
SpriteFinderImpl fabric_spriteFinder();
}
}

View file

@ -0,0 +1,26 @@
/*
* 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 org.jetbrains.annotations.Nullable;
import net.fabricmc.fabric.api.renderer.v1.model.SpriteFinder;
public interface StitchResultExtension {
@Nullable
SpriteFinder fabric_spriteFinderNullable();
}

View file

@ -28,19 +28,43 @@ import net.minecraft.client.render.model.BlockModelPart;
import net.minecraft.client.render.model.GeometryBakedModel;
import net.minecraft.util.math.Direction;
import net.fabricmc.fabric.api.renderer.v1.Renderer;
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.mesh.QuadEmitter;
import net.fabricmc.fabric.api.renderer.v1.model.MeshBakedGeometry;
import net.fabricmc.fabric.api.util.TriState;
@Mixin(GeometryBakedModel.class)
abstract class GeometryBakedModelMixin implements BlockModelPart {
@Shadow
@Final
private BakedGeometry quads;
@Shadow
@Final
private boolean useAmbientOcclusion;
@Override
public void emitQuads(QuadEmitter emitter, Predicate<@Nullable Direction> cullTest) {
if (quads instanceof MeshBakedGeometry meshBakedGeometry) {
meshBakedGeometry.getMesh().outputTo(emitter);
if (useAmbientOcclusion) {
meshBakedGeometry.getMesh().outputTo(emitter);
} else {
MaterialFinder materialFinder = Renderer.get().materialFinder();
emitter.pushTransform(quad -> {
RenderMaterial material = quad.material();
if (material.ambientOcclusion() == TriState.DEFAULT) {
materialFinder.copyFrom(quad.material());
materialFinder.ambientOcclusion(TriState.FALSE);
quad.material(materialFinder.find());
}
return true;
});
meshBakedGeometry.getMesh().outputTo(emitter);
emitter.popTransform();
}
} else {
BlockModelPart.super.emitQuads(emitter, cullTest);
}

View file

@ -57,7 +57,7 @@ abstract class FallingBlockEntityRendererMixin extends EntityRenderer<FallingBlo
BlockStateModel model = blockRenderManager.getModel(blockState);
long seed = blockState.getRenderingSeed(renderState.fallingBlockPos);
blockRenderManager.getModelRenderer().render(renderState, model, blockState, renderState.currentPos, matrixStack, layer -> vertexConsumers.getBuffer(RenderLayerHelper.getMovingBlockLayer(layer)), false, seed, OverlayTexture.DEFAULT_UV);
blockRenderManager.getModelRenderer().render(renderState, model, blockState, renderState.currentPos, matrixStack, RenderLayerHelper.movingDelegate(vertexConsumers), false, seed, OverlayTexture.DEFAULT_UV);
matrixStack.pop();
super.render(renderState, matrixStack, vertexConsumers, light);

View file

@ -40,6 +40,6 @@ abstract class PistonBlockEntityRendererMixin {
// Support multi-render layer models.
@Overwrite
private void renderModel(BlockPos pos, BlockState state, MatrixStack matrices, VertexConsumerProvider vertexConsumers, World world, boolean cull, int overlay) {
manager.getModelRenderer().render(world, manager.getModel(state), state, pos, matrices, layer -> vertexConsumers.getBuffer(RenderLayerHelper.getMovingBlockLayer(layer)), cull, state.getRenderingSeed(pos), overlay);
manager.getModelRenderer().render(world, manager.getModel(state), state, pos, matrices, RenderLayerHelper.movingDelegate(vertexConsumers), cull, state.getRenderingSeed(pos), overlay);
}
}

View file

@ -52,7 +52,7 @@ abstract class SnowGolemPumpkinFeatureRendererMixin {
FabricBlockModelRenderer.render(entry, layer -> vertexConsumer, model, 1, 1, 1, light, overlay, EmptyBlockRenderView.INSTANCE, BlockPos.ORIGIN, blockState);
} else {
// Support multi-render layer models, fix tinted quads being rendered completely black, and provide the BlockState as context.
FabricBlockModelRenderer.render(entry, layer -> vertexConsumers.getBuffer(RenderLayerHelper.getEntityBlockLayer(layer)), model, 1, 1, 1, light, overlay, EmptyBlockRenderView.INSTANCE, BlockPos.ORIGIN, blockState);
FabricBlockModelRenderer.render(entry, RenderLayerHelper.entityDelegate(vertexConsumers), model, 1, 1, 1, light, overlay, EmptyBlockRenderView.INSTANCE, BlockPos.ORIGIN, blockState);
}
}
}

View file

@ -29,7 +29,6 @@ import net.minecraft.client.render.item.model.BasicItemModel;
import net.minecraft.client.render.item.model.ItemModel;
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
import net.fabricmc.fabric.api.renderer.v1.render.FabricLayerRenderState;
import net.fabricmc.fabric.impl.renderer.BasicItemModelExtension;
@Mixin(BasicItemModel.class)
@ -41,7 +40,7 @@ abstract class BasicItemModelMixin implements ItemModel, BasicItemModelExtension
@Inject(method = "update", at = @At("RETURN"))
private void onReturnUpdate(CallbackInfo ci, @Local ItemRenderState.LayerRenderState layer) {
if (mesh != null) {
mesh.outputTo(((FabricLayerRenderState) layer).emitter());
mesh.outputTo(layer.emitter());
}
}

View file

@ -0,0 +1,41 @@
/*
* 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.renderer.client.sprite;
import java.util.Map;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import net.minecraft.client.render.model.ErrorCollectingSpriteGetter;
import net.minecraft.client.render.model.SpriteAtlasManager;
import net.minecraft.util.Identifier;
import net.fabricmc.fabric.api.renderer.v1.model.SpriteFinder;
@Mixin(targets = "net/minecraft/client/render/model/BakedModelManager$1")
abstract class BakedModelManager1Mixin implements ErrorCollectingSpriteGetter {
@Shadow
@Final
Map<Identifier, SpriteAtlasManager.AtlasPreparation> field_55477;
@Override
public SpriteFinder spriteFinder(Identifier atlasId) {
return field_55477.get(atlasId).spriteFinder();
}
}

View file

@ -0,0 +1,27 @@
/*
* 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.renderer.client.sprite;
import org.spongepowered.asm.mixin.Mixin;
import net.minecraft.client.render.model.ErrorCollectingSpriteGetter;
import net.fabricmc.fabric.api.renderer.v1.sprite.FabricErrorCollectingSpriteGetter;
@Mixin(ErrorCollectingSpriteGetter.class)
interface ErrorCollectingSpriteGetterMixin extends FabricErrorCollectingSpriteGetter {
}

View file

@ -0,0 +1,39 @@
/*
* 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.renderer.client.sprite;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import net.minecraft.client.render.model.SpriteAtlasManager;
import net.minecraft.client.texture.SpriteLoader;
import net.fabricmc.fabric.api.renderer.v1.model.SpriteFinder;
import net.fabricmc.fabric.api.renderer.v1.sprite.FabricAtlasPreparation;
@Mixin(SpriteAtlasManager.AtlasPreparation.class)
abstract class SpriteAtlasManagerAtlasPreparationMixin implements FabricAtlasPreparation {
@Shadow
@Final
private SpriteLoader.StitchResult stitchResult;
@Override
public SpriteFinder spriteFinder() {
return stitchResult.spriteFinder();
}
}

View file

@ -14,13 +14,15 @@
* limitations under the License.
*/
package net.fabricmc.fabric.mixin.renderer.client;
package net.fabricmc.fabric.mixin.renderer.client.sprite;
import java.util.Map;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@ -30,28 +32,48 @@ import net.minecraft.client.texture.SpriteAtlasTexture;
import net.minecraft.client.texture.SpriteLoader;
import net.minecraft.util.Identifier;
import net.fabricmc.fabric.api.renderer.v1.model.SpriteFinder;
import net.fabricmc.fabric.api.renderer.v1.sprite.FabricSpriteAtlasTexture;
import net.fabricmc.fabric.impl.renderer.SpriteFinderImpl;
import net.fabricmc.fabric.impl.renderer.StitchResultExtension;
@Mixin(SpriteAtlasTexture.class)
public class SpriteAtlasTextureMixin implements SpriteFinderImpl.SpriteFinderAccess {
@Final
abstract class SpriteAtlasTextureMixin implements FabricSpriteAtlasTexture {
@Shadow
@Final
private Map<Identifier, Sprite> sprites;
@Shadow
@Final
@Nullable
private Sprite missingSprite;
private SpriteFinderImpl fabric_spriteFinder = null;
@Unique
@Nullable
private volatile SpriteFinder spriteFinder;
@Inject(at = @At("RETURN"), method = "upload")
private void uploadHook(SpriteLoader.StitchResult arg, CallbackInfo ci) {
fabric_spriteFinder = null;
private void uploadHook(SpriteLoader.StitchResult stitchResult, CallbackInfo ci) {
// Clear this atlas' old finder. If the finder was already initialized in the stitch result, reuse it for this
// atlas.
spriteFinder = ((StitchResultExtension) (Object) stitchResult).fabric_spriteFinderNullable();
}
@Override
public SpriteFinderImpl fabric_spriteFinder() {
SpriteFinderImpl result = fabric_spriteFinder;
public SpriteFinder spriteFinder() {
SpriteFinder result = spriteFinder;
if (result == null) {
result = new SpriteFinderImpl(sprites, (SpriteAtlasTexture) (Object) this);
fabric_spriteFinder = result;
synchronized (this) {
result = spriteFinder;
if (result == null) {
if (missingSprite == null) {
throw new IllegalStateException("Tried to create sprite finder, but atlas is not initialized");
}
spriteFinder = result = new SpriteFinderImpl(sprites, missingSprite);
}
}
}
return result;

View file

@ -0,0 +1,70 @@
/*
* 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.renderer.client.sprite;
import java.util.Map;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.texture.SpriteLoader;
import net.minecraft.util.Identifier;
import net.fabricmc.fabric.api.renderer.v1.model.SpriteFinder;
import net.fabricmc.fabric.api.renderer.v1.sprite.FabricStitchResult;
import net.fabricmc.fabric.impl.renderer.SpriteFinderImpl;
import net.fabricmc.fabric.impl.renderer.StitchResultExtension;
@Mixin(SpriteLoader.StitchResult.class)
abstract class SpriteLoaderStitchResultMixin implements FabricStitchResult, StitchResultExtension {
@Shadow
@Final
private Sprite missing;
@Shadow
@Final
private Map<Identifier, Sprite> regions;
@Unique
@Nullable
private volatile SpriteFinder spriteFinder;
@Override
public SpriteFinder spriteFinder() {
SpriteFinder result = spriteFinder;
if (result == null) {
synchronized (this) {
result = spriteFinder;
if (result == null) {
spriteFinder = result = new SpriteFinderImpl(regions, missing);
}
}
}
return result;
}
@Nullable
public SpriteFinder fabric_spriteFinderNullable() {
return spriteFinder;
}
}

View file

@ -3,7 +3,6 @@
"package": "net.fabricmc.fabric.mixin.renderer",
"compatibilityLevel": "JAVA_21",
"client": [
"client.SpriteAtlasTextureMixin",
"client.block.model.BlockModelPartMixin",
"client.block.model.BlockStateModelMixin",
"client.block.model.GeometryBakedModelMixin",
@ -24,7 +23,12 @@
"client.block.render.SnowGolemPumpkinFeatureRendererMixin",
"client.item.BasicItemModelMixin",
"client.item.BasicItemModelUnbakedMixin",
"client.item.ItemRenderStateLayerRenderStateMixin"
"client.item.ItemRenderStateLayerRenderStateMixin",
"client.sprite.BakedModelManager1Mixin",
"client.sprite.ErrorCollectingSpriteGetterMixin",
"client.sprite.SpriteAtlasManagerAtlasPreparationMixin",
"client.sprite.SpriteAtlasTextureMixin",
"client.sprite.SpriteLoaderStitchResultMixin"
],
"injectors": {
"defaultRequire": 1

View file

@ -37,7 +37,11 @@
"net/minecraft/class_1087": ["net/fabricmc/fabric/api/renderer/v1/model/FabricBlockStateModel"],
"net/minecraft/class_778": ["net/fabricmc/fabric/api/renderer/v1/render/FabricBlockModelRenderer"],
"net/minecraft/class_776": ["net/fabricmc/fabric/api/renderer/v1/render/FabricBlockRenderManager"],
"net/minecraft/class_10444\u0024class_10446": ["net/fabricmc/fabric/api/renderer/v1/render/FabricLayerRenderState"]
"net/minecraft/class_10444\u0024class_10446": ["net/fabricmc/fabric/api/renderer/v1/render/FabricLayerRenderState"],
"net/minecraft/class_4724\u0024class_7774": ["net/fabricmc/fabric/api/renderer/v1/sprite/FabricAtlasPreparation"],
"net/minecraft/class_9826": ["net/fabricmc/fabric/api/renderer/v1/sprite/FabricErrorCollectingSpriteGetter"],
"net/minecraft/class_1059": ["net/fabricmc/fabric/api/renderer/v1/sprite/FabricSpriteAtlasTexture"],
"net/minecraft/class_7766\u0024class_7767": ["net/fabricmc/fabric/api/renderer/v1/sprite/FabricStitchResult"]
}
}
}

View file

@ -18,10 +18,11 @@ package net.fabricmc.fabric.test.renderer;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.PillarBlock;
import net.minecraft.state.StateManager;
import net.minecraft.state.property.BooleanProperty;
public class OctagonalColumnBlock extends Block {
public class OctagonalColumnBlock extends PillarBlock {
public static final BooleanProperty VANILLA_SHADE_MODE = BooleanProperty.of("vanilla_shade_mode");
public OctagonalColumnBlock(Settings settings) {
@ -31,6 +32,7 @@ public class OctagonalColumnBlock extends Block {
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder);
builder.add(VANILLA_SHADE_MODE);
}
}

View file

@ -23,6 +23,7 @@ import net.minecraft.client.render.model.ModelBakeSettings;
import net.minecraft.client.render.model.ModelTextures;
import net.minecraft.client.render.model.SimpleModel;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.texture.SpriteAtlasTexture;
import net.minecraft.util.math.Direction;
import net.fabricmc.fabric.api.renderer.v1.Renderer;
@ -32,12 +33,14 @@ import net.fabricmc.fabric.api.renderer.v1.mesh.MutableMesh;
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
import net.fabricmc.fabric.api.renderer.v1.model.MeshBakedGeometry;
import net.fabricmc.fabric.api.renderer.v1.model.ModelBakeSettingsHelper;
public record FrameGeometry(boolean emissive) implements Geometry {
@Override
public BakedGeometry bake(ModelTextures textures, Baker baker, ModelBakeSettings settings, SimpleModel model) {
MutableMesh builder = Renderer.get().mutableMesh();
QuadEmitter emitter = builder.emitter();
emitter.pushTransform(ModelBakeSettingsHelper.asQuadTransform(settings, baker.getSpriteGetter().spriteFinder(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE)));
MaterialFinder finder = Renderer.get().materialFinder();
RenderMaterial material = finder.emissive(emissive).find();

View file

@ -23,6 +23,7 @@ import net.minecraft.client.render.model.ModelBakeSettings;
import net.minecraft.client.render.model.ModelTextures;
import net.minecraft.client.render.model.SimpleModel;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.texture.SpriteAtlasTexture;
import net.minecraft.util.math.Direction;
import net.fabricmc.fabric.api.renderer.v1.Renderer;
@ -34,6 +35,7 @@ import net.fabricmc.fabric.api.renderer.v1.mesh.MutableMesh;
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
import net.fabricmc.fabric.api.renderer.v1.model.MeshBakedGeometry;
import net.fabricmc.fabric.api.renderer.v1.model.ModelBakeSettingsHelper;
public record OctagonalColumnGeometry(ShadeMode shadeMode) implements Geometry {
// (B - A) is the side length of a regular octagon that fits in a unit square.
@ -45,6 +47,7 @@ public record OctagonalColumnGeometry(ShadeMode shadeMode) implements Geometry {
public BakedGeometry bake(ModelTextures textures, Baker baker, ModelBakeSettings settings, SimpleModel model) {
MutableMesh builder = Renderer.get().mutableMesh();
QuadEmitter emitter = builder.emitter();
emitter.pushTransform(ModelBakeSettingsHelper.asQuadTransform(settings, baker.getSpriteGetter().spriteFinder(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE)));
MaterialFinder finder = Renderer.get().materialFinder();
RenderMaterial glintMaterial = finder.glintMode(GlintMode.STANDARD).shadeMode(shadeMode).find();

View file

@ -23,6 +23,7 @@ import net.minecraft.client.render.model.ModelBakeSettings;
import net.minecraft.client.render.model.ModelTextures;
import net.minecraft.client.render.model.SimpleModel;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.texture.SpriteAtlasTexture;
import net.minecraft.util.math.Direction;
import net.fabricmc.fabric.api.renderer.v1.Renderer;
@ -30,12 +31,14 @@ import net.fabricmc.fabric.api.renderer.v1.mesh.MutableMesh;
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
import net.fabricmc.fabric.api.renderer.v1.model.MeshBakedGeometry;
import net.fabricmc.fabric.api.renderer.v1.model.ModelBakeSettingsHelper;
public record PillarGeometry() implements Geometry {
@Override
public BakedGeometry bake(ModelTextures textures, Baker baker, ModelBakeSettings settings, SimpleModel model) {
MutableMesh builder = Renderer.get().mutableMesh();
QuadEmitter emitter = builder.emitter();
emitter.pushTransform(ModelBakeSettingsHelper.asQuadTransform(settings, baker.getSpriteGetter().spriteFinder(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE)));
Sprite sprite = baker.getSpriteGetter().get(textures.get("pillar"), model);

View file

@ -1,6 +1,28 @@
{
"variants": {
"vanilla_shade_mode=false": { "model": "fabric-renderer-api-v1-testmod:octagonal_column_enhanced" },
"vanilla_shade_mode=true": { "model": "fabric-renderer-api-v1-testmod:octagonal_column_vanilla" }
"axis=x,vanilla_shade_mode=false": {
"model": "fabric-renderer-api-v1-testmod:octagonal_column_enhanced",
"x": 90,
"y": 90
},
"axis=x,vanilla_shade_mode=true": {
"model": "fabric-renderer-api-v1-testmod:octagonal_column_vanilla",
"x": 90,
"y": 90
},
"axis=y,vanilla_shade_mode=false": {
"model": "fabric-renderer-api-v1-testmod:octagonal_column_enhanced"
},
"axis=y,vanilla_shade_mode=true": {
"model": "fabric-renderer-api-v1-testmod:octagonal_column_vanilla"
},
"axis=z,vanilla_shade_mode=false": {
"model": "fabric-renderer-api-v1-testmod:octagonal_column_enhanced",
"x": 90
},
"axis=z,vanilla_shade_mode=true": {
"model": "fabric-renderer-api-v1-testmod:octagonal_column_vanilla",
"x": 90
}
}
}

View file

@ -108,7 +108,7 @@ public class IndigoRenderer implements Renderer {
float red = (tint >> 16 & 255) / 255.0F;
float green = (tint >> 8 & 255) / 255.0F;
float blue = (tint & 255) / 255.0F;
FabricBlockModelRenderer.render(matrices.peek(), layer -> vertexConsumers.getBuffer(RenderLayerHelper.getEntityBlockLayer(layer)), model, red, green, blue, light, overlay, blockView, pos, state);
FabricBlockModelRenderer.render(matrices.peek(), RenderLayerHelper.entityDelegate(vertexConsumers), model, red, green, blue, light, overlay, blockView, pos, state);
((BlockRenderManagerAccessor) renderManager).getBlockEntityModelsGetter().get().render(state.getBlock(), ItemDisplayContext.NONE, matrices, vertexConsumers, light, overlay);
}
}

View file

@ -33,6 +33,7 @@ import net.minecraft.util.math.MathHelper;
import net.minecraft.world.BlockRenderView;
import net.fabricmc.fabric.impl.client.indigo.Indigo;
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.GeometryHelper;
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat;
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.QuadViewImpl;
import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderInfo;
@ -151,12 +152,18 @@ public class AoCalculator {
private void calcEnhanced(QuadViewImpl quad) {
switch (quad.geometryFlags()) {
case AXIS_ALIGNED_FLAG | CUBIC_FLAG | LIGHT_FACE_FLAG:
case AXIS_ALIGNED_FLAG | LIGHT_FACE_FLAG:
case LIGHT_FACE_FLAG | AXIS_ALIGNED_FLAG | CUBIC_FLAG:
vanillaFullFace(quad, quad.lightFace(), true, quad.hasShade());
break;
case LIGHT_FACE_FLAG | AXIS_ALIGNED_FLAG:
vanillaPartialFace(quad, quad.lightFace(), true, quad.hasShade());
break;
case AXIS_ALIGNED_FLAG | CUBIC_FLAG:
blendedFullFace(quad, quad.lightFace(), quad.hasShade());
break;
case AXIS_ALIGNED_FLAG:
blendedPartialFace(quad, quad.lightFace(), quad.hasShade());
break;
@ -167,24 +174,31 @@ public class AoCalculator {
}
}
private void vanillaFullFace(QuadViewImpl quad, Direction lightFace, boolean isOnLightFace, boolean shade) {
computeFace(lightFace, isOnLightFace, shade).toArray(ao, light, AoFace.get(lightFace).vertexMap);
private void fullFace(QuadViewImpl quad, Direction lightFace, AoFaceData faceData) {
faceData.toArrays(ao, light, AoFace.get(lightFace).vertexMap, GeometryHelper.firstCubicVertex(quad));
}
private void vanillaPartialFace(QuadViewImpl quad, Direction lightFace, boolean isOnLightFace, boolean shade) {
AoFaceData faceData = computeFace(lightFace, isOnLightFace, shade);
private void partialFace(QuadViewImpl quad, Direction lightFace, AoFaceData faceData) {
final AoFace aoFace = AoFace.get(lightFace);
final float[] w = this.w;
for (int i = 0; i < 4; i++) {
aoFace.computeCornerWeights(quad, i, w);
light[i] = faceData.weightedCombinedLight(w);
ao[i] = faceData.weigtedAo(w);
ao[i] = faceData.weightedAo(w);
}
}
/** used in {@link #blendedInsetFace(QuadViewImpl quad, int vertexIndex, Direction lightFace, boolean shade)} as return variable to avoid new allocation. */
AoFaceData tmpFace = new AoFaceData();
private void vanillaFullFace(QuadViewImpl quad, Direction lightFace, boolean isOnLightFace, boolean shade) {
fullFace(quad, lightFace, computeFace(lightFace, isOnLightFace, shade));
}
private void vanillaPartialFace(QuadViewImpl quad, Direction lightFace, boolean isOnLightFace, boolean shade) {
partialFace(quad, lightFace, computeFace(lightFace, isOnLightFace, shade));
}
/** Used in {@link #blendedInsetFace(QuadViewImpl, int, Direction, boolean)} as return variable to avoid new allocation. */
private final AoFaceData tmpFace = new AoFaceData();
/** Returns linearly interpolated blend of outer and inner face based on depth of vertex in face. */
private AoFaceData blendedInsetFace(QuadViewImpl quad, int vertexIndex, Direction lightFace, boolean shade) {
@ -194,7 +208,7 @@ public class AoCalculator {
}
/**
* Like {@link #blendedInsetFace(QuadViewImpl quad, int vertexIndex, Direction lightFace, boolean shade)} but optimizes if depth is 0 or 1.
* Like {@link #blendedInsetFace(QuadViewImpl, int, Direction, boolean)} but optimizes if depth is 0 or 1.
* Used for irregular faces when depth varies by vertex to avoid unneeded interpolation.
*/
private AoFaceData gatherInsetFace(QuadViewImpl quad, int vertexIndex, Direction lightFace, boolean shade) {
@ -210,15 +224,12 @@ public class AoCalculator {
}
}
private void blendedPartialFace(QuadViewImpl quad, Direction lightFace, boolean shade) {
AoFaceData faceData = blendedInsetFace(quad, 0, lightFace, shade);
final AoFace aoFace = AoFace.get(lightFace);
private void blendedFullFace(QuadViewImpl quad, Direction lightFace, boolean shade) {
fullFace(quad, lightFace, blendedInsetFace(quad, 0, lightFace, shade));
}
for (int i = 0; i < 4; i++) {
aoFace.computeCornerWeights(quad, i, w);
light[i] = faceData.weightedCombinedLight(w);
ao[i] = faceData.weigtedAo(w);
}
private void blendedPartialFace(QuadViewImpl quad, Direction lightFace, boolean shade) {
partialFace(quad, lightFace, blendedInsetFace(quad, 0, lightFace, shade));
}
/** used exclusively in irregular face to avoid new heap allocations each call. */
@ -243,9 +254,9 @@ public class AoCalculator {
final AoFaceData fd = gatherInsetFace(quad, i, face, shade);
AoFace.get(face).computeCornerWeights(quad, i, w);
final float n = x * x;
final float a = fd.weigtedAo(w);
final int s = fd.weigtedSkyLight(w);
final int b = fd.weigtedBlockLight(w);
final float a = fd.weightedAo(w);
final int s = fd.weightedSkyLight(w);
final int b = fd.weightedBlockLight(w);
ao += n * a;
sky += n * s;
block += n * b;
@ -261,9 +272,9 @@ public class AoCalculator {
final AoFaceData fd = gatherInsetFace(quad, i, face, shade);
AoFace.get(face).computeCornerWeights(quad, i, w);
final float n = y * y;
final float a = fd.weigtedAo(w);
final int s = fd.weigtedSkyLight(w);
final int b = fd.weigtedBlockLight(w);
final float a = fd.weightedAo(w);
final int s = fd.weightedSkyLight(w);
final int b = fd.weightedBlockLight(w);
ao += n * a;
sky += n * s;
block += n * b;
@ -279,9 +290,9 @@ public class AoCalculator {
final AoFaceData fd = gatherInsetFace(quad, i, face, shade);
AoFace.get(face).computeCornerWeights(quad, i, w);
final float n = z * z;
final float a = fd.weigtedAo(w);
final int s = fd.weigtedSkyLight(w);
final int b = fd.weigtedBlockLight(w);
final float a = fd.weightedAo(w);
final int s = fd.weightedSkyLight(w);
final int b = fd.weightedBlockLight(w);
ao += n * a;
sky += n * s;
block += n * b;

View file

@ -127,8 +127,9 @@ enum AoFace {
final Direction[] neighbors;
/**
* Vanilla models with cubic quads have vertices in a certain order, which allows
* us to map them using a lookup.
* Cubic quads have a vertex in each corner, which allows us to skip computing
* weights and map values to vertices directly. Note that vanilla assumes a
* certain vertex order, but we detect it and offset the map accordingly.
*/
final int[] vertexMap;

View file

@ -54,31 +54,37 @@ class AoFaceData {
this.s3 = (l3 >>> 16) & 0xFFFF;
}
int weigtedBlockLight(float[] w) {
int weightedBlockLight(float[] w) {
return (int) (b0 * w[0] + b1 * w[1] + b2 * w[2] + b3 * w[3]) & 0xFF;
}
int weigtedSkyLight(float[] w) {
int weightedSkyLight(float[] w) {
return (int) (s0 * w[0] + s1 * w[1] + s2 * w[2] + s3 * w[3]) & 0xFF;
}
int weightedCombinedLight(float[] w) {
return weigtedSkyLight(w) << 16 | weigtedBlockLight(w);
return weightedSkyLight(w) << 16 | weightedBlockLight(w);
}
float weigtedAo(float[] w) {
float weightedAo(float[] w) {
return a0 * w[0] + a1 * w[1] + a2 * w[2] + a3 * w[3];
}
void toArray(float[] aOut, int[] bOut, int[] vertexMap) {
aOut[vertexMap[0]] = a0;
aOut[vertexMap[1]] = a1;
aOut[vertexMap[2]] = a2;
aOut[vertexMap[3]] = a3;
bOut[vertexMap[0]] = s0 << 16 | b0;
bOut[vertexMap[1]] = s1 << 16 | b1;
bOut[vertexMap[2]] = s2 << 16 | b2;
bOut[vertexMap[3]] = s3 << 16 | b3;
void toArrays(float[] aoOut, int[] lightOut, int[] vertexMap, int vertexOffset) {
int i0 = vertexMap[vertexOffset];
int i1 = vertexMap[(vertexOffset + 1) % 4];
int i2 = vertexMap[(vertexOffset + 2) % 4];
int i3 = vertexMap[(vertexOffset + 3) % 4];
aoOut[i0] = a0;
aoOut[i1] = a1;
aoOut[i2] = a2;
aoOut[i3] = a3;
lightOut[i0] = s0 << 16 | b0;
lightOut[i1] = s1 << 16 | b1;
lightOut[i2] = s2 << 16 | b2;
lightOut[i3] = s3 << 16 | b3;
}
static AoFaceData weightedMean(AoFaceData in0, float w0, AoFaceData in1, float w1, AoFaceData out) {

View file

@ -21,6 +21,7 @@ import static net.minecraft.util.math.MathHelper.approximatelyEquals;
import org.joml.Vector3fc;
import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.client.render.model.CubeFace;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Direction.Axis;
import net.minecraft.util.math.Direction.AxisDirection;
@ -197,24 +198,6 @@ public final class GeometryHelper {
}
}
/**
* Simple 4-way compare, doesn't handle NaN values.
*/
public static float min(float a, float b, float c, float d) {
final float x = a < b ? a : b;
final float y = c < d ? c : d;
return x < y ? x : y;
}
/**
* Simple 4-way compare, doesn't handle NaN values.
*/
public static float max(float a, float b, float c, float d) {
final float x = a > b ? a : b;
final float y = c > d ? c : d;
return x > y ? x : y;
}
/**
* @see #longestAxis(float, float, float)
*/
@ -238,4 +221,71 @@ public final class GeometryHelper {
return Math.abs(normalZ) > longest
? Axis.Z : result;
}
/**
* Returns the index of the vertex which is in the first cubic corner for the given quad's light face, according to
* the directions specified in {@link CubeFace}. Assumes that the given quad is
* {@linkplain #isQuadCubic(Direction, QuadView) cubic}. Used to make smooth lighting for cubic quads work correctly
* regardless of vertex order.
*
* <p>Because cubic quads have all vertices in different corners, the implementation only has to find which corner
* the first vertex is in based on the same criteria as {@link #isQuadCubic(Direction, QuadView)}. Then, since
* the vertex winding order is always counterclockwise, it can know which vertex is in the first corner.
*/
public static int firstCubicVertex(QuadView quad) {
final float x = quad.x(0);
final float y = quad.y(0);
final float z = quad.z(0);
/*
!a & !b -> vertex 0 is in corner 0 -> vertex 0 is in corner 0
!a & b -> vertex 0 is in corner 1 -> vertex 3 is in corner 0
a & b -> vertex 0 is in corner 2 -> vertex 2 is in corner 0
a & !b -> vertex 0 is in corner 3 -> vertex 1 is in corner 0
*/
final boolean a;
final boolean b;
switch (quad.lightFace()) {
case DOWN -> {
a = x > EPS_MIN;
b = z < EPS_MAX;
}
case UP -> {
a = x > EPS_MIN;
b = z > EPS_MIN;
}
case NORTH -> {
a = x < EPS_MAX;
b = y < EPS_MAX;
}
case SOUTH -> {
a = x > EPS_MIN;
b = y < EPS_MAX;
}
case WEST -> {
a = z > EPS_MIN;
b = y < EPS_MAX;
}
case EAST -> {
a = z < EPS_MAX;
b = y < EPS_MAX;
}
default -> {
return 0;
}
}
int result = 0;
if (a) {
result ^= 1;
}
if (b) {
result ^= 3;
}
return result;
}
}

View file

@ -32,7 +32,6 @@ import org.jetbrains.annotations.Nullable;
import net.minecraft.client.render.LightmapTextureManager;
import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.client.texture.Sprite;
import net.minecraft.util.math.Direction;
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
@ -42,7 +41,6 @@ import net.fabricmc.fabric.api.renderer.v1.mesh.QuadView;
import net.fabricmc.fabric.impl.client.indigo.renderer.IndigoRenderer;
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.ColorHelper;
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.NormalHelper;
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.TextureHelper;
import net.fabricmc.fabric.impl.client.indigo.renderer.material.RenderMaterialImpl;
/**
@ -121,12 +119,6 @@ public abstract class MutableQuadViewImpl extends QuadViewImpl implements QuadEm
return this;
}
@Override
public final MutableQuadViewImpl spriteBake(Sprite sprite, int bakeFlags) {
TextureHelper.bakeSprite(this, sprite, bakeFlags);
return this;
}
@Override
public final MutableQuadViewImpl lightmap(int vertexIndex, int lightmap) {
data[baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_LIGHTMAP] = lightmap;

View file

@ -89,8 +89,8 @@ public class ItemRenderContext extends AbstractRenderContext {
final int vanillaQuadCount = vanillaQuads.size();
for (int j = 0; j < vanillaQuadCount; j++) {
final BakedQuad q = vanillaQuads.get(j);
for (int i = 0; i < vanillaQuadCount; i++) {
final BakedQuad q = vanillaQuads.get(i);
emitter.fromVanilla(q, IndigoRenderer.STANDARD_MATERIAL, null);
emitter.emit();
}

View file

@ -54,6 +54,6 @@ abstract class BlockRenderManagerMixin {
@Redirect(method = "renderBlockAsEntity(Lnet/minecraft/block/BlockState;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;II)V", at = @At(value = "INVOKE", target = "net/minecraft/client/render/block/BlockModelRenderer.render(Lnet/minecraft/client/util/math/MatrixStack$Entry;Lnet/minecraft/client/render/VertexConsumer;Lnet/minecraft/client/render/model/BlockStateModel;FFFII)V"))
private void renderProxy(MatrixStack.Entry entry, VertexConsumer vertexConsumer, BlockStateModel model, float red, float green, float blue, int light, int overlay, BlockState state, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light1, int overlay1) {
FabricBlockModelRenderer.render(entry, layer -> vertexConsumers.getBuffer(RenderLayerHelper.getEntityBlockLayer(layer)), model, red, green, blue, light, overlay, EmptyBlockRenderView.INSTANCE, BlockPos.ORIGIN, state);
FabricBlockModelRenderer.render(entry, RenderLayerHelper.entityDelegate(vertexConsumers), model, red, green, blue, light, overlay, EmptyBlockRenderView.INSTANCE, BlockPos.ORIGIN, state);
}
}