From 5281b907560eca1a13c15e2d30e3ebf6ef3f8ce7 Mon Sep 17 00:00:00 2001 From: PepperCode1 <44146161+PepperCode1@users.noreply.github.com> Date: Sat, 31 May 2025 12:19:20 -0700 Subject: [PATCH] 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 --- .../loading/BakedModelFeatureRenderer.java | 2 +- .../fabric/api/renderer/v1/Renderer.java | 2 +- .../api/renderer/v1/mesh/MutableQuadView.java | 31 ++- .../api/renderer/v1/mesh/QuadEmitter.java | 5 +- .../v1/model/FabricBlockModelPart.java | 11 +- .../v1/model/FabricBlockStateModel.java | 2 +- .../renderer/v1/model/MeshBakedGeometry.java | 13 +- .../v1/model/ModelBakeSettingsHelper.java | 234 ++++++++++++++++++ .../api/renderer/v1/model/ModelHelper.java | 12 +- .../api/renderer/v1/model/SpriteFinder.java | 49 ++-- .../v1/render/FabricBlockModelRenderer.java | 2 +- .../v1/render/FabricBlockRenderManager.java | 2 +- .../v1/render/FabricLayerRenderState.java | 3 +- .../renderer/v1/render/RenderLayerHelper.java | 17 ++ .../v1/sprite/FabricAtlasPreparation.java | 33 +++ .../FabricErrorCollectingSpriteGetter.java | 34 +++ .../v1/sprite/FabricSpriteAtlasTexture.java | 44 ++++ .../v1/sprite/FabricStitchResult.java | 33 +++ .../fabric/impl/renderer/QuadSpriteBaker.java | 17 +- .../impl/renderer/SpriteFinderImpl.java | 58 ++--- .../impl/renderer/StitchResultExtension.java | 26 ++ .../block/model/GeometryBakedModelMixin.java | 26 +- .../FallingBlockEntityRendererMixin.java | 2 +- .../PistonBlockEntityRendererMixin.java | 2 +- .../SnowGolemPumpkinFeatureRendererMixin.java | 2 +- .../client/item/BasicItemModelMixin.java | 3 +- .../sprite/BakedModelManager1Mixin.java | 41 +++ .../ErrorCollectingSpriteGetterMixin.java | 27 ++ ...riteAtlasManagerAtlasPreparationMixin.java | 39 +++ .../{ => sprite}/SpriteAtlasTextureMixin.java | 42 +++- .../sprite/SpriteLoaderStitchResultMixin.java | 70 ++++++ .../fabric-renderer-api-v1.mixins.json | 8 +- .../src/client/resources/fabric.mod.json | 6 +- .../test/renderer/OctagonalColumnBlock.java | 4 +- .../test/renderer/client/FrameGeometry.java | 3 + .../client/OctagonalColumnGeometry.java | 3 + .../test/renderer/client/PillarGeometry.java | 3 + .../blockstates/octagonal_column.json | 26 +- .../indigo/renderer/IndigoRenderer.java | 2 +- .../indigo/renderer/aocalc/AoCalculator.java | 65 +++-- .../client/indigo/renderer/aocalc/AoFace.java | 5 +- .../indigo/renderer/aocalc/AoFaceData.java | 32 ++- .../renderer/helper/GeometryHelper.java | 86 +++++-- .../renderer/mesh/MutableQuadViewImpl.java | 8 - .../renderer/render/ItemRenderContext.java | 4 +- .../renderer/BlockRenderManagerMixin.java | 2 +- 46 files changed, 952 insertions(+), 189 deletions(-) create mode 100644 fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/model/ModelBakeSettingsHelper.java create mode 100644 fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/sprite/FabricAtlasPreparation.java create mode 100644 fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/sprite/FabricErrorCollectingSpriteGetter.java create mode 100644 fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/sprite/FabricSpriteAtlasTexture.java create mode 100644 fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/sprite/FabricStitchResult.java rename fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/helper/TextureHelper.java => fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/impl/renderer/QuadSpriteBaker.java (93%) create mode 100644 fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/impl/renderer/StitchResultExtension.java create mode 100644 fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/sprite/BakedModelManager1Mixin.java create mode 100644 fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/sprite/ErrorCollectingSpriteGetterMixin.java create mode 100644 fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/sprite/SpriteAtlasManagerAtlasPreparationMixin.java rename fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/{ => sprite}/SpriteAtlasTextureMixin.java (52%) create mode 100644 fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/sprite/SpriteLoaderStitchResultMixin.java diff --git a/fabric-model-loading-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/model/loading/BakedModelFeatureRenderer.java b/fabric-model-loading-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/model/loading/BakedModelFeatureRenderer.java index d7dd6b92a9..8e8cdcd324 100644 --- a/fabric-model-loading-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/model/loading/BakedModelFeatureRenderer.java +++ b/fabric-model-loading-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/model/loading/BakedModelFeatureRenderer.java @@ -53,7 +53,7 @@ public class BakedModelFeatureRenderer 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(); } } diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/Renderer.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/Renderer.java index f7095268c5..6774093f4b 100644 --- a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/Renderer.java +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/Renderer.java @@ -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); diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/mesh/MutableQuadView.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/mesh/MutableQuadView.java index f9aba41ed8..785c54ea41 100644 --- a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/mesh/MutableQuadView.java +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/mesh/MutableQuadView.java @@ -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)}. * - *

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. + *

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 diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/mesh/QuadEmitter.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/mesh/QuadEmitter.java index f0d7a1a284..a5bbe65b4c 100644 --- a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/mesh/QuadEmitter.java +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/mesh/QuadEmitter.java @@ -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); diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/model/FabricBlockModelPart.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/model/FabricBlockModelPart.java index 483232a049..ea4bf5f583 100644 --- a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/model/FabricBlockModelPart.java +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/model/FabricBlockModelPart.java @@ -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. 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. 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. 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. * *

This method mainly exists for convenience when interacting with parts implemented and produced by vanilla * code. Custom models should generally override diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/model/FabricBlockStateModel.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/model/FabricBlockStateModel.java index 187e17c2dd..32cbd2dd41 100644 --- a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/model/FabricBlockStateModel.java +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/model/FabricBlockStateModel.java @@ -41,7 +41,7 @@ import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; * *

Implementors should have a look at {@link ModelHelper} as it contains many useful functions. * - *

Note: This interface is automatically implemented on all block state models via Mixin and interface injection. + *

Note: This interface is automatically implemented on {@link BlockStateModel} via Mixin and interface injection. */ public interface FabricBlockStateModel { /** diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/model/MeshBakedGeometry.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/model/MeshBakedGeometry.java index 596bbf5a88..35a451a127 100644 --- a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/model/MeshBakedGeometry.java +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/model/MeshBakedGeometry.java @@ -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. * *

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; diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/model/ModelBakeSettingsHelper.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/model/ModelBakeSettingsHelper.java new file mode 100644 index 0000000000..bf4efd9c7c --- /dev/null +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/model/ModelBakeSettingsHelper.java @@ -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 faceTransformations = new EnumMap<>(Direction.class); + Map 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 faceTransformations = new EnumMap<>(Direction.class); + Map 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. + * + *

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; + }; + } +} diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/model/ModelHelper.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/model/ModelHelper.java index e0639cac20..f74f6820be 100644 --- a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/model/ModelHelper.java +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/model/ModelHelper.java @@ -71,7 +71,7 @@ public final class ModelHelper { *

Retrieves sprites from the block texture atlas via {@link SpriteFinder}. */ public static List[] 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[] 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[] result = new List[7]; diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/model/SpriteFinder.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/model/SpriteFinder.java index 9c0c0d12e9..4ed0b54e4f 100644 --- a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/model/SpriteFinder.java +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/model/SpriteFinder.java @@ -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. + * + *

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)}. + * + *

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. */ diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/render/FabricBlockModelRenderer.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/render/FabricBlockModelRenderer.java index 4fc3536201..e27058f53c 100644 --- a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/render/FabricBlockModelRenderer.java +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/render/FabricBlockModelRenderer.java @@ -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 { /** diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/render/FabricBlockRenderManager.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/render/FabricBlockRenderManager.java index e3cbf5bffb..126456594e 100644 --- a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/render/FabricBlockRenderManager.java +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/render/FabricBlockRenderManager.java @@ -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 { /** diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/render/FabricLayerRenderState.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/render/FabricLayerRenderState.java index 3012030cd3..bbfa36dc60 100644 --- a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/render/FabricLayerRenderState.java +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/render/FabricLayerRenderState.java @@ -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 { /** diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/render/RenderLayerHelper.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/render/RenderLayerHelper.java index 5ff3d4b474..60b2636bb9 100644 --- a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/render/RenderLayerHelper.java +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/render/RenderLayerHelper.java @@ -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)); + } } diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/sprite/FabricAtlasPreparation.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/sprite/FabricAtlasPreparation.java new file mode 100644 index 0000000000..8db5ce93cd --- /dev/null +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/sprite/FabricAtlasPreparation.java @@ -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(); + } +} diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/sprite/FabricErrorCollectingSpriteGetter.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/sprite/FabricErrorCollectingSpriteGetter.java new file mode 100644 index 0000000000..01c1e166ba --- /dev/null +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/sprite/FabricErrorCollectingSpriteGetter.java @@ -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(); + } +} diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/sprite/FabricSpriteAtlasTexture.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/sprite/FabricSpriteAtlasTexture.java new file mode 100644 index 0000000000..75da086eb9 --- /dev/null +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/sprite/FabricSpriteAtlasTexture.java @@ -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. + * + *

This method should not be used during a resource reload 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(); + } +} diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/sprite/FabricStitchResult.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/sprite/FabricStitchResult.java new file mode 100644 index 0000000000..76751f9c76 --- /dev/null +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/api/renderer/v1/sprite/FabricStitchResult.java @@ -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(); + } +} diff --git a/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/helper/TextureHelper.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/impl/renderer/QuadSpriteBaker.java similarity index 93% rename from fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/helper/TextureHelper.java rename to fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/impl/renderer/QuadSpriteBaker.java index 4da833f9f0..4cd12d09b4 100644 --- a/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/helper/TextureHelper.java +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/impl/renderer/QuadSpriteBaker.java @@ -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)); } } diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/impl/renderer/SpriteFinderImpl.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/impl/renderer/SpriteFinderImpl.java index 8741f2a3a2..1d462e13c0 100644 --- a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/impl/renderer/SpriteFinderImpl.java +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/impl/renderer/SpriteFinderImpl.java @@ -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 sprites, SpriteAtlasTexture spriteAtlasTexture) { + public SpriteFinderImpl(Map 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 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(); - } } diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/impl/renderer/StitchResultExtension.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/impl/renderer/StitchResultExtension.java new file mode 100644 index 0000000000..528bbd2581 --- /dev/null +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/impl/renderer/StitchResultExtension.java @@ -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(); +} diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/block/model/GeometryBakedModelMixin.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/block/model/GeometryBakedModelMixin.java index 06d999efb9..d81e94e9ca 100644 --- a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/block/model/GeometryBakedModelMixin.java +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/block/model/GeometryBakedModelMixin.java @@ -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); } diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/block/render/FallingBlockEntityRendererMixin.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/block/render/FallingBlockEntityRendererMixin.java index 293793e1aa..1cc051177c 100644 --- a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/block/render/FallingBlockEntityRendererMixin.java +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/block/render/FallingBlockEntityRendererMixin.java @@ -57,7 +57,7 @@ abstract class FallingBlockEntityRendererMixin extends EntityRenderer 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); diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/block/render/PistonBlockEntityRendererMixin.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/block/render/PistonBlockEntityRendererMixin.java index ee304f7e5d..2dcdae8195 100644 --- a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/block/render/PistonBlockEntityRendererMixin.java +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/block/render/PistonBlockEntityRendererMixin.java @@ -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); } } diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/block/render/SnowGolemPumpkinFeatureRendererMixin.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/block/render/SnowGolemPumpkinFeatureRendererMixin.java index ef7c03be9c..2af36ca19e 100644 --- a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/block/render/SnowGolemPumpkinFeatureRendererMixin.java +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/block/render/SnowGolemPumpkinFeatureRendererMixin.java @@ -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); } } } diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/item/BasicItemModelMixin.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/item/BasicItemModelMixin.java index e1c1e5a219..572c7dfeac 100644 --- a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/item/BasicItemModelMixin.java +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/item/BasicItemModelMixin.java @@ -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()); } } diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/sprite/BakedModelManager1Mixin.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/sprite/BakedModelManager1Mixin.java new file mode 100644 index 0000000000..1453f8801a --- /dev/null +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/sprite/BakedModelManager1Mixin.java @@ -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 field_55477; + + @Override + public SpriteFinder spriteFinder(Identifier atlasId) { + return field_55477.get(atlasId).spriteFinder(); + } +} diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/sprite/ErrorCollectingSpriteGetterMixin.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/sprite/ErrorCollectingSpriteGetterMixin.java new file mode 100644 index 0000000000..bbe57d72e4 --- /dev/null +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/sprite/ErrorCollectingSpriteGetterMixin.java @@ -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 { +} diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/sprite/SpriteAtlasManagerAtlasPreparationMixin.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/sprite/SpriteAtlasManagerAtlasPreparationMixin.java new file mode 100644 index 0000000000..59798aa6ed --- /dev/null +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/sprite/SpriteAtlasManagerAtlasPreparationMixin.java @@ -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(); + } +} diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/SpriteAtlasTextureMixin.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/sprite/SpriteAtlasTextureMixin.java similarity index 52% rename from fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/SpriteAtlasTextureMixin.java rename to fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/sprite/SpriteAtlasTextureMixin.java index c88ce708f6..e1d7bd9612 100644 --- a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/SpriteAtlasTextureMixin.java +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/sprite/SpriteAtlasTextureMixin.java @@ -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 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; diff --git a/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/sprite/SpriteLoaderStitchResultMixin.java b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/sprite/SpriteLoaderStitchResultMixin.java new file mode 100644 index 0000000000..9affbd496d --- /dev/null +++ b/fabric-renderer-api-v1/src/client/java/net/fabricmc/fabric/mixin/renderer/client/sprite/SpriteLoaderStitchResultMixin.java @@ -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 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; + } +} diff --git a/fabric-renderer-api-v1/src/client/resources/fabric-renderer-api-v1.mixins.json b/fabric-renderer-api-v1/src/client/resources/fabric-renderer-api-v1.mixins.json index dd62952148..61efd8989f 100644 --- a/fabric-renderer-api-v1/src/client/resources/fabric-renderer-api-v1.mixins.json +++ b/fabric-renderer-api-v1/src/client/resources/fabric-renderer-api-v1.mixins.json @@ -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 diff --git a/fabric-renderer-api-v1/src/client/resources/fabric.mod.json b/fabric-renderer-api-v1/src/client/resources/fabric.mod.json index 83fce5c53c..96121253ae 100644 --- a/fabric-renderer-api-v1/src/client/resources/fabric.mod.json +++ b/fabric-renderer-api-v1/src/client/resources/fabric.mod.json @@ -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"] } } } diff --git a/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/OctagonalColumnBlock.java b/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/OctagonalColumnBlock.java index 5a3615bba4..29f99013c7 100644 --- a/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/OctagonalColumnBlock.java +++ b/fabric-renderer-api-v1/src/testmod/java/net/fabricmc/fabric/test/renderer/OctagonalColumnBlock.java @@ -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 builder) { + super.appendProperties(builder); builder.add(VANILLA_SHADE_MODE); } } diff --git a/fabric-renderer-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/renderer/client/FrameGeometry.java b/fabric-renderer-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/renderer/client/FrameGeometry.java index cd49f6d2a1..818dddd8dd 100644 --- a/fabric-renderer-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/renderer/client/FrameGeometry.java +++ b/fabric-renderer-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/renderer/client/FrameGeometry.java @@ -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(); diff --git a/fabric-renderer-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/renderer/client/OctagonalColumnGeometry.java b/fabric-renderer-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/renderer/client/OctagonalColumnGeometry.java index a2ace08a33..80b00fc802 100644 --- a/fabric-renderer-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/renderer/client/OctagonalColumnGeometry.java +++ b/fabric-renderer-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/renderer/client/OctagonalColumnGeometry.java @@ -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(); diff --git a/fabric-renderer-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/renderer/client/PillarGeometry.java b/fabric-renderer-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/renderer/client/PillarGeometry.java index 4c42292564..42f17b25cc 100644 --- a/fabric-renderer-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/renderer/client/PillarGeometry.java +++ b/fabric-renderer-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/renderer/client/PillarGeometry.java @@ -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); diff --git a/fabric-renderer-api-v1/src/testmodClient/resources/assets/fabric-renderer-api-v1-testmod/blockstates/octagonal_column.json b/fabric-renderer-api-v1/src/testmodClient/resources/assets/fabric-renderer-api-v1-testmod/blockstates/octagonal_column.json index 3b53232d21..e0c15d5b6e 100644 --- a/fabric-renderer-api-v1/src/testmodClient/resources/assets/fabric-renderer-api-v1-testmod/blockstates/octagonal_column.json +++ b/fabric-renderer-api-v1/src/testmodClient/resources/assets/fabric-renderer-api-v1-testmod/blockstates/octagonal_column.json @@ -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 + } } } diff --git a/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/IndigoRenderer.java b/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/IndigoRenderer.java index 17e9f8078b..2e11e0a204 100644 --- a/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/IndigoRenderer.java +++ b/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/IndigoRenderer.java @@ -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); } } diff --git a/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/aocalc/AoCalculator.java b/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/aocalc/AoCalculator.java index 1b56f8f54b..4352d6dbc5 100644 --- a/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/aocalc/AoCalculator.java +++ b/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/aocalc/AoCalculator.java @@ -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; diff --git a/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/aocalc/AoFace.java b/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/aocalc/AoFace.java index 9f9aa67348..a07d018fab 100644 --- a/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/aocalc/AoFace.java +++ b/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/aocalc/AoFace.java @@ -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; diff --git a/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/aocalc/AoFaceData.java b/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/aocalc/AoFaceData.java index 06f091b370..77fdffecce 100644 --- a/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/aocalc/AoFaceData.java +++ b/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/aocalc/AoFaceData.java @@ -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) { diff --git a/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/helper/GeometryHelper.java b/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/helper/GeometryHelper.java index cc28160bae..8a45af5127 100644 --- a/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/helper/GeometryHelper.java +++ b/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/helper/GeometryHelper.java @@ -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. + * + *

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; + } } diff --git a/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/mesh/MutableQuadViewImpl.java b/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/mesh/MutableQuadViewImpl.java index d5eb7efaae..1ca5ddc1f1 100644 --- a/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/mesh/MutableQuadViewImpl.java +++ b/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/mesh/MutableQuadViewImpl.java @@ -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; diff --git a/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/ItemRenderContext.java b/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/ItemRenderContext.java index d5eba06a8a..8849657b92 100644 --- a/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/ItemRenderContext.java +++ b/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/impl/client/indigo/renderer/render/ItemRenderContext.java @@ -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(); } diff --git a/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/mixin/client/indigo/renderer/BlockRenderManagerMixin.java b/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/mixin/client/indigo/renderer/BlockRenderManagerMixin.java index 5bf3f15ade..abdb657e22 100644 --- a/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/mixin/client/indigo/renderer/BlockRenderManagerMixin.java +++ b/fabric-renderer-indigo/src/client/java/net/fabricmc/fabric/mixin/client/indigo/renderer/BlockRenderManagerMixin.java @@ -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); } }