diff --git a/build.gradle b/build.gradle index c162ed6..84fae0e 100755 --- a/build.gradle +++ b/build.gradle @@ -44,7 +44,7 @@ dependencies { minecraft "com.mojang:minecraft:1.14.2" mappings "net.fabricmc:yarn:1.14.2+build.7" modCompile "net.fabricmc:fabric-loader:0.4.8+build.154" - modCompile "net.fabricmc.fabric-api:fabric-api:0.3.0+build.179" + modCompile "net.fabricmc.fabric-api:fabric-api:0.3.0+build.185" implementation "com.google.code.findbugs:jsr305:3.0.2" diff --git a/src/main/java/io/github/cottonmc/slopetest/SlopeTest.java b/src/main/java/io/github/cottonmc/slopetest/SlopeTest.java index 89d8605..79fb26a 100644 --- a/src/main/java/io/github/cottonmc/slopetest/SlopeTest.java +++ b/src/main/java/io/github/cottonmc/slopetest/SlopeTest.java @@ -15,7 +15,8 @@ public class SlopeTest implements ModInitializer { public static final String MODID = "slopetest"; public static final Block SLOPE = register("slope", new SlopeTestBlock(), ItemGroup.DECORATIONS); - public static final BlockEntityType SLOPE_ENTITY = register("slope", SlopeTestEntity::new, SLOPE); + @SuppressWarnings("unchecked") + public static final BlockEntityType SLOPE_ENTITY = register("slope", SlopeTestEntity::new, SLOPE); @Override public void onInitialize() { @@ -29,7 +30,8 @@ public class SlopeTest implements ModInitializer { return block; } - public static BlockEntityType register(String name, Supplier be, Block...blocks) { + @SuppressWarnings("rawtypes") + public static BlockEntityType register(String name, Supplier be, Block...blocks) { return Registry.register(Registry.BLOCK_ENTITY, new Identifier(MODID, name), BlockEntityType.Builder.create(be, blocks).build(null)); } diff --git a/src/main/java/io/github/cottonmc/slopetest/SlopeTestClient.java b/src/main/java/io/github/cottonmc/slopetest/SlopeTestClient.java index 4682963..df51c25 100644 --- a/src/main/java/io/github/cottonmc/slopetest/SlopeTestClient.java +++ b/src/main/java/io/github/cottonmc/slopetest/SlopeTestClient.java @@ -1,13 +1,13 @@ package io.github.cottonmc.slopetest; -import io.github.cottonmc.slopetest.block.entity.SlopeTestEntity; -import io.github.cottonmc.slopetest.block.entity.render.SlopeTestRenderer; +import io.github.cottonmc.slopetest.model.SlopeModelVariantProvider; import net.fabricmc.api.ClientModInitializer; -import net.fabricmc.fabric.api.client.render.BlockEntityRendererRegistry; +import net.fabricmc.fabric.api.client.model.ModelLoadingRegistry; public class SlopeTestClient implements ClientModInitializer { @Override public void onInitializeClient() { - BlockEntityRendererRegistry.INSTANCE.register(SlopeTestEntity.class, new SlopeTestRenderer()); + //BlockEntityRendererRegistry.INSTANCE.register(SlopeTestEntity.class, new SlopeTestRenderer()); + ModelLoadingRegistry.INSTANCE.registerVariantProvider(rm -> new SlopeModelVariantProvider()); } } diff --git a/src/main/java/io/github/cottonmc/slopetest/block/SlopeTestBlock.java b/src/main/java/io/github/cottonmc/slopetest/block/SlopeTestBlock.java index f42f607..b4960ad 100644 --- a/src/main/java/io/github/cottonmc/slopetest/block/SlopeTestBlock.java +++ b/src/main/java/io/github/cottonmc/slopetest/block/SlopeTestBlock.java @@ -85,10 +85,10 @@ public class SlopeTestBlock extends Block implements BlockEntityProvider { return false; } - @Override - public BlockRenderType getRenderType(BlockState state) { - return BlockRenderType.INVISIBLE; - } +// @Override +// public BlockRenderType getRenderType(BlockState state) { +// return BlockRenderType.INVISIBLE; +// } @Override public void onBlockRemoved(BlockState state, World world, BlockPos pos, BlockState newState, boolean boolean_1) { diff --git a/src/main/java/io/github/cottonmc/slopetest/block/entity/SlopeTestEntity.java b/src/main/java/io/github/cottonmc/slopetest/block/entity/SlopeTestEntity.java index 0da7f23..4a975c5 100644 --- a/src/main/java/io/github/cottonmc/slopetest/block/entity/SlopeTestEntity.java +++ b/src/main/java/io/github/cottonmc/slopetest/block/entity/SlopeTestEntity.java @@ -3,18 +3,18 @@ package io.github.cottonmc.slopetest.block.entity; import io.github.cottonmc.slopetest.SlopeTest; import io.github.cottonmc.slopetest.util.BlockStateUtil; import net.fabricmc.fabric.api.block.entity.BlockEntityClientSerializable; +import net.fabricmc.fabric.api.rendering.data.v1.RenderAttachmentBlockEntity; import net.fabricmc.fabric.api.server.PlayerStream; import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; import net.minecraft.block.entity.BlockEntity; +import net.minecraft.client.world.ClientWorld; import net.minecraft.nbt.CompoundTag; import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.util.Identifier; -import net.minecraft.util.registry.Registry; - -public class SlopeTestEntity extends BlockEntity implements BlockEntityClientSerializable { - private BlockState renderedState = Blocks.AIR.getDefaultState(); +public class SlopeTestEntity extends BlockEntity implements BlockEntityClientSerializable, RenderAttachmentBlockEntity { + private BlockState renderedState = Blocks.AIR.getDefaultState(); + public SlopeTestEntity() { super(SlopeTest.SLOPE_ENTITY); } @@ -32,6 +32,9 @@ public class SlopeTestEntity extends BlockEntity implements BlockEntityClientSer public void fromTag(CompoundTag tag) { super.fromTag(tag); renderedState = BlockStateUtil.fromTag(tag); + if (world.isClient) { + ((ClientWorld)world).scheduleBlockRender(pos); + } } @Override @@ -61,4 +64,9 @@ public class SlopeTestEntity extends BlockEntity implements BlockEntityClientSer } } } + + @Override + public BlockState getRenderAttachmentData() { + return renderedState; + } } diff --git a/src/main/java/io/github/cottonmc/slopetest/block/entity/render/SlopeTestRenderer.java b/src/main/java/io/github/cottonmc/slopetest/block/entity/render/SlopeTestRenderer.java deleted file mode 100644 index 6cbe9c3..0000000 --- a/src/main/java/io/github/cottonmc/slopetest/block/entity/render/SlopeTestRenderer.java +++ /dev/null @@ -1,204 +0,0 @@ -package io.github.cottonmc.slopetest.block.entity.render; - -import com.mojang.blaze3d.platform.GlStateManager; -import io.github.cottonmc.slopetest.util.SpriteSet; -import io.github.cottonmc.slopetest.block.entity.SlopeTestEntity; -import net.fabricmc.fabric.api.client.render.ColorProviderRegistry; -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.BufferBuilder; -import net.minecraft.client.render.Tessellator; -import net.minecraft.client.render.VertexFormats; -import net.minecraft.client.render.block.entity.BlockEntityRenderer; -import net.minecraft.client.render.model.BakedModel; -import net.minecraft.client.texture.Sprite; -import net.minecraft.client.texture.SpriteAtlasTexture; -import net.minecraft.state.property.Properties; -import net.minecraft.util.math.Direction; -import org.lwjgl.opengl.GL11; - -public class SlopeTestRenderer extends BlockEntityRenderer { - - @Override - public void render(SlopeTestEntity be, double x, double y, double z, float partialTicks, int destroyStage) { - final Tessellator tessellator = Tessellator.getInstance(); - final BufferBuilder buffer = tessellator.getBufferBuilder(); - MinecraftClient minecraft = MinecraftClient.getInstance(); - buffer.setOffset(x, y, z); - GlStateManager.enableBlend(); - GlStateManager.disableAlphaTest(); - GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); - renderManager.textureManager.bindTexture(SpriteAtlasTexture.BLOCK_ATLAS_TEX); - BlockState state = getWorld().getBlockState(be.getPos()); - Direction dir = state.get(Properties.HORIZONTAL_FACING); - SpriteSet sprites; - int color = 0xffffff; - if (be.getRenderedState().getBlock() != Blocks.AIR) { - BlockState renderedState = be.getRenderedState(); - BakedModel model = minecraft.getBlockRenderManager().getModel(renderedState); - sprites = new SpriteSet(model); - BlockColorProvider blockColor = ColorProviderRegistry.BLOCK.get(be.getRenderedState().getBlock()); - if (blockColor != null) { - color = blockColor.getColor(renderedState, be.getWorld(), be.getPos(), 1); - } - } else { - sprites = new SpriteSet(minecraft.getSpriteAtlas().getSprite("minecraft:block/scaffolding_top"), false); - } - buffer.begin(GL11.GL_QUADS, VertexFormats.POSITION_UV_COLOR); - drawSlope(dir, sprites.getSprite(Direction.UP), buffer, sprites.hasColor(Direction.UP)? color : 0xffffff); - drawLeftSide(dir, sprites.getSprite(dir.rotateYCounterclockwise()), buffer, sprites.hasColor(dir.rotateYCounterclockwise())? color : 0xffffff); - drawRightSide(dir, sprites.getSprite(dir.rotateYClockwise()), buffer, sprites.hasColor(dir.rotateYClockwise())? color: 0xffffff); - drawBack(dir, sprites.getSprite(dir), buffer, sprites.hasColor(dir)? color : 0xffffff); - drawBottom(sprites.getSprite(Direction.DOWN), buffer, sprites.hasColor(Direction.DOWN)? color : 0xffffff); - GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F); - tessellator.draw(); - GlStateManager.disableBlend(); - GlStateManager.enableAlphaTest(); - buffer.setOffset(0.0d, 0.0d, 0.0d); - super.render(be, x, y, z, partialTicks, destroyStage); - } - - public static void drawSlope(Direction dir, Sprite sprite, BufferBuilder buffer, int color) { - float r = (float)(color >> 16 & 255) / 255.0F; - float g = (float)(color >> 8 & 255) / 255.0F; - float b = (float)(color & 255) / 255.0F; - switch (dir) { - case NORTH: - buffer.vertex(0f, 1f, 0f).texture(sprite.getMinU(), sprite.getMinV()).color(r, g, b, 1f).next(); - buffer.vertex(0f, 0f, 1f).texture(sprite.getMinU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 0f, 1f).texture(sprite.getMaxU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 1f, 0f).texture(sprite.getMaxU(), sprite.getMinV()).color(r, g, b, 1f).next(); - break; - case SOUTH: - buffer.vertex(0f, 0f, 0f).texture(sprite.getMaxU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - buffer.vertex(0f, 1f, 1f).texture(sprite.getMaxU(), sprite.getMinV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 1f, 1f).texture(sprite.getMinU(), sprite.getMinV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 0f, 0f).texture(sprite.getMinU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - break; - case EAST: - buffer.vertex(0f, 0f, 0f).texture(sprite.getMinU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - buffer.vertex(0f, 0f, 1f).texture(sprite.getMaxU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 1f, 1f).texture(sprite.getMaxU(), sprite.getMinV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 1f, 0f).texture(sprite.getMinU(), sprite.getMinV()).color(r, g, b, 1f).next(); - break; - case WEST: - buffer.vertex(0f, 1f, 0f).texture(sprite.getMaxU(), sprite.getMinV()).color(r, g, b, 1f).next(); - buffer.vertex(0f, 1f, 1f).texture(sprite.getMinU(), sprite.getMinV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 0f, 1f).texture(sprite.getMinU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 0f, 0f).texture(sprite.getMaxU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - default: - break; - } - } - - public static void drawLeftSide(Direction dir, Sprite sprite, BufferBuilder buffer, int color) { - float r = (float)(color >> 16 & 255) / 255.0F; - float g = (float)(color >> 8 & 255) / 255.0F; - float b = (float)(color & 255) / 255.0F; - switch(dir) { - case NORTH: - buffer.vertex(0f, 0f, 0f).texture(sprite.getMinU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - buffer.vertex(0f, 0f, 1f).texture(sprite.getMaxU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - buffer.vertex(0f, 0f, 1f).texture(sprite.getMaxU(), sprite.getMinV()).color(r, g, b, 1f).next(); - buffer.vertex(0f, 1f, 0f).texture(sprite.getMinU(), sprite.getMinV()).color(r, g, b, 1f).next(); - break; - case SOUTH: - buffer.vertex(1f, 0f, 0f).texture(sprite.getMaxU(), sprite.getMinV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 1f, 1f).texture(sprite.getMinU(), sprite.getMinV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 0f, 1f).texture(sprite.getMinU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 0f, 0f).texture(sprite.getMaxU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - break; - case EAST: - buffer.vertex(1f, 0f, 0f).texture(sprite.getMinU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - buffer.vertex(0f, 0f, 0f).texture(sprite.getMaxU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - buffer.vertex(0f, 0f, 0f).texture(sprite.getMaxU(), sprite.getMinV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 1f, 0f).texture(sprite.getMinU(), sprite.getMinV()).color(r, g, b, 1f).next(); - break; - case WEST: - buffer.vertex(0f, 0f, 1f).texture(sprite.getMinU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 0f, 1f).texture(sprite.getMaxU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 0f, 1f).texture(sprite.getMaxU(), sprite.getMinV()).color(r, g, b, 1f).next(); - buffer.vertex(0f, 1f, 1f).texture(sprite.getMinU(), sprite.getMinV()).color(r, g, b, 1f).next(); - default: - break; - } - } - - public static void drawRightSide(Direction dir, Sprite sprite, BufferBuilder buffer, int color) { - float r = (float)(color >> 16 & 255) / 255.0F; - float g = (float)(color >> 8 & 255) / 255.0F; - float b = (float)(color & 255) / 255.0F; - switch(dir) { - case NORTH: - buffer.vertex(1f, 0f, 0f).texture(sprite.getMaxU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 1f, 0f).texture(sprite.getMaxU(), sprite.getMinV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 0f, 1f).texture(sprite.getMinU(), sprite.getMinV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 0f, 1f).texture(sprite.getMinU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - break; - case SOUTH: - buffer.vertex(0f, 0f, 0f).texture(sprite.getMinU(), sprite.getMinV()).color(r, g, b, 1f).next(); - buffer.vertex(0f, 0f, 0f).texture(sprite.getMinU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - buffer.vertex(0f, 0f, 1f).texture(sprite.getMaxU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - buffer.vertex(0f, 1f, 1f).texture(sprite.getMaxU(), sprite.getMinV()).color(r, g, b, 1f).next(); - break; - case EAST: - buffer.vertex(0f, 0f, 1f).texture(sprite.getMinU(), sprite.getMinV()).color(r, g, b, 1f).next(); - buffer.vertex(0f, 0f, 1f).texture(sprite.getMinU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 0f, 1f).texture(sprite.getMaxU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 1f, 1f).texture(sprite.getMaxU(), sprite.getMinV()).color(r, g, b, 1f).next(); - break; - case WEST: - buffer.vertex(0f, 0f, 0f).texture(sprite.getMaxU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - buffer.vertex(0f, 1f, 0f).texture(sprite.getMaxU(), sprite.getMinV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 0f, 0f).texture(sprite.getMinU(), sprite.getMinV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 0f, 0f).texture(sprite.getMinU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - default: - break; - } - } - - public static void drawBack(Direction dir, Sprite sprite, BufferBuilder buffer, int color) { - float r = (float)(color >> 16 & 255) / 255.0F; - float g = (float)(color >> 8 & 255) / 255.0F; - float b = (float)(color & 255) / 255.0F; - switch(dir) { - case NORTH: - buffer.vertex(0f, 0f, 0f).texture(sprite.getMaxU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - buffer.vertex(0f, 1f, 0f).texture(sprite.getMaxU(), sprite.getMinV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 1f, 0f).texture(sprite.getMinU(), sprite.getMinV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 0f, 0f).texture(sprite.getMinU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - break; - case SOUTH: - buffer.vertex(0f, 0f, 1f).texture(sprite.getMinU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 0f, 1f).texture(sprite.getMaxU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 1f, 1f).texture(sprite.getMaxU(), sprite.getMinV()).color(r, g, b, 1f).next(); - buffer.vertex(0f, 1f, 1f).texture(sprite.getMinU(), sprite.getMinV()).color(r, g, b, 1f).next(); - break; - case EAST: - buffer.vertex(1f, 0f, 0f).texture(sprite.getMaxU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 1f, 0f).texture(sprite.getMaxU(), sprite.getMinV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 1f, 1f).texture(sprite.getMinU(), sprite.getMinV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 0f, 1f).texture(sprite.getMinU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - break; - case WEST: - buffer.vertex(0f, 0f, 0f).texture(sprite.getMinU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - buffer.vertex(0f, 0f, 1f).texture(sprite.getMaxU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - buffer.vertex(0f, 1f, 1f).texture(sprite.getMaxU(), sprite.getMinV()).color(r, g, b, 1f).next(); - buffer.vertex(0f, 1f, 0f).texture(sprite.getMinU(), sprite.getMinV()).color(r, g, b, 1f).next(); - default: - break; - } - } - - public static void drawBottom(Sprite sprite, BufferBuilder buffer, int color) { - float r = (float)(color >> 16 & 255) / 255.0F; - float g = (float)(color >> 8 & 255) / 255.0F; - float b = (float)(color & 255) / 255.0F; - buffer.vertex(0f, 0f, 0f).texture(sprite.getMinU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 0f, 0f).texture(sprite.getMaxU(), sprite.getMaxV()).color(r, g, b, 1f).next(); - buffer.vertex(1f, 0f, 1f).texture(sprite.getMaxU(), sprite.getMinV()).color(r, g, b, 1f).next(); - buffer.vertex(0f, 0f, 1f).texture(sprite.getMinU(), sprite.getMinV()).color(r, g, b, 1f).next(); - } -} diff --git a/src/main/java/io/github/cottonmc/slopetest/model/AbstractModel.java b/src/main/java/io/github/cottonmc/slopetest/model/AbstractModel.java new file mode 100644 index 0000000..19fc38c --- /dev/null +++ b/src/main/java/io/github/cottonmc/slopetest/model/AbstractModel.java @@ -0,0 +1,47 @@ +package io.github.cottonmc.slopetest.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.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(); + + 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 hasDepthInGui() { + return true; + } + + @Override + public boolean isBuiltin() { + return false; + } + + @Override + public Sprite getSprite() { + return modelSprite; + } + + @Override + public ModelTransformation getTransformation() { + return transformation; + } +} diff --git a/src/main/java/io/github/cottonmc/slopetest/model/MeshTransformer.java b/src/main/java/io/github/cottonmc/slopetest/model/MeshTransformer.java new file mode 100644 index 0000000..da71907 --- /dev/null +++ b/src/main/java/io/github/cottonmc/slopetest/model/MeshTransformer.java @@ -0,0 +1,16 @@ +package io.github.cottonmc.slopetest.model; + +import java.util.Random; +import java.util.function.Supplier; + +import net.fabricmc.fabric.api.renderer.v1.render.RenderContext.QuadTransform; +import net.minecraft.block.BlockState; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.ExtendedBlockView; + +public interface MeshTransformer extends QuadTransform { + MeshTransformer prepare(ExtendedBlockView blockView, BlockState state, BlockPos pos, Supplier randomSupplier); + + MeshTransformer prepare(ItemStack stack, Supplier randomSupplier); +} diff --git a/src/main/java/io/github/cottonmc/slopetest/model/SimpleModel.java b/src/main/java/io/github/cottonmc/slopetest/model/SimpleModel.java new file mode 100644 index 0000000..a1df376 --- /dev/null +++ b/src/main/java/io/github/cottonmc/slopetest/model/SimpleModel.java @@ -0,0 +1,105 @@ +package io.github.cottonmc.slopetest.model; + +import java.lang.ref.WeakReference; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.function.Supplier; + +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.BakedModel; +import net.minecraft.client.render.model.BakedQuad; +import net.minecraft.client.render.model.json.ModelItemPropertyOverrideList; +import net.minecraft.client.render.model.json.ModelTransformation; +import net.minecraft.client.texture.Sprite; +import net.minecraft.entity.LivingEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.world.ExtendedBlockView; +import net.minecraft.world.World; + +/** + * 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(ExtendedBlockView 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 ModelItemPropertyOverrideList getItemPropertyOverrides() { + return itemProxy; + } + + protected class ItemProxy extends ModelItemPropertyOverrideList { + public ItemProxy() { + super(null, null, null, Collections.emptyList()); + } + + @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/slopetest/model/SimpleUnbakedModel.java b/src/main/java/io/github/cottonmc/slopetest/model/SimpleUnbakedModel.java new file mode 100644 index 0000000..f4313a2 --- /dev/null +++ b/src/main/java/io/github/cottonmc/slopetest/model/SimpleUnbakedModel.java @@ -0,0 +1,33 @@ +package io.github.cottonmc.slopetest.model; + +import java.util.Collection; +import java.util.Collections; +import java.util.Set; +import java.util.function.Function; + +import net.minecraft.client.render.model.BakedModel; +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.util.Identifier; + +@FunctionalInterface +public interface SimpleUnbakedModel extends UnbakedModel { + BakedModel bake(); + + @Override + default Collection getModelDependencies() { + return Collections.emptyList(); + } + + @Override + default Collection getTextureDependencies(Function var1, Set var2) { + return Collections.emptyList(); + } + + @Override + default BakedModel bake(ModelLoader loader, Function spriteFunc, ModelBakeSettings settings) { + return bake(); + } +} diff --git a/src/main/java/io/github/cottonmc/slopetest/model/SlopeModelVariantProvider.java b/src/main/java/io/github/cottonmc/slopetest/model/SlopeModelVariantProvider.java new file mode 100644 index 0000000..0611412 --- /dev/null +++ b/src/main/java/io/github/cottonmc/slopetest/model/SlopeModelVariantProvider.java @@ -0,0 +1,35 @@ +package io.github.cottonmc.slopetest.model; + +import java.util.HashMap; + +import io.github.cottonmc.slopetest.SlopeTest; +import net.fabricmc.fabric.api.client.model.ModelProviderContext; +import net.fabricmc.fabric.api.client.model.ModelProviderException; +import net.fabricmc.fabric.api.client.model.ModelVariantProvider; +import net.minecraft.block.BlockState; +import net.minecraft.client.render.block.BlockModels; +import net.minecraft.client.render.model.UnbakedModel; +import net.minecraft.client.util.ModelIdentifier; +import net.minecraft.util.registry.Registry; + +public class SlopeModelVariantProvider implements ModelVariantProvider { + + private final HashMap variants = new HashMap<>(); + + public SlopeModelVariantProvider() { + // A bit ugly to hard-code this in the constructor, but however it is done, + // best to have variants for all the mod's blocks in a single provider/map + // instance so that model loader doesn't have to query a large number of providers. + + for(BlockState state : SlopeTest.SLOPE.getStateFactory().getStates()) { + variants.put(BlockModels.getModelId(state), (SimpleUnbakedModel)() -> new SlopeTestModel(state)); + } + + variants.put(new ModelIdentifier(Registry.ITEM.getId(SlopeTest.SLOPE.asItem()), "inventory"), (SimpleUnbakedModel)() -> new SlopeTestModel(SlopeTest.SLOPE.getDefaultState())); + } + + @Override + public UnbakedModel loadModelVariant(ModelIdentifier modelId, ModelProviderContext context) throws ModelProviderException { + return variants.get(modelId); + } +} diff --git a/src/main/java/io/github/cottonmc/slopetest/model/SlopeTestModel.java b/src/main/java/io/github/cottonmc/slopetest/model/SlopeTestModel.java new file mode 100644 index 0000000..25c182c --- /dev/null +++ b/src/main/java/io/github/cottonmc/slopetest/model/SlopeTestModel.java @@ -0,0 +1,357 @@ +package io.github.cottonmc.slopetest.model; + +import java.util.Random; +import java.util.function.Supplier; + +import org.apache.commons.lang3.ObjectUtils; + +import io.github.cottonmc.slopetest.util.SpriteSet; +import net.fabricmc.fabric.api.client.render.ColorProviderRegistry; +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.minecraft.block.Block; +import net.minecraft.block.BlockRenderLayer; +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.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.world.ExtendedBlockView; + +public class SlopeTestModel extends SimpleModel { + + private static final ThreadLocal TRANSFORMERS = ThreadLocal.withInitial(Transformer::new); + + public SlopeTestModel(BlockState blockState) { + super(baseMesh(blockState), TRANSFORMERS::get, MissingSprite.getMissingSprite(), 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.spriteColor(0, -1, -1, -1, -1), dir); + drawLeftSide(quad.spriteColor(0, -1, -1, -1, -1), dir); + drawRightSide(quad.spriteColor(0, -1, -1, -1, -1), dir); + drawBack(quad.spriteColor(0, -1, -1, -1, -1), dir); + drawBottom(quad.spriteColor(0, -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(ExtendedBlockView 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(0, BlockRenderLayer.CUTOUT).find(); + } else { + material = finder.clear().disableDiffuse(0, false).disableAo(0, false).blendMode(0, block.getRenderLayer()).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.spriteColor(0, 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.spriteColor(0, 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.spriteColor(0, color, color, color, color); + } + paintRightSide(quad, dir, sprites.getSprite(rightDir)); + break; + } + + case TAG_BACK: { + if(sprites.hasColor(dir)) { + quad.spriteColor(0, color, color, color, color); + } + paintBack(quad, dir, sprites.getSprite(dir)); + break; + } + + case TAG_BOTTOM: { + if(sprites.hasColor(Direction.DOWN)) { + quad.spriteColor(0, 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.sprite(0, 0, sprite.getMinU(), sprite.getMinV()) + .sprite(1, 0, sprite.getMinU(), sprite.getMaxV()) + .sprite(2, 0, sprite.getMaxU(), sprite.getMaxV()) + .sprite(3, 0, sprite.getMaxU(), sprite.getMinV()); + break; + case SOUTH: + quad.sprite(0, 0, sprite.getMaxU(), sprite.getMaxV()) + .sprite(1, 0, sprite.getMaxU(), sprite.getMinV()) + .sprite(2, 0, sprite.getMinU(), sprite.getMinV()) + .sprite(3, 0, sprite.getMinU(), sprite.getMaxV()); + break; + case EAST: + quad.sprite(0, 0, sprite.getMinU(), sprite.getMaxV()) + .sprite(1, 0, sprite.getMaxU(), sprite.getMaxV()) + .sprite(2, 0, sprite.getMaxU(), sprite.getMinV()) + .sprite(3, 0, sprite.getMinU(), sprite.getMinV()); + break; + case WEST: + quad.sprite(0, 0, sprite.getMaxU(), sprite.getMinV()) + .sprite(1, 0, sprite.getMinU(), sprite.getMinV()) + .sprite(2, 0, sprite.getMinU(), sprite.getMaxV()) + .sprite(3, 0, sprite.getMaxU(), sprite.getMaxV()); + default: + break; + } + } + + private static void paintLeftSide(MutableQuadView quad, Direction dir, Sprite sprite) { + switch(dir) { + case NORTH: + quad.sprite(0, 0, sprite.getMinU(), sprite.getMaxV()) + .sprite(1, 0, sprite.getMaxU(), sprite.getMaxV()) + .sprite(2, 0, sprite.getMaxU(), sprite.getMinV()) + .sprite(3, 0, sprite.getMinU(), sprite.getMinV()); + break; + case SOUTH: + quad.sprite(0, 0, sprite.getMaxU(), sprite.getMinV()) + .sprite(1, 0, sprite.getMinU(), sprite.getMinV()) + .sprite(2, 0, sprite.getMinU(), sprite.getMaxV()) + .sprite(3, 0, sprite.getMaxU(), sprite.getMaxV()); + break; + case EAST: + quad.sprite(0, 0, sprite.getMinU(), sprite.getMaxV()) + .sprite(1, 0, sprite.getMaxU(), sprite.getMaxV()) + .sprite(2, 0, sprite.getMaxU(), sprite.getMinV()) + .sprite(3, 0, sprite.getMinU(), sprite.getMinV()); + break; + case WEST: + quad.sprite(0, 0, sprite.getMinU(), sprite.getMaxV()) + .sprite(1, 0, sprite.getMaxU(), sprite.getMaxV()) + .sprite(2, 0, sprite.getMaxU(), sprite.getMinV()) + .sprite(3, 0, sprite.getMinU(), sprite.getMinV()); + default: + break; + } + } + + private static void paintRightSide(MutableQuadView quad, Direction dir, Sprite sprite) { + switch(dir) { + case NORTH: + quad.sprite(0, 0, sprite.getMaxU(), sprite.getMaxV()) + .sprite(1, 0, sprite.getMaxU(), sprite.getMinV()) + .sprite(2, 0, sprite.getMinU(), sprite.getMinV()) + .sprite(3, 0, sprite.getMinU(), sprite.getMaxV()); + break; + case SOUTH: + quad.sprite(0, 0, sprite.getMinU(), sprite.getMinV()) + .sprite(1, 0, sprite.getMinU(), sprite.getMaxV()) + .sprite(2, 0, sprite.getMaxU(), sprite.getMaxV()) + .sprite(3, 0, sprite.getMaxU(), sprite.getMinV()); + break; + case EAST: + quad.sprite(0, 0, sprite.getMinU(), sprite.getMinV()) + .sprite(1, 0, sprite.getMinU(), sprite.getMaxV()) + .sprite(2, 0, sprite.getMaxU(), sprite.getMaxV()) + .sprite(3, 0, sprite.getMaxU(), sprite.getMinV()); + break; + case WEST: + quad.sprite(0, 0, sprite.getMaxU(), sprite.getMaxV()) + .sprite(1, 0, sprite.getMaxU(), sprite.getMinV()) + .sprite(2, 0, sprite.getMinU(), sprite.getMinV()) + .sprite(3, 0, sprite.getMinU(), sprite.getMaxV()); + default: + break; + } + } + + private static void paintBack(MutableQuadView quad, Direction dir, Sprite sprite) { + switch(dir) { + case NORTH: + quad.sprite(0, 0, sprite.getMaxU(), sprite.getMaxV()) + .sprite(1, 0, sprite.getMaxU(), sprite.getMinV()) + .sprite(2, 0, sprite.getMinU(), sprite.getMinV()) + .sprite(3, 0, sprite.getMinU(), sprite.getMaxV()); + break; + case SOUTH: + quad.sprite(0, 0, sprite.getMinU(), sprite.getMaxV()) + .sprite(1, 0, sprite.getMaxU(), sprite.getMaxV()) + .sprite(2, 0, sprite.getMaxU(), sprite.getMinV()) + .sprite(3, 0, sprite.getMinU(), sprite.getMinV()); + break; + case EAST: + quad.sprite(0, 0, sprite.getMaxU(), sprite.getMaxV()) + .sprite(1, 0, sprite.getMaxU(), sprite.getMinV()) + .sprite(2, 0, sprite.getMinU(), sprite.getMinV()) + .sprite(3, 0, sprite.getMinU(), sprite.getMaxV()); + break; + case WEST: + quad.sprite(0, 0, sprite.getMinU(), sprite.getMaxV()) + .sprite(1, 0, sprite.getMaxU(), sprite.getMaxV()) + .sprite(2, 0, sprite.getMaxU(), sprite.getMinV()) + .sprite(3, 0, sprite.getMinU(), sprite.getMinV()); + default: + break; + } + } + + private static void paintBottom(MutableQuadView quad, Sprite sprite) { + quad.sprite(0, 0, sprite.getMinU(), sprite.getMaxV()) + .sprite(1, 0, sprite.getMaxU(), sprite.getMaxV()) + .sprite(2, 0, sprite.getMaxU(), sprite.getMinV()) + .sprite(3, 0, sprite.getMinU(), sprite.getMinV()); + } + } +} diff --git a/src/main/java/io/github/cottonmc/slopetest/util/SpriteSet.java b/src/main/java/io/github/cottonmc/slopetest/util/SpriteSet.java index 9bc4092..3971cc2 100644 --- a/src/main/java/io/github/cottonmc/slopetest/util/SpriteSet.java +++ b/src/main/java/io/github/cottonmc/slopetest/util/SpriteSet.java @@ -1,5 +1,12 @@ package io.github.cottonmc.slopetest.util; +import java.util.List; +import java.util.Random; + +import org.apache.commons.lang3.ObjectUtils; + +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; @@ -7,42 +14,39 @@ import net.minecraft.client.texture.Sprite; import net.minecraft.util.Identifier; import net.minecraft.util.math.Direction; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; - public class SpriteSet { - private Sprite global; - private Map quads; - private boolean singleSprite = false; - private boolean globalHasColor = false; + private Object2ObjectOpenHashMap quads = new Object2ObjectOpenHashMap<>(); + private boolean isDefault = true; public static final Sprite FALLBACK = MinecraftClient.getInstance().getSpriteAtlas().getSprite(new Identifier("minecraft:block/scaffolding_top")); - public SpriteSet(Sprite allSprite, boolean hasColor) { - this.global = allSprite; - singleSprite = true; - globalHasColor = hasColor; + public SpriteSet() { + clear(); } - public SpriteSet(BakedModel model) { - Random rand = new Random(); - quads = new HashMap<>(); - for (Direction dir : Direction.values()) { - List quads = model.getQuads(null, dir, rand); - if (!quads.isEmpty()) this.quads.put(dir, quads.get(0)); - } + /** Allow re-use of instances to avoid allocation in render loop */ + public void clear() { + isDefault = true; } + + /** Allow re-use of instances to avoid allocation in render loop */ + //TODO: pass in block state? + 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); + List quads = model.getQuads(null, dir, rand); + if (!quads.isEmpty()) this.quads.put(dir, quads.get(0)); + } + } public Sprite getSprite(Direction dir) { - if (singleSprite) return global; - if (quads.get(dir) == null) return FALLBACK; - else return quads.get(dir).getSprite(); + return isDefault ? FALLBACK : ObjectUtils.defaultIfNull(quads.get(dir).getSprite(), FALLBACK); } public boolean hasColor(Direction dir) { - if (singleSprite) return globalHasColor; - if (quads.get(dir) == null) return false; - else return quads.get(dir).hasColor(); + return isDefault ? false : ObjectUtils.defaultIfNull(quads.get(dir).hasColor(), false); } }