Browse Source

Merge branch 'Terrain-Raycast-Example' of git://github.com/insominx/three.js into dev

Mr.doob 12 years ago
parent
commit
f6a0432300
1 changed files with 320 additions and 0 deletions
  1. 320 0
      examples/webgl_geometry_terrain_raycast.html

+ 320 - 0
examples/webgl_geometry_terrain_raycast.html

@@ -0,0 +1,320 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - geometry - terrain</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			body {
+				color: #61443e;
+				font-family:Monospace;
+				font-size:13px;
+				text-align:center;
+
+				background-color: #bfd1e5;
+				margin: 0px;
+				overflow: hidden;
+			}
+
+			#info {
+				position: absolute;
+				top: 0px; width: 100%;
+				padding: 5px;
+			}
+
+			a {
+
+				color: #a06851;
+			}
+
+		</style>
+	</head>
+	<body>
+
+		<div id="container"><br /><br /><br /><br /><br />Generating world...</div>
+		<div id="info"><a href="http://threejs.org" target="_blank">three.js</a> - webgl terrain raycasting demo<br />(left click: forward, right click: backward)</div>
+
+		<script src="../build/three.min.js"></script>
+
+		<script src="js/controls/OrbitControls.js"></script>
+
+		<script src="js/ImprovedNoise.js"></script>
+		<script src="js/Detector.js"></script>
+		<script src="js/libs/stats.min.js"></script>
+
+		<script>
+
+			if ( ! Detector.webgl ) {
+
+				Detector.addGetWebGLMessage();
+				document.getElementById( 'container' ).innerHTML = "";
+
+			}
+
+			var container, stats;
+
+			var camera, controls, scene, renderer;
+
+			var mesh, texture;
+
+			var worldWidth = 256, worldDepth = 256,
+			worldHalfWidth = worldWidth / 2, worldHalfDepth = worldDepth / 2;
+
+			var clock = new THREE.Clock();
+
+			var shouldSphereFollowMouse = true;
+			var checkeredSphere;
+
+			init();
+			animate();
+
+			function init() {
+
+				container = document.getElementById( 'container' );
+
+				camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 20000 );
+
+				scene = new THREE.Scene();
+
+				controls = new THREE.OrbitControls(camera);
+				controls.center.set(0.0, 100.0, 0.0);
+				controls.userPanSpeed = 100;
+
+				data = generateHeight( worldWidth, worldDepth );
+
+				controls.center.y = data[ worldHalfWidth + worldHalfDepth * worldWidth ] + 500;
+				camera.position.y =  controls.center.y + 2000;
+				camera.position.x = 2000;
+
+				var geometry = new THREE.PlaneGeometry( 7500, 7500, worldWidth - 1, worldDepth - 1 );
+				geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );
+
+				for ( var i = 0, l = geometry.vertices.length; i < l; i ++ ) {
+
+					geometry.vertices[ i ].y = data[ i ] * 10;
+
+				}
+
+				// PLEASE NOTE!! With raycasting faces must be planar!  PlaneGeometry is made up of
+				// quads and now that we have changed the height value of the verts, the quads are no
+				// longer planar.  We must break it down into triangles in order to preserve this information.
+				THREE.GeometryUtils.triangulateQuads(geometry);
+
+				texture = new THREE.Texture( generateTexture( data, worldWidth, worldDepth ), new THREE.UVMapping(), THREE.ClampToEdgeWrapping, THREE.ClampToEdgeWrapping );
+				texture.needsUpdate = true;
+
+				mesh = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { map: texture } ) );
+				scene.add( mesh );
+
+				var sphereGeometry = new THREE.SphereGeometry(100, 20, 20);
+				checkeredSphere = new THREE.Mesh(sphereGeometry, new THREE.MeshBasicMaterial( { map: generateCheckerTexture() } ) );
+				checkeredSphere.position.y = 1000;
+
+				scene.add( checkeredSphere );
+
+				renderer = new THREE.WebGLRenderer();
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+				container.innerHTML = "";
+
+				container.appendChild( renderer.domElement );
+				container.addEventListener( 'mousemove', onMouseMove, false );
+
+				stats = new Stats();
+				stats.domElement.style.position = 'absolute';
+				stats.domElement.style.top = '0px';
+				container.appendChild( stats.domElement );
+
+				//
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+				controls.handleResize();
+
+			}
+
+			function generateHeight( width, height ) {
+
+				var size = width * height, data = new Float32Array( size ),
+				perlin = new ImprovedNoise(), quality = 1, z = Math.random() * 100;
+
+				for ( var i = 0; i < size; i ++ ) {
+
+					data[ i ] = 0
+
+				}
+
+				for ( var j = 0; j < 4; j ++ ) {
+
+					for ( var i = 0; i < size; i ++ ) {
+
+						var x = i % width, y = ~~ ( i / width );
+						data[ i ] += Math.abs( perlin.noise( x / quality, y / quality, z ) * quality * 1.75 );
+
+
+					}
+
+					quality *= 5;
+
+				}
+
+				return data;
+
+			}
+
+			function generateTexture( data, width, height ) {
+
+				var canvas, canvasScaled, context, image, imageData,
+				level, diff, vector3, sun, shade;
+
+				vector3 = new THREE.Vector3( 0, 0, 0 );
+
+				sun = new THREE.Vector3( 1, 1, 1 );
+				sun.normalize();
+
+				canvas = document.createElement( 'canvas' );
+				canvas.width = width;
+				canvas.height = height;
+
+				context = canvas.getContext( '2d' );
+				context.fillStyle = '#000';
+				context.fillRect( 0, 0, width, height );
+
+				image = context.getImageData( 0, 0, canvas.width, canvas.height );
+				imageData = image.data;
+
+				for ( var i = 0, j = 0, l = imageData.length; i < l; i += 4, j ++ ) {
+
+					vector3.x = data[ j - 2 ] - data[ j + 2 ];
+					vector3.y = 2;
+					vector3.z = data[ j - width * 2 ] - data[ j + width * 2 ];
+					vector3.normalize();
+
+					shade = vector3.dot( sun );
+
+					imageData[ i ] = ( 96 + shade * 128 ) * ( 0.5 + data[ j ] * 0.007 );
+					imageData[ i + 1 ] = ( 32 + shade * 96 ) * ( 0.5 + data[ j ] * 0.007 );
+					imageData[ i + 2 ] = ( shade * 96 ) * ( 0.5 + data[ j ] * 0.007 );
+				}
+
+				context.putImageData( image, 0, 0 );
+
+				// Scaled 4x
+
+				canvasScaled = document.createElement( 'canvas' );
+				canvasScaled.width = width * 4;
+				canvasScaled.height = height * 4;
+
+				context = canvasScaled.getContext( '2d' );
+				context.scale( 4, 4 );
+				context.drawImage( canvas, 0, 0 );
+
+				image = context.getImageData( 0, 0, canvasScaled.width, canvasScaled.height );
+				imageData = image.data;
+
+				for ( var i = 0, l = imageData.length; i < l; i += 4 ) {
+
+					var v = ~~ ( Math.random() * 5 );
+
+					imageData[ i ] += v;
+					imageData[ i + 1 ] += v;
+					imageData[ i + 2 ] += v;
+
+				}
+
+				context.putImageData( image, 0, 0 );
+
+				return canvasScaled;
+
+			}
+
+			function generateCheckerTexture() {
+
+				var width = 128;
+				var height = 128;
+
+				var size = width * height;
+				var checkerSize = 16;
+				var checkerHalfSize = checkerSize / 2;
+				var data = new Uint8Array( 3 * size );
+
+				for ( var i = 0; i < height; ++i ) {
+
+					var index = i * width * 3;
+					var verticalShade = Math.floor((i / checkerSize) % 2);
+
+					for ( var j = 0; j < width; ++j ) {
+
+						var shade = Math.floor( (j / checkerSize + verticalShade) % 2);
+						shade *= 255.0;
+
+						data[ index + j * 3 ] 	  = shade;
+						data[ index + j * 3 + 1 ] = shade;
+						data[ index + j * 3 + 2 ] = shade;
+					}
+				}
+
+				var texture = new THREE.DataTexture( data, width, height, THREE.RGBFormat );
+				texture.needsUpdate = true;
+
+				return texture;
+			}
+
+			//
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+
+				render();
+				stats.update();
+
+			}
+
+			function render() {
+
+				controls.update( clock.getDelta() );
+				renderer.render( scene, camera );
+
+			}
+
+			function onMouseMove( event ) {
+
+				if ( shouldSphereFollowMouse ) {
+
+					var mouseX = ( event.clientX / window.innerWidth ) * 2 - 1;
+					var mouseY = -( event.clientY / window.innerHeight ) * 2 + 1;
+
+					var vector = new THREE.Vector3( mouseX, mouseY, camera.near );
+
+					// Convert the [-1, 1] screen coordinate into a world coordinate on the near plane
+					var projector = new THREE.Projector();
+					projector.unprojectVector( vector, camera );
+
+					var raycaster = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );
+
+					// See if the ray from the camera into the world hits one of our meshes
+					var intersects = raycaster.intersectObject( mesh );
+					lastIntersects = intersects;
+
+					// Toggle rotation bool for meshes that we clicked
+					if ( intersects.length > 0 ) {
+						checkeredSphere.position = intersects[ 0 ].point;
+
+					}
+				}
+			}
+
+		</script>
+
+	</body>
+</html>