Browse Source

Merge branch 'dev_6762' of https://github.com/rfm1201/three.js into dev

Mr.doob 10 years ago
parent
commit
374faa31ff
3 changed files with 520 additions and 0 deletions
  1. 380 0
      examples/webgl_texture_raycast.html
  2. 63 0
      src/objects/Mesh.js
  3. 77 0
      src/textures/Texture.js

+ 380 - 0
examples/webgl_texture_raycast.html

@@ -0,0 +1,380 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js ray cast - texture - canvas</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: #808080;
+				font-family:Monospace;
+				font-size:13px;
+				text-align:center;
+
+				background-color: #ffffff;
+				margin: 0px;
+				overflow: hidden;
+			}
+
+			#info {
+				position: absolute;
+				top: 0px; width: 100%;
+				padding: 5px;
+			}
+
+			#controls {
+				position: absolute;
+				text-align:left;
+				top: 40px;
+				left: 5px;
+				padding: 5px;
+			}
+
+			.control {
+				margin-bottom: 3px;
+			}
+
+			input {
+				width: 50px;
+			}
+		</style>
+	</head>
+	<body>
+		<div id="container"></div>
+		<div id="info"><a href="http://threejs.org" target="_blank">three.js</a> - texture intersection<br>Left to right: buffer geometry - geometry - indexed buffer geometry</div>
+		<fieldset id="controls">
+			<legend>Circle</legend>
+			<div class="control">
+				WrapS : <select onchange="setwrapS(this)">
+					<option value="ClampToEdgeWrapping">ClampToEdgeWrapping</option>
+					<option value="RepeatWrapping" selected>RepeatWrapping</option>
+					<option value="MirroredRepeatWrapping">MirroredRepeatWrapping</option>
+				</select>
+			</div>
+			<div class="control">
+				WrapT : <select onchange="setwrapT(this)">
+					<option value="ClampToEdgeWrapping">ClampToEdgeWrapping</option>
+					<option value="RepeatWrapping" selected>RepeatWrapping</option>
+					<option value="MirroredRepeatWrapping">MirroredRepeatWrapping</option>
+				</select>
+			</div>
+			<div class="control">
+				Offset : X <input type="number" value="0" step="0.05" onchange="setOffsetU(this)" />
+				Y <input type="number" value="0" step="0.05" onchange="setOffsetV(this)" /><br />
+			</div>
+			<div class="control">
+				Repeat : X <input type="number" value="1" step="0.1" onchange="setRepeatU(this)" />
+				Y <input type="number" value="1" step="0.1" onchange="setRepeatV(this)" />
+			</div>
+		</fieldset>
+		<script src="../build/three.min.js"></script>
+		<script>
+
+			CanvasTexture = function ( parentTexture ) {
+
+				this._canvas = document.createElement( "canvas" );
+				this._canvas.width = this._canvas.height = 1024;
+				this._context2D = this._canvas.getContext( "2d" );
+
+				if ( parentTexture ) {
+
+					this._parentTexture.push( parentTexture );
+					parentTexture.image = this._canvas;
+
+				}
+
+				var that = this;
+				this._background = document.createElement( "img" );
+				this._background.addEventListener( "load", function ( event ) {
+
+					that._canvas.width = that._background.naturalWidth;
+					that._canvas.height = that._background.naturalHeight;
+
+					that._crossRadius = Math.ceil( Math.min( that._canvas.width, that._canvas.height / 30 ) );
+					that._crossMax = Math.ceil( 0.70710678 * that._crossRadius );
+					that._crossMin = Math.ceil( that._crossMax / 10 );
+					that._crossThickness = Math.ceil( that._crossMax / 10 );
+
+					that._draw();
+
+				}, false );
+				this._background.crossOrigin = '';
+				this._background.src = "textures/UV_Grid_Sm.jpg";
+
+				this._draw();
+
+			}
+
+
+			CanvasTexture.prototype = {
+
+				constructor: CanvasTexture,
+
+				_canvas: null,
+				_context2D: null,
+				_xCross: 0,
+				_yCross: 0,
+
+				_crossRadius: 57,
+				_crossMax: 40,
+				_crossMin: 4,
+				_crossThickness: 4,
+
+				_parentTexture: [],
+
+				addParent: function ( parentTexture ) {
+
+					if ( this._parentTexture.indexOf( parentTexture ) === - 1 ) {
+
+						this._parentTexture.push( parentTexture );
+						parentTexture.image = this._canvas;
+
+					}
+
+				},
+
+				setCrossPosition: function ( x, y ) {
+
+					this._xCross = x * this._canvas.width;
+					this._yCross = y * this._canvas.height;
+
+					this._draw();
+
+				},
+
+				_draw: function () {
+
+					if ( ! this._context2D ) return;
+
+					this._context2D.clearRect( 0, 0, this._canvas.width, this._canvas.height )
+
+					// Background.
+					this._context2D.drawImage( this._background, 0, 0 );
+
+					// Yellow cross.
+					this._context2D.lineWidth = this._crossThickness * 3;
+					this._context2D.strokeStyle = "#FFFF00";
+
+					this._context2D.beginPath();
+					this._context2D.moveTo( this._xCross - this._crossMax - 2, this._yCross - this._crossMax - 2 );
+					this._context2D.lineTo( this._xCross - this._crossMin, this._yCross - this._crossMin );
+
+					this._context2D.moveTo( this._xCross + this._crossMin, this._yCross + this._crossMin );
+					this._context2D.lineTo( this._xCross + this._crossMax + 2, this._yCross + this._crossMax + 2 );
+
+					this._context2D.moveTo( this._xCross - this._crossMax - 2, this._yCross + this._crossMax + 2 );
+					this._context2D.lineTo( this._xCross - this._crossMin, this._yCross + this._crossMin );
+
+					this._context2D.moveTo( this._xCross + this._crossMin, this._yCross - this._crossMin );
+					this._context2D.lineTo( this._xCross + this._crossMax + 2, this._yCross - this._crossMax - 2 );
+
+					this._context2D.stroke();
+
+					for ( var i = 0; i < this._parentTexture.length; i ++ ) {
+
+						this._parentTexture[ i ].needsUpdate = true;
+
+					}
+
+				}
+
+			}
+
+		</script>
+		<script>
+
+			var width = window.innerWidth;
+			var height = window.innerHeight;
+
+			var canvas;
+			var objects = [];
+			var planeTexture, cubeTexture, circleTexture;
+
+			var container;
+
+			var camera, scene, renderer;
+
+			var raycaster = new THREE.Raycaster();
+			var mouse = new THREE.Vector2();
+			var onClickPosition = new THREE.Vector2();
+
+			init();
+			render();
+
+			function init() {
+
+				container = document.getElementById( "container" );
+
+				scene = new THREE.Scene();
+
+				camera = new THREE.PerspectiveCamera( 45, width / height, 1, 1000 );
+				camera.position.x = - 30;
+				camera.position.y = 40;
+				camera.position.z = 50;
+				camera.lookAt( scene.position );
+
+				renderer = new THREE.WebGLRenderer();
+				renderer.setClearColor( new THREE.Color( 0xEEEEEE, 1.0 ) );
+				renderer.setSize( width, height );
+				container.appendChild( renderer.domElement );
+
+				// A cube, in the middle.
+				cubeTexture = new THREE.Texture( undefined, THREE.UVMapping, THREE.RepeatWrapping, THREE.RepeatWrapping );
+				canvas = new CanvasTexture( cubeTexture );
+				var cubeMaterial = new THREE.MeshBasicMaterial( { map: cubeTexture } );
+				var cubeGeometry = new THREE.BoxGeometry( 20, 20, 20 );
+				// Set a specific texture mapping.
+				var uvs;
+				for ( var i = 0; i < cubeGeometry.faceVertexUvs[ 0 ].length; i ++ ) {
+
+					uvs = cubeGeometry.faceVertexUvs[ 0 ][ i ];
+					for ( var j = 0; j < 3; j ++ ) {
+
+						if ( uvs[ j ].x < 0.1 ) uvs[ j ].x = - 1;
+						if ( uvs[ j ].y < 0.1 ) uvs[ j ].y = - 1;
+
+					}
+
+				}
+				var cube = new THREE.Mesh( cubeGeometry, cubeMaterial );
+				cube.position.x = 4;
+				cube.position.y = - 5;
+				cube.position.z = 0;
+				objects.push( cube );
+				scene.add( cube );
+
+				// A plane on the left.
+				planeTexture = new THREE.Texture( undefined, THREE.UVMapping, THREE.MirroredRepeatWrapping, THREE.MirroredRepeatWrapping );
+				canvas.addParent( planeTexture );
+				var planeMaterial = new THREE.MeshBasicMaterial( { map: planeTexture } );
+				var planeGeometry = new THREE.PlaneBufferGeometry( 25, 25, 1, 1 );
+				var uvs = planeGeometry.attributes.uv.array;
+				// Set a specific texture mapping.
+				for ( var i = 0; i < uvs.length; i ++ ) {
+
+					uvs[ i ] *= 2;
+
+				}
+				var plane = new THREE.Mesh( planeGeometry, planeMaterial );
+				plane.position.x = - 16;
+				plane.position.y = - 5;
+				plane.position.z = 0;
+				objects.push( plane );
+				scene.add( plane );
+
+				// A circle on the right.
+				circleTexture = new THREE.Texture( undefined, THREE.UVMapping, THREE.RepeatWrapping, THREE.RepeatWrapping );
+				canvas.addParent( circleTexture );
+				var circleMaterial = new THREE.MeshBasicMaterial( { map: circleTexture } );
+				var circleGeometry = new THREE.CircleBufferGeometry( 25, 40, 0, Math.PI * 2 );
+				var uvs = circleGeometry.attributes.uv.array;
+				// Set a specific texture mapping.
+				for ( var i = 0; i < uvs.length; i ++ ) {
+
+					uvs[ i ] = ( uvs[ i ] - 0.25 ) * 2;
+
+				}
+				var circle = new THREE.Mesh( circleGeometry, circleMaterial );
+				circle.position.x = 24;
+				circle.position.y = - 5;
+				circle.position.z = 0;
+				objects.push( circle );
+				scene.add( circle );
+
+				window.addEventListener( 'resize', onWindowResize, false );
+				container.addEventListener( 'click', onClick, false );
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			};
+
+			function onClick( evt ) {
+
+				evt.preventDefault();
+
+				var array = getMousePosition( container, evt.clientX, evt.clientY );
+				onClickPosition.fromArray( array );
+
+				var intersects = getIntersects( onClickPosition, objects );
+				if ( intersects.length > 0 && intersects[ 0 ].uv ) {
+
+					var uv = intersects[ 0 ].uv;
+					intersects[ 0 ].object.material.map.transformUv( uv );
+					canvas.setCrossPosition( uv.x, uv.y );
+
+				}
+
+			};
+
+			var getMousePosition = function ( dom, x, y ) {
+
+				var rect = dom.getBoundingClientRect();
+				return [ ( x - rect.left ) / rect.width, ( y - rect.top ) / rect.height ];
+
+			};
+
+			var getIntersects = function ( point, objects ) {
+
+				mouse.set( ( point.x * 2 ) - 1, - ( point.y * 2 ) + 1 );
+
+				raycaster.setFromCamera( mouse, camera );
+
+				return raycaster.intersectObjects( objects );
+
+			};
+
+			function render() {
+
+				requestAnimationFrame( render );
+				renderer.render( scene, camera );
+
+			};
+
+			function setwrapS( that ) {
+
+				circleTexture.wrapS = THREE[ that.value ];
+				circleTexture.needsUpdate = true;
+
+			};
+
+			function setwrapT( that ) {
+
+				circleTexture.wrapT = THREE[ that.value ];
+				circleTexture.needsUpdate = true;
+
+			};
+
+			function setOffsetU( that ) {
+
+				circleTexture.offset.x = parseFloat( that.value );
+
+			};
+
+			function setOffsetV( that ) {
+
+				circleTexture.offset.y = parseFloat( that.value );
+
+			};
+
+			function setRepeatU( that ) {
+
+				circleTexture.repeat.x = parseFloat( that.value );
+
+			};
+
+			function setRepeatV( that ) {
+
+				circleTexture.repeat.y = parseFloat( that.value );
+
+			};
+
+		</script>
+	</body>
+</html>

+ 63 - 0
src/objects/Mesh.js

@@ -69,6 +69,12 @@ THREE.Mesh.prototype.raycast = ( function () {
 	var tempB = new THREE.Vector3();
 	var tempC = new THREE.Vector3();
 
+	var uvA = new THREE.Vector2();
+	var uvB = new THREE.Vector2();
+	var uvC = new THREE.Vector2();
+	var bary = new THREE.Vector3();
+	var pInter = new THREE.Vector3();
+
 	return function raycast( raycaster, intersects ) {
 
 		var geometry = this.geometry;
@@ -106,6 +112,21 @@ THREE.Mesh.prototype.raycast = ( function () {
 
 		var a, b, c;
 
+		var textureIntersection = function ( pIntersection, p1, p2, p3, uv1, uv2, uv3 ) {
+
+			THREE.Triangle.barycoordFromPoint( pIntersection, p1, p2, p3, bary );
+
+			uv1.multiplyScalar( bary.x );
+			uv2.multiplyScalar( bary.y );
+			uv3.multiplyScalar( bary.z );
+
+			uv1.add( uv2 );
+			uv1.add( uv3 );
+
+			return uv1.clone();
+
+		};
+
 		if ( geometry instanceof THREE.BufferGeometry ) {
 
 			var index = geometry.index;
@@ -155,16 +176,30 @@ THREE.Mesh.prototype.raycast = ( function () {
 
 						if ( intersectionPoint === null ) continue;
 
+						pInter.copy( intersectionPoint );
 						intersectionPoint.applyMatrix4( this.matrixWorld );
 
 						var distance = raycaster.ray.origin.distanceTo( intersectionPoint );
 
 						if ( distance < raycaster.near || distance > raycaster.far ) continue;
 
+						// intersectionPoint in UV coordinates.
+						var uv = undefined;
+						if ( material.map && attributes.uv !== undefined ) {
+
+							var uvs = attributes.uv.array;
+							uvA.fromArray( uvs, a * 2 );
+							uvB.fromArray( uvs, b * 2 );
+							uvC.fromArray( uvs, c * 2 );
+							uv = textureIntersection( pInter, vA, vB, vC, uvA, uvB, uvC );
+
+						}
+
 						intersects.push( {
 
 							distance: distance,
 							point: intersectionPoint,
+							uv: uv,
 							face: new THREE.Face3( a, b, c, THREE.Triangle.normal( vA, vB, vC ) ),
 							faceIndex: Math.floor( i / 3 ), // triangle number in indices buffer semantics
 							object: this
@@ -197,12 +232,25 @@ THREE.Mesh.prototype.raycast = ( function () {
 
 					if ( intersectionPoint === null ) continue;
 
+					pInter.copy( intersectionPoint );
 					intersectionPoint.applyMatrix4( this.matrixWorld );
 
 					var distance = raycaster.ray.origin.distanceTo( intersectionPoint );
 
 					if ( distance < raycaster.near || distance > raycaster.far ) continue;
 
+					// intersectionPoint in UV coordinates.
+					var uv = undefined;
+					if ( material.map && attributes.uv !== undefined ) {
+
+						var uvs = attributes.uv.array;
+						uvA.fromArray( uvs, i );
+						uvB.fromArray( uvs, i + 2 );
+						uvC.fromArray( uvs, i + 4 );
+						uv = textureIntersection( pInter, vA, vB, vC, uvA, uvB, uvC );
+
+					}
+
 					a = i / 3;
 					b = a + 1;
 					c = a + 2;
@@ -211,6 +259,7 @@ THREE.Mesh.prototype.raycast = ( function () {
 
 						distance: distance,
 						point: intersectionPoint,
+						uv: uv,
 						face: new THREE.Face3( a, b, c, THREE.Triangle.normal( vA, vB, vC ) ),
 						index: a, // triangle number in positions buffer semantics
 						object: this
@@ -285,16 +334,30 @@ THREE.Mesh.prototype.raycast = ( function () {
 
 				if ( intersectionPoint === null ) continue;
 
+				pInter.copy( intersectionPoint );
 				intersectionPoint.applyMatrix4( this.matrixWorld );
 
 				var distance = raycaster.ray.origin.distanceTo( intersectionPoint );
 
 				if ( distance < raycaster.near || distance > raycaster.far ) continue;
 
+				// intersectionPoint in UV coordinates.
+				var uv = undefined;
+				if ( material.map && geometry.faceVertexUvs[ 0 ] !== undefined ) {
+
+					var uvs = geometry.faceVertexUvs[ 0 ][ f ];
+					uvA.copy( uvs[ 0 ] );
+					uvB.copy( uvs[ 1 ] );
+					uvC.copy( uvs[ 2 ] );
+					uv = textureIntersection( pInter, a, b, c, uvA, uvB, uvC );
+
+				}
+
 				intersects.push( {
 
 					distance: distance,
 					point: intersectionPoint,
+					uv: uv,
 					face: face,
 					faceIndex: f,
 					object: this

+ 77 - 0
src/textures/Texture.js

@@ -185,6 +185,83 @@ THREE.Texture.prototype = {
 
 		this.dispatchEvent( { type: 'dispose' } );
 
+	},
+
+	transformUv: function ( uv ) {
+
+		if ( this.mapping !== THREE.UVMapping )  return;
+
+		uv.multiply( this.repeat );
+		uv.add( this.offset );
+
+		if ( uv.x < 0 || uv.x > 1 ) {
+
+			switch ( this.wrapS ) {
+
+				case THREE.RepeatWrapping:
+
+					uv.x = uv.x - Math.floor( uv.x );
+					break;
+
+				case THREE.ClampToEdgeWrapping:
+
+					uv.x = uv.x < 0 ? 0 : 1;
+					break;
+
+				case THREE.MirroredRepeatWrapping:
+
+					if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) {
+
+						uv.x = Math.ceil( uv.x ) - uv.x;
+
+					} else {
+
+						uv.x = uv.x - Math.floor( uv.x );
+
+					}
+					break;
+
+			}
+
+		}
+
+		if ( uv.y < 0 || uv.y > 1 ) {
+
+			switch ( this.wrapT ) {
+
+				case THREE.RepeatWrapping:
+
+					uv.y = uv.y - Math.floor( uv.y );
+					break;
+
+				case THREE.ClampToEdgeWrapping:
+
+					uv.y = uv.y < 0 ? 0 : 1;
+					break;
+
+				case THREE.MirroredRepeatWrapping:
+
+					if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) {
+
+						uv.y = Math.ceil( uv.y ) - uv.y;
+
+					} else {
+
+						uv.y = uv.y - Math.floor( uv.y );
+
+					}
+					break;
+
+			}
+
+		}
+
+		if ( this.flipY ) {
+
+			uv.y = 1 - uv.y;
+
+		}
+
 	}
 
 };