Browse Source

Merge pull request #10876 from Mugen87/decal

New DecalGeometry
Mr.doob 8 years ago
parent
commit
0d91aa4ea4

+ 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" ],

+ 0 - 289
examples/js/geometries/DecalGeometry.js

@@ -1,289 +0,0 @@
-THREE.DecalVertex = function( v, n ) {
-
-	this.vertex = v;
-	this.normal = n;
-
-};
-
-THREE.DecalVertex.prototype.clone = function() {
-
-	return new THREE.DecalVertex( this.vertex.clone(), this.normal.clone() );
-
-};
-
-THREE.DecalGeometry = function( mesh, position, rotation, dimensions, check ) {
-
-	THREE.Geometry.call( this );
-
-	if ( check === undefined ) check = null;
-	check = check || new THREE.Vector3( 1, 1, 1 );
-
-	this.uvs = [];
-
-	this.cube = new THREE.Mesh( new THREE.BoxGeometry( dimensions.x, dimensions.y, dimensions.z ), new THREE.MeshBasicMaterial() );
-	this.cube.rotation.set( rotation.x, rotation.y, rotation.z );
-	this.cube.position.copy( position );
-	this.cube.scale.set( 1, 1, 1 );
-	this.cube.updateMatrix();
-
-	this.iCubeMatrix = ( new THREE.Matrix4() ).getInverse( this.cube.matrix );
-
-	this.faceIndices = [ 'a', 'b', 'c', 'd' ];
-
-	this.clipFace = function( inVertices, plane ) {
-
-		var size = .5 * Math.abs( ( dimensions.clone() ).dot( plane ) );
-
-		function clip( v0, v1, p ) {
-
-			var d0 = v0.vertex.dot( p ) - size,
-				d1 = v1.vertex.dot( p ) - size;
-
-			var s = d0 / ( d0 - d1 );
-			var v = new THREE.DecalVertex(
-				new THREE.Vector3(
-					v0.vertex.x + s * ( v1.vertex.x - v0.vertex.x ),
-					v0.vertex.y + s * ( v1.vertex.y - v0.vertex.y ),
-					v0.vertex.z + s * ( v1.vertex.z - v0.vertex.z )
-				),
-				new THREE.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 )
-				)
-			);
-
-			// need to clip more values (texture coordinates)? do it this way:
-			//intersectpoint.value = a.value + s*(b.value-a.value);
-
-			return v;
-
-		}
-
-		if ( inVertices.length === 0 ) return [];
-		var outVertices = [];
-
-		for ( var j = 0; j < inVertices.length; j += 3 ) {
-
-			var v1Out, v2Out, v3Out, total = 0;
-
-			var d1 = inVertices[ j + 0 ].vertex.dot( plane ) - size,
-				d2 = inVertices[ j + 1 ].vertex.dot( plane ) - size,
-				d3 = inVertices[ j + 2 ].vertex.dot( plane ) - size;
-
-			v1Out = d1 > 0;
-			v2Out = d2 > 0;
-			v3Out = d3 > 0;
-
-			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 ] );
-					break;
-
-				}
-				case 1: {
-
-					var nV1, nV2, nV3, nV4;
-					if ( v1Out ) {
-
-						nV1 = inVertices[ j + 1 ];
-						nV2 = inVertices[ j + 2 ];
-						nV3 = clip( inVertices[ j ], nV1, plane );
-						nV4 = clip( inVertices[ j ], nV2, plane );
-
-					}
-					if ( v2Out ) {
-
-						nV1 = inVertices[ j ];
-						nV2 = inVertices[ j + 2 ];
-						nV3 = clip( inVertices[ j + 1 ], nV1, plane );
-						nV4 = clip( inVertices[ j + 1 ], nV2, plane );
-
-						outVertices.push( nV3 );
-						outVertices.push( nV2.clone() );
-						outVertices.push( nV1.clone() );
-
-						outVertices.push( nV2.clone() );
-						outVertices.push( nV3.clone() );
-						outVertices.push( nV4 );
-						break;
-
-					}
-					if ( v3Out ) {
-
-						nV1 = inVertices[ j ];
-						nV2 = inVertices[ j + 1 ];
-						nV3 = clip( inVertices[ j + 2 ], nV1, plane );
-						nV4 = clip( inVertices[ j + 2 ], nV2, plane );
-
-					}
-
-					outVertices.push( nV1.clone() );
-					outVertices.push( nV2.clone() );
-					outVertices.push( nV3 );
-
-					outVertices.push( nV4 );
-					outVertices.push( nV3.clone() );
-					outVertices.push( nV2.clone() );
-
-					break;
-
-				}
-				case 2: {
-
-					var nV1, nV2, nV3;
-					if ( ! v1Out ) {
-
-						nV1 = inVertices[ j ].clone();
-						nV2 = clip( nV1, inVertices[ j + 1 ], plane );
-						nV3 = clip( nV1, inVertices[ j + 2 ], plane );
-						outVertices.push( nV1 );
-						outVertices.push( nV2 );
-						outVertices.push( nV3 );
-
-					}
-					if ( ! v2Out ) {
-
-						nV1 = inVertices[ j + 1 ].clone();
-						nV2 = clip( nV1, inVertices[ j + 2 ], plane );
-						nV3 = clip( nV1, inVertices[ j ], plane );
-						outVertices.push( nV1 );
-						outVertices.push( nV2 );
-						outVertices.push( nV3 );
-
-					}
-					if ( ! v3Out ) {
-
-						nV1 = inVertices[ j + 2 ].clone();
-						nV2 = clip( nV1, inVertices[ j ], plane );
-						nV3 = clip( nV1, inVertices[ j + 1 ], plane );
-						outVertices.push( nV1 );
-						outVertices.push( nV2 );
-						outVertices.push( nV3 );
-
-					}
-
-					break;
-
-				}
-				case 3: {
-
-					break;
-
-				}
-			}
-
-		}
-
-		return outVertices;
-
-	};
-
-	this.pushVertex = function( vertices, id, n ) {
-
-		var v = mesh.geometry.vertices[ id ].clone();
-		v.applyMatrix4( mesh.matrix );
-		v.applyMatrix4( this.iCubeMatrix );
-		vertices.push( new THREE.DecalVertex( v, n.clone() ) );
-
-	};
-
-	this.computeDecal = function() {
-
-		var finalVertices = [];
-
-		for ( var i = 0; i < mesh.geometry.faces.length; i ++ ) {
-
-			var f = mesh.geometry.faces[ i ];
-			var vertices = [];
-
-			this.pushVertex( vertices, f[ this.faceIndices[ 0 ] ], f.vertexNormals[ 0 ] );
-			this.pushVertex( vertices, f[ this.faceIndices[ 1 ] ], f.vertexNormals[ 1 ] );
-			this.pushVertex( vertices, f[ this.faceIndices[ 2 ] ], f.vertexNormals[ 2 ] );
-
-			if ( check.x ) {
-
-				vertices = this.clipFace( vertices, new THREE.Vector3( 1, 0, 0 ) );
-				vertices = this.clipFace( vertices, new THREE.Vector3( - 1, 0, 0 ) );
-
-			}
-			if ( check.y ) {
-
-				vertices = this.clipFace( vertices, new THREE.Vector3( 0, 1, 0 ) );
-				vertices = this.clipFace( vertices, new THREE.Vector3( 0, - 1, 0 ) );
-
-			}
-			if ( check.z ) {
-
-				vertices = this.clipFace( vertices, new THREE.Vector3( 0, 0, 1 ) );
-				vertices = this.clipFace( vertices, new THREE.Vector3( 0, 0, - 1 ) );
-
-			}
-
-			for ( var j = 0; j < vertices.length; j ++ ) {
-
-				var v = vertices[ j ];
-
-				this.uvs.push( new THREE.Vector2(
-					.5 + ( v.vertex.x / dimensions.x ),
-					.5 + ( v.vertex.y / dimensions.y )
-				) );
-
-				vertices[ j ].vertex.applyMatrix4( this.cube.matrix );
-
-			}
-
-			if ( vertices.length === 0 ) continue;
-
-			finalVertices = finalVertices.concat( vertices );
-
-		}
-
-		for ( var k = 0; k < finalVertices.length; k += 3 ) {
-
-			this.vertices.push(
-				finalVertices[ k ].vertex,
-				finalVertices[ k + 1 ].vertex,
-				finalVertices[ k + 2 ].vertex
-			);
-
-			var f = new THREE.Face3(
-				k,
-				k + 1,
-				k + 2
-			);
-			f.vertexNormals.push( finalVertices[ k + 0 ].normal );
-			f.vertexNormals.push( finalVertices[ k + 1 ].normal );
-			f.vertexNormals.push( finalVertices[ k + 2 ].normal );
-
-			this.faces.push( f );
-
-			this.faceVertexUvs[ 0 ].push( [
-				this.uvs[ k ],
-				this.uvs[ k + 1 ],
-				this.uvs[ k + 2 ]
-			] );
-
-		}
-
-		this.verticesNeedUpdate = true;
-		this.elementsNeedUpdate = true;
-		this.morphTargetsNeedUpdate = true;
-		this.uvsNeedUpdate = true;
-		this.normalsNeedUpdate = true;
-		this.colorsNeedUpdate = true;
-		this.computeFaceNormals();
-
-	};
-
-	this.computeDecal();
-
-};
-
-THREE.DecalGeometry.prototype = Object.create( THREE.Geometry.prototype );
-THREE.DecalGeometry.prototype.constructor = THREE.DecalGeometry;

+ 32 - 80
examples/webgl_decals.html

@@ -1,33 +1,40 @@
 <!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/geometries/DecalGeometry.js"></script>
 		<script src="js/controls/OrbitControls.js"></script>
 		<script src="js/libs/dat.gui.min.js"></script>
 
@@ -67,15 +74,13 @@
 		} );
 
 		var decals = [];
-		var decalHelper, mouseHelper;
-		var p = new THREE.Vector3( 0, 0, 0 );
-		var r = new THREE.Vector3( 0, 0, 0 );
-		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,
@@ -98,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 );
@@ -148,7 +153,7 @@
 			window.addEventListener( 'mouseup', function() {
 
 				checkIntersection();
-				if ( ! moved ) shoot();
+				if ( ! moved && intersection.intersects ) shoot();
 
 			} );
 
@@ -213,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' );
@@ -236,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
 				} );
 
@@ -244,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;
+			position.copy( intersection.point );
+			orientation.copy( mouseHelper.rotation );
 
-				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 );
-
-				dummy = new THREE.Object3D();
-				dummy.rotation.setFromRotationMatrix( m );
-				r.set( dummy.rotation.x, dummy.rotation.y, dummy.rotation.z );
-
-			} 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 );
 
@@ -298,36 +276,10 @@
 			decals.forEach( function( d ) {
 
 				scene.remove( d );
-				d = null;
 
 			} );
-			decals = [];
-
-		}
-
-		function mergeDecals() {
-
-			var merge = {};
-			decals.forEach( function ( decal ) {
-
-				var uuid = decal.material.uuid;
-				var d = merge[ uuid ] = merge[ uuid ] || {};
-				d.material = d.material || decal.material;
-				d.geometry = d.geometry || new THREE.Geometry();
-				d.geometry.merge( decal.geometry, decal.matrix );
-
-			} );
-
-			removeDecals();
 
-			for ( var key in merge ) {
-
-				var d = merge[ key ];
-				var mesh = new THREE.Mesh( d.geometry, d.material );
-				scene.add( mesh );
-				decals.push( mesh );
-
-			}
+			decals = [];
 
 		}
 

+ 347 - 0
src/geometries/DecalGeometry.js

@@ -0,0 +1,347 @@
+import { BufferGeometry } from '../core/BufferGeometry';
+import { Float32BufferAttribute } from '../core/BufferAttribute';
+import { Vector3 } from '../math/Vector3';
+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, orientation, size ) {
+
+	BufferGeometry.call( this );
+
+	this.type = 'DecalGeometry';
+
+	// buffers
+
+	var vertices = [];
+	var normals = [];
+	var uvs = [];
+
+	// helpers
+
+	var plane = new Vector3();
+
+	// this matrix represents the transformation of the decal projector
+
+	var projectorMatrix = new Matrix4();
+	projectorMatrix.makeRotationFromEuler( orientation );
+	projectorMatrix.setPosition( position );
+
+	var projectorMatrixInverse = new Matrix4().getInverse( projectorMatrix );
+
+	// generate buffers
+
+	generate();
+
+	// build geometry
+
+	this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
+	this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
+	this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
+
+	function generate() {
+
+		var i, j;
+		var geometry = new BufferGeometry();
+		var decalVertices = [];
+
+		var vertex = new Vector3();
+		var normal = new Vector3();
+
+		// handle different geometry types
+
+		if ( mesh.geometry.isGeometry ) {
+
+			geometry.fromGeometry( mesh.geometry );
+
+		} else {
+
+			geometry.copy( mesh.geometry );
+
+		}
+
+		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
+
+			var index = geometry.index;
+
+			for ( i = 0; i < index.count; i ++ ) {
+
+				vertex.fromBufferAttribute( positionAttribute, index[ i ] );
+				normal.fromBufferAttribute( normalAttribute, index[ i ] );
+
+				pushDecalVertex( decalVertices, vertex, normal );
+
+			}
+
+		} else {
+
+			// non-indexed BufferGeometry
+
+			for ( i = 0; i < positionAttribute.count; i ++ ) {
+
+				vertex.fromBufferAttribute( positionAttribute, i );
+				normal.fromBufferAttribute( normalAttribute, i );
+
+				pushDecalVertex( decalVertices, vertex, normal );
+
+			}
+
+		}
+
+		// 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 ) );
+
+		// 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 / 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 );
+
+		}
+
+	}
+
+	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 s = 0.5 * Math.abs( size.dot( plane ) );
+
+		// a single iteration clips one face,
+		// which consists of three consecutive 'DecalVertex' objects
+
+		for ( var i = 0; i < inVertices.length; i += 3 ) {
+
+			var v1Out, v2Out, v3Out, total = 0;
+			var nV1, nV2, nV3, nV4;
+
+			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: {
+
+					// 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[ i + 1 ];
+						nV2 = inVertices[ i + 2 ];
+						nV3 = clip( inVertices[ i ], nV1, plane, s );
+						nV4 = clip( inVertices[ i ], nV2, plane, s );
+
+					}
+
+					if ( v2Out ) {
+
+						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() );
+						outVertices.push( nV1.clone() );
+
+						outVertices.push( nV2.clone() );
+						outVertices.push( nV3.clone() );
+						outVertices.push( nV4 );
+						break;
+
+					}
+
+					if ( v3Out ) {
+
+						nV1 = inVertices[ i ];
+						nV2 = inVertices[ i + 1 ];
+						nV3 = clip( inVertices[ i + 2 ], nV1, plane, s );
+						nV4 = clip( inVertices[ i + 2 ], nV2, plane, s );
+
+					}
+
+					outVertices.push( nV1.clone() );
+					outVertices.push( nV2.clone() );
+					outVertices.push( nV3 );
+
+					outVertices.push( nV4 );
+					outVertices.push( nV3.clone() );
+					outVertices.push( nV2.clone() );
+
+					break;
+
+				}
+
+				case 2: {
+
+					// two vertices lies outside of the plane, perform clipping
+
+					if ( ! v1Out ) {
+
+						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 );
+
+					}
+
+					if ( ! v2Out ) {
+
+						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 );
+
+					}
+
+					if ( ! v3Out ) {
+
+						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 );
+
+					}
+
+					break;
+
+				}
+
+				case 3: {
+
+					// the entire face lies outside of the plane, so let's discard the corresponding vertices
+
+					break;
+
+				}
+
+			}
+
+		}
+
+		return outVertices;
+
+	}
+
+	function clip( v0, v1, p, s ) {
+
+		var d0 = v0.position.dot( p ) - s;
+		var d1 = v1.position.dot( p ) - s;
+
+		var s0 = d0 / ( d0 - d1 );
+
+		var v = new DecalVertex(
+			new Vector3(
+				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 + 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 )
+			)
+		);
+
+		// need to clip more values (texture coordinates)? do it this way:
+		// intersectpoint.value = a.value + s * ( b.value - a.value );
+
+		return v;
+
+	}
+
+}
+
+DecalGeometry.prototype = Object.create( BufferGeometry.prototype );
+DecalGeometry.prototype.constructor = DecalGeometry;
+
+function DecalVertex( position, normal ) {
+
+	this.position = position;
+	this.normal = normal;
+
+}
+
+DecalVertex.prototype.clone = function() {
+
+	return new DecalVertex( this.position.clone(), this.normal.clone() );
+
+};
+
+
+export { DecalGeometry };

+ 1 - 0
src/geometries/Geometries.js

@@ -20,3 +20,4 @@ export { ConeGeometry, ConeBufferGeometry } from './ConeGeometry.js';
 export { CylinderGeometry, CylinderBufferGeometry } from './CylinderGeometry.js';
 export { CircleGeometry, CircleBufferGeometry } from './CircleGeometry.js';
 export { BoxGeometry, BoxBufferGeometry } from './BoxGeometry.js';
+export { DecalGeometry } from './DecalGeometry.js';