#include "TileRenderer.h"
#include "Chunk.h"
#include "../Minecraft.h"
#include "Tesselator.h"

#include "../../world/level/LevelSource.h"
#include "../../world/level/tile/Tile.h"
#include "../../world/level/tile/DoorTile.h"
#include "../../world/level/tile/LiquidTile.h"
#include "../../world/level/tile/FenceTile.h"
#include "../../world/level/tile/FenceGateTile.h"
#include "../../world/level/tile/ThinFenceTile.h"
#include "../../world/level/tile/BedTile.h"
#include "../../world/level/tile/StemTile.h"
#include "../../world/level/tile/StairTile.h"
#include "../../world/Direction.h"
#include "../../world/Facing.h"
#include "tileentity/TileEntityRenderer.h"
#include "EntityTileRenderer.h"

TileRenderer::TileRenderer( LevelSource* level /* = NULL */ )
:	level(level),
	fixedTexture(-1),
	xFlipTexture(false),
	noCulling(false),
	blsmooth(1),
	applyAmbienceOcclusion(false)
{
}

bool TileRenderer::tesselateBlockInWorld(Tile* tt, int x, int y, int z) {
	int col = tt->getColor(level, x, y, z);
	float r = ((col >> 16) & 0xff) / 255.0f;
	float g = ((col >> 8) & 0xff) / 255.0f;
	float b = ((col) & 0xff) / 255.0f; // xFlipTexture = (x & 1) != (y & 1);

	if (Minecraft::useAmbientOcclusion) {
		return tesselateBlockInWorldWithAmbienceOcclusion(tt, x, y, z, r, g, b);
	} else
	{
		return tesselateBlockInWorld(tt, x, y, z, r, g, b);
	}
}

bool TileRenderer::tesselateBlockInWorld( Tile* tt, int x, int y, int z, float r, float g, float b )
{
	applyAmbienceOcclusion = false;
	float xf = (float)x;
	float yf = (float)y;
	float zf = (float)z;

	Tesselator& t = Tesselator::instance;

	bool changed = false;
	float c10 = 0.5f;
	float c11 = 1;
	float c2 = 0.8f;
	float c3 = 0.6f;


	float r11 = c11 * r;
	float g11 = c11 * g;
	float b11 = c11 * b;

	if (tt == (Tile*)Tile::grass) {
		r = g = b = 1.0f;
	}

	float r10 = c10 * r;
	float r2 = c2 * r;
	float r3 = c3 * r;

	float g10 = c10 * g;
	float g2 = c2 * g;
	float g3 = c3 * g;

	float b10 = c10 * b;
	float b2 = c2 * b;
	float b3 = c3 * b;

	float centerBrightness = tt->getBrightness(level, x, y, z);

	if (noCulling || tt->shouldRenderFace(level, x, y - 1, z, Facing::DOWN)) {
		float br = tt->getBrightness(level, x, y - 1, z);
		t.color(r10 * br, g10 * br, b10 * br);
		renderFaceDown(tt, xf, yf, zf, tt->getTexture(level, x, y, z, 0));
		changed = true;
	}

	if (noCulling || tt->shouldRenderFace(level, x, y + 1, z, Facing::UP)) {
		float br = tt->getBrightness(level, x, y + 1, z);
		if (tt->yy1 != 1 && !tt->material->isLiquid()) br = centerBrightness;
		t.color(r11 * br, g11 * br, b11 * br);
		renderFaceUp(tt, xf, yf, zf, tt->getTexture(level, x, y, z, 1));
		changed = true;
	}

	if (noCulling || tt->shouldRenderFace(level, x, y, z - 1, Facing::NORTH)) {
		float br = tt->getBrightness(level, x, y, z - 1);
		if (tt->zz0 > 0) br = centerBrightness;
		t.color(r2 * br, g2 * br, b2 * br);
		renderNorth(tt, xf, yf, zf, tt->getTexture(level, x, y, z, 2));
		changed = true;
	}

	if (noCulling || tt->shouldRenderFace(level, x, y, z + 1, Facing::SOUTH)) {
		float br = tt->getBrightness(level, x, y, z + 1);
		if (tt->zz1 < 1) br = centerBrightness;
		t.color(r2 * br, g2 * br, b2 * br);
		renderSouth(tt, xf, yf, zf, tt->getTexture(level, x, y, z, 3));
		changed = true;
	}

	if (noCulling || tt->shouldRenderFace(level, x - 1, y, z, Facing::WEST)) {
		float br = tt->getBrightness(level, x - 1, y, z);
		if (tt->xx0 > 0) br = centerBrightness;
		t.color(r3 * br, g3 * br, b3 * br);
		renderWest(tt, xf, yf, zf, tt->getTexture(level, x, y, z, 4));
		changed = true;
	}

	if (noCulling || tt->shouldRenderFace(level, x + 1, y, z, Facing::EAST)) {
		float br = tt->getBrightness(level, x + 1, y, z);
		if (tt->xx1 < 1) br = centerBrightness;
		t.color(r3 * br, g3 * br, b3 * br);
		renderEast(tt, xf, yf, zf, tt->getTexture(level, x, y, z, 5));
		changed = true;
	}

	return changed;
}


void TileRenderer::tesselateInWorld( Tile* tile, int x, int y, int z, int fixedTexture )
{
	this->fixedTexture = fixedTexture;
	tesselateInWorld(tile, x, y, z);
	this->fixedTexture = -1;
}

bool TileRenderer::tesselateInWorld( Tile* tt, int x, int y, int z )
{
	int shape = tt->getRenderShape();
	tt->updateShape(level, x, y, z);

	if (shape == Tile::SHAPE_BLOCK) {
		return tesselateBlockInWorld(tt, x, y, z);
	} else if (shape == Tile::SHAPE_WATER) {
		return tesselateWaterInWorld(tt, x, y, z);
    } else if (shape == Tile::SHAPE_CACTUS) {
        return tesselateCactusInWorld(tt, x, y, z);
	} else if (shape == Tile::SHAPE_CROSS_TEXTURE) {
		return tesselateCrossInWorld(tt, x, y, z);
	} else if(shape == Tile::SHAPE_STEM) {
		return tesselateStemInWorld(tt, x, y, z);
	} else if (shape == Tile::SHAPE_ROWS) {
        return tesselateRowInWorld(tt, x, y, z);
	} else if (shape == Tile::SHAPE_TORCH) {
		return tesselateTorchInWorld(tt, x, y, z);
    //} else if (shape == Tile::SHAPE_FIRE) {
    //    return tesselateFireInWorld(tt, x, y, z);
    //} else if (shape == Tile::SHAPE_RED_DUST) {
    //    return tesselateDustInWorld(tt, x, y, z);
	} else if (shape == Tile::SHAPE_LADDER) {
		return tesselateLadderInWorld(tt, x, y, z);
	} else if (shape == Tile::SHAPE_DOOR) {
		return tesselateDoorInWorld(tt, x, y, z);
    //} else if (shape == Tile::SHAPE_RAIL) {
    //    return tesselateRailInWorld(tt, x, y, z);
	} else if (shape == Tile::SHAPE_STAIRS) {
		return tesselateStairsInWorld((StairTile*)tt, x, y, z);
    } else if (shape == Tile::SHAPE_FENCE) {
        return tesselateFenceInWorld((FenceTile*)tt, x, y, z);
	} else if (shape == Tile::SHAPE_FENCE_GATE) {
		return tesselateFenceGateInWorld((FenceGateTile*) tt, x, y, z);
    //} else if (shape == Tile::SHAPE_LEVER) {
    //    return tesselateLeverInWorld(tt, x, y, z);
    //} else if (shape == Tile::SHAPE_BED) {
    //    return tesselateBedInWorld(tt, x, y, z);
    //} else if (shape == Tile::SHAPE_DIODE) {
    //    return tesselateDiodeInWorld(tt, x, y, z);
	} else if (shape == Tile::SHAPE_IRON_FENCE) {
		return tesselateThinFenceInWorld((ThinFenceTile*) tt, x, y, z);
	} else if(shape == Tile::SHAPE_BED) {
		return tesselateBedInWorld(tt, x, y, z);
	} else {
		return false;
	}
}

void TileRenderer::tesselateInWorldNoCulling( Tile* tile, int x, int y, int z )
{
	noCulling = true;
	tesselateInWorld(tile, x, y, z);
	noCulling = false;
}

bool TileRenderer::tesselateTorchInWorld( Tile* tt, int x, int y, int z )
{
	int dir = level->getData(x, y, z);

	Tesselator& t = Tesselator::instance;

	float br = tt->getBrightness(level, x, y, z);
	if (Tile::lightEmission[tt->id] > 0) br = 1.0f;
	t.color(br, br, br);

	float r = 0.40f;
	float r2 = 0.5f - r;
	float h = 0.20f;
	if (dir == 1) {
		tesselateTorch(tt, (float)x - r2, (float)y + h, (float)z, -r, 0);
	} else if (dir == 2) {
		tesselateTorch(tt, (float)x + r2, (float)y + h, (float)z, +r, 0);
	} else if (dir == 3) {
		tesselateTorch(tt, (float)x, (float)y + h, (float)z - r2, 0, -r);
	} else if (dir == 4) {
		tesselateTorch(tt, (float)x, (float)y + h, (float)z + r2, 0, +r);
	} else {
		tesselateTorch(tt, (float)x, (float)y, (float)z, 0, 0);
	}
	return true;
}

bool TileRenderer::tesselateLadderInWorld( Tile* tt, int x, int y, int z )
{
	Tesselator& t = Tesselator::instance;

	int tex = tt->getTexture(0);

	if (fixedTexture >= 0) tex = fixedTexture;

	float br = tt->getBrightness(level, x, y, z);
	t.color(br, br, br);
	int xt = ((tex & 0xf) << 4);
	int yt = tex & 0xf0;

	float u0 = (xt) / 256.0f;
	float u1 = (xt + 15.99f) / 256.0f;
	float v0 = (yt) / 256.0f;
	float v1 = (yt + 15.99f) / 256.0f;

	int face = level->getData(x, y, z);

	float o = 0 / 16.0f;
	float r = 0.05f;
	if (face == 5) {
		t.vertexUV(x + r, y + 1 + o, z + 1 + o, u0, v0);
		t.vertexUV(x + r, y + 0 - o, z + 1 + o, u0, v1);
		t.vertexUV(x + r, y + 0 - o, z + 0 - o, u1, v1);
		t.vertexUV(x + r, y + 1 + o, z + 0 - o, u1, v0);
	}
	if (face == 4) {
		t.vertexUV(x + 1 - r, y + 0 - o, z + 1 + o, u1, v1);
		t.vertexUV(x + 1 - r, y + 1 + o, z + 1 + o, u1, v0);
		t.vertexUV(x + 1 - r, y + 1 + o, z + 0 - o, u0, v0);
		t.vertexUV(x + 1 - r, y + 0 - o, z + 0 - o, u0, v1);
	}
	if (face == 3) {
		t.vertexUV(x + 1 + o, y + 0 - o, z + r, u1, v1);
		t.vertexUV(x + 1 + o, y + 1 + o, z + r, u1, v0);
		t.vertexUV(x + 0 - o, y + 1 + o, z + r, u0, v0);
		t.vertexUV(x + 0 - o, y + 0 - o, z + r, u0, v1);
	}
	if (face == 2) {
		t.vertexUV(x + 1 + o, y + 1 + o, z + 1 - r, u0, v0);
		t.vertexUV(x + 1 + o, y + 0 - o, z + 1 - r, u0, v1);
		t.vertexUV(x + 0 - o, y + 0 - o, z + 1 - r, u1, v1);
		t.vertexUV(x + 0 - o, y + 1 + o, z + 1 - r, u1, v0);
	}

	return true;
}

bool TileRenderer::tesselateCrossInWorld( Tile* tt, int x, int y, int z )
{
	Tesselator& t = Tesselator::instance;

	float br = tt->getBrightness(level, x, y, z);
	int col = tt->getColor(level, x, y, z);
	float r = ((col >> 16) & 0xff) / 255.0f;
	float g = ((col >> 8) & 0xff) / 255.0f;
	float b = ((col) & 0xff) / 255.0f;
	t.color(br * r, br * g, br * b);

	float xt = float(x);
	float yt = float(y);
	float zt = float(z);

	if (tt == Tile::tallgrass) {
		long seed = (x * 3129871) ^ (z * 116129781l) ^ (y);
		seed = seed * seed * 42317861 + seed * 11;

		xt += ((((seed >> 16) & 0xf) / 15.0f) - 0.5f) * 0.5f;
		yt += ((((seed >> 20) & 0xf) / 15.0f) - 1.0f) * 0.2f;
		zt += ((((seed >> 24) & 0xf) / 15.0f) - 0.5f) * 0.5f;
	}

	tesselateCrossTexture(tt, level->getData(x, y, z), xt, yt, zt);
	return true;
	//return true;
	/*Tesselator& t = Tesselator::instance;

	float br = tt->getBrightness(level, x, y, z);
	t.color(br, br, br);

	tesselateCrossTexture(tt, level->getData(x, y, z), (float)x, (float)y, (float)z);
	return true;*/
}
bool TileRenderer::tesselateStemInWorld( Tile* _tt, int x, int y, int z ) {
	StemTile* tt = (StemTile*) _tt;
	Tesselator& t = Tesselator::instance;

	float br = tt->getBrightness(level, x, y, z);

	int col = tt->getColor(level, x, y, z);
	float r = ((col >> 16) & 0xff) / 255.0f;
	float g = ((col >> 8) & 0xff) / 255.0f;
	float b = ((col) & 0xff) / 255.0f;

	t.color(br * r, br * g, br * b);

	tt->updateShape(level, x, y, z);
	int dir = tt->getConnectDir(level, x, y, z);
	if (dir < 0) {
		tesselateStemTexture(tt, level->getData(x, y, z), tt->yy1, float(x), float(y - 1 / 16.0f), float(z));
	} else {
		tesselateStemTexture(tt, level->getData(x, y, z), 0.5f, float(x), float(y - 1 / 16.0f), float(z));
		tesselateStemDirTexture(tt, level->getData(x, y, z), dir, tt->yy1, float(x), float(y - 1 / 16.0f), float(z));
	}
	return true;
}
void TileRenderer::tesselateTorch( Tile* tt, float x, float y, float z, float xxa, float zza )
{
	Tesselator& t = Tesselator::instance;
	int tex = tt->getTexture(0);

	if (fixedTexture >= 0) tex = fixedTexture;
	int xt = (tex & 0xf) << 4;
	int yt = tex & 0xf0;
	float u0 = (xt) / 256.0f;
	float u1 = (xt + 15.99f) / 256.0f;
	float v0 = (yt) / 256.0f;
	float v1 = (yt + 15.99f) / 256.0f;


	float uc0 = u0 + 7 / 256.0f;
	float vc0 = v0 + 6 / 256.0f;
	float uc1 = u0 + 9 / 256.0f;
	float vc1 = v0 + 8 / 256.0f;
	x += 0.5f;
	z += 0.5f;

	float x0 = x - 0.5f;
	float x1 = x + 0.5f;
	float z0 = z - 0.5f;
	float z1 = z + 0.5f;
	float r = 1 / 16.0f;

	float h = 10.0f / 16.0f;
	t.vertexUV(x + xxa * (1 - h) - r, y + h, z + zza * (1 - h) - r, uc0, vc0);
	t.vertexUV(x + xxa * (1 - h) - r, y + h, z + zza * (1 - h) + r, uc0, vc1);
	t.vertexUV(x + xxa * (1 - h) + r, y + h, z + zza * (1 - h) + r, uc1, vc1);
	t.vertexUV(x + xxa * (1 - h) + r, y + h, z + zza * (1 - h) - r, uc1, vc0);

	t.vertexUV(x - r, y + 1, z0, u0, v0);
	t.vertexUV(x - r + xxa, y + 0, z0 + zza, u0, v1);
	t.vertexUV(x - r + xxa, y + 0, z1 + zza, u1, v1);
	t.vertexUV(x - r, y + 1, z1, u1, v0);

	t.vertexUV(x + r, y + 1, z1, u0, v0);
	t.vertexUV(x + xxa + r, y + 0, z1 + zza, u0, v1);
	t.vertexUV(x + xxa + r, y + 0, z0 + zza, u1, v1);
	t.vertexUV(x + r, y + 1, z0, u1, v0);

	t.vertexUV(x0, y + 1, z + r, u0, v0);
	t.vertexUV(x0 + xxa, y + 0, z + r + zza, u0, v1);
	t.vertexUV(x1 + xxa, y + 0, z + r + zza, u1, v1);
	t.vertexUV(x1, y + 1, z + r, u1, v0);

	t.vertexUV(x1, y + 1, z - r, u0, v0);
	t.vertexUV(x1 + xxa, y + 0, z - r + zza, u0, v1);
	t.vertexUV(x0 + xxa, y + 0, z - r + zza, u1, v1);
	t.vertexUV(x0, y + 1, z - r, u1, v0);
}

void TileRenderer::tesselateCrossTexture( Tile* tt, int data, float x, float y, float z )
{
	Tesselator& t = Tesselator::instance;

	int tex = tt->getTexture(0, data);

	if (fixedTexture >= 0) tex = fixedTexture;
	int xt = (tex & 0xf) << 4;
	int yt = tex & 0xf0;
	float u0 = (xt) / 256.0f;
	float u1 = (xt + 15.99f) / 256.0f;
	float v0 = (yt) / 256.0f;
	float v1 = (yt + 15.99f) / 256.0f;

	float x0 = x + 0.5f - 0.45f;
	float x1 = x + 0.5f + 0.45f;
	float z0 = z + 0.5f - 0.45f;
	float z1 = z + 0.5f + 0.45f;

	t.vertexUV(x0, y + 1, z0, u0, v0);
	t.vertexUV(x0, y + 0, z0, u0, v1);
	t.vertexUV(x1, y + 0, z1, u1, v1);
	t.vertexUV(x1, y + 1, z1, u1, v0);

	t.vertexUV(x1, y + 1, z1, u0, v0);
	t.vertexUV(x1, y + 0, z1, u0, v1);
	t.vertexUV(x0, y + 0, z0, u1, v1);
	t.vertexUV(x0, y + 1, z0, u1, v0);

	t.vertexUV(x0, y + 1, z1, u0, v0);
	t.vertexUV(x0, y + 0, z1, u0, v1);
	t.vertexUV(x1, y + 0, z0, u1, v1);
	t.vertexUV(x1, y + 1, z0, u1, v0);

	t.vertexUV(x1, y + 1, z0, u0, v0);
	t.vertexUV(x1, y + 0, z0, u0, v1);
	t.vertexUV(x0, y + 0, z1, u1, v1);
	t.vertexUV(x0, y + 1, z1, u1, v0);
}
void TileRenderer::tesselateStemTexture( Tile* tt, int data, float h, float x, float y, float z ) {
	Tesselator& t = Tesselator::instance;
	int tex = tt->getTexture(0, data);
	if(fixedTexture >= 0) tex = fixedTexture;
	int xt = (tex & 0xf) << 4;
	int yt = tex & 0xf0;
	float u0 = (xt) / 256.0f;
	float u1 = (xt + 15.99f) / 256.0f;
	float v0 = (yt) / 256.0f;
	float v1 = (yt + 15.99f * h) / 256.0f;

	float x0 = x + 0.5f - 0.45f;
	float x1 = x + 0.5f + 0.45f;
	float z0 = z + 0.5f - 0.45f;
	float z1 = z + 0.5f + 0.45f;

	t.vertexUV(x0, y + h, z0, u0, v0);
	t.vertexUV(x0, y + 0, z0, u0, v1);
	t.vertexUV(x1, y + 0, z1, u1, v1);
	t.vertexUV(x1, y + h, z1, u1, v0);

	t.vertexUV(x1, y + h, z1, u0, v0);
	t.vertexUV(x1, y + 0, z1, u0, v1);
	t.vertexUV(x0, y + 0, z0, u1, v1);
	t.vertexUV(x0, y + h, z0, u1, v0);

	t.vertexUV(x0, y + h, z1, u0, v0);
	t.vertexUV(x0, y + 0, z1, u0, v1);
	t.vertexUV(x1, y + 0, z0, u1, v1);
	t.vertexUV(x1, y + h, z0, u1, v0);

	t.vertexUV(x1, y + h, z0, u0, v0);
	t.vertexUV(x1, y + 0, z0, u0, v1);
	t.vertexUV(x0, y + 0, z1, u1, v1);
	t.vertexUV(x0, y + h, z1, u1, v0);
}
void TileRenderer::tesselateStemDirTexture( Tile* tt, int data, int dir, float h, float x, float y, float z ) {
	Tesselator& t = Tesselator::instance;

	int tex = tt->getTexture(0, data) + 16;

	if (fixedTexture >= 0) tex = fixedTexture;
	int xt = (tex & 0xf) << 4;
	int yt = tex & 0xf0;
	float u0 = (xt) / 256.0f;
	float u1 = (xt + 15.99f) / 256.0f;
	float v0 = (yt) / 256.0f;
	float v1 = (yt + 15.99f * h) / 256.0f;

	float x0 = x + 0.5f - 0.5f;
	float x1 = x + 0.5f + 0.5f;
	float z0 = z + 0.5f - 0.5f;
	float z1 = z + 0.5f + 0.5f;

	float xm = x + 0.5f;
	float zm = z + 0.5f;

	if ((dir + 1) / 2 % 2 == 1) {
		float tmp = u1;
		u1 = u0;
		u0 = tmp;
	}

	if (dir < 2) {
		t.vertexUV(x0, y + h, zm, u0, v0);
		t.vertexUV(x0, y + 0, zm, u0, v1);
		t.vertexUV(x1, y + 0, zm, u1, v1);
		t.vertexUV(x1, y + h, zm, u1, v0);

		t.vertexUV(x1, y + h, zm, u1, v0);
		t.vertexUV(x1, y + 0, zm, u1, v1);
		t.vertexUV(x0, y + 0, zm, u0, v1);
		t.vertexUV(x0, y + h, zm, u0, v0);
	} else {

		t.vertexUV(xm, y + h, z1, u0, v0);
		t.vertexUV(xm, y + 0, z1, u0, v1);
		t.vertexUV(xm, y + 0, z0, u1, v1);
		t.vertexUV(xm, y + h, z0, u1, v0);

		t.vertexUV(xm, y + h, z0, u1, v0);
		t.vertexUV(xm, y + 0, z0, u1, v1);
		t.vertexUV(xm, y + 0, z1, u0, v1);
		t.vertexUV(xm, y + h, z1, u0, v0);
	}
}

bool TileRenderer::tesselateWaterInWorld( Tile* tt, int x, int y, int z )
{
	Tesselator& t = Tesselator::instance;

	bool up = tt->shouldRenderFace(level, x, y + 1, z, 1);
	bool down = tt->shouldRenderFace(level, x, y - 1, z, 0);

	bool dirs[4]; // static?
	dirs[0] = tt->shouldRenderFace(level, x, y, z - 1, 2);
	dirs[1] = tt->shouldRenderFace(level, x, y, z + 1, 3);
	dirs[2] = tt->shouldRenderFace(level, x - 1, y, z, 4);
	dirs[3] = tt->shouldRenderFace(level, x + 1, y, z, 5);

	if (!up && !down && !dirs[0] && !dirs[1] && !dirs[2] && !dirs[3]) return false;

	bool changed = false;
	float c10 = 0.5f;
	float c11 = 1;
	float c2 = 0.8f;
	float c3 = 0.6f;

	const float yo0 = 0;
	const float yo1 = 1;

	const Material* m = tt->material;
	int data = level->getData(x, y, z);

	float h0 = getWaterHeight(x, y, z, m);
	float h1 = getWaterHeight(x, y, z + 1, m);
	float h2 = getWaterHeight(x + 1, y, z + 1, m);
	float h3 = getWaterHeight(x + 1, y, z, m);

	// renderFaceUp(tt, x, y, z, tt->getTexture(0));
	if (noCulling || up) {
		changed = true;
		int tex = tt->getTexture(1, data);
		float angle = (float) LiquidTile::getSlopeAngle(level, x, y, z, m);
		if (angle > -999) {
			tex = tt->getTexture(2, data);
		}
		int xt = (tex & 0xf) << 4;
		int yt = tex & 0xf0;

		float uc = (xt + 0.5f * 16) / 256.0f;
		float vc = (yt + 0.5f * 16) / 256.0f;
		if (angle < -999) {
			angle = 0;
		} else {
			uc = (xt + 1 * 16) / 256.0f;
			vc = (yt + 1 * 16) / 256.0f;
		}
		float s = (Mth::sin(angle) * 8) / 256.5f; // @attn: to get rid of "jitter" (caused
		float c = (Mth::cos(angle) * 8) / 256.5f; //  of fp rounding errors) in big oceans)

		float br = tt->getBrightness(level, x, y, z);
		t.color(c11 * br, c11 * br, c11 * br);
		t.vertexUV((float)x + 0, (float)y + h0, (float)z + 0, uc - c - s, vc - c + s);
		t.vertexUV((float)x + 0, (float)y + h1, (float)z + 1, uc - c + s, vc + c + s);
		t.vertexUV((float)x + 1, (float)y + h2, (float)z + 1, uc + c + s, vc + c - s);
		t.vertexUV((float)x + 1, (float)y + h3, (float)z + 0, uc + c - s, vc - c - s);
	}

	if (noCulling || down) {
		float br = tt->getBrightness(level, x, y - 1, z);
		t.color(c10 * br, c10 * br, c10 * br);
		renderFaceDown(tt, (float)x, (float)y, (float)z, tt->getTexture(0));
		changed = true;
	}

	for (int face = 0; face < 4; face++) {
		int xt = x;
		int yt = y;
		int zt = z;

		if (face == 0) zt--;
		if (face == 1) zt++;
		if (face == 2) xt--;
		if (face == 3) xt++;

		int tex = tt->getTexture(face + 2, data);
		int xTex = (tex & 0xf) << 4;
		int yTex = tex & 0xf0;

		if (noCulling || dirs[face]) {
			float hh0;
			float hh1;
			float x0, z0, x1, z1;
			if (face == 0) {
				hh0 = h0;
				hh1 = h3;
				x0 = (float)(x    );
				x1 = (float)(x + 1);
				z0 = (float)(z    );
				z1 = (float)(z    );
			} else if (face == 1) {
				hh0 = h2;
				hh1 = h1;
				x0 = (float)(x + 1);
				x1 = (float)(x    );
				z0 = (float)(z + 1);
				z1 = (float)(z + 1);
			} else if (face == 2) {
				hh0 = h1;
				hh1 = h0;
				x0 = (float)(x    );
				x1 = (float)(x    );
				z0 = (float)(z + 1);
				z1 = (float)(z    );
			} else {
				hh0 = h3;
				hh1 = h2;
				x0 = (float)(x + 1);
				x1 = (float)(x + 1);
				z0 = (float)(z    );
				z1 = (float)(z + 1);
			}

			changed = true;
			float u0 = (xTex + 0 * 16) / 256.0f;
			float u1 = (xTex + 1 * 16 - 0.01f) / 256.0f;

			float v01 = (yTex + (1 - hh0) * 16) / 256.0f;
			float v02 = (yTex + (1 - hh1) * 16) / 256.0f;
			float v1 = (yTex + 1 * 16 - 0.01f) / 256.0f;

			float br = tt->getBrightness(level, xt, yt, zt);
			if (face < 2) br *= c2;
			else br *= c3;

			float yf = (float)y;
			t.color(c11 * br, c11 * br, c11 * br);
			t.vertexUV(x0, yf + hh0, z0, u0, v01);
			t.vertexUV(x1, yf + hh1, z1, u1, v02);
			t.vertexUV(x1, yf + 0, z1, u1, v1);
			t.vertexUV(x0, yf + 0, z0, u0, v1);
		}
	}

	//printf("w: %d ", (dirs[0] + dirs[1] + dirs[2] + dirs[3] + up + down));

	tt->yy0 = yo0;
	tt->yy1 = yo1;

	return changed;
}

float TileRenderer::getWaterHeight( int x, int y, int z, const Material* m )
{
	int count = 0;
	float h = 0;
	for (int i = 0; i < 4; i++) {
		int xx = x - (i & 1);
		int yy = y;
		int zz = z - ((i >> 1) & 1);
		if (level->getMaterial(xx, yy + 1, zz) == m) {
			return 1;
		}
		const Material* tm = level->getMaterial(xx, yy, zz);
		if (tm == m) {
			int d = level->getData(xx, yy, zz);
			if (d >= 8 || d == 0) {
				h += (LiquidTile::getHeight(d)) * 10;
				count += 10;
			}
			h += LiquidTile::getHeight(d);
			count++;
		} else if (!tm->isSolid()) {
			h += 1;
			count++;
		}
	}
	return 1 - h / count;
}

void TileRenderer::renderBlock(Tile* tt, LevelSource* level, int x, int y, int z) {
    float c10 = 0.5f;
    float c11 = 1;
    float c2 = 0.8f;
    float c3 = 0.6f;

    Tesselator& t = Tesselator::instance;
    t.begin();

    float center = tt->getBrightness(level, x, y, z);
    float br = tt->getBrightness(level, x, y - 1, z);
    if (br < center) br = center;

    t.color(c10 * br, c10 * br, c10 * br);
    renderFaceDown(tt, -0.5f, -0.5f, -0.5f, tt->getTexture(0));

    br = tt->getBrightness(level, x, y + 1, z);
    if (br < center) br = center;
    t.color(c11 * br, c11 * br, c11 * br);
    renderFaceUp(tt, -0.5f, -0.5f, -0.5f, tt->getTexture(1));

    br = tt->getBrightness(level, x, y, z - 1);
    if (br < center) br = center;
    t.color(c2 * br, c2 * br, c2 * br);
    renderNorth(tt, -0.5f, -0.5f, -0.5f, tt->getTexture(2));

    br = tt->getBrightness(level, x, y, z + 1);
    if (br < center) br = center;
    t.color(c2 * br, c2 * br, c2 * br);
    renderSouth(tt, -0.5f, -0.5f, -0.5f, tt->getTexture(3));

    br = tt->getBrightness(level, x - 1, y, z);
    if (br < center) br = center;
    t.color(c3 * br, c3 * br, c3 * br);
    renderWest(tt, -0.5f, -0.5f, -0.5f, tt->getTexture(4));

    br = tt->getBrightness(level, x + 1, y, z);
    if (br < center) br = center;
    t.color(c3 * br, c3 * br, c3 * br);
    renderEast(tt, -0.5f, -0.5f, -0.5f, tt->getTexture(5));
    t.draw();
}

bool TileRenderer::tesselateBlockInWorldWithAmbienceOcclusion( Tile* tt, int pX, int pY, int pZ, float pBaseRed, float pBaseGreen, float pBaseBlue )
{
	applyAmbienceOcclusion = true;
	bool i = false;
	float ll1 = ll000;
	float ll2 = ll000;
	float ll3 = ll000;
	float ll4 = ll000;
	bool tint0 = true;
	bool tint1 = true;
	bool tint2 = true;
	bool tint3 = true;
	bool tint4 = true;
	bool tint5 = true;

	ll000 = tt->getBrightness(level, pX, pY, pZ);
	llx00 = tt->getBrightness(level, pX - 1, pY, pZ);
	ll0y0 = tt->getBrightness(level, pX, pY - 1, pZ);
	ll00z = tt->getBrightness(level, pX, pY, pZ - 1);
	llX00 = tt->getBrightness(level, pX + 1, pY, pZ);
	ll0Y0 = tt->getBrightness(level, pX, pY + 1, pZ);
	ll00Z = tt->getBrightness(level, pX, pY, pZ + 1);

	llTransXY0 = Tile::translucent[level->getTile(pX + 1, pY + 1, pZ)];
	llTransXy0 = Tile::translucent[level->getTile(pX + 1, pY - 1, pZ)];
	llTransX0Z = Tile::translucent[level->getTile(pX + 1, pY, pZ + 1)];
	llTransX0z = Tile::translucent[level->getTile(pX + 1, pY, pZ - 1)];
	llTransxY0 = Tile::translucent[level->getTile(pX - 1, pY + 1, pZ)];
	llTransxy0 = Tile::translucent[level->getTile(pX - 1, pY - 1, pZ)];
	llTransx0z = Tile::translucent[level->getTile(pX - 1, pY, pZ - 1)];
	llTransx0Z = Tile::translucent[level->getTile(pX - 1, pY, pZ + 1)];
	llTrans0YZ = Tile::translucent[level->getTile(pX, pY + 1, pZ + 1)];
	llTrans0Yz = Tile::translucent[level->getTile(pX, pY + 1, pZ - 1)];
	llTrans0yZ = Tile::translucent[level->getTile(pX, pY - 1, pZ + 1)];
	llTrans0yz = Tile::translucent[level->getTile(pX, pY - 1, pZ - 1)];

	if (tt->tex == 3) tint0 = tint2 = tint3 = tint4 = tint5 = false;

	if ((noCulling) || (tt->shouldRenderFace(level, pX, pY - 1, pZ, 0))) {
		if (blsmooth > 0) {
			pY--;

			llxy0 = tt->getBrightness(level, pX - 1, pY, pZ);
			ll0yz = tt->getBrightness(level, pX, pY, pZ - 1);
			ll0yZ = tt->getBrightness(level, pX, pY, pZ + 1);
			llXy0 = tt->getBrightness(level, pX + 1, pY, pZ);

			if (llTrans0yz || llTransxy0) {
				llxyz = tt->getBrightness(level, pX - 1, pY, pZ - 1);
			} else {
				llxyz = llxy0;
			}
			if (llTrans0yZ || llTransxy0) {
				llxyZ = tt->getBrightness(level, pX - 1, pY, pZ + 1);
			} else {
				llxyZ = llxy0;
			}
			if (llTrans0yz || llTransXy0) {
				llXyz = tt->getBrightness(level, pX + 1, pY, pZ - 1);
			} else {
				llXyz = llXy0;
			}
			if (llTrans0yZ || llTransXy0) {
				llXyZ = tt->getBrightness(level, pX + 1, pY, pZ + 1);
			} else {
				llXyZ = llXy0;
			}

			pY++;
			ll1 = (llxyZ + llxy0 + ll0yZ + ll0y0) / 4.0f;
			ll4 = (ll0yZ + ll0y0 + llXyZ + llXy0) / 4.0f;
			ll3 = (ll0y0 + ll0yz + llXy0 + llXyz) / 4.0f;
			ll2 = (llxy0 + llxyz + ll0y0 + ll0yz) / 4.0f;
		} else ll1 = ll2 = ll3 = ll4 = ll0y0;
		c1r = c2r = c3r = c4r = (tint0 ? pBaseRed : 1.0f) * 0.5f;
		c1g = c2g = c3g = c4g = (tint0 ? pBaseGreen : 1.0f) * 0.5f;
		c1b = c2b = c3b = c4b = (tint0 ? pBaseBlue : 1.0f) * 0.5f;
		c1r *= ll1;
		c1g *= ll1;
		c1b *= ll1;
		c2r *= ll2;
		c2g *= ll2;
		c2b *= ll2;
		c3r *= ll3;
		c3g *= ll3;
		c3b *= ll3;
		c4r *= ll4;
		c4g *= ll4;
		c4b *= ll4;

		renderFaceDown(tt, (float) pX, (float) pY, (float) pZ, tt->getTexture(level, pX, pY, pZ, 0));
		i = true;
	}
	if ((noCulling) || (tt->shouldRenderFace(level, pX, pY + 1, pZ, 1))) {
		if (blsmooth > 0) {
			pY++;

			llxY0 = tt->getBrightness(level, pX - 1, pY, pZ);
			llXY0 = tt->getBrightness(level, pX + 1, pY, pZ);
			ll0Yz = tt->getBrightness(level, pX, pY, pZ - 1);
			ll0YZ = tt->getBrightness(level, pX, pY, pZ + 1);

			if (llTrans0Yz || llTransxY0) {
				llxYz = tt->getBrightness(level, pX - 1, pY, pZ - 1);
			} else {
				llxYz = llxY0;
			}
			if (llTrans0Yz || llTransXY0) {
				llXYz = tt->getBrightness(level, pX + 1, pY, pZ - 1);
			} else {
				llXYz = llXY0;
			}
			if (llTrans0YZ || llTransxY0) {
				llxYZ = tt->getBrightness(level, pX - 1, pY, pZ + 1);
			} else {
				llxYZ = llxY0;
			}
			if (llTrans0YZ || llTransXY0) {
				llXYZ = tt->getBrightness(level, pX + 1, pY, pZ + 1);
			} else {
				llXYZ = llXY0;
			}
			pY--;

			ll4 = (llxYZ + llxY0 + ll0YZ + ll0Y0) / 4.0f;
			ll1 = (ll0YZ + ll0Y0 + llXYZ + llXY0) / 4.0f;
			ll2 = (ll0Y0 + ll0Yz + llXY0 + llXYz) / 4.0f;
			ll3 = (llxY0 + llxYz + ll0Y0 + ll0Yz) / 4.0f;
		} else ll1 = ll2 = ll3 = ll4 = ll0Y0;
		c1r = c2r = c3r = c4r = (tint1 ? pBaseRed : 1.0f);
		c1g = c2g = c3g = c4g = (tint1 ? pBaseGreen : 1.0f);
		c1b = c2b = c3b = c4b = (tint1 ? pBaseBlue : 1.0f);
		c1r *= ll1;
		c1g *= ll1;
		c1b *= ll1;
		c2r *= ll2;
		c2g *= ll2;
		c2b *= ll2;
		c3r *= ll3;
		c3g *= ll3;
		c3b *= ll3;
		c4r *= ll4;
		c4g *= ll4;
		c4b *= ll4;
		renderFaceUp(tt, (float) pX, (float) pY, (float) pZ, tt->getTexture(level, pX, pY, pZ, 1));
		i = true;
	}
	if ((noCulling) || (tt->shouldRenderFace(level, pX, pY, pZ - 1, 2))) {
		if (blsmooth > 0) {
			pZ--;
			llx0z = tt->getBrightness(level, pX - 1, pY, pZ);
			ll0yz = tt->getBrightness(level, pX, pY - 1, pZ);
			ll0Yz = tt->getBrightness(level, pX, pY + 1, pZ);
			llX0z = tt->getBrightness(level, pX + 1, pY, pZ);

			if (llTransx0z || llTrans0yz) {
				llxyz = tt->getBrightness(level, pX - 1, pY - 1, pZ);
			} else {
				llxyz = llx0z;
			}
			if (llTransx0z || llTrans0Yz) {
				llxYz = tt->getBrightness(level, pX - 1, pY + 1, pZ);
			} else {
				llxYz = llx0z;
			}
			if (llTransX0z || llTrans0yz) {
				llXyz = tt->getBrightness(level, pX + 1, pY - 1, pZ);
			} else {
				llXyz = llX0z;
			}
			if (llTransX0z || llTrans0Yz) {
				llXYz = tt->getBrightness(level, pX + 1, pY + 1, pZ);
			} else {
				llXYz = llX0z;
			}
			pZ++;
			ll1 = (llx0z + llxYz + ll00z + ll0Yz) / 4.0f;
			ll2 = (ll00z + ll0Yz + llX0z + llXYz) / 4.0f;
			ll3 = (ll0yz + ll00z + llXyz + llX0z) / 4.0f;
			ll4 = (llxyz + llx0z + ll0yz + ll00z) / 4.0f;
		} else ll1 = ll2 = ll3 = ll4 = ll00z;
		c1r = c2r = c3r = c4r = (tint2 ? pBaseRed : 1.0f) * 0.8f;
		c1g = c2g = c3g = c4g = (tint2 ? pBaseGreen : 1.0f) * 0.8f;
		c1b = c2b = c3b = c4b = (tint2 ? pBaseBlue : 1.0f) * 0.8f;
		c1r *= ll1;
		c1g *= ll1;
		c1b *= ll1;
		c2r *= ll2;
		c2g *= ll2;
		c2b *= ll2;
		c3r *= ll3;
		c3g *= ll3;
		c3b *= ll3;
		c4r *= ll4;
		c4g *= ll4;
		c4b *= ll4;
		renderNorth(tt, (float) pX, (float) pY, (float) pZ, tt->getTexture(level, pX, pY, pZ, 2));
		i = true;
	}
	if ((noCulling) || (tt->shouldRenderFace(level, pX, pY, pZ + 1, 3))) {
		if (blsmooth > 0) {
			pZ++;

			llx0Z = tt->getBrightness(level, pX - 1, pY, pZ);
			llX0Z = tt->getBrightness(level, pX + 1, pY, pZ);
			ll0yZ = tt->getBrightness(level, pX, pY - 1, pZ);
			ll0YZ = tt->getBrightness(level, pX, pY + 1, pZ);

			if (llTransx0Z || llTrans0yZ) {
				llxyZ = tt->getBrightness(level, pX - 1, pY - 1, pZ);
			} else {
				llxyZ = llx0Z;
			}
			if (llTransx0Z || llTrans0YZ) {
				llxYZ = tt->getBrightness(level, pX - 1, pY + 1, pZ);
			} else {
				llxYZ = llx0Z;
			}
			if (llTransX0Z || llTrans0yZ) {
				llXyZ = tt->getBrightness(level, pX + 1, pY - 1, pZ);
			} else {
				llXyZ = llX0Z;
			}
			if (llTransX0Z || llTrans0YZ) {
				llXYZ = tt->getBrightness(level, pX + 1, pY + 1, pZ);
			} else {
				llXYZ = llX0Z;
			}
			pZ--;
			ll1 = (llx0Z + llxYZ + ll00Z + ll0YZ) / 4.0f;
			ll4 = (ll00Z + ll0YZ + llX0Z + llXYZ) / 4.0f;
			ll3 = (ll0yZ + ll00Z + llXyZ + llX0Z) / 4.0f;
			ll2 = (llxyZ + llx0Z + ll0yZ + ll00Z) / 4.0f;
		} else ll1 = ll2 = ll3 = ll4 = ll00Z;
		c1r = c2r = c3r = c4r = (tint3 ? pBaseRed : 1.0f) * 0.8f;
		c1g = c2g = c3g = c4g = (tint3 ? pBaseGreen : 1.0f) * 0.8f;
		c1b = c2b = c3b = c4b = (tint3 ? pBaseBlue : 1.0f) * 0.8f;
		c1r *= ll1;
		c1g *= ll1;
		c1b *= ll1;
		c2r *= ll2;
		c2g *= ll2;
		c2b *= ll2;
		c3r *= ll3;
		c3g *= ll3;
		c3b *= ll3;
		c4r *= ll4;
		c4g *= ll4;
		c4b *= ll4;
		renderSouth(tt, (float) pX, (float) pY, (float) pZ, tt->getTexture(level, pX, pY, pZ, 3));
		i = true;
	}
	if ((noCulling) || (tt->shouldRenderFace(level, pX - 1, pY, pZ, 4))) {
		if (blsmooth > 0) {
			pX--;
			llxy0 = tt->getBrightness(level, pX, pY - 1, pZ);
			llx0z = tt->getBrightness(level, pX, pY, pZ - 1);
			llx0Z = tt->getBrightness(level, pX, pY, pZ + 1);
			llxY0 = tt->getBrightness(level, pX, pY + 1, pZ);

			if (llTransx0z || llTransxy0) {
				llxyz = tt->getBrightness(level, pX, pY - 1, pZ - 1);
			} else {
				llxyz = llx0z;
			}
			if (llTransx0Z || llTransxy0) {
				llxyZ = tt->getBrightness(level, pX, pY - 1, pZ + 1);
			} else {
				llxyZ = llx0Z;
			}
			if (llTransx0z || llTransxY0) {
				llxYz = tt->getBrightness(level, pX, pY + 1, pZ - 1);
			} else {
				llxYz = llx0z;
			}
			if (llTransx0Z || llTransxY0) {
				llxYZ = tt->getBrightness(level, pX, pY + 1, pZ + 1);
			} else {
				llxYZ = llx0Z;
			}
			pX++;
			ll4 = (llxy0 + llxyZ + llx00 + llx0Z) / 4.0f;
			ll1 = (llx00 + llx0Z + llxY0 + llxYZ) / 4.0f;
			ll2 = (llx0z + llx00 + llxYz + llxY0) / 4.0f;
			ll3 = (llxyz + llxy0 + llx0z + llx00) / 4.0f;
		} else ll1 = ll2 = ll3 = ll4 = llx00;
		c1r = c2r = c3r = c4r = (tint4 ? pBaseRed : 1.0f) * 0.6f;
		c1g = c2g = c3g = c4g = (tint4 ? pBaseGreen : 1.0f) * 0.6f;
		c1b = c2b = c3b = c4b = (tint4 ? pBaseBlue : 1.0f) * 0.6f;
		c1r *= ll1;
		c1g *= ll1;
		c1b *= ll1;
		c2r *= ll2;
		c2g *= ll2;
		c2b *= ll2;
		c3r *= ll3;
		c3g *= ll3;
		c3b *= ll3;
		c4r *= ll4;
		c4g *= ll4;
		c4b *= ll4;
		renderWest(tt, (float) pX, (float) pY, (float) pZ, tt->getTexture(level, pX, pY, pZ, 4));
		i = true;
	}
	if ((noCulling) || (tt->shouldRenderFace(level, pX + 1, pY, pZ, 5))) {
		if (blsmooth > 0) {
			pX++;
			llXy0 = tt->getBrightness(level, pX, pY - 1, pZ);
			llX0z = tt->getBrightness(level, pX, pY, pZ - 1);
			llX0Z = tt->getBrightness(level, pX, pY, pZ + 1);
			llXY0 = tt->getBrightness(level, pX, pY + 1, pZ);

			if (llTransXy0 || llTransX0z) {
				llXyz = tt->getBrightness(level, pX, pY - 1, pZ - 1);
			} else {
				llXyz = llX0z;
			}
			if (llTransXy0 || llTransX0Z) {
				llXyZ = tt->getBrightness(level, pX, pY - 1, pZ + 1);
			} else {
				llXyZ = llX0Z;
			}
			if (llTransXY0 || llTransX0z) {
				llXYz = tt->getBrightness(level, pX, pY + 1, pZ - 1);
			} else {
				llXYz = llX0z;
			}
			if (llTransXY0 || llTransX0Z) {
				llXYZ = tt->getBrightness(level, pX, pY + 1, pZ + 1);
			} else {
				llXYZ = llX0Z;
			}
			pX--;
			ll1 = (llXy0 + llXyZ + llX00 + llX0Z) / 4.0f;
			ll4 = (llX00 + llX0Z + llXY0 + llXYZ) / 4.0f;
			ll3 = (llX0z + llX00 + llXYz + llXY0) / 4.0f;
			ll2 = (llXyz + llXy0 + llX0z + llX00) / 4.0f;
		} else ll1 = ll2 = ll3 = ll4 = llX00;
		c1r = c2r = c3r = c4r = (tint5 ? pBaseRed : 1.0f) * 0.6f;
		c1g = c2g = c3g = c4g = (tint5 ? pBaseGreen : 1.0f) * 0.6f;
		c1b = c2b = c3b = c4b = (tint5 ? pBaseBlue : 1.0f) * 0.6f;
		c1r *= ll1;
		c1g *= ll1;
		c1b *= ll1;
		c2r *= ll2;
		c2g *= ll2;
		c2b *= ll2;
		c3r *= ll3;
		c3g *= ll3;
		c3b *= ll3;
		c4r *= ll4;
		c4g *= ll4;
		c4b *= ll4;

		renderEast(tt, (float) pX, (float) pY, (float) pZ, tt->getTexture(level, pX, pY, pZ, 5));
		i = true;
	}
	applyAmbienceOcclusion = false;
	return i;
}

bool TileRenderer::tesselateCactusInWorld(Tile* tt, int x, int y, int z) {
    int col = tt->getColor(level, x, y, z);
    float r = ((col >> 16) & 0xff) / 255.0f;
    float g = ((col >> 8) & 0xff) / 255.0f;
    float b = ((col) & 0xff) / 255.0f;
    return tesselateCactusInWorld(tt, x, y, z, r, g, b);
}

bool TileRenderer::tesselateCactusInWorld(Tile* tt, int x, int y, int z, float r, float g, float b) {
    Tesselator& t = Tesselator::instance;

    bool changed = false;
    float c10 = 0.5f;
    float c11 = 1;
    float c2 = 0.8f;
    float c3 = 0.6f;

    float r10 = c10 * r;
    float r11 = c11 * r;
    float r2 = c2 * r;
    float r3 = c3 * r;

    float g10 = c10 * g;
    float g11 = c11 * g;
    float g2 = c2 * g;
    float g3 = c3 * g;

    float b10 = c10 * b;
    float b11 = c11 * b;
    float b2 = c2 * b;
    float b3 = c3 * b;

    float s = 1 / 16.0f;
	const float X = (float)x;
	const float Y = (float)y;
	const float Z = (float)z;

    float centerBrightness = tt->getBrightness(level, x, y, z);

    if (noCulling || tt->shouldRenderFace(level, x, y - 1, z, 0)) {
        float br = tt->getBrightness(level, x, y - 1, z);
        // if (Tile::lightEmission[tt->id] > br*Level.MAX_BRIGHTNESS) br =
// Tile::lightEmission[tt->id]/Level.MAX_BRIGHTNESS;
        t.color(r10 * br, g10 * br, b10 * br);
        renderFaceDown(tt, X, Y, Z, tt->getTexture(level, x, y, z, 0));
        changed = true;
    }

    if (noCulling || tt->shouldRenderFace(level, x, y + 1, z, 1)) {
        float br = tt->getBrightness(level, x, y + 1, z);
        if (tt->yy1 != 1 && !tt->material->isLiquid()) br = centerBrightness;
        // if (Tile::lightEmission[tt->id] > br*Level.MAX_BRIGHTNESS) br =
// Tile::lightEmission[tt->id]/Level.MAX_BRIGHTNESS;
        t.color(r11 * br, g11 * br, b11 * br);
        renderFaceUp(tt, X, Y, Z, tt->getTexture(level, x, y, z, 1));
        changed = true;
    }

    if (noCulling || tt->shouldRenderFace(level, x, y, z - 1, 2)) {
        float br = tt->getBrightness(level, x, y, z - 1);
        if (tt->zz0 > 0) br = centerBrightness;
        // if (Tile::lightEmission[tt->id] > br*Level.MAX_BRIGHTNESS) br =
// Tile::lightEmission[tt->id]/Level.MAX_BRIGHTNESS;
        t.color(r2 * br, g2 * br, b2 * br);
        t.addOffset(0, 0, s);
        renderNorth(tt, X, Y, Z, tt->getTexture(level, x, y, z, 2));
        t.addOffset(0, 0, -s);
        changed = true;
    }

    if (noCulling || tt->shouldRenderFace(level, x, y, z + 1, 3)) {
        float br = tt->getBrightness(level, x, y, z + 1);
        if (tt->zz1 < 1) br = centerBrightness;
        // if (Tile::lightEmission[tt->id] > br*Level.MAX_BRIGHTNESS) br =
// Tile::lightEmission[tt->id]/Level.MAX_BRIGHTNESS;
        t.color(r2 * br, g2 * br, b2 * br);
        t.addOffset(0, 0, -s);
        renderSouth(tt, X, Y, Z, tt->getTexture(level, x, y, z, 3));
        t.addOffset(0, 0, s);
        changed = true;
    }

    if (noCulling || tt->shouldRenderFace(level, x - 1, y, z, 4)) {
        float br = tt->getBrightness(level, x - 1, y, z);
        if (tt->xx0 > 0) br = centerBrightness;
        // if (Tile::lightEmission[tt->id] > br*Level.MAX_BRIGHTNESS) br =
// Tile::lightEmission[tt->id]/Level.MAX_BRIGHTNESS;
        t.color(r3 * br, g3 * br, b3 * br);
        t.addOffset(s, 0, 0);
        renderWest(tt, X, Y, Z, tt->getTexture(level, x, y, z, 4));
        t.addOffset(-s, 0, 0);
        changed = true;
    }

    if (noCulling || tt->shouldRenderFace(level, x + 1, y, z, 5)) {
        float br = tt->getBrightness(level, x + 1, y, z);
        if (tt->xx1 < 1) br = centerBrightness;
        // if (Tile::lightEmission[tt->id] > br*Level.MAX_BRIGHTNESS) br =
// Tile::lightEmission[tt->id]/Level.MAX_BRIGHTNESS;
        t.color(r3 * br, g3 * br, b3 * br);
        t.addOffset(-s, 0, 0);
        renderEast(tt, X, Y, Z, tt->getTexture(level, x, y, z, 5));
        t.addOffset(s, 0, 0);
        changed = true;
    }

    return changed;
}

bool TileRenderer::tesselateFenceInWorld(FenceTile* tt, int x, int y, int z) {
    bool changed = true;

    float a = 6 / 16.0f;
    float b = 10 / 16.0f;
    tt->setShape(a, 0, a, b, 1, b);
    tesselateBlockInWorld(tt, x, y, z);

    bool vertical = false;
    bool horizontal = false;

	bool l = tt->connectsTo(level, x - 1, y, z);
	bool r = tt->connectsTo(level, x + 1, y, z);
	bool u = tt->connectsTo(level, x, y, z - 1);
	bool d = tt->connectsTo(level, x, y, z + 1);

	if (l || r) vertical = true;
    if (u || d) horizontal = true;

    if (!vertical && !horizontal) vertical = true;

    a = 7 / 16.0f;
    b = 9 / 16.0f;
    float h0 = 12 / 16.0f;
    float h1 = 15 / 16.0f;

    float x0 = l ? 0 : a;
    float x1 = r ? 1 : b;
    float z0 = u ? 0 : a;
    float z1 = d ? 1 : b;

    if (vertical) {
        tt->setShape(x0, h0, a, x1, h1, b);
        tesselateBlockInWorld(tt, x, y, z);
    }
    if (horizontal) {
        tt->setShape(a, h0, z0, b, h1, z1);
        tesselateBlockInWorld(tt, x, y, z);
    }

    h0 = 6 / 16.0f;
    h1 = 9 / 16.0f;
    if (vertical) {
        tt->setShape(x0, h0, a, x1, h1, b);
        tesselateBlockInWorld(tt, x, y, z);
    }
    if (horizontal) {
        tt->setShape(a, h0, z0, b, h1, z1);
        tesselateBlockInWorld(tt, x, y, z);
    }

    tt->setShape(0, 0, 0, 1, 1, 1);
    return changed;
}

bool TileRenderer::tesselateFenceGateInWorld(FenceGateTile* tt, int x, int y, int z) {
	bool changed = true;

	int data = level->getData(x, y, z);
	bool isOpen = FenceGateTile::isOpen(data);
	int direction = FenceGateTile::getDirection(data);

	const float h00 = 6 / 16.0f;
	const float h01 = 9 / 16.0f;
	const float h10 = 12 / 16.0f;
	const float h11 = 15 / 16.0f;
	const float h20 = 5 / 16.0f;
	const float h21 = 16 / 16.0f;

	// edge sticks
	if (direction == Direction::EAST || direction == Direction::WEST) {
		float x0 = 7 / 16.0f;
		float x1 = 9 / 16.0f;
		float z0 = 0 / 16.0f;
		float z1 = 2 / 16.0f;
		tt->setShape(x0, h20, z0, x1, h21, z1);
		tesselateBlockInWorld(tt, x, y, z);

		z0 = 14 / 16.0f;
		z1 = 16 / 16.0f;
		tt->setShape(x0, h20, z0, x1, h21, z1);
		tesselateBlockInWorld(tt, x, y, z);
	} else {
		float x0 = 0 / 16.0f;
		float x1 = 2 / 16.0f;
		float z0 = 7 / 16.0f;
		float z1 = 9 / 16.0f;
		tt->setShape(x0, h20, z0, x1, h21, z1);
		tesselateBlockInWorld(tt, x, y, z);

		x0 = 14 / 16.0f;
		x1 = 16 / 16.0f;
		tt->setShape(x0, h20, z0, x1, h21, z1);
		tesselateBlockInWorld(tt, x, y, z);
	}
	if (!isOpen) {
		if (direction == Direction::EAST || direction == Direction::WEST) {
			float x0 = 7 / 16.0f;
			float x1 = 9 / 16.0f;
			float z0 = 6 / 16.0f;
			float z1 = 8 / 16.0f;
			tt->setShape(x0, h00, z0, x1, h11, z1);
			tesselateBlockInWorld(tt, x, y, z);
			z0 = 8 / 16.0f;
			z1 = 10 / 16.0f;
			tt->setShape(x0, h00, z0, x1, h11, z1);
			tesselateBlockInWorld(tt, x, y, z);
			z0 = 10 / 16.0f;
			z1 = 14 / 16.0f;
			tt->setShape(x0, h00, z0, x1, h01, z1);
			tesselateBlockInWorld(tt, x, y, z);
			tt->setShape(x0, h10, z0, x1, h11, z1);
			tesselateBlockInWorld(tt, x, y, z);
			z0 = 2 / 16.0f;
			z1 = 6 / 16.0f;
			tt->setShape(x0, h00, z0, x1, h01, z1);
			tesselateBlockInWorld(tt, x, y, z);
			tt->setShape(x0, h10, z0, x1, h11, z1);
			tesselateBlockInWorld(tt, x, y, z);
		} else {
			float x0 = 6 / 16.0f;
			float x1 = 8 / 16.0f;
			float z0 = 7 / 16.0f;
			float z1 = 9 / 16.0f;
			tt->setShape(x0, h00, z0, x1, h11, z1);
			tesselateBlockInWorld(tt, x, y, z);
			x0 = 8 / 16.0f;
			x1 = 10 / 16.0f;
			tt->setShape(x0, h00, z0, x1, h11, z1);
			tesselateBlockInWorld(tt, x, y, z);
			x0 = 10 / 16.0f;
			x1 = 14 / 16.0f;
			tt->setShape(x0, h00, z0, x1, h01, z1);
			tesselateBlockInWorld(tt, x, y, z);
			tt->setShape(x0, h10, z0, x1, h11, z1);
			tesselateBlockInWorld(tt, x, y, z);
			x0 = 2 / 16.0f;
			x1 = 6 / 16.0f;
			tt->setShape(x0, h00, z0, x1, h01, z1);
			tesselateBlockInWorld(tt, x, y, z);
			tt->setShape(x0, h10, z0, x1, h11, z1);
			tesselateBlockInWorld(tt, x, y, z);

		}
	} else {
		if (direction == Direction::EAST) {

			const float z00 = 0 / 16.0f;
			const float z01 = 2 / 16.0f;
			const float z10 = 14 / 16.0f;
			const float z11 = 16 / 16.0f;

			const float x0 = 9 / 16.0f;
			const float x1 = 13 / 16.0f;
			const float x2 = 15 / 16.0f;

			tt->setShape(x1, h00, z00, x2, h11, z01);
			tesselateBlockInWorld(tt, x, y, z);
			tt->setShape(x1, h00, z10, x2, h11, z11);
			tesselateBlockInWorld(tt, x, y, z);

			tt->setShape(x0, h00, z00, x1, h01, z01);
			tesselateBlockInWorld(tt, x, y, z);
			tt->setShape(x0, h00, z10, x1, h01, z11);
			tesselateBlockInWorld(tt, x, y, z);

			tt->setShape(x0, h10, z00, x1, h11, z01);
			tesselateBlockInWorld(tt, x, y, z);
			tt->setShape(x0, h10, z10, x1, h11, z11);
			tesselateBlockInWorld(tt, x, y, z);
		} else if (direction == Direction::WEST) {
			const float z00 = 0 / 16.0f;
			const float z01 = 2 / 16.0f;
			const float z10 = 14 / 16.0f;
			const float z11 = 16 / 16.0f;

			const float x0 = 1 / 16.0f;
			const float x1 = 3 / 16.0f;
			const float x2 = 7 / 16.0f;

			tt->setShape(x0, h00, z00, x1, h11, z01);
			tesselateBlockInWorld(tt, x, y, z);
			tt->setShape(x0, h00, z10, x1, h11, z11);
			tesselateBlockInWorld(tt, x, y, z);

			tt->setShape(x1, h00, z00, x2, h01, z01);
			tesselateBlockInWorld(tt, x, y, z);
			tt->setShape(x1, h00, z10, x2, h01, z11);
			tesselateBlockInWorld(tt, x, y, z);

			tt->setShape(x1, h10, z00, x2, h11, z01);
			tesselateBlockInWorld(tt, x, y, z);
			tt->setShape(x1, h10, z10, x2, h11, z11);
			tesselateBlockInWorld(tt, x, y, z);
		} else if (direction == Direction::SOUTH) {

			const float x00 = 0 / 16.0f;
			const float x01 = 2 / 16.0f;
			const float x10 = 14 / 16.0f;
			const float x11 = 16 / 16.0f;

			const float z0 = 9 / 16.0f;
			const float z1 = 13 / 16.0f;
			const float z2 = 15 / 16.0f;

			tt->setShape(x00, h00, z1, x01, h11, z2);
			tesselateBlockInWorld(tt, x, y, z);
			tt->setShape(x10, h00, z1, x11, h11, z2);
			tesselateBlockInWorld(tt, x, y, z);

			tt->setShape(x00, h00, z0, x01, h01, z1);
			tesselateBlockInWorld(tt, x, y, z);
			tt->setShape(x10, h00, z0, x11, h01, z1);
			tesselateBlockInWorld(tt, x, y, z);

			tt->setShape(x00, h10, z0, x01, h11, z1);
			tesselateBlockInWorld(tt, x, y, z);
			tt->setShape(x10, h10, z0, x11, h11, z1);
			tesselateBlockInWorld(tt, x, y, z);
		} else if (direction == Direction::NORTH) {
			const float x00 = 0 / 16.0f;
			const float x01 = 2 / 16.0f;
			const float x10 = 14 / 16.0f;
			const float x11 = 16 / 16.0f;

			const float z0 = 1 / 16.0f;
			const float z1 = 3 / 16.0f;
			const float z2 = 7 / 16.0f;

			tt->setShape(x00, h00, z0, x01, h11, z1);
			tesselateBlockInWorld(tt, x, y, z);
			tt->setShape(x10, h00, z0, x11, h11, z1);
			tesselateBlockInWorld(tt, x, y, z);

			tt->setShape(x00, h00, z1, x01, h01, z2);
			tesselateBlockInWorld(tt, x, y, z);
			tt->setShape(x10, h00, z1, x11, h01, z2);
			tesselateBlockInWorld(tt, x, y, z);

			tt->setShape(x00, h10, z1, x01, h11, z2);
			tesselateBlockInWorld(tt, x, y, z);
			tt->setShape(x10, h10, z1, x11, h11, z2);
			tesselateBlockInWorld(tt, x, y, z);
		}
	}

	tt->setShape(0, 0, 0, 1, 1, 1);
	return changed;
}

bool TileRenderer::tesselateBedInWorld(Tile *tt, int x, int y, int z) {
	Tesselator& t = Tesselator::instance;
	int data = level->getData(x, y, z);
	int direction = BedTile::getDirection(data);
	bool isHead = BedTile::isHeadPiece(data);

	float c10 = 0.5f;
	float c11 = 1;
	float c2 = 0.8f;
	float c3 = 0.6f;

	float r11 = c11;
	float g11 = c11;
	float b11 = c11;

	float r10 = c10;
	float r2 = c2;
	float r3 = c3;

	float g10 = c10;
	float g2 = c2;
	float g3 = c3;

	float b10 = c10;
	float b2 = c2;
	float b3 = c3;

	float centerBrightness = tt->getBrightness(level, x, y, z);
	// render wooden underside
	{
		t.color(r10 * centerBrightness, g10 * centerBrightness, b10 * centerBrightness);
		int tex = tt->getTexture(level, x, y, z, Facing::DOWN);

		int xt = (tex & 0xf) << 4;
		int yt = tex & 0xf0;

		float u0 = (xt) / 256.0f;
		float u1 = (xt + 16 - 0.01f) / 256.0f;
		float v0 = (yt) / 256.0f;
		float v1 = (yt + 16 - 0.01f) / 256.0f;

		float x0 = x + tt->xx0;
		float x1 = x + tt->xx1;
		float y0 = y + tt->yy0 + 3.0f / 16.0f;
		float z0 = z + tt->zz0;
		float z1 = z + tt->zz1;

		t.vertexUV(x0, y0, z1, u0, v1);
		t.vertexUV(x0, y0, z0, u0, v0);
		t.vertexUV(x1, y0, z0, u1, v0);
		t.vertexUV(x1, y0, z1, u1, v1);
	}

	// render bed top

	float brightness = tt->getBrightness(level, x, y + 1, z);
	t.color(r11 * brightness, g11 * brightness, b11 * brightness);

	int tex = tt->getTexture(level, x, y, z, Facing::UP);

	int xt = (tex & 0xf) << 4;
	int yt = tex & 0xf0;

	float u0 = (xt) / 256.0f;
	float u1 = (xt + 16 ) / 256.0f;
	float v0 = (yt) / 256.0f;
	float v1 = (yt + 16) / 256.0f;

	// Default is west
	float topLeftU = u0;
	float topRightU = u1;
	float topLeftV = v0;
	float topRightV = v0;
	float bottomLeftU = u0;
	float bottomRightU = u1;
	float bottomLeftV = v1;
	float bottomRightV = v1;

	if (direction == Direction::SOUTH) {
		// rotate 90 degrees clockwise
		topRightU = u0;
		topLeftV = v1;
		bottomLeftU = u1;
		bottomRightV = v0;
	} else if (direction == Direction::NORTH) {
		// rotate 90 degrees counter-clockwise
		topLeftU = u1;
		topRightV = v1;
		bottomRightU = u0;
		bottomLeftV = v0;
	} else if (direction == Direction::EAST) {
		// rotate 180 degrees
		topLeftU = u1;
		topRightV = v1;
		bottomRightU = u0;
		bottomLeftV = v0;
		topRightU = u0;
		topLeftV = v1;
		bottomLeftU = u1;
		bottomRightV = v0;
	}

	float x0 = x + tt->xx0;
	float x1 = x + tt->xx1;
	float y1 = y + tt->yy1;
	float z0 = z + tt->zz0;
	float z1 = z + tt->zz1;

	t.vertexUV(x1, y1, z1, bottomLeftU, bottomLeftV);
	t.vertexUV(x1, y1, z0, topLeftU, topLeftV);
	t.vertexUV(x0, y1, z0, topRightU, topRightV);
	t.vertexUV(x0, y1, z1, bottomRightU, bottomRightV);

	// determine which edge to skip (the one between foot and head piece)
	int skipEdge = Direction::DIRECTION_FACING[direction];
	if (isHead) {
		skipEdge = Direction::DIRECTION_FACING[Direction::DIRECTION_OPPOSITE[direction]];
	}
	// and which edge to x-flip
	int flipEdge = Facing::WEST;
	switch (direction) {
	case Direction::NORTH:
		break;
	case Direction::SOUTH:
		flipEdge = Facing::EAST;
		break;
	case Direction::EAST:
		flipEdge = Facing::NORTH;
		break;
	case Direction::WEST:
		flipEdge = Facing::SOUTH;
		break;
	}

	if ((skipEdge != Facing::NORTH) && (noCulling || tt->shouldRenderFace(level, x, y, z - 1, Facing::NORTH))) {
		float br = tt->getBrightness(level, x, y, z - 1);
		if (tt->zz0 > 0) br = centerBrightness;

		t.color(r2 * br, g2 * br, b2 * br);
		xFlipTexture = flipEdge == Facing::NORTH;
		renderNorth(tt, float(x), float(y), float(z), tt->getTexture(level, x, y, z, 2));
	}

	if ((skipEdge != Facing::SOUTH) && (noCulling || tt->shouldRenderFace(level, x, y, z + 1, Facing::SOUTH))) {
		float br = tt->getBrightness(level, x, y, z + 1);
		if (tt->zz1 < 1) br = centerBrightness;

		t.color(r2 * br, g2 * br, b2 * br);

		xFlipTexture = flipEdge == Facing::SOUTH;
		renderSouth(tt, float(x), float(y), float(z), tt->getTexture(level, x, y, z, 3));
	}

	if ((skipEdge != Facing::WEST) && (noCulling || tt->shouldRenderFace(level, x - 1, y, z, Facing::WEST))) {
		float br = tt->getBrightness(level, x - 1, y, z);
		if (tt->xx0 > 0) br = centerBrightness;

		t.color(r3 * br, g3 * br, b3 * br);
		xFlipTexture = flipEdge == Facing::WEST;
		renderWest(tt, float(x), float(y), float(z), tt->getTexture(level, x, y, z, 4));
	}

	if ((skipEdge != Facing::EAST) && (noCulling || tt->shouldRenderFace(level, x + 1, y, z, Facing::EAST))) {
		float br = tt->getBrightness(level, x + 1, y, z);
		if (tt->xx1 < 1) br = centerBrightness;

		t.color(r3 * br, g3 * br, b3 * br);
		xFlipTexture = flipEdge == Facing::EAST;
		renderEast(tt, float(x), float(y), float(z), tt->getTexture(level, x, y, z, 5));
	}
	xFlipTexture = false;
	return true;
}

bool TileRenderer::tesselateStairsInWorld( StairTile* tt, int x, int y, int z )
{

	tt->setBaseShape(level, x, y, z);
	tesselateBlockInWorld(tt, x, y, z);

	bool checkInnerPiece = tt->setStepShape(level, x, y, z);
	tesselateBlockInWorld(tt, x, y, z);

	if (checkInnerPiece) {
		if (tt->setInnerPieceShape(level, x, y, z)) {
			tesselateBlockInWorld(tt, x, y, z);
		}
	}

	//        setShape(0, 0, 0, 1, 1, 1);
	return true;
}

bool TileRenderer::tesselateDoorInWorld( Tile* tt, int x, int y, int z )
{
	Tesselator& t = Tesselator::instance;

	DoorTile* dt = (DoorTile*) tt;

	bool changed = false;
	float c10 = 0.5f;
	float c11 = 1;
	float c2 = 0.8f;
	float c3 = 0.6f;

	float centerBrightness = tt->getBrightness(level, x, y, z);

	{
		float br = tt->getBrightness(level, x, y - 1, z);
		if (dt->yy0 > 0) br = centerBrightness;
		if (Tile::lightEmission[tt->id] > 0) br = 1.0f;
		t.color(c10 * br, c10 * br, c10 * br);
		renderFaceDown(tt, (float)x, (float)y, (float)z, tt->getTexture(level, x, y, z, 0));
		changed = true;
	}

	{
		float br = tt->getBrightness(level, x, y + 1, z);
		if (dt->yy1 < 1) br = centerBrightness;
		if (Tile::lightEmission[tt->id] > 0) br = 1.0f;
		t.color(c11 * br, c11 * br, c11 * br);
		renderFaceUp(tt, (float)x, (float)y, (float)z, tt->getTexture(level, x, y, z, 1));
		changed = true;
	}

	{
		float br = tt->getBrightness(level, x, y, z - 1);
		if (dt->zz0 > 0) br = centerBrightness;
		if (Tile::lightEmission[tt->id] > 0) br = 1.0f;
		t.color(c2 * br, c2 * br, c2 * br);
		int tex = tt->getTexture(level, x, y, z, 2);
		if (tex < 0) {
			xFlipTexture = true;
			tex = -tex;
		}
		renderNorth(tt, (float)x, (float)y, (float)z, tex);
		changed = true;
		xFlipTexture = false;
	}

	{
		float br = tt->getBrightness(level, x, y, z + 1);
		if (dt->zz1 < 1) br = centerBrightness;
		if (Tile::lightEmission[tt->id] > 0) br = 1.0f;
		t.color(c2 * br, c2 * br, c2 * br);
		int tex = tt->getTexture(level, x, y, z, 3);
		if (tex < 0) {
			xFlipTexture = true;
			tex = -tex;
		}
		renderSouth(tt, (float)x, (float)y, (float)z, tex);
		changed = true;
		xFlipTexture = false;
	}

	{
		float br = tt->getBrightness(level, x - 1, y, z);
		if (dt->xx0 > 0) br = centerBrightness;
		if (Tile::lightEmission[tt->id] > 0) br = 1.0f;
		t.color(c3 * br, c3 * br, c3 * br);
		int tex = tt->getTexture(level, x, y, z, 4);
		if (tex < 0) {
			xFlipTexture = true;
			tex = -tex;
		}
		renderWest(tt, (float)x, (float)y, (float)z, tex);
		changed = true;
		xFlipTexture = false;
	}

	{
		float br = tt->getBrightness(level, x + 1, y, z);
		if (dt->xx1 < 1) br = centerBrightness;
		if (Tile::lightEmission[tt->id] > 0) br = 1.0f;
		t.color(c3 * br, c3 * br, c3 * br);
		int tex = tt->getTexture(level, x, y, z, 5);
		if (tex < 0) {
			xFlipTexture = true;
			tex = -tex;
		}
		renderEast(tt, (float)x, (float)y, (float)z, tex);
		changed = true;
		xFlipTexture = false;
	}

	return changed;
}

bool TileRenderer::tesselateRowInWorld( Tile* tt, int x, int y, int z ) {
	Tesselator& t = Tesselator::instance;
	float br = tt->getBrightness(level, x, y, z);
	t.color(br, br, br);
	tesselateRowTexture(tt, level->getData(x, y, z), float(x), y - 1 / 16.0f, float(z));
	return true;
}

void TileRenderer::renderFaceDown( Tile* tt, float x, float y, float z, int tex )
{
	Tesselator& t = Tesselator::instance;

	if (fixedTexture >= 0) tex = fixedTexture;
	int xt = (tex & 0xf) << 4;
	int yt = tex & 0xf0;

	float u0 = (xt + tt->xx0 * 16) / 256.0f;
	float u1 = (xt + tt->xx1 * 16 - 0.01f) / 256.0f;
	float v0 = (yt + tt->zz0 * 16) / 256.0f;
	float v1 = (yt + tt->zz1 * 16 - 0.01f) / 256.0f;

	if (tt->xx0 < 0 || tt->xx1 > 1) {
		u0 = (xt + 0 * 15.99f) / 256.0f;
		u1 = (xt + 1 * 15.99f) / 256.0f;
	}
	if (tt->zz0 < 0 || tt->zz1 > 1) {
		v0 = (yt + 0 * 15.99f) / 256.0f;
		v1 = (yt + 1 * 15.99f) / 256.0f;
	}

	float x0 = x + tt->xx0;
	float x1 = x + tt->xx1;
	float y0 = y + tt->yy0;
	float z0 = z + tt->zz0;
	float z1 = z + tt->zz1;

	if (applyAmbienceOcclusion) {
		t.color(c1r, c1g, c1b);
		t.vertexUV(x0, y0, z1, u0, v1);
		t.color(c2r, c2g, c2b);
		t.vertexUV(x0, y0, z0, u0, v0);
		t.color(c3r, c3g, c3b);
		t.vertexUV(x1, y0, z0, u1, v0);
		t.color(c4r, c4g, c4b);
		t.vertexUV(x1, y0, z1, u1, v1);
	} else {
		t.vertexUV(x0, y0, z1, u0, v1);
		t.vertexUV(x0, y0, z0, u0, v0);
		t.vertexUV(x1, y0, z0, u1, v0);
		t.vertexUV(x1, y0, z1, u1, v1);
	}
}

void TileRenderer::renderFaceUp( Tile* tt, float x, float y, float z, int tex )
{
	Tesselator& t = Tesselator::instance;

	if (fixedTexture >= 0) tex = fixedTexture;
	int xt = (tex & 0xf) << 4;
	int yt = tex & 0xf0;

	float u0 = (xt + tt->xx0 * 16) / 256.0f;
	float u1 = (xt + tt->xx1 * 16 - 0.01f) / 256.0f;
	float v0 = (yt + tt->zz0 * 16) / 256.0f;
	float v1 = (yt + tt->zz1 * 16 - 0.01f) / 256.0f;

	if (tt->xx0 < 0 || tt->xx1 > 1) {
		u0 = (xt + 0 * 15.99f) / 256.0f;
		u1 = (xt + 1 * 15.99f) / 256.0f;
	}
	if (tt->zz0 < 0 || tt->zz1 > 1) {
		v0 = (yt + 0 * 15.99f) / 256.0f;
		v1 = (yt + 1 * 15.99f) / 256.0f;
	}

	float x0 = x + tt->xx0;
	float x1 = x + tt->xx1;
	float y1 = y + tt->yy1;
	float z0 = z + tt->zz0;
	float z1 = z + tt->zz1;

	if (applyAmbienceOcclusion) {
		t.color(c1r, c1g, c1b);
		t.vertexUV(x1, y1, z1, u1, v1);
		t.color(c2r, c2g, c2b);
		t.vertexUV(x1, y1, z0, u1, v0);
		t.color(c3r, c3g, c3b);
		t.vertexUV(x0, y1, z0, u0, v0);
		t.color(c4r, c4g, c4b);
		t.vertexUV(x0, y1, z1, u0, v1);
	} else {
		t.vertexUV(x1, y1, z1, u1, v1);
		t.vertexUV(x1, y1, z0, u1, v0);
		t.vertexUV(x0, y1, z0, u0, v0);
		t.vertexUV(x0, y1, z1, u0, v1);
	}
}

void TileRenderer::renderNorth( Tile* tt, float x, float y, float z, int tex )
{
	Tesselator& t = Tesselator::instance;

	if (fixedTexture >= 0) tex = fixedTexture;
	int xt = (tex & 0xf) << 4;
	int yt = tex & 0xf0;
	float u0 = (xt + tt->xx0 * 16) / 256.0f;
	float u1 = (xt + tt->xx1 * 16 - 0.01f) / 256.0f;
	float v0 = (yt + 16 - tt->yy1 * 16) / 256.0f;
	float v1 = (yt + 16 - tt->yy0 * 16 - 0.01f) / 256.0f;
	if (xFlipTexture) {
		float tmp = u0;
		u0 = u1;
		u1 = tmp;
	}

	if (tt->xx0 < 0 || tt->xx1 > 1) {
		u0 = (xt + 0 * 15.99f) / 256.0f;
		u1 = (xt + 1 * 15.99f) / 256.0f;
	}
	if (tt->yy0 < 0 || tt->yy1 > 1) {
		v0 = (yt + 0 * 15.99f) / 256.0f;
		v1 = (yt + 1 * 15.99f) / 256.0f;
	}

	float x0 = x + tt->xx0;
	float x1 = x + tt->xx1;
	float y0 = y + tt->yy0;
	float y1 = y + tt->yy1;
	float z0 = z + tt->zz0;

	if (applyAmbienceOcclusion) {
		t.color(c1r, c1g, c1b);
		t.vertexUV(x0, y1, z0, u1, v0);
		t.color(c2r, c2g, c2b);
		t.vertexUV(x1, y1, z0, u0, v0);
		t.color(c3r, c3g, c3b);
		t.vertexUV(x1, y0, z0, u0, v1);
		t.color(c4r, c4g, c4b);
		t.vertexUV(x0, y0, z0, u1, v1);
	} else {
		t.vertexUV(x0, y1, z0, u1, v0);
		t.vertexUV(x1, y1, z0, u0, v0);
		t.vertexUV(x1, y0, z0, u0, v1);
		t.vertexUV(x0, y0, z0, u1, v1);
	}
}

void TileRenderer::renderSouth( Tile* tt, float x, float y, float z, int tex )
{
	Tesselator& t = Tesselator::instance;

	if (fixedTexture >= 0) tex = fixedTexture;
	int xt = (tex & 0xf) << 4;
	int yt = tex & 0xf0;

	float u0 = (xt + tt->xx0 * 16) / 256.0f;
	float u1 = (xt + tt->xx1 * 16 - 0.01f) / 256.0f;
	float v0 = (yt + 16 - tt->yy1 * 16) / 256.0f;
	float v1 = (yt + 16 - tt->yy0 * 16 - 0.01f) / 256.0f;
	if (xFlipTexture) {
		float tmp = u0;
		u0 = u1;
		u1 = tmp;
	}

	if (tt->xx0 < 0 || tt->xx1 > 1) {
		u0 = (xt + 0 * 15.99f) / 256.0f;
		u1 = (xt + 1 * 15.99f) / 256.0f;
	}
	if (tt->yy0 < 0 || tt->yy1 > 1) {
		v0 = (yt + 0 * 15.99f) / 256.0f;
		v1 = (yt + 1 * 15.99f) / 256.0f;
	}

	float x0 = x + tt->xx0;
	float x1 = x + tt->xx1;
	float y0 = y + tt->yy0;
	float y1 = y + tt->yy1;
	float z1 = z + tt->zz1;

	if (applyAmbienceOcclusion) {
		t.color(c1r, c1g, c1b);
		t.vertexUV(x0, y1, z1, u0, v0);
		t.color(c2r, c2g, c2b);
		t.vertexUV(x0, y0, z1, u0, v1);
		t.color(c3r, c3g, c3b);
		t.vertexUV(x1, y0, z1, u1, v1);
		t.color(c4r, c4g, c4b);
		t.vertexUV(x1, y1, z1, u1, v0);
	} else {
		t.vertexUV(x0, y1, z1, u0, v0);
		t.vertexUV(x0, y0, z1, u0, v1);
		t.vertexUV(x1, y0, z1, u1, v1);
		t.vertexUV(x1, y1, z1, u1, v0);
	}
}

void TileRenderer::renderWest( Tile* tt, float x, float y, float z, int tex )
{
	Tesselator& t = Tesselator::instance;

	if (fixedTexture >= 0) tex = fixedTexture;
	int xt = (tex & 0xf) << 4;
	int yt = tex & 0xf0;

	float u0 = (xt + tt->zz0 * 16) / 256.0f;
	float u1 = (xt + tt->zz1 * 16 - 0.01f) / 256.0f;
	float v0 = (yt + 16 - tt->yy1 * 16) / 256.0f;
	float v1 = (yt + 16 - tt->yy0 * 16 - 0.01f) / 256.0f;
	if (xFlipTexture) {
		float tmp = u0;
		u0 = u1;
		u1 = tmp;
	}

	if (tt->zz0 < 0 || tt->zz1 > 1) {
		u0 = (xt + 0 * 15.99f) / 256.0f;
		u1 = (xt + 1 * 15.99f) / 256.0f;
	}
	if (tt->yy0 < 0 || tt->yy1 > 1) {
		v0 = (yt + 0 * 15.99f) / 256.0f;
		v1 = (yt + 1 * 15.99f) / 256.0f;
	}

	float x0 = x + tt->xx0;
	float y0 = y + tt->yy0;
	float y1 = y + tt->yy1;
	float z0 = z + tt->zz0;
	float z1 = z + tt->zz1;

	if (applyAmbienceOcclusion) {
		t.color(c1r, c1g, c1b);
		t.vertexUV(x0, y1, z1, u1, v0);
		t.color(c2r, c2g, c2b);
		t.vertexUV(x0, y1, z0, u0, v0);
		t.color(c3r, c3g, c3b);
		t.vertexUV(x0, y0, z0, u0, v1);
		t.color(c4r, c4g, c4b);
		t.vertexUV(x0, y0, z1, u1, v1);
	} else {
		t.vertexUV(x0, y1, z1, u1, v0);
		t.vertexUV(x0, y1, z0, u0, v0);
		t.vertexUV(x0, y0, z0, u0, v1);
		t.vertexUV(x0, y0, z1, u1, v1);
	}
}

void TileRenderer::renderEast( Tile* tt, float x, float y, float z, int tex )
{
	Tesselator& t = Tesselator::instance;

	if (fixedTexture >= 0) tex = fixedTexture;
	int xt = (tex & 0xf) << 4;
	int yt = tex & 0xf0;

	float u0 = (xt + tt->zz0 * 16) / 256.0f;
	float u1 = (xt + tt->zz1 * 16 - 0.01f) / 256.0f;
	float v0 = (yt + 16 - tt->yy1 * 16) / 256.0f;
	float v1 = (yt + 16 - tt->yy0 * 16 - 0.01f) / 256.0f;
	if (xFlipTexture) {
		float tmp = u0;
		u0 = u1;
		u1 = tmp;
	}

	if (tt->zz0 < 0 || tt->zz1 > 1) {
		u0 = (xt + 0 * 15.99f) / 256.0f;
		u1 = (xt + 1 * 15.99f) / 256.0f;
	}
	if (tt->yy0 < 0 || tt->yy1 > 1) {
		v0 = (yt + 0 * 15.99f) / 256.0f;
		v1 = (yt + 1 * 15.99f) / 256.0f;
	}

	float x1 = x + tt->xx1;
	float y0 = y + tt->yy0;
	float y1 = y + tt->yy1;
	float z0 = z + tt->zz0;
	float z1 = z + tt->zz1;

	if (applyAmbienceOcclusion) {
		t.color(c1r, c1g, c1b);
		t.vertexUV(x1, y0, z1, u0, v1);
		t.color(c2r, c2g, c2b);
		t.vertexUV(x1, y0, z0, u1, v1);
		t.color(c3r, c3g, c3b);
		t.vertexUV(x1, y1, z0, u1, v0);
		t.color(c4r, c4g, c4b);
		t.vertexUV(x1, y1, z1, u0, v0);
	} else {
		t.vertexUV(x1, y0, z1, u0, v1);
		t.vertexUV(x1, y0, z0, u1, v1);
		t.vertexUV(x1, y1, z0, u1, v0);
		t.vertexUV(x1, y1, z1, u0, v0);
	}
}

void TileRenderer::renderTile( Tile* tile, int data )
{
	Tesselator& t = Tesselator::instance;

	t.color(0xff, 0xff, 0xff);
	int shape = tile->getRenderShape();

	if (shape == Tile::SHAPE_BLOCK) {
		tile->updateDefaultShape();
		t.addOffset(-0.5f, -0.5f, -0.5f);
		t.begin();
		renderFaceDown(tile, 0, 0, 0, tile->getTexture(0, data));
		renderFaceUp(tile, 0, 0, 0, tile->getTexture(1, data));
		renderNorth(tile, 0, 0, 0, tile->getTexture(2, data));
		renderSouth(tile, 0, 0, 0, tile->getTexture(3, data));
		renderWest(tile, 0, 0, 0, tile->getTexture(4, data));
		renderEast(tile, 0, 0, 0, tile->getTexture(5, data));
		t.draw();

		t.addOffset(0.5f, 0.5f, 0.5f);

	} else if (shape == Tile::SHAPE_CROSS_TEXTURE) {
		t.begin();
		tesselateCrossTexture(tile, data, -0.5f, -0.5f, -0.5f);
		t.draw();
	} else if(shape == Tile::SHAPE_STEM) {
		t.begin();
		tile->updateDefaultShape();
		tesselateStemTexture(tile, data, tile->yy1, -0.5f, -0.5f, -0.5f);
		t.draw();
	} else if (shape == Tile::SHAPE_CACTUS) {
		tile->updateDefaultShape();
		t.offset(-0.5f, -0.5f, -0.5f);
		float s = 1 / 16.0f;
		t.begin();
		renderFaceDown(tile, 0, 0, 0, tile->getTexture(0));
		renderFaceUp(tile, 0, 0, 0, tile->getTexture(1));
		t.addOffset(0, 0, s);
		renderNorth(tile, 0, 0, 0, tile->getTexture(2));
		t.addOffset(0, 0, -s);
		t.addOffset(0, 0, -s);
		renderSouth(tile, 0, 0, 0, tile->getTexture(3));
		t.addOffset(0, 0, s);
		t.addOffset(s, 0, 0);
		renderWest(tile, 0, 0, 0, tile->getTexture(4));
		t.addOffset(-s, 0, 0);
		t.addOffset(-s, 0, 0);
		renderEast(tile, 0, 0, 0, tile->getTexture(5));
		t.addOffset(s, 0, 0);
		t.draw();
		t.offset(0, 0, 0);//0.5f, 0.5f, 0.5f);
		} else if (shape == Tile::SHAPE_ROWS) {
			t.begin();
			t.normal(0, -1, 0);
			tesselateRowTexture(tile, data, -0.5f, -0.5f, -0.5f);
		//}  else if (shape == Tile::SHAPE_TORCH) {
		////    t.begin();
		////    t.normal(0, -1, 0);
		////    tesselateTorch(tile, -0.5f, -0.5f, -0.5f, 0, 0);
		////    t.end();
        } else if (shape == Tile::SHAPE_ENTITYTILE_ANIMATED) {
            EntityTileRenderer::instance->render(tile, data, 1.0f);
            //glEnable(GL_RESCALE_NORMAL);
		} else if (shape == Tile::SHAPE_STAIRS) {
		    t.addOffset(-0.5f, -0.5f, -0.5f);
			t.begin();
			for (int i = 0; i < 2; i++) {
		        if (i == 0) tile->setShape(0, 0, 0, 1, 1, 0.5f);
		        if (i == 1) tile->setShape(0, 0, 0.5f, 1, 0.5f, 1);

		        renderFaceDown(tile, 0, 0, 0, tile->getTexture(0));
		        renderFaceUp(tile, 0, 0, 0, tile->getTexture(1));
		        renderNorth(tile, 0, 0, 0, tile->getTexture(2));
		        renderSouth(tile, 0, 0, 0, tile->getTexture(3));
		        renderWest(tile, 0, 0, 0, tile->getTexture(4));
		        renderEast(tile, 0, 0, 0, tile->getTexture(5));
		    }
			t.draw();
			t.addOffset(0.5f, 0.5f, 0.5f);
		}
		else if (shape == Tile::SHAPE_FENCE) {
			t.addOffset(-0.5f, -0.5f, -0.5f);
			t.begin();
			for (int i = 0; i < 4; i++) {
				float w = 2 / 16.0f;
				if (i == 0) tile->setShape(0.5f - w, 0, 0, 0.5f + w, 1, w * 2);
				if (i == 1) tile->setShape(0.5f - w, 0, 1 - w * 2, 0.5f + w, 1, 1);
				w = 1 / 16.0f;
				if (i == 2) tile->setShape(0.5f - w, 1 - w * 3, -w * 2, 0.5f + w, 1 - w, 1 + w * 2);
				if (i == 3) tile->setShape(0.5f - w, 0.5f - w * 3, -w * 2, 0.5f + w, 0.5f - w, 1 + w * 2);

				renderFaceDown(tile, 0, 0, 0, tile->getTexture(0));
				renderFaceUp(tile, 0, 0, 0, tile->getTexture(1));
				renderNorth(tile, 0, 0, 0, tile->getTexture(2));
				renderSouth(tile, 0, 0, 0, tile->getTexture(3));
				renderWest(tile, 0, 0, 0, tile->getTexture(4));
				renderEast(tile, 0, 0, 0, tile->getTexture(5));
			}
			t.draw();
			t.addOffset(0.5f, 0.5f, 0.5f);
			tile->setShape(0, 0, 0, 1, 1, 1);
		} else if (shape == Tile::SHAPE_FENCE_GATE) {
			t.addOffset(-0.5f, -0.5f, -0.5f);
			t.begin();
			for (int i = 0; i < 3; i++) {
				float w = 1 / 16.0f;
				if (i == 0) tile->setShape(0.5f - w, .3f, 0, 0.5f + w, 1, w * 2);
				if (i == 1) tile->setShape(0.5f - w, .3f, 1 - w * 2, 0.5f + w, 1, 1);
				if (i == 2) tile->setShape(0.5f - w, .5f, w * 2, 0.5f + w, 1 - w, 1 - w * 2);

				renderFaceUp(tile, 0, 0, 0, tile->getTexture(0));
				renderFaceDown(tile, 0, 0, 0, tile->getTexture(1));
				renderNorth(tile, 0, 0, 0, tile->getTexture(2));
				renderSouth(tile, 0, 0, 0, tile->getTexture(3));
				renderWest(tile, 0, 0, 0, tile->getTexture(4));
				renderEast(tile, 0, 0, 0, tile->getTexture(5));
			}
			t.draw();
			t.addOffset(0.5f, 0.5f, 0.5f);
			tile->setShape(0, 0, 0, 1, 1, 1);
		}

}

bool TileRenderer::canRender( int renderShape )
{
	if (renderShape == Tile::SHAPE_BLOCK) return true;
	if (renderShape == Tile::SHAPE_CACTUS) return true;
	if (renderShape == Tile::SHAPE_STAIRS) return true;
	if (renderShape == Tile::SHAPE_FENCE) return true;
	if (renderShape == Tile::SHAPE_FENCE_GATE) return true;
	//if (renderShape == Tile::SHAPE_CROSS_TEXTURE) return true;
	//if (renderShape == Tile::SHAPE_ENTITYTILE_ANIMATED) return true;

	return false;
}

void TileRenderer::renderGuiTile( Tile* tile, int data )
{
	Tesselator& t = Tesselator::instance;

	int shape = tile->getRenderShape();

	if (shape == Tile::SHAPE_BLOCK) {
		tile->updateDefaultShape();

		t.begin();
		t.addOffset(-0.5f, -0.5f, -0.5f);
		t.color(0xff, 0xff, 0xff);
		renderFaceUp(tile, 0, 0, 0, tile->getTexture(1, data));
		renderFaceDown(tile, 0, 0, 0, tile->getTexture(0, data));

		t.color(0x80, 0x80, 0x80);
		renderNorth(tile, 0, 0, 0, tile->getTexture(2, data));
		renderSouth(tile, 0, 0, 0, tile->getTexture(3, data));

		t.color(0xbb, 0xbb, 0xbb);
		renderEast(tile, 0, 0, 0, tile->getTexture(5, data));
		renderWest(tile, 0, 0, 0, tile->getTexture(4, data));
		t.draw();
		t.addOffset(0.5f, 0.5f, 0.5f);

	} else if (shape == Tile::SHAPE_CROSS_TEXTURE) {
		t.begin();
		//t.normal(0, -1, 0);
		tesselateCrossTexture(tile, data, -0.5f, -0.5f, -0.5f);
		//t.end();
		t.draw();
	} else if (shape == Tile::SHAPE_CACTUS) {
		tile->updateDefaultShape();
		t.begin();
		t.offset(-0.5f, -0.5f, -0.5f);
		float s = 1 / 16.0f;
		t.color(0xff, 0xff, 0xff);
		renderFaceDown(tile, 0, 0, 0, tile->getTexture(0));
		renderFaceUp(tile, 0, 0, 0, tile->getTexture(1));

		t.color(0x80, 0x80, 0x80);
		t.addOffset(0, 0, s);
		renderNorth(tile, 0, 0, 0, tile->getTexture(2));
		t.addOffset(0, 0, -s-s);
		renderSouth(tile, 0, 0, 0, tile->getTexture(3));

		t.color(0xbb, 0xbb, 0xbb);
		t.addOffset(s, 0, s);
		renderWest(tile, 0, 0, 0, tile->getTexture(4));
		t.addOffset(-s-s, 0, 0);
		renderEast(tile, 0, 0, 0, tile->getTexture(5));

		t.draw();
		t.addOffset(s+0.5f, 0.5f, 0.5f);
	} else if (shape == Tile::SHAPE_STAIRS) {
		t.offset(-0.5f, -0.5f, -0.5f);
		t.begin();
		for (int i = 0; i < 2; i++) {
			if (i == 0) tile->setShape(0, 0, 0, 1, 1, 0.5f);
			if (i == 1) tile->setShape(0, 0, 0.5f, 1, 0.5f, 1);

			t.color(0xff, 0xff, 0xff);
			renderFaceDown(tile, 0, 0, 0, tile->getTexture(0));
			renderFaceUp(tile, 0, 0, 0, tile->getTexture(1));

			t.color(0x80, 0x80, 0x80);
			renderNorth(tile, 0, 0, 0, tile->getTexture(2));
			renderSouth(tile, 0, 0, 0, tile->getTexture(3));

			t.color(0xbb, 0xbb, 0xbb);
			renderWest(tile, 0, 0, 0, tile->getTexture(4));
			renderEast(tile, 0, 0, 0, tile->getTexture(5));
		}
		t.draw();
		t.offset(0, 0, 0);
	}
	else if (shape == Tile::SHAPE_FENCE) {
		t.addOffset(-0.5f, -0.5f, -0.5f);
		t.begin();
		for (int i = 0; i < 4; i++) {
			float w = 2 / 16.0f;
			if (i == 0) tile->setShape(0.5f - w, 0, 0, 0.5f + w, 1, w * 2);
			if (i == 1) tile->setShape(0.5f - w, 0, 1 - w * 2, 0.5f + w, 1, 1);
			w = 1 / 16.0f;
			if (i == 2) tile->setShape(0.5f - w, 1 - w * 3, -w * 2, 0.5f + w, 1 - w, 1 + w * 2);
			if (i == 3) tile->setShape(0.5f - w, 0.5f - w * 3, -w * 2, 0.5f + w, 0.5f - w, 1 + w * 2);

			t.color(0xff, 0xff, 0xff);

			renderFaceDown(tile, 0, 0, 0, tile->getTexture(0));
			renderFaceUp(tile, 0, 0, 0, tile->getTexture(1));

			t.color(0x80, 0x80, 0x80);
			renderNorth(tile, 0, 0, 0, tile->getTexture(2));
			renderSouth(tile, 0, 0, 0, tile->getTexture(3));

			t.color(0xbb, 0xbb, 0xbb);
			renderWest(tile, 0, 0, 0, tile->getTexture(4));
			renderEast(tile, 0, 0, 0, tile->getTexture(5));
		}
		t.draw();
		t.addOffset(0.5f, 0.5f, 0.5f);
		tile->setShape(0, 0, 0, 1, 1, 1);
	}
	else if (shape == Tile::SHAPE_FENCE_GATE) {
		t.addOffset(-0.5f, -0.5f, -0.5f);
		t.begin();
		for (int i = 0; i < 3; i++) {
			float w = 1 / 16.0f;
			if (i == 0) tile->setShape(0.5f - w, .3f, 0, 0.5f + w, 1, w * 2);
			if (i == 1) tile->setShape(0.5f - w, .3f, 1 - w * 2, 0.5f + w, 1, 1);
			w = 1 / 16.0f;
			if (i == 2) tile->setShape(0.5f - w, .5f, 0, 0.5f + w, 1 - w, 1);

			t.color(0xff, 0xff, 0xff);
			renderFaceUp(tile, 0, 0, 0, tile->getTexture(0));
			renderFaceDown(tile, 0, 0, 0, tile->getTexture(1));

			t.color(0x80, 0x80, 0x80);
			renderNorth(tile, 0, 0, 0, tile->getTexture(2));
			renderSouth(tile, 0, 0, 0, tile->getTexture(3));

			t.color(0xbb, 0xbb, 0xbb);
			renderWest(tile, 0, 0, 0, tile->getTexture(4));
			renderEast(tile, 0, 0, 0, tile->getTexture(5));
		}
		t.draw();
		tile->setShape(0, 0, 0, 1, 1, 1);
		t.addOffset(0.5f, 0.5f, 0.5f);
	}
}

bool TileRenderer::tesselateThinFenceInWorld(ThinFenceTile* tt, int x, int y, int z) {
	const int depth = 128;
	Tesselator& t = Tesselator::instance;

	float br = tt->getBrightness(level, x, y, z);
	int col = tt->getColor(level, x, y, z);
	float r = ((col >> 16) & 0xff) / 255.0f;
	float g = ((col >> 8) & 0xff) / 255.0f;
	float b = ((col) & 0xff) / 255.0f;

	//if (GameRenderer::anaglyph3d) {
	//	float cr = (r * 30 + g * 59 + b * 11) / 100;
	//	float cg = (r * 30 + g * 70) / (100);
	//	float cb = (r * 30 + b * 70) / (100);

	//	r = cr;
	//	g = cg;
	//	b = cb;
	//}
	t.color(br * r, br * g, br * b);

	int tex = 0;
	int edgeTex = 0;

	if (fixedTexture >= 0) {
		tex = fixedTexture;
		edgeTex = fixedTexture;
	} else {
		int data = level->getData(x, y, z);
		tex = tt->getTexture(0, data);
		edgeTex = tt->getEdgeTexture();
	}

	const int xt = (tex & 0xf) << 4;
	const int yt = tex & 0xf0;
	float u0 = (xt) / 256.0f;
	const float u1 = (xt + 7.99f) / 256.0f;
	const float u2 = (xt + 15.99f) / 256.0f;
	const float v0 = (yt) / 256.0f;
	const float v2 = (yt + 15.99f) / 256.0f;

	const int xet = (edgeTex & 0xf) << 4;
	const int yet = edgeTex & 0xf0;

	const float iu0 = (xet + 7) / 256.0f;
	const float iu1 = (xet + 8.99f) / 256.0f;
	const float iv0 = (yet) / 256.0f;
	const float iv1 = (yet + 8) / 256.0f;
	const float iv2 = (yet + 15.99f) / 256.0f;

	const float x0 = (float)x;
	const float x1 = x0 + .5f;
	const float x2 = x0 + 1;
	const float y0 = (float)y + 0.001f;
	const float y1 = y0 + 1 - 0.002f;
	const float z0 = (float)z;
	const float z1 = z0 + .5f;
	const float z2 = z0 + 1;
	const float ix0 = x0 + .5f - 1.0f / 16.0f;
	const float ix1 = x0 + .5f + 1.0f / 16.0f;
	const float iz0 = z0 + .5f - 1.0f / 16.0f;
	const float iz1 = z0 + .5f + 1.0f / 16.0f;

	const bool n = tt->attachsTo(level->getTile(x, y, z - 1));
	const bool s = tt->attachsTo(level->getTile(x, y, z + 1));
	const bool w = tt->attachsTo(level->getTile(x - 1, y, z));
	const bool e = tt->attachsTo(level->getTile(x + 1, y, z));

	const bool up = tt->shouldRenderFace(level, x, y + 1, z, Facing::UP);
	const bool down = tt->shouldRenderFace(level, x, y - 1, z, Facing::DOWN);

	const float noZFightingOffset = 0.01f;

	if ((w && e) || (!w && !e && !n && !s)) {
		t.vertexUV(x0, y1, z1, u0, v0);
		t.vertexUV(x0, y0, z1, u0, v2);
		t.vertexUV(x2, y0, z1, u2, v2);
		t.vertexUV(x2, y1, z1, u2, v0);

		t.vertexUV(x2, y1, z1, u0, v0);
		t.vertexUV(x2, y0, z1, u0, v2);
		t.vertexUV(x0, y0, z1, u2, v2);
		t.vertexUV(x0, y1, z1, u2, v0);

		if (up) {
			// small edge texture
			t.vertexUV(x0, y1 + noZFightingOffset, iz1, iu1, iv2);
			t.vertexUV(x2, y1 + noZFightingOffset, iz1, iu1, iv0);
			t.vertexUV(x2, y1 + noZFightingOffset, iz0, iu0, iv0);
			t.vertexUV(x0, y1 + noZFightingOffset, iz0, iu0, iv2);

			t.vertexUV(x2, y1 + noZFightingOffset, iz1, iu1, iv2);
			t.vertexUV(x0, y1 + noZFightingOffset, iz1, iu1, iv0);
			t.vertexUV(x0, y1 + noZFightingOffset, iz0, iu0, iv0);
			t.vertexUV(x2, y1 + noZFightingOffset, iz0, iu0, iv2);
		} else {
			if (y < (depth - 1) && level->isEmptyTile(x - 1, y + 1, z)) {
				t.vertexUV(x0, y1 + noZFightingOffset, iz1, iu1, iv1);
				t.vertexUV(x1, y1 + noZFightingOffset, iz1, iu1, iv2);
				t.vertexUV(x1, y1 + noZFightingOffset, iz0, iu0, iv2);
				t.vertexUV(x0, y1 + noZFightingOffset, iz0, iu0, iv1);

				t.vertexUV(x1, y1 + noZFightingOffset, iz1, iu1, iv1);
				t.vertexUV(x0, y1 + noZFightingOffset, iz1, iu1, iv2);
				t.vertexUV(x0, y1 + noZFightingOffset, iz0, iu0, iv2);
				t.vertexUV(x1, y1 + noZFightingOffset, iz0, iu0, iv1);
			}
			if (y < (depth - 1) && level->isEmptyTile(x + 1, y + 1, z)) {
				t.vertexUV(x1, y1 + noZFightingOffset, iz1, iu1, iv0);
				t.vertexUV(x2, y1 + noZFightingOffset, iz1, iu1, iv1);
				t.vertexUV(x2, y1 + noZFightingOffset, iz0, iu0, iv1);
				t.vertexUV(x1, y1 + noZFightingOffset, iz0, iu0, iv0);

				t.vertexUV(x2, y1 + noZFightingOffset, iz1, iu1, iv0);
				t.vertexUV(x1, y1 + noZFightingOffset, iz1, iu1, iv1);
				t.vertexUV(x1, y1 + noZFightingOffset, iz0, iu0, iv1);
				t.vertexUV(x2, y1 + noZFightingOffset, iz0, iu0, iv0);
			}
		}
		if (down) {
			// small edge texture
			t.vertexUV(x0, y0 - noZFightingOffset, iz1, iu1, iv2);
			t.vertexUV(x2, y0 - noZFightingOffset, iz1, iu1, iv0);
			t.vertexUV(x2, y0 - noZFightingOffset, iz0, iu0, iv0);
			t.vertexUV(x0, y0 - noZFightingOffset, iz0, iu0, iv2);

			t.vertexUV(x2, y0 - noZFightingOffset, iz1, iu1, iv2);
			t.vertexUV(x0, y0 - noZFightingOffset, iz1, iu1, iv0);
			t.vertexUV(x0, y0 - noZFightingOffset, iz0, iu0, iv0);
			t.vertexUV(x2, y0 - noZFightingOffset, iz0, iu0, iv2);
		} else {
			if (y > 1 && level->isEmptyTile(x - 1, y - 1, z)) {
				t.vertexUV(x0, y0 - noZFightingOffset, iz1, iu1, iv1);
				t.vertexUV(x1, y0 - noZFightingOffset, iz1, iu1, iv2);
				t.vertexUV(x1, y0 - noZFightingOffset, iz0, iu0, iv2);
				t.vertexUV(x0, y0 - noZFightingOffset, iz0, iu0, iv1);

				t.vertexUV(x1, y0 - noZFightingOffset, iz1, iu1, iv1);
				t.vertexUV(x0, y0 - noZFightingOffset, iz1, iu1, iv2);
				t.vertexUV(x0, y0 - noZFightingOffset, iz0, iu0, iv2);
				t.vertexUV(x1, y0 - noZFightingOffset, iz0, iu0, iv1);
			}
			if (y > 1 && level->isEmptyTile(x + 1, y - 1, z)) {
				t.vertexUV(x1, y0 - noZFightingOffset, iz1, iu1, iv0);
				t.vertexUV(x2, y0 - noZFightingOffset, iz1, iu1, iv1);
				t.vertexUV(x2, y0 - noZFightingOffset, iz0, iu0, iv1);
				t.vertexUV(x1, y0 - noZFightingOffset, iz0, iu0, iv0);

				t.vertexUV(x2, y0 - noZFightingOffset, iz1, iu1, iv0);
				t.vertexUV(x1, y0 - noZFightingOffset, iz1, iu1, iv1);
				t.vertexUV(x1, y0 - noZFightingOffset, iz0, iu0, iv1);
				t.vertexUV(x2, y0 - noZFightingOffset, iz0, iu0, iv0);
			}
		}

	} else if (w && !e) {
		// half-step towards west
		t.vertexUV(x0, y1, z1, u0, v0);
		t.vertexUV(x0, y0, z1, u0, v2);
		t.vertexUV(x1, y0, z1, u1, v2);
		t.vertexUV(x1, y1, z1, u1, v0);

		t.vertexUV(x1, y1, z1, u0, v0);
		t.vertexUV(x1, y0, z1, u0, v2);
		t.vertexUV(x0, y0, z1, u1, v2);
		t.vertexUV(x0, y1, z1, u1, v0);

		// small edge texture
		if (!s && !n) {
			t.vertexUV(x1, y1, iz1, iu0, iv0);
			t.vertexUV(x1, y0, iz1, iu0, iv2);
			t.vertexUV(x1, y0, iz0, iu1, iv2);
			t.vertexUV(x1, y1, iz0, iu1, iv0);

			t.vertexUV(x1, y1, iz0, iu0, iv0);
			t.vertexUV(x1, y0, iz0, iu0, iv2);
			t.vertexUV(x1, y0, iz1, iu1, iv2);
			t.vertexUV(x1, y1, iz1, iu1, iv0);
		}

		if (up || (y < (depth - 1) && level->isEmptyTile(x - 1, y + 1, z))) {
			// small edge texture
			t.vertexUV(x0, y1 + noZFightingOffset, iz1, iu1, iv1);
			t.vertexUV(x1, y1 + noZFightingOffset, iz1, iu1, iv2);
			t.vertexUV(x1, y1 + noZFightingOffset, iz0, iu0, iv2);
			t.vertexUV(x0, y1 + noZFightingOffset, iz0, iu0, iv1);

			t.vertexUV(x1, y1 + noZFightingOffset, iz1, iu1, iv1);
			t.vertexUV(x0, y1 + noZFightingOffset, iz1, iu1, iv2);
			t.vertexUV(x0, y1 + noZFightingOffset, iz0, iu0, iv2);
			t.vertexUV(x1, y1 + noZFightingOffset, iz0, iu0, iv1);
		}
		if (down || (y > 1 && level->isEmptyTile(x - 1, y - 1, z))) {
			// small edge texture
			t.vertexUV(x0, y0 - noZFightingOffset, iz1, iu1, iv1);
			t.vertexUV(x1, y0 - noZFightingOffset, iz1, iu1, iv2);
			t.vertexUV(x1, y0 - noZFightingOffset, iz0, iu0, iv2);
			t.vertexUV(x0, y0 - noZFightingOffset, iz0, iu0, iv1);

			t.vertexUV(x1, y0 - noZFightingOffset, iz1, iu1, iv1);
			t.vertexUV(x0, y0 - noZFightingOffset, iz1, iu1, iv2);
			t.vertexUV(x0, y0 - noZFightingOffset, iz0, iu0, iv2);
			t.vertexUV(x1, y0 - noZFightingOffset, iz0, iu0, iv1);
		}

	} else if (!w && e) {
		// half-step towards east
		t.vertexUV(x1, y1, z1, u1, v0);
		t.vertexUV(x1, y0, z1, u1, v2);
		t.vertexUV(x2, y0, z1, u2, v2);
		t.vertexUV(x2, y1, z1, u2, v0);

		t.vertexUV(x2, y1, z1, u1, v0);
		t.vertexUV(x2, y0, z1, u1, v2);
		t.vertexUV(x1, y0, z1, u2, v2);
		t.vertexUV(x1, y1, z1, u2, v0);

		// small edge texture
		if (!s && !n) {
			t.vertexUV(x1, y1, iz0, iu0, iv0);
			t.vertexUV(x1, y0, iz0, iu0, iv2);
			t.vertexUV(x1, y0, iz1, iu1, iv2);
			t.vertexUV(x1, y1, iz1, iu1, iv0);

			t.vertexUV(x1, y1, iz1, iu0, iv0);
			t.vertexUV(x1, y0, iz1, iu0, iv2);
			t.vertexUV(x1, y0, iz0, iu1, iv2);
			t.vertexUV(x1, y1, iz0, iu1, iv0);
		}

		if (up || (y < (depth - 1) && level->isEmptyTile(x + 1, y + 1, z))) {
			// small edge texture
			t.vertexUV(x1, y1 + noZFightingOffset, iz1, iu1, iv0);
			t.vertexUV(x2, y1 + noZFightingOffset, iz1, iu1, iv1);
			t.vertexUV(x2, y1 + noZFightingOffset, iz0, iu0, iv1);
			t.vertexUV(x1, y1 + noZFightingOffset, iz0, iu0, iv0);

			t.vertexUV(x2, y1 + noZFightingOffset, iz1, iu1, iv0);
			t.vertexUV(x1, y1 + noZFightingOffset, iz1, iu1, iv1);
			t.vertexUV(x1, y1 + noZFightingOffset, iz0, iu0, iv1);
			t.vertexUV(x2, y1 + noZFightingOffset, iz0, iu0, iv0);
		}
		if (down || (y > 1 && level->isEmptyTile(x + 1, y - 1, z))) {
			// small edge texture
			t.vertexUV(x1, y0 - noZFightingOffset, iz1, iu1, iv0);
			t.vertexUV(x2, y0 - noZFightingOffset, iz1, iu1, iv1);
			t.vertexUV(x2, y0 - noZFightingOffset, iz0, iu0, iv1);
			t.vertexUV(x1, y0 - noZFightingOffset, iz0, iu0, iv0);

			t.vertexUV(x2, y0 - noZFightingOffset, iz1, iu1, iv0);
			t.vertexUV(x1, y0 - noZFightingOffset, iz1, iu1, iv1);
			t.vertexUV(x1, y0 - noZFightingOffset, iz0, iu0, iv1);
			t.vertexUV(x2, y0 - noZFightingOffset, iz0, iu0, iv0);
		}

	}

	if ((n && s) || (!w && !e && !n && !s)) {
		// straight north-south
		t.vertexUV(x1, y1, z2, u0, v0);
		t.vertexUV(x1, y0, z2, u0, v2);
		t.vertexUV(x1, y0, z0, u2, v2);
		t.vertexUV(x1, y1, z0, u2, v0);

		t.vertexUV(x1, y1, z0, u0, v0);
		t.vertexUV(x1, y0, z0, u0, v2);
		t.vertexUV(x1, y0, z2, u2, v2);
		t.vertexUV(x1, y1, z2, u2, v0);

		if (up) {
			// small edge texture
			t.vertexUV(ix1, y1, z2, iu1, iv2);
			t.vertexUV(ix1, y1, z0, iu1, iv0);
			t.vertexUV(ix0, y1, z0, iu0, iv0);
			t.vertexUV(ix0, y1, z2, iu0, iv2);

			t.vertexUV(ix1, y1, z0, iu1, iv2);
			t.vertexUV(ix1, y1, z2, iu1, iv0);
			t.vertexUV(ix0, y1, z2, iu0, iv0);
			t.vertexUV(ix0, y1, z0, iu0, iv2);
		} else {
			if (y < (depth - 1) && level->isEmptyTile(x, y + 1, z - 1)) {
				t.vertexUV(ix0, y1, z0, iu1, iv0);
				t.vertexUV(ix0, y1, z1, iu1, iv1);
				t.vertexUV(ix1, y1, z1, iu0, iv1);
				t.vertexUV(ix1, y1, z0, iu0, iv0);

				t.vertexUV(ix0, y1, z1, iu1, iv0);
				t.vertexUV(ix0, y1, z0, iu1, iv1);
				t.vertexUV(ix1, y1, z0, iu0, iv1);
				t.vertexUV(ix1, y1, z1, iu0, iv0);
			}
			if (y < (depth - 1) && level->isEmptyTile(x, y + 1, z + 1)) {
				t.vertexUV(ix0, y1, z1, iu0, iv1);
				t.vertexUV(ix0, y1, z2, iu0, iv2);
				t.vertexUV(ix1, y1, z2, iu1, iv2);
				t.vertexUV(ix1, y1, z1, iu1, iv1);

				t.vertexUV(ix0, y1, z2, iu0, iv1);
				t.vertexUV(ix0, y1, z1, iu0, iv2);
				t.vertexUV(ix1, y1, z1, iu1, iv2);
				t.vertexUV(ix1, y1, z2, iu1, iv1);
			}
		}
		if (down) {
			// small edge texture
			t.vertexUV(ix1, y0, z2, iu1, iv2);
			t.vertexUV(ix1, y0, z0, iu1, iv0);
			t.vertexUV(ix0, y0, z0, iu0, iv0);
			t.vertexUV(ix0, y0, z2, iu0, iv2);

			t.vertexUV(ix1, y0, z0, iu1, iv2);
			t.vertexUV(ix1, y0, z2, iu1, iv0);
			t.vertexUV(ix0, y0, z2, iu0, iv0);
			t.vertexUV(ix0, y0, z0, iu0, iv2);
		} else {
			if (y > 1 && level->isEmptyTile(x, y - 1, z - 1)) {
				// north half-step
				t.vertexUV(ix0, y0, z0, iu1, iv0);
				t.vertexUV(ix0, y0, z1, iu1, iv1);
				t.vertexUV(ix1, y0, z1, iu0, iv1);
				t.vertexUV(ix1, y0, z0, iu0, iv0);

				t.vertexUV(ix0, y0, z1, iu1, iv0);
				t.vertexUV(ix0, y0, z0, iu1, iv1);
				t.vertexUV(ix1, y0, z0, iu0, iv1);
				t.vertexUV(ix1, y0, z1, iu0, iv0);
			}
			if (y > 1 && level->isEmptyTile(x, y - 1, z + 1)) {
				// south half-step
				t.vertexUV(ix0, y0, z1, iu0, iv1);
				t.vertexUV(ix0, y0, z2, iu0, iv2);
				t.vertexUV(ix1, y0, z2, iu1, iv2);
				t.vertexUV(ix1, y0, z1, iu1, iv1);

				t.vertexUV(ix0, y0, z2, iu0, iv1);
				t.vertexUV(ix0, y0, z1, iu0, iv2);
				t.vertexUV(ix1, y0, z1, iu1, iv2);
				t.vertexUV(ix1, y0, z2, iu1, iv1);
			}
		}

	} else if (n && !s) {
		// half-step towards north
		t.vertexUV(x1, y1, z0, u0, v0);
		t.vertexUV(x1, y0, z0, u0, v2);
		t.vertexUV(x1, y0, z1, u1, v2);
		t.vertexUV(x1, y1, z1, u1, v0);

		t.vertexUV(x1, y1, z1, u0, v0);
		t.vertexUV(x1, y0, z1, u0, v2);
		t.vertexUV(x1, y0, z0, u1, v2);
		t.vertexUV(x1, y1, z0, u1, v0);

		// small edge texture
		if (!e && !w) {
			t.vertexUV(ix0, y1, z1, iu0, iv0);
			t.vertexUV(ix0, y0, z1, iu0, iv2);
			t.vertexUV(ix1, y0, z1, iu1, iv2);
			t.vertexUV(ix1, y1, z1, iu1, iv0);

			t.vertexUV(ix1, y1, z1, iu0, iv0);
			t.vertexUV(ix1, y0, z1, iu0, iv2);
			t.vertexUV(ix0, y0, z1, iu1, iv2);
			t.vertexUV(ix0, y1, z1, iu1, iv0);
		}

		if (up || (y < (depth - 1) && level->isEmptyTile(x, y + 1, z - 1))) {
			// small edge texture
			t.vertexUV(ix0, y1, z0, iu1, iv0);
			t.vertexUV(ix0, y1, z1, iu1, iv1);
			t.vertexUV(ix1, y1, z1, iu0, iv1);
			t.vertexUV(ix1, y1, z0, iu0, iv0);

			t.vertexUV(ix0, y1, z1, iu1, iv0);
			t.vertexUV(ix0, y1, z0, iu1, iv1);
			t.vertexUV(ix1, y1, z0, iu0, iv1);
			t.vertexUV(ix1, y1, z1, iu0, iv0);
		}

		if (down || (y > 1 && level->isEmptyTile(x, y - 1, z - 1))) {
			// small edge texture
			t.vertexUV(ix0, y0, z0, iu1, iv0);
			t.vertexUV(ix0, y0, z1, iu1, iv1);
			t.vertexUV(ix1, y0, z1, iu0, iv1);
			t.vertexUV(ix1, y0, z0, iu0, iv0);

			t.vertexUV(ix0, y0, z1, iu1, iv0);
			t.vertexUV(ix0, y0, z0, iu1, iv1);
			t.vertexUV(ix1, y0, z0, iu0, iv1);
			t.vertexUV(ix1, y0, z1, iu0, iv0);
		}

	} else if (!n && s) {
		// half-step towards south
		t.vertexUV(x1, y1, z1, u1, v0);
		t.vertexUV(x1, y0, z1, u1, v2);
		t.vertexUV(x1, y0, z2, u2, v2);
		t.vertexUV(x1, y1, z2, u2, v0);

		t.vertexUV(x1, y1, z2, u1, v0);
		t.vertexUV(x1, y0, z2, u1, v2);
		t.vertexUV(x1, y0, z1, u2, v2);
		t.vertexUV(x1, y1, z1, u2, v0);

		// small edge texture
		if (!e && !w) {
			t.vertexUV(ix1, y1, z1, iu0, iv0);
			t.vertexUV(ix1, y0, z1, iu0, iv2);
			t.vertexUV(ix0, y0, z1, iu1, iv2);
			t.vertexUV(ix0, y1, z1, iu1, iv0);

			t.vertexUV(ix0, y1, z1, iu0, iv0);
			t.vertexUV(ix0, y0, z1, iu0, iv2);
			t.vertexUV(ix1, y0, z1, iu1, iv2);
			t.vertexUV(ix1, y1, z1, iu1, iv0);
		}

		if (up || (y < (depth - 1) && level->isEmptyTile(x, y + 1, z + 1))) {
			// small edge texture
			t.vertexUV(ix0, y1, z1, iu0, iv1);
			t.vertexUV(ix0, y1, z2, iu0, iv2);
			t.vertexUV(ix1, y1, z2, iu1, iv2);
			t.vertexUV(ix1, y1, z1, iu1, iv1);

			t.vertexUV(ix0, y1, z2, iu0, iv1);
			t.vertexUV(ix0, y1, z1, iu0, iv2);
			t.vertexUV(ix1, y1, z1, iu1, iv2);
			t.vertexUV(ix1, y1, z2, iu1, iv1);
		}
		if (down || (y > 1 && level->isEmptyTile(x, y - 1, z + 1))) {
			// small edge texture
			t.vertexUV(ix0, y0, z1, iu0, iv1);
			t.vertexUV(ix0, y0, z2, iu0, iv2);
			t.vertexUV(ix1, y0, z2, iu1, iv2);
			t.vertexUV(ix1, y0, z1, iu1, iv1);

			t.vertexUV(ix0, y0, z2, iu0, iv1);
			t.vertexUV(ix0, y0, z1, iu0, iv2);
			t.vertexUV(ix1, y0, z1, iu1, iv2);
			t.vertexUV(ix1, y0, z2, iu1, iv1);
		}

	}

	return true;
}

void TileRenderer::tesselateRowTexture( Tile* tt, int data, float x, float y, float z ) {
	Tesselator& t = Tesselator::instance;

	int tex = tt->getTexture(0, data);
	if(fixedTexture >= 0)
		tex = fixedTexture;

	int xt = (tex & 0xf) << 4;
	int yt = tex & 0xf0;
	float u0 = (xt) / 256.0f;
	float u1 = (xt + 15.99f) / 256.f;
	float v0 = (yt) / 256.0f;
	float v1 = (yt + 15.99f) / 256.0f;

	float x0 = x + 0.5f - 0.25f;
	float x1 = x + 0.5f + 0.25f;
	float z0 = z + 0.5f - 0.5f;
	float z1 = z + 0.5f + 0.5f;
	t.vertexUV(x0, y + 1, z0, u0, v0);
	t.vertexUV(x0, y + 0, z0, u0, v1);
	t.vertexUV(x0, y + 0, z1, u1, v1);
	t.vertexUV(x0, y + 1, z1, u1, v0);

	t.vertexUV(x0, y + 1, z1, u0, v0);
	t.vertexUV(x0, y + 0, z1, u0, v1);
	t.vertexUV(x0, y + 0, z0, u1, v1);
	t.vertexUV(x0, y + 1, z0, u1, v0);

	t.vertexUV(x1, y + 1, z1, u0, v0);
	t.vertexUV(x1, y + 0, z1, u0, v1);
	t.vertexUV(x1, y + 0, z0, u1, v1);
	t.vertexUV(x1, y + 1, z0, u1, v0);

	t.vertexUV(x1, y + 1, z0, u0, v0);
	t.vertexUV(x1, y + 0, z0, u0, v1);
	t.vertexUV(x1, y + 0, z1, u1, v1);
	t.vertexUV(x1, y + 1, z1, u1, v0);

	x0 = x + 0.5f - 0.5f;
	x1 = x + 0.5f + 0.5f;
	z0 = z + 0.5f - 0.25f;
	z1 = z + 0.5f + 0.25f;

	t.vertexUV(x0, y + 1, z0, u0, v0);
	t.vertexUV(x0, y + 0, z0, u0, v1);
	t.vertexUV(x1, y + 0, z0, u1, v1);
	t.vertexUV(x1, y + 1, z0, u1, v0);

	t.vertexUV(x1, y + 1, z0, u0, v0);
	t.vertexUV(x1, y + 0, z0, u0, v1);
	t.vertexUV(x0, y + 0, z0, u1, v1);
	t.vertexUV(x0, y + 1, z0, u1, v0);

	t.vertexUV(x1, y + 1, z1, u0, v0);
	t.vertexUV(x1, y + 0, z1, u0, v1);
	t.vertexUV(x0, y + 0, z1, u1, v1);
	t.vertexUV(x0, y + 1, z1, u1, v0);

	t.vertexUV(x0, y + 1, z1, u0, v0);
	t.vertexUV(x0, y + 0, z1, u0, v1);
	t.vertexUV(x1, y + 0, z1, u1, v1);
	t.vertexUV(x1, y + 1, z1, u1, v0);
}
