diff --git a/src/main/java/com/refinedmods/refinedstorage/RSItems.java b/src/main/java/com/refinedmods/refinedstorage/RSItems.java index e787f4371..ceb7d993d 100644 --- a/src/main/java/com/refinedmods/refinedstorage/RSItems.java +++ b/src/main/java/com/refinedmods/refinedstorage/RSItems.java @@ -43,6 +43,8 @@ public final class RSItems { public static final RegistryObject WIRELESS_CRAFTING_MONITOR; public static final RegistryObject CREATIVE_WIRELESS_CRAFTING_MONITOR; public static final RegistryObject MACHINE_CASING; + public static final RegistryObject COVER; + public static final RegistryObject HOLLOW_COVER; public static final Map> PROCESSORS = new EnumMap<>(ProcessorItem.Type.class); @@ -121,6 +123,8 @@ public final class RSItems { registerBlockItemFor(RSBlocks.QUARTZ_ENRICHED_IRON); MACHINE_CASING = registerBlockItemFor(RSBlocks.MACHINE_CASING); + COVER = ITEMS.register("cover", CoverItem::new); + HOLLOW_COVER = ITEMS.register("hollow_cover", HollowCoverItem::new); registerBlockItemFor(RSBlocks.CABLE); registerBlockItemFor(RSBlocks.DISK_DRIVE); diff --git a/src/main/java/com/refinedmods/refinedstorage/api/network/node/ICoverable.java b/src/main/java/com/refinedmods/refinedstorage/api/network/node/ICoverable.java new file mode 100644 index 000000000..32df42678 --- /dev/null +++ b/src/main/java/com/refinedmods/refinedstorage/api/network/node/ICoverable.java @@ -0,0 +1,9 @@ +package com.refinedmods.refinedstorage.api.network.node; + +import com.refinedmods.refinedstorage.apiimpl.network.node.cover.CoverManager; + +public interface ICoverable { + + CoverManager getCoverManager(); + +} diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/network/node/CableNetworkNode.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/network/node/CableNetworkNode.java index 4d9af480a..3476316b4 100644 --- a/src/main/java/com/refinedmods/refinedstorage/apiimpl/network/node/CableNetworkNode.java +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/network/node/CableNetworkNode.java @@ -1,15 +1,22 @@ package com.refinedmods.refinedstorage.apiimpl.network.node; import com.refinedmods.refinedstorage.RS; +import com.refinedmods.refinedstorage.api.network.node.ICoverable; +import com.refinedmods.refinedstorage.apiimpl.network.node.cover.CoverManager; +import com.refinedmods.refinedstorage.util.WorldUtils; +import net.minecraft.nbt.CompoundNBT; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; -public class CableNetworkNode extends NetworkNode { +public class CableNetworkNode extends NetworkNode implements ICoverable { public static final ResourceLocation ID = new ResourceLocation(RS.ID, "cable"); + private CoverManager coverManager; + public CableNetworkNode(World world, BlockPos pos) { super(world, pos); + this.coverManager = new CoverManager(this); } @Override @@ -21,4 +28,27 @@ public class CableNetworkNode extends NetworkNode { public ResourceLocation getId() { return ID; } + + @Override + public CoverManager getCoverManager() { + return coverManager; + } + + @Override + public void read(CompoundNBT tag) { + if (tag.contains("Cover")) this.coverManager.readFromNbt(tag.getCompound("Cover")); + super.read(tag); + } + + @Override + public void update() { + super.update(); + //WorldUtils.updateBlock(world, pos); + } + + @Override + public CompoundNBT write(CompoundNBT tag) { + tag.put("Cover", this.coverManager.writeToNbt()); + return super.write(tag); + } } diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/network/node/cover/Cover.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/network/node/cover/Cover.java new file mode 100644 index 000000000..a43740a9e --- /dev/null +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/network/node/cover/Cover.java @@ -0,0 +1,22 @@ +package com.refinedmods.refinedstorage.apiimpl.network.node.cover; + +import net.minecraft.item.ItemStack; + +public class Cover { + + private ItemStack stack; + private CoverType type; + + public Cover(ItemStack stack, CoverType type) { + this.stack = stack; + this.type = type; + } + + public ItemStack getStack() { + return stack; + } + + public CoverType getType() { + return type; + } +} diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/network/node/cover/CoverManager.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/network/node/cover/CoverManager.java new file mode 100644 index 000000000..f78320724 --- /dev/null +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/network/node/cover/CoverManager.java @@ -0,0 +1,199 @@ +package com.refinedmods.refinedstorage.apiimpl.network.node.cover; + +import com.refinedmods.refinedstorage.api.network.node.ICoverable; +import com.refinedmods.refinedstorage.api.network.node.INetworkNode; +import com.refinedmods.refinedstorage.api.util.Action; +import com.refinedmods.refinedstorage.apiimpl.API; +import com.refinedmods.refinedstorage.apiimpl.network.node.CableNetworkNode; +import com.refinedmods.refinedstorage.apiimpl.network.node.NetworkNode; +import com.refinedmods.refinedstorage.item.CoverItem; +import net.minecraft.block.*; +import net.minecraft.client.Minecraft; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.util.Direction; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.server.ServerWorld; +import net.minecraftforge.client.model.data.ModelProperty; +import net.minecraftforge.items.IItemHandlerModifiable; +import net.minecraftforge.items.ItemStackHandler; + +import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.Map; + +public class CoverManager { + + public static final ModelProperty PROPERTY = new ModelProperty<>(); + + private static final String NBT_DIRECTION = "Direction"; + private static final String NBT_ITEM = "Item"; + private static final String NBT_TYPE = "Type"; + + private Map covers = new HashMap<>(); + private NetworkNode node; + + public CoverManager(NetworkNode node) { + this.node = node; + } + + public boolean canConduct(Direction direction) { + Cover cover = getCover(direction); + if (cover != null && cover.getType() != CoverType.HOLLOW) { + return false; + } + + if (node.getWorld() instanceof ServerWorld){ + INetworkNode neighbor = API.instance().getNetworkNodeManager((ServerWorld) node.getWorld()).getNode(node.getPos().offset(direction)); + if (neighbor instanceof ICoverable) { + cover = ((ICoverable) neighbor).getCoverManager().getCover(direction.getOpposite()); + + if (cover != null && cover.getType() != CoverType.HOLLOW) { + return false; + } + } + } + + return true; + } + + @Nullable + public Cover getCover(Direction facing) { + return covers.get(facing); + } + + public boolean hasCover(Direction facing) { + return covers.containsKey(facing); + } + + public boolean setCover(Direction facing, @Nullable Cover cover) { + if (cover == null || (isValidCover(cover.getStack()) && !hasCover(facing))) { + if (cover != null) { + if (!(node instanceof CableNetworkNode) && facing == node.getDirection() && cover.getType() != CoverType.HOLLOW) { + return false; + } + } + + if (cover == null) { + covers.remove(facing); + } else { + covers.put(facing, cover); + } + + node.markDirty(); + + if (node.getNetwork() != null) { + node.getNetwork().getNodeGraph().invalidate(Action.PERFORM, node.getNetwork().getWorld(), node.getNetwork().getPosition()); + } + + return true; + } + + return false; + } + + public void readFromNbt(CompoundNBT nbt) { + covers.clear(); + System.out.println(nbt); + for (String s : nbt.keySet()) { + CompoundNBT tag = nbt.getCompound(s); + if (tag.contains(NBT_DIRECTION) && tag.contains(NBT_ITEM)) { + Direction direction = Direction.byIndex(tag.getInt(NBT_DIRECTION)); + ItemStack item = ItemStack.read(tag.getCompound(NBT_ITEM)); + int type = tag.contains(NBT_TYPE) ? tag.getInt(NBT_TYPE) : 0; + + if (type >= CoverType.values().length) { + type = 0; + } + + if (isValidCover(item)) { + covers.put(direction, new Cover(item, CoverType.values()[type])); + } + } + } + } + + public CompoundNBT writeToNbt() { + CompoundNBT list = new CompoundNBT(); + + for (Map.Entry entry : covers.entrySet()) { + CompoundNBT tag = new CompoundNBT(); + + tag.putInt(NBT_DIRECTION, entry.getKey().ordinal()); + tag.put(NBT_ITEM, entry.getValue().getStack().serializeNBT()); + tag.putInt(NBT_TYPE, entry.getValue().getType().ordinal()); + + list.put(entry.getKey().ordinal() + "",tag); + } + return list; + } + + public IItemHandlerModifiable getAsInventory() { + ItemStackHandler handler = new ItemStackHandler(covers.size()); + + int i = 0; + + for (Map.Entry entry : covers.entrySet()) { + ItemStack cover = entry.getValue().getType().createStack(); + + CoverItem.setItem(cover, entry.getValue().getStack()); + + handler.setStackInSlot(i++, cover); + } + + return handler; + } + + @SuppressWarnings("deprecation") + public static boolean isValidCover(ItemStack item) { + if (item.isEmpty()) { + return false; + } + + Block block = getBlock(item); + + BlockState state = getBlockState(item); + + return block != null && state != null && ((isModelSupported(state) && !block.ticksRandomly(state) && !block.hasTileEntity(state)) || block instanceof GlassBlock || block instanceof StainedGlassBlock); //Removed is top solid as it needs world param + } + + private static boolean isModelSupported(BlockState state) { + if (state.getRenderType() != BlockRenderType.MODEL) { + return false; + } + + return state.isSolid() && state.isSolidSide(Minecraft.getInstance().world, new BlockPos(0,0,0), Direction.UP); //I dont trust this + } + + @Nullable + public static Block getBlock(@Nullable ItemStack item) { + if (item == null) { + return null; + } + + Block block = Block.getBlockFromItem(item.getItem()); + + if (block == Blocks.AIR) { + return null; + } + + return block; + } + + @Nullable + @SuppressWarnings("deprecation") + public static BlockState getBlockState(@Nullable ItemStack item) { + Block block = getBlock(item); + + if (block == null) { + return null; + } + + try { + return block.getDefaultState(); + } catch (Exception e) { + return null; + } + } + +} diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/network/node/cover/CoverType.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/network/node/cover/CoverType.java new file mode 100644 index 000000000..443af0481 --- /dev/null +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/network/node/cover/CoverType.java @@ -0,0 +1,14 @@ +package com.refinedmods.refinedstorage.apiimpl.network.node.cover; + +import com.refinedmods.refinedstorage.RSItems; +import net.minecraft.block.Blocks; +import net.minecraft.item.ItemStack; + +public enum CoverType { + NORMAL, + HOLLOW; + + public ItemStack createStack() { + return new ItemStack(this == NORMAL ? RSItems.COVER.get() : RSItems.HOLLOW_COVER.get() ); + } +} diff --git a/src/main/java/com/refinedmods/refinedstorage/integration/jei/RSJeiPlugin.java b/src/main/java/com/refinedmods/refinedstorage/integration/jei/RSJeiPlugin.java index 4da6927ba..4c5ab5abd 100644 --- a/src/main/java/com/refinedmods/refinedstorage/integration/jei/RSJeiPlugin.java +++ b/src/main/java/com/refinedmods/refinedstorage/integration/jei/RSJeiPlugin.java @@ -1,11 +1,13 @@ package com.refinedmods.refinedstorage.integration.jei; import com.refinedmods.refinedstorage.RS; +import com.refinedmods.refinedstorage.RSItems; import com.refinedmods.refinedstorage.screen.BaseScreen; import mezz.jei.api.IModPlugin; import mezz.jei.api.JeiPlugin; import mezz.jei.api.registration.IGuiHandlerRegistration; import mezz.jei.api.registration.IRecipeTransferRegistration; +import mezz.jei.api.registration.ISubtypeRegistration; import mezz.jei.api.runtime.IJeiRuntime; import net.minecraft.util.ResourceLocation; @@ -39,4 +41,9 @@ public class RSJeiPlugin implements IModPlugin { public static IJeiRuntime getRuntime() { return runtime; } + + @Override + public void registerItemSubtypes(ISubtypeRegistration registration) { + registration.useNbtForSubtypes(RSItems.COVER.get(), RSItems.HOLLOW_COVER.get()); + } } diff --git a/src/main/java/com/refinedmods/refinedstorage/item/CoverItem.java b/src/main/java/com/refinedmods/refinedstorage/item/CoverItem.java new file mode 100644 index 000000000..771bf1c5b --- /dev/null +++ b/src/main/java/com/refinedmods/refinedstorage/item/CoverItem.java @@ -0,0 +1,164 @@ +package com.refinedmods.refinedstorage.item; + +import com.refinedmods.refinedstorage.RS; +import com.refinedmods.refinedstorage.api.network.node.ICoverable; +import com.refinedmods.refinedstorage.api.network.node.INetworkNode; +import com.refinedmods.refinedstorage.api.network.security.Permission; +import com.refinedmods.refinedstorage.apiimpl.API; +import com.refinedmods.refinedstorage.apiimpl.network.node.cover.Cover; +import com.refinedmods.refinedstorage.apiimpl.network.node.cover.CoverManager; +import com.refinedmods.refinedstorage.apiimpl.network.node.cover.CoverType; +import com.refinedmods.refinedstorage.tile.NetworkNodeTile; +import com.refinedmods.refinedstorage.util.WorldUtils; +import net.minecraft.block.Block; +import net.minecraft.block.Blocks; +import net.minecraft.client.util.ITooltipFlag; +import net.minecraft.item.*; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ActionResultType; +import net.minecraft.util.Direction; +import net.minecraft.util.NonNullList; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.world.World; +import net.minecraft.world.server.ServerWorld; +import net.minecraftforge.client.model.ModelDataManager; +import net.minecraftforge.registries.ForgeRegistries; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; + +public class CoverItem extends Item { + + private static final String NBT_ITEM = "Item"; + + public static final ItemStack HIDDEN_COVER_ALTERNATIVE = new ItemStack(Blocks.STONE_BRICKS); + + + public CoverItem() { + super(new Item.Properties().group(RS.MAIN_GROUP)); + } + + public static void setItem(ItemStack cover, ItemStack item) { + if (!cover.hasTag()) { + cover.setTag(new CompoundNBT()); + } + + cover.getTag().put(NBT_ITEM, item.serializeNBT()); + } + + @Nonnull + public static ItemStack getItem(ItemStack cover) { + if (!cover.hasTag() || !cover.getTag().contains(NBT_ITEM)) { + return ItemStack.EMPTY; + } + + return ItemStack.read(cover.getTag().getCompound(NBT_ITEM)); + } + + @Override + public void addInformation(ItemStack stack, @Nullable World worldIn, List tooltip, ITooltipFlag flagIn) { + super.addInformation(stack, worldIn, tooltip, flagIn); + ItemStack item = getItem(stack); + + if (!item.isEmpty()) { + tooltip.add(item.getItem().getDisplayName(item)); + } + } + + @Override + public void fillItemGroup(ItemGroup group, NonNullList items) { + + //if (RS.INSTANCE.config.hideCovers) { + // ItemStack stack = new ItemStack(this); + + //setItem(stack, HIDDEN_COVER_ALTERNATIVE); + + //items.add(stack); + + //return; + //} + if (this.isInGroup(group)) { + for (Block block : ForgeRegistries.BLOCKS.getValues()) { + Item item = Item.getItemFromBlock(block); + + if (item == Items.AIR) { + continue; + } + + NonNullList subBlocks = NonNullList.create(); + + block.fillItemGroup(ItemGroup.SEARCH, subBlocks); + + for (ItemStack subBlock : subBlocks) { + if (CoverManager.isValidCover(subBlock)) { + ItemStack stack = new ItemStack(this); + + setItem(stack, subBlock); + + items.add(stack); + } + } + } + } + } + + @Override + public ActionResultType onItemUse(ItemUseContext context) { + BlockPos pos = context.getPos(); + Direction facing = context.getFace(); + World world = context.getWorld(); + + ItemStack stack = context.getPlayer().getHeldItem(context.getHand()); + + TileEntity tile = world.getTileEntity(pos); + + // Support placing on the bottom side without too much hassle. + if (!canPlaceOn(tile)) { + pos = pos.up(); + + facing = Direction.DOWN; + + tile = world.getTileEntity(pos); + } + + if (canPlaceOn(tile)) { + if (world.isRemote) { + ModelDataManager.requestModelDataRefresh(tile); + return ActionResultType.SUCCESS; + } + + INetworkNode node = ((NetworkNodeTile) tile).getNode(); + + if (node.getNetwork() != null && !node.getNetwork().getSecurityManager().hasPermission(Permission.BUILD, context.getPlayer())) { + WorldUtils.sendNoPermissionMessage(context.getPlayer()); + + return ActionResultType.FAIL; + } + + if (((ICoverable) node).getCoverManager().setCover(facing, createCover(getItem(stack)))) { + context.getPlayer().getHeldItem(context.getHand()).shrink(1); + + WorldUtils.updateBlock(world, pos); + API.instance().getNetworkNodeManager((ServerWorld) world).markForSaving(); + return ActionResultType.SUCCESS; + } + + return ActionResultType.FAIL; + } + + return ActionResultType.PASS; + } + + + private boolean canPlaceOn(TileEntity tile) { + return tile instanceof NetworkNodeTile && ((NetworkNodeTile) tile).getNode() instanceof ICoverable; + } + + protected Cover createCover(ItemStack stack) { + return new Cover(stack, CoverType.NORMAL); + } + +} diff --git a/src/main/java/com/refinedmods/refinedstorage/item/HollowCoverItem.java b/src/main/java/com/refinedmods/refinedstorage/item/HollowCoverItem.java new file mode 100644 index 000000000..fb8997125 --- /dev/null +++ b/src/main/java/com/refinedmods/refinedstorage/item/HollowCoverItem.java @@ -0,0 +1,17 @@ +package com.refinedmods.refinedstorage.item; + +import com.refinedmods.refinedstorage.apiimpl.network.node.cover.Cover; +import com.refinedmods.refinedstorage.apiimpl.network.node.cover.CoverType; +import net.minecraft.item.ItemStack; + +public class HollowCoverItem extends CoverItem{ + + public HollowCoverItem() { + super(); + } + + @Override + protected Cover createCover(ItemStack stack) { + return new Cover(stack, CoverType.HOLLOW); + } +} diff --git a/src/main/java/com/refinedmods/refinedstorage/render/ConstantsCable.java b/src/main/java/com/refinedmods/refinedstorage/render/ConstantsCable.java new file mode 100644 index 000000000..6ef2fbe99 --- /dev/null +++ b/src/main/java/com/refinedmods/refinedstorage/render/ConstantsCable.java @@ -0,0 +1,67 @@ +package com.refinedmods.refinedstorage.render; + +import com.refinedmods.refinedstorage.render.collision.CollisionGroup; +import com.refinedmods.refinedstorage.util.CollisionUtils; +import net.minecraft.util.Direction; +import net.minecraft.util.math.AxisAlignedBB; + +import javax.annotation.Nonnull; + +public class ConstantsCable { + + public static final CollisionGroup CORE = new CollisionGroup().addItem(CollisionUtils.getBounds(6, 6, 6, 10, 10, 10)); + public static final CollisionGroup NORTH = new CollisionGroup().addItem(CollisionUtils.getBounds(6, 6, 0, 10, 10, 6)); + public static final CollisionGroup EAST = new CollisionGroup().addItem(CollisionUtils.getBounds(10, 6, 6, 16, 10, 10)); + public static final CollisionGroup SOUTH = new CollisionGroup().addItem(CollisionUtils.getBounds(6, 6, 10, 10, 10, 16)); + public static final CollisionGroup WEST = new CollisionGroup().addItem(CollisionUtils.getBounds(0, 6, 6, 6, 10, 10)); + public static final CollisionGroup UP = new CollisionGroup().addItem(CollisionUtils.getBounds(6, 10, 6, 10, 16, 10)); + public static final CollisionGroup DOWN = new CollisionGroup().addItem(CollisionUtils.getBounds(6, 0, 6, 10, 6, 10)); + + public static final CollisionGroup HOLDER_NORTH = new CollisionGroup().addItem(getHolderBounds(Direction.NORTH)); + public static final CollisionGroup HOLDER_EAST = new CollisionGroup().addItem(getHolderBounds(Direction.EAST)); + public static final CollisionGroup HOLDER_SOUTH = new CollisionGroup().addItem(getHolderBounds(Direction.SOUTH)); + public static final CollisionGroup HOLDER_WEST = new CollisionGroup().addItem(getHolderBounds(Direction.WEST)); + public static final CollisionGroup HOLDER_UP = new CollisionGroup().addItem(getHolderBounds(Direction.UP)); + public static final CollisionGroup HOLDER_DOWN = new CollisionGroup().addItem(getHolderBounds(Direction.DOWN)); + + @Nonnull + public static AxisAlignedBB getCoverBounds(Direction side) { + switch (side) { + case DOWN: + return CollisionUtils.getBounds(0, 0, 0, 16, 2, 16); + case UP: + return CollisionUtils.getBounds(0, 14, 0, 16, 16, 16); + case NORTH: + return CollisionUtils.getBounds(0, 0, 0, 16, 16, 2); + case SOUTH: + return CollisionUtils.getBounds(0, 0, 14, 16, 16, 16); + case WEST: + return CollisionUtils.getBounds(0, 0, 0, 2, 16, 16); + case EAST: + return CollisionUtils.getBounds(14, 0, 0, 16, 16, 16); + default: + return null; + } + } + + @Nonnull + public static AxisAlignedBB getHolderBounds(Direction side) { + switch (side) { + case DOWN: + return CollisionUtils.getBounds(7, 2, 7, 9, 6, 9); + case UP: + return CollisionUtils.getBounds(7, 10, 7, 9, 14, 9); + case NORTH: + return CollisionUtils.getBounds(7, 7, 2, 9, 9, 6); + case SOUTH: + return CollisionUtils.getBounds(7, 7, 10, 9, 9, 14); + case WEST: + return CollisionUtils.getBounds(2, 7, 7, 6, 9, 9); + case EAST: + return CollisionUtils.getBounds(10, 7, 7, 14, 9, 9); + default: + return null; + } + } + +} diff --git a/src/main/java/com/refinedmods/refinedstorage/render/collision/CollisionGroup.java b/src/main/java/com/refinedmods/refinedstorage/render/collision/CollisionGroup.java new file mode 100644 index 000000000..06ed72c43 --- /dev/null +++ b/src/main/java/com/refinedmods/refinedstorage/render/collision/CollisionGroup.java @@ -0,0 +1,48 @@ +package com.refinedmods.refinedstorage.render.collision; + +import net.minecraft.util.Direction; +import net.minecraft.util.math.AxisAlignedBB; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; + +public class CollisionGroup { + + private List items = new ArrayList<>(); + private boolean canAccessGui; + @Nullable + private Direction direction; + + public CollisionGroup addItem(AxisAlignedBB item) { + items.add(item); + + return this; + } + + public List getItems() { + return items; + } + + public boolean canAccessGui() { + return canAccessGui; + } + + public CollisionGroup setCanAccessGui(boolean canAccessGui) { + this.canAccessGui = canAccessGui; + + return this; + } + + public CollisionGroup setDirection(Direction direction) { + this.direction = direction; + + return this; + } + + @Nullable + public Direction getDirection() { + return direction; + } + +} diff --git a/src/main/java/com/refinedmods/refinedstorage/render/model/BakedModelCableCover.java b/src/main/java/com/refinedmods/refinedstorage/render/model/BakedModelCableCover.java new file mode 100644 index 000000000..f6b4db704 --- /dev/null +++ b/src/main/java/com/refinedmods/refinedstorage/render/model/BakedModelCableCover.java @@ -0,0 +1,364 @@ +package com.refinedmods.refinedstorage.render.model; + +import com.refinedmods.refinedstorage.RS; +import com.refinedmods.refinedstorage.RSBlocks; +import com.refinedmods.refinedstorage.apiimpl.network.node.cover.Cover; +import com.refinedmods.refinedstorage.apiimpl.network.node.cover.CoverManager; +import com.refinedmods.refinedstorage.block.BaseBlock; +import com.refinedmods.refinedstorage.render.ConstantsCable; +import com.refinedmods.refinedstorage.util.RenderUtils; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.Atlases; +import net.minecraft.client.renderer.model.BakedQuad; +import net.minecraft.client.renderer.model.IBakedModel; +import net.minecraft.client.renderer.texture.AtlasTexture; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.util.Direction; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.vector.Vector3f; +import net.minecraftforge.client.model.data.IModelData; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +public class BakedModelCableCover extends DelegateBakedModel{ + + private static TextureAtlasSprite BORDER_SPRITE; + + public BakedModelCableCover(IBakedModel base) { + super(base); + } + + @Override + public List getQuads(@Nullable BlockState state, @Nullable Direction side, Random rand, IModelData data) { + List quads = new ArrayList<>(base.getQuads(state, side, rand, data)); + if (data != null && data.hasProperty(CoverManager.PROPERTY)) { + CoverManager manager = data.getData(CoverManager.PROPERTY); + addCover(quads, manager.getCover(Direction.NORTH), Direction.NORTH, side, rand, manager, state, true); + addCover(quads, manager.getCover(Direction.DOWN), Direction.SOUTH, side, rand, manager, state, true); + addCover(quads, manager.getCover(Direction.EAST), Direction.EAST, side, rand, manager, state, true); + addCover(quads, manager.getCover(Direction.WEST), Direction.WEST, side, rand, manager, state, true); + addCover(quads, manager.getCover(Direction.DOWN), Direction.DOWN, side, rand, manager, state, true); + addCover(quads, manager.getCover(Direction.UP), Direction.UP, side, rand, manager, state, true); + } + return quads; + } + + private static int getHollowCoverSize(@Nullable BlockState state, Direction coverSide) { + if (state == null) { + return 6; + } + + BaseBlock block = (BaseBlock) state.getBlock(); + if (block == RSBlocks.CABLE.get()){ + return 6; + } + + if (block.getDirection() != null && state.get(block.getDirection().getProperty()) == coverSide) { + if (block == RSBlocks.EXPORTER.get()) { + return 6; + } else if (block == RSBlocks.EXTERNAL_STORAGE.get() || block == RSBlocks.IMPORTER.get()) { + return 3; + } else if (block == RSBlocks.CONSTRUCTOR.get() || block == RSBlocks.DESTRUCTOR.get()) { //Removed reader and writer + return 2; + } + } + + return 6; + } + + protected static void addCover(List quads, @Nullable Cover cover, Direction coverSide, Direction side, Random rand, @Nullable CoverManager manager, BlockState state, boolean handle) { + if (cover == null) { + return; + } + + BlockState coverState = CoverManager.getBlockState(cover.getStack()); + + if (coverState == null) { + return; + } + + boolean hasUp = false, hasDown = false, hasEast = false, hasWest = false; + + if (manager != null) { + hasUp = manager.hasCover(Direction.UP); + hasDown = manager.hasCover(Direction.DOWN); + hasEast = manager.hasCover(Direction.EAST); + hasWest = manager.hasCover(Direction.WEST); + } + + TextureAtlasSprite sprite = RenderUtils.getSprite(Minecraft.getInstance().getBlockRendererDispatcher().getModelForState(coverState), coverState, side, rand); + + switch (cover.getType()) { + case NORMAL: + addNormalCover(quads, sprite, coverSide, hasUp, hasDown, hasEast, hasWest, handle); + break; + case HOLLOW: + addHollowCover(quads, sprite, coverSide, hasUp, hasDown, hasEast, hasWest, getHollowCoverSize(state, coverSide)); + break; + } + } + + private static void addNormalCover(List quads, TextureAtlasSprite sprite, Direction coverSide, boolean hasUp, boolean hasDown, boolean hasEast, boolean hasWest, boolean handle) { + AxisAlignedBB bounds = ConstantsCable.getCoverBounds(coverSide); + + Vector3f from = new Vector3f((float) bounds.minX * 16, (float) bounds.minY * 16, (float) bounds.minZ * 16); + Vector3f to = new Vector3f((float) bounds.maxX * 16, (float) bounds.maxY * 16, (float) bounds.maxZ * 16); + + if (coverSide == Direction.NORTH) { + if (hasWest) { + from.setX(2); + } + + if (hasEast) { + to.setX(14); + } + } else if (coverSide == Direction.SOUTH) { + if (hasWest) { + from.setX(2); + } + + if (hasEast) { + to.setX(14); + } + } + + if (coverSide.getAxis() != Direction.Axis.Y) { + if (hasDown) { + from.setY(2); + } + + if (hasUp) { + to.setY(14); + } + } + + quads.addAll(new CubeBuilder().from(from.getX(), from.getY(), from.getZ()).to(to.getX(), to.getY(), to.getZ()).addFaces(face -> new CubeBuilder.Face(face, sprite)).bake()); + + if (handle) { + if (BORDER_SPRITE == null) { + BORDER_SPRITE = Minecraft.getInstance().getAtlasSpriteGetter(AtlasTexture.LOCATION_BLOCKS_TEXTURE).apply(new ResourceLocation(RS.ID , "block/cable_part_border")); + } + + bounds = ConstantsCable.getHolderBounds(coverSide); + + from = new Vector3f((float) bounds.minX * 16, (float) bounds.minY * 16, (float) bounds.minZ * 16); + to = new Vector3f((float) bounds.maxX * 16, (float) bounds.maxY * 16, (float) bounds.maxZ * 16); + + quads.addAll(new CubeBuilder().from(from.getX(), from.getY(), from.getZ()).to(to.getX(), to.getY(), to.getZ()).addFaces(face -> new CubeBuilder.Face(face, BORDER_SPRITE)).bake()); + } + } + + private static void addHollowCover(List quads, TextureAtlasSprite sprite, Direction coverSide, boolean hasUp, boolean hasDown, boolean hasEast, boolean hasWest, int size) { + AxisAlignedBB bounds = ConstantsCable.getCoverBounds(coverSide); + + Vector3f from = new Vector3f((float) bounds.minX * 16, (float) bounds.minY * 16, (float) bounds.minZ * 16); + Vector3f to = new Vector3f((float) bounds.maxX * 16, (float) bounds.maxY * 16, (float) bounds.maxZ * 16); + + if (coverSide.getAxis() != Direction.Axis.Y) { + if (hasDown) { + from.setY(2); + } + + if (hasUp) { + to.setY(14); + } + } + + // Right + if (coverSide == Direction.NORTH) { + if (hasWest) { + from.setX(2); + } else { + from.setX(0); + } + + to.setX(size); + } else if (coverSide == Direction.SOUTH) { + if (hasEast) { + to.setX(14); + } else { + to.setX(16); + } + + from.setX(16 - size); + } else if (coverSide == Direction.EAST) { + from.setZ(0); + to.setZ(size); + } else if (coverSide == Direction.WEST) { + from.setZ(16 - size); + to.setZ(16); + } else if (coverSide == Direction.DOWN || coverSide == Direction.UP) { + from.setZ(16 - size); + to.setZ(16); + } + + quads.addAll(new CubeBuilder() + .from(from.getX(), from.getY(), from.getZ()) + .to(to.getX(), to.getY(), to.getZ()) + .addFaces(face -> new CubeBuilder.Face(face, sprite)) + .bake() + ); + + // Left + if (coverSide == Direction.NORTH) { + if (hasEast) { + to.setX(14); + } else { + to.setX(16); + } + + from.setX(16 - size); + } else if (coverSide == Direction.SOUTH) { + if (hasWest) { + from.setX(2); + } else { + from.setX(0); + } + + to.setX(size); + } else if (coverSide == Direction.EAST) { + from.setZ(16 - size); + to.setZ(16); + } else if (coverSide == Direction.WEST) { + from.setZ(0); + to.setZ(size); + } else if (coverSide == Direction.DOWN || coverSide == Direction.UP) { + from.setZ(0); + to.setZ(size); + } + + quads.addAll(new CubeBuilder() + .from(from.getX(), from.getY(), from.getZ()) + .to(to.getX(), to.getY(), to.getZ()) + .addFaces(face -> new CubeBuilder.Face(face, sprite)) + .bake() + ); + + // Bottom + if (coverSide == Direction.NORTH) { + from.setX(size); + to.setX(16 - size); + + if (hasDown) { + from.setY(2); + } else { + from.setY(0); + } + + to.setY(size); + } else if (coverSide == Direction.SOUTH) { + from.setX(size); + to.setX(16 - size); + + if (hasDown) { + from.setY(2); + } else { + from.setY(0); + } + + to.setY(size); + } else if (coverSide == Direction.EAST) { + from.setZ(size); + to.setZ(16 - size); + + if (hasDown) { + from.setY(2); + } else { + from.setY(0); + } + + to.setY(size); + } else if (coverSide == Direction.WEST) { + from.setZ(size); + to.setZ(16 - size); + + if (hasDown) { + from.setY(2); + } else { + from.setY(0); + } + + to.setY(size); + } else if (coverSide == Direction.DOWN || coverSide == Direction.UP) { + from.setZ(size); + to.setZ(16 - size); + + from.setX(0); + to.setX(size); + } + + quads.addAll(new CubeBuilder() + .from(from.getX(), from.getY(), from.getZ()) + .to(to.getX(), to.getY(), to.getZ()) + .addFaces(face -> new CubeBuilder.Face(face, sprite)) + .bake() + ); + + // Up + if (coverSide == Direction.NORTH) { + from.setX(size); + to.setX(16 - size); + + if (hasUp) { + to.setY(14); + } else { + to.setY(16); + } + + from.setY(16 - size); + } else if (coverSide == Direction.SOUTH) { + from.setX(size); + to.setX(16 - size); + + if (hasUp) { + to.setY(14); + } else { + to.setY(16); + } + + from.setY(16 - size); + } else if (coverSide == Direction.EAST) { + from.setZ(size); + to.setZ(16 - size); + + if (hasUp) { + to.setY(14); + } else { + to.setY(16); + } + + from.setY(16 - size); + } else if (coverSide == Direction.WEST) { + from.setZ(size); + to.setZ(16 - size); + + if (hasUp) { + to.setY(14); + } else { + to.setY(16); + } + + from.setY(16 - size); + } else if (coverSide == Direction.DOWN || coverSide == Direction.UP) { + from.setZ(size); + to.setZ(16 - size); + + from.setX(16 - size); + to.setX(16); + } + + quads.addAll(new CubeBuilder() + .from(from.getX(), from.getY(), from.getZ()) + .to(to.getX(), to.getY(), to.getZ()) + .addFaces(face -> new CubeBuilder.Face(face, sprite)) + .bake() + ); + } +} + diff --git a/src/main/java/com/refinedmods/refinedstorage/render/model/BakedModelCover.java b/src/main/java/com/refinedmods/refinedstorage/render/model/BakedModelCover.java new file mode 100644 index 000000000..4e2592f9d --- /dev/null +++ b/src/main/java/com/refinedmods/refinedstorage/render/model/BakedModelCover.java @@ -0,0 +1,158 @@ +package com.refinedmods.refinedstorage.render.model; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.mojang.blaze3d.matrix.MatrixStack; +import com.refinedmods.refinedstorage.apiimpl.network.node.cover.Cover; +import com.refinedmods.refinedstorage.apiimpl.network.node.cover.CoverType; +import com.refinedmods.refinedstorage.item.CoverItem; +import com.refinedmods.refinedstorage.util.RenderUtils; +import net.minecraft.block.BlockState; +import net.minecraft.client.renderer.model.BakedQuad; +import net.minecraft.client.renderer.model.IBakedModel; +import net.minecraft.client.renderer.model.ItemCameraTransforms; +import net.minecraft.client.renderer.model.ItemOverrideList; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.LivingEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Direction; +import net.minecraft.util.math.vector.TransformationMatrix; + +import javax.annotation.Nullable; +import java.util.*; + +public class BakedModelCover extends BakedModelCableCover{ + + private class CacheKey { + private BlockState state; + private ItemStack stack; + private Direction side; + private CoverType type; + + CacheKey(BlockState state, ItemStack stack, Direction side, CoverType type) { + this.state = state; + this.stack = stack; + this.side = side; + this.type = type; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } + + BakedModelCover.CacheKey cacheKey = (BakedModelCover.CacheKey) o; + + return cacheKey.type == type && cacheKey.stack.getItem() == stack.getItem() /* && cacheKey.stack.getItemDamage() == stack.getItemDamage()*/ && cacheKey.side == side && Objects.equals(cacheKey.state, state); + } + + @Override + public int hashCode() { + int result = stack.getItem().hashCode(); + //result = 31 * result + stack.getDamage(); + result = 31 * result + (side != null ? side.hashCode() : 0); + result = 31 * result + (state != null ? state.hashCode() : 0); + result = 31 * result + type.hashCode(); + return result; + } + } + + private static final LoadingCache> CACHE = CacheBuilder.newBuilder().build(new CacheLoader>() { + @Override + public List load(CacheKey key) { + List quads = new ArrayList<>(); + + addCover(quads, new Cover(key.stack, key.type), Direction.NORTH, key.side, new Random(), null, null, true); + + return quads; + } + }); + + private ItemStack stack; + private CoverType type; + + public BakedModelCover(ItemStack stack, CoverType type) { + super(null); + + this.stack = stack; + this.type = type; + } + + @Override + public List getQuads(@Nullable BlockState state, @Nullable Direction side, Random rand) { + if (stack.isEmpty()) { + return Collections.emptyList(); + } + + CacheKey key = new CacheKey(state, CoverItem.getItem(stack), side, type); + + return CACHE.getUnchecked(key); + } + + @Override + public ItemOverrideList getOverrides() { + return new ItemOverrideList() { + @Override + public IBakedModel func_239290_a_(IBakedModel originalModel, ItemStack stack, @Nullable ClientWorld world, @Nullable LivingEntity entity) { + return new BakedModelCover(stack, type); + } + }; + } + + // @Override + // public Pair handlePerspective(ItemCameraTransforms.TransformType cameraTransformType) { + // TRSRTransformation transform = RenderUtils.getDefaultBlockTransforms().get(cameraTransformType); + // + // return Pair.of(this, transform == null ? RenderUtils.EMPTY_MATRIX_TRANSFORM : transform.getMatrix()); + // } + + @Override + public IBakedModel handlePerspective(ItemCameraTransforms.TransformType cameraTransformType, MatrixStack matrixStack) { + TransformationMatrix transform = RenderUtils.getDefaultBlockTransforms().get(cameraTransformType); + if (transform != null) transform.push(matrixStack); + return this; + } + + @Override + public boolean isAmbientOcclusion() { + return true; + } + + @Override + public boolean isGui3d() { + return true; + } + + @Override + public boolean isBuiltInRenderer() { + return false; + } + + @Override + public TextureAtlasSprite getParticleTexture() { + return null; + } + + @Override + public boolean isAmbientOcclusion(BlockState state) { + return true; + } + + @Override + @SuppressWarnings("deprecation") + public ItemCameraTransforms getItemCameraTransforms() { + return ItemCameraTransforms.DEFAULT; + } + + @Override + public boolean func_230044_c_() { + return true; + } +} diff --git a/src/main/java/com/refinedmods/refinedstorage/render/model/CubeBuilder.java b/src/main/java/com/refinedmods/refinedstorage/render/model/CubeBuilder.java new file mode 100644 index 000000000..6052bc808 --- /dev/null +++ b/src/main/java/com/refinedmods/refinedstorage/render/model/CubeBuilder.java @@ -0,0 +1,354 @@ +package com.refinedmods.refinedstorage.render.model; + +import com.refinedmods.refinedstorage.util.RenderUtils; +import net.minecraft.client.renderer.model.BakedQuad; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.client.renderer.vertex.VertexFormat; +import net.minecraft.client.renderer.vertex.VertexFormatElement; +import net.minecraft.util.Direction; +import net.minecraft.util.math.vector.Vector3f; +import net.minecraftforge.client.model.pipeline.BakedQuadBuilder; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +public class CubeBuilder { + + public enum UvRotation { + CLOCKWISE_0, + CLOCKWISE_90, + CLOCKWISE_180, + CLOCKWISE_270 + } + + private static class Uv { + private float xFrom; + private float xTo; + private float yFrom; + private float yTo; + } + + public static class Face { + private Direction face; + private TextureAtlasSprite sprite; + private int light; + private UvRotation uvRotation = UvRotation.CLOCKWISE_0; + + public Face(Direction face, TextureAtlasSprite sprite) { + this.face = face; + this.sprite = sprite; + } + + public Face(Direction face, TextureAtlasSprite sprite, UvRotation uvRotation) { + this(face, sprite); + + this.uvRotation = uvRotation; + } + + public Face(Direction face, TextureAtlasSprite sprite, UvRotation uvRotation, int light) { + this(face, sprite, uvRotation); + + this.light = light; + } + } + + private Vector3f from; + private Vector3f to; + private VertexFormat format = DefaultVertexFormats.BLOCK; //Changed from Item + private Map faces = new HashMap<>(); + private int color = 0xFFFFFFFF; + + public CubeBuilder from(float x, float y, float z) { + this.from = new Vector3f(x / 16, y / 16, z / 16); + + return this; + } + + public CubeBuilder to(float x, float y, float z) { + this.to = new Vector3f(x / 16, y / 16, z / 16); + + return this; + } + + public CubeBuilder color(int color) { + this.color = color; + + return this; + } + + public CubeBuilder lightmap() { + this.format = RenderUtils.getFormatWithLightMap(format); + + return this; + } + + public CubeBuilder addFaces(Function faceSupplier) { + for (Direction facing : Direction.values()) { + addFace(faceSupplier.apply(facing)); + } + + return this; + } + + public CubeBuilder addFace(Face face) { + faces.put(face.face, face); + + return this; + } + + public List bake() { + List quads = new ArrayList<>(); + + for (Map.Entry entry : faces.entrySet()) { + quads.add(bakeFace(entry.getKey(), entry.getValue())); + } + + return quads; + } + + private BakedQuad bakeFace(Direction facing, Face cubeFace) { + BakedQuadBuilder builder = new BakedQuadBuilder(cubeFace.sprite); //TODO See if can change the vertex format + + //builder.setTexture(cubeFace.sprite); + builder.setQuadOrientation(facing); + builder.setQuadTint(-1); + builder.setApplyDiffuseLighting(true); + + Uv uv = getDefaultUv(facing, cubeFace.sprite, from.getX(), from.getY(), from.getZ(), to.getX(), to.getY(), to.getZ()); + + switch (facing) { + case DOWN: + addVertexTopRight(builder, cubeFace, to.getX(), from.getY(), from.getZ(), uv); + addVertexBottomRight(builder, cubeFace, to.getX(), from.getY(), to.getZ(), uv); + addVertexBottomLeft(builder, cubeFace, from.getX(), from.getY(), to.getZ(), uv); + addVertexTopLeft(builder, cubeFace, from.getX(), from.getY(), from.getZ(), uv); + break; + case UP: + addVertexTopLeft(builder, cubeFace, from.getX(), to.getY(), from.getZ(), uv); + addVertexBottomLeft(builder, cubeFace, from.getX(), to.getY(), to.getZ(), uv); + addVertexBottomRight(builder, cubeFace, to.getX(), to.getY(), to.getZ(), uv); + addVertexTopRight(builder, cubeFace, to.getX(), to.getY(), from.getZ(), uv); + break; + case NORTH: + addVertexBottomRight(builder, cubeFace, to.getX(), to.getY(), from.getZ(), uv); + addVertexTopRight(builder, cubeFace, to.getX(), from.getY(), from.getZ(), uv); + addVertexTopLeft(builder, cubeFace, from.getX(), from.getY(), from.getZ(), uv); + addVertexBottomLeft(builder, cubeFace, from.getX(), to.getY(), from.getZ(), uv); + break; + case SOUTH: + addVertexBottomLeft(builder, cubeFace, from.getX(), to.getY(), to.getZ(), uv); + addVertexTopLeft(builder, cubeFace, from.getX(), from.getY(), to.getZ(), uv); + addVertexTopRight(builder, cubeFace, to.getX(), from.getY(), to.getZ(), uv); + addVertexBottomRight(builder, cubeFace, to.getX(), to.getY(), to.getZ(), uv); + break; + case WEST: + addVertexTopLeft(builder, cubeFace, from.getX(), from.getY(), from.getZ(), uv); + addVertexTopRight(builder, cubeFace, from.getX(), from.getY(), to.getZ(), uv); + addVertexBottomRight(builder, cubeFace, from.getX(), to.getY(), to.getZ(), uv); + addVertexBottomLeft(builder, cubeFace, from.getX(), to.getY(), from.getZ(), uv); + break; + case EAST: + addVertexBottomRight(builder, cubeFace, to.getX(), to.getY(), from.getZ(), uv); + addVertexBottomLeft(builder, cubeFace, to.getX(), to.getY(), to.getZ(), uv); + addVertexTopLeft(builder, cubeFace, to.getX(), from.getY(), to.getZ(), uv); + addVertexTopRight(builder, cubeFace, to.getX(), from.getY(), from.getZ(), uv); + break; + } + + return builder.build(); + } + + private Uv getDefaultUv(Direction face, TextureAtlasSprite texture, float fromX, float fromY, float fromZ, float toX, float toY, float toZ) { + Uv uv = new Uv(); + + switch (face) { + case DOWN: + uv.xFrom = texture.getInterpolatedU(fromX * 16); + uv.yFrom = texture.getInterpolatedV(16 - fromZ * 16); + uv.xTo = texture.getInterpolatedU(toX * 16); + uv.yTo = texture.getInterpolatedV(16 - toZ * 16); + break; + case UP: + uv.xFrom = texture.getInterpolatedU(fromX * 16); + uv.yFrom = texture.getInterpolatedV(fromZ * 16); + uv.xTo = texture.getInterpolatedU(toX * 16); + uv.yTo = texture.getInterpolatedV(toZ * 16); + break; + case NORTH: + uv.xFrom = texture.getInterpolatedU(16 - fromX * 16); + uv.yFrom = texture.getInterpolatedV(16 - fromY * 16); + uv.xTo = texture.getInterpolatedU(16 - toX * 16); + uv.yTo = texture.getInterpolatedV(16 - toY * 16); + break; + case SOUTH: + uv.xFrom = texture.getInterpolatedU(fromX * 16); + uv.yFrom = texture.getInterpolatedV(16 - fromY * 16); + uv.xTo = texture.getInterpolatedU(toX * 16); + uv.yTo = texture.getInterpolatedV(16 - toY * 16); + break; + case WEST: + uv.xFrom = texture.getInterpolatedU(fromZ * 16); + uv.yFrom = texture.getInterpolatedV(16 - fromY * 16); + uv.xTo = texture.getInterpolatedU(toZ * 16); + uv.yTo = texture.getInterpolatedV(16 - toY * 16); + break; + case EAST: + uv.xFrom = texture.getInterpolatedU(16 - toZ * 16); + uv.yFrom = texture.getInterpolatedV(16 - fromY * 16); + uv.xTo = texture.getInterpolatedU(16 - fromZ * 16); + uv.yTo = texture.getInterpolatedV(16 - toY * 16); + break; + } + + return uv; + } + + private void addVertexTopLeft(BakedQuadBuilder builder, Face face, float x, float y, float z, Uv uv) { + float u; + float v; + + switch (face.uvRotation) { + default: + case CLOCKWISE_0: + u = uv.xFrom; + v = uv.yFrom; + break; + case CLOCKWISE_90: + u = uv.xFrom; + v = uv.yTo; + break; + case CLOCKWISE_180: + u = uv.xTo; + v = uv.yTo; + break; + case CLOCKWISE_270: + u = uv.xTo; + v = uv.yFrom; + break; + } + + addVertex(builder, face, x, y, z, u, v); + } + + private void addVertexTopRight(BakedQuadBuilder builder, Face face, float x, float y, float z, Uv uv) { + float u; + float v; + + switch (face.uvRotation) { + default: + case CLOCKWISE_0: + u = uv.xTo; + v = uv.yFrom; + break; + case CLOCKWISE_90: + u = uv.xFrom; + v = uv.yFrom; + break; + case CLOCKWISE_180: + u = uv.xFrom; + v = uv.yTo; + break; + case CLOCKWISE_270: + u = uv.xTo; + v = uv.yTo; + break; + } + + addVertex(builder, face, x, y, z, u, v); + } + + private void addVertexBottomRight(BakedQuadBuilder builder, Face face, float x, float y, float z, Uv uv) { + float u; + float v; + + switch (face.uvRotation) { + default: + case CLOCKWISE_0: + u = uv.xTo; + v = uv.yTo; + break; + case CLOCKWISE_90: + u = uv.xTo; + v = uv.yFrom; + break; + case CLOCKWISE_180: + u = uv.xFrom; + v = uv.yFrom; + break; + case CLOCKWISE_270: + u = uv.xFrom; + v = uv.yTo; + break; + } + + addVertex(builder, face, x, y, z, u, v); + } + + private void addVertexBottomLeft(BakedQuadBuilder builder, Face face, float x, float y, float z, Uv uv) { + float u; + float v; + + switch (face.uvRotation) { + default: + case CLOCKWISE_0: + u = uv.xFrom; + v = uv.yTo; + break; + case CLOCKWISE_90: + u = uv.xTo; + v = uv.yTo; + break; + case CLOCKWISE_180: + u = uv.xTo; + v = uv.yFrom; + break; + case CLOCKWISE_270: + u = uv.xFrom; + v = uv.yFrom; + break; + } + + addVertex(builder, face, x, y, z, u, v); + } + + private void addVertex(BakedQuadBuilder builder, Face face, float x, float y, float z, float u, float v) { + VertexFormat format = builder.getVertexFormat(); + + for (int i = 0; i < format.getElements().size(); i++) { + VertexFormatElement e = format.getElements().get(i); + + switch (e.getUsage()) { + case POSITION: + builder.put(i, x, y, z); + break; + case NORMAL: + builder.put(i, face.face.getXOffset(), face.face.getYOffset(), face.face.getZOffset()); + break; + case COLOR: + float r = (color >> 16 & 0xFF) / 255F; + float g = (color >> 8 & 0xFF) / 255F; + float b = (color & 0xFF) / 255F; + float a = (color >> 24 & 0xFF) / 255F; + + builder.put(i, r, g, b, a); + break; + case UV: + if (e.getIndex() == 0) { + builder.put(i, u, v); + } else { + builder.put(i, (float) (face.light * 0x20) / 0xFFFF, (float) (face.light * 0x20) / 0xFFFF); + } + + break; + default: + builder.put(i); + break; + } + } + } + +} diff --git a/src/main/java/com/refinedmods/refinedstorage/setup/ClientSetup.java b/src/main/java/com/refinedmods/refinedstorage/setup/ClientSetup.java index 508d6b31d..323e3e418 100644 --- a/src/main/java/com/refinedmods/refinedstorage/setup/ClientSetup.java +++ b/src/main/java/com/refinedmods/refinedstorage/setup/ClientSetup.java @@ -2,6 +2,7 @@ package com.refinedmods.refinedstorage.setup; import com.refinedmods.refinedstorage.*; import com.refinedmods.refinedstorage.apiimpl.API; +import com.refinedmods.refinedstorage.apiimpl.network.node.cover.CoverType; import com.refinedmods.refinedstorage.container.CrafterContainer; import com.refinedmods.refinedstorage.container.CrafterManagerContainer; import com.refinedmods.refinedstorage.container.slot.CrafterManagerSlot; @@ -23,14 +24,17 @@ import net.minecraft.client.gui.ScreenManager; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderTypeLookup; +import net.minecraft.client.renderer.texture.AtlasTexture; import net.minecraft.inventory.container.Container; import net.minecraft.inventory.container.Slot; import net.minecraft.item.DyeColor; import net.minecraft.item.ItemModelsProperties; +import net.minecraft.item.ItemStack; import net.minecraft.resources.IReloadableResourceManager; import net.minecraft.resources.IResourceManager; import net.minecraft.util.ResourceLocation; import net.minecraftforge.client.event.ModelBakeEvent; +import net.minecraftforge.client.event.TextureStitchEvent; import net.minecraftforge.client.model.ModelLoader; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.eventbus.api.SubscribeEvent; @@ -107,6 +111,9 @@ public class ClientSetup { false, new ResourceLocation(RS.ID, "block/disks/leds") )); + bakedModelOverrideRegistry.add(new ResourceLocation(RS.ID, "cable"), (base, registry) -> new BakedModelCableCover(base)); + bakedModelOverrideRegistry.add(new ResourceLocation(RS.ID, "cover"), (base, registry) -> new BakedModelCover(ItemStack.EMPTY, CoverType.NORMAL)); + bakedModelOverrideRegistry.add(new ResourceLocation(RS.ID, "hollow_cover"), (base, registry) -> new BakedModelCover(ItemStack.EMPTY, CoverType.HOLLOW)); forEachColorApply("disk_manipulator", (name, color) -> bakedModelOverrideRegistry.add(name, (base, registry) -> new FullbrightBakedModel( new DiskManipulatorBakedModel( @@ -167,6 +174,7 @@ public class ClientSetup { FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onClientSetup); FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onModelBake); + FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onTextureStitch); MinecraftForge.EVENT_BUS.addListener(new ExperimentalLightingPipelineNagger()::onPlayerLoggedIn); API.instance().addPatternRenderHandler(pattern -> Screen.hasShiftDown()); @@ -305,4 +313,10 @@ public class ClientSetup { } } } + + @SubscribeEvent + public void onTextureStitch(TextureStitchEvent.Pre event){ + if (event.getMap().getTextureLocation().equals(AtlasTexture.LOCATION_BLOCKS_TEXTURE)) + event.addSprite(new ResourceLocation(RS.ID ,"block/cable_part_border")); + } } diff --git a/src/main/java/com/refinedmods/refinedstorage/tile/CableTile.java b/src/main/java/com/refinedmods/refinedstorage/tile/CableTile.java index a3910ae86..e482637c1 100644 --- a/src/main/java/com/refinedmods/refinedstorage/tile/CableTile.java +++ b/src/main/java/com/refinedmods/refinedstorage/tile/CableTile.java @@ -1,15 +1,46 @@ package com.refinedmods.refinedstorage.tile; import com.refinedmods.refinedstorage.RSTiles; +import com.refinedmods.refinedstorage.api.network.node.ICoverable; import com.refinedmods.refinedstorage.apiimpl.network.node.CableNetworkNode; +import com.refinedmods.refinedstorage.apiimpl.network.node.DiskState; +import com.refinedmods.refinedstorage.apiimpl.network.node.cover.CoverManager; +import com.refinedmods.refinedstorage.tile.data.TileDataManager; +import com.refinedmods.refinedstorage.tile.data.TileDataParameter; +import com.refinedmods.refinedstorage.util.WorldUtils; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.client.Minecraft; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.IntNBT; +import net.minecraft.nbt.ListNBT; +import net.minecraft.network.datasync.DataSerializers; +import net.minecraft.network.datasync.IDataSerializer; +import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; +import net.minecraftforge.client.model.ModelDataManager; +import net.minecraftforge.client.model.data.IModelData; +import net.minecraftforge.client.model.data.ModelDataMap; +import net.minecraftforge.client.model.data.ModelProperty; +import net.minecraftforge.common.util.Constants; import javax.annotation.Nonnull; +import java.util.HashMap; public class CableTile extends NetworkNodeTile { + + public static final TileDataParameter COVER_MANAGER = new TileDataParameter<>(DataSerializers.COMPOUND_NBT, new CompoundNBT(), t -> t.getNode().getCoverManager().writeToNbt(), (t, v) -> t.getNode().getCoverManager().readFromNbt(v), (initial, p) -> Minecraft.getInstance().enqueue(() -> { + + })); + + static { + TileDataManager.registerParameter(COVER_MANAGER); + } + public CableTile() { super(RSTiles.CABLE); + dataManager.addWatchedParameter(COVER_MANAGER); } @Override @@ -17,4 +48,30 @@ public class CableTile extends NetworkNodeTile { public CableNetworkNode createNode(World world, BlockPos pos) { return new CableNetworkNode(world, pos); } + + @Nonnull + @Override + public IModelData getModelData() { + return new ModelDataMap.Builder().withInitial(CoverManager.PROPERTY, this.getNode().getCoverManager()).build(); + } + + @Override + public CompoundNBT writeUpdate(CompoundNBT tag) { + super.writeUpdate(tag); + tag.put("Covers", this.getNode().getCoverManager().writeToNbt()); + return tag; + } + + @Override + public void readUpdate(CompoundNBT tag) { + super.readUpdate(tag); + + this.getNode().getCoverManager().readFromNbt(tag.getCompound("Covers")); + + requestModelDataUpdate(); + + WorldUtils.updateBlock(world, pos); + } + + } diff --git a/src/main/java/com/refinedmods/refinedstorage/util/CollisionUtils.java b/src/main/java/com/refinedmods/refinedstorage/util/CollisionUtils.java index f298ca8c2..8eebc7273 100644 --- a/src/main/java/com/refinedmods/refinedstorage/util/CollisionUtils.java +++ b/src/main/java/com/refinedmods/refinedstorage/util/CollisionUtils.java @@ -19,4 +19,9 @@ public final class CollisionUtils { && hit.z >= aabb.minZ && hit.z <= aabb.maxZ; } + + public static AxisAlignedBB getBounds(int fromX, int fromY, int fromZ, int toX, int toY, int toZ) { + return new AxisAlignedBB((float) fromX / 16F, (float) fromY / 16F, (float) fromZ / 16F, (float) toX / 16F, (float) toY / 16F, (float) toZ / 16F); + } + } diff --git a/src/main/java/com/refinedmods/refinedstorage/util/RenderUtils.java b/src/main/java/com/refinedmods/refinedstorage/util/RenderUtils.java index a3238868d..002ea91b8 100644 --- a/src/main/java/com/refinedmods/refinedstorage/util/RenderUtils.java +++ b/src/main/java/com/refinedmods/refinedstorage/util/RenderUtils.java @@ -1,29 +1,45 @@ package com.refinedmods.refinedstorage.util; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.systems.RenderSystem; import com.refinedmods.refinedstorage.api.util.IComparer; import com.refinedmods.refinedstorage.apiimpl.API; import com.refinedmods.refinedstorage.render.Styles; import com.refinedmods.refinedstorage.screen.BaseScreen; +import net.minecraft.block.BlockState; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.renderer.IRenderTypeBuffer; +import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.model.BakedQuad; +import net.minecraft.client.renderer.model.IBakedModel; +import net.minecraft.client.renderer.model.ItemCameraTransforms; +import net.minecraft.client.renderer.model.ItemTransformVec3f; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.client.renderer.vertex.VertexFormat; +import net.minecraft.client.renderer.vertex.VertexFormatElement; import net.minecraft.client.util.ITooltipFlag; import net.minecraft.item.ItemStack; +import net.minecraft.util.Direction; import net.minecraft.util.math.vector.Matrix4f; +import net.minecraft.util.math.vector.Quaternion; +import net.minecraft.util.math.vector.TransformationMatrix; +import net.minecraft.util.math.vector.Vector3f; import net.minecraft.util.text.*; +import net.minecraftforge.client.ForgeHooksClient; +import net.minecraftforge.client.MinecraftForgeClient; import net.minecraftforge.client.event.RenderTooltipEvent; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fml.client.gui.GuiUtils; import javax.annotation.Nonnull; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; +import java.util.function.Consumer; public final class RenderUtils { private RenderUtils() { @@ -299,4 +315,100 @@ public final class RenderUtils { public static boolean inBounds(int x, int y, int w, int h, double ox, double oy) { return ox >= x && ox <= x + w && oy >= y && oy <= y + h; } + + public static boolean isLightMapDisabled() { + return false; //FMLClientHandler.instance().hasOptifine() || !ForgeModContainer.forgeLightPipelineEnabled; TODO + } + + public static VertexFormat getFormatWithLightMap(VertexFormat format) { + if (isLightMapDisabled()) { + return format; + } + + if (format == DefaultVertexFormats.BLOCK) { + return DefaultVertexFormats.BLOCK; + } else if (!format.hasUV(1)) { ; + return new VertexFormat(ImmutableList.builder().addAll(format.getElements()).add(DefaultVertexFormats.TEX_2S).build()); + } + + return format; + } + + public static TextureAtlasSprite getSprite(IBakedModel coverModel, BlockState coverState, Direction facing, Random rand) { + TextureAtlasSprite sprite = null; + + RenderType originalLayer = MinecraftForgeClient.getRenderLayer(); + + try { + for (RenderType layer : RenderType.getBlockRenderTypes()) { + ForgeHooksClient.setRenderLayer(layer); + + for (BakedQuad bakedQuad : coverModel.getQuads(coverState, facing, rand)) { + return bakedQuad.func_187508_a(); + } + + for (BakedQuad bakedQuad : coverModel.getQuads(coverState, null, rand)) { + if (sprite == null) { + sprite = bakedQuad.func_187508_a(); + } + + if (bakedQuad.getFace() == facing) { + return bakedQuad.func_187508_a(); + } + } + } + } catch (Exception e) { + // NO OP + } finally { + ForgeHooksClient.setRenderLayer(originalLayer); + } + + if (sprite == null) { + try { + sprite = coverModel.getParticleTexture(); + } catch (Exception e) { + // NO OP + } + } + + if (sprite == null) { + sprite = null; //TODO Get missing sprite + } + + return sprite; + } + + private static ImmutableMap DEFAULT_BLOCK_TRANSFORM; + + public static ImmutableMap getDefaultBlockTransforms() { + if (DEFAULT_BLOCK_TRANSFORM != null) { + return DEFAULT_BLOCK_TRANSFORM; + } + + TransformationMatrix thirdperson = getTransform(0, 2.5f, 0, 75, 45, 0, 0.375f); + + return DEFAULT_BLOCK_TRANSFORM = ImmutableMap.builder() + .put(ItemCameraTransforms.TransformType.GUI, getTransform(0, 0, 0, 30, 225, 0, 0.625f)) + .put(ItemCameraTransforms.TransformType.GROUND, getTransform(0, 3, 0, 0, 0, 0, 0.25f)) + .put(ItemCameraTransforms.TransformType.FIXED, getTransform(0, 0, 0, 0, 0, 0, 0.5f)) + .put(ItemCameraTransforms.TransformType.THIRD_PERSON_RIGHT_HAND, thirdperson) + .put(ItemCameraTransforms.TransformType.THIRD_PERSON_LEFT_HAND, leftifyTransform(thirdperson)) + .put(ItemCameraTransforms.TransformType.FIRST_PERSON_RIGHT_HAND, getTransform(0, 0, 0, 0, 45, 0, 0.4f)) + .put(ItemCameraTransforms.TransformType.FIRST_PERSON_LEFT_HAND, getTransform(0, 0, 0, 0, 225, 0, 0.4f)) + .build(); + } + + private static TransformationMatrix leftifyTransform(TransformationMatrix transform) { + return transform.blockCornerToCenter().blockCenterToCorner(); + } + + private static TransformationMatrix getTransform(float tx, float ty, float tz, float ax, float ay, float az, float s) { + return new TransformationMatrix( + new Vector3f(tx / 16, ty / 16, tz / 16), + new Quaternion(ax, ay, az, true), + new Vector3f(s, s, s), + null + ); + } + }