Files
Chisel/src/main/java/team/chisel/ctmlib/RenderBlocksCTM.java
T
DarkShadow44andboubou19 a431e2a65c Little Tiles support (#107)
* Refactor: Extract "matches" as method and use early exit

* Extend API to allow Little Tiles support

* Render little tiles with partial textures

* spotless

* Fix LT AO

* Ask for the right block

---------

Co-authored-by: boubou19 <miisterunknown@gmail.com>
2026-11-09 11:37:43 +00:00

679 lines
28 KiB
Java

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);
}
}