diff --git a/README.md b/README.md index c1a1f62..a3ffab6 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Template blocks can be placed in the world, then right-clicked with a full-size block to set the textures for the template. Template blocks will inherit light and redstone values from the blocks they're given, or they can have light or redstone output added to any given block by right-clicking the template with glowstone dust or a redstone torch, respectively. -While Templates itself adds a handful of common shapes, it's not too hard for other mods to interface with Templates and add their own templatable blocks. +While Templates itself adds a handful of common shapes, it's also not too hard for other mods to interface with Templates and add their own templatable blocks. # quat was here @@ -18,7 +18,6 @@ Todo move this into the main readme section ## Todo -* `uvlock` in a blockstate will not work for `RetexturedMeshTemplateUnbakedModel`s. Can it be fixed? * More templates !! # For addon developers @@ -81,6 +80,6 @@ That's all you need in order to construct a `RetexturedMeshUnbakedModel`, so to * If your base model lives at `yourmod:block/awesome_template`, something like `yourmod:awesome_template_special` would do. 2. Register it using `TemplatesClient.provider.addTemplateModel`. 3. Create a blockstate json for your block, and point it at the ID you decided for your special model in 1). - * You may rotate the blockmodel with the `x` and `y` properties. + * You may rotate the blockmodel with the `x` and `y` properties. You can also toggle `uvlock`. Things should work as expected. You may create a regular item model, or use ours by calling `TemplatesClient.provider.assignItemModel`, passing the ID of the special model & the items you want to assign it to. (The reason you have to do this instead of simply creating a regular item model and setting its `parent`, is that `JsonUnbakedModel`s can't have non-`JsonUnbakedModel`s as their `parent`, and even a trivial item model with only the `parent` field set counts as a `JsonUnbakedModel`. This isn't a problem for block models because blockstates are a layer of indirection before model loading.) \ No newline at end of file diff --git a/src/main/java/io/github/cottonmc/templates/model/MatrixTransformer.java b/src/main/java/io/github/cottonmc/templates/model/MeshTransformUtil.java similarity index 67% rename from src/main/java/io/github/cottonmc/templates/model/MatrixTransformer.java rename to src/main/java/io/github/cottonmc/templates/model/MeshTransformUtil.java index 197ede0..50cfa5b 100644 --- a/src/main/java/io/github/cottonmc/templates/model/MatrixTransformer.java +++ b/src/main/java/io/github/cottonmc/templates/model/MeshTransformUtil.java @@ -5,6 +5,8 @@ import net.fabricmc.fabric.api.renderer.v1.Renderer; import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh; import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder; import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; +import net.fabricmc.fabric.api.renderer.v1.render.RenderContext; +import net.minecraft.client.render.model.ModelBakeSettings; import net.minecraft.util.math.AffineTransformation; import net.minecraft.util.math.Direction; import org.joml.Matrix4f; @@ -14,16 +16,14 @@ import org.joml.Vector4f; import java.util.EnumMap; import java.util.Map; -/** - * Transforms the position of each vertex in a `Mesh`. - * The transformation's origin is bumped to (0.5, 0.5, 0.5) just because it's more convenient for me lol. - */ -public class MatrixTransformer { - public static Mesh meshAroundCenter(AffineTransformation aff, Mesh oldMesh) { - return meshAroundCenter(aff.getMatrix(), oldMesh); +public class MeshTransformUtil { + public static Mesh aroundCenter(Mesh oldMesh, ModelBakeSettings settings) { + return aroundCenter(oldMesh, settings.getRotation().getMatrix()); } - public static Mesh meshAroundCenter(Matrix4f mat, Mesh oldMesh) { + public static Mesh aroundCenter(Mesh oldMesh, Matrix4f mat) { + Map facePermutation = facePermutation(mat); + Renderer r = TemplatesClient.getFabricRenderer(); MeshBuilder newMesh = r.meshBuilder(); QuadEmitter emitter = newMesh.getEmitter(); @@ -52,6 +52,11 @@ public class MatrixTransformer { emitter.pos(i, pos4.x + 0.5f, pos4.y + 0.5f, pos4.z + 0.5f); } + emitter.nominalFace(facePermutation.get(emitter.lightFace())); + + Direction cull = emitter.cullFace(); + if(cull != null) emitter.cullFace(facePermutation.get(cull)); + //Output the quad emitter.emit(); }); @@ -68,12 +73,28 @@ public class MatrixTransformer { // //This seems to work, but I'm kinda surprised I don't need to invert the transformation here, which is a clue that //I don't really understand all the math, loool - public static Map facePermutation(AffineTransformation aff) { + public static Map facePermutation(ModelBakeSettings aff) { + return facePermutation(aff.getRotation().getMatrix()); + } + + public static Map facePermutation(Matrix4f mat) { Map facePermutation = new EnumMap<>(Direction.class); for(Direction input : Direction.values()) { - Direction output = Direction.transform(aff.getMatrix(), input); + Direction output = Direction.transform(mat, input); facePermutation.put(input, output); } return facePermutation; } + + public static Mesh pretransformMesh(Mesh mesh, RenderContext.QuadTransform transform) { + MeshBuilder builder = TemplatesClient.getFabricRenderer().meshBuilder(); + QuadEmitter emitter = builder.getEmitter(); + + mesh.forEach(quad -> { + emitter.copyFrom(quad); + if(transform.transform(emitter)) emitter.emit(); + }); + + return builder.build(); + } } diff --git a/src/main/java/io/github/cottonmc/templates/model/RetexturedJsonModelBakedModel.java b/src/main/java/io/github/cottonmc/templates/model/RetexturedJsonModelBakedModel.java index 25dcb72..17d3348 100644 --- a/src/main/java/io/github/cottonmc/templates/model/RetexturedJsonModelBakedModel.java +++ b/src/main/java/io/github/cottonmc/templates/model/RetexturedJsonModelBakedModel.java @@ -50,7 +50,6 @@ public class RetexturedJsonModelBakedModel extends ForwardingBakedModel { private final Sprite[] specialSprites = new Sprite[DIRECTIONS.length]; private final BlockState itemModelState; - //TODO: Check that TemplateAppearance equals() behavior is what i want, and also that it's fast private record CacheKey(BlockState state, TemplateAppearance appearance) {} private final ConcurrentHashMap meshCache = new ConcurrentHashMap<>(); @@ -103,7 +102,12 @@ public class RetexturedJsonModelBakedModel extends ForwardingBakedModel { QuadUvBounds bounds = QuadUvBounds.read(emitter); for(int i = 0; i < specialSprites.length; i++) { if(bounds.displaysSprite(specialSprites[i])) { - bounds.remap(emitter, specialSprites[i], key.appearance().getSprite(DIRECTIONS[i])); + bounds.remap( + emitter, + specialSprites[i], + key.appearance().getSprite(DIRECTIONS[i]), + key.appearance().getBakeFlags(DIRECTIONS[i]) + ); break; } } @@ -131,22 +135,31 @@ public class RetexturedJsonModelBakedModel extends ForwardingBakedModel { return sprite.getMinU() <= minU && sprite.getMaxU() >= maxU && sprite.getMinV() <= minV && sprite.getMaxV() >= maxV; } - void remap(MutableQuadView quad, Sprite specialSprite, Sprite newSprite) { - float remappedMinU = rangeRemap(minU, specialSprite.getMinU(), specialSprite.getMaxU(), newSprite.getMinU(), newSprite.getMaxU()); - float remappedMaxU = rangeRemap(maxU, specialSprite.getMinU(), specialSprite.getMaxU(), newSprite.getMinU(), newSprite.getMaxU()); - float remappedMinV = rangeRemap(minV, specialSprite.getMinV(), specialSprite.getMaxV(), newSprite.getMinV(), newSprite.getMaxV()); - float remappedMaxV = rangeRemap(maxV, specialSprite.getMinV(), specialSprite.getMaxV(), newSprite.getMinV(), newSprite.getMaxV()); - + void remap(MutableQuadView quad, Sprite specialSprite, Sprite newSprite, int bakeFlags) { + //move the UVs into 0..1 range + float remappedMinU = norm(minU, specialSprite.getMinU(), specialSprite.getMaxU()); + float remappedMaxU = norm(maxU, specialSprite.getMinU(), specialSprite.getMaxU()); + float remappedMinV = norm(minV, specialSprite.getMinV(), specialSprite.getMaxV()); + float remappedMaxV = norm(maxV, specialSprite.getMinV(), specialSprite.getMaxV()); quad.uv(0, MathHelper.approximatelyEquals(quad.u(0), minU) ? remappedMinU : remappedMaxU, MathHelper.approximatelyEquals(quad.v(0), minV) ? remappedMinV : remappedMaxV); quad.uv(1, MathHelper.approximatelyEquals(quad.u(1), minU) ? remappedMinU : remappedMaxU, MathHelper.approximatelyEquals(quad.v(1), minV) ? remappedMinV : remappedMaxV); quad.uv(2, MathHelper.approximatelyEquals(quad.u(2), minU) ? remappedMinU : remappedMaxU, MathHelper.approximatelyEquals(quad.v(2), minV) ? remappedMinV : remappedMaxV); quad.uv(3, MathHelper.approximatelyEquals(quad.u(3), minU) ? remappedMinU : remappedMaxU, MathHelper.approximatelyEquals(quad.v(3), minV) ? remappedMinV : remappedMaxV); + + //call spriteBake + //done this way (instead of directly setting UV coordinates to their final values) so that I can use the convenient bakeFlags option + quad.spriteBake(newSprite, MutableQuadView.BAKE_NORMALIZED | bakeFlags); } - static float rangeRemap(float value, float low1, float high1, float low2, float high2) { - float value2 = MathHelper.clamp(value, low1, high1); - return low2 + (value2 - low1) * (high2 - low2) / (high1 - low1); + static float norm(float value, float low, float high) { + float value2 = MathHelper.clamp(value, low, high); + return (value2 - low) / (high - low); } + + //static float rangeRemap(float value, float low1, float high1, float low2, float high2) { + // float value2 = MathHelper.clamp(value, low1, high1); + // return low2 + (value2 - low1) * (high2 - low2) / (high1 - low1); + //} } private static final Direction[] DIRECTIONS = Direction.values(); diff --git a/src/main/java/io/github/cottonmc/templates/model/RetexturedMeshBakedModel.java b/src/main/java/io/github/cottonmc/templates/model/RetexturedMeshBakedModel.java index d8b7f18..00582d2 100644 --- a/src/main/java/io/github/cottonmc/templates/model/RetexturedMeshBakedModel.java +++ b/src/main/java/io/github/cottonmc/templates/model/RetexturedMeshBakedModel.java @@ -1,24 +1,21 @@ package io.github.cottonmc.templates.model; -import io.github.cottonmc.templates.TemplatesClient; import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry; import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh; -import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder; import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView; -import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel; import net.fabricmc.fabric.api.renderer.v1.render.RenderContext; import net.fabricmc.fabric.api.rendering.data.v1.RenderAttachedBlockView; import net.minecraft.block.BlockState; import net.minecraft.client.color.block.BlockColorProvider; import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.render.model.ModelBakeSettings; import net.minecraft.client.texture.Sprite; import net.minecraft.item.BlockItem; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtHelper; import net.minecraft.registry.Registries; -import net.minecraft.util.math.AffineTransformation; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.util.math.random.Random; @@ -29,18 +26,19 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; public class RetexturedMeshBakedModel extends ForwardingBakedModel { - public RetexturedMeshBakedModel(BakedModel baseModel, TemplateAppearanceManager tam, AffineTransformation aff, Mesh baseMesh) { + public RetexturedMeshBakedModel(BakedModel baseModel, TemplateAppearanceManager tam, ModelBakeSettings settings, Mesh baseMesh) { this.wrapped = baseModel; this.tam = tam; - this.baseMesh = MatrixTransformer.meshAroundCenter(aff, baseMesh); - this.facePermutation = MatrixTransformer.facePermutation(aff); + this.baseMesh = MeshTransformUtil.aroundCenter(baseMesh, settings); + this.facePermutation = MeshTransformUtil.facePermutation(settings); + this.uvLock = settings.isUvLocked(); } private final TemplateAppearanceManager tam; private final Mesh baseMesh; private final Map facePermutation; + private final boolean uvLock; - //TODO: Check that TemplateAppearance equals() behavior is what i want, and also that it's fast private final ConcurrentHashMap meshCache = new ConcurrentHashMap<>(); @Override @@ -69,7 +67,7 @@ public class RetexturedMeshBakedModel extends ForwardingBakedModel { //is likely unnecessary and will fill the cache with a ton of single-use meshes with only slighly different colors. //We'd also have to percolate that tint color into the cache key, which is an allocation, blah blah blah. //Let's fall back to a quad transform. In practice this is still nice and quick. - context.pushTransform(new RetexturingTransformer(ta, tint, facePermutation)); + context.pushTransform(new RetexturingTransformer(ta, tint, facePermutation, uvLock)); context.meshConsumer().accept(baseMesh); context.popTransform(); } @@ -99,10 +97,10 @@ public class RetexturedMeshBakedModel extends ForwardingBakedModel { } protected Mesh makeUntintedMesh(TemplateAppearance appearance) { - return new RetexturingTransformer(appearance, 0xFFFFFF, facePermutation).applyTo(baseMesh); + return MeshTransformUtil.pretransformMesh(baseMesh, new RetexturingTransformer(appearance, 0xFFFFFFFF, facePermutation, uvLock)); } - public static record RetexturingTransformer(TemplateAppearance appearance, int color, Map facePermutation) implements RenderContext.QuadTransform { + public static record RetexturingTransformer(TemplateAppearance appearance, int color, Map facePermutation, boolean uvLock) implements RenderContext.QuadTransform { private static final Direction[] DIRECTIONS = Direction.values(); @Override @@ -116,22 +114,14 @@ public class RetexturedMeshBakedModel extends ForwardingBakedModel { if(appearance.hasColor(dir)) quad.color(color, color, color, color); Sprite sprite = appearance.getSprite(dir); - quad.spriteBake(sprite, MutableQuadView.BAKE_NORMALIZED); + + int flags = MutableQuadView.BAKE_NORMALIZED; + flags |= appearance.getBakeFlags(dir); + if(uvLock) flags |= MutableQuadView.BAKE_LOCK_UV; + + quad.spriteBake(sprite, flags); return true; } - - //Pass a Mesh through a QuadTransform all at once, instead of at render time - private Mesh applyTo(Mesh original) { - MeshBuilder builder = TemplatesClient.getFabricRenderer().meshBuilder(); - QuadEmitter emitter = builder.getEmitter(); - - original.forEach(quad -> { - emitter.copyFrom(quad); - if(transform(emitter)) emitter.emit(); - }); - - return builder.build(); - } } } diff --git a/src/main/java/io/github/cottonmc/templates/model/RetexturedMeshUnbakedModel.java b/src/main/java/io/github/cottonmc/templates/model/RetexturedMeshUnbakedModel.java index 8f4fa93..2611fff 100644 --- a/src/main/java/io/github/cottonmc/templates/model/RetexturedMeshUnbakedModel.java +++ b/src/main/java/io/github/cottonmc/templates/model/RetexturedMeshUnbakedModel.java @@ -40,7 +40,7 @@ public class RetexturedMeshUnbakedModel implements UnbakedModel { return new RetexturedMeshBakedModel( baker.bake(parent, modelBakeSettings), TemplatesClient.provider.getOrCreateTemplateApperanceManager(spriteLookup), - modelBakeSettings.getRotation(), + modelBakeSettings, baseMeshFactory.get() ); } diff --git a/src/main/java/io/github/cottonmc/templates/model/SlopeBaseMesh.java b/src/main/java/io/github/cottonmc/templates/model/SlopeBaseMesh.java index da11e58..ec88220 100644 --- a/src/main/java/io/github/cottonmc/templates/model/SlopeBaseMesh.java +++ b/src/main/java/io/github/cottonmc/templates/model/SlopeBaseMesh.java @@ -26,27 +26,29 @@ public class SlopeBaseMesh { qu.tag(TAG_SLOPE) .pos(0, 0f, 0f, 0f).pos(1, 0f, 1f, 1f).pos(2, 1f, 1f, 1f).pos(3, 1f, 0f, 0f) .color(-1, -1, -1, -1) - .uv(0, 1f, 1f).uv(1, 1f, 0f).uv(2, 0f, 0f).uv(3, 0f, 1f) + .uv(0, 0f, 0f).uv(1, 0f, 1f).uv(2, 1f, 1f).uv(3, 1f, 0f) .emit() .tag(TAG_LEFT) .pos(0, 1f, 0f, 0f).pos(1, 1f, 0.5f, 0.5f).pos(2, 1f, 1f, 1f).pos(3, 1f, 0f, 1f) .color(-1, -1, -1, -1) .uv(0, 1f, 1f).uv(1, 0.5f, 0.5f).uv(2, 0f, 0f).uv(3, 0f, 1f) + .cullFace(Direction.EAST) .emit() .tag(TAG_RIGHT) - .pos(0, 0f, 0f, 0f).pos(1, 0f, 0f, 1f).pos(2, 0f, 1f, 1f).pos(3, 0f, 0.5f, 0.5f) + .pos(0, 0f, 0.5f, 0.5f).pos(1, 0, 0f, 0f).pos(2, 0f, 0f, 1f).pos(3, 0f, 1f, 1f) .color(-1, -1, -1, -1) - .uv(0, 0f, 1f).uv(1, 1f, 1f).uv(2, 1f, 0f).uv(3, 0.5f, 0.5f) + .uv(0, 0.5f, 0.5f).uv(1, 0f, 1f).uv(2, 1f, 1f).uv(3, 1f, 0f) + .cullFace(Direction.WEST) .emit() .tag(TAG_BACK) - .pos(0, 0f, 0f, 1f).pos(1, 1f, 0f, 1f).pos(2, 1f, 1f, 1f).pos(3, 0f, 1f, 1f) + .square(Direction.SOUTH, 0, 0, 1, 1, 0) //sets pos & cullFace .color(-1, -1, -1, -1) - .uv(0, 0f, 1f).uv(1, 1f, 1f).uv(2, 1f, 0f).uv(3, 0f, 0f) + .uvUnitSquare() .emit() .tag(TAG_BOTTOM) - .pos(0, 0f, 0f, 0f).pos(1, 1f, 0f, 0f).pos(2, 1f, 0f, 1f).pos(3, 0f, 0f, 1f) + .square(Direction.DOWN, 0, 0, 1, 1, 0) //sets pos & cullFace .color(-1, -1, -1, -1) - .uv(0, 0f, 1f).uv(1, 1f, 1f).uv(2, 1f, 0f).uv(3, 0f, 0f) + .uvUnitSquare() .emit(); return builder.build(); } diff --git a/src/main/java/io/github/cottonmc/templates/model/TemplateAppearance.java b/src/main/java/io/github/cottonmc/templates/model/TemplateAppearance.java index cdbb4f5..5efa23a 100644 --- a/src/main/java/io/github/cottonmc/templates/model/TemplateAppearance.java +++ b/src/main/java/io/github/cottonmc/templates/model/TemplateAppearance.java @@ -10,5 +10,6 @@ public interface TemplateAppearance { @NotNull RenderMaterial getRenderMaterial(); @NotNull Sprite getSprite(Direction dir); + int getBakeFlags(Direction dir); boolean hasColor(Direction dir); } diff --git a/src/main/java/io/github/cottonmc/templates/model/TemplateAppearanceManager.java b/src/main/java/io/github/cottonmc/templates/model/TemplateAppearanceManager.java index c7c79cf..3acbbb9 100644 --- a/src/main/java/io/github/cottonmc/templates/model/TemplateAppearanceManager.java +++ b/src/main/java/io/github/cottonmc/templates/model/TemplateAppearanceManager.java @@ -4,6 +4,8 @@ import io.github.cottonmc.templates.TemplatesClient; import net.fabricmc.fabric.api.renderer.v1.material.BlendMode; import net.fabricmc.fabric.api.renderer.v1.material.MaterialFinder; import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial; +import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView; +import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; import net.fabricmc.fabric.api.util.TriState; import net.minecraft.block.BlockState; import net.minecraft.client.MinecraftClient; @@ -15,6 +17,7 @@ import net.minecraft.client.util.SpriteIdentifier; import net.minecraft.screen.PlayerScreenHandler; import net.minecraft.util.Identifier; import net.minecraft.util.math.Direction; +import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.random.Random; import org.jetbrains.annotations.NotNull; @@ -60,7 +63,12 @@ public class TemplateAppearanceManager { Random rand = Random.create(); BakedModel model = MinecraftClient.getInstance().getBlockRenderManager().getModel(state); + //Only for parsing vanilla quads: + QuadEmitter emitter = TemplatesClient.getFabricRenderer().meshBuilder().getEmitter(); + RenderMaterial defaultMat = TemplatesClient.getFabricRenderer().materialFinder().clear().find(); + Sprite[] sprites = new Sprite[7]; + int[] bakeFlags = new int[6]; byte hasColorMask = 0b000000; //Read quads off the model by their `cullface` @@ -75,8 +83,41 @@ public class TemplateAppearanceManager { Sprite sprite = arbitraryQuad.getSprite(); if(sprite == null) continue; - sprites[dir.ordinal()] = sprite; + + //GOAL: Reconstruct the `"rotation": 90` stuff that a json model might provide, so we can bake our texture on the same way + emitter.fromVanilla(arbitraryQuad, defaultMat, dir); + int lowHighSignature = 0; + for(int i = 0; i < 4; i++) { + //For some reason the uvs stored on the Sprite have less ?precision? than the ones retrieved from the QuadEmitter. + // [STDOUT]: emitter u 0.14065552, sprite min u 0.140625 + //?precision? in question marks cause it could be float noise. It's way higher than the epsilon in MathHelper.approximatelyEquals. + //So im gonna guesstimate using "is it closer to the sprite's min or max u", rather than doing an approximately-equals check + + float diffMinU = Math.abs(emitter.u(i) - sprite.getMinU()); + float diffMaxU = Math.abs(emitter.u(i) - sprite.getMaxU()); + boolean minU = diffMinU < diffMaxU; + + float diffMinV = Math.abs(emitter.v(i) - sprite.getMinV()); + float diffMaxV = Math.abs(emitter.v(i) - sprite.getMaxV()); + boolean minV = diffMinV < diffMaxV; + + lowHighSignature <<= 2; + lowHighSignature |= (minU ? 2 : 0) | (minV ? 1 : 0); + } + + if(lowHighSignature == 0b11100001) { + bakeFlags[dir.ordinal()] = 0; + } else if(lowHighSignature == 0b10000111) { + bakeFlags[dir.ordinal()] = MutableQuadView.BAKE_ROTATE_90; + } else if(lowHighSignature == 0b00011110) { + bakeFlags[dir.ordinal()] = MutableQuadView.BAKE_ROTATE_180; + } else if(lowHighSignature == 0b01111000) { + bakeFlags[dir.ordinal()] = MutableQuadView.BAKE_ROTATE_270; + } else { + //Its not critical error or anything, the texture will show rotated or flipped + //System.out.println("unknown sig " + Integer.toString(lowHighSignature, 2) + ", state: " + state + ", sprite: " + sprite.getContents().getId() + ", side: " + dir); + } } //Just for space-usage purposes, we store the particle in sprites[6] instead of using another field. @@ -89,6 +130,7 @@ public class TemplateAppearanceManager { return new ComputedApperance( sprites, + bakeFlags, hasColorMask, blockMaterials.get(BlendMode.fromRenderLayer(RenderLayers.getBlockLayer(state))), serialNumber.getAndIncrement() @@ -98,12 +140,14 @@ public class TemplateAppearanceManager { @SuppressWarnings("ClassCanBeRecord") private static final class ComputedApperance implements TemplateAppearance { private final Sprite @NotNull[] sprites; + private final int @NotNull[] bakeFlags; private final byte hasColorMask; private final RenderMaterial mat; private final int id; - private ComputedApperance(@NotNull Sprite @NotNull[] sprites, byte hasColorMask, RenderMaterial mat, int id) { + private ComputedApperance(@NotNull Sprite @NotNull[] sprites, int @NotNull[] bakeFlags, byte hasColorMask, RenderMaterial mat, int id) { this.sprites = sprites; + this.bakeFlags = bakeFlags; this.hasColorMask = hasColorMask; this.mat = mat; this.id = id; @@ -124,6 +168,11 @@ public class TemplateAppearanceManager { return sprites[dir.ordinal()]; } + @Override + public int getBakeFlags(Direction dir) { + return bakeFlags[dir.ordinal()]; + } + @Override public boolean hasColor(Direction dir) { return (hasColorMask & (1 << dir.ordinal())) != 0; @@ -144,7 +193,7 @@ public class TemplateAppearanceManager { @Override public String toString() { - return "ComputedApperance[sprites=%s, hasColorMask=%s, mat=%s, id=%d]".formatted(Arrays.toString(sprites), hasColorMask, mat, id); + return "ComputedApperance{sprites=%s, bakeFlags=%s, hasColorMask=%s, mat=%s, id=%d}".formatted(Arrays.toString(sprites), Arrays.toString(bakeFlags), hasColorMask, mat, id); } } @@ -175,6 +224,11 @@ public class TemplateAppearanceManager { return defaultSprite; } + @Override + public int getBakeFlags(Direction dir) { + return 0; + } + @Override public boolean hasColor(Direction dir) { return false; diff --git a/src/main/resources/assets/templates/blockstates/slope.json b/src/main/resources/assets/templates/blockstates/slope.json index 544a4b9..992a2d7 100644 --- a/src/main/resources/assets/templates/blockstates/slope.json +++ b/src/main/resources/assets/templates/blockstates/slope.json @@ -2,10 +2,12 @@ "variants": { "facing=east,half=bottom": { "model": "templates:slope_special", + "uvlock": true, "y": 270 }, "facing=north,half=bottom": { "model": "templates:slope_special", + "uvlock": true, "y": 180 }, "facing=south,half=bottom": { @@ -13,24 +15,29 @@ }, "facing=west,half=bottom": { "model": "templates:slope_special", + "uvlock": true, "y": 90 }, "facing=east,half=top": { "model": "templates:slope_special", + "uvlock": true, "x": 180, "y": 90 }, "facing=north,half=top": { "model": "templates:slope_special", + "uvlock": true, "x": 180 }, "facing=south,half=top": { "model": "templates:slope_special", + "uvlock": true, "x": 180, "y": 180 }, "facing=west,half=top": { "model": "templates:slope_special", + "uvlock": true, "x": 180, "y": 270 }