package crazypants.enderio.machine.capbank.render; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.resources.IResourceManager; import net.minecraft.client.resources.IResourceManagerReloadListener; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.IIcon; import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; import com.enderio.core.client.render.BoundingBox; import com.enderio.core.client.render.RenderUtil; import com.enderio.core.common.util.BlockCoord; import com.enderio.core.common.util.ForgeDirectionOffsets; import com.enderio.core.common.vecmath.Vector3d; import com.enderio.core.common.vecmath.Vector4f; import com.enderio.core.common.vecmath.Vertex; import crazypants.enderio.EnderIO; import crazypants.enderio.machine.capbank.CapBankType; import crazypants.enderio.machine.capbank.InfoDisplayType; import crazypants.enderio.machine.capbank.TileCapBank; import crazypants.enderio.machine.capbank.network.CapBankClientNetwork; public class FillGauge implements IInfoRenderer, IResourceManagerReloadListener { private static final double HEIGHT = 0.75; private static final double VERT_BORDER = (1 - HEIGHT) / 2; private static final double WIDTH = 0.25; enum Type { SINGLE, TOP, BOTTOM, MIDDLE } private IIcon barIcon; private IIcon gaugeIcon; private float barHeightV; private Map<GaugeKey, List<Vertex>> gaugeVertexCache; private Map<GaugeKey, List<Vertex>> levelVertexCache; private float barMinV; FillGauge() { RenderUtil.registerReloadListener(this); } @Override public void render(TileCapBank cb, ForgeDirection dir, double x, double y, double z, float partialTick) { CapBankClientNetwork nw = null; if(cb.getNetwork() != null) { nw = (CapBankClientNetwork) cb.getNetwork(); nw.requestPowerUpdate(cb, 20); } int brightness = cb.getWorldObj().getLightBrightnessForSkyBlocks(cb.xCoord + dir.offsetX, cb.yCoord + dir.offsetY, cb.zCoord + dir.offsetZ, 0); GaugeInfo info = getGaugeInfo(cb, dir); GaugeKey key = new GaugeKey(dir, info.type); doRender(nw, brightness, info, key); } public void doRender(CapBankClientNetwork nw, int brightness, GaugeInfo info, GaugeKey key) { if(gaugeVertexCache == null) { createVertexCache(); } RenderUtil.bindBlockTexture(); Tessellator tes = Tessellator.instance; tes.startDrawingQuads(); tes.setBrightness(brightness); tes.setColorOpaque_F(1, 1, 1); List<Vertex> verts = gaugeVertexCache.get(key); RenderUtil.addVerticesToTessellator(verts, Tessellator.instance); renderFillBar(key, nw, info); tes.draw(); } private void renderFillBar(GaugeKey key, CapBankClientNetwork nw, GaugeInfo info) { double ratio = 0; if(nw != null) { ratio = nw.getEnergyStoredRatio(); } if(ratio <= 0) { return; } double maxY = ratio * info.height; if(maxY <= info.yPosition) { //empty return; } Vector3d offset = ForgeDirectionOffsets.offsetScaled(key.dir, 0.005); Tessellator.instance.addTranslation((float) offset.x, (float) offset.y, (float) offset.z); List<Vertex> verts = levelVertexCache.get(key); if(maxY >= info.yPosition + 1) { //full bar RenderUtil.addVerticesToTessellator(verts, Tessellator.instance); } else { //need to render partial bar double myMaxY = maxY - info.yPosition; if(info.type == Type.BOTTOM || info.type == Type.SINGLE) { //If we have some power and we are the bottom bit of the display, //always show at least a little bit in the bar myMaxY = Math.max(0.2, myMaxY); } List<Vertex> newVerts = new ArrayList<Vertex>(); for (Vertex v : verts) { v = new Vertex(v); newVerts.add(v); if(v.y() > myMaxY) { v.setXYZ(v.x(), myMaxY, v.z()); v.setUV(v.u(), barMinV + (float) (myMaxY * barHeightV)); } } RenderUtil.addVerticesToTessellator(newVerts, Tessellator.instance); } offset.scale(-1); Tessellator.instance.addTranslation((float) offset.x, (float) offset.y, (float) offset.z); } private GaugeInfo getGaugeInfo(TileCapBank cb, ForgeDirection dir) { if (!cb.getType().isMultiblock()) { return new GaugeInfo(1, 0); } int height = 1; int yPos = 0; BlockCoord loc = cb.getLocation(); boolean found = true; while (found) { loc = loc.getLocation(ForgeDirection.UP); if(isGaugeType(cb.getWorldObj(), loc, dir, cb.getType())) { height++; } else { found = false; } } loc = cb.getLocation(); found = true; while (found) { loc = loc.getLocation(ForgeDirection.DOWN); if(isGaugeType(cb.getWorldObj(), loc, dir, cb.getType())) { height++; yPos++; } else { found = false; } } return new GaugeInfo(height, yPos); } private boolean isGaugeType(World worldObj, BlockCoord bc, ForgeDirection face, CapBankType type) { TileEntity te = worldObj.getTileEntity(bc.x, bc.y, bc.z); if(te instanceof TileCapBank) { TileCapBank cb = (TileCapBank) te; return type == cb.getType() && cb.getDisplayType(face) == InfoDisplayType.LEVEL_BAR; } return false; } @Override public void onResourceManagerReload(IResourceManager p_110549_1_) { createVertexCache(); } private void createVertexCache() { barIcon = EnderIO.blockCapBank.getFillBarIcon(); barMinV = barIcon.getMinV(); barHeightV = barIcon.getMaxV() - barIcon.getMinV(); gaugeIcon = EnderIO.blockCapBank.getGaugeIcon(); gaugeVertexCache = new HashMap<FillGauge.GaugeKey, List<Vertex>>(); levelVertexCache = new HashMap<FillGauge.GaugeKey, List<Vertex>>(); for (ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) { if(dir.offsetY == 0) { for (Type type : Type.values()) { GaugeKey key = new GaugeKey(dir, type); gaugeVertexCache.put(key, createGaugeBoundForFace(key, gaugeIcon)); levelVertexCache.put(key, createGaugeBoundForFace(key, barIcon)); } } } } protected List<Vertex> createGaugeBoundForFace(GaugeKey key, IIcon icon) { ForgeDirection dir = key.dir; Type type = key.type; double widthScale = 0.25; double heightScale = 0.8; double xScale = dir.offsetX == 0 ? widthScale : 1; double yScale = 1; double zScale = dir.offsetZ == 0 ? widthScale : 1; BoundingBox bb = BoundingBox.UNIT_CUBE; Vector3d off = ForgeDirectionOffsets.forDirCopy(dir); off.scale(-1); bb = bb.translate(off); bb = bb.scale(xScale, yScale, zScale); off.scale(-1); bb = bb.translate(off); Vector4f uv = getUvForType(key.type, icon); List<Vertex> result = bb.getCornersWithUvForFace(dir, uv.x, uv.y, uv.z, uv.w); return result; } private Vector4f getUvForType(Type type, IIcon icon) { double uWidth = (icon.getMaxU() - icon.getMinU()) / 4; Vector4f res = new Vector4f(); res.x = (float) (icon.getMinU() + type.ordinal() * uWidth); res.y = (float) (res.x + uWidth); res.z = icon.getMinV(); res.w = icon.getMaxV(); return res; } static class GaugeInfo { int height; int yPosition; Type type; GaugeInfo(int height, int position) { this.height = height; yPosition = position; type = calcType(); } Type calcType() { if(height == 1) { return Type.SINGLE; } if(yPosition == 0) { return Type.BOTTOM; } if(yPosition == height - 1) { return Type.TOP; } return Type.MIDDLE; } } static class GaugeKey { ForgeDirection dir; Type type; GaugeKey(ForgeDirection dir, Type type) { this.dir = dir; this.type = type; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((dir == null) ? 0 : dir.hashCode()); result = prime * result + ((type == null) ? 0 : type.hashCode()); return result; } @Override public boolean equals(Object obj) { if(this == obj) { return true; } if(obj == null) { return false; } if(getClass() != obj.getClass()) { return false; } GaugeKey other = (GaugeKey) obj; if(dir != other.dir) { return false; } if(type != other.type) { return false; } return true; } } }