extends base
append base_parms
	- tech = "three"
	
append base_help
	:markdown
		Can I get some help here?

append base_body

	script.
		if ( ! Detector.webgl ) Detector.addGetWebGLMessage();

		var container, stats;
		var camera, scene, renderer;
		var projector, plane, cube;
		var mouse2D, mouse3D, raycaster,
		rollOveredFace, isShiftDown = false,
		theta = 45 * 0.5, isCtrlDown = false;

		var rollOverMesh, rollOverMaterial;
		var voxelPosition = new THREE.Vector3(), tmpVec = new THREE.Vector3(), normalMatrix = new THREE.Matrix3();
		var cubeGeo, cubeMaterial;
		var i, intersector;

		var objects = [];

		init();
		animate();

		function init() {
			container = document.createElement( 'div' );
			document.body.appendChild( container );

			var info = document.createElement( 'div' );
			info.style.position = 'absolute';
			info.style.top = '10px';
			info.style.width = '100%';
			info.style.textAlign = 'center';
			info.innerHTML = '<a href="http://threejs.org" target="_blank">three.js</a> - voxel painter - webgl<br><strong>click</strong>: add voxel, <strong>shift + click</strong>: remove voxel, <strong>control</strong>: rotate';
			container.appendChild( info );

			camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 10000 );
			camera.position.y = 800;

			scene = new THREE.Scene();

			// roll-over helpers

			rollOverGeo = new THREE.BoxGeometry( 50, 50, 50 );
			rollOverMaterial = new THREE.MeshBasicMaterial( { color: 0xff0000, opacity: 0.5, transparent: true } );
			rollOverMesh = new THREE.Mesh( rollOverGeo, rollOverMaterial );
			scene.add( rollOverMesh );

			// cubes

			cubeGeo = new THREE.BoxGeometry( 50, 50, 50 );
			cubeMaterial = new THREE.MeshLambertMaterial( { color: 0xfeb74c, ambient: 0x00ff80, shading: THREE.FlatShading, map: THREE.ImageUtils.loadTexture( "textures/square-outline-textured.png" ) } );
			cubeMaterial.ambient = cubeMaterial.color;

			// picking

			projector = new THREE.Projector();

			// grid

			var size = 500, step = 50;

			var geometry = new THREE.Geometry();

			for ( var i = - size; i <= size; i += step ) {
				geometry.vertices.push( new THREE.Vector3( - size, 0, i ) );
				geometry.vertices.push( new THREE.Vector3(   size, 0, i ) );

				geometry.vertices.push( new THREE.Vector3( i, 0, - size ) );
				geometry.vertices.push( new THREE.Vector3( i, 0,   size ) );
			}

			var material = new THREE.LineBasicMaterial( { color: 0x000000, opacity: 0.2, transparent: true } );

			var line = new THREE.Line( geometry, material );
			line.type = THREE.LinePieces;
			scene.add( line );

			plane = new THREE.Mesh( new THREE.PlaneGeometry( 1000, 1000 ), new THREE.MeshBasicMaterial() );
			plane.rotation.x = - Math.PI / 2;
			plane.visible = false;
			scene.add( plane );

			objects.push( plane );

			mouse2D = new THREE.Vector3( 0, 10000, 0.5 );

			// Lights

			var ambientLight = new THREE.AmbientLight( 0x606060 );
			scene.add( ambientLight );

			var directionalLight = new THREE.DirectionalLight( 0xffffff );
			directionalLight.position.set( 1, 0.75, 0.5 ).normalize();
			scene.add( directionalLight );

			renderer = new THREE.WebGLRenderer( { antialias: true } );
			renderer.setClearColor( 0xf0f0f0 );
			renderer.setSize( window.innerWidth, window.innerHeight );

			container.appendChild( renderer.domElement );

			stats = new Stats();
			stats.domElement.style.position = 'absolute';
			stats.domElement.style.top = '0px';
			container.appendChild( stats.domElement );

			document.addEventListener( 'mousemove', onDocumentMouseMove, false );
			document.addEventListener( 'mousedown', onDocumentMouseDown, false );
			document.addEventListener( 'keydown', onDocumentKeyDown, false );
			document.addEventListener( 'keyup', onDocumentKeyUp, false );

			//

			window.addEventListener( 'resize', onWindowResize, false );
		}

		function onWindowResize() {
			camera.aspect = window.innerWidth / window.innerHeight;
			camera.updateProjectionMatrix();

			renderer.setSize( window.innerWidth, window.innerHeight );
		}

		function getRealIntersector( intersects ) {
			for( i = 0; i < intersects.length; i++ ) {
				intersector = intersects[ i ];
				if ( intersector.object != rollOverMesh ) {
					return intersector;
				}
			}
			return null;
		}

		function setVoxelPosition( intersector ) {

			if ( intersector.face === null ) {
				console.log( intersector )
			}

			normalMatrix.getNormalMatrix( intersector.object.matrixWorld );

			tmpVec.copy( intersector.face.normal );
			tmpVec.applyMatrix3( normalMatrix ).normalize();

			voxelPosition.addVectors( intersector.point, tmpVec );
			voxelPosition.divideScalar( 50 ).floor().multiplyScalar( 50 ).addScalar( 25 );
		}

		function onDocumentMouseMove( event ) {
			event.preventDefault();
			mouse2D.x = ( event.clientX / window.innerWidth ) * 2 - 1;
			mouse2D.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
		}

		function onDocumentMouseDown( event ) {

			event.preventDefault();

			var intersects = raycaster.intersectObjects( objects );

			if ( intersects.length > 0 ) {
				intersector = getRealIntersector( intersects );

				// delete cube

				if ( isShiftDown ) {
					if ( intersector.object != plane ) {
						scene.remove( intersector.object );
						objects.splice( objects.indexOf( intersector.object ), 1 );
					}

				// create cube
				} else {
					intersector = getRealIntersector( intersects );
					setVoxelPosition( intersector );

					var voxel = new THREE.Mesh( cubeGeo, cubeMaterial );
					voxel.position.copy( voxelPosition );
					voxel.matrixAutoUpdate = false;
					voxel.updateMatrix();
					scene.add( voxel );

					objects.push( voxel );
				}
			}
		}

		function onDocumentKeyDown( event ) {
			switch( event.keyCode ) {
				case 16: isShiftDown = true; break;
				case 17: isCtrlDown = true; break;
			}
		}

		function onDocumentKeyUp( event ) {
			switch ( event.keyCode ) {
				case 16: isShiftDown = false; break;
				case 17: isCtrlDown = false; break;
			}
		}

		//

		function animate() {
			requestAnimationFrame( animate );
			render();
			stats.update();
		}

		function render() {

			if ( isCtrlDown ) {
				theta += mouse2D.x * 1.5;
			}

			raycaster = projector.pickingRay( mouse2D.clone(), camera );

			var intersects = raycaster.intersectObjects( objects );

			if ( intersects.length > 0 ) {
				intersector = getRealIntersector( intersects );

				if ( intersector ) {
					setVoxelPosition( intersector );
					rollOverMesh.position.copy( voxelPosition );
				}
			}

			camera.position.x = 1400 * Math.sin( THREE.Math.degToRad( theta ) );
			camera.position.z = 1400 * Math.cos( THREE.Math.degToRad( theta ) );

			camera.lookAt( scene.position );

			renderer.render( scene, camera );
		}
