2024-02-19 11:13:25 -08:00
|
|
|
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;
|
|
|
|
|
|
2026-11-09 12:37:43 +01:00
|
|
|
import com.cricketcraft.chisel.api.IConnectionAccess;
|
|
|
|
|
|
2024-02-19 11:13:25 -08:00
|
|
|
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
|
|
|
|
|
|
2026-04-20 11:45:05 +02:00
|
|
|
private final double x, y, z;
|
2024-02-19 11:13:25 -08:00
|
|
|
|
2026-04-20 11:45:05 +02:00
|
|
|
Vert(double x, double y, double z) {
|
2024-02-19 11:13:25 -08:00
|
|
|
this.x = x;
|
|
|
|
|
this.y = y;
|
|
|
|
|
this.z = z;
|
|
|
|
|
}
|
|
|
|
|
|
2026-11-09 12:37:43 +01:00
|
|
|
double get(int axis) {
|
|
|
|
|
return axis == 0 ? x : axis == 1 ? y : z;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void render(RenderBlocksCTM inst, SubSide side, int cacheID) {
|
2024-02-19 11:43:31 -08:00
|
|
|
final Tessellator tessellator = Tessellator.instance;
|
2026-11-09 12:37:43 +01:00
|
|
|
final ForgeDirection normal = side.normal;
|
2024-02-19 11:43:31 -08:00
|
|
|
|
2024-02-19 11:13:25 -08:00
|
|
|
if (inst.enableAO) {
|
2024-02-19 11:43:31 -08:00
|
|
|
tessellator.setColorOpaque_F(inst.redCache[cacheID], inst.grnCache[cacheID], inst.bluCache[cacheID]);
|
|
|
|
|
tessellator.setBrightness(inst.lightingCache[cacheID]);
|
2024-02-19 11:13:25 -08:00
|
|
|
}
|
|
|
|
|
|
2026-04-20 11:45:05 +02:00
|
|
|
double u = cacheID == 1 || cacheID == 2 ? inst.maxU : inst.minU;
|
|
|
|
|
double v = cacheID < 2 ? inst.maxV : inst.minV;
|
2024-02-19 11:13:25 -08:00
|
|
|
|
2026-11-09 12:37:43 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-20 11:45:05 +02:00
|
|
|
double uDiff = inst.maxU - inst.minU;
|
|
|
|
|
double vDiff = inst.maxV - inst.minV;
|
2024-02-19 11:13:25 -08:00
|
|
|
|
2026-04-20 11:45:05 +02:00
|
|
|
double xDiff;
|
|
|
|
|
double yDiff;
|
|
|
|
|
double zDiff;
|
2024-02-19 11:13:25 -08:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-19 11:43:31 -08:00
|
|
|
tessellator.addVertexWithUV(
|
2024-02-19 11:13:25 -08:00
|
|
|
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
|
2026-04-20 11:45:05 +02:00
|
|
|
private final Vert xmin, xmax, ymin, ymax;
|
|
|
|
|
private final ForgeDirection normal;
|
2026-11-09 12:37:43 +01:00
|
|
|
/** 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;
|
2024-02-19 11:13:25 -08:00
|
|
|
|
|
|
|
|
SubSide(Vert xmin, Vert ymin, Vert ymax, Vert xmax) {
|
|
|
|
|
this.xmin = xmin;
|
|
|
|
|
this.ymin = ymin;
|
|
|
|
|
this.ymax = ymax;
|
|
|
|
|
this.xmax = xmax;
|
|
|
|
|
this.normal = calcNormal();
|
2026-11-09 12:37:43 +01:00
|
|
|
// 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;
|
2024-02-19 11:13:25 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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) {
|
2026-11-09 12:37:43 +01:00
|
|
|
xmin.render(inst, this, 0);
|
|
|
|
|
ymin.render(inst, this, 1);
|
|
|
|
|
ymax.render(inst, this, 2);
|
|
|
|
|
xmax.render(inst, this, 3);
|
2024-02-19 11:13:25 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2026-11-09 12:37:43 +01:00
|
|
|
/**
|
|
|
|
|
* 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;
|
|
|
|
|
|
2024-02-19 11:13:25 -08:00
|
|
|
@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);
|
2024-02-19 11:43:31 -08:00
|
|
|
final Tessellator tessellator = Tessellator.instance;
|
2024-02-19 11:13:25 -08:00
|
|
|
|
2024-02-19 11:43:31 -08:00
|
|
|
tessellator.setColorOpaque_F(1.0F, 1.0F, 1.0F);
|
|
|
|
|
tessellator.addTranslation(x, y, z);
|
2024-02-19 11:13:25 -08:00
|
|
|
inWorld = true;
|
2026-11-09 12:37:43 +01:00
|
|
|
renderPart = CTMLib.chiselLoaded() && blockAccess instanceof IConnectionAccess;
|
2024-02-19 11:13:25 -08:00
|
|
|
boolean res = super.renderStandardBlock(block, x, y, z);
|
2026-11-09 12:37:43 +01:00
|
|
|
renderPart = false;
|
2024-02-19 11:13:25 -08:00
|
|
|
inWorld = false;
|
2024-02-19 11:43:31 -08:00
|
|
|
tessellator.addTranslation(-x, -y, -z);
|
2024-02-19 11:13:25 -08:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2026-11-09 12:37:43 +01:00
|
|
|
/**
|
|
|
|
|
* @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);
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-19 11:13:25 -08:00
|
|
|
protected void side(Block block, SubSide side, int iconIndex) {
|
|
|
|
|
|
2025-12-30 02:37:43 +03:00
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-19 11:13:25 -08:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-30 02:37:43 +03:00
|
|
|
minU = icon.getMinU();
|
|
|
|
|
maxU = icon.getMaxU();
|
|
|
|
|
minV = icon.getMinV();
|
|
|
|
|
maxV = icon.getMaxV();
|
2024-02-19 11:13:25 -08:00
|
|
|
|
|
|
|
|
side.render(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void renderFaceXNeg(Block block, double x, double y, double z, IIcon icon) {
|
|
|
|
|
pre(ForgeDirection.WEST);
|
2025-12-30 02:37:43 +03:00
|
|
|
if (!inWorld || submap == null) {
|
2024-02-19 11:13:25 -08:00
|
|
|
super.renderFaceXNeg(block, 0, 0, 0, icon);
|
|
|
|
|
} else {
|
2026-04-20 11:45:05 +02:00
|
|
|
int[] tex = ctm.getSubmapIndices(blockAccess, bx, by, bz, 4);
|
2024-02-19 11:13:25 -08:00
|
|
|
|
|
|
|
|
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);
|
2025-12-30 02:37:43 +03:00
|
|
|
if (!inWorld || submap == null) {
|
2024-02-19 11:13:25 -08:00
|
|
|
super.renderFaceXPos(block, 0, 0, 0, icon);
|
|
|
|
|
} else {
|
2026-04-20 11:45:05 +02:00
|
|
|
int[] tex = ctm.getSubmapIndices(blockAccess, bx, by, bz, 5);
|
2024-02-19 11:13:25 -08:00
|
|
|
|
|
|
|
|
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);
|
2025-12-30 02:37:43 +03:00
|
|
|
if (!inWorld || submap == null) {
|
2024-02-19 11:13:25 -08:00
|
|
|
super.renderFaceZNeg(block, 0, 0, 0, icon);
|
|
|
|
|
} else {
|
2026-04-20 11:45:05 +02:00
|
|
|
int[] tex = ctm.getSubmapIndices(blockAccess, bx, by, bz, 2);
|
2024-02-19 11:13:25 -08:00
|
|
|
|
|
|
|
|
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);
|
2025-12-30 02:37:43 +03:00
|
|
|
if (!inWorld || submap == null) {
|
2024-02-19 11:13:25 -08:00
|
|
|
super.renderFaceZPos(block, 0, 0, 0, icon);
|
|
|
|
|
} else {
|
2026-04-20 11:45:05 +02:00
|
|
|
int[] tex = ctm.getSubmapIndices(blockAccess, bx, by, bz, 3);
|
2024-02-19 11:13:25 -08:00
|
|
|
|
|
|
|
|
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);
|
2025-12-30 02:37:43 +03:00
|
|
|
if (!inWorld || submap == null) {
|
2024-02-19 11:13:25 -08:00
|
|
|
super.renderFaceYNeg(block, 0, 0, 0, icon);
|
|
|
|
|
} else {
|
2026-04-20 11:45:05 +02:00
|
|
|
int[] tex = ctm.getSubmapIndices(blockAccess, bx, by, bz, 0);
|
2024-02-19 11:13:25 -08:00
|
|
|
|
|
|
|
|
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);
|
2025-12-30 02:37:43 +03:00
|
|
|
if (!inWorld || submap == null) {
|
2024-02-19 11:13:25 -08:00
|
|
|
super.renderFaceYPos(block, 0, 0, 0, icon);
|
|
|
|
|
} else {
|
2026-04-20 11:45:05 +02:00
|
|
|
int[] tex = ctm.getSubmapIndices(blockAccess, bx, by, bz, 1);
|
2024-02-19 11:13:25 -08:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|