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>
This commit is contained in:
DarkShadow44andboubou19 authored and GitHub committed 2026-11-09 11:37:43 +00:00
1 parent aa803a1e51
commit a431e2a65c
7 files changed
+280 -57

No files matched your search

@@ -0,0 +1,34 @@
package com.cricketcraft.chisel.api;
import net.minecraft.block.Block;
import net.minecraft.world.IBlockAccess;
/**
* To be implemented on an {@link IBlockAccess} that renders blocks which are only part of a block space, so it can
* decide connected textures itself instead of reporting the blocks around it.
* <p>
* Connected textures ask this for every location they check, while rendering in this world. Render bounds smaller than
* the block are then a part of the block: they show the matching part of the block's connected texture, instead of the
* whole texture squeezed into the bounds.
*/
public interface IConnectionAccess {
/**
* Whether the given location shows the block being rendered, as seen from the block being rendered.
*
* @param x X coord of the block to check against. NOT the position of the block being rendered.
* @param y Y coord of the block to check against.
* @param z Z coord of the block to check against.
* @param side The side being rendered, NOT the side being connected from.
* <p>
* This value can be -1 if no side is specified. Please handle this appropriately.
* @param fromX X coord of the block being rendered. It is not necessarily adjacent, it can also be diagonal to the
* location.
* @param fromY Y coord of the block being rendered.
* @param fromZ Z coord of the block being rendered.
* @param block The block being rendered.
* @param meta The metadata of the block being rendered.
* @return True if the location shows the given block and metadata.
*/
boolean matches(int x, int y, int z, int side, int fromX, int fromY, int fromZ, Block block, int meta);
}
@@ -36,4 +36,57 @@ public interface IFacade {
* @return The metadata of your facade block.
*/
int getFacadeMetadata(IBlockAccess world, int x, int y, int z, int side);
/**
* Gets the block this facade is acting as towards the block that is checking for a connection. Implement this if
* the facade can show different blocks to different neighbours.
* <p>
* Defaults to {@link #getFacade(IBlockAccess, int, int, int, int)}.
*
* @param world {@link World}
* @param x X coord of your block
* @param y Y coord of your block
* @param z Z coord of your block
* @param side The side being rendered, NOT the side being connected from.
* <p>
* This value can be -1 if no side is specified. Please handle this appropriately.
* @param fromX X coord of the block being rendered, which is checking for a connection. NOT the position of
* your block. It is not necessarily adjacent, it can also be diagonal to your block.
* @param fromY Y coord of the block being rendered.
* @param fromZ Z coord of the block being rendered.
* @param fromBlock The block being rendered.
* @param fromMeta The metadata of the block being rendered.
* @return The block inside of your facade block, as seen from the block being rendered.
*/
default Block getFacade(IBlockAccess world, int x, int y, int z, int side, int fromX, int fromY, int fromZ,
Block fromBlock, int fromMeta) {
return getFacade(world, x, y, z, side);
}
/**
* Gets the metadata of the block that this facade is acting as towards the block that is checking for a
* connection. Implement this together with
* {@link #getFacade(IBlockAccess, int, int, int, int, int, int, int, Block, int)}.
* <p>
* Defaults to {@link #getFacadeMetadata(IBlockAccess, int, int, int, int)}.
*
* @param world {@link World}
* @param x X coord of your block
* @param y Y coord of your block
* @param z Z coord of your block
* @param side The side being rendered, NOT the side being connected from.
* <p>
* This value can be -1 if no side is specified. Please handle this appropriately.
* @param fromX X coord of the block being rendered, which is checking for a connection. NOT the position of
* your block. It is not necessarily adjacent, it can also be diagonal to your block.
* @param fromY Y coord of the block being rendered.
* @param fromZ Z coord of the block being rendered.
* @param fromBlock The block being rendered.
* @param fromMeta The metadata of the block being rendered.
* @return The metadata of your facade block, as seen from the block being rendered.
*/
default int getFacadeMetadata(IBlockAccess world, int x, int y, int z, int side, int fromX, int fromY, int fromZ,
Block fromBlock, int fromMeta) {
return getFacadeMetadata(world, x, y, z, side);
}
}
@@ -90,8 +90,8 @@ public enum TextureType {
return data.getRight();
Block block = world.getBlock(x, y, z);
boolean topConnected = ctm.isConnected(world, x, y + 1, z, side, block, meta);
boolean botConnected = ctm.isConnected(world, x, y - 1, z, side, block, meta);
boolean topConnected = ctm.isConnected(world, x, y + 1, z, side, x, y, z, block, meta);
boolean botConnected = ctm.isConnected(world, x, y - 1, z, side, x, y, z, block, meta);
TextureSubmap map = data.getLeft();
if (topConnected && botConnected)
@@ -142,11 +142,11 @@ public enum TextureType {
boolean reverse = side == 3 || side == 4;
if (side < 4) {
p = ctm.isConnected(world, x - 1, y, z, side, block, meta);
n = ctm.isConnected(world, x + 1, y, z, side, block, meta);
p = ctm.isConnected(world, x - 1, y, z, side, x, y, z, block, meta);
n = ctm.isConnected(world, x + 1, y, z, side, x, y, z, block, meta);
} else {
p = ctm.isConnected(world, x, y, z - 1, side, block, meta);
n = ctm.isConnected(world, x, y, z + 1, side, block, meta);
p = ctm.isConnected(world, x, y, z - 1, side, x, y, z, block, meta);
n = ctm.isConnected(world, x, y, z + 1, side, x, y, z, block, meta);
}
TextureSubmap map = data.getLeft();
+78 -24
View File
@@ -16,6 +16,7 @@ import net.minecraft.world.IBlockAccess;
import net.minecraftforge.common.util.ForgeDirection;
import com.cricketcraft.chisel.api.IConnectable;
import com.cricketcraft.chisel.api.IConnectionAccess;
import com.cricketcraft.chisel.api.IFacade;
import com.google.common.base.Optional;
import com.google.common.collect.Maps;
@@ -191,24 +192,30 @@ public class CTM {
}
/**
* A simple check for if the given block can connect to the given direction on the given side.
* A check for if the block being rendered can connect to the given location on the given side. Passes the block
* being rendered on to {@link IFacade} blocks, so they can decide per neighbour.
*
* @param world
* @param x The x coordinate of the block to check <i>against</i>. This is not the position of your block.
* @param y The y coordinate of the block to check <i>against</i>. This is not the position of your block.
* @param z The z coordinate of the block to check <i>against</i>. This is not the position of your block.
* @param side The side of the block to check for connection status. This is <i>not</i> the direction to check in.
* @param block The block to check against for connection.
* @param meta The metadata to check against for connection.
* @param fromX The x coordinate of your block, the block being rendered.
* @param fromY The y coordinate of your block, the block being rendered.
* @param fromZ The z coordinate of your block, the block being rendered.
* @param block The block being rendered.
* @param meta The metadata of the block being rendered.
* @return True if the given block can connect to the given location on the given side.
*/
public boolean isConnected(IBlockAccess world, int x, int y, int z, int side, Block block, int meta) {
public boolean isConnected(IBlockAccess world, int x, int y, int z, int side, int fromX, int fromY, int fromZ,
Block block, int meta) {
ForgeDirection dir = ForgeDirection.VALID_DIRECTIONS[side];
return isConnected(world, x, y, z, dir, block, meta);
return isConnected(world, x, y, z, dir, fromX, fromY, fromZ, block, meta);
}
/**
* A simple check for if the given block can connect to the given direction on the given side.
* A check for if the block being rendered can connect to the given location on the given side. Passes the block
* being rendered on to {@link IFacade} blocks, so they can decide per neighbour.
*
* @param world
* @param x The x coordinate of the block to check <i>against</i>. This is not the position of your block.
@@ -216,41 +223,72 @@ public class CTM {
* @param z The z coordinate of the block to check <i>against</i>. This is not the position of your block.
* @param dir The {@link ForgeDirection side} of the block to check for connection status. This is <i>not</i> the
* direction to check in.
* @param block The block to check against for connection.
* @param meta The metadata to check against for connection.
* @param fromX The x coordinate of your block, the block being rendered.
* @param fromY The y coordinate of your block, the block being rendered.
* @param fromZ The z coordinate of your block, the block being rendered.
* @param block The block being rendered.
* @param meta The metadata of the block being rendered.
* @return True if the given block can connect to the given location on the given side.
*/
public boolean isConnected(IBlockAccess world, int x, int y, int z, ForgeDirection dir, Block block, int meta) {
public boolean isConnected(IBlockAccess world, int x, int y, int z, ForgeDirection dir, int fromX, int fromY,
int fromZ, Block block, int meta) {
if (CTMLib.chiselLoaded() && connectionBlocked(world, x, y, z, dir.ordinal())) {
return false;
}
if (!matches(world, x, y, z, dir.ordinal(), fromX, fromY, fromZ, block, meta)) {
return false;
}
if (disableObscuredFaceCheck.or(disableObscuredFaceCheckConfig)) {
return true;
}
// check that we aren't already connected outwards from this side
int x2 = x + dir.offsetX;
int y2 = y + dir.offsetY;
int z2 = z + dir.offsetZ;
return !matches(world, x2, y2, z2, dir.ordinal(), fromX, fromY, fromZ, block, meta);
}
boolean disableObscured = disableObscuredFaceCheck.or(disableObscuredFaceCheckConfig);
/**
* Whether the given location shows the block being rendered, as seen from the block being rendered. Asks the world
* if it is an {@link IConnectionAccess}, otherwise compares the block or {@link IFacade} at that location.
*
* @param world
* @param x The x coordinate of the block to check <i>against</i>. This is not the position of your block.
* @param y The y coordinate of the block to check <i>against</i>. This is not the position of your block.
* @param z The z coordinate of the block to check <i>against</i>. This is not the position of your block.
* @param side The side being rendered. -1 for unknown.
* @param fromX The x coordinate of your block, the block being rendered.
* @param fromY The y coordinate of your block, the block being rendered.
* @param fromZ The z coordinate of your block, the block being rendered.
* @param block The block being rendered.
* @param meta The metadata of the block being rendered.
* @return True if the given location shows the given block and metadata.
*/
public boolean matches(IBlockAccess world, int x, int y, int z, int side, int fromX, int fromY, int fromZ,
Block block, int meta) {
if (CTMLib.chiselLoaded()) {
return matchesAccess(world, x, y, z, side, fromX, fromY, fromZ, block, meta);
}
return block.equals(world.getBlock(x, y, z)) && world.getBlockMetadata(x, y, z) == meta;
}
Block con = getBlockOrFacade(world, x, y, z, dir.ordinal());
Block obscuring = disableObscured ? null : getBlockOrFacade(world, x2, y2, z2, dir.ordinal());
private boolean matchesAccess(IBlockAccess world, int x, int y, int z, int side, int fromX, int fromY, int fromZ,
Block block, int meta) {
if (world instanceof IConnectionAccess) {
return ((IConnectionAccess) world).matches(x, y, z, side, fromX, fromY, fromZ, block, meta);
}
Block blk = world.getBlock(x, y, z);
Block con = getFacade(blk, world, x, y, z, side, fromX, fromY, fromZ, block, meta);
// no block or a bad API user
if (con == null) {
return false;
}
boolean ret = con.equals(block) && getBlockOrFacadeMetadata(world, x, y, z, dir.ordinal()) == meta;
// no block obscuring this face
if (obscuring == null) {
return ret;
}
// check that we aren't already connected outwards from this side
ret &= !(obscuring.equals(block) && getBlockOrFacadeMetadata(world, x2, y2, z2, dir.ordinal()) == meta);
return ret;
return con.equals(block) && getFacadeMeta(blk, world, x, y, z, side, fromX, fromY, fromZ, block, meta) == meta;
}
private boolean connectionBlocked(IBlockAccess world, int x, int y, int z, int side) {
@@ -310,4 +348,20 @@ public class CTM {
}
return blk;
}
private int getFacadeMeta(Block blk, IBlockAccess world, int x, int y, int z, int side, int fromX, int fromY,
int fromZ, Block fromBlock, int fromMeta) {
if (blk instanceof IFacade) {
return ((IFacade) blk).getFacadeMetadata(world, x, y, z, side, fromX, fromY, fromZ, fromBlock, fromMeta);
}
return world.getBlockMetadata(x, y, z);
}
private Block getFacade(Block blk, IBlockAccess world, int x, int y, int z, int side, int fromX, int fromY,
int fromZ, Block fromBlock, int fromMeta) {
if (blk instanceof IFacade) {
blk = ((IFacade) blk).getFacade(world, x, y, z, side, fromX, fromY, fromZ, fromBlock, fromMeta);
}
return blk;
}
}
+4 -1
View File
@@ -61,12 +61,15 @@ public enum Dir {
public boolean isConnected(CTM inst, IBlockAccess world, int x, int y, int z, int sideIdx, Block block, int meta) {
ForgeDirection side = getOrientation(sideIdx);
ForgeDirection[] dirs = getNormalizedDirs(side);
int fromX = x;
int fromY = y;
int fromZ = z;
for (ForgeDirection dir : dirs) {
x += dir.offsetX;
y += dir.offsetY;
z += dir.offsetZ;
}
return inst.isConnected(world, x, y, z, side, block, meta);
return inst.isConnected(world, x, y, z, side, fromX, fromY, fromZ, block, meta);
}
private ForgeDirection[] getNormalizedDirs(ForgeDirection normal) {
@@ -58,6 +58,8 @@ 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 {
@@ -110,8 +112,13 @@ public class RenderBlocksCTM extends RenderBlocks {
this.z = z;
}
void render(RenderBlocksCTM inst, ForgeDirection normal, int cacheID) {
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]);
@@ -121,6 +128,26 @@ public class RenderBlocksCTM extends RenderBlocks {
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;
@@ -181,6 +208,9 @@ public class RenderBlocksCTM extends RenderBlocks {
// @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;
@@ -188,6 +218,15 @@ public class RenderBlocksCTM extends RenderBlocks {
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() {
@@ -205,10 +244,10 @@ public class RenderBlocksCTM extends RenderBlocks {
}
void render(RenderBlocksCTM inst) {
xmin.render(inst, normal, 0);
ymin.render(inst, normal, 1);
ymax.render(inst, normal, 2);
xmax.render(inst, normal, 3);
xmin.render(inst, this, 0);
ymin.render(inst, this, 1);
ymax.render(inst, this, 2);
xmax.render(inst, this, 3);
}
}
@@ -247,6 +286,12 @@ public class RenderBlocksCTM extends RenderBlocks {
@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;
@@ -258,7 +303,9 @@ public class RenderBlocksCTM extends RenderBlocks {
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);
@@ -356,6 +403,39 @@ public class RenderBlocksCTM extends RenderBlocks {
| ((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,
@@ -23,9 +23,8 @@ public class RenderBlocksColumn extends RenderBlocks {
super();
}
boolean connected(IBlockAccess world, int x, int y, int z, Block block, int meta) {
Block inWorld = ctm.getBlockOrFacade(world, x, y, z, -1);
return inWorld != null && inWorld.equals(block) && ctm.getBlockOrFacadeMetadata(world, x, y, z, -1) == meta;
boolean connected(IBlockAccess world, int x, int y, int z, int fromX, int fromY, int fromZ, Block block, int meta) {
return ctm.matches(world, x, y, z, -1, fromX, fromY, fromZ, block, meta);
}
@Override
@@ -33,8 +32,8 @@ public class RenderBlocksColumn extends RenderBlocks {
int metadata = blockAccess.getBlockMetadata(x, y, z);
inWorld = true;
boolean yp = connected(blockAccess, x, y + 1, z, block, metadata);
boolean yn = connected(blockAccess, x, y - 1, z, block, metadata);
boolean yp = connected(blockAccess, x, y + 1, z, x, y, z, block, metadata);
boolean yn = connected(blockAccess, x, y - 1, z, x, y, z, block, metadata);
if (yp || yn) {
sides[0] = iconTop;
@@ -46,13 +45,13 @@ public class RenderBlocksColumn extends RenderBlocks {
sides[3] = sides[4] = sides[5] = sides[2];
} else {
boolean xp = connected(blockAccess, x + 1, y, z, block, metadata);
boolean xn = connected(blockAccess, x - 1, y, z, block, metadata);
boolean xp = connected(blockAccess, x + 1, y, z, x, y, z, block, metadata);
boolean xn = connected(blockAccess, x - 1, y, z, x, y, z, block, metadata);
if (xp && (connected(blockAccess, x + 1, y + 1, z, block, metadata)
|| connected(blockAccess, x + 1, y - 1, z, block, metadata))) xp = false;
if (xn && (connected(blockAccess, x - 1, y + 1, z, block, metadata)
|| connected(blockAccess, x - 1, y - 1, z, block, metadata))) xn = false;
if (xp && (connected(blockAccess, x + 1, y + 1, z, x + 1, y, z, block, metadata)
|| connected(blockAccess, x + 1, y - 1, z, x + 1, y, z, block, metadata))) xp = false;
if (xn && (connected(blockAccess, x - 1, y + 1, z, x - 1, y, z, block, metadata)
|| connected(blockAccess, x - 1, y - 1, z, x - 1, y, z, block, metadata))) xn = false;
if (xp || xn) {
uvRotateEast = 2;
@@ -76,17 +75,17 @@ public class RenderBlocksColumn extends RenderBlocks {
}
sides[2] = sides[3] = sides[1];
} else {
boolean zp = connected(blockAccess, x, y, z + 1, block, metadata);
boolean zn = connected(blockAccess, x, y, z - 1, block, metadata);
boolean zp = connected(blockAccess, x, y, z + 1, x, y, z, block, metadata);
boolean zn = connected(blockAccess, x, y, z - 1, x, y, z, block, metadata);
if (zp && (connected(blockAccess, x, y + 1, z + 1, block, metadata)
|| connected(blockAccess, x, y - 1, z + 1, block, metadata))) zp = false;
if (zp && (connected(blockAccess, x + 1, y, z + 1, block, metadata)
|| connected(blockAccess, x - 1, y, z + 1, block, metadata))) zp = false;
if (zn && (connected(blockAccess, x, y + 1, z - 1, block, metadata)
|| connected(blockAccess, x, y - 1, z - 1, block, metadata))) zn = false;
if (zn && (connected(blockAccess, x + 1, y, z - 1, block, metadata)
|| connected(blockAccess, x - 1, y, z - 1, block, metadata))) zn = false;
if (zp && (connected(blockAccess, x, y + 1, z + 1, x, y, z + 1, block, metadata)
|| connected(blockAccess, x, y - 1, z + 1, x, y, z + 1, block, metadata))) zp = false;
if (zp && (connected(blockAccess, x + 1, y, z + 1, x, y, z + 1, block, metadata)
|| connected(blockAccess, x - 1, y, z + 1, x, y, z + 1, block, metadata))) zp = false;
if (zn && (connected(blockAccess, x, y + 1, z - 1, x, y, z - 1, block, metadata)
|| connected(blockAccess, x, y - 1, z - 1, x, y, z - 1, block, metadata))) zn = false;
if (zn && (connected(blockAccess, x + 1, y, z - 1, x, y, z - 1, block, metadata)
|| connected(blockAccess, x - 1, y, z - 1, x, y, z - 1, block, metadata))) zn = false;
if (zp || zn) {
uvRotateSouth = 1;