package team.chisel.ctmlib; import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.XNEG_LB; import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.XNEG_LT; import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.XNEG_RB; import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.XNEG_RT; import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.XPOS_LB; import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.XPOS_LT; import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.XPOS_RB; import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.XPOS_RT; import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.YNEG_LB; import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.YNEG_LT; import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.YNEG_RB; import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.YNEG_RT; import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.YPOS_LB; import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.YPOS_LT; import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.YPOS_RB; import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.YPOS_RT; import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.ZNEG_LB; import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.ZNEG_LT; import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.ZNEG_RB; import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.ZNEG_RT; import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.ZPOS_LB; import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.ZPOS_LT; import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.ZPOS_RB; import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.ZPOS_RT; import static team.chisel.ctmlib.RenderBlocksCTM.Vert.X; import static team.chisel.ctmlib.RenderBlocksCTM.Vert.XY; import static team.chisel.ctmlib.RenderBlocksCTM.Vert.XYZ; import static team.chisel.ctmlib.RenderBlocksCTM.Vert.XY_HALF; import static team.chisel.ctmlib.RenderBlocksCTM.Vert.XY_HALF_Z; import static team.chisel.ctmlib.RenderBlocksCTM.Vert.XZ; import static team.chisel.ctmlib.RenderBlocksCTM.Vert.XZ_HALF; import static team.chisel.ctmlib.RenderBlocksCTM.Vert.XZ_HALF_Y; import static team.chisel.ctmlib.RenderBlocksCTM.Vert.X_HALF; import static team.chisel.ctmlib.RenderBlocksCTM.Vert.X_HALF_Y; import static team.chisel.ctmlib.RenderBlocksCTM.Vert.X_HALF_YZ; import static team.chisel.ctmlib.RenderBlocksCTM.Vert.X_HALF_Z; import static team.chisel.ctmlib.RenderBlocksCTM.Vert.Y; import static team.chisel.ctmlib.RenderBlocksCTM.Vert.YZ; import static team.chisel.ctmlib.RenderBlocksCTM.Vert.YZ_HALF; import static team.chisel.ctmlib.RenderBlocksCTM.Vert.YZ_HALF_X; import static team.chisel.ctmlib.RenderBlocksCTM.Vert.Y_HALF; import static team.chisel.ctmlib.RenderBlocksCTM.Vert.Y_HALF_X; import static team.chisel.ctmlib.RenderBlocksCTM.Vert.Y_HALF_XZ; import static team.chisel.ctmlib.RenderBlocksCTM.Vert.Y_HALF_Z; import static team.chisel.ctmlib.RenderBlocksCTM.Vert.Z; import static team.chisel.ctmlib.RenderBlocksCTM.Vert.ZERO; import static team.chisel.ctmlib.RenderBlocksCTM.Vert.Z_HALF; import static team.chisel.ctmlib.RenderBlocksCTM.Vert.Z_HALF_X; import static team.chisel.ctmlib.RenderBlocksCTM.Vert.Z_HALF_XY; import static team.chisel.ctmlib.RenderBlocksCTM.Vert.Z_HALF_Y; import net.minecraft.block.Block; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.RenderBlocks; import net.minecraft.client.renderer.Tessellator; import net.minecraft.util.IIcon; import net.minecraftforge.common.util.ForgeDirection; import com.cricketcraft.chisel.api.IConnectionAccess; import lombok.Getter; public class RenderBlocksCTM extends RenderBlocks { /** * An enum for all possible 26 sub-side vertices. * * The naming scheme is as follows: * * ZERO is a special case, it is the absolute min point of the block. X, Y, Z, or any combination means that the * axes listed in the name are at 1. X, Y, Z, or any combination followed by HALF means that those axes are at 0.5. * X, Y, Z, or any combination after a HALF means those axes are at 1. */ protected enum Vert { // @formatter:off ZERO(0, 0, 0), XYZ(1, 1, 1), X(1, 0, 0), Y(0, 1, 0), Z(0, 0, 1), XY(1, 1, 0), YZ(0, 1, 1), XZ(1, 0, 1), X_HALF(0.5, 0, 0), Y_HALF(0, 0.5, 0), Z_HALF(0, 0, 0.5), XY_HALF(0.5, 0.5, 0), YZ_HALF(0, 0.5, 0.5), XZ_HALF(0.5, 0, 0.5), X_HALF_Y(0.5, 1, 0), X_HALF_Z(0.5, 0, 1), Y_HALF_X(1, 0.5, 0), Y_HALF_Z(0, 0.5, 1), Z_HALF_X(1, 0, 0.5), Z_HALF_Y(0, 1, 0.5), X_HALF_YZ(0.5, 1, 1), Y_HALF_XZ(1, 0.5, 1), Z_HALF_XY(1, 1, 0.5), XY_HALF_Z(0.5, 0.5, 1), YZ_HALF_X(1, 0.5, 0.5), XZ_HALF_Y(0.5, 1, 0.5); // @formatter:on private final double x, y, z; Vert(double x, double y, double z) { this.x = x; this.y = y; this.z = z; } double get(int axis) { return axis == 0 ? x : axis == 1 ? y : z; } void render(RenderBlocksCTM inst, SubSide side, int cacheID) { final Tessellator tessellator = Tessellator.instance; final ForgeDirection normal = side.normal; if (inst.enableAO) { tessellator.setColorOpaque_F(inst.redCache[cacheID], inst.grnCache[cacheID], inst.bluCache[cacheID]); tessellator.setBrightness(inst.lightingCache[cacheID]); } double u = cacheID == 1 || cacheID == 2 ? inst.maxU : inst.minU; double v = cacheID < 2 ? inst.maxV : inst.minV; if (inst.renderPart) { // only the part inside the bounds, showing the texture where it lies in the block double px = Math.min(Math.max(x, inst.renderMinX), inst.renderMaxX); double py = Math.min(Math.max(y, inst.renderMinY), inst.renderMaxY); double pz = Math.min(Math.max(z, inst.renderMinZ), inst.renderMaxZ); double uMoved = Math.abs(normal.offsetX != 0 ? pz - z : px - x); double vMoved = Math.abs(normal.offsetY != 0 ? pz - z : py - y); // a sub-side is half a block wide u += (u == inst.minU ? 2 : -2) * (inst.maxU - inst.minU) * uMoved; v += (v == inst.minV ? 2 : -2) * (inst.maxV - inst.minV) * vMoved; if (inst.enableAO) { // the caches hold the light at the sub-side's corners, the vertex moved, so take the light there double lightX = inst.boundsFraction(side.lightAxisX, px, py, pz); double lightY = inst.boundsFraction(side.lightAxisY, px, py, pz); inst.mixLight(side.lightRisingX ? lightX : 1 - lightX, side.lightRisingY ? lightY : 1 - lightY); } tessellator.addVertexWithUV(px, py, pz, u, v); return; } double uDiff = inst.maxU - inst.minU; double vDiff = inst.maxV - inst.minV; double xDiff; double yDiff; double zDiff; if (inst.renderMinX + inst.renderMinY + inst.renderMinZ != 0 || inst.renderMaxX + inst.renderMaxY + inst.renderMaxZ != 3) { boolean uMin = u == inst.minU; boolean vMin = v == inst.minV; xDiff = inst.renderMaxX - inst.renderMinX; yDiff = inst.renderMaxY - inst.renderMinY; zDiff = inst.renderMaxZ - inst.renderMinZ; if (normal.offsetY != 0) { uDiff *= uMin ? inst.renderMinX : 1 - inst.renderMaxX; vDiff *= vMin ? inst.renderMinZ : 1 - inst.renderMaxZ; } else if (normal.offsetX != 0) { uDiff *= uMin ? inst.renderMinZ : 1 - inst.renderMaxZ; vDiff *= vMin ? inst.renderMinY : 1 - inst.renderMaxY; } else if (normal.offsetZ != 0) { uDiff *= uMin ? inst.renderMinX : 1 - inst.renderMaxX; vDiff *= vMin ? inst.renderMinY : 1 - inst.renderMaxY; } u = u == inst.minU ? inst.minU + uDiff : inst.maxU - uDiff; v = v == inst.minV ? inst.minV + vDiff : inst.maxV - vDiff; } else { xDiff = yDiff = zDiff = 1; } tessellator.addVertexWithUV( inst.renderMinX + (x * xDiff), inst.renderMinY + (y * yDiff), inst.renderMinZ + (z * zDiff), u, v); } } /** * Each side is divided into 4 sub-sides. LB(left bottom), RB(right bottom), LT(right top), and RT(right top). * * Each sub-side contains 4 {@link Vert} objects representing its position on the block. */ protected enum SubSide { // @formatter:off XNEG_LB(ZERO, Z_HALF, YZ_HALF, Y_HALF), XNEG_RB(Z_HALF, Z, Y_HALF_Z, YZ_HALF), XNEG_RT(YZ_HALF, Y_HALF_Z, YZ, Z_HALF_Y), XNEG_LT(Y_HALF, YZ_HALF, Z_HALF_Y, Y), XPOS_LB(XZ, Z_HALF_X, YZ_HALF_X, Y_HALF_XZ), XPOS_RB(Z_HALF_X, X, Y_HALF_X, YZ_HALF_X), XPOS_RT(YZ_HALF_X, Y_HALF_X, XY, Z_HALF_XY), XPOS_LT(Y_HALF_XZ, YZ_HALF_X, Z_HALF_XY, XYZ), ZNEG_LB(X, X_HALF, XY_HALF, Y_HALF_X), ZNEG_RB(X_HALF, ZERO, Y_HALF, XY_HALF), ZNEG_RT(XY_HALF, Y_HALF, Y, X_HALF_Y), ZNEG_LT(Y_HALF_X, XY_HALF, X_HALF_Y, XY), ZPOS_LB(Z, X_HALF_Z, XY_HALF_Z, Y_HALF_Z), ZPOS_RB(X_HALF_Z, XZ, Y_HALF_XZ, XY_HALF_Z), ZPOS_RT(XY_HALF_Z, Y_HALF_XZ, XYZ, X_HALF_YZ), ZPOS_LT(Y_HALF_Z, XY_HALF_Z, X_HALF_YZ, YZ), YNEG_LB(ZERO, X_HALF, XZ_HALF, Z_HALF), YNEG_RB(X_HALF, X, Z_HALF_X, XZ_HALF), YNEG_RT(XZ_HALF, Z_HALF_X, XZ, X_HALF_Z), YNEG_LT(Z_HALF, XZ_HALF, X_HALF_Z, Z), YPOS_LB(YZ, X_HALF_YZ, XZ_HALF_Y, Z_HALF_Y), YPOS_RB(X_HALF_YZ, XYZ, Z_HALF_XY, XZ_HALF_Y), YPOS_RT(XZ_HALF_Y, Z_HALF_XY, XY, X_HALF_Y), YPOS_LT(Z_HALF_Y, XZ_HALF_Y, X_HALF_Y, Y); // @formatter:on private final Vert xmin, xmax, ymin, ymax; private final ForgeDirection normal; /** The block axes the light maps' x and y run along, and whether they run towards 1. */ private final int lightAxisX, lightAxisY; private final boolean lightRisingX, lightRisingY; SubSide(Vert xmin, Vert ymin, Vert ymax, Vert xmax) { this.xmin = xmin; this.ymin = ymin; this.ymax = ymax; this.xmax = xmax; this.normal = calcNormal(); // the caches step along the light maps' x from xmin to ymin, and along their y from xmin to xmax this.lightAxisX = axis(xmin, ymin); this.lightAxisY = axis(xmin, xmax); this.lightRisingX = ymin.get(lightAxisX) > xmin.get(lightAxisX); this.lightRisingY = xmax.get(lightAxisY) > xmin.get(lightAxisY); } private static int axis(Vert from, Vert to) { return from.x != to.x ? 0 : from.y != to.y ? 1 : 2; } private ForgeDirection calcNormal() { double xTot = xmin.x + xmax.x + ymin.x + ymax.x; double yTot = xmin.y + xmax.y + ymin.y + ymax.y; double zTot = xmin.z + xmax.z + ymin.z + ymax.z; if (xTot % 4 == 0) { return xTot > 0 ? ForgeDirection.EAST : ForgeDirection.WEST; } else if (yTot % 4 == 0) { return yTot > 0 ? ForgeDirection.UP : ForgeDirection.DOWN; } else if (zTot % 4 == 0) { return zTot > 0 ? ForgeDirection.SOUTH : ForgeDirection.NORTH; } return ForgeDirection.UNKNOWN; } void render(RenderBlocksCTM inst) { xmin.render(inst, this, 0); ymin.render(inst, this, 1); ymax.render(inst, this, 2); xmax.render(inst, this, 3); } } public CTM ctm = CTM.getInstance(); // globals added to save the JVM some trouble. No need to constantly create // and destroy ints if we don't have to protected int blockLightBitChannel = 0; protected int redBitChannel = 0; protected int greenBitChannel = 0; protected int blueBitChannel = 0; protected int sunlightBitChannel = 0; public RenderBlocksCTM() { super(); renderMaxX = renderMaxY = renderMaxZ = 1; } protected double minU, maxU; protected double minV, maxV; protected int[] lightingCache = new int[4]; protected float[] redCache = new float[4]; protected float[] grnCache = new float[4]; protected float[] bluCache = new float[4]; public TextureSubmap submap; public TextureSubmap submapSmall; public ISubmapManager manager; protected int[][] lightmap = new int[3][3]; protected float[][] redmap = new float[3][3]; protected float[][] grnmap = new float[3][3]; protected float[][] blumap = new float[3][3]; protected int bx, by, bz, meta; @Getter protected boolean inWorld = false; /** * Whether the render bounds are a part of the block, which shows its part of the block's texture instead of the * whole texture squeezed in. Set when rendering in an {@link IConnectionAccess}. */ protected boolean renderPart = false; @Override public boolean renderStandardBlock(Block block, int x, int y, int z) { bx = x; by = y; bz = z; meta = Minecraft.getMinecraft().theWorld.getBlockMetadata(x, y, z); final Tessellator tessellator = Tessellator.instance; tessellator.setColorOpaque_F(1.0F, 1.0F, 1.0F); tessellator.addTranslation(x, y, z); inWorld = true; renderPart = CTMLib.chiselLoaded() && blockAccess instanceof IConnectionAccess; boolean res = super.renderStandardBlock(block, x, y, z); renderPart = false; inWorld = false; tessellator.addTranslation(-x, -y, -z); return res; } /* @formatter:off * This method fills a 3x3 grid of light values based on the four corners, by averaging them together. * * 2 TL x TR * * 1 x x x * * 0 BL x BR * 0 1 2 * * Note: Variable names mean nothing... don't touch * * ._. * * shakes fist in anger */ // @formatter:on protected void fillLightmap(int bottomLeft, int bottomRight, int topRight, int topLeft) { lightmap[0][0] = bottomLeft; lightmap[2][0] = bottomRight; lightmap[2][2] = topRight; lightmap[0][2] = topLeft; lightmap[1][0] = avg(bottomLeft, bottomRight); lightmap[2][1] = avg(bottomRight, topRight); lightmap[1][2] = avg(topRight, topLeft); lightmap[0][1] = avg(topLeft, bottomLeft); lightmap[1][1] = avg(bottomLeft, bottomRight, topRight, topLeft); } protected void fillColormap(float bottomLeft, float bottomRight, float topRight, float topLeft, float[][] map) { map[0][0] = bottomLeft; map[2][0] = bottomRight; map[2][2] = topRight; map[0][2] = topLeft; map[1][0] = (bottomLeft + bottomRight) / 2.0F; map[2][1] = (bottomRight + topRight) / 2.0F; map[1][2] = (topRight + topLeft) / 2.0F; map[0][1] = (topLeft + bottomLeft) / 2.0F; map[1][1] = (bottomLeft + bottomRight + topRight + topLeft) / 4.0F; } protected void getLight(int x, int y) { lightingCache[0] = lightmap[0 + x][0 + y]; lightingCache[1] = lightmap[1 + x][0 + y]; lightingCache[2] = lightmap[1 + x][1 + y]; lightingCache[3] = lightmap[0 + x][1 + y]; redCache[0] = redmap[0 + x][0 + y]; redCache[1] = redmap[1 + x][0 + y]; redCache[2] = redmap[1 + x][1 + y]; redCache[3] = redmap[0 + x][1 + y]; grnCache[0] = grnmap[0 + x][0 + y]; grnCache[1] = grnmap[1 + x][0 + y]; grnCache[2] = grnmap[1 + x][1 + y]; grnCache[3] = grnmap[0 + x][1 + y]; bluCache[0] = blumap[0 + x][0 + y]; bluCache[1] = blumap[1 + x][0 + y]; bluCache[2] = blumap[1 + x][1 + y]; bluCache[3] = blumap[0 + x][1 + y]; } /** * This works around a bug in CLC atm */ private int avg(int... lightVals) { blockLightBitChannel = 0; redBitChannel = 0; greenBitChannel = 0; blueBitChannel = 0; sunlightBitChannel = 0; for (int light : lightVals) { blockLightBitChannel += (light & 0xFF); redBitChannel += (light & 0xF00); greenBitChannel += (light & 0xF000); blueBitChannel += (light & 0xF0000); sunlightBitChannel += (light & 0xF00000); } return ((blockLightBitChannel / lightVals.length) & 0xFF) | ((redBitChannel / lightVals.length) & 0xF00) | ((greenBitChannel / lightVals.length) & 0xF000) | ((blueBitChannel / lightVals.length) & 0xF0000) | ((sunlightBitChannel / lightVals.length) & 0xF00000); } /** * @return where the point lies between the render bounds along the axis, from 0 at their min to 1 at their max */ protected double boundsFraction(int axis, double x, double y, double z) { double min = axis == 0 ? renderMinX : axis == 1 ? renderMinY : renderMinZ; double max = axis == 0 ? renderMaxX : axis == 1 ? renderMaxY : renderMaxZ; double pos = axis == 0 ? x : axis == 1 ? y : z; return max > min ? (pos - min) / (max - min) : 0; } /** * Sets the color and brightness at a point of the face, mixed from the corners of the light maps, which are the * corners of the render bounds. The point is given in the maps' x and y, from 0 to 1. */ protected void mixLight(double x, double y) { double w00 = (1 - x) * (1 - y); double w20 = x * (1 - y); double w22 = x * y; double w02 = (1 - x) * y; final Tessellator tessellator = Tessellator.instance; tessellator.setColorOpaque_F( mixColor(redmap, w00, w20, w22, w02), mixColor(grnmap, w00, w20, w22, w02), mixColor(blumap, w00, w20, w22, w02)); // vanilla mixes the corners of partial bounds the same way tessellator.setBrightness( mixAoBrightness(lightmap[0][0], lightmap[2][0], lightmap[2][2], lightmap[0][2], w00, w20, w22, w02)); } private static float mixColor(float[][] map, double w00, double w20, double w22, double w02) { return (float) (map[0][0] * w00 + map[2][0] * w20 + map[2][2] * w22 + map[0][2] * w02); } protected void side(Block block, SubSide side, int iconIndex) { // If we have an overridden texture when we're supposed to render a CTM texture, // we'll treat it as a normal block texture but still render it the CTM way: quarter of a texture at a time. // This way has one benefit: if an overridden texture is a base block texture and a CTM texture (that is being // rendered next) is an overlay texture above the base, we're avoiding any z-fighting issues because we // create identical vertices and now only order of rendering dictates which texture will be rendered above if (hasOverrideBlockTexture()) { IIcon icon = overrideBlockTexture; double u0 = icon.getMinU(); double u1 = icon.getMaxU(); double v0 = icon.getMinV(); double v1 = icon.getMaxV(); double uMid = (u0 + u1) / 2; double vMid = (v0 + v1) / 2; // Map all LB-style subsides to bottom-left quarter, RB to bottom-right, RT to top-right, LT to top-left switch (side) { // bottom-left quarter case XNEG_LB, XPOS_LB, YNEG_LB, YPOS_LB, ZNEG_LB, ZPOS_LB -> { minU = u0; maxU = uMid; minV = vMid; maxV = v1; } // bottom-right quarter case XNEG_RB, XPOS_RB, YNEG_RB, YPOS_RB, ZNEG_RB, ZPOS_RB -> { minU = uMid; maxU = u1; minV = vMid; maxV = v1; } // top-right quarter case XNEG_RT, XPOS_RT, YNEG_RT, YPOS_RT, ZNEG_RT, ZPOS_RT -> { minU = uMid; maxU = u1; minV = v0; maxV = vMid; } // top-left quarter case XNEG_LT, XPOS_LT, YNEG_LT, YPOS_LT, ZNEG_LT, ZPOS_LT -> { minU = u0; maxU = uMid; minV = v0; maxV = vMid; } } side.render(this); return; } IIcon icon; TextureSubmap map; if (iconIndex >= 16) { iconIndex -= 16; map = submapSmall; } else { map = submap; } if (map == null) { icon = getBlockIconFromSideAndMetadata(block, side.normal.ordinal(), meta); } else { int x = iconIndex % map.getWidth(); int y = iconIndex / map.getHeight(); icon = map.getSubIcon(x, y); } minU = icon.getMinU(); maxU = icon.getMaxU(); minV = icon.getMinV(); maxV = icon.getMaxV(); side.render(this); } @Override public void renderFaceXNeg(Block block, double x, double y, double z, IIcon icon) { pre(ForgeDirection.WEST); if (!inWorld || submap == null) { super.renderFaceXNeg(block, 0, 0, 0, icon); } else { int[] tex = ctm.getSubmapIndices(blockAccess, bx, by, bz, 4); fillLightmap(brightnessBottomRight, brightnessTopRight, brightnessTopLeft, brightnessBottomLeft); fillColormap(colorRedBottomRight, colorRedTopRight, colorRedTopLeft, colorRedBottomLeft, redmap); fillColormap(colorGreenBottomRight, colorGreenTopRight, colorGreenTopLeft, colorGreenBottomLeft, grnmap); fillColormap(colorBlueBottomRight, colorBlueTopRight, colorBlueTopLeft, colorBlueBottomLeft, blumap); getLight(0, 0); side(block, XNEG_LB, tex[0]); getLight(1, 0); side(block, XNEG_RB, tex[1]); getLight(1, 1); side(block, XNEG_RT, tex[2]); getLight(0, 1); side(block, XNEG_LT, tex[3]); } post(ForgeDirection.WEST); } @Override public void renderFaceXPos(Block block, double x, double y, double z, IIcon icon) { pre(ForgeDirection.EAST); if (!inWorld || submap == null) { super.renderFaceXPos(block, 0, 0, 0, icon); } else { int[] tex = ctm.getSubmapIndices(blockAccess, bx, by, bz, 5); fillLightmap(brightnessTopLeft, brightnessBottomLeft, brightnessBottomRight, brightnessTopRight); fillColormap(colorRedTopLeft, colorRedBottomLeft, colorRedBottomRight, colorRedTopRight, redmap); fillColormap(colorGreenTopLeft, colorGreenBottomLeft, colorGreenBottomRight, colorGreenTopRight, grnmap); fillColormap(colorBlueTopLeft, colorBlueBottomLeft, colorBlueBottomRight, colorBlueTopRight, blumap); getLight(0, 0); side(block, XPOS_LB, tex[0]); getLight(1, 0); side(block, XPOS_RB, tex[1]); getLight(1, 1); side(block, XPOS_RT, tex[2]); getLight(0, 1); side(block, XPOS_LT, tex[3]); } post(ForgeDirection.EAST); } @Override public void renderFaceZNeg(Block block, double x, double y, double z, IIcon icon) { pre(ForgeDirection.NORTH); if (!inWorld || submap == null) { super.renderFaceZNeg(block, 0, 0, 0, icon); } else { int[] tex = ctm.getSubmapIndices(blockAccess, bx, by, bz, 2); fillLightmap(brightnessBottomRight, brightnessTopRight, brightnessTopLeft, brightnessBottomLeft); fillColormap(colorRedBottomRight, colorRedTopRight, colorRedTopLeft, colorRedBottomLeft, redmap); fillColormap(colorGreenBottomRight, colorGreenTopRight, colorGreenTopLeft, colorGreenBottomLeft, grnmap); fillColormap(colorBlueBottomRight, colorBlueTopRight, colorBlueTopLeft, colorBlueBottomLeft, blumap); getLight(0, 0); side(block, ZNEG_LB, tex[0]); getLight(1, 0); side(block, ZNEG_RB, tex[1]); getLight(1, 1); side(block, ZNEG_RT, tex[2]); getLight(0, 1); side(block, ZNEG_LT, tex[3]); } post(ForgeDirection.NORTH); } @Override public void renderFaceZPos(Block block, double x, double y, double z, IIcon icon) { pre(ForgeDirection.SOUTH); if (!inWorld || submap == null) { super.renderFaceZPos(block, 0, 0, 0, icon); } else { int[] tex = ctm.getSubmapIndices(blockAccess, bx, by, bz, 3); fillLightmap(brightnessBottomLeft, brightnessBottomRight, brightnessTopRight, brightnessTopLeft); fillColormap(colorRedBottomLeft, colorRedBottomRight, colorRedTopRight, colorRedTopLeft, redmap); fillColormap(colorGreenBottomLeft, colorGreenBottomRight, colorGreenTopRight, colorGreenTopLeft, grnmap); fillColormap(colorBlueBottomLeft, colorBlueBottomRight, colorBlueTopRight, colorBlueTopLeft, blumap); getLight(0, 0); side(block, ZPOS_LB, tex[0]); getLight(1, 0); side(block, ZPOS_RB, tex[1]); getLight(1, 1); side(block, ZPOS_RT, tex[2]); getLight(0, 1); side(block, ZPOS_LT, tex[3]); } post(ForgeDirection.SOUTH); } @Override public void renderFaceYNeg(Block block, double x, double y, double z, IIcon icon) { pre(ForgeDirection.DOWN); if (!inWorld || submap == null) { super.renderFaceYNeg(block, 0, 0, 0, icon); } else { int[] tex = ctm.getSubmapIndices(blockAccess, bx, by, bz, 0); fillLightmap(brightnessBottomLeft, brightnessBottomRight, brightnessTopRight, brightnessTopLeft); fillColormap(colorRedBottomLeft, colorRedBottomRight, colorRedTopRight, colorRedTopLeft, redmap); fillColormap(colorGreenBottomLeft, colorGreenBottomRight, colorGreenTopRight, colorGreenTopLeft, grnmap); fillColormap(colorBlueBottomLeft, colorBlueBottomRight, colorBlueTopRight, colorBlueTopLeft, blumap); getLight(0, 0); side(block, YNEG_LB, tex[0]); getLight(1, 0); side(block, YNEG_RB, tex[1]); getLight(1, 1); side(block, YNEG_RT, tex[2]); getLight(0, 1); side(block, YNEG_LT, tex[3]); } post(ForgeDirection.DOWN); } @Override public void renderFaceYPos(Block block, double x, double y, double z, IIcon icon) { pre(ForgeDirection.UP); if (!inWorld || submap == null) { super.renderFaceYPos(block, 0, 0, 0, icon); } else { int[] tex = ctm.getSubmapIndices(blockAccess, bx, by, bz, 1); fillLightmap(brightnessTopRight, brightnessTopLeft, brightnessBottomLeft, brightnessBottomRight); fillColormap(colorRedTopRight, colorRedTopLeft, colorRedBottomLeft, colorRedBottomRight, redmap); fillColormap(colorGreenTopRight, colorGreenTopLeft, colorGreenBottomLeft, colorGreenBottomRight, grnmap); fillColormap(colorBlueTopRight, colorBlueTopLeft, colorBlueBottomLeft, colorBlueBottomRight, blumap); getLight(0, 0); side(block, YPOS_LB, tex[0]); getLight(1, 0); side(block, YPOS_RB, tex[1]); getLight(1, 1); side(block, YPOS_RT, tex[2]); getLight(0, 1); side(block, YPOS_LT, tex[3]); } post(ForgeDirection.UP); } protected void pre(ForgeDirection face) { manager.preRenderSide(this, blockAccess, bx, by, bz, face); } protected void post(ForgeDirection face) { manager.postRenderSide(this, blockAccess, bx, by, bz, face); } }