source:

import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;


public class BlockTest extends Applet implements Runnable {

	private int[][][] blocks;
	private BufferedImage[] blockMap;
	private BufferedImage backBuffer;
	
	private static final int[][] shadows = new int[][]{
		{0x1004400,56,44,16,56,90}, // southeast
		{0x4000000, 0,44,64,4    }, // south
		{0x2008400,-28,44,56,56,0}, // southwest
		{0x40000000, 56,24,8,48}, // east
		{0x80000000, 0,24,28,48}, // west
		{0x8006000, 56,-4,16,56,180}, // northeast
		{0x20000000, 0,0,64,52}, // north
		{0x1000A000, -28,-4,56,56,270}, // northwest
		{0x00020004, 0,72,28,24}, // side
	};
	
	private static final int getShadowMap(int[][][] blocks, int x, int y, int z){
		int shadow = 0;
		for(int i = 0; i < 16; i++){
			int xy = i%8;
			int dx = (xy+2)%3-1;
			int dy = (xy/3+2)%3-1;
			int dz = i/8;
			if (blocks[x+dx][y+dy][z+dz] != 0){
				shadow |= (1<<(i + 16));
			} else {
				shadow |= (1<>3);
		}
		return img;
	}
	
	public BlockTest(){
		backBuffer = new BufferedImage(640,480,BufferedImage.TYPE_INT_ARGB);
		String data = "\u0000\u0018@H\u0006/\u007f\u0001\u0019=/\u0004wg\u0001F>\u0004\u0007Ui\u0002\u0018=/\u0007eN\u0001I>\u0016\u0003ER\u0002\u0019<\u0001\u0001{]\u0000\u001b=,\u0005d\u0013\u0002\u0018\u0001)\u0002k\u0016";
		blockMap = new BufferedImage[]{
				drawBlock(data, new Color(85,153,255)), // water
				drawBlock(data, new Color(145,100,60)), // grass
			    drawBlock(data, new Color(170,200,55))  // grass
		};
		setIgnoreRepaint(true);
	}
	
	public void start() {
		new Thread(this).start();
	}
	
	public void dropBlock(int x, int y, int type){
		int z = blocks[x][y].length-1;
		while(blocks[x][y][z] == 0 && z > 0){
			blocks[x][y][z] = type;
			try { Thread.sleep(10); } catch (InterruptedException e) {}
			render();
			blocks[x][y][z] = 0;
			z--;
		}
		blocks[x][y][z+1] = type;
		render();
	}
	
	public void run(){
		// obviously encode this later
		int[][][] srcBlocks = new int[][][]{
				{{0,0,0,0,0},{0,0,0,0,0},{0,0,0,0,0},{0,0,0,0,0},{0,0,0,0,0},{0,0,0,0,0},{0,0,0,0,0},{0,0,0,0,0},{0,0,0,0,0},{0,0,0,0,0}},
				{{0,0,0,0,0},{1,0,0,0,0},{1,0,0,0,0},{1,0,0,0,0},{1,0,0,0,0},{1,0,0,0,0},{1,0,0,0,0},{1,0,0,0,0},{1,0,0,0,0},{0,0,0,0,0}},
				{{0,0,0,0,0},{1,0,0,0,0},{1,0,0,0,0},{1,0,0,0,0},{1,0,0,0,0},{1,0,0,0,0},{1,0,0,0,0},{1,0,0,0,0},{1,0,0,0,0},{0,0,0,0,0}},
				{{0,0,0,0,0},{1,2,3,0,0},{1,2,3,0,0},{1,2,3,0,0},{1,2,0,0,0},{1,0,0,0,0},{1,2,3,0,0},{1,2,3,0,0},{1,2,3,0,0},{0,0,0,0,0}},
				{{0,0,0,0,0},{1,2,0,0,0},{1,2,3,0,0},{1,2,0,0,0},{1,2,0,0,0},{1,2,0,0,0},{1,2,3,0,0},{1,2,0,0,0},{1,2,0,0,0},{0,0,0,0,0}},
				{{0,0,0,0,0},{1,2,0,0,0},{1,2,3,0,0},{1,2,0,0,0},{1,2,0,0,0},{1,2,0,0,0},{1,2,3,0,0},{1,2,0,0,0},{1,2,3,0,0},{0,0,0,0,0}},
				{{0,0,0,0,0},{1,2,3,0,0},{1,2,3,0,0},{1,2,0,0,0},{1,0,0,0,0},{1,2,0,0,0},{1,2,3,0,0},{1,2,3,0,0},{1,2,3,0,0},{0,0,0,0,0}},
				{{0,0,0,0,0},{1,0,0,0,0},{1,0,0,0,0},{1,0,0,0,0},{1,0,0,0,0},{1,0,0,0,0},{1,0,0,0,0},{1,2,0,0,0},{1,2,0,0,0},{0,0,0,0,0}},
				{{0,0,0,0,0},{0,0,0,0,0},{0,0,0,0,0},{0,0,0,0,0},{0,0,0,0,0},{0,0,0,0,0},{0,0,0,0,0},{0,0,0,0,0},{0,0,0,0,0},{0,0,0,0,0}}
		};
		blocks = new int[srcBlocks[0].length][srcBlocks.length][srcBlocks[0][0].length];
		// swap x/y coords, makes it easier to layout with the array initializer above.
		for(int x = 0; x < srcBlocks.length; x++){
			for(int y = 0; y < srcBlocks[x].length; y++){
				for(int z = 0; z < srcBlocks[x][y].length; z++){
					//blocks[y][x][z] = srcBlocks[x][y][z];
					if (srcBlocks[x][y][z] != 0){
						dropBlock(y, x, srcBlocks[x][y][z]);
					}
				}
			}
		}
	}
	
	public void render(){
		Graphics g = backBuffer.getGraphics();
		g.setColor(Color.LIGHT_GRAY);
		g.fillRect(0,0,getWidth(),getHeight());
		g.setColor(new Color(0,0,0,32));
		for(int z = 0; z < blocks[0][0].length-1; z++){
			for(int x = 1; x < blocks.length-1; x++){
				for(int y = 1; y < blocks[x].length-1; y++){
					int block = blocks[x][y][z]; 
					if (block > 0){
						g.drawImage(blockMap[block-1],x*64,y*48 - z*24,null);
					}
					
					boolean topBlock = block != 0;
					for(int ztest = z+1; ztest < blocks[x][y].length; ztest++){
						if (blocks[x][y][ztest] != 0){
							topBlock = false;
						}
					}
					if (topBlock){
						int shadowMask = getShadowMap(blocks,x,y,z);
						for(int i = 0; i < shadows.length; i++){
							if ((shadows[i][0] & shadowMask) == shadows[i][0]){
								g.translate(x*64,y*48 - z*24);
								if (shadows[i].length == 5){
									g.fillRect(shadows[i][1],shadows[i][2],shadows[i][3],shadows[i][4]);
								} else {
									g.fillArc(shadows[i][1],shadows[i][2],shadows[i][3],shadows[i][4],shadows[i][5],90);
								}
								g.translate(-x*64,-y*48 + z*24);
							}
						}
					}
				}
			}
		}
		g.dispose();
		getGraphics().drawImage(backBuffer,0,0,null);
	}
	
}