From 9271edf298dc7c92fc8ac830ca3a3da13f8097af Mon Sep 17 00:00:00 2001 From: PepperCode1 <44146161+PepperCode1@users.noreply.github.com> Date: Fri, 24 Feb 2023 18:08:31 -0800 Subject: [PATCH] Wheels are rendered - Add water wheel rendering - Use NbtUtils to read block states in CopycatBlockEntity and WaterWheelBlockEntity --- .../simibubi/create/AllBlockEntityTypes.java | 12 +- .../com/simibubi/create/AllPartialModels.java | 5 + .../com/simibubi/create/CreateClient.java | 2 + .../base/CutoutRotatingInstance.java | 1 + .../waterwheel/WaterWheelBlockEntity.java | 75 +++++------- .../waterwheel/WaterWheelInstance.java | 51 +++++++- .../waterwheel/WaterWheelModelKey.java | 6 + .../waterwheel/WaterWheelRenderer.java | 115 ++++++++++++++++++ .../relays/gauge/GaugeRenderer.java | 5 +- .../frames/CopycatBlockEntity.java | 16 +-- .../foundation/model/BakedModelHelper.java | 60 +++++++++ 11 files changed, 276 insertions(+), 72 deletions(-) create mode 100644 src/main/java/com/simibubi/create/content/contraptions/components/waterwheel/WaterWheelModelKey.java create mode 100644 src/main/java/com/simibubi/create/content/contraptions/components/waterwheel/WaterWheelRenderer.java diff --git a/src/main/java/com/simibubi/create/AllBlockEntityTypes.java b/src/main/java/com/simibubi/create/AllBlockEntityTypes.java index 9a4d59714..ffaf9231c 100644 --- a/src/main/java/com/simibubi/create/AllBlockEntityTypes.java +++ b/src/main/java/com/simibubi/create/AllBlockEntityTypes.java @@ -85,6 +85,8 @@ import com.simibubi.create.content.contraptions.components.structureMovement.pul import com.simibubi.create.content.contraptions.components.turntable.TurntableBlockEntity; import com.simibubi.create.content.contraptions.components.waterwheel.LargeWaterWheelBlockEntity; import com.simibubi.create.content.contraptions.components.waterwheel.WaterWheelBlockEntity; +import com.simibubi.create.content.contraptions.components.waterwheel.WaterWheelInstance; +import com.simibubi.create.content.contraptions.components.waterwheel.WaterWheelRenderer; import com.simibubi.create.content.contraptions.fluids.PumpBlockEntity; import com.simibubi.create.content.contraptions.fluids.PumpCogInstance; import com.simibubi.create.content.contraptions.fluids.PumpRenderer; @@ -602,16 +604,16 @@ public class AllBlockEntityTypes { public static final BlockEntityEntry WATER_WHEEL = REGISTRATE .blockEntity("water_wheel", WaterWheelBlockEntity::new) - .instance(() -> CutoutRotatingInstance::new, false) + .instance(() -> WaterWheelInstance::standard, false) .validBlocks(AllBlocks.WATER_WHEEL) - .renderer(() -> KineticBlockEntityRenderer::new) + .renderer(() -> WaterWheelRenderer::standard) .register(); - + public static final BlockEntityEntry LARGE_WATER_WHEEL = REGISTRATE .blockEntity("large_water_wheel", LargeWaterWheelBlockEntity::new) - .instance(() -> CutoutRotatingInstance::new, false) + .instance(() -> WaterWheelInstance::large, false) .validBlocks(AllBlocks.LARGE_WATER_WHEEL) - .renderer(() -> KineticBlockEntityRenderer::new) + .renderer(() -> WaterWheelRenderer::large) .register(); public static final BlockEntityEntry MECHANICAL_PRESS = REGISTRATE diff --git a/src/main/java/com/simibubi/create/AllPartialModels.java b/src/main/java/com/simibubi/create/AllPartialModels.java index 0a4ea2121..096fd33bf 100644 --- a/src/main/java/com/simibubi/create/AllPartialModels.java +++ b/src/main/java/com/simibubi/create/AllPartialModels.java @@ -159,6 +159,11 @@ public class AllPartialModels { WHISTLE_MOUTH_MEDIUM = block("steam_whistle/medium_mouth"), WHISTLE_MOUTH_SMALL = block("steam_whistle/small_mouth"), + WATER_WHEEL = block("water_wheel/block"), + WATER_WHEEL_EXTENSION = block("water_wheel/block_extension"), + LARGE_WATER_WHEEL = block("large_water_wheel/block"), + LARGE_WATER_WHEEL_EXTENSION = block("large_water_wheel/block_extension"), + CRAFTING_BLUEPRINT_1x1 = entity("crafting_blueprint_small"), CRAFTING_BLUEPRINT_2x2 = entity("crafting_blueprint_medium"), CRAFTING_BLUEPRINT_3x3 = entity("crafting_blueprint_large"), diff --git a/src/main/java/com/simibubi/create/CreateClient.java b/src/main/java/com/simibubi/create/CreateClient.java index 83bfe4fbf..937ec8b4c 100644 --- a/src/main/java/com/simibubi/create/CreateClient.java +++ b/src/main/java/com/simibubi/create/CreateClient.java @@ -5,6 +5,7 @@ import com.simibubi.create.content.contraptions.components.structureMovement.glu import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.TrainHUD; import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionRenderDispatcher; import com.simibubi.create.content.contraptions.components.structureMovement.render.SBBContraptionManager; +import com.simibubi.create.content.contraptions.components.waterwheel.WaterWheelRenderer; import com.simibubi.create.content.contraptions.goggles.GoggleOverlayRenderer; import com.simibubi.create.content.contraptions.relays.encased.CasingConnectivity; import com.simibubi.create.content.curiosities.armor.RemainingAirOverlay; @@ -80,6 +81,7 @@ public class CreateClient { BUFFER_CACHE.registerCompartment(CachedBufferer.PARTIAL); BUFFER_CACHE.registerCompartment(CachedBufferer.DIRECTIONAL_PARTIAL); BUFFER_CACHE.registerCompartment(KineticBlockEntityRenderer.KINETIC_BLOCK); + BUFFER_CACHE.registerCompartment(WaterWheelRenderer.WATER_WHEEL); BUFFER_CACHE.registerCompartment(SBBContraptionManager.CONTRAPTION, 20); BUFFER_CACHE.registerCompartment(WorldSectionElement.DOC_WORLD_SECTION, 20); diff --git a/src/main/java/com/simibubi/create/content/contraptions/base/CutoutRotatingInstance.java b/src/main/java/com/simibubi/create/content/contraptions/base/CutoutRotatingInstance.java index 21d6b99fe..7f7017f5f 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/base/CutoutRotatingInstance.java +++ b/src/main/java/com/simibubi/create/content/contraptions/base/CutoutRotatingInstance.java @@ -10,6 +10,7 @@ public class CutoutRotatingInstance extends Single super(materialManager, blockEntity); } + @Override protected Material getRotatingMaterial() { return materialManager.defaultCutout() .material(AllMaterialSpecs.ROTATING); diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/waterwheel/WaterWheelBlockEntity.java b/src/main/java/com/simibubi/create/content/contraptions/components/waterwheel/WaterWheelBlockEntity.java index 271a3a3db..d127275bd 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/waterwheel/WaterWheelBlockEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/waterwheel/WaterWheelBlockEntity.java @@ -1,13 +1,11 @@ package com.simibubi.create.content.contraptions.components.waterwheel; +import java.util.EnumMap; import java.util.HashSet; -import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Set; -import com.google.gson.JsonParser; -import com.mojang.serialization.JsonOps; import com.simibubi.create.content.contraptions.base.GeneratingKineticBlockEntity; import com.simibubi.create.content.contraptions.base.IRotate; import com.simibubi.create.foundation.advancement.AllAdvancements; @@ -22,6 +20,7 @@ import net.minecraft.core.Direction.Axis; import net.minecraft.core.Direction.AxisDirection; import net.minecraft.core.Vec3i; import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtUtils; import net.minecraft.tags.BlockTags; import net.minecraft.util.Mth; import net.minecraft.world.InteractionResult; @@ -34,14 +33,11 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; -import net.minecraftforge.client.model.data.IModelData; -import net.minecraftforge.client.model.data.ModelDataMap; -import net.minecraftforge.client.model.data.ModelProperty; public class WaterWheelBlockEntity extends GeneratingKineticBlockEntity { - public static Map> SMALL_OFFSETS = new IdentityHashMap<>(); - public static Map> LARGE_OFFSETS = new IdentityHashMap<>(); + public static final Map> SMALL_OFFSETS = new EnumMap<>(Axis.class); + public static final Map> LARGE_OFFSETS = new EnumMap<>(Axis.class); static { for (Axis axis : Iterate.axes) { @@ -70,11 +66,11 @@ public class WaterWheelBlockEntity extends GeneratingKineticBlockEntity { } public int flowScore; - public BlockState baseBlock; // <-- TODO use planks/corresponding logs in rendered instance + public BlockState material; public WaterWheelBlockEntity(BlockEntityType type, BlockPos pos, BlockState state) { super(type, pos, state); - baseBlock = Blocks.SPRUCE_PLANKS.defaultBlockState(); + material = Blocks.SPRUCE_PLANKS.defaultBlockState(); } protected int getSize() { @@ -85,15 +81,15 @@ public class WaterWheelBlockEntity extends GeneratingKineticBlockEntity { return (getSize() == 1 ? SMALL_OFFSETS : LARGE_OFFSETS).get(getAxis()); } - public InteractionResult applyMaterialIfValid(ItemStack item) { - if (!(item.getItem() instanceof BlockItem blockItem)) + public InteractionResult applyMaterialIfValid(ItemStack stack) { + if (!(stack.getItem() instanceof BlockItem blockItem)) return InteractionResult.PASS; BlockState material = blockItem.getBlock().defaultBlockState(); - if (material == this.baseBlock) + if (material == this.material) return InteractionResult.PASS; if (!material.is(BlockTags.PLANKS)) return InteractionResult.PASS; - baseBlock = material; + this.material = material; notifyUpdate(); return InteractionResult.SUCCESS; } @@ -155,30 +151,6 @@ public class WaterWheelBlockEntity extends GeneratingKineticBlockEntity { setChanged(); } - @Override - public void addBehaviours(List behaviours) { - super.addBehaviours(behaviours); - registerAwardables(behaviours, AllAdvancements.LAVA_WHEEL, AllAdvancements.WATER_WHEEL); - } - - @Override - protected void read(CompoundTag compound, boolean clientPacket) { - super.read(compound, clientPacket); - flowScore = compound.getInt("FlowScore"); - - BlockState prevMaterial = baseBlock; - if (!compound.contains("Material")) - return; - - JsonOps ops = JsonOps.INSTANCE; - BlockState.CODEC.decode(ops, JsonParser.parseString(compound.getString("Material"))) - .result() - .ifPresent(p -> baseBlock = p.getFirst()); - - if (clientPacket && prevMaterial != baseBlock) - redraw(); - } - private void redraw() { if (!isVirtual()) requestModelDataUpdate(); @@ -190,23 +162,32 @@ public class WaterWheelBlockEntity extends GeneratingKineticBlockEntity { } } - public static final ModelProperty MATERIAL_PROPERTY = new ModelProperty<>(); + @Override + public void addBehaviours(List behaviours) { + super.addBehaviours(behaviours); + registerAwardables(behaviours, AllAdvancements.LAVA_WHEEL, AllAdvancements.WATER_WHEEL); + } @Override - public IModelData getModelData() { - return new ModelDataMap.Builder().withInitial(MATERIAL_PROPERTY, baseBlock) - .build(); + protected void read(CompoundTag compound, boolean clientPacket) { + super.read(compound, clientPacket); + flowScore = compound.getInt("FlowScore"); + + BlockState prevMaterial = material; + if (!compound.contains("Material")) + return; + + material = NbtUtils.readBlockState(compound.getCompound("Material")); + + if (clientPacket && prevMaterial != material) + redraw(); } @Override public void write(CompoundTag compound, boolean clientPacket) { super.write(compound, clientPacket); compound.putInt("FlowScore", flowScore); - JsonOps ops = JsonOps.INSTANCE; - BlockState.CODEC.encode(baseBlock, ops, ops.empty()) - .result() - .map(je -> je.toString()) - .ifPresent(s -> compound.putString("Material", s)); + compound.put("Material", NbtUtils.writeBlockState(material)); } @Override diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/waterwheel/WaterWheelInstance.java b/src/main/java/com/simibubi/create/content/contraptions/components/waterwheel/WaterWheelInstance.java index 6a1092226..f125d09c2 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/waterwheel/WaterWheelInstance.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/waterwheel/WaterWheelInstance.java @@ -1,13 +1,56 @@ package com.simibubi.create.content.contraptions.components.waterwheel; +import com.jozufozu.flywheel.api.Instancer; import com.jozufozu.flywheel.api.MaterialManager; +import com.jozufozu.flywheel.core.model.BlockModel; +import com.mojang.blaze3d.vertex.PoseStack; import com.simibubi.create.content.contraptions.base.CutoutRotatingInstance; -import com.simibubi.create.content.contraptions.base.KineticBlockEntity; +import com.simibubi.create.content.contraptions.base.flwdata.RotatingData; +import com.simibubi.create.foundation.render.CachedBufferer; -public class WaterWheelInstance extends CutoutRotatingInstance { +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.core.Direction; +import net.minecraft.core.Direction.AxisDirection; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; - public WaterWheelInstance(MaterialManager modelManager, KineticBlockEntity tile) { - super(modelManager, tile); +public class WaterWheelInstance extends CutoutRotatingInstance { + protected final boolean large; + protected final WaterWheelModelKey key; + + public WaterWheelInstance(MaterialManager materialManager, T blockEntity, boolean large) { + super(materialManager, blockEntity); + this.large = large; + key = new WaterWheelModelKey(large, getRenderedBlockState(), blockEntity.material); } + public static WaterWheelInstance standard(MaterialManager materialManager, T blockEntity) { + return new WaterWheelInstance<>(materialManager, blockEntity, false); + } + + public static WaterWheelInstance large(MaterialManager materialManager, T blockEntity) { + return new WaterWheelInstance<>(materialManager, blockEntity, true); + } + + @Override + public boolean shouldReset() { + return super.shouldReset() || key.material() != blockEntity.material; + } + + @Override + protected Instancer getModel() { + return getRotatingMaterial().model(key, () -> { + BakedModel model = WaterWheelRenderer.generateModel(key); + BlockState state = key.state(); + // TODO waterwheels + Direction dir; + if (key.large()) { + dir = Direction.fromAxisAndDirection(state.getValue(LargeWaterWheelBlock.AXIS), AxisDirection.POSITIVE); + } else { + dir = state.getValue(WaterWheelBlock.FACING); + } + PoseStack transform = CachedBufferer.rotateToFaceVertical(dir).get(); + return new BlockModel(model, Blocks.AIR.defaultBlockState(), transform); + }); + } } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/waterwheel/WaterWheelModelKey.java b/src/main/java/com/simibubi/create/content/contraptions/components/waterwheel/WaterWheelModelKey.java new file mode 100644 index 000000000..1fefc5a89 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/components/waterwheel/WaterWheelModelKey.java @@ -0,0 +1,6 @@ +package com.simibubi.create.content.contraptions.components.waterwheel; + +import net.minecraft.world.level.block.state.BlockState; + +public record WaterWheelModelKey(boolean large, BlockState state, BlockState material) { +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/waterwheel/WaterWheelRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/components/waterwheel/WaterWheelRenderer.java new file mode 100644 index 000000000..f241dce25 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/components/waterwheel/WaterWheelRenderer.java @@ -0,0 +1,115 @@ +package com.simibubi.create.content.contraptions.components.waterwheel; + +import java.util.Map; +import java.util.function.Function; + +import com.jozufozu.flywheel.core.PartialModel; +import com.jozufozu.flywheel.core.StitchedSprite; +import com.mojang.blaze3d.vertex.PoseStack; +import com.simibubi.create.AllPartialModels; +import com.simibubi.create.CreateClient; +import com.simibubi.create.content.contraptions.base.KineticBlockEntityRenderer; +import com.simibubi.create.foundation.model.BakedModelHelper; +import com.simibubi.create.foundation.render.BakedModelRenderHelper; +import com.simibubi.create.foundation.render.CachedBufferer; +import com.simibubi.create.foundation.render.SuperByteBuffer; +import com.simibubi.create.foundation.render.SuperByteBufferCache.Compartment; +import com.simibubi.create.foundation.utility.RegisteredObjects; + +import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider.Context; +import net.minecraft.client.renderer.texture.TextureAtlas; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.core.Direction; +import net.minecraft.core.Direction.AxisDirection; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; + +public class WaterWheelRenderer extends KineticBlockEntityRenderer { + public static final Compartment WATER_WHEEL = new Compartment<>(); + + public static final StitchedSprite OAK_PLANKS_TEMPLATE = new StitchedSprite(new ResourceLocation("block/oak_planks")); + public static final StitchedSprite OAK_LOG_TEMPLATE = new StitchedSprite(new ResourceLocation("block/oak_log")); + public static final StitchedSprite OAK_LOG_TOP_TEMPLATE = new StitchedSprite(new ResourceLocation("block/oak_log_top")); + + protected final boolean large; + + public WaterWheelRenderer(Context context, boolean large) { + super(context); + this.large = large; + } + + public static WaterWheelRenderer standard(Context context) { + return new WaterWheelRenderer<>(context, false); + } + + public static WaterWheelRenderer large(Context context) { + return new WaterWheelRenderer<>(context, true); + } + + @Override + protected SuperByteBuffer getRotatedModel(T be, BlockState state) { + WaterWheelModelKey key = new WaterWheelModelKey(large, state, be.material); + return CreateClient.BUFFER_CACHE.get(WATER_WHEEL, key, () -> { + BakedModel model = WaterWheelRenderer.generateModel(key); + BlockState state1 = key.state(); + // TODO waterwheels + Direction dir; + if (key.large()) { + dir = Direction.fromAxisAndDirection(state1.getValue(LargeWaterWheelBlock.AXIS), AxisDirection.POSITIVE); + } else { + dir = state1.getValue(WaterWheelBlock.FACING); + } + PoseStack transform = CachedBufferer.rotateToFaceVertical(dir).get(); + return BakedModelRenderHelper.standardModelRender(model, Blocks.AIR.defaultBlockState(), transform); + }); + } + + public static PartialModel getTemplateModel(boolean large, boolean extension) { + if (large) { + if (extension) { + return AllPartialModels.LARGE_WATER_WHEEL_EXTENSION; + } else { + return AllPartialModels.LARGE_WATER_WHEEL; + } + } else { + if (extension) { + return AllPartialModels.WATER_WHEEL_EXTENSION; + } else { + return AllPartialModels.WATER_WHEEL; + } + } + } + + public static BakedModel generateModel(WaterWheelModelKey key) { + // TODO waterwheels +// boolean extension = state.getValue(LargeWaterWheelBlock.EXTENSION); + boolean extension = key.state().getOptionalValue(LargeWaterWheelBlock.EXTENSION).orElse(false); + BakedModel template = getTemplateModel(key.large(), extension).get(); + + // TODO waterwheels: improve sprite selection + BlockState material = key.material(); + Block block = material.getBlock(); + ResourceLocation id = RegisteredObjects.getKeyOrThrow(block); + String path = id.getPath(); + + if (path.endsWith("_planks")) { + String namespace = id.getNamespace(); + String wood = path.substring(0, path.length() - 7); + Function spriteFunc = Minecraft.getInstance().getTextureAtlas(TextureAtlas.LOCATION_BLOCKS); + + Map map = new Reference2ReferenceOpenHashMap<>(); + map.put(OAK_PLANKS_TEMPLATE.get(), spriteFunc.apply(new ResourceLocation(namespace, "block/" + wood + "_planks"))); + map.put(OAK_LOG_TEMPLATE.get(), spriteFunc.apply(new ResourceLocation(namespace, "block/" + wood + "_log"))); + map.put(OAK_LOG_TOP_TEMPLATE.get(), spriteFunc.apply(new ResourceLocation(namespace, "block/" + wood + "_log_top"))); + + return BakedModelHelper.generateModel(template, map::get); + } + + return BakedModelHelper.generateModel(template, sprite -> null); + } +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/relays/gauge/GaugeRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/relays/gauge/GaugeRenderer.java index 8ea2d0779..3945f9e51 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/relays/gauge/GaugeRenderer.java +++ b/src/main/java/com/simibubi/create/content/contraptions/relays/gauge/GaugeRenderer.java @@ -5,7 +5,6 @@ import com.jozufozu.flywheel.core.PartialModel; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import com.simibubi.create.AllPartialModels; -import com.simibubi.create.content.contraptions.base.KineticBlockEntity; import com.simibubi.create.content.contraptions.relays.encased.ShaftRenderer; import com.simibubi.create.content.contraptions.relays.gauge.GaugeBlock.Type; import com.simibubi.create.foundation.render.CachedBufferer; @@ -19,7 +18,7 @@ import net.minecraft.core.Direction; import net.minecraft.util.Mth; import net.minecraft.world.level.block.state.BlockState; -public class GaugeRenderer extends ShaftRenderer { +public class GaugeRenderer extends ShaftRenderer { protected GaugeBlock.Type type; @@ -37,7 +36,7 @@ public class GaugeRenderer extends ShaftRenderer { } @Override - protected void renderSafe(KineticBlockEntity be, float partialTicks, PoseStack ms, MultiBufferSource buffer, + protected void renderSafe(GaugeBlockEntity be, float partialTicks, PoseStack ms, MultiBufferSource buffer, int light, int overlay) { if (Backend.canUseInstancing(be.getLevel())) return; diff --git a/src/main/java/com/simibubi/create/content/curiosities/frames/CopycatBlockEntity.java b/src/main/java/com/simibubi/create/content/curiosities/frames/CopycatBlockEntity.java index af906c220..b017f018c 100644 --- a/src/main/java/com/simibubi/create/content/curiosities/frames/CopycatBlockEntity.java +++ b/src/main/java/com/simibubi/create/content/curiosities/frames/CopycatBlockEntity.java @@ -2,8 +2,6 @@ package com.simibubi.create.content.curiosities.frames; import java.util.List; -import com.google.gson.JsonParser; -import com.mojang.serialization.JsonOps; import com.simibubi.create.AllBlocks; import com.simibubi.create.content.contraptions.components.structureMovement.ITransformableBlockEntity; import com.simibubi.create.content.contraptions.components.structureMovement.StructureTransform; @@ -18,6 +16,7 @@ import com.simibubi.create.foundation.utility.Iterate; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtUtils; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; @@ -136,10 +135,7 @@ public class CopycatBlockEntity extends SmartBlockEntity implements ISpecialBloc if (!tag.contains("Material")) return; - JsonOps ops = JsonOps.INSTANCE; - BlockState.CODEC.decode(ops, JsonParser.parseString(tag.getString("Material"))) - .result() - .ifPresent(p -> material = p.getFirst()); + material = NbtUtils.readBlockState(tag.getCompound("Material")); if (clientPacket && prevMaterial != material) redraw(); @@ -148,14 +144,8 @@ public class CopycatBlockEntity extends SmartBlockEntity implements ISpecialBloc @Override protected void write(CompoundTag tag, boolean clientPacket) { super.write(tag, clientPacket); - tag.put("Item", consumedItem.serializeNBT()); - - JsonOps ops = JsonOps.INSTANCE; - BlockState.CODEC.encode(material, ops, ops.empty()) - .result() - .map(je -> je.toString()) - .ifPresent(s -> tag.putString("Material", s)); + tag.put("Material", NbtUtils.writeBlockState(material)); } @Override diff --git a/src/main/java/com/simibubi/create/foundation/model/BakedModelHelper.java b/src/main/java/com/simibubi/create/foundation/model/BakedModelHelper.java index 16f4915c2..fcc005b8a 100644 --- a/src/main/java/com/simibubi/create/foundation/model/BakedModelHelper.java +++ b/src/main/java/com/simibubi/create/foundation/model/BakedModelHelper.java @@ -3,12 +3,25 @@ package com.simibubi.create.foundation.model; import static com.simibubi.create.foundation.block.render.SpriteShiftEntry.getUnInterpolatedU; import static com.simibubi.create.foundation.block.render.SpriteShiftEntry.getUnInterpolatedV; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.function.UnaryOperator; + +import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.VecHelper; import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.block.model.ItemOverrides; import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.client.resources.model.SimpleBakedModel; +import net.minecraft.core.Direction; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; +import net.minecraftforge.client.model.data.EmptyModelData; public class BakedModelHelper { public static void cropAndMove(BakedQuad quad, AABB crop, Vec3 move) { @@ -79,4 +92,51 @@ public class BakedModelHelper { BakedQuadHelper.setXYZ(vertexData, vertex, newXyz.add(move)); } } + + public static BakedModel generateModel(BakedModel template, UnaryOperator spriteSwapper) { + Random random = new Random(); + + Map> culledFaces = new EnumMap<>(Direction.class); + for (Direction cullFace : Iterate.directions) { + random.setSeed(42L); + List quads = template.getQuads(null, cullFace, random, EmptyModelData.INSTANCE); + culledFaces.put(cullFace, swapSprites(quads, spriteSwapper)); + } + + random.setSeed(42L); + List quads = template.getQuads(null, null, random, EmptyModelData.INSTANCE); + List unculledFaces = swapSprites(quads, spriteSwapper); + + TextureAtlasSprite particleSprite = template.getParticleIcon(EmptyModelData.INSTANCE); + TextureAtlasSprite swappedParticleSprite = spriteSwapper.apply(particleSprite); + if (swappedParticleSprite != null) { + particleSprite = swappedParticleSprite; + } + return new SimpleBakedModel(unculledFaces, culledFaces, template.useAmbientOcclusion(), template.usesBlockLight(), template.isGui3d(), particleSprite, template.getTransforms(), ItemOverrides.EMPTY); + } + + public static List swapSprites(List quads, UnaryOperator spriteSwapper) { + List newQuads = new ArrayList<>(quads); + int size = quads.size(); + for (int i = 0; i < size; i++) { + BakedQuad quad = quads.get(i); + TextureAtlasSprite sprite = quad.getSprite(); + TextureAtlasSprite newSprite = spriteSwapper.apply(sprite); + if (newSprite == null || sprite == newSprite) + continue; + + BakedQuad newQuad = BakedQuadHelper.clone(quad); + int[] vertexData = newQuad.getVertices(); + + for (int vertex = 0; vertex < 4; vertex++) { + float u = BakedQuadHelper.getU(vertexData, vertex); + float v = BakedQuadHelper.getV(vertexData, vertex); + BakedQuadHelper.setU(vertexData, vertex, newSprite.getU(getUnInterpolatedU(sprite, u))); + BakedQuadHelper.setV(vertexData, vertex, newSprite.getV(getUnInterpolatedV(sprite, v))); + } + + newQuads.set(i, newQuad); + } + return newQuads; + } }