Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <html>
<head>
	<title>3D Minecraft Three.js Clone</title>
	<style type="text/css">

	</style>
</head>
<body>

	<script src = "perlin.js"></script>
	<script src = "PointerLockControls.js"></script>
	<script src = "stats.js"></script>

</html>
              
            
!

CSS

              
                		body {
			margin : 0;
		}
              
            
!

JS

              
                
 		var stats = new Stats();
 		stats.showPanel(0); // 0:fps, 1:ms, 2:mb, 3+:custom
 		document.body.appendChild(stats.dom);

 		function animate(){
 			stats.begin();

 			// monitored code goes between this called functions

 			stats.end();

 			requestAnimationFrame(animate);
 		}

 		requestAnimationFrame(animate);

 		noise.seed(Math.random());

 		var scene = new THREE.Scene();
 		scene.background = new THREE.Color(0x00ffff);
 		scene.fog = new THREE.Fog(0x00ffff, 10, 650);
		var renderer = new THREE.WebGLRenderer();
		renderer.setSize(window.innerWidth, window.innerHeight);
		document.body.appendChild(renderer.domElement);
		var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);

		/*
		var groundBox = new THREE.BoxBufferGeometry(25, 1, 50);
		var groundMesh = new THREE.MeshBasicMaterial({color : 0x00ff00});
		var ground = new THREE.Mesh(groundBox, groundMesh);
		scene.add(ground);
		ground.position.y = -5;
		// Creating the border lines for ground
		var edges = new THREE.EdgesGeometry(groundBox);
		var line = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({color : 0x000000}));
		scene.add(line);
		line.position.y = -5;
		*/

		var faces = [
			{ // left
			    dir: [ -5,  0,  0, "left"],
			},
			{ // right
			    dir: [  5,  0,  0, "right"],
			},
			{ // bottom
			    dir: [  0, -5,  0, "bottom"],
			},
			{ // top
			    dir: [  0,  5,  0, "top"],
			},
			{ // back
			    dir: [  0,  0, -5, "back"],
			},
			{ // front
			    dir: [  0,  0,  5, "front"],
			},
		];

		function Block(x, y, z, placed){
			this.x = x;
			this.y = y;
			this.z = z;
			this.placed = placed;
		}

		// var axesHelper = new THREE.AxesHelper( 5 );
		// scene.add( axesHelper );

		/*
		var blocks = [];
		var xoff = 0;
		var zoff = 0;
		var inc = 0.05;
		var amplitude = 50;
		for(var x = 0; x < 20; x++){
			xoff = 0;
			for(var z = 0; z < 20; z++){
				var v = Math.round(noise.perlin2(xoff, zoff) * amplitude / 5) * 5;
				blocks.push(new Block(x * 5, v, z * 5));
				xoff = xoff + inc;
			}
			zoff = zoff + inc;
		}
		*/

		var chunks = [];
		var xoff = 0;
		var zoff = 0;
		var inc = 0.05;
		var amplitude = 30 + (Math.random() * 70);
		var renderDistance = 5;
		var chunkSize = 10;
		camera.position.x = renderDistance * chunkSize / 2 * 5;
		camera.position.z = renderDistance * chunkSize / 2 * 5;
		camera.position.y = 50;

		var loader = new THREE.TextureLoader();
		var materialArray = [
			new THREE.MeshBasicMaterial({map : loader.load("https://assets.codepen.io/3581324/side.jpg")}),
			new THREE.MeshBasicMaterial({map : loader.load("https://assets.codepen.io/3581324/side.jpg")}),
			new THREE.MeshBasicMaterial({map : loader.load("https://assets.codepen.io/3581324/top.jpg")}),
			new THREE.MeshBasicMaterial({map : loader.load("https://assets.codepen.io/3581324/bottom.jpg")}),
			new THREE.MeshBasicMaterial({map : loader.load("https://assets.codepen.io/3581324/side.jpg")}),
			new THREE.MeshBasicMaterial({map : loader.load("https://assets.codepen.io/3581324/side.jpg")}),
		];

		var blockBox = new THREE.BoxGeometry(5, 5, 5)
		var instancedChunk = new THREE.InstancedMesh(blockBox, materialArray, renderDistance * renderDistance * chunkSize * chunkSize);
		var count = 0;
		for(var i = 0; i < renderDistance; i++){
			for(j = 0; j < renderDistance; j++){
				var chunk = [];
				for(var x = i * chunkSize; x < (i * chunkSize) + chunkSize; x++){
					for(var z = j * chunkSize; z < (j * chunkSize) + chunkSize; z++){
						xoff = inc * x;
						zoff = inc * z;
						var v = Math.round(noise.perlin2(xoff, zoff) * amplitude / 5) * 5;
						chunk.push(new Block(x * 5, v, z * 5));
						let matrix = new THREE.Matrix4().makeTranslation(
							x * 5,
							v,
							z * 5
						);
						instancedChunk.setMatrixAt(count, matrix);
						count++;
					}
				}
				chunks.push(chunk);
			}
		}

		scene.add(instancedChunk);

		var keys = [];
		var canJump = true;
		var controlOptions = {
			forward : "w",
			backward : "s",
			right : "d",
			left : "a",
			jump : " ", // " " = space
			placeBlock : "q" 
		};

		var placedBlocks = [];
		var chunkMap = [];
		for(var x = 0; x < renderDistance; x++){
			for(var z = 0; z < renderDistance; z++){
				chunkMap.push({x : x, z : z});
			}
		}

		function identifyChunk(x, z){
			var lowestX = lowestXBlock();
			var lowestZ = lowestZBlock();
			var difX = x - lowestX;
			var difZ = z - lowestZ;
			var divX = Math.floor(difX / (chunkSize * 5));
			var divZ = Math.floor(difZ / (chunkSize * 5));
			var index = undefined;
			for(var i = 0; i < chunkMap.length; i++){
				if(chunkMap[i].x == divX && chunkMap[i].z == divZ){
					index = i;
					break;
				}
			}
			return index; // Identified the chunks!!!
		}

		var start = 0;
		var sprint = true;
		document.addEventListener("keydown", function(e){
			if(e.key == "w") {
		        var elapsed = new Date().getTime();
		        if(elapsed - start <= 300){
		        	sprint = true;
		        }
		        start = elapsed;
		    }

			keys.push(e.key);

			if(e.key == controlOptions.jump && canJump == true){
				ySpeed = -1;
				canJump = false;
			}
			if(e.key == controlOptions.placeBlock){
				const raycaster = new THREE.Raycaster();
				const pointer = new THREE.Vector2();
				pointer.x = (0.5) * 2 - 1;
				pointer.y = -1 * (0.5) * 2 + 1;
				raycaster.setFromCamera(pointer, camera);
				var intersection = raycaster.intersectObject(instancedChunk);

				if(intersection[0] != undefined && intersection[0].distance < 40){
					console.log(intersection[0]);
					var materialIndex = intersection[0].face.materialIndex;
					var position = intersection[0].point; // object with x, y and z coords
					var x = 0;
					var y = 10;
					var z = 0;
					const inc = 2.5; 
					switch(materialIndex){
						case 0: // right
							x = position.x + inc;
							y = Math.round(position.y / 5) * 5;
							z = Math.round(position.z / 5) * 5;
							break;
						case 1: // left
							x = position.x - inc;
							y = Math.round(position.y / 5) * 5;
							z = Math.round(position.z / 5) * 5;
							break;
						case 2: // top
							x = Math.round(position.x / 5) * 5;
							y = position.y + inc;
							z = Math.round(position.z / 5) * 5;
							break;
						case 3: // bottom
							x = Math.round(position.x / 5) * 5;
							y = position.y - inc;
							z = Math.round(position.z / 5) * 5;
							break;
						case 4: // front
							x = Math.round(position.x / 5) * 5;
							y = Math.round(position.y / 5) * 5;
							z = position.z + inc;
							break;
						case 5: // back
							x = Math.round(position.x / 5) * 5;
							y = Math.round(position.y / 5) * 5;
							z = position.z - inc;
							break;
					}
					y = Math.round(y); // sometimes, y is for some reason e.g 4.999999999999
					var b = {x : x, y : y, z : z};
					if(!intersect(b.x, b.y, b.z, 5, 5, 5, player.x, player.y, player.z, player.w, player.h, player.d)){
						chunks[identifyChunk(x, z)].push(new Block(x, y, z, true));
						placedBlocks.push(b);

						scene.remove(instancedChunk);

						instancedChunk = new THREE.InstancedMesh(blockBox, materialArray, (renderDistance * renderDistance * chunkSize * chunkSize) + placedBlocks.length);
						var count = 0;
						for(var i = 0; i < chunks.length; i++){
							for(var j = 0; j < chunks[i].length; j++){
								let matrix = new THREE.Matrix4().makeTranslation(
									chunks[i][j].x,
									chunks[i][j].y,
									chunks[i][j].z
								);
								instancedChunk.setMatrixAt(count, matrix);
								count++;
							}
						}
						scene.add(instancedChunk);
					}		
				}
			}
		});
		document.addEventListener("keyup", function(e){
			var newArr = [];
			for(var i = 0; i < keys.length; i++){
				if(keys[i] != e.key){
					newArr.push(keys[i]);
				}
			}
			keys = newArr;
			if(!keys.includes("w")){
				sprint = false;
			}
		});

		var controls = new THREE.PointerLockControls(camera, document.body);
		var brokenBlocks = [];
		document.body.addEventListener("click", function(){
			controls.lock();
			// Breaking blocks
			if(controls.isLocked){
				// Shooting a ray
				const raycaster = new THREE.Raycaster();
				const pointer = new THREE.Vector2();
				pointer.x = (0.5) * 2 - 1;
				pointer.y = -1 * (0.5) * 2 + 1;
				raycaster.setFromCamera(pointer, camera);
				var intersection = raycaster.intersectObject(instancedChunk);
				if(intersection[0] != undefined && intersection[0].distance < 40){
					// finding x, y, z positions of that 
					console.log(intersection[0].point);
					var materialIndex = intersection[0].face.materialIndex;
					var position = intersection[0].point; // object with x, y and z coords
					var x = 0;
					var y = 0;
					var z = 0;
					const inc = 2.5; 
					switch(materialIndex){ // finding x, y, z positions of block
						case 0: // right
							x = position.x - inc;
							y = Math.round(position.y / 5) * 5;
							z = Math.round(position.z / 5) * 5;
							break;
						case 1: // left
							x = position.x + inc;
							y = Math.round(position.y / 5) * 5;
							z = Math.round(position.z / 5) * 5;
							break;
						case 2: // top
							x = Math.round(position.x / 5) * 5;
							y = position.y - inc;
							z = Math.round(position.z / 5) * 5;
							break;
						case 3: // bottom
							x = Math.round(position.x / 5) * 5;
							y = position.y + inc;
							z = Math.round(position.z / 5) * 5;
							break;
						case 4: // front
							x = Math.round(position.x / 5) * 5;
							y = Math.round(position.y / 5) * 5;
							z = position.z - inc;
							break;
						case 5: // back
							x = Math.round(position.x / 5) * 5;
							y = Math.round(position.y / 5) * 5;
							z = position.z + inc;
							break;
					}
					// Find block with those x, y, z positions
					// More efficient by finding it inside it's chunk
					var index1 = identifyChunk(x, z);
					var chunk = chunks[index1];
					y = Math.round(y); // sometimes, y is for some reason e.g 4.999999999999
					for(var i = 0; i < chunk.length; i++){
						if(chunk[i].x == x && chunk[i].y == y && chunk[i].z == z){
							// Found the block!
							if(chunk[i].placed){
								// find the placedBlock and remove it
								for(var j = 0; j < placedBlocks.length; j++){
									if(placedBlocks[j].x == x && placedBlocks[j].y == y && placedBlocks[j].z == z){
										placedBlocks.splice(j, 1);
										break;
									}
								}
							} else { // if it is a normal block
								brokenBlocks.push({x : x, y : y, z : z});
							}
							chunks[index1].splice(i, 1); // block is removed from chunks variable
							break;
						}
					}
					// update chunks, array.splice(index, 1);
					scene.remove(instancedChunk);
					instancedChunk = new THREE.InstancedMesh(blockBox, materialArray, (renderDistance * renderDistance * chunkSize * chunkSize) + placedBlocks.length);
					var count = 0;
					for(var i = 0; i < chunks.length; i++){
						for(var j = 0; j < chunks[i].length; j++){
							let matrix = new THREE.Matrix4().makeTranslation(
								chunks[i][j].x,
								chunks[i][j].y,
								chunks[i][j].z
							);
							instancedChunk.setMatrixAt(count, matrix);
							count++;
						}
					}
					scene.add(instancedChunk);
				}
			}
		});
		controls.addEventListener("lock", function(){

		});
		controls.addEventListener("unlock", function(){
			keys = [];
		});

		var movingSpeed = 0.7;
		var ySpeed = 0;
		var acc = 0.065;

		var player = {
			w : 0.6, // width
			h : 8, // height
			d : 0.5, // depth
			x : camera.position.x,
			y : camera.position.y,
			z : camera.position.z,
			forward : function(speed){
				controls.moveForward(speed);
				this.updatePosition();
			},
			backward : function(speed){
				controls.moveForward(-1 * speed);
				this.updatePosition();
			},
			right : function(speed){
				controls.moveRight(speed);
				this.updatePosition();
			},
			left : function(speed){
				controls.moveRight(-1 * speed);
				this.updatePosition();
			},
			updatePosition : function(){
				this.x = camera.position.x;
				this.y = camera.position.y - (this.h / 2);
				this.z = camera.position.z;
			}
		};

		function intersect(x1, y1, z1, w1, h1, d1, x2, y2, z2, w2, h2, d2){
			var a = {
				minX : x1 - (w1/2),
				maxX : x1 + (w1/2),
				minZ : z1 - (d1/2),
				maxZ : z1 + (d1/2),
				minY : y1 - (h1/2),
				maxY : y1 + (h1/2),
			};
			var b = {
				minX : x2 - (w2/2),
				maxX : x2 + (w2/2),
				minZ : z2 - (d2/2),
				maxZ : z2 + (d2/2),
				minY : y2 - (h2/2),
				maxY : y2 + (h2/2),
			};
			return (a.minX <= b.maxX && a.maxX >= b.minX) &&
		           (a.minY <= b.maxY && a.maxY >= b.minY) &&
		           (a.minZ <= b.maxZ && a.maxZ >= b.minZ);
		}

		var deceleration = 1.15;
		var forback = 0; // 1 = forward, -1 = backward
		var rightleft = 0; // 1 = right, -1 = left
		var sprintSpeedInc = 1.3; // 30% faster than walking
		function update(){	
			player.updatePosition();

			if(keys.includes(controlOptions.forward)){
				player.forward(movingSpeed * (sprint ? sprintSpeedInc : 1));
				forback = 1 * movingSpeed;
				for(var i = 0; i < chunks.length; i++){
					for(var j = 0; j < chunks[i].length; j++){
						var b = chunks[i][j];
						var c = intersect(b.x, b.y, b.z, 5, 5, 5, player.x, player.y, player.z, player.w, player.h, player.d);
						if(c && (b.y - 2.5 < player.y + (player.h / 2) && b.y + 2.5 > player.y - (player.h / 2))){
							player.backward((movingSpeed * (sprint ? sprintSpeedInc : 1)));
							forback = 0;
							rightleft = 0;
							sprint = false;
						}
					}
				}
			}
			if(keys.includes(controlOptions.backward)){
				player.backward(movingSpeed * (sprint ? sprintSpeedInc : 1));
				forback = -1 * movingSpeed;
				for(var i = 0; i < chunks.length; i++){
					for(var j = 0; j < chunks[i].length; j++){
						var b = chunks[i][j];
						var c = intersect(b.x, b.y, b.z, 5, 5, 5, player.x, player.y, player.z, player.w, player.h, player.d);
						if(c && (b.y - 2.5 < player.y + (player.h / 2) && b.y + 2.5 > player.y - (player.h / 2))){
							player.forward(movingSpeed * (sprint ? sprintSpeedInc : 1));
							forback = 0;
							rightleft = 0;
							sprint = false;
						}
					}
				}
			}
			if(keys.includes(controlOptions.right)){
				player.right(movingSpeed * (sprint ? sprintSpeedInc : 1));
				rightleft = 1 * movingSpeed;
				for(var i = 0; i < chunks.length; i++){
					for(var j = 0; j < chunks[i].length; j++){
						var b = chunks[i][j];
						var c = intersect(b.x, b.y, b.z, 5, 5, 5, player.x, player.y, player.z, player.w, player.h, player.d);
						if(c && (b.y - 2.5 < player.y + (player.h / 2) && b.y + 2.5 > player.y - (player.h / 2))){
							player.left(movingSpeed * (sprint ? sprintSpeedInc : 1));
							forback = 0;
							rightleft = 0;
							sprint = false;
						}
					}
				}
			}
			if(keys.includes(controlOptions.left)){
				player.left(movingSpeed * (sprint ? sprintSpeedInc : 1));
				rightleft = -1 * movingSpeed;
				for(var i = 0; i < chunks.length; i++){
					for(var j = 0; j < chunks[i].length; j++){
						var b = chunks[i][j];
						var c = intersect(b.x, b.y, b.z, 5, 5, 5, player.x, player.y, player.z, player.w, player.h, player.d);
						if(c && (b.y - 2.5 < player.y + (player.h / 2) && b.y + 2.5 > player.y - (player.h / 2))){
							player.right(movingSpeed * (sprint ? sprintSpeedInc : 1));
							forback = 0;
							rightleft = 0;
							sprint = false;
						}
					}
				}
			}

			// Decceleration part
			if(!keys.includes(controlOptions.forward) && !keys.includes(controlOptions.backward) && !keys.includes(controlOptions.right) && !keys.includes(controlOptions.left)){
				forback /= deceleration;
				rightleft /= deceleration;
				for(var i = 0; i < chunks.length; i++){
					for(var j = 0; j < chunks[i].length; j++){
						var b = chunks[i][j];
						var c = intersect(b.x, b.y, b.z, 5, 5, 5, player.x, player.y, player.z, player.w, player.h, player.d);
						if(c && (b.y - 2.5 < player.y + (player.h / 2) && b.y + 2.5 > player.y - (player.h / 2))){
							var br = true;
							forback /= -deceleration;
							rightleft /= -deceleration;
							sprint = false;
							break;
						}
					}
					if(br){
						break;
					}
				}
				player.forward(forback * (sprint ? sprintSpeedInc : 1));
				player.right(rightleft * (sprint ? sprintSpeedInc : 1));
			}
			
			camera.position.y = camera.position.y - ySpeed;
			ySpeed = ySpeed + acc;

			// Not falling through a block or above a block (above collision)
			for(var i = 0; i < chunks.length; i++){
				for(var j = 0; j < chunks[i].length; j++){
					var b = chunks[i][j];
					var c = intersect(b.x, b.y + 10, b.z, 5, 5, 5, player.x, player.y, player.z, player.w, player.h, player.d);
					if(c && camera.position.y <= chunks[i][j].y + 2.5 + player.h && camera.position.y >= chunks[i][j].y){
						camera.position.y = chunks[i][j].y + 2.5 + player.h;
						ySpeed = 0;
						canJump = true;
					}
					var c = intersect(b.x, b.y, b.z, 5, 5, 5, player.x, player.y, player.z, player.w, player.h, player.d); // this one doesn't have a + 10 in the b.y
					if(c && camera.position.y >= chunks[i][j].y - 2.5 && camera.position.y <= chunks[i][j].y){
						ySpeed = 0.5;
					}
				}
			}

			// INFINITE TERRAIN GENERATION PART!
			var worldSize = chunkSize * renderDistance * 5;
			var ratio = 0.4;
			if(camera.position.z < lowestZBlock() + (worldSize * ratio)){ // 20 is 4 blocks
				/*
					
					[0], [3], [6],
					[1], [x], [7],
					[2], [5], [8],
				*/

				var newChunks = [];
				for(var i = 0; i < chunks.length; i++){
					if((i + 1) % renderDistance != 0){
						newChunks.push(chunks[i]);
					}
				}

				// add blocks
				var lowestX = lowestXBlock();
				var lowestZ = lowestZBlock();

				for(var i = 0; i < renderDistance; i++){
					var chunk = [];
					for(var x = lowestX + (i * chunkSize * 5); x < lowestX + (i * chunkSize * 5) + (chunkSize * 5); x = x + 5){
						for(var z = lowestZ - (chunkSize * 5); z < lowestZ; z = z + 5){
							xoff = inc * x / 5;
							zoff = inc * z / 5;
							var v = Math.round(noise.perlin2(xoff, zoff) * amplitude / 5) * 5;
							// Try to find a broken block in that position
							var blockIsDestroyed = false;
							for(var d = 0; d < brokenBlocks.length; d++){
								if(brokenBlocks[d].x == x && brokenBlocks[d].y == v && brokenBlocks[d].z == z){
									blockIsDestroyed = true;
									break;
								}
							}
							if(!blockIsDestroyed){
								chunk.push(new Block(x, v, z));
							}
							// Check if there is also a placed block there
							for(var b = 0; b < placedBlocks.length; b++){
								if(placedBlocks[b].x == x && placedBlocks[b].z == z){
									chunk.push(new Block(placedBlocks[b].x, placedBlocks[b].y, placedBlocks[b].z, true));
								}
							}
						}
					}
					newChunks.splice(i * renderDistance, 0, chunk);
				}

				chunks = newChunks;

				scene.remove(instancedChunk);

				instancedChunk = new THREE.InstancedMesh(blockBox, materialArray, (renderDistance * renderDistance * chunkSize * chunkSize) + placedBlocks.length);
				var count = 0;
				for(var i = 0; i < chunks.length; i++){
					for(var j = 0; j < chunks[i].length; j++){
						let matrix = new THREE.Matrix4().makeTranslation(
							chunks[i][j].x,
							chunks[i][j].y,
							chunks[i][j].z
						);
						instancedChunk.setMatrixAt(count, matrix);
						count++;
					}
				}
				scene.add(instancedChunk);
			}

			if(camera.position.z > highestZBlock() - (worldSize * ratio)){ // 20 is 4 blocks
				/*
					
					[0], [3], [6],
					[1], [x], [7],
					[2], [5], [8],
				*/

				var newChunks = [];
				for(var i = 0; i < chunks.length; i++){
					if(i % renderDistance != 0){
						newChunks.push(chunks[i]);
					}
				}

				// add blocks
				var lowestX = lowestXBlock();
				var highestZ = highestZBlock();
				for(var i = 0; i < renderDistance; i++){
					var chunk = [];
					for(var x = lowestX + (i * chunkSize * 5); x < lowestX + (i * chunkSize * 5) + (chunkSize * 5); x = x + 5){
						for(var z = highestZ + 5; z < (highestZ + 5) + (chunkSize * 5); z = z + 5){
							xoff = inc * x / 5;
							zoff = inc * z / 5;
							var v = Math.round(noise.perlin2(xoff, zoff) * amplitude / 5) * 5;
							// Try to find a broken block in that position
							var blockIsDestroyed = false;
							for(var d = 0; d < brokenBlocks.length; d++){
								if(brokenBlocks[d].x == x && brokenBlocks[d].y == v && brokenBlocks[d].z == z){
									blockIsDestroyed = true;
									break;
								}
							}
							if(!blockIsDestroyed){
								chunk.push(new Block(x, v, z));
							}
							// Check if there is also a placed block there
							for(var b = 0; b < placedBlocks.length; b++){
								if(placedBlocks[b].x == x && placedBlocks[b].z == z){
									chunk.push(new Block(placedBlocks[b].x, placedBlocks[b].y, placedBlocks[b].z, true));
								}
							}
						}
					}
					newChunks.splice(((i + 1) * renderDistance) - 1, 0, chunk);
				}

				chunks = newChunks;

				scene.remove(instancedChunk);

				instancedChunk = new THREE.InstancedMesh(blockBox, materialArray, (renderDistance * renderDistance * chunkSize * chunkSize) + placedBlocks.length);
				var count = 0;
				for(var i = 0; i < chunks.length; i++){
					for(var j = 0; j < chunks[i].length; j++){
						let matrix = new THREE.Matrix4().makeTranslation(
							chunks[i][j].x,
							chunks[i][j].y,
							chunks[i][j].z
						);
						instancedChunk.setMatrixAt(count, matrix);
						count++;
					}
				}
				scene.add(instancedChunk);		
			}

			if(camera.position.x > highestXBlock() - (worldSize * ratio)){ // 20 is 4 blocks
				/*
					
					[0], [3], [6],
					[1], [x], [7],
					[2], [5], [8],
				*/

				var newChunks = [];
				for(var i = renderDistance; i < chunks.length; i++){
					newChunks.push(chunks[i]);
				}

				// add blocks
				var highestX = highestXBlock();
				var lowestZ = lowestZBlock();

				for(var i = 0; i < renderDistance; i++){
					var chunk = [];
					for(var z = lowestZ + (i * chunkSize * 5); z < lowestZ + (i * chunkSize * 5) + (chunkSize * 5); z = z + 5){
						for(var x = highestX + 5; x < highestX + 5 + (chunkSize * 5); x = x + 5){
							xoff = inc * x / 5;
							zoff = inc * z / 5;
							var v = Math.round(noise.perlin2(xoff, zoff) * amplitude / 5) * 5;
							// Try to find a broken block in that position
							var blockIsDestroyed = false;
							for(var d = 0; d < brokenBlocks.length; d++){
								if(brokenBlocks[d].x == x && brokenBlocks[d].y == v && brokenBlocks[d].z == z){
									blockIsDestroyed = true;
									break;
								}
							}
							if(!blockIsDestroyed){
								chunk.push(new Block(x, v, z));
							}
							// Check if there is also a placed block there
							for(var b = 0; b < placedBlocks.length; b++){
								if(placedBlocks[b].x == x && placedBlocks[b].z == z){
									chunk.push(new Block(placedBlocks[b].x, placedBlocks[b].y, placedBlocks[b].z, true));
								}
							}
						}
					}
					newChunks.splice(chunks.length - (renderDistance - i), 0, chunk);
				}

				chunks = newChunks;

				scene.remove(instancedChunk);

				instancedChunk = new THREE.InstancedMesh(blockBox, materialArray, (renderDistance * renderDistance * chunkSize * chunkSize) + placedBlocks.length);
				var count = 0;
				for(var i = 0; i < chunks.length; i++){
					for(var j = 0; j < chunks[i].length; j++){
						let matrix = new THREE.Matrix4().makeTranslation(
							chunks[i][j].x,
							chunks[i][j].y,
							chunks[i][j].z
						);
						instancedChunk.setMatrixAt(count, matrix);
						count++;
					}
				}
				scene.add(instancedChunk);				
			}

			if(camera.position.x < lowestXBlock() + (worldSize * ratio)){ // 20 is 4 blocks
				/*
					
					[0], [3], [6],
					[1], [x], [7],
					[2], [5], [8],
				*/

				var newChunks = [];
				for(var i = 0; i < chunks.length - renderDistance; i++){
					newChunks.push(chunks[i]);
				}

				// add blocks
				var lowestX = lowestXBlock();
				var lowestZ = lowestZBlock();
				for(var i = 0; i < renderDistance; i++){
					var chunk = [];
					for(var z = lowestZ + (i * chunkSize * 5); z < lowestZ + (i * chunkSize * 5) + (chunkSize * 5); z = z + 5){
						for(var x = lowestX - (chunkSize * 5); x < lowestX; x = x + 5){
							xoff = inc * x / 5;
							zoff = inc * z / 5;
							var v = Math.round(noise.perlin2(xoff, zoff) * amplitude / 5) * 5;
							// Try to find a broken block in that position
							var blockIsDestroyed = false;
							for(var d = 0; d < brokenBlocks.length; d++){
								if(brokenBlocks[d].x == x && brokenBlocks[d].y == v && brokenBlocks[d].z == z){
									blockIsDestroyed = true;
									break;
								}
							}
							if(!blockIsDestroyed){
								chunk.push(new Block(x, v, z));
							}
							// Check if there is also a placed block there
							for(var b = 0; b < placedBlocks.length; b++){
								if(placedBlocks[b].x == x && placedBlocks[b].z == z){
									chunk.push(new Block(placedBlocks[b].x, placedBlocks[b].y, placedBlocks[b].z, true));
								}
							}
						}
					}
					newChunks.splice(i, 0, chunk);
				}

				chunks = newChunks;

				scene.remove(instancedChunk);

				instancedChunk = new THREE.InstancedMesh(blockBox, materialArray, (renderDistance * renderDistance * chunkSize * chunkSize) + placedBlocks.length);
				var count = 0;
				for(var i = 0; i < chunks.length; i++){
					for(var j = 0; j < chunks[i].length; j++){
						let matrix = new THREE.Matrix4().makeTranslation(
							chunks[i][j].x,
							chunks[i][j].y,
							chunks[i][j].z
						);
						instancedChunk.setMatrixAt(count, matrix);
						count++;
					}
				}
				scene.add(instancedChunk);	
			}
		}

		function lowestXBlock(){
			var xPosArray = [];
			for(var i = 0; i < chunks.length; i++){
				for(var j = 0; j < chunks[i].length; j++){
					xPosArray.push(chunks[i][j].x);
				}
			}
			return Math.min.apply(null, xPosArray);
		}

		function highestXBlock(){
			var xPosArray = [];
			for(var i = 0; i < chunks.length; i++){
				for(var j = 0; j < chunks[i].length; j++){
					xPosArray.push(chunks[i][j].x);
				}
			}
			return Math.max.apply(null, xPosArray);
		}

		function lowestZBlock(){
			var zPosArray = [];
			for(var i = 0; i < chunks.length; i++){
				for(var j = 0; j < chunks[i].length; j++){
					zPosArray.push(chunks[i][j].z);
				}
			}
			return Math.min.apply(null, zPosArray);
		}

		function highestZBlock(){
			var zPosArray = [];
			for(var i = 0; i < chunks.length; i++){
				for(var j = 0; j < chunks[i].length; j++){
					zPosArray.push(chunks[i][j].z);
				}
			}
			return Math.max.apply(null, zPosArray);
		}

		// Resize Window
		window.addEventListener("resize", function(){
			renderer.setSize(window.innerWidth, window.innerHeight);
			camera.aspect = window.innerWidth / window.innerHeight;
			camera.updateProjectionMatrix();
		});

		const raycaster = new THREE.Raycaster();
		const pointer = new THREE.Vector2();
		pointer.x = (0.5) * 2 - 1;
		pointer.y = -1 * (0.5) * 2 + 1;

		var plane;
		function render(){
			raycaster.setFromCamera(pointer, camera);
			var intersection = raycaster.intersectObject(instancedChunk);
			if(intersection[0] != undefined && intersection[0].distance < 40){
				//console.log(intersection[0]);
				if(!scene.children.includes(plane)){
					var planeG = new THREE.PlaneGeometry(5, 5);
					var planeM = new THREE.MeshBasicMaterial({color : 0xffffff, side : THREE.DoubleSide});
					planeM.transparent = true;
					planeM.opacity = 0.5;
					plane = new THREE.Mesh(planeG, planeM);
					scene.add(plane);
				} else {
					plane.visible = true;
					var materialIndex = intersection[0].face.materialIndex;
					var position = intersection[0].point; // object with x, y and z coords
					var x = 0;
					var y = 0;
					var z = 0;
					const inc = 0.1; 
					switch(materialIndex){
						case 0: // right
							plane.rotation.x = 0;
							plane.rotation.y = (Math.PI / 2);
							plane.rotation.z = 0;
							x = position.x + inc;
							y = Math.round(position.y / 5) * 5;
							z = Math.round(position.z / 5) * 5;
							break;
						case 1: // left
							plane.rotation.x = 0;
							plane.rotation.y = (Math.PI / 2);
							plane.rotation.z = 0;
							x = position.x - inc;
							y = Math.round(position.y / 5) * 5;
							z = Math.round(position.z / 5) * 5;
							break;
						case 2: // top
							plane.rotation.x = (Math.PI / 2);
							plane.rotation.y = 0;
							plane.rotation.z = 0;
							x = Math.round(position.x / 5) * 5;
							y = position.y + inc;
							z = Math.round(position.z / 5) * 5;
							break;
						case 3: // bottom
							plane.rotation.x = (Math.PI / 2);
							plane.rotation.y = 0;
							plane.rotation.z = 0;
							x = Math.round(position.x / 5) * 5;
							y = position.y - inc;
							z = Math.round(position.z / 5) * 5;
							break;
						case 4: // front
							plane.rotation.x = 0;
							plane.rotation.y = 0;
							plane.rotation.z = 0;
							x = Math.round(position.x / 5) * 5;
							y = Math.round(position.y / 5) * 5;
							z = position.z + inc;
							break;
						case 5: // back
							plane.rotation.x = 0;
							plane.rotation.y = 0;
							plane.rotation.z = 0;
							x = Math.round(position.x / 5) * 5;
							y = Math.round(position.y / 5) * 5;
							z = position.z - inc;
							break;
					}
					plane.position.x = x;
					plane.position.y = y;
					plane.position.z = z;
				}
			} else {
				if(plane){
					plane.visible = false;
				}
			}

			renderer.render(scene, camera);
		}

		function GameLoop(){
			requestAnimationFrame(GameLoop);
			update();
			render();
		}

		GameLoop();

              
            
!
999px

Console