From 7928a9de70bbcd505db781e6b67def2540bf6b8d Mon Sep 17 00:00:00 2001 From: quat1024 Date: Thu, 15 Jun 2023 04:24:11 -0400 Subject: [PATCH] Draw the rest of the fucking owl --- README.md | 9 + .../cottonmc/templates/TemplatesClient.java | 8 +- .../templates/block/TemplateBlock.java | 9 +- .../block/entity/TemplateEntity.java | 70 ++-- .../templates/model/AbstractModel.java | 58 --- .../cottonmc/templates/model/SimpleModel.java | 97 ----- .../templates/model/SimpleUnbakedModel.java | 35 -- .../templates/model/SlopeBakedModel.java | 64 +++ .../templates/model/SlopeBaseMesh.java | 110 ++++++ .../templates/model/SlopeMeshTransformer.java | 178 +++++++++ .../cottonmc/templates/model/SlopeModel.java | 370 ------------------ .../templates/model/SlopeUnbakedModel.java | 36 ++ .../model/TemplateModelVariantProvider.java | 6 +- .../cottonmc/templates/util/SpriteSet.java | 51 +-- 14 files changed, 480 insertions(+), 621 deletions(-) delete mode 100644 src/main/java/io/github/cottonmc/templates/model/AbstractModel.java delete mode 100644 src/main/java/io/github/cottonmc/templates/model/SimpleModel.java delete mode 100644 src/main/java/io/github/cottonmc/templates/model/SimpleUnbakedModel.java create mode 100644 src/main/java/io/github/cottonmc/templates/model/SlopeBakedModel.java create mode 100644 src/main/java/io/github/cottonmc/templates/model/SlopeBaseMesh.java create mode 100644 src/main/java/io/github/cottonmc/templates/model/SlopeMeshTransformer.java delete mode 100644 src/main/java/io/github/cottonmc/templates/model/SlopeModel.java create mode 100644 src/main/java/io/github/cottonmc/templates/model/SlopeUnbakedModel.java diff --git a/README.md b/README.md index 57522ef..ba00abd 100644 --- a/README.md +++ b/README.md @@ -11,3 +11,12 @@ Templates is an API for Carpenter's Blocks-like templated blocks. Currently, plain slopes are the only built-in template blocks. 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. + +# quat was here - TODO + +* Schedule chunk rerenders when a player edits a template block :sweat_smile: +* Re-generalize the model system (I removed a layer of indirection while rewriting it, so it's just slopes now) +* See what I can do about using the vanilla rotation system (`ModelBakeSettings.getRotation`) instead of manually rotating the `Mesh` + * A simplification of the mesh system would *definitely* reduce the friction of adding new meshes + * Upside-down slopes would be nice... +* (if i may): Packages-ish "retexturing" of json blockmodels \ No newline at end of file diff --git a/src/main/java/io/github/cottonmc/templates/TemplatesClient.java b/src/main/java/io/github/cottonmc/templates/TemplatesClient.java index 98cafe1..e105852 100644 --- a/src/main/java/io/github/cottonmc/templates/TemplatesClient.java +++ b/src/main/java/io/github/cottonmc/templates/TemplatesClient.java @@ -1,10 +1,12 @@ package io.github.cottonmc.templates; import io.github.cottonmc.templates.block.SlopeBlock; -import io.github.cottonmc.templates.model.SlopeModel; +import io.github.cottonmc.templates.model.SlopeUnbakedModel; import io.github.cottonmc.templates.model.TemplateModelVariantProvider; import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap; import net.fabricmc.fabric.api.client.model.ModelLoadingRegistry; +import net.minecraft.client.render.RenderLayer; import net.minecraft.util.math.Direction; public class TemplatesClient implements ClientModInitializer { @@ -13,6 +15,8 @@ public class TemplatesClient implements ClientModInitializer { @Override public void onInitializeClient() { ModelLoadingRegistry.INSTANCE.registerVariantProvider(rm -> provider); - provider.registerTemplateModels(Templates.SLOPE, Templates.SLOPE.getDefaultState().with(SlopeBlock.FACING, Direction.SOUTH), SlopeModel::new); + provider.registerTemplateModels2(Templates.SLOPE, Templates.SLOPE.getDefaultState().with(SlopeBlock.FACING, Direction.SOUTH), SlopeUnbakedModel::new); + + BlockRenderLayerMap.INSTANCE.putBlock(Templates.SLOPE, RenderLayer.getCutout()); } } diff --git a/src/main/java/io/github/cottonmc/templates/block/TemplateBlock.java b/src/main/java/io/github/cottonmc/templates/block/TemplateBlock.java index 6f6d594..029bf8f 100644 --- a/src/main/java/io/github/cottonmc/templates/block/TemplateBlock.java +++ b/src/main/java/io/github/cottonmc/templates/block/TemplateBlock.java @@ -3,7 +3,6 @@ package io.github.cottonmc.templates.block; import io.github.cottonmc.templates.Templates; import io.github.cottonmc.templates.block.entity.TemplateEntity; import io.github.cottonmc.templates.util.StateContainer; -import net.minecraft.block.AbstractBlock; import net.minecraft.block.Block; import net.minecraft.block.BlockEntityProvider; import net.minecraft.block.BlockState; @@ -35,7 +34,9 @@ public abstract class TemplateBlock extends Block implements BlockEntityProvider } public static Settings configureSettings(Settings s) { - return s.luminance(state -> ((TemplateBlock) state.getBlock()).luminance(state)); + return s + .luminance(state -> ((TemplateBlock) state.getBlock()).luminance(state)) + .nonOpaque(); } @Override @@ -46,7 +47,7 @@ public abstract class TemplateBlock extends Block implements BlockEntityProvider if(stack.getItem() instanceof BlockItem) { Block block = ((BlockItem) stack.getItem()).getBlock(); if(block == Blocks.REDSTONE_TORCH) { - be.addRedstone(); + be.setRedstone(true); if(!player.isCreative()) stack.decrement(1); } ItemPlacementContext ctx = new ItemPlacementContext(new ItemUsageContext(player, hand, hit)); @@ -60,7 +61,7 @@ public abstract class TemplateBlock extends Block implements BlockEntityProvider if(!player.isCreative()) stack.decrement(1); } } else if(stack.getItem() == Items.GLOWSTONE_DUST) { - be.addGlowstone(); + be.setGlowstone(true); if(!player.isCreative()) stack.decrement(1); } return ActionResult.SUCCESS; diff --git a/src/main/java/io/github/cottonmc/templates/block/entity/TemplateEntity.java b/src/main/java/io/github/cottonmc/templates/block/entity/TemplateEntity.java index 3d90bbc..a572f60 100644 --- a/src/main/java/io/github/cottonmc/templates/block/entity/TemplateEntity.java +++ b/src/main/java/io/github/cottonmc/templates/block/entity/TemplateEntity.java @@ -10,10 +10,14 @@ import net.minecraft.block.entity.BlockEntityType; import net.minecraft.client.world.ClientWorld; import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtHelper; +import net.minecraft.network.listener.ClientPlayPacketListener; +import net.minecraft.network.packet.Packet; +import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket; import net.minecraft.registry.Registries; -import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Direction; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; public abstract class TemplateEntity extends BlockEntity implements RenderAttachmentBlockEntity { protected BlockState renderedState = Blocks.AIR.getDefaultState(); @@ -26,15 +30,6 @@ public abstract class TemplateEntity extends BlockEntity implements RenderAttach this.baseBlock = baseBlock; } - public BlockState getRenderedState() { - return renderedState; - } - - public void setRenderedState(BlockState state) { - this.renderedState = state; - markDirty(); - } - @Override public void readNbt(NbtCompound tag) { super.readNbt(tag); @@ -57,17 +52,15 @@ public abstract class TemplateEntity extends BlockEntity implements RenderAttach tag.putBoolean("Redstone", redstone); } + @Nullable @Override - public void markDirty() { - super.markDirty(); - if(world != null && !world.isClient) { - for(ServerPlayerEntity player : PlayerLookup.tracking(this)) { - player.networkHandler.sendPacket(this.toUpdatePacket()); - } - world.updateNeighborsAlways(pos.offset(Direction.UP), baseBlock); - BlockState state = world.getBlockState(pos); - world.updateListeners(pos, state, state, 1); - } + public Packet toUpdatePacket() { + return BlockEntityUpdateS2CPacket.create(this); + } + + @Override + public NbtCompound toInitialChunkDataNbt() { + return createNbt(); } @Override @@ -75,21 +68,44 @@ public abstract class TemplateEntity extends BlockEntity implements RenderAttach return renderedState; } + public void change() { + markDirty(); + if(world != null && !world.isClient) { + //for(ServerPlayerEntity player : PlayerLookup.tracking(this)) player.networkHandler.sendPacket(this.toUpdatePacket()); + + //TODO is this needed + //world.updateNeighborsAlways(pos.offset(Direction.UP), baseBlock); + world.updateListeners(pos, getCachedState(), getCachedState(), 3); + } + } + + public BlockState getRenderedState() { + return renderedState; + } + + public void setRenderedState(BlockState newState) { + BlockState lastState = renderedState; + renderedState = newState; + if(!Objects.equals(lastState, newState)) change(); + } + public boolean hasGlowstone() { return glowstone; } - public void addGlowstone() { - glowstone = true; - markDirty(); + public void setGlowstone(boolean newGlowstone) { + boolean lastGlowstone = glowstone; + glowstone = newGlowstone; + if(lastGlowstone != newGlowstone) change(); } public boolean hasRedstone() { return redstone; } - public void addRedstone() { - redstone = true; - markDirty(); + public void setRedstone(boolean newRedstone) { + boolean lastRedstone = redstone; + redstone = newRedstone; + if(lastRedstone != newRedstone) change(); } } diff --git a/src/main/java/io/github/cottonmc/templates/model/AbstractModel.java b/src/main/java/io/github/cottonmc/templates/model/AbstractModel.java deleted file mode 100644 index 8345ab9..0000000 --- a/src/main/java/io/github/cottonmc/templates/model/AbstractModel.java +++ /dev/null @@ -1,58 +0,0 @@ -package io.github.cottonmc.templates.model; - -import net.fabricmc.fabric.api.renderer.v1.Renderer; -import net.fabricmc.fabric.api.renderer.v1.RendererAccess; -import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.render.model.BakedModel; -import net.minecraft.client.render.model.json.ModelTransformation; -import net.minecraft.client.texture.Sprite; - -public abstract class AbstractModel implements BakedModel, FabricBakedModel { - protected static final Renderer RENDERER = RendererAccess.INSTANCE.getRenderer(); - - static { - if(RENDERER == null) { - throw new ExceptionInInitializerError("RenderAccess.INSTANCE must be populated"); - } - } - - protected final Sprite modelSprite; - protected final ModelTransformation transformation; - - protected AbstractModel(Sprite sprite, ModelTransformation transformation) { - this.modelSprite = sprite; - this.transformation = transformation; - } - - @Override - public boolean useAmbientOcclusion() { - return true; - } - - @Override - public boolean hasDepth() { - return true; - } - - @Override - public boolean isBuiltin() { - return false; - } - - @Override - public Sprite getParticleSprite() { - //TODO - return MinecraftClient.getInstance().getBakedModelManager().getMissingModel().getParticleSprite(); - } - - @Override - public boolean isSideLit() { - return false; //? - } - - @Override - public ModelTransformation getTransformation() { - return transformation; - } -} diff --git a/src/main/java/io/github/cottonmc/templates/model/SimpleModel.java b/src/main/java/io/github/cottonmc/templates/model/SimpleModel.java deleted file mode 100644 index c6319b3..0000000 --- a/src/main/java/io/github/cottonmc/templates/model/SimpleModel.java +++ /dev/null @@ -1,97 +0,0 @@ -package io.github.cottonmc.templates.model; - -import com.google.common.collect.ImmutableList; -import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh; -import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper; -import net.fabricmc.fabric.api.renderer.v1.render.RenderContext; -import net.minecraft.block.BlockState; -import net.minecraft.client.render.model.BakedQuad; -import net.minecraft.client.render.model.json.ModelOverrideList; -import net.minecraft.client.render.model.json.ModelTransformation; -import net.minecraft.client.texture.Sprite; -import net.minecraft.item.ItemStack; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Direction; -import net.minecraft.util.math.random.Random; -import net.minecraft.world.BlockRenderView; - -import java.lang.ref.WeakReference; -import java.util.List; -import java.util.function.Supplier; - -/** - * Simple baked model supporting the Fabric Render API features.

- */ -public class SimpleModel extends AbstractModel { - protected final Mesh mesh; - protected final Supplier transformerFactory; - protected WeakReference[]> quadLists = null; - //protected final ItemProxy itemProxy = new ItemProxy(); - - public SimpleModel(Mesh mesh, Supplier transformerFactory, Sprite sprite, ModelTransformation transformation) { - super(sprite, transformation); - this.mesh = mesh; - this.transformerFactory = transformerFactory; - } - - @Override - public boolean isVanillaAdapter() { - return false; - } - - @Override - public List getQuads(BlockState state, Direction face, Random rand) { - List[] lists = quadLists == null ? null : quadLists.get(); - if(lists == null) { - lists = ModelHelper.toQuadLists(this.mesh); - quadLists = new WeakReference<>(lists); - } - List result = lists[face == null ? 6 : face.getId()]; - return result == null ? ImmutableList.of() : result; - } - - @Override - public void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier randomSupplier, RenderContext context) { - final MeshTransformer transform = transformerFactory == null ? null : transformerFactory.get().prepare(blockView, state, pos, randomSupplier); - if(transform != null) { - context.pushTransform(transform); - } - if(mesh != null) { - context.meshConsumer().accept(mesh); - } - if(transform != null) { - context.popTransform(); - } - } - - @Override - public ModelOverrideList getOverrides() { - return ModelOverrideList.EMPTY; - } - - // @Override -// public ModelItemPropertyOverrideList getItemPropertyOverrides() { -// return itemProxy; -// } -// -// protected class ItemProxy extends ModelOverrideList { -// @Override -// public BakedModel apply(BakedModel bakedModel_1, ItemStack itemStack_1, World world_1, LivingEntity livingEntity_1) { -// return SimpleModel.this; -// } -// } - - @Override - public void emitItemQuads(ItemStack stack, Supplier randomSupplier, RenderContext context) { - final MeshTransformer transform = transformerFactory == null ? null : transformerFactory.get().prepare(stack, randomSupplier); - if(transform != null) { - context.pushTransform(transform); - } - if(mesh != null) { - context.meshConsumer().accept(mesh); - } - if(transform != null) { - context.popTransform(); - } - } -} diff --git a/src/main/java/io/github/cottonmc/templates/model/SimpleUnbakedModel.java b/src/main/java/io/github/cottonmc/templates/model/SimpleUnbakedModel.java deleted file mode 100644 index 3cc7176..0000000 --- a/src/main/java/io/github/cottonmc/templates/model/SimpleUnbakedModel.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.github.cottonmc.templates.model; - -import net.minecraft.client.render.model.BakedModel; -import net.minecraft.client.render.model.Baker; -import net.minecraft.client.render.model.ModelBakeSettings; -import net.minecraft.client.render.model.ModelLoader; -import net.minecraft.client.render.model.UnbakedModel; -import net.minecraft.client.texture.Sprite; -import net.minecraft.client.util.SpriteIdentifier; -import net.minecraft.util.Identifier; -import org.jetbrains.annotations.Nullable; - -import java.util.Collection; -import java.util.Collections; -import java.util.function.Function; - -@FunctionalInterface -public interface SimpleUnbakedModel extends UnbakedModel { - BakedModel bake(); - - @Override - default Collection getModelDependencies() { - return Collections.emptyList(); - } - - @Override - default void setParents(Function function) { - // nope - } - - @Override - default @Nullable BakedModel bake(Baker baker, Function function, ModelBakeSettings modelBakeSettings, Identifier identifier) { - return bake(); - } -} diff --git a/src/main/java/io/github/cottonmc/templates/model/SlopeBakedModel.java b/src/main/java/io/github/cottonmc/templates/model/SlopeBakedModel.java new file mode 100644 index 0000000..a49a1fb --- /dev/null +++ b/src/main/java/io/github/cottonmc/templates/model/SlopeBakedModel.java @@ -0,0 +1,64 @@ +package io.github.cottonmc.templates.model; + +import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh; +import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel; +import net.fabricmc.fabric.api.renderer.v1.render.RenderContext; +import net.minecraft.block.BlockState; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.texture.Sprite; +import net.minecraft.client.util.SpriteIdentifier; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.random.Random; +import net.minecraft.world.BlockRenderView; + +import java.util.function.Function; +import java.util.function.Supplier; + +public final class SlopeBakedModel extends ForwardingBakedModel { + public SlopeBakedModel(BakedModel baseModel, BlockState slopeState, Function spriteLookup) { + this.wrapped = baseModel; + this.slopeState = slopeState; + this.spriteLookup = spriteLookup; + + this.xform = new SlopeMeshTransformer(spriteLookup); + this.baseMesh = SlopeBaseMesh.make(slopeState); + } + + public final BlockState slopeState; + public final Function spriteLookup; + + private final MeshTransformer xform; + private final Mesh baseMesh; + + @Override + public boolean isVanillaAdapter() { + return false; + } + + @Override + public void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier randomSupplier, RenderContext context) { + MeshTransformer xform2 = xform.prepare(blockView, state, pos, randomSupplier); + + if(xform2 != null) { + context.pushTransform(xform2); + context.meshConsumer().accept(baseMesh); + context.popTransform(); + } else { + context.meshConsumer().accept(baseMesh); + } + } + + @Override + public void emitItemQuads(ItemStack stack, Supplier randomSupplier, RenderContext context) { + MeshTransformer xform2 = xform.prepare(stack, randomSupplier); + + if(xform2 != null) { + context.pushTransform(xform2); + context.meshConsumer().accept(baseMesh); + context.popTransform(); + } else { + context.meshConsumer().accept(baseMesh); + } + } +} diff --git a/src/main/java/io/github/cottonmc/templates/model/SlopeBaseMesh.java b/src/main/java/io/github/cottonmc/templates/model/SlopeBaseMesh.java new file mode 100644 index 0000000..4abe2e2 --- /dev/null +++ b/src/main/java/io/github/cottonmc/templates/model/SlopeBaseMesh.java @@ -0,0 +1,110 @@ +package io.github.cottonmc.templates.model; + +import net.fabricmc.fabric.api.renderer.v1.Renderer; +import net.fabricmc.fabric.api.renderer.v1.RendererAccess; +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.minecraft.block.BlockState; +import net.minecraft.state.property.Properties; +import net.minecraft.util.math.Direction; + +public class SlopeBaseMesh { + public static Mesh make(BlockState state) { + Renderer renderer = RendererAccess.INSTANCE.getRenderer(); + if(renderer == null) throw new IllegalStateException("RenderAccess.INSTANCE not populated - no Fabric Renderer API?"); + + final MeshBuilder builder = renderer.meshBuilder(); + final QuadEmitter quad = builder.getEmitter(); + final Direction dir = state.get(Properties.HORIZONTAL_FACING); + drawSlope(quad.color(-1, -1, -1, -1), dir); + drawLeftSide(quad.color(-1, -1, -1, -1), dir); + drawRightSide(quad.color(-1, -1, -1, -1), dir); + drawBack(quad.color(-1, -1, -1, -1), dir); + drawBottom(quad.color(-1, -1, -1, -1)); + return builder.build(); + } + + public static final int TAG_SLOPE = 0; + public static final int TAG_LEFT = 1; + public static final int TAG_RIGHT = 2; + public static final int TAG_BACK = 3; + public static final int TAG_BOTTOM = 4; + + private static void drawSlope(QuadEmitter quad, Direction dir) { + quad.tag(TAG_SLOPE); + switch(dir) { + case NORTH: + quad.pos(0, 0f, 1f, 0f).pos(1, 0f, 0f, 1f).pos(2, 1f, 0f, 1f).pos(3, 1f, 1f, 0f).emit(); + break; + case SOUTH: + quad.pos(0, 0f, 0f, 0f).pos(1, 0f, 1f, 1f).pos(2, 1f, 1f, 1f).pos(3, 1f, 0f, 0f).emit(); + break; + case EAST: + quad.pos(0, 0f, 0f, 0f).pos(1, 0f, 0f, 1f).pos(2, 1f, 1f, 1f).pos(3, 1f, 1f, 0f).emit(); + break; + case WEST: + quad.pos(0, 0f, 1f, 0f).pos(1, 0f, 1f, 1f).pos(2, 1f, 0f, 1f).pos(3, 1f, 0f, 0f).emit(); + default: + break; + } + } + + private static void drawLeftSide(QuadEmitter quad, Direction dir) { + switch(dir) { + case NORTH: + quad.tag(TAG_LEFT).pos(0, 0f, 0f, 0f).pos(1, 0f, 0f, 1f).pos(2, 0f, 0f, 1f).pos(3, 0f, 1f, 0f).emit(); + break; + case SOUTH: + quad.tag(TAG_LEFT).pos(0, 1f, 0f, 0f).pos(1, 1f, 1f, 1f).pos(2, 1f, 0f, 1f).pos(3, 1f, 0f, 1f).emit(); + break; + case EAST: + quad.tag(TAG_LEFT).pos(0, 1f, 0f, 0f).pos(1, 0f, 0f, 0f).pos(2, 0f, 0f, 0f).pos(3, 1f, 1f, 0f).emit(); + break; + case WEST: + quad.tag(TAG_LEFT).pos(0, 0f, 0f, 1f).pos(1, 1f, 0f, 1f).pos(2, 1f, 0f, 1f).pos(3, 0f, 1f, 1f).emit(); + default: + break; + } + } + + private static void drawRightSide(QuadEmitter quad, Direction dir) { + switch(dir) { + case NORTH: + quad.tag(TAG_RIGHT).pos(0, 1f, 0f, 0f).pos(1, 1f, 1f, 0f).pos(2, 1f, 0f, 1f).pos(3, 1f, 0f, 1f).emit(); + break; + case SOUTH: + quad.tag(TAG_RIGHT).pos(0, 0f, 0f, 0f).pos(1, 0f, 0f, 0f).pos(2, 0f, 0f, 1f).pos(3, 0f, 1f, 1f).emit(); + break; + case EAST: + quad.tag(TAG_RIGHT).pos(0, 0f, 0f, 1f).pos(1, 0f, 0f, 1f).pos(2, 1f, 0f, 1f).pos(3, 1f, 1f, 1f).emit(); + break; + case WEST: + quad.tag(TAG_RIGHT).pos(0, 0f, 0f, 0f).pos(1, 0f, 1f, 0f).pos(2, 1f, 0f, 0f).pos(3, 1f, 0f, 0f).emit(); + default: + break; + } + } + + private static void drawBack(QuadEmitter quad, Direction dir) { + switch(dir) { + case NORTH: + quad.tag(TAG_BACK).pos(0, 0f, 0f, 0f).pos(1, 0f, 1f, 0f).pos(2, 1f, 1f, 0f).pos(3, 1f, 0f, 0f).emit(); + break; + case SOUTH: + quad.tag(TAG_BACK).pos(0, 0f, 0f, 1f).pos(1, 1f, 0f, 1f).pos(2, 1f, 1f, 1f).pos(3, 0f, 1f, 1f).emit(); + break; + case EAST: + quad.tag(TAG_BACK).pos(0, 1f, 0f, 0f).pos(1, 1f, 1f, 0f).pos(2, 1f, 1f, 1f).pos(3, 1f, 0f, 1f).emit(); + break; + case WEST: + quad.tag(TAG_BACK).pos(0, 0f, 0f, 0f).pos(1, 0f, 0f, 1f).pos(2, 0f, 1f, 1f).pos(3, 0f, 1f, 0f).emit(); + default: + break; + } + } + + private static void drawBottom(QuadEmitter quad) { + quad.tag(TAG_BOTTOM).pos(0, 0f, 0f, 0f).pos(1, 1f, 0f, 0f).pos(2, 1f, 0f, 1f).pos(3, 0f, 0f, 1f).emit(); + } +} diff --git a/src/main/java/io/github/cottonmc/templates/model/SlopeMeshTransformer.java b/src/main/java/io/github/cottonmc/templates/model/SlopeMeshTransformer.java new file mode 100644 index 0000000..31ae5f7 --- /dev/null +++ b/src/main/java/io/github/cottonmc/templates/model/SlopeMeshTransformer.java @@ -0,0 +1,178 @@ +package io.github.cottonmc.templates.model; + +import io.github.cottonmc.templates.util.SpriteSet; +import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry; +import net.fabricmc.fabric.api.renderer.v1.RendererAccess; +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.rendering.data.v1.RenderAttachedBlockView; +import net.fabricmc.fabric.api.util.TriState; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.color.block.BlockColorProvider; +import net.minecraft.client.render.RenderLayers; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.texture.Sprite; +import net.minecraft.client.util.SpriteIdentifier; +import net.minecraft.item.ItemStack; +import net.minecraft.state.property.Properties; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.random.Random; +import net.minecraft.world.BlockRenderView; + +import java.util.function.Function; +import java.util.function.Supplier; + +public class SlopeMeshTransformer implements MeshTransformer { + public SlopeMeshTransformer(Function spriteLookup) { + this.sprites = new SpriteSet(spriteLookup); + } + + private final MinecraftClient minecraft = MinecraftClient.getInstance(); + private final SpriteSet sprites; + private final MaterialFinder finder = RendererAccess.INSTANCE.getRenderer().materialFinder(); + + private int color; + private Direction dir; + private RenderMaterial material; + + @Override + public MeshTransformer prepare(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier randomSupplier) { + dir = state.get(Properties.HORIZONTAL_FACING); + color = 0xffffff; + + Object renderAttach = ((RenderAttachedBlockView) blockView).getBlockEntityRenderAttachment(pos); + BlockState template = (renderAttach instanceof BlockState s) ? s : Blocks.AIR.getDefaultState(); + final Block block = template.getBlock(); + + if(block == Blocks.AIR) { + sprites.clear(); + material = finder.clear().blendMode(BlendMode.CUTOUT).find(); + } else { + material = finder.clear() + .disableDiffuse(false) + .ambientOcclusion(TriState.FALSE) + .blendMode(BlendMode.fromRenderLayer(RenderLayers.getBlockLayer(state))) + .find(); + + BakedModel model = minecraft.getBlockRenderManager().getModel(template); + sprites.prepare(model, randomSupplier.get()); + BlockColorProvider blockColor = ColorProviderRegistry.BLOCK.get(block); + if(blockColor != null) color = 0xff000000 | blockColor.getColor(template, blockView, pos, 1); + } + return this; + } + + @Override + public MeshTransformer prepare(ItemStack stack, Supplier randomSupplier) { + dir = Direction.NORTH; + color = 0xffffff; + sprites.clear(); + material = finder.clear().find(); + return this; + } + + @Override + public boolean transform(MutableQuadView quad) { + quad.material(material); + + final SpriteSet sprites = this.sprites; + switch(quad.tag()) { + case SlopeBaseMesh.TAG_SLOPE -> { + if(sprites.hasColor(Direction.UP)) quad.color(color, color, color, color); + paintSlope(quad, dir, sprites.getSprite(Direction.UP)); + } + case SlopeBaseMesh.TAG_LEFT -> { + final Direction leftDir = this.dir.rotateYCounterclockwise(); + if(sprites.hasColor(leftDir)) quad.color(color, color, color, color); + paintLeftSide(quad, dir, sprites.getSprite(leftDir)); + } + case SlopeBaseMesh.TAG_RIGHT -> { + final Direction rightDir = this.dir.rotateYClockwise(); + if(sprites.hasColor(rightDir)) quad.color(color, color, color, color); + paintRightSide(quad, dir, sprites.getSprite(rightDir)); + } + case SlopeBaseMesh.TAG_BACK -> { + if(sprites.hasColor(dir)) quad.color(color, color, color, color); + paintBack(quad, dir, sprites.getSprite(dir)); + } + case SlopeBaseMesh.TAG_BOTTOM -> { + if(sprites.hasColor(Direction.DOWN)) quad.color(color, color, color, color); + paintBottom(quad, sprites.getSprite(Direction.DOWN)); + } + } + return true; + } + + private static void paintSlope(MutableQuadView quad, Direction dir, Sprite sprite) { + switch(dir) { + case NORTH -> quad.uv(0, sprite.getMinU(), sprite.getMinV()) + .uv(1, sprite.getMinU(), sprite.getMaxV()) + .uv(2, sprite.getMaxU(), sprite.getMaxV()) + .uv(3, sprite.getMaxU(), sprite.getMinV()); + case SOUTH -> quad.uv(0, sprite.getMaxU(), sprite.getMaxV()) + .uv(1, sprite.getMaxU(), sprite.getMinV()) + .uv(2, sprite.getMinU(), sprite.getMinV()) + .uv(3, sprite.getMinU(), sprite.getMaxV()); + case EAST -> quad.uv(0, sprite.getMinU(), sprite.getMaxV()) + .uv(1, sprite.getMaxU(), sprite.getMaxV()) + .uv(2, sprite.getMaxU(), sprite.getMinV()) + .uv(3, sprite.getMinU(), sprite.getMinV()); + case WEST -> quad.uv(0, sprite.getMaxU(), sprite.getMinV()) + .uv(1, sprite.getMinU(), sprite.getMinV()) + .uv(2, sprite.getMinU(), sprite.getMaxV()) + .uv(3, sprite.getMaxU(), sprite.getMaxV()); + } + } + + private static void paintLeftSide(MutableQuadView quad, Direction dir, Sprite sprite) { + switch(dir) { + case NORTH, EAST, WEST -> quad.uv(0, sprite.getMinU(), sprite.getMaxV()) + .uv(1, sprite.getMaxU(), sprite.getMaxV()) + .uv(2, sprite.getMaxU(), sprite.getMinV()) + .uv(3, sprite.getMinU(), sprite.getMinV()); + case SOUTH -> quad.uv(0, sprite.getMaxU(), sprite.getMinV()) + .uv(1, sprite.getMinU(), sprite.getMinV()) + .uv(2, sprite.getMinU(), sprite.getMaxV()) + .uv(3, sprite.getMaxU(), sprite.getMaxV()); + } + } + + private static void paintRightSide(MutableQuadView quad, Direction dir, Sprite sprite) { + switch(dir) { + case NORTH, WEST -> quad.uv(0, sprite.getMaxU(), sprite.getMaxV()) + .uv(1, sprite.getMaxU(), sprite.getMinV()) + .uv(2, sprite.getMinU(), sprite.getMinV()) + .uv(3, sprite.getMinU(), sprite.getMaxV()); + case SOUTH, EAST -> quad.uv(0, sprite.getMinU(), sprite.getMinV()) + .uv(1, sprite.getMinU(), sprite.getMaxV()) + .uv(2, sprite.getMaxU(), sprite.getMaxV()) + .uv(3, sprite.getMaxU(), sprite.getMinV()); + } + } + + private static void paintBack(MutableQuadView quad, Direction dir, Sprite sprite) { + switch(dir) { + case NORTH, EAST -> quad.uv(0, sprite.getMaxU(), sprite.getMaxV()) + .uv(1, sprite.getMaxU(), sprite.getMinV()) + .uv(2, sprite.getMinU(), sprite.getMinV()) + .uv(3, sprite.getMinU(), sprite.getMaxV()); + case SOUTH, WEST -> quad.uv(0, sprite.getMinU(), sprite.getMaxV()) + .uv(1, sprite.getMaxU(), sprite.getMaxV()) + .uv(2, sprite.getMaxU(), sprite.getMinV()) + .uv(3, sprite.getMinU(), sprite.getMinV()); + } + } + + private static void paintBottom(MutableQuadView quad, Sprite sprite) { + quad.uv(0, sprite.getMinU(), sprite.getMaxV()) + .uv(1, sprite.getMaxU(), sprite.getMaxV()) + .uv(2, sprite.getMaxU(), sprite.getMinV()) + .uv(3, sprite.getMinU(), sprite.getMinV()); + } +} diff --git a/src/main/java/io/github/cottonmc/templates/model/SlopeModel.java b/src/main/java/io/github/cottonmc/templates/model/SlopeModel.java deleted file mode 100644 index d56c384..0000000 --- a/src/main/java/io/github/cottonmc/templates/model/SlopeModel.java +++ /dev/null @@ -1,370 +0,0 @@ -package io.github.cottonmc.templates.model; - -import io.github.cottonmc.templates.util.SpriteSet; -import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap; -import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry; -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.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.ModelHelper; -import net.fabricmc.fabric.api.rendering.data.v1.RenderAttachedBlockView; -import net.fabricmc.fabric.api.util.TriState; -import net.minecraft.block.Block; -import net.minecraft.block.BlockState; -import net.minecraft.block.Blocks; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.color.block.BlockColorProvider; -import net.minecraft.client.render.RenderLayer; -import net.minecraft.client.render.RenderLayers; -import net.minecraft.client.render.model.BakedModel; -import net.minecraft.client.texture.MissingSprite; -import net.minecraft.client.texture.Sprite; -import net.minecraft.item.ItemStack; -import net.minecraft.state.property.Properties; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Direction; -import net.minecraft.util.math.random.Random; -import net.minecraft.world.BlockRenderView; -import org.apache.commons.lang3.ObjectUtils; - -import java.util.function.Supplier; - -public class SlopeModel extends SimpleModel { - - private static final ThreadLocal TRANSFORMERS = ThreadLocal.withInitial(Transformer::new); - - public SlopeModel(BlockState blockState) { - super( - baseMesh(blockState), - TRANSFORMERS::get, - SpriteSet.FALLBACK, - ModelHelper.MODEL_TRANSFORM_BLOCK - ); - } - - private static Mesh baseMesh(BlockState state) { - final MeshBuilder builder = RENDERER.meshBuilder(); - final QuadEmitter quad = builder.getEmitter(); - final Direction dir = state.get(Properties.HORIZONTAL_FACING); - drawSlope(quad.color(-1, -1, -1, -1), dir); - drawLeftSide(quad.color(-1, -1, -1, -1), dir); - drawRightSide(quad.color(-1, -1, -1, -1), dir); - drawBack(quad.color(-1, -1, -1, -1), dir); - drawBottom(quad.color(-1, -1, -1, -1)); - return builder.build(); - } - - private static final int TAG_SLOPE = 0; - private static final int TAG_LEFT = 1; - private static final int TAG_RIGHT = 2; - private static final int TAG_BACK = 3; - private static final int TAG_BOTTOM = 4; - - private static void drawSlope(QuadEmitter quad, Direction dir) { - quad.tag(TAG_SLOPE); - switch(dir) { - case NORTH: - quad.pos(0, 0f, 1f, 0f).pos(1, 0f, 0f, 1f).pos(2, 1f, 0f, 1f).pos(3, 1f, 1f, 0f).emit(); - break; - case SOUTH: - quad.pos(0, 0f, 0f, 0f).pos(1, 0f, 1f, 1f).pos(2, 1f, 1f, 1f).pos(3, 1f, 0f, 0f).emit(); - break; - case EAST: - quad.pos(0, 0f, 0f, 0f).pos(1, 0f, 0f, 1f).pos(2, 1f, 1f, 1f).pos(3, 1f, 1f, 0f).emit(); - break; - case WEST: - quad.pos(0, 0f, 1f, 0f).pos(1, 0f, 1f, 1f).pos(2, 1f, 0f, 1f).pos(3, 1f, 0f, 0f).emit(); - default: - break; - } - } - - private static void drawLeftSide(QuadEmitter quad, Direction dir) { - switch(dir) { - case NORTH: - quad.tag(TAG_LEFT).pos(0, 0f, 0f, 0f).pos(1, 0f, 0f, 1f).pos(2, 0f, 0f, 1f).pos(3, 0f, 1f, 0f).emit(); - break; - case SOUTH: - quad.tag(TAG_LEFT).pos(0, 1f, 0f, 0f).pos(1, 1f, 1f, 1f).pos(2, 1f, 0f, 1f).pos(3, 1f, 0f, 1f).emit(); - break; - case EAST: - quad.tag(TAG_LEFT).pos(0, 1f, 0f, 0f).pos(1, 0f, 0f, 0f).pos(2, 0f, 0f, 0f).pos(3, 1f, 1f, 0f).emit(); - break; - case WEST: - quad.tag(TAG_LEFT).pos(0, 0f, 0f, 1f).pos(1, 1f, 0f, 1f).pos(2, 1f, 0f, 1f).pos(3, 0f, 1f, 1f).emit(); - default: - break; - } - } - - private static void drawRightSide(QuadEmitter quad, Direction dir) { - switch(dir) { - case NORTH: - quad.tag(TAG_RIGHT).pos(0, 1f, 0f, 0f).pos(1, 1f, 1f, 0f).pos(2, 1f, 0f, 1f).pos(3, 1f, 0f, 1f).emit(); - break; - case SOUTH: - quad.tag(TAG_RIGHT).pos(0, 0f, 0f, 0f).pos(1, 0f, 0f, 0f).pos(2, 0f, 0f, 1f).pos(3, 0f, 1f, 1f).emit(); - break; - case EAST: - quad.tag(TAG_RIGHT).pos(0, 0f, 0f, 1f).pos(1, 0f, 0f, 1f).pos(2, 1f, 0f, 1f).pos(3, 1f, 1f, 1f).emit(); - break; - case WEST: - quad.tag(TAG_RIGHT).pos(0, 0f, 0f, 0f).pos(1, 0f, 1f, 0f).pos(2, 1f, 0f, 0f).pos(3, 1f, 0f, 0f).emit(); - default: - break; - } - } - - private static void drawBack(QuadEmitter quad, Direction dir) { - switch(dir) { - case NORTH: - quad.tag(TAG_BACK).pos(0, 0f, 0f, 0f).pos(1, 0f, 1f, 0f).pos(2, 1f, 1f, 0f).pos(3, 1f, 0f, 0f).emit(); - break; - case SOUTH: - quad.tag(TAG_BACK).pos(0, 0f, 0f, 1f).pos(1, 1f, 0f, 1f).pos(2, 1f, 1f, 1f).pos(3, 0f, 1f, 1f).emit(); - break; - case EAST: - quad.tag(TAG_BACK).pos(0, 1f, 0f, 0f).pos(1, 1f, 1f, 0f).pos(2, 1f, 1f, 1f).pos(3, 1f, 0f, 1f).emit(); - break; - case WEST: - quad.tag(TAG_BACK).pos(0, 0f, 0f, 0f).pos(1, 0f, 0f, 1f).pos(2, 0f, 1f, 1f).pos(3, 0f, 1f, 0f).emit(); - default: - break; - } - } - - private static void drawBottom(QuadEmitter quad) { - quad.tag(TAG_BOTTOM).pos(0, 0f, 0f, 0f).pos(1, 1f, 0f, 0f).pos(2, 1f, 0f, 1f).pos(3, 0f, 0f, 1f).emit(); - } - - private static class Transformer implements MeshTransformer { - private final MinecraftClient minecraft = MinecraftClient.getInstance(); - private final SpriteSet sprites = new SpriteSet(); - private final MaterialFinder finder = RENDERER.materialFinder(); - - private int color; - private Direction dir; - private RenderMaterial material; - - @Override - public MeshTransformer prepare(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier randomSupplier) { - dir = state.get(Properties.HORIZONTAL_FACING); - color = 0xffffff; - final BlockState template = ObjectUtils.defaultIfNull((BlockState) ((RenderAttachedBlockView) blockView).getBlockEntityRenderAttachment(pos), Blocks.AIR.getDefaultState()); - final Block block = template.getBlock(); - - if(block == Blocks.AIR) { - sprites.clear(); - material = finder.clear().blendMode(BlendMode.CUTOUT).find(); - } else { - material = finder.clear() - .disableDiffuse(false) - .ambientOcclusion(TriState.FALSE) - .blendMode(BlendMode.fromRenderLayer(RenderLayers.getBlockLayer(state))) - .find(); - - BakedModel model = minecraft.getBlockRenderManager().getModel(template); - sprites.prepare(model, randomSupplier.get()); - BlockColorProvider blockColor = ColorProviderRegistry.BLOCK.get(block); - if(blockColor != null) { - color = 0xff000000 | blockColor.getColor(template, blockView, pos, 1); - } - } - return this; - } - - @Override - public MeshTransformer prepare(ItemStack stack, Supplier randomSupplier) { - dir = Direction.NORTH; - color = 0xffffff; - sprites.clear(); - material = finder.clear().find(); - return this; - } - - @Override - public boolean transform(MutableQuadView quad) { - quad.material(material); - - final SpriteSet sprites = this.sprites; - switch(quad.tag()) { - - case TAG_SLOPE: - if(sprites.hasColor(Direction.UP)) { - quad.color(color, color, color, color); - } - paintSlope(quad, dir, sprites.getSprite(Direction.UP)); - break; - - - case TAG_LEFT: - final Direction leftDir = this.dir.rotateYCounterclockwise(); - if(sprites.hasColor(leftDir)) { - quad.color(color, color, color, color); - } - paintLeftSide(quad, dir, sprites.getSprite(leftDir)); - break; - - - case TAG_RIGHT: { - final Direction rightDir = this.dir.rotateYClockwise(); - if(sprites.hasColor(rightDir)) { - quad.color(color, color, color, color); - } - paintRightSide(quad, dir, sprites.getSprite(rightDir)); - break; - } - - case TAG_BACK: { - if(sprites.hasColor(dir)) { - quad.color(color, color, color, color); - } - paintBack(quad, dir, sprites.getSprite(dir)); - break; - } - - case TAG_BOTTOM: { - if(sprites.hasColor(Direction.DOWN)) { - quad.color(color, color, color, color); - } - paintBottom(quad, sprites.getSprite(Direction.DOWN)); - break; - } - - default: - } - return true; - } - - private static void paintSlope(MutableQuadView quad, Direction dir, Sprite sprite) { - switch(dir) { - case NORTH: - quad.uv(0, sprite.getMinU(), sprite.getMinV()) - .uv(1, sprite.getMinU(), sprite.getMaxV()) - .uv(2, sprite.getMaxU(), sprite.getMaxV()) - .uv(3, sprite.getMaxU(), sprite.getMinV()); - break; - case SOUTH: - quad.uv(0, sprite.getMaxU(), sprite.getMaxV()) - .uv(1, sprite.getMaxU(), sprite.getMinV()) - .uv(2, sprite.getMinU(), sprite.getMinV()) - .uv(3, sprite.getMinU(), sprite.getMaxV()); - break; - case EAST: - quad.uv(0, sprite.getMinU(), sprite.getMaxV()) - .uv(1, sprite.getMaxU(), sprite.getMaxV()) - .uv(2, sprite.getMaxU(), sprite.getMinV()) - .uv(3, sprite.getMinU(), sprite.getMinV()); - break; - case WEST: - quad.uv(0, sprite.getMaxU(), sprite.getMinV()) - .uv(1, sprite.getMinU(), sprite.getMinV()) - .uv(2, sprite.getMinU(), sprite.getMaxV()) - .uv(3, sprite.getMaxU(), sprite.getMaxV()); - default: - break; - } - } - - private static void paintLeftSide(MutableQuadView quad, Direction dir, Sprite sprite) { - switch(dir) { - case NORTH: - quad.uv(0, sprite.getMinU(), sprite.getMaxV()) - .uv(1, sprite.getMaxU(), sprite.getMaxV()) - .uv(2, sprite.getMaxU(), sprite.getMinV()) - .uv(3, sprite.getMinU(), sprite.getMinV()); - break; - case SOUTH: - quad.uv(0, sprite.getMaxU(), sprite.getMinV()) - .uv(1, sprite.getMinU(), sprite.getMinV()) - .uv(2, sprite.getMinU(), sprite.getMaxV()) - .uv(3, sprite.getMaxU(), sprite.getMaxV()); - break; - case EAST: - quad.uv(0, sprite.getMinU(), sprite.getMaxV()) - .uv(1, sprite.getMaxU(), sprite.getMaxV()) - .uv(2, sprite.getMaxU(), sprite.getMinV()) - .uv(3, sprite.getMinU(), sprite.getMinV()); - break; - case WEST: - quad.uv(0, sprite.getMinU(), sprite.getMaxV()) - .uv(1, sprite.getMaxU(), sprite.getMaxV()) - .uv(2, sprite.getMaxU(), sprite.getMinV()) - .uv(3, sprite.getMinU(), sprite.getMinV()); - default: - break; - } - } - - private static void paintRightSide(MutableQuadView quad, Direction dir, Sprite sprite) { - switch(dir) { - case NORTH: - quad.uv(0, sprite.getMaxU(), sprite.getMaxV()) - .uv(1, sprite.getMaxU(), sprite.getMinV()) - .uv(2, sprite.getMinU(), sprite.getMinV()) - .uv(3, sprite.getMinU(), sprite.getMaxV()); - break; - case SOUTH: - quad.uv(0, sprite.getMinU(), sprite.getMinV()) - .uv(1, sprite.getMinU(), sprite.getMaxV()) - .uv(2, sprite.getMaxU(), sprite.getMaxV()) - .uv(3, sprite.getMaxU(), sprite.getMinV()); - break; - case EAST: - quad.uv(0, sprite.getMinU(), sprite.getMinV()) - .uv(1, sprite.getMinU(), sprite.getMaxV()) - .uv(2, sprite.getMaxU(), sprite.getMaxV()) - .uv(3, sprite.getMaxU(), sprite.getMinV()); - break; - case WEST: - quad.uv(0, sprite.getMaxU(), sprite.getMaxV()) - .uv(1, sprite.getMaxU(), sprite.getMinV()) - .uv(2, sprite.getMinU(), sprite.getMinV()) - .uv(3, sprite.getMinU(), sprite.getMaxV()); - default: - break; - } - } - - private static void paintBack(MutableQuadView quad, Direction dir, Sprite sprite) { - switch(dir) { - case NORTH: - quad.uv(0, sprite.getMaxU(), sprite.getMaxV()) - .uv(1, sprite.getMaxU(), sprite.getMinV()) - .uv(2, sprite.getMinU(), sprite.getMinV()) - .uv(3, sprite.getMinU(), sprite.getMaxV()); - break; - case SOUTH: - quad.uv(0, sprite.getMinU(), sprite.getMaxV()) - .uv(1, sprite.getMaxU(), sprite.getMaxV()) - .uv(2, sprite.getMaxU(), sprite.getMinV()) - .uv(3, sprite.getMinU(), sprite.getMinV()); - break; - case EAST: - quad.uv(0, sprite.getMaxU(), sprite.getMaxV()) - .uv(1, sprite.getMaxU(), sprite.getMinV()) - .uv(2, sprite.getMinU(), sprite.getMinV()) - .uv(3, sprite.getMinU(), sprite.getMaxV()); - break; - case WEST: - quad.uv(0, sprite.getMinU(), sprite.getMaxV()) - .uv(1, sprite.getMaxU(), sprite.getMaxV()) - .uv(2, sprite.getMaxU(), sprite.getMinV()) - .uv(3, sprite.getMinU(), sprite.getMinV()); - default: - break; - } - } - - private static void paintBottom(MutableQuadView quad, Sprite sprite) { - quad.uv(0, sprite.getMinU(), sprite.getMaxV()) - .uv(1, sprite.getMaxU(), sprite.getMaxV()) - .uv(2, sprite.getMaxU(), sprite.getMinV()) - .uv(3, sprite.getMinU(), sprite.getMinV()); - } - } -} diff --git a/src/main/java/io/github/cottonmc/templates/model/SlopeUnbakedModel.java b/src/main/java/io/github/cottonmc/templates/model/SlopeUnbakedModel.java new file mode 100644 index 0000000..3fc476a --- /dev/null +++ b/src/main/java/io/github/cottonmc/templates/model/SlopeUnbakedModel.java @@ -0,0 +1,36 @@ +package io.github.cottonmc.templates.model; + +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.client.render.block.BlockModels; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.client.render.model.Baker; +import net.minecraft.client.render.model.ModelBakeSettings; +import net.minecraft.client.render.model.UnbakedModel; +import net.minecraft.client.texture.Sprite; +import net.minecraft.client.util.SpriteIdentifier; +import net.minecraft.util.Identifier; + +import java.util.Collection; +import java.util.Collections; +import java.util.function.Function; + +public record SlopeUnbakedModel(BlockState slopeState) implements UnbakedModel { + @Override + public Collection getModelDependencies() { + return Collections.emptyList(); + } + + @Override + public void setParents(Function function) { + //nothing to see here + } + + @Override + public BakedModel bake(Baker baker, Function function, ModelBakeSettings modelBakeSettings, Identifier identifier) { + //TODO: weird, should use my own model instead of STONE + BakedModel baseModel = baker.bake(BlockModels.getModelId(Blocks.SANDSTONE_STAIRS.getDefaultState()), modelBakeSettings); + + return new SlopeBakedModel(baseModel, slopeState, function); + } +} diff --git a/src/main/java/io/github/cottonmc/templates/model/TemplateModelVariantProvider.java b/src/main/java/io/github/cottonmc/templates/model/TemplateModelVariantProvider.java index 96c7f62..2b5d5c2 100644 --- a/src/main/java/io/github/cottonmc/templates/model/TemplateModelVariantProvider.java +++ b/src/main/java/io/github/cottonmc/templates/model/TemplateModelVariantProvider.java @@ -24,10 +24,10 @@ public class TemplateModelVariantProvider implements ModelVariantProvider { return variants.get(modelId); } - public void registerTemplateModels(Block block, BlockState itemState, Function model) { + public void registerTemplateModels2(Block block, BlockState itemState, Function model) { for(BlockState state : block.getStateManager().getStates()) { - variants.put(BlockModels.getModelId(state), (SimpleUnbakedModel) () -> model.apply(state)); + variants.put(BlockModels.getModelId(state), model.apply(state)); } - variants.put(new ModelIdentifier(Registries.ITEM.getId(block.asItem()), "inventory"), (SimpleUnbakedModel) () -> model.apply(itemState)); + variants.put(new ModelIdentifier(Registries.ITEM.getId(block.asItem()), "inventory"), model.apply(itemState)); } } diff --git a/src/main/java/io/github/cottonmc/templates/util/SpriteSet.java b/src/main/java/io/github/cottonmc/templates/util/SpriteSet.java index 3e28754..a640fdf 100644 --- a/src/main/java/io/github/cottonmc/templates/util/SpriteSet.java +++ b/src/main/java/io/github/cottonmc/templates/util/SpriteSet.java @@ -1,35 +1,41 @@ package io.github.cottonmc.templates.util; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper; -import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BakedQuad; import net.minecraft.client.texture.MissingSprite; import net.minecraft.client.texture.Sprite; import net.minecraft.client.texture.SpriteAtlasTexture; +import net.minecraft.client.util.SpriteIdentifier; import net.minecraft.util.Identifier; import net.minecraft.util.math.Direction; import net.minecraft.util.math.random.Random; +import java.util.EnumMap; import java.util.List; +import java.util.function.Function; public class SpriteSet { - private Object2ObjectOpenHashMap quads = new Object2ObjectOpenHashMap<>(); - private boolean isDefault = true; - public static final Sprite DEFAULT = findSprite(new Identifier("minecraft:block/scaffolding_top")); - public static final Sprite FALLBACK = findSprite(MissingSprite.getMissingSpriteId()); - - private static Sprite findSprite(Identifier id) { - Sprite s = MinecraftClient.getInstance().getBakedModelManager().getAtlas(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE).getSprite(id); - if(false) throw new IllegalStateException("null sprite " + id); - else return s; - } - - public SpriteSet() { + public SpriteSet(Function spriteLookup) { + //TODO: I can probably find these from a public static location + // I think I tried that, though, and they were null. But I might have been doing it too early + // Regardless, they should not be stored in static fields (resource-reload could invalidate them) + this.defaultSprite = spriteLookup.apply(new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, new Identifier("minecraft:block/scaffolding_top"))); + this.missingSprite = spriteLookup.apply(new SpriteIdentifier(SpriteAtlasTexture.BLOCK_ATLAS_TEXTURE, MissingSprite.getMissingSpriteId())); + + if(defaultSprite == null) throw new IllegalStateException("defaultSprite == null!"); + if(missingSprite == null) throw new IllegalStateException("missingSprite == null!"); + clear(); } + private static final Direction[] DIRECTIONS = Direction.values(); + + private final Sprite defaultSprite; + private final Sprite missingSprite; + + private final EnumMap quads = new EnumMap<>(Direction.class); + private boolean isDefault = true; + /** Allow re-use of instances to avoid allocation in render loop */ public void clear() { isDefault = true; @@ -40,26 +46,21 @@ public class SpriteSet { public void prepare(BakedModel model, Random rand) { this.quads.clear(); isDefault = false; - // avoid Direction.values() in hot loop - for thread safety may generate new array instances - //for (Direction dir : Direction.values()) { - for(int i = 0; i < 6; i++) { - final Direction dir = ModelHelper.faceFromIndex(i); + + for(Direction dir : DIRECTIONS) { List quads = model.getQuads(null, dir, rand); if(!quads.isEmpty()) this.quads.put(dir, quads.get(0)); } } public Sprite getSprite(Direction dir) { - //TODO - if(true) return MinecraftClient.getInstance().getBakedModelManager().getMissingModel().getParticleSprite(); - - if(isDefault) return DEFAULT; + if(isDefault) return defaultSprite; BakedQuad quad = quads.get(dir); - if(quad == null) return FALLBACK; + if(quad == null) return missingSprite; Sprite sprite = quad.getSprite(); - if(sprite == null) return FALLBACK; + if(sprite == null) return missingSprite; return sprite; }