function fx_voxelWorld_init()
{
	document.fx_voxelWorld_w	= 48
	document.fx_voxelWorld_h	= 36
	document.fx_voxelWorld_imgDataIso	= buildBmp24Header( document.fx_voxelWorld_w, document.fx_voxelWorld_h )
	document.fx_voxelWorld_yaw		= 0
	document.fx_voxelWorld_pitch	= 64
	document.fx_voxelWorld_player 	= new v3f( 15.5*1024, 15.5*1024, 15.5*1024 )



	var
		paletteIso = []
	for( i=0;i<128;i++ )
	{
		if( i<64 )
		{
			j=63-i
			r = 64 	*j>>6
			g = 32 	*j>>6
			b =   8	*j>>6

			posterize=255
			paletteIso[ i     ] = byteToIsoArray[ ((255*i>>6)+b)&posterize ]+byteToIsoArray[ ((255*i>>6)+g)&posterize ]+byteToIsoArray[ ((255*i>>6)+r)&posterize ]
			paletteIso[ i+128 ] = byteToIsoArray[ ((51 *i>>6)+b)&posterize ]+byteToIsoArray[ ((153*i>>6)+g)&posterize ]+byteToIsoArray[ ((255*i>>6)+r)&posterize ]
			paletteIso[ i+256 ] = byteToIsoArray[ ((51 *i>>6)+b)&posterize ]+byteToIsoArray[ ((153*i>>6)+g)&posterize ]+byteToIsoArray[ ((153*i>>6)+r)&posterize ]
			paletteIso[ i+384 ] = byteToIsoArray[ ((153*i>>6)+b)&posterize ]+byteToIsoArray[ ((204*i>>6)+g)&posterize ]+byteToIsoArray[ ((153*i>>6)+r)&posterize ]
		}
		else
		{
			// saturation
			paletteIso[ i     ] = paletteIso[  63 ]
			paletteIso[ i+128 ] = paletteIso[ 191 ]
			paletteIso[ i+256 ] = paletteIso[ 319 ]
			paletteIso[ i+384 ] = paletteIso[ 447 ]
		}
	}
	document.fx_voxelWorld_palette	= paletteIso

	var
		texture = []
	for( j=0;j<8;j++ )
	{
		for( i=0;i<8;i++ )
		{
			// rock
			texture[ i+j*8		] = (Math.round( (i-3.5)*(i-3.5)+(j-3.5)*(j-3.5)+(Math.random()*2) )&3)+56-(i==7||j==7?4:0)+(i==0||j==0?4:0)
			// rock + grass
			texture[ i+j*8+128	] = (Math.round( (i-3.5)*(i-3.5)+(j-3.5)*(j-3.5)+(Math.random()*2) )&3)+56-8-(i==7||j==7?4:0)+(i==0||j==0?4:0)+(Math.random()*100>50?0:384+8)
			// rock (dark)
			texture[ i+j*8+64		] = (Math.round( (i-3.5)*(i-3.5)+(j-3.5)*(j-3.5)+(Math.random()*2) )&3)+56-8-(i==7||j==7?4:0)+(i==0||j==0?4:0)
			// grass
	//		texture[ i+j*8+128	] = 63-(Math.round( 16*Math.cos(i*0.3+.5*Math.cos(j*.3))+(Math.random()*4) )&15)+(Math.random()*100>50?192:128)
			// wood
			texture[ i+j*8+192	] = 63-(Math.round( 16*Math.cos(i*0.7-Math.cos(j*.1))+(Math.random()*4) )&15)+128
			// wood + grass
			texture[ i+j*8+256	] = 63-(Math.round( 16*Math.cos(i*0.3+.5*Math.cos(j*.3))+(Math.random()*4) )&15)+(Math.random()*100>50?128:256)
		}
	}
	document.fx_voxelWorld_texture	= texture


	var
		x,y,z,border,
		map = []
	for( i=0;i<32*32*32;i++)
	{
		x = i&31
		y = (i>>5)&31
		z = i>>10

		border = 0
		if( x==0 || x==31 || y==0 || y==31 || z==0 || z==31 )
			border = 1

		textureIndex = (Math.round(Math.random()*64)%(texture.length>>6))
		if( Math.abs(x-16)<3 && Math.abs(y-16)<3 && Math.abs(z-16)<3  )
			border = 0

		if( border )
			textureIndex %= 2

		full = border || 100*Math.random()<10

		map[ i ] = full?textureIndex+1:0
	}
	document.fx_voxelWorld_map = map

	
	progressBarIndex++
}

function fx_voxelWorld_update( time, extraArguments )
{
	var
		i,
		w			= document.fx_voxelWorld_w,
		h			= document.fx_voxelWorld_h,
		imgDataIso	= document.fx_voxelWorld_imgDataIso,
		texture		= document.fx_voxelWorld_texture,
		paletteIso	= document.fx_voxelWorld_palette,
		map			= document.fx_voxelWorld_map,
		player		= document.fx_voxelWorld_player,
		w_2 = w/2,
		h_2 = h/2





	if( mouse.hover )
	{
		// turn head
		document.fx_voxelWorld_pitch	-= (.5+mouse.x)*.4
		document.fx_voxelWorld_yaw		+= (.5+mouse.y)*.3
	}


	document.fx_voxelWorld_pitch	= time/128
	document.fx_voxelWorld_yaw		= time/192



	if( mouse.down )
		light = (64*Math.cos(document.fx_voxelWorld_pitch+document.fx_voxelWorld_yaw+time))&15
	else
		light = 0



	var
		clampedPlayerPosX	= player.x&32767,
		clampedPlayerPosY	= player.y&32767,
	    clampedPlayerPosZ	= player.z&32767,
	    playerPosOffsetX	= -player.x&1023,
	    playerPosOffsetY	= -player.y&1023,
	    playerPosOffsetZ	= -player.z&1023,
	    fogIso				= paletteIso[0],
		textureOffset




	/*
	 *	compute the rotation matrix, rotate the screen plane, and compute the interpolation vectors
	 */

	rOrigin	= new v3f( 0,0,-48 )
	vP1	= new v3f( -64, 48,0)
	vP2	= new v3f(  64, 48,0)
	vP4	= new v3f( -64,-48,0)

	z = 24+0*Math.cos(0*1.5)
	Z = 8*8
	vOrigin = new v3f( player.x,player.y,player.z )
	vTarget = new v3f(	player.x+Z*Math.cos(document.fx_voxelWorld_pitch*Math.PI/128)*Math.cos(document.fx_voxelWorld_yaw*Math.PI/128),
						player.y+Z*Math.cos(document.fx_voxelWorld_pitch*Math.PI/128)*Math.sin(document.fx_voxelWorld_yaw*Math.PI/128),
						player.z+Z*Math.sin(document.fx_voxelWorld_pitch*Math.PI/128) )

	if( mouse.down && !map[ (vTarget.z&31744)+( vTarget.y>>5&992 )+( vTarget.x>>10&31 ) ] )
		player = new v3f( vTarget.x, vTarget.y, vTarget.z )
	document.fx_voxelWorld_player	= player



	zaxis	= new v3f( vTarget.x-vOrigin.x, vTarget.y-vOrigin.y, vTarget.z-vOrigin.z )

	l = 1.0/Math.sqrt( zaxis.x*zaxis.x+zaxis.y*zaxis.y+zaxis.z*zaxis.z )
	zaxis.x	*= l
	zaxis.y	*= l
	zaxis.z	*= l

	xaxis = new v3f( zaxis.z, 0, -zaxis.x )
	yaxis = new v3f( xaxis.z*zaxis.y-xaxis.y*zaxis.z, xaxis.x*zaxis.z-xaxis.z*zaxis.x, xaxis.y*zaxis.x-xaxis.x*zaxis.y )

	mcell = []

   	mcell[0]	= xaxis.x
   	mcell[1]	= yaxis.x
   	mcell[2]	= zaxis.x
  	mcell[3]	= rOrigin.x

   	mcell[4]	= xaxis.y
   	mcell[5]	= yaxis.y
   	mcell[6]	= zaxis.y
	mcell[7]	= rOrigin.y

   	mcell[8]	= xaxis.z
   	mcell[9]	= yaxis.z
   	mcell[10]	= zaxis.z
	mcell[11]	= rOrigin.z

	rOrigin = new v3f(	mcell[0] * rOrigin.x + mcell[1] * rOrigin.y + mcell[2]  * rOrigin.z + mcell[3],
              			mcell[4] * rOrigin.x + mcell[5] * rOrigin.y + mcell[6]  * rOrigin.z + mcell[7],
              			mcell[8] * rOrigin.x + mcell[9] * rOrigin.y + mcell[10] * rOrigin.z + mcell[11] )

	rP1 = new v3f(	mcell[0] * vP1.x + mcell[1] * vP1.y + mcell[2]  * vP1.z + mcell[3],
           			mcell[4] * vP1.x + mcell[5] * vP1.y + mcell[6]  * vP1.z + mcell[7],
           			mcell[8] * vP1.x + mcell[9] * vP1.y + mcell[10] * vP1.z + mcell[11] )


	rP2 = new v3f(	mcell[0] * vP2.x + mcell[1] * vP2.y + mcell[2]  * vP2.z + mcell[3],
           			mcell[4] * vP2.x + mcell[5] * vP2.y + mcell[6]  * vP2.z + mcell[7],
           			mcell[8] * vP2.x + mcell[9] * vP2.y + mcell[10] * vP2.z + mcell[11] )


	rP4 = new v3f(	mcell[0] * vP4.x + mcell[1] * vP4.y + mcell[2]  * vP4.z + mcell[3],
           			mcell[4] * vP4.x + mcell[5] * vP4.y + mcell[6]  * vP4.z + mcell[7],
           			mcell[8] * vP4.x + mcell[9] * vP4.y + mcell[10] * vP4.z + mcell[11] )

	l = 1.0/w
	rDx = new v3f( (rP2.x-rP1.x)*l, (rP2.y-rP1.y)*l, (rP2.z-rP1.z)*l )
	l = 1.0/h
	rDy = new v3f( (rP4.x-rP1.x)*l, (rP4.y-rP1.y)*l, (rP4.z-rP1.z)*l )

	rP1.x -= rOrigin.x
	rP1.y -= rOrigin.y
	rP1.z -= rOrigin.z

	/*
	 *
	 */


	k = 0
	for( var j=h;j--;)
	{
		rP2.x = rP1.x
		rP2.y = rP1.y
		rP2.z = rP1.z
		for( var i=w;i--;)
		{
			var
				impactSide,
				impactTexture,
				impactDistanceIncrement,
				currentImpactDistance,

				rayX = rP2.x,
				rayY = rP2.y,
				rayZ = rP2.z,

				rayOriginX		= Math.round( clampedPlayerPosX+rayX*.1 ),
				rayOriginY		= Math.round( clampedPlayerPosY+rayY*.1 ),
				rayOriginZ		= Math.round( clampedPlayerPosZ+rayZ*.1 ),
				impactDistance	= 64

			for( var side=3;side--; )
			{
				switch( side )
				{
					case 1:
					{
						// -/-> x
						impactDistanceIncrement = 1024/Math.abs( rayX )
						currentImpactDistance = playerPosOffsetX/rayX
						break
					}
					case 2:
					{
						// -/-> y
						impactDistanceIncrement = 1024/Math.abs( rayY )
						currentImpactDistance = playerPosOffsetY/rayY
						break
					}
					default:
					{
						// -/-> z
						impactDistanceIncrement = 1024/Math.abs( rayZ )
						currentImpactDistance = playerPosOffsetZ/rayZ
						break
					}
				}

				var
					rayXimpactDistanceIncrement = rayX*impactDistanceIncrement,
					rayYimpactDistanceIncrement = rayY*impactDistanceIncrement,
					rayZimpactDistanceIncrement = rayZ*impactDistanceIncrement,
					x = rayOriginX + rayX*currentImpactDistance,
					y = rayOriginY + rayY*currentImpactDistance,
					z = rayOriginZ + rayZ*currentImpactDistance

				while( currentImpactDistance<impactDistance )
				{
					if( currentImpactDistance>0 && (n=map[ (z&31744)+( y>>5&992 )+( x>>10&31 ) ]) )
					{
						impactDistance	= currentImpactDistance
						impactSide		= side
						impactTexture	= n
						break
					}
					x += rayXimpactDistanceIncrement
					y += rayYimpactDistanceIncrement
					z += rayZimpactDistanceIncrement
					currentImpactDistance+=impactDistanceIncrement
				}
			}
//*/


			// render texel
			if( impactDistance == 64 )
				imgDataIso += fogIso //paletteIso[ (i^j)&7 ]
			else
			{
				var
					impactX = rayOriginX + impactDistance*rayX,
					impactY = rayOriginY + impactDistance*rayY,
					impactZ = rayOriginZ + impactDistance*rayZ,
					texel,
					colorIndex
//*
				switch( impactSide )
				{
					case 1:
					{
						texel = (impactY>>4&56)+(impactZ>>7&7)
						break;
					}
					case 2:
					{
						texel = (impactX>>4&56)+(impactZ>>7&7)
						break;
					}
					default:
					{
						texel = (impactX>>4&56)+(impactY>>7&7)
					}
				}
/*/
				texel = 15
//*/
				colorIndex = texture[ (impactTexture-1)*64+texel ]
				if( light )
					imgDataIso += paletteIso[ Math.max( colorIndex&-128, Math.min( 127+(colorIndex&-128), colorIndex+light+impactSide*8-Math.round(impactDistance) ) ) ]
				else
					imgDataIso += paletteIso[ Math.max( colorIndex&-128, colorIndex+impactSide*8-Math.round(impactDistance) ) ]
			}
			rP2.x += rDx.x
			rP2.y += rDx.y
			rP2.z += rDx.z
		}
		rP1.x += rDy.x
		rP1.y += rDy.y
		rP1.z += rDy.z
	}
	customImageHandle.src = imgDataIso
}