Browse Source

DecalGeometry: Doc, Example and Code Clean up

Mugen87 8 years ago
parent
commit
74db944a45
4 changed files with 172 additions and 128 deletions
  1. 42 0
      docs/api/geometries/DecalGeometry.html
  2. 1 0
      docs/list.js
  3. 32 53
      examples/webgl_decals.html
  4. 97 75
      src/geometries/DecalGeometry.js

+ 42 - 0
docs/api/geometries/DecalGeometry.html

@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="utf-8" />
+		<base href="../../" />
+		<script src="list.js"></script>
+		<script src="page.js"></script>
+		<link type="text/css" rel="stylesheet" href="page.css" />
+	</head>
+	<body>
+		[page:BufferGeometry] &rarr;
+
+		<h1>[name]</h1>
+
+		<div class="desc"> You can use this geometry to create a decal mesh, that serves different kinds of purposes e.g.:
+			adding unique details to models, performing dynamic visual environmental changes or covering seams. </div>
+
+		<h2>Example</h2>
+
+		[example:webgl_decals decals]
+
+		<code>var geometry = new THREE.DecalGeometry( mesh, position, orientation, size );
+		var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
+		var decal = new THREE.Mesh( geometry, material );
+		scene.add( decal );
+		</code>
+
+		<h2>Constructor</h2>
+
+		<h3>[name]( [page:Mesh mesh], [page:Vector3 position], [page:Euler orientation], [page:Vector3 size] )</h3>
+		<div>
+		mesh — Any mesh object.<br />
+		position — Position of the decal projector.<br />
+		orientation — Orientation of the decal projector.<br />
+		size — Size of the decal projector.<br />
+		</div>
+
+		<h2>Source</h2>
+
+		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
+	</body>
+</html>

+ 1 - 0
docs/list.js

@@ -140,6 +140,7 @@ var list = {
 			[ "ConeGeometry", "api/geometries/ConeGeometry" ],
 			[ "CylinderBufferGeometry", "api/geometries/CylinderBufferGeometry" ],
 			[ "CylinderGeometry", "api/geometries/CylinderGeometry" ],
+			[ "DecalGeometry", "api/geometries/DecalGeometry" ],
 			[ "DodecahedronBufferGeometry", "api/geometries/DodecahedronBufferGeometry" ],
 			[ "DodecahedronGeometry", "api/geometries/DodecahedronGeometry" ],
 			[ "EdgesGeometry", "api/geometries/EdgesGeometry" ],

+ 32 - 53
examples/webgl_decals.html

@@ -1,30 +1,38 @@
 <!DOCTYPE html>
 <html lang="en">
 	<head>
-		<title>WebGL decals</title>
+		<title>three.js webgl - decals - Decal Splatter</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: #fff;
+				background:#777;
+				padding:0;
+				margin:0;
+				font-weight: bold;
+				overflow:hidden;
+			}
+
+			#info {
+				position: absolute;
+				top: 0px;
+				width: 100%;
+				color: #ffffff;
+				padding: 5px;
 				font-family:Monospace;
 				font-size:13px;
-				margin: 0px;
 				text-align:center;
-				overflow: hidden;
 			}
-			#info{ position: absolute; width: 100%; padding: 5px; }
+
+			a {
+				color: #ffffff;
+			}
 		</style>
 	</head>
 	<body>
 
 		<div id="container"></div>
-		<div id="info">
-			<p>
-				<strong>Decal Splatter</strong><br />
-				Click or tap to shoot.
-			</p>
-		</div>
+		<div id="info"><a href="https://threejs.org" target="_blank">three.js</a> - decals - Decal Splatter (click or tap to shoot)</div>
 
 		<script src="../build/three.js"></script>
 		<script src="js/controls/OrbitControls.js"></script>
@@ -66,15 +74,13 @@
 		} );
 
 		var decals = [];
-		var decalHelper, mouseHelper;
-		var p = new THREE.Vector3();
-		var r = new THREE.Euler();
-		var s = new THREE.Vector3( 10, 10, 10 );
+		var mouseHelper;
+		var position = new THREE.Vector3();
+		var orientation = new THREE.Euler();
+		var size = new THREE.Vector3( 10, 10, 10 );
 		var up = new THREE.Vector3( 0, 1, 0 );
-		var check = new THREE.Vector3( 1, 1, 1 );
 
 		var params = {
-			projection: 'normal',
 			minScale: 10,
 			maxScale: 20,
 			rotate: true,
@@ -97,7 +103,7 @@
 			scene = new THREE.Scene();
 
 			camera = new THREE.PerspectiveCamera( fov, window.innerWidth / window.innerHeight, 1, 1000 );
-			camera.position.z = 100;
+			camera.position.z = 120;
 			camera.target = new THREE.Vector3();
 
 			controls = new THREE.OrbitControls( camera, renderer.domElement );
@@ -147,7 +153,7 @@
 			window.addEventListener( 'mouseup', function() {
 
 				checkIntersection();
-				if ( ! moved ) shoot();
+				if ( ! moved && intersection.intersects ) shoot();
 
 			} );
 
@@ -212,7 +218,6 @@
 
 			var gui = new dat.GUI();
 
-			gui.add( params, 'projection', { 'From cam to mesh': 'camera', 'Normal to mesh': 'normal' } );
 			gui.add( params, 'minScale', 1, 30 );
 			gui.add( params, 'maxScale', 1, 30 );
 			gui.add( params, 'rotate' );
@@ -235,7 +240,6 @@
 					map: textureLoader.load( 'obj/leeperrysmith/Map-COL.jpg' ),
 					specularMap: textureLoader.load( 'obj/leeperrysmith/Map-SPEC.jpg' ),
 					normalMap: textureLoader.load( 'obj/leeperrysmith/Infinite-Level_02_Tangent_SmoothUV.jpg' ),
-					normalScale: new THREE.Vector2( 0.75, 0.75 ),
 					shininess: 25
 				} );
 
@@ -243,50 +247,25 @@
 				scene.add( mesh );
 				mesh.scale.set( 10, 10, 10 );
 
-				//scene.add( new THREE.FaceNormalsHelper( mesh, 1 ) );
-				//scene.add( new THREE.VertexNormalsHelper( mesh, 1 ) );
-
 			} );
 
 		}
 
 		function shoot() {
 
-			if ( params.projection == 'camera' ) {
-
-				var dir = camera.target.clone();
-				dir.sub( camera.position );
-
-				p = intersection.point;
-
-				var m = new THREE.Matrix4();
-				var c = dir.clone();
-				c.negate();
-				c.multiplyScalar( 10 );
-				c.add( p );
-				m.lookAt( p, c, up );
-				m = m.extractRotation( m );
+			position.copy( intersection.point );
+			orientation.copy( mouseHelper.rotation );
 
-				dummy = new THREE.Object3D();
-				dummy.rotation.setFromRotationMatrix( m );
-				r.copy( dummy.rotation );
-
-			} else {
-
-				p = intersection.point;
-				r.copy( mouseHelper.rotation );
-
-			}
+			if ( params.rotate ) orientation.z = Math.random() * 2 * Math.PI;
 
 			var scale = params.minScale + Math.random() * ( params.maxScale - params.minScale );
-			s.set( scale, scale, scale );
-
-			if ( params.rotate ) r.z = Math.random() * 2 * Math.PI;
+			size.set( scale, scale, scale );
 
 			var material = decalMaterial.clone();
 			material.color.setHex( Math.random() * 0xffffff );
 
-			var m = new THREE.Mesh( new THREE.DecalGeometry( mesh, p, r, s, check ), material );
+			var m = new THREE.Mesh( new THREE.DecalGeometry( mesh, position, orientation, size ), material );
+
 			decals.push( m );
 			scene.add( m );
 
@@ -297,9 +276,9 @@
 			decals.forEach( function( d ) {
 
 				scene.remove( d );
-				d = null;
 
 			} );
+
 			decals = [];
 
 		}

+ 97 - 75
src/geometries/DecalGeometry.js

@@ -5,16 +5,18 @@ import { Matrix4 } from '../math/Matrix4';
 
 /**
  * @author Mugen87 / https://github.com/Mugen87
+ * @author spite / https://github.com/spite
+ *
+ * reference: http://blog.wolfire.com/2009/06/how-to-project-decals/
+ *
  */
 
-function DecalGeometry( mesh, position, rotation, dimensions, check ) {
+function DecalGeometry( mesh, position, orientation, size ) {
 
 	BufferGeometry.call( this );
 
 	this.type = 'DecalGeometry';
 
-	check = check || new Vector3( 1, 1, 1 );
-
 	// buffers
 
 	var vertices = [];
@@ -23,14 +25,16 @@ function DecalGeometry( mesh, position, rotation, dimensions, check ) {
 
 	// helpers
 
+	var plane = new Vector3();
+
+	// this matrix represents the transformation of the decal projector
+
 	var projectorMatrix = new Matrix4();
-	projectorMatrix.makeRotationFromEuler( rotation );
+	projectorMatrix.makeRotationFromEuler( orientation );
 	projectorMatrix.setPosition( position );
 
 	var projectorMatrixInverse = new Matrix4().getInverse( projectorMatrix );
 
-	var plane = new Vector3();
-
 	// generate buffers
 
 	generate();
@@ -65,6 +69,11 @@ function DecalGeometry( mesh, position, rotation, dimensions, check ) {
 		var positionAttribute = geometry.attributes.position;
 		var normalAttribute = geometry.attributes.normal;
 
+		// first, create an array of 'DecalVertex' objects
+		// three consecutive 'DecalVertex' objects represent a single face
+		//
+		// this data structure will be later used to perform the clipping
+
 		if ( geometry.index !== null ) {
 
 			// indexed BufferGeometry
@@ -76,10 +85,7 @@ function DecalGeometry( mesh, position, rotation, dimensions, check ) {
 				vertex.fromBufferAttribute( positionAttribute, index[ i ] );
 				normal.fromBufferAttribute( normalAttribute, index[ i ] );
 
-				vertex.applyMatrix4( mesh.matrix );
-				vertex.applyMatrix4( projectorMatrixInverse );
-
-				decalVertices.push( new DecalVertex( vertex.clone(), normal.clone() ) );
+				pushDecalVertex( decalVertices, vertex, normal );
 
 			}
 
@@ -92,49 +98,40 @@ function DecalGeometry( mesh, position, rotation, dimensions, check ) {
 				vertex.fromBufferAttribute( positionAttribute, i );
 				normal.fromBufferAttribute( normalAttribute, i );
 
-				vertex.applyMatrix4( mesh.matrix );
-				vertex.applyMatrix4( projectorMatrixInverse );
-
-				decalVertices.push( new DecalVertex( vertex.clone(), normal.clone() ) );
+				pushDecalVertex( decalVertices, vertex, normal );
 
 			}
 
 		}
 
-		// check
-
-		if ( check.x ) {
-
-			decalVertices = clipGeometry( decalVertices, plane.set( 1, 0, 0 ) );
-			decalVertices = clipGeometry( decalVertices, plane.set( - 1, 0, 0 ) );
-
-		}
-		if ( check.y ) {
-
-			decalVertices = clipGeometry( decalVertices, plane.set( 0, 1, 0 ) );
-			decalVertices = clipGeometry( decalVertices, plane.set( 0, - 1, 0 ) );
-
-		}
-		if ( check.z ) {
-
-			decalVertices = clipGeometry( decalVertices, plane.set( 0, 0, 1 ) );
-			decalVertices = clipGeometry( decalVertices, plane.set( 0, 0, - 1 ) );
+		// second, clip the geometry so that it doesn't extend out from the projector
 
-		}
+		decalVertices = clipGeometry( decalVertices, plane.set(   1,   0,   0 ) );
+		decalVertices = clipGeometry( decalVertices, plane.set( - 1,   0,   0 ) );
+		decalVertices = clipGeometry( decalVertices, plane.set(   0,   1,   0 ) );
+		decalVertices = clipGeometry( decalVertices, plane.set(   0, - 1,   0 ) );
+		decalVertices = clipGeometry( decalVertices, plane.set(   0,   0,   1 ) );
+		decalVertices = clipGeometry( decalVertices, plane.set(   0,   0, - 1 ) );
 
-		// generate vertices, normals and uvs
+		// third, generate final vertices, normals and uvs
 
 		for ( i = 0; i < decalVertices.length; i ++ ) {
 
 			var decalVertex = decalVertices[ i ];
 
+			// create texture coordinates (we are still in projector space)
+
 			uvs.push(
-				0.5 + ( decalVertex.position.x / dimensions.x ),
-				0.5 + ( decalVertex.position.y / dimensions.y )
+				0.5 + ( decalVertex.position.x / size.x ),
+				0.5 + ( decalVertex.position.y / size.y )
 			);
 
+			// transform the vertex back to world space
+
 			decalVertex.position.applyMatrix4( projectorMatrix );
 
+			// now create vertex and normal buffer data
+
 			vertices.push( decalVertex.position.x, decalVertex.position.y, decalVertex.position.z );
 			normals.push( decalVertex.normal.x, decalVertex.normal.y, decalVertex.normal.z );
 
@@ -142,55 +139,75 @@ function DecalGeometry( mesh, position, rotation, dimensions, check ) {
 
 	}
 
+	function pushDecalVertex( decalVertices, vertex, normal ) {
+
+		// transform the vertex to world space, then to projector space
+
+		vertex.applyMatrix4( mesh.matrix );
+		vertex.applyMatrix4( projectorMatrixInverse );
+
+		decalVertices.push( new DecalVertex( vertex.clone(), normal.clone() ) );
+
+	}
+
 	function clipGeometry( inVertices, plane ) {
 
 		var outVertices = [];
 
-		var size = 0.5 * Math.abs( dimensions.dot( plane ) );
+		var s = 0.5 * Math.abs( size.dot( plane ) );
+
+		// a single iteration clips one face,
+		// which consists of three consecutive 'DecalVertex' objects
 
-		for ( var j = 0; j < inVertices.length; j += 3 ) {
+		for ( var i = 0; i < inVertices.length; i += 3 ) {
 
 			var v1Out, v2Out, v3Out, total = 0;
 			var nV1, nV2, nV3, nV4;
 
-			var d1 = inVertices[ j + 0 ].position.dot( plane ) - size;
-			var d2 = inVertices[ j + 1 ].position.dot( plane ) - size;
-			var d3 = inVertices[ j + 2 ].position.dot( plane ) - size;
+			var d1 = inVertices[ i + 0 ].position.dot( plane ) - s;
+			var d2 = inVertices[ i + 1 ].position.dot( plane ) - s;
+			var d3 = inVertices[ i + 2 ].position.dot( plane ) - s;
 
 			v1Out = d1 > 0;
 			v2Out = d2 > 0;
 			v3Out = d3 > 0;
 
+			// calculate, how many vertices of the face lie outside of the clipping plane
+
 			total = ( v1Out ? 1 : 0 ) + ( v2Out ? 1 : 0 ) + ( v3Out ? 1 : 0 );
 
 			switch ( total ) {
 
 				case 0: {
 
-					outVertices.push( inVertices[ j ] );
-					outVertices.push( inVertices[ j + 1 ] );
-					outVertices.push( inVertices[ j + 2 ] );
+					// the entire face lies inside of the plane, no clipping needed
+
+					outVertices.push( inVertices[ i ] );
+					outVertices.push( inVertices[ i + 1 ] );
+					outVertices.push( inVertices[ i + 2 ] );
 					break;
 
 				}
 
 				case 1: {
 
+					// one vertex lies outside of the plane, perform clipping
+
 					if ( v1Out ) {
 
-						nV1 = inVertices[ j + 1 ];
-						nV2 = inVertices[ j + 2 ];
-						nV3 = clip( inVertices[ j ], nV1, plane, size );
-						nV4 = clip( inVertices[ j ], nV2, plane, size );
+						nV1 = inVertices[ i + 1 ];
+						nV2 = inVertices[ i + 2 ];
+						nV3 = clip( inVertices[ i ], nV1, plane, s );
+						nV4 = clip( inVertices[ i ], nV2, plane, s );
 
 					}
 
 					if ( v2Out ) {
 
-						nV1 = inVertices[ j ];
-						nV2 = inVertices[ j + 2 ];
-						nV3 = clip( inVertices[ j + 1 ], nV1, plane, size );
-						nV4 = clip( inVertices[ j + 1 ], nV2, plane, size );
+						nV1 = inVertices[ i ];
+						nV2 = inVertices[ i + 2 ];
+						nV3 = clip( inVertices[ i + 1 ], nV1, plane, s );
+						nV4 = clip( inVertices[ i + 1 ], nV2, plane, s );
 
 						outVertices.push( nV3 );
 						outVertices.push( nV2.clone() );
@@ -205,10 +222,10 @@ function DecalGeometry( mesh, position, rotation, dimensions, check ) {
 
 					if ( v3Out ) {
 
-						nV1 = inVertices[ j ];
-						nV2 = inVertices[ j + 1 ];
-						nV3 = clip( inVertices[ j + 2 ], nV1, plane, size );
-						nV4 = clip( inVertices[ j + 2 ], nV2, plane, size );
+						nV1 = inVertices[ i ];
+						nV2 = inVertices[ i + 1 ];
+						nV3 = clip( inVertices[ i + 2 ], nV1, plane, s );
+						nV4 = clip( inVertices[ i + 2 ], nV2, plane, s );
 
 					}
 
@@ -226,11 +243,13 @@ function DecalGeometry( mesh, position, rotation, dimensions, check ) {
 
 				case 2: {
 
+					// two vertices lies outside of the plane, perform clipping
+
 					if ( ! v1Out ) {
 
-						nV1 = inVertices[ j ].clone();
-						nV2 = clip( nV1, inVertices[ j + 1 ], plane, size );
-						nV3 = clip( nV1, inVertices[ j + 2 ], plane, size );
+						nV1 = inVertices[ i ].clone();
+						nV2 = clip( nV1, inVertices[ i + 1 ], plane, s );
+						nV3 = clip( nV1, inVertices[ i + 2 ], plane, s );
 						outVertices.push( nV1 );
 						outVertices.push( nV2 );
 						outVertices.push( nV3 );
@@ -239,9 +258,9 @@ function DecalGeometry( mesh, position, rotation, dimensions, check ) {
 
 					if ( ! v2Out ) {
 
-						nV1 = inVertices[ j + 1 ].clone();
-						nV2 = clip( nV1, inVertices[ j + 2 ], plane, size );
-						nV3 = clip( nV1, inVertices[ j ], plane, size );
+						nV1 = inVertices[ i + 1 ].clone();
+						nV2 = clip( nV1, inVertices[ i + 2 ], plane, s );
+						nV3 = clip( nV1, inVertices[ i ], plane, s );
 						outVertices.push( nV1 );
 						outVertices.push( nV2 );
 						outVertices.push( nV3 );
@@ -250,9 +269,9 @@ function DecalGeometry( mesh, position, rotation, dimensions, check ) {
 
 					if ( ! v3Out ) {
 
-						nV1 = inVertices[ j + 2 ].clone();
-						nV2 = clip( nV1, inVertices[ j ], plane, size );
-						nV3 = clip( nV1, inVertices[ j + 1 ], plane, size );
+						nV1 = inVertices[ i + 2 ].clone();
+						nV2 = clip( nV1, inVertices[ i ], plane, s );
+						nV3 = clip( nV1, inVertices[ i + 1 ], plane, s );
 						outVertices.push( nV1 );
 						outVertices.push( nV2 );
 						outVertices.push( nV3 );
@@ -265,6 +284,8 @@ function DecalGeometry( mesh, position, rotation, dimensions, check ) {
 
 				case 3: {
 
+					// the entire face lies outside of the plane, so let's discard the corresponding vertices
+
 					break;
 
 				}
@@ -277,22 +298,23 @@ function DecalGeometry( mesh, position, rotation, dimensions, check ) {
 
 	}
 
-	function clip( v0, v1, p, size ) {
+	function clip( v0, v1, p, s ) {
+
+		var d0 = v0.position.dot( p ) - s;
+		var d1 = v1.position.dot( p ) - s;
 
-		var d0 = v0.position.dot( p ) - size;
-		var d1 = v1.position.dot( p ) - size;
+		var s0 = d0 / ( d0 - d1 );
 
-		var s = d0 / ( d0 - d1 );
 		var v = new DecalVertex(
 			new Vector3(
-				v0.position.x + s * ( v1.position.x - v0.position.x ),
-				v0.position.y + s * ( v1.position.y - v0.position.y ),
-				v0.position.z + s * ( v1.position.z - v0.position.z )
+				v0.position.x + s0 * ( v1.position.x - v0.position.x ),
+				v0.position.y + s0 * ( v1.position.y - v0.position.y ),
+				v0.position.z + s0 * ( v1.position.z - v0.position.z )
 			),
 			new Vector3(
-				v0.normal.x + s * ( v1.normal.x - v0.normal.x ),
-				v0.normal.y + s * ( v1.normal.y - v0.normal.y ),
-				v0.normal.z + s * ( v1.normal.z - v0.normal.z )
+				v0.normal.x + s0 * ( v1.normal.x - v0.normal.x ),
+				v0.normal.y + s0 * ( v1.normal.y - v0.normal.y ),
+				v0.normal.z + s0 * ( v1.normal.z - v0.normal.z )
 			)
 		);