summary refs log tree commit diff
path: root/src/Builder.c
diff options
context:
space:
mode:
authorWlodekM <[email protected]>2024-06-16 10:35:45 +0300
committerWlodekM <[email protected]>2024-06-16 10:35:45 +0300
commitabef6da56913f1c55528103e60a50451a39628b1 (patch)
treeb3c8092471ecbb73e568cd0d336efa0e7871ee8d /src/Builder.c
initial commit
Diffstat (limited to 'src/Builder.c')
-rw-r--r--src/Builder.c1662
1 files changed, 1662 insertions, 0 deletions
diff --git a/src/Builder.c b/src/Builder.c
new file mode 100644
index 0000000..654a2b7
--- /dev/null
+++ b/src/Builder.c
@@ -0,0 +1,1662 @@
+#include "Builder.h"
+#include "Constants.h"
+#include "World.h"
+#include "Funcs.h"
+#include "Lighting.h"
+#include "Platform.h"
+#include "MapRenderer.h"
+#include "Graphics.h"
+#include "Drawer.h"
+#include "ExtMath.h"
+#include "Block.h"
+#include "PackedCol.h"
+#include "TexturePack.h"
+#include "Game.h"
+#include "Options.h"
+
+int Builder_SidesLevel, Builder_EdgeLevel;
+/* Packs an index into the 16x16x16 count array. Coordinates range from 0 to 15. */
+#define Builder_PackCount(xx, yy, zz) ((((yy) << 8) | ((zz) << 4) | (xx)) * FACE_COUNT)
+/* Packs an index into the 18x18x18 chunk array. Coordinates range from -1 to 16. */
+#define Builder_PackChunk(xx, yy, zz) (((yy) + 1) * EXTCHUNK_SIZE_2 + ((zz) + 1) * EXTCHUNK_SIZE + ((xx) + 1))
+
+static BlockID* Builder_Chunk;
+static cc_uint8* Builder_Counts;
+static int* Builder_BitFlags;
+static int Builder_X, Builder_Y, Builder_Z;
+static BlockID Builder_Block;
+static int Builder_ChunkIndex;
+static cc_bool Builder_FullBright;
+static int Builder_ChunkEndX, Builder_ChunkEndZ;
+static int Builder_Offsets[FACE_COUNT] = { -1,1, -EXTCHUNK_SIZE,EXTCHUNK_SIZE, -EXTCHUNK_SIZE_2,EXTCHUNK_SIZE_2 };
+
+static int (*Builder_StretchXLiquid)(int countIndex, int x, int y, int z, int chunkIndex, BlockID block);
+static int (*Builder_StretchX)(int countIndex, int x, int y, int z, int chunkIndex, BlockID block, Face face);
+static int (*Builder_StretchZ)(int countIndex, int x, int y, int z, int chunkIndex, BlockID block, Face face);
+static void (*Builder_RenderBlock)(int countsIndex, int x, int y, int z);
+static void (*Builder_PrePrepareChunk)(void);
+static void (*Builder_PostPrepareChunk)(void);
+
+/* Contains state for vertices for a portion of a chunk mesh (vertices that are in a 1D atlas) */
+struct Builder1DPart {
+	/* Union to save on memory, since chunk building is divided into counting then building phases */
+	union FaceData {
+		struct VertexTextured* vertices[FACE_COUNT];
+		int count[FACE_COUNT];
+	} faces;
+	int sCount, sOffset;
+};
+
+/* Part builder data, for both normal and translucent parts.
+The first ATLAS1D_MAX_ATLASES parts are for normal parts, remainder are for translucent parts. */
+static struct Builder1DPart Builder_Parts[ATLAS1D_MAX_ATLASES * 2];
+static struct VertexTextured* Builder_Vertices;
+
+static int Builder1DPart_VerticesCount(struct Builder1DPart* part) {
+	int i, count = part->sCount;
+	for (i = 0; i < FACE_COUNT; i++) { count += part->faces.count[i]; }
+	return count;
+}
+
+static int Builder1DPart_CalcOffsets(struct Builder1DPart* part, int offset) {
+	int i, counts[FACE_COUNT];
+	part->sOffset = offset;
+
+	/* Have to copy first due to count and vertices fields being a union */
+	for (i = 0; i < FACE_COUNT; i++)
+	{
+		counts[i] = part->faces.count[i];
+	}
+
+	offset += part->sCount;
+	for (i = 0; i < FACE_COUNT; i++) 
+	{
+		part->faces.vertices[i] = &Builder_Vertices[offset];
+		offset += counts[i];
+	}
+	return offset;
+}
+
+static int Builder_TotalVerticesCount(void) {
+	int i, count = 0;
+	for (i = 0; i < ATLAS1D_MAX_ATLASES * 2; i++) {
+		count += Builder1DPart_VerticesCount(&Builder_Parts[i]);
+	}
+	return count;
+}
+
+
+/*########################################################################################################################*
+*----------------------------------------------------Base mesh builder----------------------------------------------------*
+*#########################################################################################################################*/
+static void AddSpriteVertices(BlockID block) {
+	int i = Atlas1D_Index(Block_Tex(block, FACE_XMAX));
+	struct Builder1DPart* part = &Builder_Parts[i];
+	part->sCount += 4 * 4;
+}
+
+static void AddVertices(BlockID block, Face face) {
+	int baseOffset = (Blocks.Draw[block] == DRAW_TRANSLUCENT) * ATLAS1D_MAX_ATLASES;
+	int i = Atlas1D_Index(Block_Tex(block, face));
+	struct Builder1DPart* part = &Builder_Parts[baseOffset + i];
+	part->faces.count[face] += 4;
+}
+
+#ifdef CC_BUILD_GL11
+static void BuildPartVbs(struct ChunkPartInfo* info) {
+	/* Sprites vertices are stored before chunk face sides */
+	int i, count, offset = info->offset + info->spriteCount;
+	for (i = 0; i < FACE_COUNT; i++) {
+		count = info->counts[i];
+
+		if (count) {
+			info->vbs[i] = Gfx_CreateVb2(&Builder_Vertices[offset], VERTEX_FORMAT_TEXTURED, count);
+			offset += count;
+		} else {
+			info->vbs[i] = 0;
+		}
+	}
+
+	count  = info->spriteCount;
+	offset = info->offset;
+	if (count) {
+		info->vbs[i] = Gfx_CreateVb2(&Builder_Vertices[offset], VERTEX_FORMAT_TEXTURED, count);
+	} else {
+		info->vbs[i] = 0;
+	}
+}
+#endif
+
+static cc_bool SetPartInfo(struct Builder1DPart* part, int* offset, struct ChunkPartInfo* info) {
+	int vCount = Builder1DPart_VerticesCount(part);
+	info->offset = -1;
+	if (!vCount) return false;
+
+	info->offset = *offset;
+	*offset += vCount;
+
+	info->counts[FACE_XMIN] = part->faces.count[FACE_XMIN];
+	info->counts[FACE_XMAX] = part->faces.count[FACE_XMAX];
+	info->counts[FACE_ZMIN] = part->faces.count[FACE_ZMIN];
+	info->counts[FACE_ZMAX] = part->faces.count[FACE_ZMAX];
+	info->counts[FACE_YMIN] = part->faces.count[FACE_YMIN];
+	info->counts[FACE_YMAX] = part->faces.count[FACE_YMAX];
+	info->spriteCount       = part->sCount;
+	return true;
+}
+
+
+static void PrepareChunk(int x1, int y1, int z1) {
+	int xMax = min(World.Width,  x1 + CHUNK_SIZE);
+	int yMax = min(World.Height, y1 + CHUNK_SIZE);
+	int zMax = min(World.Length, z1 + CHUNK_SIZE);
+
+	int cIndex, index, tileIdx;
+	BlockID b;
+	int x, y, z, xx, yy, zz;
+
+#ifdef OCCLUSION
+	int flags = ComputeOcclusion();
+#endif
+#ifdef DEBUG_OCCLUSION
+	FastColour col = new FastColour(60, 60, 60, 255);
+	if (flags & 1) col.R = 255; /* x */
+	if (flags & 4) col.G = 255; /* y */
+	if (flags & 2) col.B = 255; /* z */
+	map.Sunlight = map.Shadowlight = col;
+	map.SunlightXSide = map.ShadowlightXSide = col;
+	map.SunlightZSide = map.ShadowlightZSide = col;
+	map.SunlightYBottom = map.ShadowlightYBottom = col;
+#endif
+	
+	for (y = y1, yy = 0; y < yMax; y++, yy++) {
+		for (z = z1, zz = 0; z < zMax; z++, zz++) {
+			cIndex = Builder_PackChunk(0, yy, zz);
+
+			for (x = x1, xx = 0; x < xMax; x++, xx++, cIndex++) {
+				b = Builder_Chunk[cIndex];
+				if (Blocks.Draw[b] == DRAW_GAS) continue;
+				index = Builder_PackCount(xx, yy, zz);
+
+				/* Sprites can't be stretched, nor can then be they hidden by other blocks. */
+				/* Note sprites are drawn using DrawSprite and not with any of the DrawXFace. */
+				if (Blocks.Draw[b] == DRAW_SPRITE) { AddSpriteVertices(b); continue; }
+
+				Builder_X = x; Builder_Y = y; Builder_Z = z;
+				Builder_FullBright = Blocks.Brightness[b];
+				tileIdx = b * BLOCK_COUNT;
+				/* All of these function calls are inlined as they can be called tens of millions to hundreds of millions of times. */
+
+				if (Builder_Counts[index] == 0 ||
+					(x == 0 && (y < Builder_SidesLevel || (b >= BLOCK_WATER && b <= BLOCK_STILL_LAVA && y < Builder_EdgeLevel))) ||
+					(x != 0 && (Blocks.Hidden[tileIdx + Builder_Chunk[cIndex - 1]] & (1 << FACE_XMIN)) != 0)) {
+					Builder_Counts[index] = 0;
+				} else {
+					Builder_Counts[index] = Builder_StretchZ(index, x, y, z, cIndex, b, FACE_XMIN);
+				}
+
+				index++;
+				if (Builder_Counts[index] == 0 ||
+					(x == World.MaxX && (y < Builder_SidesLevel || (b >= BLOCK_WATER && b <= BLOCK_STILL_LAVA && y < Builder_EdgeLevel))) ||
+					(x != World.MaxX && (Blocks.Hidden[tileIdx + Builder_Chunk[cIndex + 1]] & (1 << FACE_XMAX)) != 0)) {
+					Builder_Counts[index] = 0;
+				} else {
+					Builder_Counts[index] = Builder_StretchZ(index, x, y, z, cIndex, b, FACE_XMAX);
+				}
+
+				index++;
+				if (Builder_Counts[index] == 0 ||
+					(z == 0 && (y < Builder_SidesLevel || (b >= BLOCK_WATER && b <= BLOCK_STILL_LAVA && y < Builder_EdgeLevel))) ||
+					(z != 0 && (Blocks.Hidden[tileIdx + Builder_Chunk[cIndex - EXTCHUNK_SIZE]] & (1 << FACE_ZMIN)) != 0)) {
+					Builder_Counts[index] = 0;
+				} else {
+					Builder_Counts[index] = Builder_StretchX(index, x, y, z, cIndex, b, FACE_ZMIN);
+				}
+
+				index++;
+				if (Builder_Counts[index] == 0 ||
+					(z == World.MaxZ && (y < Builder_SidesLevel || (b >= BLOCK_WATER && b <= BLOCK_STILL_LAVA && y < Builder_EdgeLevel))) ||
+					(z != World.MaxZ && (Blocks.Hidden[tileIdx + Builder_Chunk[cIndex + EXTCHUNK_SIZE]] & (1 << FACE_ZMAX)) != 0)) {
+					Builder_Counts[index] = 0;
+				} else {
+					Builder_Counts[index] = Builder_StretchX(index, x, y, z, cIndex, b, FACE_ZMAX);
+				}
+
+				index++;
+				if (Builder_Counts[index] == 0 || y == 0 ||
+					(Blocks.Hidden[tileIdx + Builder_Chunk[cIndex - EXTCHUNK_SIZE_2]] & (1 << FACE_YMIN)) != 0) {
+					Builder_Counts[index] = 0;
+				} else {
+					Builder_Counts[index] = Builder_StretchX(index, x, y, z, cIndex, b, FACE_YMIN);
+				}
+
+				index++;
+				if (Builder_Counts[index] == 0 ||
+					(Blocks.Hidden[tileIdx + Builder_Chunk[cIndex + EXTCHUNK_SIZE_2]] & (1 << FACE_YMAX)) != 0) {
+					Builder_Counts[index] = 0;
+				} else if (b < BLOCK_WATER || b > BLOCK_STILL_LAVA) {
+					Builder_Counts[index] = Builder_StretchX(index, x, y, z, cIndex, b, FACE_YMAX);
+				} else {
+					Builder_Counts[index] = Builder_StretchXLiquid(index, x, y, z, cIndex, b);
+				}
+			}
+		}
+	}
+}
+
+#define ReadChunkBody(get_block)\
+for (yy = -1; yy < 17; ++yy) {\
+	y = yy + y1;\
+	for (zz = -1; zz < 17; ++zz) {\
+\
+		index  = World_Pack(x1 - 1, y, z1 + zz);\
+		cIndex = Builder_PackChunk(-1, yy, zz);\
+		for (xx = -1; xx < 17; ++xx, ++index, ++cIndex) {\
+\
+			block    = get_block;\
+			allAir   = allAir   && Blocks.Draw[block] == DRAW_GAS;\
+			allSolid = allSolid && Blocks.FullOpaque[block];\
+			Builder_Chunk[cIndex] = block;\
+		}\
+	}\
+}
+
+static cc_bool ReadChunkData(int x1, int y1, int z1, cc_bool* outAllAir) {
+	BlockRaw* blocks = World.Blocks;
+	BlockRaw* blocks2;
+	cc_bool allAir = true, allSolid = true;
+	int index, cIndex;
+	BlockID block;
+	int xx, yy, zz, y;
+
+#ifndef EXTENDED_BLOCKS
+	ReadChunkBody(blocks[index]);
+#else
+	if (World.IDMask <= 0xFF) {
+		ReadChunkBody(blocks[index]);
+	} else {
+		blocks2 = World.Blocks2;
+		ReadChunkBody(blocks[index] | (blocks2[index] << 8));
+	}
+#endif
+
+	*outAllAir = allAir;
+	return allSolid;
+}
+
+#define ReadBorderChunkBody(get_block)\
+for (yy = -1; yy < 17; ++yy) {\
+	y = yy + y1;\
+	if (y < 0) continue;\
+	if (y >= World.Height) break;\
+\
+	for (zz = -1; zz < 17; ++zz) {\
+		z = zz + z1;\
+		if (z < 0) continue;\
+		if (z >= World.Length) break;\
+\
+		index  = World_Pack(x1 - 1, y, z);\
+		cIndex = Builder_PackChunk(-1, yy, zz);\
+\
+		for (xx = -1; xx < 17; ++xx, ++index, ++cIndex) {\
+			x = xx + x1;\
+			if (x < 0) continue;\
+			if (x >= World.Width) break;\
+\
+			block  = get_block;\
+			allAir = allAir && Blocks.Draw[block] == DRAW_GAS;\
+			Builder_Chunk[cIndex] = block;\
+		}\
+	}\
+}
+
+static cc_bool ReadBorderChunkData(int x1, int y1, int z1, cc_bool* outAllAir) {
+	BlockRaw* blocks = World.Blocks;
+	BlockRaw* blocks2;
+	cc_bool allAir = true;
+	int index, cIndex;
+	BlockID block;
+	int xx, yy, zz, x, y, z;
+
+#ifndef EXTENDED_BLOCKS
+	ReadBorderChunkBody(blocks[index]);
+#else
+	if (World.IDMask <= 0xFF) {
+		ReadBorderChunkBody(blocks[index]);
+	} else {
+		blocks2 = World.Blocks2;
+		ReadBorderChunkBody(blocks[index] | (blocks2[index] << 8));
+	}
+#endif
+
+	*outAllAir = allAir;
+	return false;
+}
+
+static void OutputChunkPartsMeta(int x, int y, int z, struct ChunkInfo* info) {
+	cc_bool hasNorm, hasTran;
+	int partsIndex;
+	int i, j, curIdx, offset;
+	
+	partsIndex = World_ChunkPack(x >> CHUNK_SHIFT, y >> CHUNK_SHIFT, z >> CHUNK_SHIFT);
+	offset  = 0;
+	hasNorm = false;
+	hasTran = false;
+
+	for (i = 0; i < MapRenderer_1DUsedCount; i++) {
+		j = i + ATLAS1D_MAX_ATLASES;
+		curIdx = partsIndex + i * World.ChunksCount;
+
+		hasNorm |= SetPartInfo(&Builder_Parts[i], &offset, &MapRenderer_PartsNormal[curIdx]);
+		hasTran |= SetPartInfo(&Builder_Parts[j], &offset, &MapRenderer_PartsTranslucent[curIdx]);
+	}
+
+	if (hasNorm) {
+		info->normalParts      = &MapRenderer_PartsNormal[partsIndex];
+	}
+	if (hasTran) {
+		info->translucentParts = &MapRenderer_PartsTranslucent[partsIndex];
+	}
+}
+
+void Builder_MakeChunk(struct ChunkInfo* info) {
+#ifdef CC_BUILD_SATURN
+	/* The Saturn build only has 16 kb stack, not large enough */
+	static BlockID chunk[EXTCHUNK_SIZE_3]; 
+	static cc_uint8 counts[CHUNK_SIZE_3 * FACE_COUNT]; 
+	static int bitFlags[1];
+#else
+	BlockID chunk[EXTCHUNK_SIZE_3]; 
+	cc_uint8 counts[CHUNK_SIZE_3 * FACE_COUNT]; 
+	int bitFlags[EXTCHUNK_SIZE_3];
+#endif
+
+	cc_bool allAir, allSolid, onBorder;
+	int xMax, yMax, zMax, totalVerts;
+	int cIndex, index;
+	int x, y, z, xx, yy, zz;
+	int x1 = info->centreX - 8, y1 = info->centreY - 8, z1 = info->centreZ - 8;
+
+	Builder_Chunk  = chunk;
+	Builder_Counts = counts;
+	Builder_BitFlags = bitFlags;
+	Builder_PrePrepareChunk();
+	
+	onBorder = 
+		x1 == 0 || y1 == 0 || z1 == 0   || x1 + CHUNK_SIZE >= World.Width ||
+		y1 + CHUNK_SIZE >= World.Height || z1 + CHUNK_SIZE >= World.Length;
+
+	if (onBorder) {
+		/* less optimal case here */
+		Mem_Set(chunk, BLOCK_AIR, EXTCHUNK_SIZE_3 * sizeof(BlockID));
+		allSolid = ReadBorderChunkData(x1, y1, z1, &allAir);
+	} else {
+		allSolid = ReadChunkData(x1, y1, z1, &allAir);
+	}
+
+	info->allAir = allAir;
+	if (allAir || allSolid) return;
+	Lighting.LightHint(x1 - 1, y1 - 1, z1 - 1);
+
+	Mem_Set(counts, 1, CHUNK_SIZE_3 * FACE_COUNT);
+	xMax = min(World.Width,  x1 + CHUNK_SIZE);
+	yMax = min(World.Height, y1 + CHUNK_SIZE);
+	zMax = min(World.Length, z1 + CHUNK_SIZE);
+
+	Builder_ChunkEndX = xMax; Builder_ChunkEndZ = zMax;
+	PrepareChunk(x1, y1, z1);
+
+	totalVerts = Builder_TotalVerticesCount();
+	if (!totalVerts) return;
+	
+	OutputChunkPartsMeta(x1, y1, z1, info);
+#ifdef OCCLUSION
+	if (info.NormalParts != null || info.TranslucentParts != null)
+		info.occlusionFlags = (cc_uint8)ComputeOcclusion();
+#endif
+
+#ifndef CC_BUILD_GL11
+	/* add an extra element to fix crashing on some GPUs */
+	Builder_Vertices = (struct VertexTextured*)Gfx_RecreateAndLockVb(&info->vb,
+													VERTEX_FORMAT_TEXTURED, totalVerts + 1);
+#else
+	/* NOTE: Relies on assumption vb is ignored by GL11 Gfx_LockVb implementation */
+	Builder_Vertices = (struct VertexTextured*)Gfx_LockVb(0, 
+													VERTEX_FORMAT_TEXTURED, totalVerts + 1);
+#endif
+	Builder_PostPrepareChunk();
+	/* now render the chunk */
+
+	for (y = y1, yy = 0; y < yMax; y++, yy++) {
+		for (z = z1, zz = 0; z < zMax; z++, zz++) {
+			cIndex = Builder_PackChunk(0, yy, zz);
+
+			for (x = x1, xx = 0; x < xMax; x++, xx++, cIndex++) {
+				Builder_Block = chunk[cIndex];
+				if (Blocks.Draw[Builder_Block] == DRAW_GAS) continue;
+
+				index = Builder_PackCount(xx, yy, zz);
+				Builder_ChunkIndex = cIndex;
+				Builder_RenderBlock(index, x, y, z);
+			}
+		}
+	}
+
+#ifdef CC_BUILD_GL11
+	cIndex = World_ChunkPack(x1 >> CHUNK_SHIFT, y1 >> CHUNK_SHIFT, z1 >> CHUNK_SHIFT);
+
+	for (index = 0; index < MapRenderer_1DUsedCount; index++) {
+		int curIdx = cIndex + index * World.ChunksCount;
+
+		BuildPartVbs(&MapRenderer_PartsNormal[curIdx]);
+		BuildPartVbs(&MapRenderer_PartsTranslucent[curIdx]);
+	}
+#else
+	Gfx_UnlockVb(info->vb);
+#endif
+}
+
+static cc_bool Builder_OccludedLiquid(int chunkIndex) {
+	chunkIndex += EXTCHUNK_SIZE_2; /* Checking y above */
+	return
+		Blocks.FullOpaque[Builder_Chunk[chunkIndex]]
+		&& Blocks.Draw[Builder_Chunk[chunkIndex - EXTCHUNK_SIZE]] != DRAW_GAS
+		&& Blocks.Draw[Builder_Chunk[chunkIndex - 1]] != DRAW_GAS
+		&& Blocks.Draw[Builder_Chunk[chunkIndex + 1]] != DRAW_GAS
+		&& Blocks.Draw[Builder_Chunk[chunkIndex + EXTCHUNK_SIZE]] != DRAW_GAS;
+}
+
+static void DefaultPrePrepateChunk(void) {
+	Mem_Set(Builder_Parts, 0, sizeof(Builder_Parts));
+}
+
+static void DefaultPostStretchChunk(void) {
+	int i, j, offset;
+	offset = 0;
+	for (i = 0; i < ATLAS1D_MAX_ATLASES; i++) {
+		j = i + ATLAS1D_MAX_ATLASES;
+
+		offset = Builder1DPart_CalcOffsets(&Builder_Parts[i], offset);
+		offset = Builder1DPart_CalcOffsets(&Builder_Parts[j], offset);
+	}
+}
+
+static RNGState spriteRng;
+static void Builder_DrawSprite(int x, int y, int z) {
+	struct Builder1DPart* part;
+	struct VertexTextured* v;
+	cc_uint8 offsetType;
+	cc_bool bright;
+	PackedCol color;
+	TextureLoc loc;
+	float v1, v2;
+
+	float X, Y, Z;
+	float valX, valY, valZ;
+	float x1,y1,z1, x2,y2,z2;
+	
+	X  = (float)x; Y = (float)y; Z = (float)z;
+	x1 = X + 2.50f/16.0f; y1 = Y;        z1 = Z + 2.50f/16.0f;
+	x2 = X + 13.5f/16.0f; y2 = Y + 1.0f; z2 = Z + 13.5f/16.0f;
+
+#define s_u1 0.0f
+#define s_u2 UV2_Scale
+	loc = Block_Tex(Builder_Block, FACE_XMAX);
+	v1  = Atlas1D_RowId(loc) * Atlas1D.InvTileSize;
+	v2  = v1 + Atlas1D.InvTileSize * UV2_Scale;
+
+	offsetType = Blocks.SpriteOffset[Builder_Block];
+	if (offsetType >= 6 && offsetType <= 7) {
+		Random_Seed(&spriteRng, (x + 1217 * z) & 0x7fffffff);
+		valX = Random_Range(&spriteRng, -3, 3 + 1) / 16.0f;
+		valY = Random_Range(&spriteRng, 0,  3 + 1) / 16.0f;
+		valZ = Random_Range(&spriteRng, -3, 3 + 1) / 16.0f;
+
+		x1 += valX - 1.7f/16.0f; x2 += valX + 1.7f/16.0f;
+		z1 += valZ - 1.7f/16.0f; z2 += valZ + 1.7f/16.0f;
+		if (offsetType == 7) { y1 -= valY; y2 -= valY; }
+	}
+	
+	bright = Blocks.Brightness[Builder_Block];
+	part   = &Builder_Parts[Atlas1D_Index(loc)];
+	color  = bright ? PACKEDCOL_WHITE : Lighting.Color_Sprite_Fast(x, y, z);
+	Block_Tint(color, Builder_Block);
+
+	/* Draw Z axis */
+	v = &Builder_Vertices[part->sOffset];
+	v->x = x1; v->y = y1; v->z = z1; v->Col = color; v->U = s_u2; v->V = v2; v++;
+	v->x = x1; v->y = y2; v->z = z1; v->Col = color; v->U = s_u2; v->V = v1; v++;
+	v->x = x2; v->y = y2; v->z = z2; v->Col = color; v->U = s_u1; v->V = v1; v++;
+	v->x = x2; v->y = y1; v->z = z2; v->Col = color; v->U = s_u1; v->V = v2; v++;
+
+	/* Draw Z axis mirrored */
+	v -= 4; v += part->sCount >> 2;
+	v->x = x2; v->y = y1; v->z = z2; v->Col = color; v->U = s_u2; v->V = v2; v++;
+	v->x = x2; v->y = y2; v->z = z2; v->Col = color; v->U = s_u2; v->V = v1; v++;
+	v->x = x1; v->y = y2; v->z = z1; v->Col = color; v->U = s_u1; v->V = v1; v++;
+	v->x = x1; v->y = y1; v->z = z1; v->Col = color; v->U = s_u1; v->V = v2; v++;
+
+	/* Draw X axis */
+	v -= 4; v += part->sCount >> 2;
+	v->x = x1; v->y = y1; v->z = z2; v->Col = color; v->U = s_u2; v->V = v2; v++;
+	v->x = x1; v->y = y2; v->z = z2; v->Col = color; v->U = s_u2; v->V = v1; v++;
+	v->x = x2; v->y = y2; v->z = z1; v->Col = color; v->U = s_u1; v->V = v1; v++;
+	v->x = x2; v->y = y1; v->z = z1; v->Col = color; v->U = s_u1; v->V = v2; v++;
+
+	/* Draw X axis mirrored */
+	v -= 4; v += part->sCount >> 2;
+	v->x = x2; v->y = y1; v->z = z1; v->Col = color; v->U = s_u2; v->V = v2; v++;
+	v->x = x2; v->y = y2; v->z = z1; v->Col = color; v->U = s_u2; v->V = v1; v++;
+	v->x = x1; v->y = y2; v->z = z2; v->Col = color; v->U = s_u1; v->V = v1; v++;
+	v->x = x1; v->y = y1; v->z = z2; v->Col = color; v->U = s_u1; v->V = v2; v++;
+
+	part->sOffset += 4;
+}
+
+
+/*########################################################################################################################*
+*--------------------------------------------------Normal mesh builder----------------------------------------------------*
+*#########################################################################################################################*/
+static PackedCol Normal_LightColor(int x, int y, int z, Face face, BlockID block) {
+	int offset = (Blocks.LightOffset[block] >> face) & 1;
+
+	switch (face) {
+	case FACE_XMIN:
+		return x < offset                ? Env.SunXSide : Lighting.Color_XSide_Fast(x - offset, y, z);
+	case FACE_XMAX:
+		return x > (World.MaxX - offset) ? Env.SunXSide : Lighting.Color_XSide_Fast(x + offset, y, z);
+	case FACE_ZMIN:
+		return z < offset                ? Env.SunZSide : Lighting.Color_ZSide_Fast(x, y, z - offset);
+	case FACE_ZMAX:
+		return z > (World.MaxZ - offset) ? Env.SunZSide : Lighting.Color_ZSide_Fast(x, y, z + offset);
+
+	case FACE_YMIN:
+		return Lighting.Color_YMin_Fast(x, y - offset, z);		
+	case FACE_YMAX:
+		return Lighting.Color_YMax_Fast(x, y + offset, z);
+	}
+	return 0; /* should never happen */
+}
+
+static cc_bool Normal_CanStretch(BlockID initial, int chunkIndex, int x, int y, int z, Face face) {
+	BlockID cur = Builder_Chunk[chunkIndex];
+
+	if (cur != initial || Block_IsFaceHidden(cur, Builder_Chunk[chunkIndex + Builder_Offsets[face]], face)) return false;
+	if (Builder_FullBright) return true;
+
+	return Normal_LightColor(Builder_X, Builder_Y, Builder_Z, face, initial) == Normal_LightColor(x, y, z, face, cur);
+}
+
+static int NormalBuilder_StretchXLiquid(int countIndex, int x, int y, int z, int chunkIndex, BlockID block) {
+	int count = 1; cc_bool stretchTile;
+	if (Builder_OccludedLiquid(chunkIndex)) return 0;
+	
+	x++;
+	chunkIndex++;
+	countIndex += FACE_COUNT;
+	stretchTile = (Blocks.CanStretch[block] & (1 << FACE_YMAX)) != 0;
+
+	while (x < Builder_ChunkEndX && stretchTile && Normal_CanStretch(block, chunkIndex, x, y, z, FACE_YMAX) && !Builder_OccludedLiquid(chunkIndex)) {
+		Builder_Counts[countIndex] = 0;
+		count++;
+		x++;
+		chunkIndex++;
+		countIndex += FACE_COUNT;
+	}
+	AddVertices(block, FACE_YMAX);
+	return count;
+}
+
+static int NormalBuilder_StretchX(int countIndex, int x, int y, int z, int chunkIndex, BlockID block, Face face) {
+	int count = 1; cc_bool stretchTile;
+	x++;
+	chunkIndex++;
+	countIndex += FACE_COUNT;
+	stretchTile = (Blocks.CanStretch[block] & (1 << face)) != 0;
+
+	while (x < Builder_ChunkEndX && stretchTile && Normal_CanStretch(block, chunkIndex, x, y, z, face)) {
+		Builder_Counts[countIndex] = 0;
+		count++;
+		x++;
+		chunkIndex++;
+		countIndex += FACE_COUNT;
+	}
+	AddVertices(block, face);
+	return count;
+}
+
+static int NormalBuilder_StretchZ(int countIndex, int x, int y, int z, int chunkIndex, BlockID block, Face face) {
+	int count = 1; cc_bool stretchTile;
+	z++;
+	chunkIndex += EXTCHUNK_SIZE;
+	countIndex += CHUNK_SIZE * FACE_COUNT;
+	stretchTile = (Blocks.CanStretch[block] & (1 << face)) != 0;
+
+	while (z < Builder_ChunkEndZ && stretchTile && Normal_CanStretch(block, chunkIndex, x, y, z, face)) {
+		Builder_Counts[countIndex] = 0;
+		count++;
+		z++;
+		chunkIndex += EXTCHUNK_SIZE;
+		countIndex += CHUNK_SIZE * FACE_COUNT;
+	}
+	AddVertices(block, face);
+	return count;
+}
+
+static void NormalBuilder_RenderBlock(int index, int x, int y, int z) {	
+	/* counters */
+	int count_XMin, count_XMax, count_ZMin;
+	int count_ZMax, count_YMin, count_YMax;
+
+	/* block state */
+	Vec3 min, max;
+	int baseOffset, lightFlags;
+	cc_bool fullBright;
+
+	/* per-face state */
+	struct Builder1DPart* part;
+	TextureLoc loc;
+	PackedCol col;
+	int offset;
+
+	if (Blocks.Draw[Builder_Block] == DRAW_SPRITE) {
+		Builder_DrawSprite(x, y, z); return;
+	}
+
+	count_XMin = Builder_Counts[index + FACE_XMIN];
+	count_XMax = Builder_Counts[index + FACE_XMAX];
+	count_ZMin = Builder_Counts[index + FACE_ZMIN];
+	count_ZMax = Builder_Counts[index + FACE_ZMAX];
+	count_YMin = Builder_Counts[index + FACE_YMIN];
+	count_YMax = Builder_Counts[index + FACE_YMAX];
+
+	if (!count_XMin && !count_XMax && !count_ZMin &&
+		!count_ZMax && !count_YMin && !count_YMax) return;
+
+	fullBright = Blocks.Brightness[Builder_Block];
+	baseOffset = (Blocks.Draw[Builder_Block] == DRAW_TRANSLUCENT) * ATLAS1D_MAX_ATLASES;
+	lightFlags = Blocks.LightOffset[Builder_Block];
+
+	Drawer.MinBB = Blocks.MinBB[Builder_Block]; Drawer.MinBB.y = 1.0f - Drawer.MinBB.y;
+	Drawer.MaxBB = Blocks.MaxBB[Builder_Block]; Drawer.MaxBB.y = 1.0f - Drawer.MaxBB.y;
+
+	min = Blocks.RenderMinBB[Builder_Block]; max = Blocks.RenderMaxBB[Builder_Block];
+	Drawer.X1 = x + min.x; Drawer.Y1 = y + min.y; Drawer.Z1 = z + min.z;
+	Drawer.X2 = x + max.x; Drawer.Y2 = y + max.y; Drawer.Z2 = z + max.z;
+
+	Drawer.Tinted  = Blocks.Tinted[Builder_Block];
+	Drawer.TintCol = Blocks.FogCol[Builder_Block];
+
+	if (count_XMin) {
+		loc    = Block_Tex(Builder_Block, FACE_XMIN);
+		offset = (lightFlags >> FACE_XMIN) & 1;
+		part   = &Builder_Parts[baseOffset + Atlas1D_Index(loc)];
+
+		col = fullBright ? PACKEDCOL_WHITE :
+			x >= offset ? Lighting.Color_XSide_Fast(x - offset, y, z) : Env.SunXSide;
+		Drawer_XMin(count_XMin, col, loc, &part->faces.vertices[FACE_XMIN]);
+	}
+
+	if (count_XMax) {
+		loc    = Block_Tex(Builder_Block, FACE_XMAX);
+		offset = (lightFlags >> FACE_XMAX) & 1;
+		part   = &Builder_Parts[baseOffset + Atlas1D_Index(loc)];
+
+		col = fullBright ? PACKEDCOL_WHITE :
+			x <= (World.MaxX - offset) ? Lighting.Color_XSide_Fast(x + offset, y, z) : Env.SunXSide;
+		Drawer_XMax(count_XMax, col, loc, &part->faces.vertices[FACE_XMAX]);
+	}
+
+	if (count_ZMin) {
+		loc    = Block_Tex(Builder_Block, FACE_ZMIN);
+		offset = (lightFlags >> FACE_ZMIN) & 1;
+		part   = &Builder_Parts[baseOffset + Atlas1D_Index(loc)];
+
+		col = fullBright ? PACKEDCOL_WHITE :
+			z >= offset ? Lighting.Color_ZSide_Fast(x, y, z - offset) : Env.SunZSide;
+		Drawer_ZMin(count_ZMin, col, loc, &part->faces.vertices[FACE_ZMIN]);
+	}
+
+	if (count_ZMax) {
+		loc    = Block_Tex(Builder_Block, FACE_ZMAX);
+		offset = (lightFlags >> FACE_ZMAX) & 1;
+		part   = &Builder_Parts[baseOffset + Atlas1D_Index(loc)];
+
+		col = fullBright ? PACKEDCOL_WHITE :
+			z <= (World.MaxZ - offset) ? Lighting.Color_ZSide_Fast(x, y, z + offset) : Env.SunZSide;
+		Drawer_ZMax(count_ZMax, col, loc, &part->faces.vertices[FACE_ZMAX]);
+	}
+
+	if (count_YMin) {
+		loc    = Block_Tex(Builder_Block, FACE_YMIN);
+		offset = (lightFlags >> FACE_YMIN) & 1;
+		part   = &Builder_Parts[baseOffset + Atlas1D_Index(loc)];
+
+		col = fullBright ? PACKEDCOL_WHITE : Lighting.Color_YMin_Fast(x, y - offset, z);
+		Drawer_YMin(count_YMin, col, loc, &part->faces.vertices[FACE_YMIN]);
+	}
+
+	if (count_YMax) {
+		loc    = Block_Tex(Builder_Block, FACE_YMAX);
+		offset = (lightFlags >> FACE_YMAX) & 1;
+		part   = &Builder_Parts[baseOffset + Atlas1D_Index(loc)];
+
+		col = fullBright ? PACKEDCOL_WHITE : Lighting.Color_YMax_Fast(x, y + offset, z);
+		Drawer_YMax(count_YMax, col, loc, &part->faces.vertices[FACE_YMAX]);
+	}
+}
+
+static void Builder_SetDefault(void) {
+	Builder_StretchXLiquid = NULL;
+	Builder_StretchX       = NULL;
+	Builder_StretchZ       = NULL;
+	Builder_RenderBlock    = NULL;
+
+	Builder_PrePrepareChunk  = DefaultPrePrepateChunk;
+	Builder_PostPrepareChunk = DefaultPostStretchChunk;
+}
+
+static void NormalBuilder_SetActive(void) {
+	Builder_SetDefault();
+	Builder_StretchXLiquid = NormalBuilder_StretchXLiquid;
+	Builder_StretchX       = NormalBuilder_StretchX;
+	Builder_StretchZ       = NormalBuilder_StretchZ;
+	Builder_RenderBlock    = NormalBuilder_RenderBlock;
+}
+
+
+/*########################################################################################################################*
+*-------------------------------------------------Advanced mesh builder---------------------------------------------------*
+*#########################################################################################################################*/
+#ifdef CC_BUILD_ADVLIGHTING
+static Vec3 adv_minBB, adv_maxBB;
+static int adv_initBitFlags, adv_baseOffset;
+static int* adv_bitFlags;
+static float adv_x1, adv_y1, adv_z1, adv_x2, adv_y2, adv_z2;
+static PackedCol adv_lerp[5], adv_lerpX[5], adv_lerpZ[5], adv_lerpY[5];
+static cc_bool adv_tinted;
+
+enum ADV_MASK {
+	/* z-1 cube points */
+	xM1_yM1_zM1, xM1_yCC_zM1, xM1_yP1_zM1,
+	xCC_yM1_zM1, xCC_yCC_zM1, xCC_yP1_zM1,
+	xP1_yM1_zM1, xP1_yCC_zM1, xP1_yP1_zM1,
+	/* z cube points */
+	xM1_yM1_zCC, xM1_yCC_zCC, xM1_yP1_zCC,
+	xCC_yM1_zCC, xCC_yCC_zCC, xCC_yP1_zCC,
+	xP1_yM1_zCC, xP1_yCC_zCC, xP1_yP1_zCC,
+	/* z+1 cube points */
+	xM1_yM1_zP1, xM1_yCC_zP1, xM1_yP1_zP1,
+	xCC_yM1_zP1, xCC_yCC_zP1, xCC_yP1_zP1,
+	xP1_yM1_zP1, xP1_yCC_zP1, xP1_yP1_zP1,
+};
+
+/* Bit-or the Adv_Lit flags with these to set the appropriate light values */
+#define LIT_M1 (1 << 0)
+#define LIT_CC (1 << 1)
+#define LIT_P1 (1 << 2)
+/* Returns a 3 bit value where */
+/* - bit 0 set: Y-1 is in light */
+/* - bit 1 set: Y   is in light */
+/* - bit 2 set: Y+1 is in light */
+static int Adv_Lit(int x, int y, int z, int cIndex) {
+	int flags, offset, lightFlags;
+	BlockID block;
+	if (y < 0 || y >= World.Height) return LIT_M1 | LIT_CC | LIT_P1; /* all faces lit */
+
+	/* TODO: check sides height (if sides > edges), check if edge block casts a shadow */
+	if (!World_ContainsXZ(x, z)) {
+		return y >= Builder_EdgeLevel ? LIT_M1 | LIT_CC | LIT_P1 : y == (Builder_EdgeLevel - 1) ? LIT_CC | LIT_P1 : 0;
+	}
+
+	flags = 0;
+	block = Builder_Chunk[cIndex];
+	lightFlags = Blocks.LightOffset[block];
+
+	/* TODO using LIGHT_FLAG_SHADES_FROM_BELOW is wrong here, */
+	/*  but still produces less broken results than YMIN/YMAX */
+
+	/* Use fact Light(Y.YMin) == Light((Y-1).YMax) */
+	offset = (lightFlags >> LIGHT_FLAG_SHADES_FROM_BELOW) & 1;
+	flags |= Lighting.IsLit_Fast(x, y - offset, z) ? LIT_M1 : 0;
+
+	/* Light is same for all the horizontal faces */
+	flags |= Lighting.IsLit_Fast(x, y, z) ? LIT_CC : 0;
+
+	/* Use fact Light((Y+1).YMin) == Light(Y.YMax) */
+	offset = (lightFlags >> LIGHT_FLAG_SHADES_FROM_BELOW) & 1;
+	flags |= Lighting.IsLit_Fast(x, (y + 1) - offset, z) ? LIT_P1 : 0;
+
+	/* If a block is fullbright, it should also look as if that spot is lit */
+	if (Blocks.Brightness[Builder_Chunk[cIndex - 324]]) flags |= LIT_M1;
+	if (Blocks.Brightness[block])                       flags |= LIT_CC;
+	if (Blocks.Brightness[Builder_Chunk[cIndex + 324]]) flags |= LIT_P1;
+	
+	return flags;
+}
+
+static int Adv_ComputeLightFlags(int x, int y, int z, int cIndex) {
+	if (Builder_FullBright) return (1 << xP1_yP1_zP1) - 1; /* all faces fully bright */
+
+	return
+		Adv_Lit(x - 1, y, z - 1, cIndex - 1 - 18) << xM1_yM1_zM1 |
+		Adv_Lit(x - 1, y, z,     cIndex - 1)      << xM1_yM1_zCC |
+		Adv_Lit(x - 1, y, z + 1, cIndex - 1 + 18) << xM1_yM1_zP1 |
+		Adv_Lit(x,     y, z - 1, cIndex + 0 - 18) << xCC_yM1_zM1 |
+		Adv_Lit(x,     y, z,     cIndex + 0)      << xCC_yM1_zCC |
+		Adv_Lit(x,     y, z + 1, cIndex + 0 + 18) << xCC_yM1_zP1 |
+		Adv_Lit(x + 1, y, z - 1, cIndex + 1 - 18) << xP1_yM1_zM1 |
+		Adv_Lit(x + 1, y, z,     cIndex + 1)      << xP1_yM1_zCC |
+		Adv_Lit(x + 1, y, z + 1, cIndex + 1 + 18) << xP1_yM1_zP1;
+}
+
+static int adv_masks[FACE_COUNT] = {
+	/* XMin face */
+	(1 << xM1_yM1_zM1) | (1 << xM1_yM1_zCC) | (1 << xM1_yM1_zP1) |
+	(1 << xM1_yCC_zM1) | (1 << xM1_yCC_zCC) | (1 << xM1_yCC_zP1) |
+	(1 << xM1_yP1_zM1) | (1 << xM1_yP1_zCC) | (1 << xM1_yP1_zP1),
+	/* XMax face */
+	(1 << xP1_yM1_zM1) | (1 << xP1_yM1_zCC) | (1 << xP1_yM1_zP1) |
+	(1 << xP1_yP1_zM1) | (1 << xP1_yP1_zCC) | (1 << xP1_yP1_zP1) |
+	(1 << xP1_yCC_zM1) | (1 << xP1_yCC_zCC) | (1 << xP1_yCC_zP1),
+	/* ZMin face */
+	(1 << xM1_yM1_zM1) | (1 << xCC_yM1_zM1) | (1 << xP1_yM1_zM1) |
+	(1 << xM1_yCC_zM1) | (1 << xCC_yCC_zM1) | (1 << xP1_yCC_zM1) |
+	(1 << xM1_yP1_zM1) | (1 << xCC_yP1_zM1) | (1 << xP1_yP1_zM1),
+	/* ZMax face */
+	(1 << xM1_yM1_zP1) | (1 << xCC_yM1_zP1) | (1 << xP1_yM1_zP1) |
+	(1 << xM1_yCC_zP1) | (1 << xCC_yCC_zP1) | (1 << xP1_yCC_zP1) |
+	(1 << xM1_yP1_zP1) | (1 << xCC_yP1_zP1) | (1 << xP1_yP1_zP1),
+	/* YMin face */
+	(1 << xM1_yM1_zM1) | (1 << xM1_yM1_zCC) | (1 << xM1_yM1_zP1) |
+	(1 << xCC_yM1_zM1) | (1 << xCC_yM1_zCC) | (1 << xCC_yM1_zP1) |
+	(1 << xP1_yM1_zM1) | (1 << xP1_yM1_zCC) | (1 << xP1_yM1_zP1),
+	/* YMax face */
+	(1 << xM1_yP1_zM1) | (1 << xM1_yP1_zCC) | (1 << xM1_yP1_zP1) |
+	(1 << xCC_yP1_zM1) | (1 << xCC_yP1_zCC) | (1 << xCC_yP1_zP1) |
+	(1 << xP1_yP1_zM1) | (1 << xP1_yP1_zCC) | (1 << xP1_yP1_zP1),
+};
+
+
+static cc_bool Adv_CanStretch(BlockID initial, int chunkIndex, int x, int y, int z, Face face) {
+	BlockID cur = Builder_Chunk[chunkIndex];
+	adv_bitFlags[chunkIndex] = Adv_ComputeLightFlags(x, y, z, chunkIndex);
+
+	return cur == initial
+		&& !Block_IsFaceHidden(cur, Builder_Chunk[chunkIndex + Builder_Offsets[face]], face)
+		&& (adv_initBitFlags == adv_bitFlags[chunkIndex]
+		/* Check that this face is either fully bright or fully in shadow */
+		&& (adv_initBitFlags == 0 || (adv_initBitFlags & adv_masks[face]) == adv_masks[face]));
+}
+
+static int Adv_StretchXLiquid(int countIndex, int x, int y, int z, int chunkIndex, BlockID block) {
+	int count = 1; cc_bool stretchTile;
+	if (Builder_OccludedLiquid(chunkIndex)) return 0;
+	adv_initBitFlags = Adv_ComputeLightFlags(x, y, z, chunkIndex);
+	adv_bitFlags[chunkIndex] = adv_initBitFlags;
+
+	x++;
+	chunkIndex++;
+	countIndex += FACE_COUNT;
+	stretchTile = (Blocks.CanStretch[block] & (1 << FACE_YMAX)) != 0;
+
+	while (x < Builder_ChunkEndX && stretchTile && Adv_CanStretch(block, chunkIndex, x, y, z, FACE_YMAX) && !Builder_OccludedLiquid(chunkIndex)) {
+		Builder_Counts[countIndex] = 0;
+		count++;
+		x++;
+		chunkIndex++;
+		countIndex += FACE_COUNT;
+	}
+	AddVertices(block, FACE_YMAX);
+	return count;
+}
+
+static int Adv_StretchX(int countIndex, int x, int y, int z, int chunkIndex, BlockID block, Face face) {
+	int count = 1; cc_bool stretchTile;
+	adv_initBitFlags = Adv_ComputeLightFlags(x, y, z, chunkIndex);
+	adv_bitFlags[chunkIndex] = adv_initBitFlags;
+	
+	x++;
+	chunkIndex++;
+	countIndex += FACE_COUNT;
+	stretchTile = (Blocks.CanStretch[block] & (1 << face)) != 0;
+
+	while (x < Builder_ChunkEndX && stretchTile && Adv_CanStretch(block, chunkIndex, x, y, z, face)) {
+		Builder_Counts[countIndex] = 0;
+		count++;
+		x++;
+		chunkIndex++;
+		countIndex += FACE_COUNT;
+	}
+	AddVertices(block, face);
+	return count;
+}
+
+static int Adv_StretchZ(int countIndex, int x, int y, int z, int chunkIndex, BlockID block, Face face) {
+	int count = 1; cc_bool stretchTile;
+	adv_initBitFlags = Adv_ComputeLightFlags(x, y, z, chunkIndex);
+	adv_bitFlags[chunkIndex] = adv_initBitFlags;
+
+	z++;
+	chunkIndex += EXTCHUNK_SIZE;
+	countIndex += CHUNK_SIZE * FACE_COUNT;
+	stretchTile = (Blocks.CanStretch[block] & (1 << face)) != 0;
+
+	while (z < Builder_ChunkEndZ && stretchTile && Adv_CanStretch(block, chunkIndex, x, y, z, face)) {
+		Builder_Counts[countIndex] = 0;
+		count++;
+		z++;
+		chunkIndex += EXTCHUNK_SIZE;
+		countIndex += CHUNK_SIZE * FACE_COUNT;
+	}
+	AddVertices(block, face);
+	return count;
+}
+
+
+#define Adv_CountBits(F, a, b, c, d) (((F >> a) & 1) + ((F >> b) & 1) + ((F >> c) & 1) + ((F >> d) & 1))
+
+static void Adv_DrawXMin(int count) {
+	TextureLoc texLoc = Block_Tex(Builder_Block, FACE_XMIN);
+	float vOrigin = Atlas1D_RowId(texLoc) * Atlas1D.InvTileSize;
+
+	float u1 = adv_minBB.z, u2 = (count - 1) + adv_maxBB.z * UV2_Scale;
+	float v1 = vOrigin + adv_maxBB.y * Atlas1D.InvTileSize;
+	float v2 = vOrigin + adv_minBB.y * Atlas1D.InvTileSize * UV2_Scale;
+	struct Builder1DPart* part = &Builder_Parts[adv_baseOffset + Atlas1D_Index(texLoc)];
+
+	int F = adv_bitFlags[Builder_ChunkIndex];
+	int aY0_Z0 = Adv_CountBits(F, xM1_yM1_zM1, xM1_yCC_zM1, xM1_yM1_zCC, xM1_yCC_zCC);
+	int aY0_Z1 = Adv_CountBits(F, xM1_yM1_zP1, xM1_yCC_zP1, xM1_yM1_zCC, xM1_yCC_zCC);
+	int aY1_Z0 = Adv_CountBits(F, xM1_yP1_zM1, xM1_yCC_zM1, xM1_yP1_zCC, xM1_yCC_zCC);
+	int aY1_Z1 = Adv_CountBits(F, xM1_yP1_zP1, xM1_yCC_zP1, xM1_yP1_zCC, xM1_yCC_zCC);
+
+	PackedCol tint, white = PACKEDCOL_WHITE;
+	PackedCol col0_0 = Builder_FullBright ? white : adv_lerpX[aY0_Z0], col1_0 = Builder_FullBright ? white : adv_lerpX[aY1_Z0];
+	PackedCol col1_1 = Builder_FullBright ? white : adv_lerpX[aY1_Z1], col0_1 = Builder_FullBright ? white : adv_lerpX[aY0_Z1];
+	struct VertexTextured* vertices, v;
+
+	if (adv_tinted) {
+		tint   = Blocks.FogCol[Builder_Block];
+		col0_0 = PackedCol_Tint(col0_0, tint); col1_0 = PackedCol_Tint(col1_0, tint);
+		col1_1 = PackedCol_Tint(col1_1, tint); col0_1 = PackedCol_Tint(col0_1, tint);
+	}
+
+	vertices = part->faces.vertices[FACE_XMIN];
+	v.x = adv_x1;
+	if (aY0_Z0 + aY1_Z1 > aY0_Z1 + aY1_Z0) {
+		v.y = adv_y2; v.z = adv_z1;               v.U = u1; v.V = v1; v.Col = col1_0; *vertices++ = v;
+		v.y = adv_y1;                                       v.V = v2; v.Col = col0_0; *vertices++ = v;
+		              v.z = adv_z2 + (count - 1); v.U = u2;           v.Col = col0_1; *vertices++ = v;
+		v.y = adv_y2;                                       v.V = v1; v.Col = col1_1; *vertices++ = v;
+	} else {
+		v.y = adv_y2; v.z = adv_z2 + (count - 1); v.U = u2; v.V = v1; v.Col = col1_1; *vertices++ = v;
+		              v.z = adv_z1;               v.U = u1;           v.Col = col1_0; *vertices++ = v;
+		v.y = adv_y1;                                       v.V = v2; v.Col = col0_0; *vertices++ = v;
+		              v.z = adv_z2 + (count - 1); v.U = u2;           v.Col = col0_1; *vertices++ = v;
+	}
+	part->faces.vertices[FACE_XMIN] = vertices;
+}
+
+static void Adv_DrawXMax(int count) {
+	TextureLoc texLoc = Block_Tex(Builder_Block, FACE_XMAX);
+	float vOrigin = Atlas1D_RowId(texLoc) * Atlas1D.InvTileSize;
+
+	float u1 = (count - adv_minBB.z), u2 = (1 - adv_maxBB.z) * UV2_Scale;
+	float v1 = vOrigin + adv_maxBB.y * Atlas1D.InvTileSize;
+	float v2 = vOrigin + adv_minBB.y * Atlas1D.InvTileSize * UV2_Scale;
+	struct Builder1DPart* part = &Builder_Parts[adv_baseOffset + Atlas1D_Index(texLoc)];
+
+	int F = adv_bitFlags[Builder_ChunkIndex];
+	int aY0_Z0 = Adv_CountBits(F, xP1_yM1_zM1, xP1_yCC_zM1, xP1_yM1_zCC, xP1_yCC_zCC);
+	int aY0_Z1 = Adv_CountBits(F, xP1_yM1_zP1, xP1_yCC_zP1, xP1_yM1_zCC, xP1_yCC_zCC);
+	int aY1_Z0 = Adv_CountBits(F, xP1_yP1_zM1, xP1_yCC_zM1, xP1_yP1_zCC, xP1_yCC_zCC);
+	int aY1_Z1 = Adv_CountBits(F, xP1_yP1_zP1, xP1_yCC_zP1, xP1_yP1_zCC, xP1_yCC_zCC);
+
+	PackedCol tint, white = PACKEDCOL_WHITE;
+	PackedCol col0_0 = Builder_FullBright ? white : adv_lerpX[aY0_Z0], col1_0 = Builder_FullBright ? white : adv_lerpX[aY1_Z0];
+	PackedCol col1_1 = Builder_FullBright ? white : adv_lerpX[aY1_Z1], col0_1 = Builder_FullBright ? white : adv_lerpX[aY0_Z1];
+	struct VertexTextured* vertices, v;
+
+	if (adv_tinted) {
+		tint   = Blocks.FogCol[Builder_Block];
+		col0_0 = PackedCol_Tint(col0_0, tint); col1_0 = PackedCol_Tint(col1_0, tint);
+		col1_1 = PackedCol_Tint(col1_1, tint); col0_1 = PackedCol_Tint(col0_1, tint);
+	}
+
+	vertices = part->faces.vertices[FACE_XMAX];
+	v.x = adv_x2;
+	if (aY0_Z0 + aY1_Z1 > aY0_Z1 + aY1_Z0) {
+		v.y = adv_y2; v.z = adv_z1;               v.U = u1; v.V = v1; v.Col = col1_0; *vertices++ = v;
+		              v.z = adv_z2 + (count - 1); v.U = u2;           v.Col = col1_1; *vertices++ = v;
+		v.y = adv_y1;                                       v.V = v2; v.Col = col0_1; *vertices++ = v;
+		              v.z = adv_z1;               v.U = u1;           v.Col = col0_0; *vertices++ = v;
+	} else {
+		v.y = adv_y2; v.z = adv_z2 + (count - 1); v.U = u2; v.V = v1; v.Col = col1_1; *vertices++ = v;
+		v.y = adv_y1;                                       v.V = v2; v.Col = col0_1; *vertices++ = v;
+		              v.z = adv_z1;               v.U = u1;           v.Col = col0_0; *vertices++ = v;
+		v.y = adv_y2;                                       v.V = v1; v.Col = col1_0; *vertices++ = v;
+	}
+	part->faces.vertices[FACE_XMAX] = vertices;
+}
+
+static void Adv_DrawZMin(int count) {
+	TextureLoc texLoc = Block_Tex(Builder_Block, FACE_ZMIN);
+	float vOrigin = Atlas1D_RowId(texLoc) * Atlas1D.InvTileSize;
+
+	float u1 = (count - adv_minBB.x), u2 = (1 - adv_maxBB.x) * UV2_Scale;
+	float v1 = vOrigin + adv_maxBB.y * Atlas1D.InvTileSize;
+	float v2 = vOrigin + adv_minBB.y * Atlas1D.InvTileSize * UV2_Scale;
+	struct Builder1DPart* part = &Builder_Parts[adv_baseOffset + Atlas1D_Index(texLoc)];
+
+	int F = adv_bitFlags[Builder_ChunkIndex];
+	int aX0_Y0 = Adv_CountBits(F, xM1_yM1_zM1, xM1_yCC_zM1, xCC_yM1_zM1, xCC_yCC_zM1);
+	int aX0_Y1 = Adv_CountBits(F, xM1_yP1_zM1, xM1_yCC_zM1, xCC_yP1_zM1, xCC_yCC_zM1);
+	int aX1_Y0 = Adv_CountBits(F, xP1_yM1_zM1, xP1_yCC_zM1, xCC_yM1_zM1, xCC_yCC_zM1);
+	int aX1_Y1 = Adv_CountBits(F, xP1_yP1_zM1, xP1_yCC_zM1, xCC_yP1_zM1, xCC_yCC_zM1);
+
+	PackedCol tint, white = PACKEDCOL_WHITE;
+	PackedCol col0_0 = Builder_FullBright ? white : adv_lerpZ[aX0_Y0], col1_0 = Builder_FullBright ? white : adv_lerpZ[aX1_Y0];
+	PackedCol col1_1 = Builder_FullBright ? white : adv_lerpZ[aX1_Y1], col0_1 = Builder_FullBright ? white : adv_lerpZ[aX0_Y1];
+	struct VertexTextured* vertices, v;
+
+	if (adv_tinted) {
+		tint   = Blocks.FogCol[Builder_Block];
+		col0_0 = PackedCol_Tint(col0_0, tint); col1_0 = PackedCol_Tint(col1_0, tint);
+		col1_1 = PackedCol_Tint(col1_1, tint); col0_1 = PackedCol_Tint(col0_1, tint);
+	}
+
+	vertices = part->faces.vertices[FACE_ZMIN];
+	v.z = adv_z1;
+	if (aX1_Y1 + aX0_Y0 > aX0_Y1 + aX1_Y0) {
+		v.x = adv_x2 + (count - 1); v.y = adv_y1; v.U = u2; v.V = v2; v.Col = col1_0; *vertices++ = v;
+		v.x = adv_x1;                             v.U = u1;           v.Col = col0_0; *vertices++ = v;
+		                            v.y = adv_y2;           v.V = v1; v.Col = col0_1; *vertices++ = v;
+		v.x = adv_x2 + (count - 1);               v.U = u2;           v.Col = col1_1; *vertices++ = v;
+	} else {
+		v.x = adv_x1;               v.y = adv_y1; v.U = u1; v.V = v2; v.Col = col0_0; *vertices++ = v;
+		                            v.y = adv_y2;           v.V = v1; v.Col = col0_1; *vertices++ = v;
+		v.x = adv_x2 + (count - 1);               v.U = u2;           v.Col = col1_1; *vertices++ = v;
+		                            v.y = adv_y1;           v.V = v2; v.Col = col1_0; *vertices++ = v;
+	}
+	part->faces.vertices[FACE_ZMIN] = vertices;
+}
+
+static void Adv_DrawZMax(int count) {
+	TextureLoc texLoc = Block_Tex(Builder_Block, FACE_ZMAX);
+	float vOrigin = Atlas1D_RowId(texLoc) * Atlas1D.InvTileSize;
+
+	float u1 = adv_minBB.x, u2 = (count - 1) + adv_maxBB.x * UV2_Scale;
+	float v1 = vOrigin + adv_maxBB.y * Atlas1D.InvTileSize;
+	float v2 = vOrigin + adv_minBB.y * Atlas1D.InvTileSize * UV2_Scale;
+	struct Builder1DPart* part = &Builder_Parts[adv_baseOffset + Atlas1D_Index(texLoc)];
+
+	int F = adv_bitFlags[Builder_ChunkIndex];
+	int aX0_Y0 = Adv_CountBits(F, xM1_yM1_zP1, xM1_yCC_zP1, xCC_yM1_zP1, xCC_yCC_zP1);
+	int aX1_Y0 = Adv_CountBits(F, xP1_yM1_zP1, xP1_yCC_zP1, xCC_yM1_zP1, xCC_yCC_zP1);
+	int aX0_Y1 = Adv_CountBits(F, xM1_yP1_zP1, xM1_yCC_zP1, xCC_yP1_zP1, xCC_yCC_zP1);
+	int aX1_Y1 = Adv_CountBits(F, xP1_yP1_zP1, xP1_yCC_zP1, xCC_yP1_zP1, xCC_yCC_zP1);
+
+	PackedCol tint, white = PACKEDCOL_WHITE;
+	PackedCol col1_1 = Builder_FullBright ? white : adv_lerpZ[aX1_Y1], col1_0 = Builder_FullBright ? white : adv_lerpZ[aX1_Y0];
+	PackedCol col0_0 = Builder_FullBright ? white : adv_lerpZ[aX0_Y0], col0_1 = Builder_FullBright ? white : adv_lerpZ[aX0_Y1];
+	struct VertexTextured* vertices, v;
+
+	if (adv_tinted) {
+		tint   = Blocks.FogCol[Builder_Block];
+		col0_0 = PackedCol_Tint(col0_0, tint); col1_0 = PackedCol_Tint(col1_0, tint);
+		col1_1 = PackedCol_Tint(col1_1, tint); col0_1 = PackedCol_Tint(col0_1, tint);
+	}
+
+	vertices = part->faces.vertices[FACE_ZMAX];
+	v.z = adv_z2;
+	if (aX1_Y1 + aX0_Y0 > aX0_Y1 + aX1_Y0) {
+		v.x = adv_x1;               v.y = adv_y2; v.U = u1; v.V = v1; v.Col = col0_1; *vertices++ = v;
+		                            v.y = adv_y1;           v.V = v2; v.Col = col0_0; *vertices++ = v;
+		v.x = adv_x2 + (count - 1);               v.U = u2;           v.Col = col1_0; *vertices++ = v;
+		                            v.y = adv_y2;           v.V = v1; v.Col = col1_1; *vertices++ = v;
+	} else {
+		v.x = adv_x2 + (count - 1); v.y = adv_y2; v.U = u2; v.V = v1; v.Col = col1_1; *vertices++ = v;
+		v.x = adv_x1;                             v.U = u1;           v.Col = col0_1; *vertices++ = v;
+		                            v.y = adv_y1;           v.V = v2; v.Col = col0_0; *vertices++ = v;
+		v.x = adv_x2 + (count - 1);               v.U = u2;           v.Col = col1_0; *vertices++ = v;
+	}
+	part->faces.vertices[FACE_ZMAX] = vertices;
+}
+
+static void Adv_DrawYMin(int count) {
+	TextureLoc texLoc = Block_Tex(Builder_Block, FACE_YMIN);
+	float vOrigin = Atlas1D_RowId(texLoc) * Atlas1D.InvTileSize;
+
+	float u1 = adv_minBB.x, u2 = (count - 1) + adv_maxBB.x * UV2_Scale;
+	float v1 = vOrigin + adv_minBB.z * Atlas1D.InvTileSize;
+	float v2 = vOrigin + adv_maxBB.z * Atlas1D.InvTileSize * UV2_Scale;
+	struct Builder1DPart* part = &Builder_Parts[adv_baseOffset + Atlas1D_Index(texLoc)];
+
+	int F = adv_bitFlags[Builder_ChunkIndex];
+	int aX0_Z0 = Adv_CountBits(F, xM1_yM1_zM1, xM1_yM1_zCC, xCC_yM1_zM1, xCC_yM1_zCC);
+	int aX1_Z0 = Adv_CountBits(F, xP1_yM1_zM1, xP1_yM1_zCC, xCC_yM1_zM1, xCC_yM1_zCC);
+	int aX0_Z1 = Adv_CountBits(F, xM1_yM1_zP1, xM1_yM1_zCC, xCC_yM1_zP1, xCC_yM1_zCC);
+	int aX1_Z1 = Adv_CountBits(F, xP1_yM1_zP1, xP1_yM1_zCC, xCC_yM1_zP1, xCC_yM1_zCC);
+
+	PackedCol tint, white = PACKEDCOL_WHITE;
+	PackedCol col0_1 = Builder_FullBright ? white : adv_lerpY[aX0_Z1], col1_1 = Builder_FullBright ? white : adv_lerpY[aX1_Z1];
+	PackedCol col1_0 = Builder_FullBright ? white : adv_lerpY[aX1_Z0], col0_0 = Builder_FullBright ? white : adv_lerpY[aX0_Z0];
+	struct VertexTextured* vertices, v;
+
+	if (adv_tinted) {
+		tint   = Blocks.FogCol[Builder_Block];
+		col0_0 = PackedCol_Tint(col0_0, tint); col1_0 = PackedCol_Tint(col1_0, tint);
+		col1_1 = PackedCol_Tint(col1_1, tint); col0_1 = PackedCol_Tint(col0_1, tint);
+	}
+
+	vertices = part->faces.vertices[FACE_YMIN];
+	v.y = adv_y1;
+	if (aX0_Z1 + aX1_Z0 > aX0_Z0 + aX1_Z1) {
+		v.x = adv_x2 + (count - 1); v.z = adv_z2; v.U = u2; v.V = v2; v.Col = col1_1; *vertices++ = v;
+		v.x = adv_x1;                             v.U = u1;           v.Col = col0_1; *vertices++ = v;
+		                            v.z = adv_z1;           v.V = v1; v.Col = col0_0; *vertices++ = v;
+		v.x = adv_x2 + (count - 1);               v.U = u2;           v.Col = col1_0; *vertices++ = v;
+	} else {
+		v.x = adv_x1;               v.z = adv_z2; v.U = u1; v.V = v2; v.Col = col0_1; *vertices++ = v;
+		                            v.z = adv_z1;           v.V = v1; v.Col = col0_0; *vertices++ = v;
+		v.x = adv_x2 + (count - 1);               v.U = u2;           v.Col = col1_0; *vertices++ = v;
+		                            v.z = adv_z2;           v.V = v2; v.Col = col1_1; *vertices++ = v;
+	}
+	part->faces.vertices[FACE_YMIN] = vertices;
+}
+
+static void Adv_DrawYMax(int count) {
+	TextureLoc texLoc = Block_Tex(Builder_Block, FACE_YMAX);
+	float vOrigin = Atlas1D_RowId(texLoc) * Atlas1D.InvTileSize;
+
+	float u1 = adv_minBB.x, u2 = (count - 1) + adv_maxBB.x * UV2_Scale;
+	float v1 = vOrigin + adv_minBB.z * Atlas1D.InvTileSize;
+	float v2 = vOrigin + adv_maxBB.z * Atlas1D.InvTileSize * UV2_Scale;
+	struct Builder1DPart* part = &Builder_Parts[adv_baseOffset + Atlas1D_Index(texLoc)];
+
+	int F = adv_bitFlags[Builder_ChunkIndex];
+	int aX0_Z0 = Adv_CountBits(F, xM1_yP1_zM1, xM1_yP1_zCC, xCC_yP1_zM1, xCC_yP1_zCC);
+	int aX1_Z0 = Adv_CountBits(F, xP1_yP1_zM1, xP1_yP1_zCC, xCC_yP1_zM1, xCC_yP1_zCC);
+	int aX0_Z1 = Adv_CountBits(F, xM1_yP1_zP1, xM1_yP1_zCC, xCC_yP1_zP1, xCC_yP1_zCC);
+	int aX1_Z1 = Adv_CountBits(F, xP1_yP1_zP1, xP1_yP1_zCC, xCC_yP1_zP1, xCC_yP1_zCC);
+
+	PackedCol tint, white = PACKEDCOL_WHITE;
+	PackedCol col0_0 = Builder_FullBright ? white : adv_lerp[aX0_Z0], col1_0 = Builder_FullBright ? white : adv_lerp[aX1_Z0];
+	PackedCol col1_1 = Builder_FullBright ? white : adv_lerp[aX1_Z1], col0_1 = Builder_FullBright ? white : adv_lerp[aX0_Z1];
+	struct VertexTextured* vertices, v;
+
+	if (adv_tinted) {
+		tint   = Blocks.FogCol[Builder_Block];
+		col0_0 = PackedCol_Tint(col0_0, tint); col1_0 = PackedCol_Tint(col1_0, tint);
+		col1_1 = PackedCol_Tint(col1_1, tint); col0_1 = PackedCol_Tint(col0_1, tint);
+	}
+
+	vertices = part->faces.vertices[FACE_YMAX];
+	v.y = adv_y2;
+	if (aX0_Z0 + aX1_Z1 > aX0_Z1 + aX1_Z0) {
+		v.x = adv_x2 + (count - 1); v.z = adv_z1; v.U = u2; v.V = v1; v.Col = col1_0; *vertices++ = v;
+		v.x = adv_x1;                             v.U = u1;           v.Col = col0_0; *vertices++ = v;
+		                            v.z = adv_z2;           v.V = v2; v.Col = col0_1; *vertices++ = v;
+		v.x = adv_x2 + (count - 1);               v.U = u2;           v.Col = col1_1; *vertices++ = v;
+	} else {
+		v.x = adv_x1;               v.z = adv_z1; v.U = u1; v.V = v1; v.Col = col0_0; *vertices++ = v;
+		                            v.z = adv_z2;           v.V = v2; v.Col = col0_1; *vertices++ = v;
+		v.x = adv_x2 + (count - 1);               v.U = u2;           v.Col = col1_1; *vertices++ = v;
+		                            v.z = adv_z1;           v.V = v1; v.Col = col1_0; *vertices++ = v;
+	}
+	part->faces.vertices[FACE_YMAX] = vertices;
+}
+
+static void Adv_RenderBlock(int index, int x, int y, int z) {
+	Vec3 min, max;
+	int count_XMin, count_XMax, count_ZMin;
+	int count_ZMax, count_YMin, count_YMax;
+
+	if (Blocks.Draw[Builder_Block] == DRAW_SPRITE) {
+		Builder_DrawSprite(x, y, z); return;
+	}
+
+	count_XMin = Builder_Counts[index + FACE_XMIN];
+	count_XMax = Builder_Counts[index + FACE_XMAX];
+	count_ZMin = Builder_Counts[index + FACE_ZMIN];
+	count_ZMax = Builder_Counts[index + FACE_ZMAX];
+	count_YMin = Builder_Counts[index + FACE_YMIN];
+	count_YMax = Builder_Counts[index + FACE_YMAX];
+
+	if (!count_XMin && !count_XMax && !count_ZMin &&
+		!count_ZMax && !count_YMin && !count_YMax) return;
+
+	Builder_FullBright = Blocks.Brightness[Builder_Block];
+	adv_baseOffset = (Blocks.Draw[Builder_Block] == DRAW_TRANSLUCENT) * ATLAS1D_MAX_ATLASES;
+	adv_tinted     = Blocks.Tinted[Builder_Block];
+
+	min = Blocks.RenderMinBB[Builder_Block]; max = Blocks.RenderMaxBB[Builder_Block];
+	adv_x1 = x + min.x; adv_y1 = y + min.y; adv_z1 = z + min.z;
+	adv_x2 = x + max.x; adv_y2 = y + max.y; adv_z2 = z + max.z;
+
+	adv_minBB = Blocks.MinBB[Builder_Block]; adv_maxBB = Blocks.MaxBB[Builder_Block];
+	adv_minBB.y = 1.0f - adv_minBB.y; adv_maxBB.y = 1.0f - adv_maxBB.y;
+
+	if (count_XMin) Adv_DrawXMin(count_XMin);
+	if (count_XMax) Adv_DrawXMax(count_XMax);
+	if (count_ZMin) Adv_DrawZMin(count_ZMin);
+	if (count_ZMax) Adv_DrawZMax(count_ZMax);
+	if (count_YMin) Adv_DrawYMin(count_YMin);
+	if (count_YMax) Adv_DrawYMax(count_YMax);
+}
+
+static void Adv_PrePrepareChunk(void) {
+	int i;
+	DefaultPrePrepateChunk();
+	adv_bitFlags = Builder_BitFlags;
+
+	for (i = 0; i <= 4; i++) {
+		adv_lerp[i]  = PackedCol_Lerp(Env.ShadowCol,   Env.SunCol,   i / 4.0f);
+		adv_lerpX[i] = PackedCol_Lerp(Env.ShadowXSide, Env.SunXSide, i / 4.0f);
+		adv_lerpZ[i] = PackedCol_Lerp(Env.ShadowZSide, Env.SunZSide, i / 4.0f);
+		adv_lerpY[i] = PackedCol_Lerp(Env.ShadowYMin,  Env.SunYMin,  i / 4.0f);
+	}
+}
+
+static void AdvBuilder_SetActive(void) {
+	Builder_SetDefault();
+	Builder_StretchXLiquid  = Adv_StretchXLiquid;
+	Builder_StretchX        = Adv_StretchX;
+	Builder_StretchZ        = Adv_StretchZ;
+	Builder_RenderBlock     = Adv_RenderBlock;
+	Builder_PrePrepareChunk = Adv_PrePrepareChunk;
+}
+#else
+static void AdvBuilder_SetActive(void) { NormalBuilder_SetActive(); }
+#endif
+
+
+
+/*########################################################################################################################*
+*-------------------------------------------------Modern mesh builder-----------------------------------------------------*
+*#########################################################################################################################*/
+#ifdef CC_BUILD_ADVLIGHTING
+/* Fast color averaging wizardy from https://stackoverflow.com/questions/8440631/how-would-you-average-two-32-bit-colors-packed-into-an-integer */
+#define AVERAGE(a, b)   ( ((((a) ^ (b)) & 0xfefefefe) >> 1) + ((a) & (b)) )
+
+static cc_bool Modern_IsOccluded(int x, int y, int z) {
+	BlockID block = World_SafeGetBlock(x, y, z);
+	if (Blocks.Brightness[block] > 0) { return false; }
+	/* If the block we're pulling colors from is solid, return a darker version of original and increment how many are like this */
+	if (Blocks.FullOpaque[block] || (Blocks.Draw[block] == DRAW_TRANSPARENT && Blocks.BlocksLight[block] && Blocks.LightOffset[block] == 0xFF)) {
+		return true;
+	}
+	return false;
+}
+
+static cc_bool Modern_CanStretch(BlockID initial, int chunkIndex, int x, int y, int z, Face face) {
+	return false;
+}
+
+static int Modern_StretchXLiquid(int countIndex, int x, int y, int z, int chunkIndex, BlockID block) {
+	int count = 1;
+	if (Builder_OccludedLiquid(chunkIndex)) return 0;
+	AddVertices(block, FACE_YMAX);
+	return count;
+}
+
+static int Modern_StretchX(int countIndex, int x, int y, int z, int chunkIndex, BlockID block, Face face) {
+	int count = 1;
+	AddVertices(block, face);
+	return count;
+}
+
+static int Modern_StretchZ(int countIndex, int x, int y, int z, int chunkIndex, BlockID block, Face face) {
+	int count = 1;
+	AddVertices(block, face);
+	return count;
+}
+
+static PackedCol Modern_GetColorX(PackedCol orig, int x, int y, int z, int oY, int oZ) {
+	cc_bool xOccluded =  Modern_IsOccluded(x, y + oY, z     );
+	cc_bool zOccluded =  Modern_IsOccluded(x, y     , z + oZ);
+	cc_bool xzOccluded = Modern_IsOccluded(x, y + oY, z + oZ);
+
+	PackedCol CoX = xOccluded ? PackedCol_Scale(orig, FANCY_AO) : Lighting.Color_XSide_Fast(x, y + oY, z     );
+	PackedCol CoZ = zOccluded ? PackedCol_Scale(orig, FANCY_AO) : Lighting.Color_XSide_Fast(x, y     , z + oZ);
+	PackedCol CoXoZ = (xzOccluded || (xOccluded && zOccluded)) ? PackedCol_Scale(orig, FANCY_AO) : Lighting.Color_XSide_Fast(x, y + oY, z + oZ);
+
+	PackedCol ab = AVERAGE(CoX, CoZ);
+	PackedCol cd = AVERAGE(CoXoZ, orig);
+	return AVERAGE(ab, cd);
+}
+static void Modern_DrawXMin(int count, int x, int y, int z) {
+	TextureLoc texLoc = Block_Tex(Builder_Block, FACE_XMIN);
+	float vOrigin = Atlas1D_RowId(texLoc) * Atlas1D.InvTileSize;
+
+	float u1 = adv_minBB.z, u2 = (count - 1) + adv_maxBB.z * UV2_Scale;
+	float v1 = vOrigin + adv_maxBB.y * Atlas1D.InvTileSize;
+	float v2 = vOrigin + adv_minBB.y * Atlas1D.InvTileSize * UV2_Scale;
+	struct Builder1DPart* part = &Builder_Parts[adv_baseOffset + Atlas1D_Index(texLoc)];
+
+	PackedCol tint, white = PACKEDCOL_WHITE;
+	int offset = 1;// (Blocks.LightOffset[Builder_Block] >> FACE_XMIN) & 1;
+	PackedCol orig = Lighting.Color_XSide_Fast(x-offset, y, z);
+	PackedCol col0_0 = Builder_FullBright ? white : Modern_GetColorX(orig, x-offset, y, z, -1, -1);
+	PackedCol col1_0 = Builder_FullBright ? white : Modern_GetColorX(orig, x-offset, y, z, 1, -1);
+	PackedCol col1_1 = Builder_FullBright ? white : Modern_GetColorX(orig, x-offset, y, z, 1, 1);
+	PackedCol col0_1 = Builder_FullBright ? white : Modern_GetColorX(orig, x-offset, y, z, -1, 1);
+	struct VertexTextured* vertices, v;
+
+	if (adv_tinted) {
+		tint   = Blocks.FogCol[Builder_Block];
+		col0_0 = PackedCol_Tint(col0_0, tint); col1_0 = PackedCol_Tint(col1_0, tint);
+		col1_1 = PackedCol_Tint(col1_1, tint); col0_1 = PackedCol_Tint(col0_1, tint);
+	}
+
+	vertices = part->faces.vertices[FACE_XMIN];
+	v.x = adv_x1;
+		v.y = adv_y2; v.z = adv_z2 + (count - 1); v.U = u2; v.V = v1; v.Col = col1_1; *vertices++ = v;
+		              v.z = adv_z1;               v.U = u1;           v.Col = col1_0; *vertices++ = v;
+		v.y = adv_y1;                                       v.V = v2; v.Col = col0_0; *vertices++ = v;
+		              v.z = adv_z2 + (count - 1); v.U = u2;           v.Col = col0_1; *vertices++ = v;
+	part->faces.vertices[FACE_XMIN] = vertices;
+}
+
+static void Modern_DrawXMax(int count, int x, int y, int z) {
+	TextureLoc texLoc = Block_Tex(Builder_Block, FACE_XMAX);
+	float vOrigin = Atlas1D_RowId(texLoc) * Atlas1D.InvTileSize;
+
+	float u1 = (count - adv_minBB.z), u2 = (1 - adv_maxBB.z) * UV2_Scale;
+	float v1 = vOrigin + adv_maxBB.y * Atlas1D.InvTileSize;
+	float v2 = vOrigin + adv_minBB.y * Atlas1D.InvTileSize * UV2_Scale;
+	struct Builder1DPart* part = &Builder_Parts[adv_baseOffset + Atlas1D_Index(texLoc)];
+
+	PackedCol tint, white = PACKEDCOL_WHITE;
+	int offset = 1;// (Blocks.LightOffset[Builder_Block] >> FACE_XMAX) & 1;
+	PackedCol orig = Lighting.Color_XSide_Fast(x+offset, y, z);
+	PackedCol col0_0 = Builder_FullBright ? white : Modern_GetColorX(orig, x+offset, y, z, -1, -1);
+	PackedCol col1_0 = Builder_FullBright ? white : Modern_GetColorX(orig, x+offset, y, z, 1, -1);
+	PackedCol col1_1 = Builder_FullBright ? white : Modern_GetColorX(orig, x+offset, y, z, 1, 1);
+	PackedCol col0_1 = Builder_FullBright ? white : Modern_GetColorX(orig, x+offset, y, z, -1, 1);
+	struct VertexTextured* vertices, v;
+
+	if (adv_tinted) {
+		tint   = Blocks.FogCol[Builder_Block];
+		col0_0 = PackedCol_Tint(col0_0, tint); col1_0 = PackedCol_Tint(col1_0, tint);
+		col1_1 = PackedCol_Tint(col1_1, tint); col0_1 = PackedCol_Tint(col0_1, tint);
+	}
+
+	vertices = part->faces.vertices[FACE_XMAX];
+	v.x = adv_x2;
+		v.y = adv_y2; v.z = adv_z2 + (count - 1); v.U = u2; v.V = v1; v.Col = col1_1; *vertices++ = v;
+		v.y = adv_y1;                                       v.V = v2; v.Col = col0_1; *vertices++ = v;
+		              v.z = adv_z1;               v.U = u1;           v.Col = col0_0; *vertices++ = v;
+		v.y = adv_y2;                                       v.V = v1; v.Col = col1_0; *vertices++ = v;
+	part->faces.vertices[FACE_XMAX] = vertices;
+}
+
+static PackedCol Modern_GetColorZ(PackedCol orig, int x, int y, int z, int oX, int oY) {
+	cc_bool xOccluded  = Modern_IsOccluded(x + oX, y     , z);
+	cc_bool zOccluded  = Modern_IsOccluded(x,      y + oY, z);
+	cc_bool xzOccluded = Modern_IsOccluded(x + oX, y + oY, z);
+
+	PackedCol CoX   =                                xOccluded ? PackedCol_Scale(orig, FANCY_AO) : Lighting.Color_ZSide_Fast(x + oX, y     , z);
+	PackedCol CoZ   =                                zOccluded ? PackedCol_Scale(orig, FANCY_AO) : Lighting.Color_ZSide_Fast(x     , y + oY, z);
+	PackedCol CoXoZ = (xzOccluded || (xOccluded && zOccluded)) ? PackedCol_Scale(orig, FANCY_AO) : Lighting.Color_ZSide_Fast(x + oX, y + oY, z);
+
+	PackedCol ab = AVERAGE(CoX, CoZ);
+	PackedCol cd = AVERAGE(CoXoZ, orig);
+	return AVERAGE(ab, cd);
+}
+static void Modern_DrawZMin(int count, int x, int y, int z) {
+	TextureLoc texLoc = Block_Tex(Builder_Block, FACE_ZMIN);
+	float vOrigin = Atlas1D_RowId(texLoc) * Atlas1D.InvTileSize;
+
+	float u1 = (count - adv_minBB.x), u2 = (1 - adv_maxBB.x) * UV2_Scale;
+	float v1 = vOrigin + adv_maxBB.y * Atlas1D.InvTileSize;
+	float v2 = vOrigin + adv_minBB.y * Atlas1D.InvTileSize * UV2_Scale;
+	struct Builder1DPart* part = &Builder_Parts[adv_baseOffset + Atlas1D_Index(texLoc)];
+
+	PackedCol tint, white = PACKEDCOL_WHITE;
+	int offset = 1;// (Blocks.LightOffset[Builder_Block] >> FACE_ZMIN) & 1;
+	PackedCol orig = Lighting.Color_ZSide_Fast(x, y, z-offset);
+	PackedCol col0_0 = Builder_FullBright ? white : Modern_GetColorZ(orig, x, y, z-offset, -1, -1);
+	PackedCol col1_0 = Builder_FullBright ? white : Modern_GetColorZ(orig, x, y, z-offset, 1, -1);
+	PackedCol col1_1 = Builder_FullBright ? white : Modern_GetColorZ(orig, x, y, z-offset, 1, 1);
+	PackedCol col0_1 = Builder_FullBright ? white : Modern_GetColorZ(orig, x, y, z-offset, -1, 1);
+	struct VertexTextured* vertices, v;
+
+	if (adv_tinted) {
+		tint   = Blocks.FogCol[Builder_Block];
+		col0_0 = PackedCol_Tint(col0_0, tint); col1_0 = PackedCol_Tint(col1_0, tint);
+		col1_1 = PackedCol_Tint(col1_1, tint); col0_1 = PackedCol_Tint(col0_1, tint);
+	}
+
+	vertices = part->faces.vertices[FACE_ZMIN];
+	v.z = adv_z1;
+		v.x = adv_x1;               v.y = adv_y1; v.U = u1; v.V = v2; v.Col = col0_0; *vertices++ = v;
+		                            v.y = adv_y2;           v.V = v1; v.Col = col0_1; *vertices++ = v;
+		v.x = adv_x2 + (count - 1);               v.U = u2;           v.Col = col1_1; *vertices++ = v;
+		                            v.y = adv_y1;           v.V = v2; v.Col = col1_0; *vertices++ = v;
+	part->faces.vertices[FACE_ZMIN] = vertices;
+}
+
+static void Modern_DrawZMax(int count, int x, int y, int z) {
+	TextureLoc texLoc = Block_Tex(Builder_Block, FACE_ZMAX);
+	float vOrigin = Atlas1D_RowId(texLoc) * Atlas1D.InvTileSize;
+
+	float u1 = adv_minBB.x, u2 = (count - 1) + adv_maxBB.x * UV2_Scale;
+	float v1 = vOrigin + adv_maxBB.y * Atlas1D.InvTileSize;
+	float v2 = vOrigin + adv_minBB.y * Atlas1D.InvTileSize * UV2_Scale;
+	struct Builder1DPart* part = &Builder_Parts[adv_baseOffset + Atlas1D_Index(texLoc)];
+
+	PackedCol tint, white = PACKEDCOL_WHITE;
+	int offset = 1;// (Blocks.LightOffset[Builder_Block] >> FACE_ZMAX) & 1;
+	PackedCol orig = Lighting.Color_ZSide_Fast(x, y, z+offset);
+	PackedCol col0_0 = Builder_FullBright ? white : Modern_GetColorZ(orig, x, y, z+offset, -1, -1);
+	PackedCol col1_0 = Builder_FullBright ? white : Modern_GetColorZ(orig, x, y, z+offset, 1, -1);
+	PackedCol col1_1 = Builder_FullBright ? white : Modern_GetColorZ(orig, x, y, z+offset, 1, 1);
+	PackedCol col0_1 = Builder_FullBright ? white : Modern_GetColorZ(orig, x, y, z+offset, -1, 1);
+	struct VertexTextured* vertices, v;
+
+	if (adv_tinted) {
+		tint   = Blocks.FogCol[Builder_Block];
+		col0_0 = PackedCol_Tint(col0_0, tint); col1_0 = PackedCol_Tint(col1_0, tint);
+		col1_1 = PackedCol_Tint(col1_1, tint); col0_1 = PackedCol_Tint(col0_1, tint);
+	}
+
+	vertices = part->faces.vertices[FACE_ZMAX];
+	v.z = adv_z2;
+		v.x = adv_x2 + (count - 1); v.y = adv_y2; v.U = u2; v.V = v1; v.Col = col1_1; *vertices++ = v;
+		v.x = adv_x1;                             v.U = u1;           v.Col = col0_1; *vertices++ = v;
+		                            v.y = adv_y1;           v.V = v2; v.Col = col0_0; *vertices++ = v;
+		v.x = adv_x2 + (count - 1);               v.U = u2;           v.Col = col1_0; *vertices++ = v;
+	part->faces.vertices[FACE_ZMAX] = vertices;
+}
+
+static PackedCol Modern_GetColorYMin(PackedCol orig, int x, int y, int z, int oX, int oZ) {
+	cc_bool xOccluded  = Modern_IsOccluded(x + oX, y, z     );
+	cc_bool zOccluded  = Modern_IsOccluded(x,      y, z + oZ);
+	cc_bool xzOccluded = Modern_IsOccluded(x + oX, y, z + oZ);
+
+	PackedCol CoX   =                                xOccluded ? PackedCol_Scale(orig, FANCY_AO) : Lighting.Color_YMin_Fast(x + oX, y, z     );
+	PackedCol CoZ   =                                zOccluded ? PackedCol_Scale(orig, FANCY_AO) : Lighting.Color_YMin_Fast(x     , y, z + oZ);
+	PackedCol CoXoZ = (xzOccluded || (xOccluded && zOccluded)) ? PackedCol_Scale(orig, FANCY_AO) : Lighting.Color_YMin_Fast(x + oX, y, z + oZ);
+
+	PackedCol ab = AVERAGE(CoX, CoZ);
+	PackedCol cd = AVERAGE(CoXoZ, orig);
+	return AVERAGE(ab, cd);
+}
+static void Modern_DrawYMin(int count, int x, int y, int z) {
+	TextureLoc texLoc = Block_Tex(Builder_Block, FACE_YMIN);
+	float vOrigin = Atlas1D_RowId(texLoc) * Atlas1D.InvTileSize;
+
+	float u1 = adv_minBB.x, u2 = (count - 1) + adv_maxBB.x * UV2_Scale;
+	float v1 = vOrigin + adv_minBB.z * Atlas1D.InvTileSize;
+	float v2 = vOrigin + adv_maxBB.z * Atlas1D.InvTileSize * UV2_Scale;
+	struct Builder1DPart* part = &Builder_Parts[adv_baseOffset + Atlas1D_Index(texLoc)];
+
+	PackedCol tint, white = PACKEDCOL_WHITE;
+	int offset = 1;// (Blocks.LightOffset[Builder_Block] >> FACE_YMIN) & 1;
+	PackedCol orig = Lighting.Color_YMin_Fast(x, y-offset, z);
+	PackedCol col0_0 = Builder_FullBright ? white : Modern_GetColorYMin(orig, x, y-offset, z, -1, -1);
+	PackedCol col1_0 = Builder_FullBright ? white : Modern_GetColorYMin(orig, x, y-offset, z,  1, -1);
+	PackedCol col1_1 = Builder_FullBright ? white : Modern_GetColorYMin(orig, x, y-offset, z,  1,  1);
+	PackedCol col0_1 = Builder_FullBright ? white : Modern_GetColorYMin(orig, x, y-offset, z, -1,  1);
+	struct VertexTextured* vertices, v;
+
+	if (adv_tinted) {
+		tint   = Blocks.FogCol[Builder_Block];
+		col0_0 = PackedCol_Tint(col0_0, tint); col1_0 = PackedCol_Tint(col1_0, tint);
+		col1_1 = PackedCol_Tint(col1_1, tint); col0_1 = PackedCol_Tint(col0_1, tint);
+	}
+
+	vertices = part->faces.vertices[FACE_YMIN];
+	v.y = adv_y1;
+		v.x = adv_x1;               v.z = adv_z2; v.U = u1; v.V = v2; v.Col = col0_1; *vertices++ = v;
+		                            v.z = adv_z1;           v.V = v1; v.Col = col0_0; *vertices++ = v;
+		v.x = adv_x2 + (count - 1);               v.U = u2;           v.Col = col1_0; *vertices++ = v;
+		                            v.z = adv_z2;           v.V = v2; v.Col = col1_1; *vertices++ = v;
+	part->faces.vertices[FACE_YMIN] = vertices;
+}
+
+static PackedCol Modern_GetColorYMax(PackedCol orig, int x, int y, int z, int oX, int oZ) {
+	cc_bool xOccluded  = Modern_IsOccluded(x + oX, y, z     );
+	cc_bool zOccluded  = Modern_IsOccluded(x,      y, z + oZ);
+	cc_bool xzOccluded = Modern_IsOccluded(x + oX, y, z + oZ);
+
+	PackedCol CoX   =                                xOccluded ? PackedCol_Scale(orig, FANCY_AO) : Lighting.Color(x + oX, y, z     );
+	PackedCol CoZ   =                                zOccluded ? PackedCol_Scale(orig, FANCY_AO) : Lighting.Color(x     , y, z + oZ);
+	PackedCol CoXoZ = (xzOccluded || (xOccluded && zOccluded)) ? PackedCol_Scale(orig, FANCY_AO) : Lighting.Color(x + oX, y, z + oZ);
+
+	PackedCol ab = AVERAGE(CoX, CoZ);
+	PackedCol cd = AVERAGE(CoXoZ, orig);
+	return AVERAGE(ab, cd);
+}
+static void Modern_DrawYMax(int count, int x, int y, int z) {
+	TextureLoc texLoc = Block_Tex(Builder_Block, FACE_YMAX);
+	float vOrigin = Atlas1D_RowId(texLoc) * Atlas1D.InvTileSize;
+
+	float u1 = adv_minBB.x, u2 = (count - 1) + adv_maxBB.x * UV2_Scale;
+	float v1 = vOrigin + adv_minBB.z * Atlas1D.InvTileSize;
+	float v2 = vOrigin + adv_maxBB.z * Atlas1D.InvTileSize * UV2_Scale;
+	struct Builder1DPart* part = &Builder_Parts[adv_baseOffset + Atlas1D_Index(texLoc)];
+
+	PackedCol tint, white = PACKEDCOL_WHITE;
+	int offset = 1;// (Blocks.LightOffset[Builder_Block] >> FACE_YMAX) & 1;
+	PackedCol orig = Lighting.Color(x, y+offset, z);
+	PackedCol col0_0 = Builder_FullBright ? white : Modern_GetColorYMax(orig, x, y+offset, z, -1, -1);
+	PackedCol col1_0 = Builder_FullBright ? white : Modern_GetColorYMax(orig, x, y+offset, z,  1, -1);
+	PackedCol col1_1 = Builder_FullBright ? white : Modern_GetColorYMax(orig, x, y+offset, z,  1,  1);
+	PackedCol col0_1 = Builder_FullBright ? white : Modern_GetColorYMax(orig, x, y+offset, z, -1,  1);
+
+	struct VertexTextured* vertices, v;
+
+	if (adv_tinted) {
+		tint   = Blocks.FogCol[Builder_Block];
+		col0_0 = PackedCol_Tint(col0_0, tint); col1_0 = PackedCol_Tint(col1_0, tint);
+		col1_1 = PackedCol_Tint(col1_1, tint); col0_1 = PackedCol_Tint(col0_1, tint);
+	}
+
+	vertices = part->faces.vertices[FACE_YMAX];
+	v.y = adv_y2;
+		v.x = adv_x1;               v.z = adv_z1; v.U = u1; v.V = v1; v.Col = col0_0; *vertices++ = v;
+		                            v.z = adv_z2;           v.V = v2; v.Col = col0_1; *vertices++ = v;
+		v.x = adv_x2 + (count - 1);               v.U = u2;           v.Col = col1_1; *vertices++ = v;
+		                            v.z = adv_z1;           v.V = v1; v.Col = col1_0; *vertices++ = v;
+	part->faces.vertices[FACE_YMAX] = vertices;
+}
+
+static void Modern_RenderBlock(int index, int x, int y, int z) {
+	Vec3 min, max;
+	int count_XMin, count_XMax, count_ZMin;
+	int count_ZMax, count_YMin, count_YMax;
+
+	if (Blocks.Draw[Builder_Block] == DRAW_SPRITE) {
+		Builder_DrawSprite(x, y, z); return;
+	}
+
+	count_XMin = Builder_Counts[index + FACE_XMIN];
+	count_XMax = Builder_Counts[index + FACE_XMAX];
+	count_ZMin = Builder_Counts[index + FACE_ZMIN];
+	count_ZMax = Builder_Counts[index + FACE_ZMAX];
+	count_YMin = Builder_Counts[index + FACE_YMIN];
+	count_YMax = Builder_Counts[index + FACE_YMAX];
+
+	if (!count_XMin && !count_XMax && !count_ZMin &&
+		!count_ZMax && !count_YMin && !count_YMax) return;
+
+	Builder_FullBright = Blocks.Brightness[Builder_Block];
+	adv_baseOffset = (Blocks.Draw[Builder_Block] == DRAW_TRANSLUCENT) * ATLAS1D_MAX_ATLASES;
+	adv_tinted = Blocks.Tinted[Builder_Block];
+
+	min = Blocks.RenderMinBB[Builder_Block]; max = Blocks.RenderMaxBB[Builder_Block];
+	adv_x1 = x + min.x; adv_y1 = y + min.y; adv_z1 = z + min.z;
+	adv_x2 = x + max.x; adv_y2 = y + max.y; adv_z2 = z + max.z;
+
+	adv_minBB = Blocks.MinBB[Builder_Block]; adv_maxBB = Blocks.MaxBB[Builder_Block];
+	adv_minBB.y = 1.0f - adv_minBB.y; adv_maxBB.y = 1.0f - adv_maxBB.y;
+
+	if (count_XMin) Modern_DrawXMin(count_XMin, x, y, z);
+	if (count_XMax) Modern_DrawXMax(count_XMax, x, y, z);
+	if (count_ZMin) Modern_DrawZMin(count_ZMin, x, y, z);
+	if (count_ZMax) Modern_DrawZMax(count_ZMax, x, y, z);
+	if (count_YMin) Modern_DrawYMin(count_YMin, x, y, z);
+	if (count_YMax) Modern_DrawYMax(count_YMax, x, y, z);
+}
+
+static void Modern_PrePrepareChunk(void) {
+	DefaultPrePrepateChunk();
+	adv_bitFlags = Builder_BitFlags;
+}
+
+static void ModernBuilder_SetActive(void) {
+	Builder_SetDefault();
+	Builder_StretchXLiquid =  Modern_StretchXLiquid;
+	Builder_StretchX =        Modern_StretchX;
+	Builder_StretchZ =        Modern_StretchZ;
+	Builder_RenderBlock =     Modern_RenderBlock;
+	Builder_PrePrepareChunk = Modern_PrePrepareChunk;
+}
+#else
+static void ModernBuilder_SetActive(void) { NormalBuilder_SetActive(); }
+#endif
+
+/*########################################################################################################################*
+*---------------------------------------------------Builder interface-----------------------------------------------------*
+*#########################################################################################################################*/
+cc_bool Builder_SmoothLighting;
+void Builder_ApplyActive(void) {
+	if (Builder_SmoothLighting) {
+		if (Lighting_Mode != LIGHTING_MODE_CLASSIC) {
+			ModernBuilder_SetActive();
+		}
+		else {
+			AdvBuilder_SetActive();
+		}
+	} else {
+		NormalBuilder_SetActive();
+	}
+}
+
+static void OnInit(void) {
+	Builder_Offsets[FACE_XMIN] = -1;
+	Builder_Offsets[FACE_XMAX] =  1;
+	Builder_Offsets[FACE_ZMIN] = -EXTCHUNK_SIZE;
+	Builder_Offsets[FACE_ZMAX] =  EXTCHUNK_SIZE;
+	Builder_Offsets[FACE_YMIN] = -EXTCHUNK_SIZE_2;
+	Builder_Offsets[FACE_YMAX] =  EXTCHUNK_SIZE_2;
+
+	if (!Game_ClassicMode) Builder_SmoothLighting = Options_GetBool(OPT_SMOOTH_LIGHTING, false);
+	Builder_ApplyActive();
+}
+
+static void OnNewMapLoaded(void) {
+	Builder_SidesLevel = max(0, Env_SidesHeight);
+	Builder_EdgeLevel  = max(0, Env.EdgeHeight);
+}
+
+struct IGameComponent Builder_Component = {
+	OnInit, /* Init */
+	NULL, /* Free */
+	NULL, /* Reset */
+	NULL, /* OnNewMap */
+	OnNewMapLoaded /* OnNewMapLoaded */
+};