diff --git a/examples/index.html b/examples/index.html index 5beca35..1350af8 100644 --- a/examples/index.html +++ b/examples/index.html @@ -36,7 +36,7 @@ // if you add renderertype:'Canvas' to the options for ThreejsLayer, you can force the usage of CanvasRenderer new ThreejsLayer({ map: map }, function(layer){ - + if (layer.renderertype=='Canvas' || !Detector.webgl) { texture = new THREE.Texture(generateSprite()); particles = new THREE.Object3D(); @@ -48,8 +48,8 @@ depthTest: false, transparent: true }); - - + + photos.forEach(function (photo) { var particle = new THREE.Sprite(material); var location = new google.maps.LatLng(photo[0], photo[1]), @@ -74,7 +74,7 @@ texture.needsUpdate = true; - material = new THREE.ParticleBasicMaterial({ + material = new THREE.PointCloudMaterial({ size: 20, map: texture, opacity: 0.3, @@ -83,7 +83,7 @@ transparent: true }); - particles = new THREE.ParticleSystem( geometry, material ); + particles = new THREE.PointCloud( geometry, material ); } layer.add( particles ); diff --git a/lib/three.js b/lib/three.js index b6b1e41..636b15d 100644 --- a/lib/three.js +++ b/lib/three.js @@ -4,15 +4,32 @@ * @author mrdoob / http://mrdoob.com/ */ -var THREE = { REVISION: '68' }; +var THREE = { REVISION: '69' }; // browserify support + if ( typeof module === 'object' ) { module.exports = THREE; } +// polyfills + +if ( Math.sign === undefined ) { + + Math.sign = function ( x ) { + + return ( x < 0 ) ? - 1 : ( x > 0 ) ? 1 : 0; + + }; + +} + +// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent.button + +THREE.MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2 }; + // GL STATE CONSTANTS THREE.CullFaceNone = 0; @@ -65,6 +82,8 @@ THREE.CustomBlending = 5; THREE.AddEquation = 100; THREE.SubtractEquation = 101; THREE.ReverseSubtractEquation = 102; +THREE.MinEquation = 103; +THREE.MaxEquation = 104; // custom blending destination factors @@ -146,20 +165,21 @@ THREE.RGBAFormat = 1021; THREE.LuminanceFormat = 1022; THREE.LuminanceAlphaFormat = 1023; -// Compressed texture formats +// DDS / ST3C Compressed texture formats THREE.RGB_S3TC_DXT1_Format = 2001; THREE.RGBA_S3TC_DXT1_Format = 2002; THREE.RGBA_S3TC_DXT3_Format = 2003; THREE.RGBA_S3TC_DXT5_Format = 2004; -/* -// Potential future PVRTC compressed texture formats + +// PVRTC compressed texture formats + THREE.RGB_PVRTC_4BPPV1_Format = 2100; THREE.RGB_PVRTC_2BPPV1_Format = 2101; THREE.RGBA_PVRTC_4BPPV1_Format = 2102; THREE.RGBA_PVRTC_2BPPV1_Format = 2103; -*/ + // File:src/math/Color.js @@ -973,6 +993,9 @@ THREE.Quaternion.prototype = { slerp: function ( qb, t ) { + if ( t === 0 ) return this; + if ( t === 1 ) return this.copy( qb ); + var x = this._x, y = this._y, z = this._z, w = this._w; // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ @@ -1039,12 +1062,14 @@ THREE.Quaternion.prototype = { }, - fromArray: function ( array ) { + fromArray: function ( array, offset ) { - this._x = array[ 0 ]; - this._y = array[ 1 ]; - this._z = array[ 2 ]; - this._w = array[ 3 ]; + if ( offset === undefined ) offset = 0; + + this._x = array[ offset ]; + this._y = array[ offset + 1 ]; + this._z = array[ offset + 2 ]; + this._w = array[ offset + 3 ]; this.onChangeCallback(); @@ -1052,9 +1077,17 @@ THREE.Quaternion.prototype = { }, - toArray: function () { + toArray: function ( array, offset ) { - return [ this._x, this._y, this._z, this._w ]; + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this._x; + array[ offset + 1 ] = this._y; + array[ offset + 2 ] = this._z; + array[ offset + 3 ] = this._w; + + return array; }, @@ -1461,18 +1494,26 @@ THREE.Vector2.prototype = { }, - fromArray: function ( array ) { + fromArray: function ( array, offset ) { - this.x = array[ 0 ]; - this.y = array[ 1 ]; + if ( offset === undefined ) offset = 0; + + this.x = array[ offset ]; + this.y = array[ offset + 1 ]; return this; }, - toArray: function () { + toArray: function ( array, offset ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this.x; + array[ offset + 1 ] = this.y; - return [ this.x, this.y ]; + return array; }, @@ -1793,6 +1834,36 @@ THREE.Vector3.prototype = { }, + project: function () { + + var matrix; + + return function ( camera ) { + + if ( matrix === undefined ) matrix = new THREE.Matrix4(); + + matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) ); + return this.applyProjection( matrix ); + + }; + + }(), + + unproject: function () { + + var matrix; + + return function ( camera ) { + + if ( matrix === undefined ) matrix = new THREE.Matrix4(); + + matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) ); + return this.applyProjection( matrix ); + + }; + + }(), + transformDirection: function ( m ) { // input: THREE.Matrix4 affine matrix @@ -2242,19 +2313,28 @@ THREE.Vector3.prototype = { }, - fromArray: function ( array ) { + fromArray: function ( array, offset ) { - this.x = array[ 0 ]; - this.y = array[ 1 ]; - this.z = array[ 2 ]; + if ( offset === undefined ) offset = 0; + + this.x = array[ offset ]; + this.y = array[ offset + 1 ]; + this.z = array[ offset + 2 ]; return this; }, - toArray: function () { + toArray: function ( array, offset ) { - return [ this.x, this.y, this.z ]; + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this.x; + array[ offset + 1 ] = this.y; + array[ offset + 2 ] = this.z; + + return array; }, @@ -2891,20 +2971,30 @@ THREE.Vector4.prototype = { }, - fromArray: function ( array ) { + fromArray: function ( array, offset ) { - this.x = array[ 0 ]; - this.y = array[ 1 ]; - this.z = array[ 2 ]; - this.w = array[ 3 ]; + if ( offset === undefined ) offset = 0; + + this.x = array[ offset ]; + this.y = array[ offset + 1 ]; + this.z = array[ offset + 2 ]; + this.w = array[ offset + 3 ]; return this; }, - toArray: function () { + toArray: function ( array, offset ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this.x; + array[ offset + 1 ] = this.y; + array[ offset + 2 ] = this.z; + array[ offset + 3 ] = this.w; - return [ this.x, this.y, this.z, this.w ]; + return array; }, @@ -3708,17 +3798,37 @@ THREE.Box3.prototype = { object.traverse( function ( node ) { - if ( node.geometry !== undefined && node.geometry.vertices !== undefined ) { + var geometry = node.geometry; + + if ( geometry !== undefined ) { + + if ( geometry instanceof THREE.Geometry ) { + + var vertices = geometry.vertices; + + for ( var i = 0, il = vertices.length; i < il; i ++ ) { + + v1.copy( vertices[ i ] ); + + v1.applyMatrix4( node.matrixWorld ); + + scope.expandByPoint( v1 ); - var vertices = node.geometry.vertices; + } + + } else if ( geometry instanceof THREE.BufferGeometry && geometry.attributes[ 'position' ] !== undefined ) { + + var positions = geometry.attributes[ 'position' ].array; + + for ( var i = 0, il = positions.length; i < il; i += 3 ) { - for ( var i = 0, il = vertices.length; i < il; i ++ ) { + v1.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); - v1.copy( vertices[ i ] ); + v1.applyMatrix4( node.matrixWorld ); - v1.applyMatrix4( node.matrixWorld ); + scope.expandByPoint( v1 ); - scope.expandByPoint( v1 ); + } } @@ -3978,15 +4088,21 @@ THREE.Box3.prototype = { * @author bhouston / http://exocortex.com */ -THREE.Matrix3 = function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { +THREE.Matrix3 = function () { + + this.elements = new Float32Array( [ + + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 + + ] ); - this.elements = new Float32Array( 9 ); + if ( arguments.length > 0 ) { - var te = this.elements; + console.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' ); - te[ 0 ] = ( n11 !== undefined ) ? n11 : 1; te[ 3 ] = n12 || 0; te[ 6 ] = n13 || 0; - te[ 1 ] = n21 || 0; te[ 4 ] = ( n22 !== undefined ) ? n22 : 1; te[ 7 ] = n23 || 0; - te[ 2 ] = n31 || 0; te[ 5 ] = n32 || 0; te[ 8 ] = ( n33 !== undefined ) ? n33 : 1; + } }; @@ -4233,15 +4349,7 @@ THREE.Matrix3.prototype = { clone: function () { - var te = this.elements; - - return new THREE.Matrix3( - - te[ 0 ], te[ 3 ], te[ 6 ], - te[ 1 ], te[ 4 ], te[ 7 ], - te[ 2 ], te[ 5 ], te[ 8 ] - - ); + return new THREE.Matrix3().fromArray( this.elements ); } @@ -4262,20 +4370,22 @@ THREE.Matrix3.prototype = { * @author WestLangley / http://github.com/WestLangley */ +THREE.Matrix4 = function () { + + this.elements = new Float32Array( [ -THREE.Matrix4 = function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 - this.elements = new Float32Array( 16 ); + ] ); - // TODO: if n11 is undefined, then just set to identity, otherwise copy all other values into matrix - // we should not support semi specification of Matrix4, it is just weird. + if ( arguments.length > 0 ) { - var te = this.elements; + console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' ); - te[ 0 ] = ( n11 !== undefined ) ? n11 : 1; te[ 4 ] = n12 || 0; te[ 8 ] = n13 || 0; te[ 12 ] = n14 || 0; - te[ 1 ] = n21 || 0; te[ 5 ] = ( n22 !== undefined ) ? n22 : 1; te[ 9 ] = n23 || 0; te[ 13 ] = n24 || 0; - te[ 2 ] = n31 || 0; te[ 6 ] = n32 || 0; te[ 10 ] = ( n33 !== undefined ) ? n33 : 1; te[ 14 ] = n34 || 0; - te[ 3 ] = n41 || 0; te[ 7 ] = n42 || 0; te[ 11 ] = n43 || 0; te[ 15 ] = ( n44 !== undefined ) ? n44 : 1; + } }; @@ -4321,7 +4431,7 @@ THREE.Matrix4.prototype = { extractPosition: function ( m ) { - console.warn( 'THREEMatrix4: .extractPosition() has been renamed to .copyPosition().' ); + console.warn( 'THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().' ); return this.copyPosition( m ); }, @@ -5205,16 +5315,7 @@ THREE.Matrix4.prototype = { clone: function () { - var te = this.elements; - - return new THREE.Matrix4( - - te[ 0 ], te[ 4 ], te[ 8 ], te[ 12 ], - te[ 1 ], te[ 5 ], te[ 9 ], te[ 13 ], - te[ 2 ], te[ 6 ], te[ 10 ], te[ 14 ], - te[ 3 ], te[ 7 ], te[ 11 ], te[ 15 ] - - ); + return new THREE.Matrix4().fromArray( this.elements ); } @@ -6422,12 +6523,6 @@ THREE.Math = { }, - sign: function ( x ) { - - return ( x < 0 ) ? - 1 : ( x > 0 ) ? 1 : 0; - - }, - degToRad: function () { var degreeToRadiansFactor = Math.PI / 180; @@ -7070,7 +7165,7 @@ THREE.EventDispatcher.prototype = { }; // - + THREE.Raycaster.prototype = { constructor: THREE.Raycaster, @@ -7101,6 +7196,13 @@ THREE.EventDispatcher.prototype = { var intersects = []; + if ( objects instanceof Array === false ) { + + console.log( 'THREE.Raycaster.intersectObjects: objects is not an Array.' ); + return intersects; + + } + for ( var i = 0, l = objects.length; i < l; i ++ ) { intersectObject( objects[ i ], this, intersects, recursive ); @@ -7128,10 +7230,12 @@ THREE.EventDispatcher.prototype = { THREE.Object3D = function () { - this.id = THREE.Object3DIdCount ++; + Object.defineProperty( this, 'id', { value: THREE.Object3DIdCount ++ } ); + this.uuid = THREE.Math.generateUUID(); this.name = ''; + this.type = 'Object3D'; this.parent = undefined; this.children = []; @@ -7145,13 +7249,16 @@ THREE.Object3D = function () { var quaternion = new THREE.Quaternion(); var scale = new THREE.Vector3( 1, 1, 1 ); - rotation.onChange( function () { + var onRotationChange = function () { quaternion.setFromEuler( rotation, false ); - } ); + }; - quaternion.onChange( function () { + var onQuaternionChange = function () { rotation.setFromQuaternion( quaternion, undefined, false ); - } ); + }; + + rotation.onChange( onRotationChange ); + quaternion.onChange( onQuaternionChange ); Object.defineProperties( this, { position: { @@ -7450,26 +7557,10 @@ THREE.Object3D.prototype = { this.children.push( object ); - // add to scene - - var scene = this; - - while ( scene.parent !== undefined ) { - - scene = scene.parent; - - } - - if ( scene !== undefined && scene instanceof THREE.Scene ) { - - scene.__addObject( object ); - - } - } else { - + console.error( "THREE.Object3D.add:", object, "is not an instance of THREE.Object3D." ); - + } return this; @@ -7493,122 +7584,169 @@ THREE.Object3D.prototype = { if ( index !== - 1 ) { object.parent = undefined; + object.dispatchEvent( { type: 'removed' } ); this.children.splice( index, 1 ); - // remove from scene + } + + }, - var scene = this; + getChildByName: function ( name, recursive ) { - while ( scene.parent !== undefined ) { + console.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' ); + return this.getObjectByName( name, recursive ); - scene = scene.parent; + }, - } + getObjectById: function ( id, recursive ) { - if ( scene !== undefined && scene instanceof THREE.Scene ) { + if ( this.id === id ) return this; - scene.__removeObject( object ); + for ( var i = 0, l = this.children.length; i < l; i ++ ) { + + var child = this.children[ i ]; + var object = child.getObjectById( id, recursive ); + + if ( object !== undefined ) { + + return object; } } - }, + return undefined; - raycast: function () {}, + }, - traverse: function ( callback ) { + getObjectByName: function ( name, recursive ) { - callback( this ); + if ( this.name === name ) return this; for ( var i = 0, l = this.children.length; i < l; i ++ ) { - this.children[ i ].traverse( callback ); + var child = this.children[ i ]; + var object = child.getObjectByName( name, recursive ); + + if ( object !== undefined ) { + + return object; + + } } + return undefined; + }, - traverseVisible: function ( callback ) { + getWorldPosition: function ( optionalTarget ) { - if ( this.visible === false ) return; + var result = optionalTarget || new THREE.Vector3(); - callback( this ); + this.updateMatrixWorld( true ); - for ( var i = 0, l = this.children.length; i < l; i ++ ) { + return result.setFromMatrixPosition( this.matrixWorld ); - this.children[ i ].traverseVisible( callback ); + }, - } + getWorldQuaternion: function () { - }, + var position = new THREE.Vector3(); + var scale = new THREE.Vector3(); - getObjectById: function ( id, recursive ) { + return function ( optionalTarget ) { - for ( var i = 0, l = this.children.length; i < l; i ++ ) { + var result = optionalTarget || new THREE.Quaternion(); - var child = this.children[ i ]; + this.updateMatrixWorld( true ); + + this.matrixWorld.decompose( position, result, scale ); - if ( child.id === id ) { + return result; - return child; + } - } + }(), - if ( recursive === true ) { + getWorldRotation: function () { - child = child.getObjectById( id, recursive ); + var quaternion = new THREE.Quaternion(); - if ( child !== undefined ) { + return function ( optionalTarget ) { - return child; + var result = optionalTarget || new THREE.Euler(); - } + this.getWorldQuaternion( quaternion ); - } + return result.setFromQuaternion( quaternion, this.rotation.order, false ); } - return undefined; + }(), - }, + getWorldScale: function () { - getObjectByName: function ( name, recursive ) { + var position = new THREE.Vector3(); + var quaternion = new THREE.Quaternion(); - for ( var i = 0, l = this.children.length; i < l; i ++ ) { + return function ( optionalTarget ) { - var child = this.children[ i ]; + var result = optionalTarget || new THREE.Vector3(); - if ( child.name === name ) { + this.updateMatrixWorld( true ); - return child; + this.matrixWorld.decompose( position, quaternion, result ); - } + return result; + + } - if ( recursive === true ) { + }(), - child = child.getObjectByName( name, recursive ); + getWorldDirection: function () { - if ( child !== undefined ) { + var quaternion = new THREE.Quaternion(); - return child; + return function ( optionalTarget ) { - } + var result = optionalTarget || new THREE.Vector3(); - } + this.getWorldQuaternion( quaternion ); + + return result.set( 0, 0, 1 ).applyQuaternion( quaternion ); } - return undefined; + }(), + + raycast: function () {}, + + traverse: function ( callback ) { + + callback( this ); + + for ( var i = 0, l = this.children.length; i < l; i ++ ) { + + this.children[ i ].traverse( callback ); + + } }, - getChildByName: function ( name, recursive ) { + traverseVisible: function ( callback ) { - console.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' ); - return this.getObjectByName( name, recursive ); + if ( this.visible === false ) return; + + callback( this ); + + for ( var i = 0, l = this.children.length; i < l; i ++ ) { + + this.children[ i ].traverseVisible( callback ); + + } }, @@ -7652,1780 +7790,1901 @@ THREE.Object3D.prototype = { }, - clone: function ( object, recursive ) { + toJSON: function () { - if ( object === undefined ) object = new THREE.Object3D(); - if ( recursive === undefined ) recursive = true; + var output = { + metadata: { + version: 4.3, + type: 'Object', + generator: 'ObjectExporter' + } + }; - object.name = this.name; + // - object.up.copy( this.up ); + var geometries = {}; - object.position.copy( this.position ); - object.quaternion.copy( this.quaternion ); - object.scale.copy( this.scale ); + var parseGeometry = function ( geometry ) { - object.renderDepth = this.renderDepth; + if ( output.geometries === undefined ) { - object.rotationAutoUpdate = this.rotationAutoUpdate; + output.geometries = []; - object.matrix.copy( this.matrix ); - object.matrixWorld.copy( this.matrixWorld ); + } - object.matrixAutoUpdate = this.matrixAutoUpdate; - object.matrixWorldNeedsUpdate = this.matrixWorldNeedsUpdate; + if ( geometries[ geometry.uuid ] === undefined ) { - object.visible = this.visible; + var json = geometry.toJSON(); - object.castShadow = this.castShadow; - object.receiveShadow = this.receiveShadow; + delete json.metadata; - object.frustumCulled = this.frustumCulled; + geometries[ geometry.uuid ] = json; - object.userData = JSON.parse( JSON.stringify( this.userData ) ); + output.geometries.push( json ); - if ( recursive === true ) { + } - for ( var i = 0; i < this.children.length; i ++ ) { + return geometry.uuid; - var child = this.children[ i ]; - object.add( child.clone() ); + }; - } + // - } + var materials = {}; - return object; + var parseMaterial = function ( material ) { - } + if ( output.materials === undefined ) { -}; + output.materials = []; -THREE.EventDispatcher.prototype.apply( THREE.Object3D.prototype ); + } -THREE.Object3DIdCount = 0; + if ( materials[ material.uuid ] === undefined ) { -// File:src/core/Projector.js + var json = material.toJSON(); -/** - * @author mrdoob / http://mrdoob.com/ - * @author supereggbert / http://www.paulbrunt.co.uk/ - * @author julianwa / https://github.com/julianwa - */ + delete json.metadata; -THREE.Projector = function () { + materials[ material.uuid ] = json; - var _object, _objectCount, _objectPool = [], _objectPoolLength = 0, - _vertex, _vertexCount, _vertexPool = [], _vertexPoolLength = 0, - _face, _faceCount, _facePool = [], _facePoolLength = 0, - _line, _lineCount, _linePool = [], _linePoolLength = 0, - _sprite, _spriteCount, _spritePool = [], _spritePoolLength = 0, + output.materials.push( json ); - _renderData = { objects: [], lights: [], elements: [] }, + } - _vA = new THREE.Vector3(), - _vB = new THREE.Vector3(), - _vC = new THREE.Vector3(), + return material.uuid; - _vector3 = new THREE.Vector3(), - _vector4 = new THREE.Vector4(), + }; - _clipBox = new THREE.Box3( new THREE.Vector3( - 1, - 1, - 1 ), new THREE.Vector3( 1, 1, 1 ) ), - _boundingBox = new THREE.Box3(), - _points3 = new Array( 3 ), - _points4 = new Array( 4 ), + // - _viewMatrix = new THREE.Matrix4(), - _viewProjectionMatrix = new THREE.Matrix4(), + var parseObject = function ( object ) { - _modelMatrix, - _modelViewProjectionMatrix = new THREE.Matrix4(), + var data = {}; - _normalMatrix = new THREE.Matrix3(), + data.uuid = object.uuid; + data.type = object.type; - _frustum = new THREE.Frustum(), + if ( object.name !== '' ) data.name = object.name; + if ( JSON.stringify( object.userData ) !== '{}' ) data.userData = object.userData; + if ( object.visible !== true ) data.visible = object.visible; - _clippedVertex1PositionScreen = new THREE.Vector4(), - _clippedVertex2PositionScreen = new THREE.Vector4(); + if ( object instanceof THREE.PerspectiveCamera ) { - this.projectVector = function ( vector, camera ) { + data.fov = object.fov; + data.aspect = object.aspect; + data.near = object.near; + data.far = object.far; - camera.matrixWorldInverse.getInverse( camera.matrixWorld ); + } else if ( object instanceof THREE.OrthographicCamera ) { - _viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); + data.left = object.left; + data.right = object.right; + data.top = object.top; + data.bottom = object.bottom; + data.near = object.near; + data.far = object.far; - return vector.applyProjection( _viewProjectionMatrix ); + } else if ( object instanceof THREE.AmbientLight ) { - }; + data.color = object.color.getHex(); - this.unprojectVector = function () { + } else if ( object instanceof THREE.DirectionalLight ) { - var projectionMatrixInverse = new THREE.Matrix4(); + data.color = object.color.getHex(); + data.intensity = object.intensity; - return function ( vector, camera ) { + } else if ( object instanceof THREE.PointLight ) { - projectionMatrixInverse.getInverse( camera.projectionMatrix ); - _viewProjectionMatrix.multiplyMatrices( camera.matrixWorld, projectionMatrixInverse ); + data.color = object.color.getHex(); + data.intensity = object.intensity; + data.distance = object.distance; - return vector.applyProjection( _viewProjectionMatrix ); + } else if ( object instanceof THREE.SpotLight ) { - }; + data.color = object.color.getHex(); + data.intensity = object.intensity; + data.distance = object.distance; + data.angle = object.angle; + data.exponent = object.exponent; - }(); + } else if ( object instanceof THREE.HemisphereLight ) { - this.pickingRay = function ( vector, camera ) { + data.color = object.color.getHex(); + data.groundColor = object.groundColor.getHex(); - // set two vectors with opposing z values - vector.z = - 1.0; - var end = new THREE.Vector3( vector.x, vector.y, 1.0 ); + } else if ( object instanceof THREE.Mesh ) { - this.unprojectVector( vector, camera ); - this.unprojectVector( end, camera ); + data.geometry = parseGeometry( object.geometry ); + data.material = parseMaterial( object.material ); - // find direction from vector to end - end.sub( vector ).normalize(); + } else if ( object instanceof THREE.Line ) { - return new THREE.Raycaster( vector, end ); + data.geometry = parseGeometry( object.geometry ); + data.material = parseMaterial( object.material ); - }; + } else if ( object instanceof THREE.Sprite ) { - var RenderList = function () { + data.material = parseMaterial( object.material ); - var normals = []; - var uvs = []; + } - var object = null; - var material = null; + data.matrix = object.matrix.toArray(); - var normalMatrix = new THREE.Matrix3(); + if ( object.children.length > 0 ) { - var setObject = function ( value ) { + data.children = []; - object = value; - material = object.material; + for ( var i = 0; i < object.children.length; i ++ ) { - normalMatrix.getNormalMatrix( object.matrixWorld ); + data.children.push( parseObject( object.children[ i ] ) ); - normals.length = 0; - uvs.length = 0; + } - }; + } - var projectVertex = function ( vertex ) { + return data; - var position = vertex.position; - var positionWorld = vertex.positionWorld; - var positionScreen = vertex.positionScreen; + } - positionWorld.copy( position ).applyMatrix4( _modelMatrix ); - positionScreen.copy( positionWorld ).applyMatrix4( _viewProjectionMatrix ); + output.object = parseObject( this ); - var invW = 1 / positionScreen.w; + return output; - positionScreen.x *= invW; - positionScreen.y *= invW; - positionScreen.z *= invW; + }, - vertex.visible = positionScreen.x >= - 1 && positionScreen.x <= 1 && - positionScreen.y >= - 1 && positionScreen.y <= 1 && - positionScreen.z >= - 1 && positionScreen.z <= 1; + clone: function ( object, recursive ) { - }; + if ( object === undefined ) object = new THREE.Object3D(); + if ( recursive === undefined ) recursive = true; - var pushVertex = function ( x, y, z ) { + object.name = this.name; - _vertex = getNextVertexInPool(); - _vertex.position.set( x, y, z ); + object.up.copy( this.up ); - projectVertex( _vertex ); + object.position.copy( this.position ); + object.quaternion.copy( this.quaternion ); + object.scale.copy( this.scale ); - }; + object.renderDepth = this.renderDepth; - var pushNormal = function ( x, y, z ) { + object.rotationAutoUpdate = this.rotationAutoUpdate; - normals.push( x, y, z ); + object.matrix.copy( this.matrix ); + object.matrixWorld.copy( this.matrixWorld ); - }; + object.matrixAutoUpdate = this.matrixAutoUpdate; + object.matrixWorldNeedsUpdate = this.matrixWorldNeedsUpdate; - var pushUv = function ( x, y ) { + object.visible = this.visible; - uvs.push( x, y ); + object.castShadow = this.castShadow; + object.receiveShadow = this.receiveShadow; - }; + object.frustumCulled = this.frustumCulled; - var checkTriangleVisibility = function ( v1, v2, v3 ) { + object.userData = JSON.parse( JSON.stringify( this.userData ) ); - if ( v1.visible === true || v2.visible === true || v3.visible === true ) return true; + if ( recursive === true ) { - _points3[ 0 ] = v1.positionScreen; - _points3[ 1 ] = v2.positionScreen; - _points3[ 2 ] = v3.positionScreen; + for ( var i = 0; i < this.children.length; i ++ ) { - return _clipBox.isIntersectionBox( _boundingBox.setFromPoints( _points3 ) ); + var child = this.children[ i ]; + object.add( child.clone() ); - }; + } - var checkBackfaceCulling = function ( v1, v2, v3 ) { + } - return ( ( v3.positionScreen.x - v1.positionScreen.x ) * - ( v2.positionScreen.y - v1.positionScreen.y ) - - ( v3.positionScreen.y - v1.positionScreen.y ) * - ( v2.positionScreen.x - v1.positionScreen.x ) ) < 0; + return object; - }; + } - var pushLine = function ( a, b ) { +}; - var v1 = _vertexPool[ a ]; - var v2 = _vertexPool[ b ]; +THREE.EventDispatcher.prototype.apply( THREE.Object3D.prototype ); - _line = getNextLineInPool(); +THREE.Object3DIdCount = 0; - _line.id = object.id; - _line.v1.copy( v1 ); - _line.v2.copy( v2 ); - _line.z = ( v1.positionScreen.z + v2.positionScreen.z ) / 2; +// File:src/core/Projector.js - _line.material = object.material; +/** + * @author mrdoob / http://mrdoob.com/ + */ - _renderData.elements.push( _line ); +THREE.Projector = function () { - }; + console.warn( 'THREE.Projector has been moved to /examples/renderers/Projector.js.' ); - var pushTriangle = function ( a, b, c ) { + this.projectVector = function ( vector, camera ) { - var v1 = _vertexPool[ a ]; - var v2 = _vertexPool[ b ]; - var v3 = _vertexPool[ c ]; + console.warn( 'THREE.Projector: .projectVector() is now vector.project().' ); + vector.project( camera ); - if ( checkTriangleVisibility( v1, v2, v3 ) === false ) return; + }; - if ( material.side === THREE.DoubleSide || checkBackfaceCulling( v1, v2, v3 ) === true ) { + this.unprojectVector = function ( vector, camera ) { - _face = getNextFaceInPool(); + console.warn( 'THREE.Projector: .unprojectVector() is now vector.unproject().' ); + vector.unproject( camera ); - _face.id = object.id; - _face.v1.copy( v1 ); - _face.v2.copy( v2 ); - _face.v3.copy( v3 ); - _face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3; + }; - for ( var i = 0; i < 3; i ++ ) { + this.pickingRay = function ( vector, camera ) { - var offset = arguments[ i ] * 3; - var normal = _face.vertexNormalsModel[ i ]; + console.error( 'THREE.Projector: .pickingRay() has been removed.' ); - normal.set( normals[ offset ], normals[ offset + 1 ], normals[ offset + 2 ] ); - normal.applyMatrix3( normalMatrix ).normalize(); + }; - var offset2 = arguments[ i ] * 2; +}; - var uv = _face.uvs[ i ]; - uv.set( uvs[ offset2 ], uvs[ offset2 + 1 ] ); +// File:src/core/Face3.js - } +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ - _face.vertexNormalsLength = 3; +THREE.Face3 = function ( a, b, c, normal, color, materialIndex ) { - _face.material = object.material; + this.a = a; + this.b = b; + this.c = c; - _renderData.elements.push( _face ); + this.normal = normal instanceof THREE.Vector3 ? normal : new THREE.Vector3(); + this.vertexNormals = normal instanceof Array ? normal : []; - } + this.color = color instanceof THREE.Color ? color : new THREE.Color(); + this.vertexColors = color instanceof Array ? color : []; - }; + this.vertexTangents = []; - return { - setObject: setObject, - projectVertex: projectVertex, - checkTriangleVisibility: checkTriangleVisibility, - checkBackfaceCulling: checkBackfaceCulling, - pushVertex: pushVertex, - pushNormal: pushNormal, - pushUv: pushUv, - pushLine: pushLine, - pushTriangle: pushTriangle - } + this.materialIndex = materialIndex !== undefined ? materialIndex : 0; - }; +}; - var renderList = new RenderList(); +THREE.Face3.prototype = { - this.projectScene = function ( scene, camera, sortObjects, sortElements ) { + constructor: THREE.Face3, - _faceCount = 0; - _lineCount = 0; - _spriteCount = 0; + clone: function () { - _renderData.elements.length = 0; + var face = new THREE.Face3( this.a, this.b, this.c ); - if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); - if ( camera.parent === undefined ) camera.updateMatrixWorld(); + face.normal.copy( this.normal ); + face.color.copy( this.color ); - _viewMatrix.copy( camera.matrixWorldInverse.getInverse( camera.matrixWorld ) ); - _viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix ); + face.materialIndex = this.materialIndex; - _frustum.setFromMatrix( _viewProjectionMatrix ); + for ( var i = 0, il = this.vertexNormals.length; i < il; i ++ ) { - // + face.vertexNormals[ i ] = this.vertexNormals[ i ].clone(); - _objectCount = 0; + } - _renderData.objects.length = 0; - _renderData.lights.length = 0; + for ( var i = 0, il = this.vertexColors.length; i < il; i ++ ) { - scene.traverseVisible( function ( object ) { + face.vertexColors[ i ] = this.vertexColors[ i ].clone(); - if ( object instanceof THREE.Light ) { + } - _renderData.lights.push( object ); + for ( var i = 0, il = this.vertexTangents.length; i < il; i ++ ) { - } else if ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Sprite ) { + face.vertexTangents[ i ] = this.vertexTangents[ i ].clone(); - if ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) { + } - _object = getNextObjectInPool(); - _object.id = object.id; - _object.object = object; + return face; - if ( object.renderDepth !== null ) { + } - _object.z = object.renderDepth; +}; - } else { +// File:src/core/Face4.js - _vector3.setFromMatrixPosition( object.matrixWorld ); - _vector3.applyProjection( _viewProjectionMatrix ); - _object.z = _vector3.z; +/** + * @author mrdoob / http://mrdoob.com/ + */ - } +THREE.Face4 = function ( a, b, c, d, normal, color, materialIndex ) { - _renderData.objects.push( _object ); + console.warn( 'THREE.Face4 has been removed. A THREE.Face3 will be created instead.' ) + return new THREE.Face3( a, b, c, normal, color, materialIndex ); - } +}; - } +// File:src/core/BufferAttribute.js - } ); +/** + * @author mrdoob / http://mrdoob.com/ + */ - if ( sortObjects === true ) { +THREE.BufferAttribute = function ( array, itemSize ) { - _renderData.objects.sort( painterSort ); + this.array = array; + this.itemSize = itemSize; - } + this.needsUpdate = false; - // +}; - for ( var o = 0, ol = _renderData.objects.length; o < ol; o ++ ) { +THREE.BufferAttribute.prototype = { - var object = _renderData.objects[ o ].object; - var geometry = object.geometry; + constructor: THREE.BufferAttribute, - renderList.setObject( object ); + get length () { - _modelMatrix = object.matrixWorld; + return this.array.length; - _vertexCount = 0; + }, - if ( object instanceof THREE.Mesh ) { + copyAt: function ( index1, attribute, index2 ) { - if ( geometry instanceof THREE.BufferGeometry ) { + index1 *= this.itemSize; + index2 *= attribute.itemSize; - var attributes = geometry.attributes; - var offsets = geometry.offsets; + for ( var i = 0, l = this.itemSize; i < l; i ++ ) { - if ( attributes.position === undefined ) continue; + this.array[ index1 + i ] = attribute.array[ index2 + i ]; - var positions = attributes.position.array; + } - for ( var i = 0, l = positions.length; i < l; i += 3 ) { + }, - renderList.pushVertex( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); + set: function ( value ) { - } + this.array.set( value ); - if ( attributes.normal !== undefined ) { + return this; - var normals = attributes.normal.array; + }, - for ( var i = 0, l = normals.length; i < l; i += 3 ) { + setX: function ( index, x ) { - renderList.pushNormal( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ); + this.array[ index * this.itemSize ] = x; - } + return this; - } + }, - if ( attributes.uv !== undefined ) { + setY: function ( index, y ) { - var uvs = attributes.uv.array; + this.array[ index * this.itemSize + 1 ] = y; - for ( var i = 0, l = uvs.length; i < l; i += 2 ) { + return this; - renderList.pushUv( uvs[ i ], uvs[ i + 1 ] ); + }, - } + setZ: function ( index, z ) { - } + this.array[ index * this.itemSize + 2 ] = z; - if ( attributes.index !== undefined ) { + return this; - var indices = attributes.index.array; + }, - if ( offsets.length > 0 ) { + setXY: function ( index, x, y ) { - for ( var o = 0; o < offsets.length; o ++ ) { + index *= this.itemSize; - var offset = offsets[ o ]; - var index = offset.index; + this.array[ index ] = x; + this.array[ index + 1 ] = y; - for ( var i = offset.start, l = offset.start + offset.count; i < l; i += 3 ) { + return this; - renderList.pushTriangle( indices[ i ] + index, indices[ i + 1 ] + index, indices[ i + 2 ] + index ); + }, - } + setXYZ: function ( index, x, y, z ) { - } + index *= this.itemSize; - } else { + this.array[ index ] = x; + this.array[ index + 1 ] = y; + this.array[ index + 2 ] = z; - for ( var i = 0, l = indices.length; i < l; i += 3 ) { + return this; - renderList.pushTriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] ); + }, - } + setXYZW: function ( index, x, y, z, w ) { - } + index *= this.itemSize; - } else { + this.array[ index ] = x; + this.array[ index + 1 ] = y; + this.array[ index + 2 ] = z; + this.array[ index + 3 ] = w; - for ( var i = 0, l = positions.length / 3; i < l; i += 3 ) { + return this; - renderList.pushTriangle( i, i + 1, i + 2 ); + }, - } + clone: function () { - } + return new THREE.BufferAttribute( new this.array.constructor( this.array ), this.itemSize ); - } else if ( geometry instanceof THREE.Geometry ) { + } - var vertices = geometry.vertices; - var faces = geometry.faces; - var faceVertexUvs = geometry.faceVertexUvs[ 0 ]; +}; - _normalMatrix.getNormalMatrix( _modelMatrix ); +// - var isFaceMaterial = object.material instanceof THREE.MeshFaceMaterial; - var objectMaterials = isFaceMaterial === true ? object.material : null; +THREE.Int8Attribute = function ( data, itemSize ) { - for ( var v = 0, vl = vertices.length; v < vl; v ++ ) { + console.warn( 'THREE.Int8Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); + return new THREE.BufferAttribute( data, itemSize ); - var vertex = vertices[ v ]; - renderList.pushVertex( vertex.x, vertex.y, vertex.z ); +}; - } +THREE.Uint8Attribute = function ( data, itemSize ) { - for ( var f = 0, fl = faces.length; f < fl; f ++ ) { + console.warn( 'THREE.Uint8Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); + return new THREE.BufferAttribute( data, itemSize ); - var face = faces[ f ]; +}; - var material = isFaceMaterial === true - ? objectMaterials.materials[ face.materialIndex ] - : object.material; +THREE.Uint8ClampedAttribute = function ( data, itemSize ) { - if ( material === undefined ) continue; + console.warn( 'THREE.Uint8ClampedAttribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); + return new THREE.BufferAttribute( data, itemSize ); - var side = material.side; - var v1 = _vertexPool[ face.a ]; - var v2 = _vertexPool[ face.b ]; - var v3 = _vertexPool[ face.c ]; +}; - if ( material.morphTargets === true ) { +THREE.Int16Attribute = function ( data, itemSize ) { - var morphTargets = geometry.morphTargets; - var morphInfluences = object.morphTargetInfluences; + console.warn( 'THREE.Int16Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); + return new THREE.BufferAttribute( data, itemSize ); - var v1p = v1.position; - var v2p = v2.position; - var v3p = v3.position; +}; - _vA.set( 0, 0, 0 ); - _vB.set( 0, 0, 0 ); - _vC.set( 0, 0, 0 ); +THREE.Uint16Attribute = function ( data, itemSize ) { - for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) { + console.warn( 'THREE.Uint16Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); + return new THREE.BufferAttribute( data, itemSize ); - var influence = morphInfluences[ t ]; +}; - if ( influence === 0 ) continue; +THREE.Int32Attribute = function ( data, itemSize ) { - var targets = morphTargets[ t ].vertices; + console.warn( 'THREE.Int32Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); + return new THREE.BufferAttribute( data, itemSize ); - _vA.x += ( targets[ face.a ].x - v1p.x ) * influence; - _vA.y += ( targets[ face.a ].y - v1p.y ) * influence; - _vA.z += ( targets[ face.a ].z - v1p.z ) * influence; +}; - _vB.x += ( targets[ face.b ].x - v2p.x ) * influence; - _vB.y += ( targets[ face.b ].y - v2p.y ) * influence; - _vB.z += ( targets[ face.b ].z - v2p.z ) * influence; +THREE.Uint32Attribute = function ( data, itemSize ) { - _vC.x += ( targets[ face.c ].x - v3p.x ) * influence; - _vC.y += ( targets[ face.c ].y - v3p.y ) * influence; - _vC.z += ( targets[ face.c ].z - v3p.z ) * influence; + console.warn( 'THREE.Uint32Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); + return new THREE.BufferAttribute( data, itemSize ); - } +}; - v1.position.add( _vA ); - v2.position.add( _vB ); - v3.position.add( _vC ); +THREE.Float32Attribute = function ( data, itemSize ) { - renderList.projectVertex( v1 ); - renderList.projectVertex( v2 ); - renderList.projectVertex( v3 ); + console.warn( 'THREE.Float32Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); + return new THREE.BufferAttribute( data, itemSize ); - } +}; - if ( renderList.checkTriangleVisibility( v1, v2, v3 ) === false ) continue; +THREE.Float64Attribute = function ( data, itemSize ) { - var visible = renderList.checkBackfaceCulling( v1, v2, v3 ); + console.warn( 'THREE.Float64Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); + return new THREE.BufferAttribute( data, itemSize ); - if ( side !== THREE.DoubleSide ) { - if ( side === THREE.FrontSide && visible === false ) continue; - if ( side === THREE.BackSide && visible === true ) continue; - } +}; - _face = getNextFaceInPool(); +// File:src/core/BufferGeometry.js - _face.id = object.id; - _face.v1.copy( v1 ); - _face.v2.copy( v2 ); - _face.v3.copy( v3 ); +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ - _face.normalModel.copy( face.normal ); +THREE.BufferGeometry = function () { - if ( visible === false && ( side === THREE.BackSide || side === THREE.DoubleSide ) ) { + Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } ); - _face.normalModel.negate(); + this.uuid = THREE.Math.generateUUID(); - } + this.name = ''; + this.type = 'BufferGeometry'; - _face.normalModel.applyMatrix3( _normalMatrix ).normalize(); + this.attributes = {}; + this.attributesKeys = []; - var faceVertexNormals = face.vertexNormals; + this.drawcalls = []; + this.offsets = this.drawcalls; // backwards compatibility - for ( var n = 0, nl = Math.min( faceVertexNormals.length, 3 ); n < nl; n ++ ) { + this.boundingBox = null; + this.boundingSphere = null; - var normalModel = _face.vertexNormalsModel[ n ]; - normalModel.copy( faceVertexNormals[ n ] ); +}; - if ( visible === false && ( side === THREE.BackSide || side === THREE.DoubleSide ) ) { +THREE.BufferGeometry.prototype = { - normalModel.negate(); + constructor: THREE.BufferGeometry, - } + addAttribute: function ( name, attribute ) { - normalModel.applyMatrix3( _normalMatrix ).normalize(); + if ( attribute instanceof THREE.BufferAttribute === false ) { - } + console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' ); - _face.vertexNormalsLength = faceVertexNormals.length; + this.attributes[ name ] = { array: arguments[ 1 ], itemSize: arguments[ 2 ] }; - var vertexUvs = faceVertexUvs[ f ]; + return; - if ( vertexUvs !== undefined ) { + } - for ( var u = 0; u < 3; u ++ ) { + this.attributes[ name ] = attribute; + this.attributesKeys = Object.keys( this.attributes ); - _face.uvs[ u ].copy( vertexUvs[ u ] ); + }, - } + getAttribute: function ( name ) { - } + return this.attributes[ name ]; - _face.color = face.color; - _face.material = material; + }, - _face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3; + addDrawCall: function ( start, count, indexOffset ) { - _renderData.elements.push( _face ); + this.drawcalls.push( { - } + start: start, + count: count, + index: indexOffset !== undefined ? indexOffset : 0 - } + } ); - } else if ( object instanceof THREE.Line ) { + }, - if ( geometry instanceof THREE.BufferGeometry ) { + applyMatrix: function ( matrix ) { - var attributes = geometry.attributes; + var position = this.attributes.position; - if ( attributes.position !== undefined ) { + if ( position !== undefined ) { - var positions = attributes.position.array; + matrix.applyToVector3Array( position.array ); + position.needsUpdate = true; - for ( var i = 0, l = positions.length; i < l; i += 3 ) { + } - renderList.pushVertex( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); + var normal = this.attributes.normal; - } + if ( normal !== undefined ) { - if ( attributes.index !== undefined ) { + var normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); - var indices = attributes.index.array; + normalMatrix.applyToVector3Array( normal.array ); + normal.needsUpdate = true; - for ( var i = 0, l = indices.length; i < l; i += 2 ) { + } - renderList.pushLine( indices[ i ], indices[ i + 1 ] ); + }, - } + center: function () { - } else { + // TODO - var step = object.type === THREE.LinePieces ? 2 : 1; + }, - for ( var i = 0, l = ( positions.length / 3 ) - 1; i < l; i += step ) { + fromGeometry: function ( geometry, settings ) { - renderList.pushLine( i, i + 1 ); + settings = settings || { 'vertexColors': THREE.NoColors }; - } + var vertices = geometry.vertices; + var faces = geometry.faces; + var faceVertexUvs = geometry.faceVertexUvs; + var vertexColors = settings.vertexColors; + var hasFaceVertexUv = faceVertexUvs[ 0 ].length > 0; + var hasFaceVertexNormals = faces[ 0 ].vertexNormals.length == 3; - } + var positions = new Float32Array( faces.length * 3 * 3 ); + this.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) ); - } + var normals = new Float32Array( faces.length * 3 * 3 ); + this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) ); - } else if ( geometry instanceof THREE.Geometry ) { + if ( vertexColors !== THREE.NoColors ) { - _modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix ); + var colors = new Float32Array( faces.length * 3 * 3 ); + this.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) ); - var vertices = object.geometry.vertices; + } - if ( vertices.length === 0 ) continue; + if ( hasFaceVertexUv === true ) { - v1 = getNextVertexInPool(); - v1.positionScreen.copy( vertices[ 0 ] ).applyMatrix4( _modelViewProjectionMatrix ); + var uvs = new Float32Array( faces.length * 3 * 2 ); + this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); - // Handle LineStrip and LinePieces - var step = object.type === THREE.LinePieces ? 2 : 1; + } - for ( var v = 1, vl = vertices.length; v < vl; v ++ ) { + for ( var i = 0, i2 = 0, i3 = 0; i < faces.length; i ++, i2 += 6, i3 += 9 ) { - v1 = getNextVertexInPool(); - v1.positionScreen.copy( vertices[ v ] ).applyMatrix4( _modelViewProjectionMatrix ); + var face = faces[ i ]; - if ( ( v + 1 ) % step > 0 ) continue; + var a = vertices[ face.a ]; + var b = vertices[ face.b ]; + var c = vertices[ face.c ]; - v2 = _vertexPool[ _vertexCount - 2 ]; + positions[ i3 ] = a.x; + positions[ i3 + 1 ] = a.y; + positions[ i3 + 2 ] = a.z; - _clippedVertex1PositionScreen.copy( v1.positionScreen ); - _clippedVertex2PositionScreen.copy( v2.positionScreen ); + positions[ i3 + 3 ] = b.x; + positions[ i3 + 4 ] = b.y; + positions[ i3 + 5 ] = b.z; - if ( clipLine( _clippedVertex1PositionScreen, _clippedVertex2PositionScreen ) === true ) { + positions[ i3 + 6 ] = c.x; + positions[ i3 + 7 ] = c.y; + positions[ i3 + 8 ] = c.z; - // Perform the perspective divide - _clippedVertex1PositionScreen.multiplyScalar( 1 / _clippedVertex1PositionScreen.w ); - _clippedVertex2PositionScreen.multiplyScalar( 1 / _clippedVertex2PositionScreen.w ); + if ( hasFaceVertexNormals === true ) { - _line = getNextLineInPool(); + var na = face.vertexNormals[ 0 ]; + var nb = face.vertexNormals[ 1 ]; + var nc = face.vertexNormals[ 2 ]; - _line.id = object.id; - _line.v1.positionScreen.copy( _clippedVertex1PositionScreen ); - _line.v2.positionScreen.copy( _clippedVertex2PositionScreen ); + normals[ i3 ] = na.x; + normals[ i3 + 1 ] = na.y; + normals[ i3 + 2 ] = na.z; - _line.z = Math.max( _clippedVertex1PositionScreen.z, _clippedVertex2PositionScreen.z ); + normals[ i3 + 3 ] = nb.x; + normals[ i3 + 4 ] = nb.y; + normals[ i3 + 5 ] = nb.z; - _line.material = object.material; + normals[ i3 + 6 ] = nc.x; + normals[ i3 + 7 ] = nc.y; + normals[ i3 + 8 ] = nc.z; - if ( object.material.vertexColors === THREE.VertexColors ) { + } else { - _line.vertexColors[ 0 ].copy( object.geometry.colors[ v ] ); - _line.vertexColors[ 1 ].copy( object.geometry.colors[ v - 1 ] ); + var n = face.normal; - } + normals[ i3 ] = n.x; + normals[ i3 + 1 ] = n.y; + normals[ i3 + 2 ] = n.z; - _renderData.elements.push( _line ); + normals[ i3 + 3 ] = n.x; + normals[ i3 + 4 ] = n.y; + normals[ i3 + 5 ] = n.z; - } + normals[ i3 + 6 ] = n.x; + normals[ i3 + 7 ] = n.y; + normals[ i3 + 8 ] = n.z; - } + } - } + if ( vertexColors === THREE.FaceColors ) { - } else if ( object instanceof THREE.Sprite ) { + var fc = face.color; - _vector4.set( _modelMatrix.elements[ 12 ], _modelMatrix.elements[ 13 ], _modelMatrix.elements[ 14 ], 1 ); - _vector4.applyMatrix4( _viewProjectionMatrix ); + colors[ i3 ] = fc.r; + colors[ i3 + 1 ] = fc.g; + colors[ i3 + 2 ] = fc.b; - var invW = 1 / _vector4.w; + colors[ i3 + 3 ] = fc.r; + colors[ i3 + 4 ] = fc.g; + colors[ i3 + 5 ] = fc.b; - _vector4.z *= invW; + colors[ i3 + 6 ] = fc.r; + colors[ i3 + 7 ] = fc.g; + colors[ i3 + 8 ] = fc.b; - if ( _vector4.z >= - 1 && _vector4.z <= 1 ) { + } else if ( vertexColors === THREE.VertexColors ) { - _sprite = getNextSpriteInPool(); - _sprite.id = object.id; - _sprite.x = _vector4.x * invW; - _sprite.y = _vector4.y * invW; - _sprite.z = _vector4.z; - _sprite.object = object; + var vca = face.vertexColors[ 0 ]; + var vcb = face.vertexColors[ 1 ]; + var vcc = face.vertexColors[ 2 ]; - _sprite.rotation = object.rotation; + colors[ i3 ] = vca.r; + colors[ i3 + 1 ] = vca.g; + colors[ i3 + 2 ] = vca.b; - _sprite.scale.x = object.scale.x * Math.abs( _sprite.x - ( _vector4.x + camera.projectionMatrix.elements[ 0 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 12 ] ) ); - _sprite.scale.y = object.scale.y * Math.abs( _sprite.y - ( _vector4.y + camera.projectionMatrix.elements[ 5 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 13 ] ) ); + colors[ i3 + 3 ] = vcb.r; + colors[ i3 + 4 ] = vcb.g; + colors[ i3 + 5 ] = vcb.b; - _sprite.material = object.material; + colors[ i3 + 6 ] = vcc.r; + colors[ i3 + 7 ] = vcc.g; + colors[ i3 + 8 ] = vcc.b; - _renderData.elements.push( _sprite ); + } - } + if ( hasFaceVertexUv === true ) { - } + var uva = faceVertexUvs[ 0 ][ i ][ 0 ]; + var uvb = faceVertexUvs[ 0 ][ i ][ 1 ]; + var uvc = faceVertexUvs[ 0 ][ i ][ 2 ]; - } + uvs[ i2 ] = uva.x; + uvs[ i2 + 1 ] = uva.y; - if ( sortElements === true ) _renderData.elements.sort( painterSort ); + uvs[ i2 + 2 ] = uvb.x; + uvs[ i2 + 3 ] = uvb.y; - return _renderData; + uvs[ i2 + 4 ] = uvc.x; + uvs[ i2 + 5 ] = uvc.y; - }; + } - // Pools + } - function getNextObjectInPool() { + this.computeBoundingSphere() - if ( _objectCount === _objectPoolLength ) { + return this; - var object = new THREE.RenderableObject(); - _objectPool.push( object ); - _objectPoolLength ++; - _objectCount ++; - return object; + }, - } + computeBoundingBox: function () { - return _objectPool[ _objectCount ++ ]; + var vector = new THREE.Vector3(); - } + return function () { - function getNextVertexInPool() { + if ( this.boundingBox === null ) { - if ( _vertexCount === _vertexPoolLength ) { + this.boundingBox = new THREE.Box3(); - var vertex = new THREE.RenderableVertex(); - _vertexPool.push( vertex ); - _vertexPoolLength ++; - _vertexCount ++; - return vertex; + } - } + var positions = this.attributes.position.array; - return _vertexPool[ _vertexCount ++ ]; + if ( positions ) { - } + var bb = this.boundingBox; + bb.makeEmpty(); - function getNextFaceInPool() { + for ( var i = 0, il = positions.length; i < il; i += 3 ) { - if ( _faceCount === _facePoolLength ) { + vector.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); + bb.expandByPoint( vector ); - var face = new THREE.RenderableFace(); - _facePool.push( face ); - _facePoolLength ++; - _faceCount ++; - return face; + } - } + } - return _facePool[ _faceCount ++ ]; + if ( positions === undefined || positions.length === 0 ) { + this.boundingBox.min.set( 0, 0, 0 ); + this.boundingBox.max.set( 0, 0, 0 ); - } + } - function getNextLineInPool() { + if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { - if ( _lineCount === _linePoolLength ) { + console.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.' ); - var line = new THREE.RenderableLine(); - _linePool.push( line ); - _linePoolLength ++; - _lineCount ++ - return line; + } } - return _linePool[ _lineCount ++ ]; + }(), - } + computeBoundingSphere: function () { - function getNextSpriteInPool() { + var box = new THREE.Box3(); + var vector = new THREE.Vector3(); - if ( _spriteCount === _spritePoolLength ) { + return function () { - var sprite = new THREE.RenderableSprite(); - _spritePool.push( sprite ); - _spritePoolLength ++; - _spriteCount ++ - return sprite; + if ( this.boundingSphere === null ) { - } + this.boundingSphere = new THREE.Sphere(); - return _spritePool[ _spriteCount ++ ]; + } - } + var positions = this.attributes.position.array; - // + if ( positions ) { - function painterSort( a, b ) { + box.makeEmpty(); - if ( a.z !== b.z ) { + var center = this.boundingSphere.center; - return b.z - a.z; + for ( var i = 0, il = positions.length; i < il; i += 3 ) { - } else if ( a.id !== b.id ) { + vector.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); + box.expandByPoint( vector ); - return a.id - b.id; + } - } else { + box.center( center ); - return 0; + // hoping to find a boundingSphere with a radius smaller than the + // boundingSphere of the boundingBox: sqrt(3) smaller in the best case - } + var maxRadiusSq = 0; - } + for ( var i = 0, il = positions.length; i < il; i += 3 ) { - function clipLine( s1, s2 ) { + vector.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); + maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) ); - var alpha1 = 0, alpha2 = 1, + } - // Calculate the boundary coordinate of each vertex for the near and far clip planes, - // Z = -1 and Z = +1, respectively. - bc1near = s1.z + s1.w, - bc2near = s2.z + s2.w, - bc1far = - s1.z + s1.w, - bc2far = - s2.z + s2.w; + this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); - if ( bc1near >= 0 && bc2near >= 0 && bc1far >= 0 && bc2far >= 0 ) { + if ( isNaN( this.boundingSphere.radius ) ) { - // Both vertices lie entirely within all clip planes. - return true; + console.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.' ); - } else if ( ( bc1near < 0 && bc2near < 0 ) || ( bc1far < 0 && bc2far < 0 ) ) { + } - // Both vertices lie entirely outside one of the clip planes. - return false; + } - } else { + } - // The line segment spans at least one clip plane. + }(), - if ( bc1near < 0 ) { + computeFaceNormals: function () { - // v1 lies outside the near plane, v2 inside - alpha1 = Math.max( alpha1, bc1near / ( bc1near - bc2near ) ); + // backwards compatibility - } else if ( bc2near < 0 ) { + }, - // v2 lies outside the near plane, v1 inside - alpha2 = Math.min( alpha2, bc1near / ( bc1near - bc2near ) ); + computeVertexNormals: function () { - } + var attributes = this.attributes; - if ( bc1far < 0 ) { + if ( attributes.position ) { - // v1 lies outside the far plane, v2 inside - alpha1 = Math.max( alpha1, bc1far / ( bc1far - bc2far ) ); + var positions = attributes.position.array; - } else if ( bc2far < 0 ) { + if ( attributes.normal === undefined ) { - // v2 lies outside the far plane, v2 inside - alpha2 = Math.min( alpha2, bc1far / ( bc1far - bc2far ) ); + this.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( positions.length ), 3 ) ); - } + } else { - if ( alpha2 < alpha1 ) { + // reset existing normals to zero - // The line segment spans two boundaries, but is outside both of them. - // (This can't happen when we're only clipping against just near/far but good - // to leave the check here for future usage if other clip planes are added.) - return false; + var normals = attributes.normal.array; - } else { + for ( var i = 0, il = normals.length; i < il; i ++ ) { - // Update the s1 and s2 vertices to match the clipped line segment. - s1.lerp( s2, alpha1 ); - s2.lerp( s1, 1 - alpha2 ); + normals[ i ] = 0; - return true; + } } - } + var normals = attributes.normal.array; - } + var vA, vB, vC, -}; + pA = new THREE.Vector3(), + pB = new THREE.Vector3(), + pC = new THREE.Vector3(), -// File:src/core/Face3.js - -/** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ + cb = new THREE.Vector3(), + ab = new THREE.Vector3(); -THREE.Face3 = function ( a, b, c, normal, color, materialIndex ) { + // indexed elements - this.a = a; - this.b = b; - this.c = c; + if ( attributes.index ) { - this.normal = normal instanceof THREE.Vector3 ? normal : new THREE.Vector3(); - this.vertexNormals = normal instanceof Array ? normal : []; + var indices = attributes.index.array; - this.color = color instanceof THREE.Color ? color : new THREE.Color(); - this.vertexColors = color instanceof Array ? color : []; + var offsets = ( this.offsets.length > 0 ? this.offsets : [ { start: 0, count: indices.length, index: 0 } ] ); - this.vertexTangents = []; + for ( var j = 0, jl = offsets.length; j < jl; ++ j ) { - this.materialIndex = materialIndex !== undefined ? materialIndex : 0; + var start = offsets[ j ].start; + var count = offsets[ j ].count; + var index = offsets[ j ].index; -}; + for ( var i = start, il = start + count; i < il; i += 3 ) { -THREE.Face3.prototype = { + vA = ( index + indices[ i ] ) * 3; + vB = ( index + indices[ i + 1 ] ) * 3; + vC = ( index + indices[ i + 2 ] ) * 3; - constructor: THREE.Face3, + pA.fromArray( positions, vA ); + pB.fromArray( positions, vB ); + pC.fromArray( positions, vC ); - clone: function () { + cb.subVectors( pC, pB ); + ab.subVectors( pA, pB ); + cb.cross( ab ); - var face = new THREE.Face3( this.a, this.b, this.c ); + normals[ vA ] += cb.x; + normals[ vA + 1 ] += cb.y; + normals[ vA + 2 ] += cb.z; - face.normal.copy( this.normal ); - face.color.copy( this.color ); + normals[ vB ] += cb.x; + normals[ vB + 1 ] += cb.y; + normals[ vB + 2 ] += cb.z; - face.materialIndex = this.materialIndex; + normals[ vC ] += cb.x; + normals[ vC + 1 ] += cb.y; + normals[ vC + 2 ] += cb.z; - for ( var i = 0, il = this.vertexNormals.length; i < il; i ++ ) { + } - face.vertexNormals[ i ] = this.vertexNormals[ i ].clone(); + } - } + } else { - for ( var i = 0, il = this.vertexColors.length; i < il; i ++ ) { + // non-indexed elements (unconnected triangle soup) - face.vertexColors[ i ] = this.vertexColors[ i ].clone(); + for ( var i = 0, il = positions.length; i < il; i += 9 ) { - } + pA.fromArray( positions, i ); + pB.fromArray( positions, i + 3 ); + pC.fromArray( positions, i + 6 ); - for ( var i = 0, il = this.vertexTangents.length; i < il; i ++ ) { + cb.subVectors( pC, pB ); + ab.subVectors( pA, pB ); + cb.cross( ab ); - face.vertexTangents[ i ] = this.vertexTangents[ i ].clone(); + normals[ i ] = cb.x; + normals[ i + 1 ] = cb.y; + normals[ i + 2 ] = cb.z; - } + normals[ i + 3 ] = cb.x; + normals[ i + 4 ] = cb.y; + normals[ i + 5 ] = cb.z; - return face; + normals[ i + 6 ] = cb.x; + normals[ i + 7 ] = cb.y; + normals[ i + 8 ] = cb.z; - } + } -}; + } -// File:src/core/Face4.js + this.normalizeNormals(); -/** - * @author mrdoob / http://mrdoob.com/ - */ + attributes.normal.needsUpdate = true; -THREE.Face4 = function ( a, b, c, d, normal, color, materialIndex ) { + } - console.warn( 'THREE.Face4 has been removed. A THREE.Face3 will be created instead.' ) - return new THREE.Face3( a, b, c, normal, color, materialIndex ); + }, -}; + computeTangents: function () { -// File:src/core/BufferAttribute.js + // based on http://www.terathon.com/code/tangent.html + // (per vertex tangents) -/** - * @author mrdoob / http://mrdoob.com/ - */ + if ( this.attributes.index === undefined || + this.attributes.position === undefined || + this.attributes.normal === undefined || + this.attributes.uv === undefined ) { -THREE.BufferAttribute = function ( array, itemSize ) { + console.warn( 'Missing required attributes (index, position, normal or uv) in BufferGeometry.computeTangents()' ); + return; - this.array = array; - this.itemSize = itemSize; + } -}; + var indices = this.attributes.index.array; + var positions = this.attributes.position.array; + var normals = this.attributes.normal.array; + var uvs = this.attributes.uv.array; -THREE.BufferAttribute.prototype = { + var nVertices = positions.length / 3; - constructor: THREE.BufferAttribute, + if ( this.attributes.tangent === undefined ) { - get length () { + this.addAttribute( 'tangent', new THREE.BufferAttribute( new Float32Array( 4 * nVertices ), 4 ) ); - return this.array.length; + } - }, + var tangents = this.attributes.tangent.array; - set: function ( value ) { + var tan1 = [], tan2 = []; - this.array.set( value ); + for ( var k = 0; k < nVertices; k ++ ) { - return this; + tan1[ k ] = new THREE.Vector3(); + tan2[ k ] = new THREE.Vector3(); - }, + } - setX: function ( index, x ) { + var vA = new THREE.Vector3(), + vB = new THREE.Vector3(), + vC = new THREE.Vector3(), - this.array[ index * this.itemSize ] = x; + uvA = new THREE.Vector2(), + uvB = new THREE.Vector2(), + uvC = new THREE.Vector2(), - return this; + x1, x2, y1, y2, z1, z2, + s1, s2, t1, t2, r; - }, + var sdir = new THREE.Vector3(), tdir = new THREE.Vector3(); - setY: function ( index, y ) { + function handleTriangle( a, b, c ) { - this.array[ index * this.itemSize + 1 ] = y; + vA.fromArray( positions, a * 3 ); + vB.fromArray( positions, b * 3 ); + vC.fromArray( positions, c * 3 ); - return this; + uvA.fromArray( uvs, a * 2 ); + uvB.fromArray( uvs, b * 2 ); + uvC.fromArray( uvs, c * 2 ); - }, + x1 = vB.x - vA.x; + x2 = vC.x - vA.x; - setZ: function ( index, z ) { + y1 = vB.y - vA.y; + y2 = vC.y - vA.y; - this.array[ index * this.itemSize + 2 ] = z; + z1 = vB.z - vA.z; + z2 = vC.z - vA.z; - return this; + s1 = uvB.x - uvA.x; + s2 = uvC.x - uvA.x; - }, + t1 = uvB.y - uvA.y; + t2 = uvC.y - uvA.y; - setXY: function ( index, x, y ) { + r = 1.0 / ( s1 * t2 - s2 * t1 ); - index *= this.itemSize; + sdir.set( + ( t2 * x1 - t1 * x2 ) * r, + ( t2 * y1 - t1 * y2 ) * r, + ( t2 * z1 - t1 * z2 ) * r + ); - this.array[ index ] = x; - this.array[ index + 1 ] = y; + tdir.set( + ( s1 * x2 - s2 * x1 ) * r, + ( s1 * y2 - s2 * y1 ) * r, + ( s1 * z2 - s2 * z1 ) * r + ); - return this; + tan1[ a ].add( sdir ); + tan1[ b ].add( sdir ); + tan1[ c ].add( sdir ); - }, + tan2[ a ].add( tdir ); + tan2[ b ].add( tdir ); + tan2[ c ].add( tdir ); - setXYZ: function ( index, x, y, z ) { + } - index *= this.itemSize; + var i, il; + var j, jl; + var iA, iB, iC; - this.array[ index ] = x; - this.array[ index + 1 ] = y; - this.array[ index + 2 ] = z; + if ( this.drawcalls.length === 0 ) { - return this; + this.addDrawCall( 0, indices.length, 0 ); - }, + } - setXYZW: function ( index, x, y, z, w ) { + var drawcalls = this.drawcalls; - index *= this.itemSize; + for ( j = 0, jl = drawcalls.length; j < jl; ++ j ) { - this.array[ index ] = x; - this.array[ index + 1 ] = y; - this.array[ index + 2 ] = z; - this.array[ index + 3 ] = w; + var start = drawcalls[ j ].start; + var count = drawcalls[ j ].count; + var index = drawcalls[ j ].index; - return this; + for ( i = start, il = start + count; i < il; i += 3 ) { - } + iA = index + indices[ i ]; + iB = index + indices[ i + 1 ]; + iC = index + indices[ i + 2 ]; -}; + handleTriangle( iA, iB, iC ); -// + } -THREE.Int8Attribute = function ( data, itemSize ) { + } - console.warn( 'THREE.Int8Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); - return new THREE.BufferAttribute( data, itemSize ); + var tmp = new THREE.Vector3(), tmp2 = new THREE.Vector3(); + var n = new THREE.Vector3(), n2 = new THREE.Vector3(); + var w, t, test; -}; + function handleVertex( v ) { -THREE.Uint8Attribute = function ( data, itemSize ) { + n.fromArray( normals, v * 3 ); + n2.copy( n ); - console.warn( 'THREE.Uint8Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); - return new THREE.BufferAttribute( data, itemSize ); + t = tan1[ v ]; -}; + // Gram-Schmidt orthogonalize -THREE.Uint8ClampedAttribute = function ( data, itemSize ) { + tmp.copy( t ); + tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize(); - console.warn( 'THREE.Uint8ClampedAttribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); - return new THREE.BufferAttribute( data, itemSize ); + // Calculate handedness + tmp2.crossVectors( n2, t ); + test = tmp2.dot( tan2[ v ] ); + w = ( test < 0.0 ) ? - 1.0 : 1.0; -}; + tangents[ v * 4 ] = tmp.x; + tangents[ v * 4 + 1 ] = tmp.y; + tangents[ v * 4 + 2 ] = tmp.z; + tangents[ v * 4 + 3 ] = w; -THREE.Int16Attribute = function ( data, itemSize ) { + } - console.warn( 'THREE.Int16Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); - return new THREE.BufferAttribute( data, itemSize ); + for ( j = 0, jl = drawcalls.length; j < jl; ++ j ) { -}; + var start = drawcalls[ j ].start; + var count = drawcalls[ j ].count; + var index = drawcalls[ j ].index; -THREE.Uint16Attribute = function ( data, itemSize ) { + for ( i = start, il = start + count; i < il; i += 3 ) { - console.warn( 'THREE.Uint16Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); - return new THREE.BufferAttribute( data, itemSize ); + iA = index + indices[ i ]; + iB = index + indices[ i + 1 ]; + iC = index + indices[ i + 2 ]; -}; + handleVertex( iA ); + handleVertex( iB ); + handleVertex( iC ); -THREE.Int32Attribute = function ( data, itemSize ) { + } - console.warn( 'THREE.Int32Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); - return new THREE.BufferAttribute( data, itemSize ); + } -}; + }, -THREE.Uint32Attribute = function ( data, itemSize ) { + /* + computeOffsets + Compute the draw offset for large models by chunking the index buffer into chunks of 65k addressable vertices. + This method will effectively rewrite the index buffer and remap all attributes to match the new indices. + WARNING: This method will also expand the vertex count to prevent sprawled triangles across draw offsets. + indexBufferSize - Defaults to 65535, but allows for larger or smaller chunks. + */ + computeOffsets: function ( indexBufferSize ) { - console.warn( 'THREE.Uint32Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); - return new THREE.BufferAttribute( data, itemSize ); + var size = indexBufferSize; + if ( indexBufferSize === undefined ) + size = 65535; //WebGL limits type of index buffer values to 16-bit. -}; + var s = Date.now(); -THREE.Float32Attribute = function ( data, itemSize ) { + var indices = this.attributes.index.array; + var vertices = this.attributes.position.array; - console.warn( 'THREE.Float32Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); - return new THREE.BufferAttribute( data, itemSize ); + var verticesCount = ( vertices.length / 3 ); + var facesCount = ( indices.length / 3 ); -}; + /* + console.log("Computing buffers in offsets of "+size+" -> indices:"+indices.length+" vertices:"+vertices.length); + console.log("Faces to process: "+(indices.length/3)); + console.log("Reordering "+verticesCount+" vertices."); + */ -THREE.Float64Attribute = function ( data, itemSize ) { + var sortedIndices = new Uint16Array( indices.length ); //16-bit buffers + var indexPtr = 0; + var vertexPtr = 0; - console.warn( 'THREE.Float64Attribute has been removed. Use THREE.BufferAttribute( array, itemSize ) instead.' ); - return new THREE.BufferAttribute( data, itemSize ); + var offsets = [ { start:0, count:0, index:0 } ]; + var offset = offsets[ 0 ]; -}; + var duplicatedVertices = 0; + var newVerticeMaps = 0; + var faceVertices = new Int32Array( 6 ); + var vertexMap = new Int32Array( vertices.length ); + var revVertexMap = new Int32Array( vertices.length ); + for ( var j = 0; j < vertices.length; j ++ ) { vertexMap[ j ] = - 1; revVertexMap[ j ] = - 1; } -// File:src/core/BufferGeometry.js + /* + Traverse every face and reorder vertices in the proper offsets of 65k. + We can have more than 65k entries in the index buffer per offset, but only reference 65k values. + */ + for ( var findex = 0; findex < facesCount; findex ++ ) { + newVerticeMaps = 0; -/** - * @author alteredq / http://alteredqualia.com/ - */ + for ( var vo = 0; vo < 3; vo ++ ) { + var vid = indices[ findex * 3 + vo ]; + if ( vertexMap[ vid ] == - 1 ) { + //Unmapped vertice + faceVertices[ vo * 2 ] = vid; + faceVertices[ vo * 2 + 1 ] = - 1; + newVerticeMaps ++; + } else if ( vertexMap[ vid ] < offset.index ) { + //Reused vertices from previous block (duplicate) + faceVertices[ vo * 2 ] = vid; + faceVertices[ vo * 2 + 1 ] = - 1; + duplicatedVertices ++; + } else { + //Reused vertice in the current block + faceVertices[ vo * 2 ] = vid; + faceVertices[ vo * 2 + 1 ] = vertexMap[ vid ]; + } + } -THREE.BufferGeometry = function () { + var faceMax = vertexPtr + newVerticeMaps; + if ( faceMax > ( offset.index + size ) ) { + var new_offset = { start:indexPtr, count:0, index:vertexPtr }; + offsets.push( new_offset ); + offset = new_offset; - this.id = THREE.GeometryIdCount ++; - this.uuid = THREE.Math.generateUUID(); + //Re-evaluate reused vertices in light of new offset. + for ( var v = 0; v < 6; v += 2 ) { + var new_vid = faceVertices[ v + 1 ]; + if ( new_vid > - 1 && new_vid < offset.index ) + faceVertices[ v + 1 ] = - 1; + } + } - this.name = ''; + //Reindex the face. + for ( var v = 0; v < 6; v += 2 ) { + var vid = faceVertices[ v ]; + var new_vid = faceVertices[ v + 1 ]; - this.attributes = {}; - this.drawcalls = []; - this.offsets = this.drawcalls; // backwards compatibility + if ( new_vid === - 1 ) + new_vid = vertexPtr ++; - this.boundingBox = null; - this.boundingSphere = null; + vertexMap[ vid ] = new_vid; + revVertexMap[ new_vid ] = vid; + sortedIndices[ indexPtr ++ ] = new_vid - offset.index; //XXX overflows at 16bit + offset.count ++; + } + } -}; + /* Move all attribute values to map to the new computed indices , also expand the vertice stack to match our new vertexPtr. */ + this.reorderBuffers( sortedIndices, revVertexMap, vertexPtr ); + this.offsets = offsets; -THREE.BufferGeometry.prototype = { + /* + var orderTime = Date.now(); + console.log("Reorder time: "+(orderTime-s)+"ms"); + console.log("Duplicated "+duplicatedVertices+" vertices."); + console.log("Compute Buffers time: "+(Date.now()-s)+"ms"); + console.log("Draw offsets: "+offsets.length); + */ - constructor: THREE.BufferGeometry, + return offsets; + }, - addAttribute: function ( name, attribute ) { + merge: function () { - if ( attribute instanceof THREE.BufferAttribute === false ) { + console.log( 'BufferGeometry.merge(): TODO' ); - console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' ); + }, - this.attributes[ name ] = { array: arguments[ 1 ], itemSize: arguments[ 2 ] }; + normalizeNormals: function () { - return; + var normals = this.attributes.normal.array; - } + var x, y, z, n; - this.attributes[ name ] = attribute; + for ( var i = 0, il = normals.length; i < il; i += 3 ) { - }, + x = normals[ i ]; + y = normals[ i + 1 ]; + z = normals[ i + 2 ]; - getAttribute: function ( name ) { + n = 1.0 / Math.sqrt( x * x + y * y + z * z ); - return this.attributes[ name ]; + normals[ i ] *= n; + normals[ i + 1 ] *= n; + normals[ i + 2 ] *= n; - }, + } - addDrawCall: function ( start, count, indexOffset ) { + }, - this.drawcalls.push( { + /* + reoderBuffers: + Reorder attributes based on a new indexBuffer and indexMap. + indexBuffer - Uint16Array of the new ordered indices. + indexMap - Int32Array where the position is the new vertex ID and the value the old vertex ID for each vertex. + vertexCount - Amount of total vertices considered in this reordering (in case you want to grow the vertice stack). + */ + reorderBuffers: function ( indexBuffer, indexMap, vertexCount ) { - start: start, - count: count, - index: indexOffset !== undefined ? indexOffset : 0 + /* Create a copy of all attributes for reordering. */ + var sortedAttributes = {}; + for ( var attr in this.attributes ) { + if ( attr == 'index' ) + continue; + var sourceArray = this.attributes[ attr ].array; + sortedAttributes[ attr ] = new sourceArray.constructor( this.attributes[ attr ].itemSize * vertexCount ); + } - } ); + /* Move attribute positions based on the new index map */ + for ( var new_vid = 0; new_vid < vertexCount; new_vid ++ ) { + var vid = indexMap[ new_vid ]; + for ( var attr in this.attributes ) { + if ( attr == 'index' ) + continue; + var attrArray = this.attributes[ attr ].array; + var attrSize = this.attributes[ attr ].itemSize; + var sortedAttr = sortedAttributes[ attr ]; + for ( var k = 0; k < attrSize; k ++ ) + sortedAttr[ new_vid * attrSize + k ] = attrArray[ vid * attrSize + k ]; + } + } + /* Carry the new sorted buffers locally */ + this.attributes[ 'index' ].array = indexBuffer; + for ( var attr in this.attributes ) { + if ( attr == 'index' ) + continue; + this.attributes[ attr ].array = sortedAttributes[ attr ]; + this.attributes[ attr ].numItems = this.attributes[ attr ].itemSize * vertexCount; + } }, - applyMatrix: function ( matrix ) { + toJSON: function () { - var position = this.attributes.position; + var output = { + metadata: { + version: 4.0, + type: 'BufferGeometry', + generator: 'BufferGeometryExporter' + }, + uuid: this.uuid, + type: this.type, + data: { + attributes: {} + } + }; - if ( position !== undefined ) { + var attributes = this.attributes; + var offsets = this.offsets; + var boundingSphere = this.boundingSphere; - matrix.applyToVector3Array( position.array ); - position.needsUpdate = true; + for ( var key in attributes ) { - } + var attribute = attributes[ key ]; - var normal = this.attributes.normal; + var array = [], typeArray = attribute.array; - if ( normal !== undefined ) { + for ( var i = 0, l = typeArray.length; i < l; i ++ ) { - var normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); + array[ i ] = typeArray[ i ]; - normalMatrix.applyToVector3Array( normal.array ); - normal.needsUpdate = true; + } + + output.data.attributes[ key ] = { + itemSize: attribute.itemSize, + type: attribute.array.constructor.name, + array: array + } } - }, + if ( offsets.length > 0 ) { - fromGeometry: function ( geometry, settings ) { + output.data.offsets = JSON.parse( JSON.stringify( offsets ) ); - settings = settings || { 'vertexColors': THREE.NoColors }; + } - var vertices = geometry.vertices; - var faces = geometry.faces; - var faceVertexUvs = geometry.faceVertexUvs; - var vertexColors = settings.vertexColors; - var hasFaceVertexUv = faceVertexUvs[ 0 ].length > 0; - var hasFaceVertexNormals = faces[ 0 ].vertexNormals.length == 3; + if ( boundingSphere !== null ) { - var positions = new Float32Array( faces.length * 3 * 3 ); - this.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) ); + output.data.boundingSphere = { + center: boundingSphere.center.toArray(), + radius: boundingSphere.radius + } - var normals = new Float32Array( faces.length * 3 * 3 ); - this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) ); + } - if ( vertexColors !== THREE.NoColors ) { + return output; - var colors = new Float32Array( faces.length * 3 * 3 ); - this.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) ); + }, - } + clone: function () { - if ( hasFaceVertexUv === true ) { + var geometry = new THREE.BufferGeometry(); - var uvs = new Float32Array( faces.length * 3 * 2 ); - this.addAttribute( 'uvs', new THREE.BufferAttribute( uvs, 2 ) ); + for ( var attr in this.attributes ) { + + var sourceAttr = this.attributes[ attr ]; + geometry.addAttribute( attr, sourceAttr.clone() ); } - for ( var i = 0, i2 = 0, i3 = 0; i < faces.length; i ++, i2 += 6, i3 += 9 ) { + for ( var i = 0, il = this.offsets.length; i < il; i ++ ) { - var face = faces[ i ]; + var offset = this.offsets[ i ]; - var a = vertices[ face.a ]; - var b = vertices[ face.b ]; - var c = vertices[ face.c ]; + geometry.offsets.push( { - positions[ i3 ] = a.x; - positions[ i3 + 1 ] = a.y; - positions[ i3 + 2 ] = a.z; + start: offset.start, + index: offset.index, + count: offset.count - positions[ i3 + 3 ] = b.x; - positions[ i3 + 4 ] = b.y; - positions[ i3 + 5 ] = b.z; + } ); - positions[ i3 + 6 ] = c.x; - positions[ i3 + 7 ] = c.y; - positions[ i3 + 8 ] = c.z; + } - if ( hasFaceVertexNormals === true ) { + return geometry; - var na = face.vertexNormals[ 0 ]; - var nb = face.vertexNormals[ 1 ]; - var nc = face.vertexNormals[ 2 ]; + }, - normals[ i3 ] = na.x; - normals[ i3 + 1 ] = na.y; - normals[ i3 + 2 ] = na.z; + dispose: function () { - normals[ i3 + 3 ] = nb.x; - normals[ i3 + 4 ] = nb.y; - normals[ i3 + 5 ] = nb.z; + this.dispatchEvent( { type: 'dispose' } ); - normals[ i3 + 6 ] = nc.x; - normals[ i3 + 7 ] = nc.y; - normals[ i3 + 8 ] = nc.z; + } - } else { +}; - var n = face.normal; +THREE.EventDispatcher.prototype.apply( THREE.BufferGeometry.prototype ); - normals[ i3 ] = n.x; - normals[ i3 + 1 ] = n.y; - normals[ i3 + 2 ] = n.z; - - normals[ i3 + 3 ] = n.x; - normals[ i3 + 4 ] = n.y; - normals[ i3 + 5 ] = n.z; - - normals[ i3 + 6 ] = n.x; - normals[ i3 + 7 ] = n.y; - normals[ i3 + 8 ] = n.z; - - } +// File:src/core/Geometry.js - if ( vertexColors === THREE.FaceColors ) { +/** + * @author mrdoob / http://mrdoob.com/ + * @author kile / http://kile.stravaganza.org/ + * @author alteredq / http://alteredqualia.com/ + * @author mikael emtinger / http://gomo.se/ + * @author zz85 / http://www.lab4games.net/zz85/blog + * @author bhouston / http://exocortex.com + */ - var fc = face.color; +THREE.Geometry = function () { - colors[ i3 ] = fc.r; - colors[ i3 + 1 ] = fc.g; - colors[ i3 + 2 ] = fc.b; + Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } ); - colors[ i3 + 3 ] = fc.r; - colors[ i3 + 4 ] = fc.g; - colors[ i3 + 5 ] = fc.b; + this.uuid = THREE.Math.generateUUID(); - colors[ i3 + 6 ] = fc.r; - colors[ i3 + 7 ] = fc.g; - colors[ i3 + 8 ] = fc.b; + this.name = ''; + this.type = 'Geometry'; - } else if ( vertexColors === THREE.VertexColors ) { + this.vertices = []; + this.colors = []; // one-to-one vertex colors, used in Points and Line - var vca = face.vertexColors[ 0 ]; - var vcb = face.vertexColors[ 1 ]; - var vcc = face.vertexColors[ 2 ]; + this.faces = []; - colors[ i3 ] = vca.r; - colors[ i3 + 1 ] = vca.g; - colors[ i3 + 2 ] = vca.b; + this.faceVertexUvs = [ [] ]; - colors[ i3 + 3 ] = vcb.r; - colors[ i3 + 4 ] = vcb.g; - colors[ i3 + 5 ] = vcb.b; + this.morphTargets = []; + this.morphColors = []; + this.morphNormals = []; - colors[ i3 + 6 ] = vcc.r; - colors[ i3 + 7 ] = vcc.g; - colors[ i3 + 8 ] = vcc.b; + this.skinWeights = []; + this.skinIndices = []; - } + this.lineDistances = []; - if ( hasFaceVertexUv === true ) { + this.boundingBox = null; + this.boundingSphere = null; - var uva = faceVertexUvs[ 0 ][ i ][ 0 ]; - var uvb = faceVertexUvs[ 0 ][ i ][ 1 ]; - var uvc = faceVertexUvs[ 0 ][ i ][ 2 ]; + this.hasTangents = false; - uvs[ i2 ] = uva.x; - uvs[ i2 + 1 ] = uva.y; + this.dynamic = true; // the intermediate typed arrays will be deleted when set to false - uvs[ i2 + 2 ] = uvb.x; - uvs[ i2 + 3 ] = uvb.y; + // update flags - uvs[ i2 + 4 ] = uvc.x; - uvs[ i2 + 5 ] = uvc.y; + this.verticesNeedUpdate = false; + this.elementsNeedUpdate = false; + this.uvsNeedUpdate = false; + this.normalsNeedUpdate = false; + this.tangentsNeedUpdate = false; + this.colorsNeedUpdate = false; + this.lineDistancesNeedUpdate = false; - } + this.groupsNeedUpdate = false; - } +}; - this.computeBoundingSphere() +THREE.Geometry.prototype = { - return this; + constructor: THREE.Geometry, - }, + applyMatrix: function ( matrix ) { - computeBoundingBox: function () { + var normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); - if ( this.boundingBox === null ) { + for ( var i = 0, il = this.vertices.length; i < il; i ++ ) { - this.boundingBox = new THREE.Box3(); + var vertex = this.vertices[ i ]; + vertex.applyMatrix4( matrix ); } - var positions = this.attributes[ 'position' ].array; + for ( var i = 0, il = this.faces.length; i < il; i ++ ) { + + var face = this.faces[ i ]; + face.normal.applyMatrix3( normalMatrix ).normalize(); - if ( positions ) { + for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { - var bb = this.boundingBox; + face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize(); - if ( positions.length >= 3 ) { - bb.min.x = bb.max.x = positions[ 0 ]; - bb.min.y = bb.max.y = positions[ 1 ]; - bb.min.z = bb.max.z = positions[ 2 ]; } - for ( var i = 3, il = positions.length; i < il; i += 3 ) { - - var x = positions[ i ]; - var y = positions[ i + 1 ]; - var z = positions[ i + 2 ]; + } - // bounding box + if ( this.boundingBox instanceof THREE.Box3 ) { - if ( x < bb.min.x ) { + this.computeBoundingBox(); - bb.min.x = x; + } - } else if ( x > bb.max.x ) { + if ( this.boundingSphere instanceof THREE.Sphere ) { - bb.max.x = x; + this.computeBoundingSphere(); - } + } - if ( y < bb.min.y ) { + }, - bb.min.y = y; + fromBufferGeometry: function ( geometry ) { - } else if ( y > bb.max.y ) { + var scope = this; - bb.max.y = y; + var attributes = geometry.attributes; - } + var vertices = attributes.position.array; + var indices = attributes.index !== undefined ? attributes.index.array : undefined; + var normals = attributes.normal !== undefined ? attributes.normal.array : undefined; + var colors = attributes.color !== undefined ? attributes.color.array : undefined; + var uvs = attributes.uv !== undefined ? attributes.uv.array : undefined; - if ( z < bb.min.z ) { + var tempNormals = []; + var tempUVs = []; - bb.min.z = z; + for ( var i = 0, j = 0; i < vertices.length; i += 3, j += 2 ) { - } else if ( z > bb.max.z ) { + scope.vertices.push( new THREE.Vector3( vertices[ i ], vertices[ i + 1 ], vertices[ i + 2 ] ) ); - bb.max.z = z; + if ( normals !== undefined ) { - } + tempNormals.push( new THREE.Vector3( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ) ); } - } + if ( colors !== undefined ) { - if ( positions === undefined || positions.length === 0 ) { + scope.colors.push( new THREE.Color( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ) ); - this.boundingBox.min.set( 0, 0, 0 ); - this.boundingBox.max.set( 0, 0, 0 ); + } - } + if ( uvs !== undefined ) { - if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { + tempUVs.push( new THREE.Vector2( uvs[ j ], uvs[ j + 1 ] ) ); - console.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.' ); + } } - }, + var addFace = function ( a, b, c ) { - computeBoundingSphere: function () { + var vertexNormals = normals !== undefined ? [ tempNormals[ a ].clone(), tempNormals[ b ].clone(), tempNormals[ c ].clone() ] : []; + var vertexColors = colors !== undefined ? [ scope.colors[ a ].clone(), scope.colors[ b ].clone(), scope.colors[ c ].clone() ] : []; - var box = new THREE.Box3(); - var vector = new THREE.Vector3(); + scope.faces.push( new THREE.Face3( a, b, c, vertexNormals, vertexColors ) ); + scope.faceVertexUvs[ 0 ].push( [ tempUVs[ a ], tempUVs[ b ], tempUVs[ c ] ] ); - return function () { + }; - if ( this.boundingSphere === null ) { + if ( indices !== undefined ) { - this.boundingSphere = new THREE.Sphere(); + for ( var i = 0; i < indices.length; i += 3 ) { + + addFace( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] ); } - var positions = this.attributes[ 'position' ].array; + } else { - if ( positions ) { + for ( var i = 0; i < vertices.length / 3; i += 3 ) { - box.makeEmpty(); + addFace( i, i + 1, i + 2 ); - var center = this.boundingSphere.center; + } - for ( var i = 0, il = positions.length; i < il; i += 3 ) { + } + + this.computeFaceNormals(); - vector.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); - box.expandByPoint( vector ); + if ( geometry.boundingBox !== null ) { - } + this.boundingBox = geometry.boundingBox.clone(); - box.center( center ); + } - // hoping to find a boundingSphere with a radius smaller than the - // boundingSphere of the boundingBox: sqrt(3) smaller in the best case + if ( geometry.boundingSphere !== null ) { - var maxRadiusSq = 0; + this.boundingSphere = geometry.boundingSphere.clone(); - for ( var i = 0, il = positions.length; i < il; i += 3 ) { + } - vector.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) ); + return this; - } + }, - this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); + center: function () { - if ( isNaN( this.boundingSphere.radius ) ) { + this.computeBoundingBox(); - console.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.' ); + var offset = new THREE.Vector3(); - } + offset.addVectors( this.boundingBox.min, this.boundingBox.max ); + offset.multiplyScalar( - 0.5 ); - } + this.applyMatrix( new THREE.Matrix4().makeTranslation( offset.x, offset.y, offset.z ) ); + this.computeBoundingBox(); - } + return offset; - }(), + }, computeFaceNormals: function () { - // backwards compatibility + var cb = new THREE.Vector3(), ab = new THREE.Vector3(); - }, + for ( var f = 0, fl = this.faces.length; f < fl; f ++ ) { - computeVertexNormals: function () { + var face = this.faces[ f ]; - if ( this.attributes[ 'position' ] ) { + var vA = this.vertices[ face.a ]; + var vB = this.vertices[ face.b ]; + var vC = this.vertices[ face.c ]; - var i, il; - var j, jl; + cb.subVectors( vC, vB ); + ab.subVectors( vA, vB ); + cb.cross( ab ); - var nVertexElements = this.attributes[ 'position' ].array.length; + cb.normalize(); - if ( this.attributes[ 'normal' ] === undefined ) { + face.normal.copy( cb ); - this.attributes[ 'normal' ] = { + } - itemSize: 3, - array: new Float32Array( nVertexElements ) + }, - }; + computeVertexNormals: function ( areaWeighted ) { - } else { + var v, vl, f, fl, face, vertices; - // reset existing normals to zero + vertices = new Array( this.vertices.length ); - for ( i = 0, il = this.attributes[ 'normal' ].array.length; i < il; i ++ ) { + for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { - this.attributes[ 'normal' ].array[ i ] = 0; + vertices[ v ] = new THREE.Vector3(); - } + } - } + if ( areaWeighted ) { - var positions = this.attributes[ 'position' ].array; - var normals = this.attributes[ 'normal' ].array; + // vertex normals weighted by triangle areas + // http://www.iquilezles.org/www/articles/normals/normals.htm - var vA, vB, vC, x, y, z, + var vA, vB, vC, vD; + var cb = new THREE.Vector3(), ab = new THREE.Vector3(), + db = new THREE.Vector3(), dc = new THREE.Vector3(), bc = new THREE.Vector3(); - pA = new THREE.Vector3(), - pB = new THREE.Vector3(), - pC = new THREE.Vector3(), + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - cb = new THREE.Vector3(), - ab = new THREE.Vector3(); + face = this.faces[ f ]; - // indexed elements + vA = this.vertices[ face.a ]; + vB = this.vertices[ face.b ]; + vC = this.vertices[ face.c ]; - if ( this.attributes[ 'index' ] ) { + cb.subVectors( vC, vB ); + ab.subVectors( vA, vB ); + cb.cross( ab ); - var indices = this.attributes[ 'index' ].array; + vertices[ face.a ].add( cb ); + vertices[ face.b ].add( cb ); + vertices[ face.c ].add( cb ); - var offsets = ( this.offsets.length > 0 ? this.offsets : [ { start: 0, count: indices.length, index: 0 } ] ); + } - for ( j = 0, jl = offsets.length; j < jl; ++ j ) { + } else { - var start = offsets[ j ].start; - var count = offsets[ j ].count; - var index = offsets[ j ].index; + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - for ( i = start, il = start + count; i < il; i += 3 ) { + face = this.faces[ f ]; - vA = index + indices[ i ]; - vB = index + indices[ i + 1 ]; - vC = index + indices[ i + 2 ]; + vertices[ face.a ].add( face.normal ); + vertices[ face.b ].add( face.normal ); + vertices[ face.c ].add( face.normal ); - x = positions[ vA * 3 ]; - y = positions[ vA * 3 + 1 ]; - z = positions[ vA * 3 + 2 ]; - pA.set( x, y, z ); + } - x = positions[ vB * 3 ]; - y = positions[ vB * 3 + 1 ]; - z = positions[ vB * 3 + 2 ]; - pB.set( x, y, z ); + } - x = positions[ vC * 3 ]; - y = positions[ vC * 3 + 1 ]; - z = positions[ vC * 3 + 2 ]; - pC.set( x, y, z ); + for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { - cb.subVectors( pC, pB ); - ab.subVectors( pA, pB ); - cb.cross( ab ); + vertices[ v ].normalize(); - normals[ vA * 3 ] += cb.x; - normals[ vA * 3 + 1 ] += cb.y; - normals[ vA * 3 + 2 ] += cb.z; + } - normals[ vB * 3 ] += cb.x; - normals[ vB * 3 + 1 ] += cb.y; - normals[ vB * 3 + 2 ] += cb.z; + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - normals[ vC * 3 ] += cb.x; - normals[ vC * 3 + 1 ] += cb.y; - normals[ vC * 3 + 2 ] += cb.z; + face = this.faces[ f ]; - } + face.vertexNormals[ 0 ] = vertices[ face.a ].clone(); + face.vertexNormals[ 1 ] = vertices[ face.b ].clone(); + face.vertexNormals[ 2 ] = vertices[ face.c ].clone(); - } + } - // non-indexed elements (unconnected triangle soup) + }, - } else { + computeMorphNormals: function () { - for ( i = 0, il = positions.length; i < il; i += 9 ) { + var i, il, f, fl, face; - x = positions[ i ]; - y = positions[ i + 1 ]; - z = positions[ i + 2 ]; - pA.set( x, y, z ); + // save original normals + // - create temp variables on first access + // otherwise just copy (for faster repeated calls) - x = positions[ i + 3 ]; - y = positions[ i + 4 ]; - z = positions[ i + 5 ]; - pB.set( x, y, z ); + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - x = positions[ i + 6 ]; - y = positions[ i + 7 ]; - z = positions[ i + 8 ]; - pC.set( x, y, z ); + face = this.faces[ f ]; - cb.subVectors( pC, pB ); - ab.subVectors( pA, pB ); - cb.cross( ab ); + if ( ! face.__originalFaceNormal ) { - normals[ i ] = cb.x; - normals[ i + 1 ] = cb.y; - normals[ i + 2 ] = cb.z; + face.__originalFaceNormal = face.normal.clone(); - normals[ i + 3 ] = cb.x; - normals[ i + 4 ] = cb.y; - normals[ i + 5 ] = cb.z; + } else { - normals[ i + 6 ] = cb.x; - normals[ i + 7 ] = cb.y; - normals[ i + 8 ] = cb.z; + face.__originalFaceNormal.copy( face.normal ); + + } + + if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = []; + + for ( i = 0, il = face.vertexNormals.length; i < il; i ++ ) { + + if ( ! face.__originalVertexNormals[ i ] ) { + + face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone(); + + } else { + + face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] ); } } - this.normalizeNormals(); + } - this.normalsNeedUpdate = true; + // use temp geometry to compute face and vertex normals for each morph - } + var tmpGeo = new THREE.Geometry(); + tmpGeo.faces = this.faces; - }, + for ( i = 0, il = this.morphTargets.length; i < il; i ++ ) { - computeTangents: function () { + // create on first access - // based on http://www.terathon.com/code/tangent.html - // (per vertex tangents) + if ( ! this.morphNormals[ i ] ) { - if ( this.attributes[ 'index' ] === undefined || - this.attributes[ 'position' ] === undefined || - this.attributes[ 'normal' ] === undefined || - this.attributes[ 'uv' ] === undefined ) { + this.morphNormals[ i ] = {}; + this.morphNormals[ i ].faceNormals = []; + this.morphNormals[ i ].vertexNormals = []; - console.warn( 'Missing required attributes (index, position, normal or uv) in BufferGeometry.computeTangents()' ); - return; + var dstNormalsFace = this.morphNormals[ i ].faceNormals; + var dstNormalsVertex = this.morphNormals[ i ].vertexNormals; - } + var faceNormal, vertexNormals; - var indices = this.attributes[ 'index' ].array; - var positions = this.attributes[ 'position' ].array; - var normals = this.attributes[ 'normal' ].array; - var uvs = this.attributes[ 'uv' ].array; + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - var nVertices = positions.length / 3; + faceNormal = new THREE.Vector3(); + vertexNormals = { a: new THREE.Vector3(), b: new THREE.Vector3(), c: new THREE.Vector3() }; - if ( this.attributes[ 'tangent' ] === undefined ) { + dstNormalsFace.push( faceNormal ); + dstNormalsVertex.push( vertexNormals ); - var nTangentElements = 4 * nVertices; + } - this.attributes[ 'tangent' ] = { + } - itemSize: 4, - array: new Float32Array( nTangentElements ) + var morphNormals = this.morphNormals[ i ]; - }; + // set vertices to morph target - } + tmpGeo.vertices = this.morphTargets[ i ].vertices; - var tangents = this.attributes[ 'tangent' ].array; + // compute morph normals - var tan1 = [], tan2 = []; + tmpGeo.computeFaceNormals(); + tmpGeo.computeVertexNormals(); - for ( var k = 0; k < nVertices; k ++ ) { + // store morph normals - tan1[ k ] = new THREE.Vector3(); - tan2[ k ] = new THREE.Vector3(); + var faceNormal, vertexNormals; - } + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - var xA, yA, zA, - xB, yB, zB, - xC, yC, zC, + face = this.faces[ f ]; - uA, vA, - uB, vB, - uC, vC, + faceNormal = morphNormals.faceNormals[ f ]; + vertexNormals = morphNormals.vertexNormals[ f ]; - x1, x2, y1, y2, z1, z2, - s1, s2, t1, t2, r; + faceNormal.copy( face.normal ); - var sdir = new THREE.Vector3(), tdir = new THREE.Vector3(); + vertexNormals.a.copy( face.vertexNormals[ 0 ] ); + vertexNormals.b.copy( face.vertexNormals[ 1 ] ); + vertexNormals.c.copy( face.vertexNormals[ 2 ] ); - function handleTriangle( a, b, c ) { + } + + } + + // restore original normals - xA = positions[ a * 3 ]; - yA = positions[ a * 3 + 1 ]; - zA = positions[ a * 3 + 2 ]; + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - xB = positions[ b * 3 ]; - yB = positions[ b * 3 + 1 ]; - zB = positions[ b * 3 + 2 ]; + face = this.faces[ f ]; - xC = positions[ c * 3 ]; - yC = positions[ c * 3 + 1 ]; - zC = positions[ c * 3 + 2 ]; + face.normal = face.__originalFaceNormal; + face.vertexNormals = face.__originalVertexNormals; - uA = uvs[ a * 2 ]; - vA = uvs[ a * 2 + 1 ]; + } - uB = uvs[ b * 2 ]; - vB = uvs[ b * 2 + 1 ]; + }, - uC = uvs[ c * 2 ]; - vC = uvs[ c * 2 + 1 ]; + computeTangents: function () { - x1 = xB - xA; - x2 = xC - xA; + // based on http://www.terathon.com/code/tangent.html + // tangents go to vertices - y1 = yB - yA; - y2 = yC - yA; + var f, fl, v, vl, i, il, vertexIndex, + face, uv, vA, vB, vC, uvA, uvB, uvC, + x1, x2, y1, y2, z1, z2, + s1, s2, t1, t2, r, t, test, + tan1 = [], tan2 = [], + sdir = new THREE.Vector3(), tdir = new THREE.Vector3(), + tmp = new THREE.Vector3(), tmp2 = new THREE.Vector3(), + n = new THREE.Vector3(), w; - z1 = zB - zA; - z2 = zC - zA; + for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { - s1 = uB - uA; - s2 = uC - uA; + tan1[ v ] = new THREE.Vector3(); + tan2[ v ] = new THREE.Vector3(); - t1 = vB - vA; - t2 = vC - vA; + } - r = 1.0 / ( s1 * t2 - s2 * t1 ); + function handleTriangle( context, a, b, c, ua, ub, uc ) { - sdir.set( - ( t2 * x1 - t1 * x2 ) * r, - ( t2 * y1 - t1 * y2 ) * r, - ( t2 * z1 - t1 * z2 ) * r - ); + vA = context.vertices[ a ]; + vB = context.vertices[ b ]; + vC = context.vertices[ c ]; - tdir.set( - ( s1 * x2 - s2 * x1 ) * r, - ( s1 * y2 - s2 * y1 ) * r, - ( s1 * z2 - s2 * z1 ) * r - ); + uvA = uv[ ua ]; + uvB = uv[ ub ]; + uvC = uv[ uc ]; + + x1 = vB.x - vA.x; + x2 = vC.x - vA.x; + y1 = vB.y - vA.y; + y2 = vC.y - vA.y; + z1 = vB.z - vA.z; + z2 = vC.z - vA.z; + + s1 = uvB.x - uvA.x; + s2 = uvC.x - uvA.x; + t1 = uvB.y - uvA.y; + t2 = uvC.y - uvA.y; + + r = 1.0 / ( s1 * t2 - s2 * t1 ); + sdir.set( ( t2 * x1 - t1 * x2 ) * r, + ( t2 * y1 - t1 * y2 ) * r, + ( t2 * z1 - t1 * z2 ) * r ); + tdir.set( ( s1 * x2 - s2 * x1 ) * r, + ( s1 * y2 - s2 * y1 ) * r, + ( s1 * z2 - s2 * z1 ) * r ); tan1[ a ].add( sdir ); tan1[ b ].add( sdir ); @@ -9437,2290 +9696,1771 @@ THREE.BufferGeometry.prototype = { } - var i, il; - var j, jl; - var iA, iB, iC; + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - var offsets = this.offsets; + face = this.faces[ f ]; + uv = this.faceVertexUvs[ 0 ][ f ]; // use UV layer 0 for tangents - for ( j = 0, jl = offsets.length; j < jl; ++ j ) { + handleTriangle( this, face.a, face.b, face.c, 0, 1, 2 ); - var start = offsets[ j ].start; - var count = offsets[ j ].count; - var index = offsets[ j ].index; + } - for ( i = start, il = start + count; i < il; i += 3 ) { + var faceIndex = [ 'a', 'b', 'c', 'd' ]; - iA = index + indices[ i ]; - iB = index + indices[ i + 1 ]; - iC = index + indices[ i + 2 ]; + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - handleTriangle( iA, iB, iC ); + face = this.faces[ f ]; - } + for ( i = 0; i < Math.min( face.vertexNormals.length, 3 ); i ++ ) { - } + n.copy( face.vertexNormals[ i ] ); - var tmp = new THREE.Vector3(), tmp2 = new THREE.Vector3(); - var n = new THREE.Vector3(), n2 = new THREE.Vector3(); - var w, t, test; + vertexIndex = face[ faceIndex[ i ] ]; - function handleVertex( v ) { + t = tan1[ vertexIndex ]; - n.x = normals[ v * 3 ]; - n.y = normals[ v * 3 + 1 ]; - n.z = normals[ v * 3 + 2 ]; + // Gram-Schmidt orthogonalize - n2.copy( n ); + tmp.copy( t ); + tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize(); - t = tan1[ v ]; + // Calculate handedness - // Gram-Schmidt orthogonalize + tmp2.crossVectors( face.vertexNormals[ i ], t ); + test = tmp2.dot( tan2[ vertexIndex ] ); + w = ( test < 0.0 ) ? - 1.0 : 1.0; - tmp.copy( t ); - tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize(); + face.vertexTangents[ i ] = new THREE.Vector4( tmp.x, tmp.y, tmp.z, w ); - // Calculate handedness + } - tmp2.crossVectors( n2, t ); - test = tmp2.dot( tan2[ v ] ); - w = ( test < 0.0 ) ? - 1.0 : 1.0; + } - tangents[ v * 4 ] = tmp.x; - tangents[ v * 4 + 1 ] = tmp.y; - tangents[ v * 4 + 2 ] = tmp.z; - tangents[ v * 4 + 3 ] = w; + this.hasTangents = true; - } + }, - for ( j = 0, jl = offsets.length; j < jl; ++ j ) { + computeLineDistances: function () { - var start = offsets[ j ].start; - var count = offsets[ j ].count; - var index = offsets[ j ].index; + var d = 0; + var vertices = this.vertices; - for ( i = start, il = start + count; i < il; i += 3 ) { + for ( var i = 0, il = vertices.length; i < il; i ++ ) { - iA = index + indices[ i ]; - iB = index + indices[ i + 1 ]; - iC = index + indices[ i + 2 ]; + if ( i > 0 ) { - handleVertex( iA ); - handleVertex( iB ); - handleVertex( iC ); + d += vertices[ i ].distanceTo( vertices[ i - 1 ] ); } + this.lineDistances[ i ] = d; + } }, - /* - computeOffsets - Compute the draw offset for large models by chunking the index buffer into chunks of 65k addressable vertices. - This method will effectively rewrite the index buffer and remap all attributes to match the new indices. - WARNING: This method will also expand the vertex count to prevent sprawled triangles across draw offsets. - indexBufferSize - Defaults to 65535, but allows for larger or smaller chunks. - */ - computeOffsets: function ( indexBufferSize ) { + computeBoundingBox: function () { - var size = indexBufferSize; - if ( indexBufferSize === undefined ) - size = 65535; //WebGL limits type of index buffer values to 16-bit. + if ( this.boundingBox === null ) { - var s = Date.now(); + this.boundingBox = new THREE.Box3(); - var indices = this.attributes[ 'index' ].array; - var vertices = this.attributes[ 'position' ].array; + } - var verticesCount = ( vertices.length / 3 ); - var facesCount = ( indices.length / 3 ); + this.boundingBox.setFromPoints( this.vertices ); - /* - console.log("Computing buffers in offsets of "+size+" -> indices:"+indices.length+" vertices:"+vertices.length); - console.log("Faces to process: "+(indices.length/3)); - console.log("Reordering "+verticesCount+" vertices."); - */ + }, - var sortedIndices = new Uint16Array( indices.length ); //16-bit buffers - var indexPtr = 0; - var vertexPtr = 0; + computeBoundingSphere: function () { - var offsets = [ { start:0, count:0, index:0 } ]; - var offset = offsets[ 0 ]; + if ( this.boundingSphere === null ) { - var duplicatedVertices = 0; - var newVerticeMaps = 0; - var faceVertices = new Int32Array( 6 ); - var vertexMap = new Int32Array( vertices.length ); - var revVertexMap = new Int32Array( vertices.length ); - for ( var j = 0; j < vertices.length; j ++ ) { vertexMap[ j ] = - 1; revVertexMap[ j ] = - 1; } + this.boundingSphere = new THREE.Sphere(); - /* - Traverse every face and reorder vertices in the proper offsets of 65k. - We can have more than 65k entries in the index buffer per offset, but only reference 65k values. - */ - for ( var findex = 0; findex < facesCount; findex ++ ) { - newVerticeMaps = 0; + } - for ( var vo = 0; vo < 3; vo ++ ) { - var vid = indices[ findex * 3 + vo ]; - if ( vertexMap[ vid ] == - 1 ) { - //Unmapped vertice - faceVertices[ vo * 2 ] = vid; - faceVertices[ vo * 2 + 1 ] = - 1; - newVerticeMaps ++; - } else if ( vertexMap[ vid ] < offset.index ) { - //Reused vertices from previous block (duplicate) - faceVertices[ vo * 2 ] = vid; - faceVertices[ vo * 2 + 1 ] = - 1; - duplicatedVertices ++; - } else { - //Reused vertice in the current block - faceVertices[ vo * 2 ] = vid; - faceVertices[ vo * 2 + 1 ] = vertexMap[ vid ]; - } - } + this.boundingSphere.setFromPoints( this.vertices ); - var faceMax = vertexPtr + newVerticeMaps; - if ( faceMax > ( offset.index + size ) ) { - var new_offset = { start:indexPtr, count:0, index:vertexPtr }; - offsets.push( new_offset ); - offset = new_offset; + }, - //Re-evaluate reused vertices in light of new offset. - for ( var v = 0; v < 6; v += 2 ) { - var new_vid = faceVertices[ v + 1 ]; - if ( new_vid > - 1 && new_vid < offset.index ) - faceVertices[ v + 1 ] = - 1; - } - } + merge: function ( geometry, matrix, materialIndexOffset ) { - //Reindex the face. - for ( var v = 0; v < 6; v += 2 ) { - var vid = faceVertices[ v ]; - var new_vid = faceVertices[ v + 1 ]; + if ( geometry instanceof THREE.Geometry === false ) { - if ( new_vid === - 1 ) - new_vid = vertexPtr ++; + console.error( 'THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry ); + return; - vertexMap[ vid ] = new_vid; - revVertexMap[ new_vid ] = vid; - sortedIndices[ indexPtr ++ ] = new_vid - offset.index; //XXX overflows at 16bit - offset.count ++; - } } - /* Move all attribute values to map to the new computed indices , also expand the vertice stack to match our new vertexPtr. */ - this.reorderBuffers( sortedIndices, revVertexMap, vertexPtr ); - this.offsets = offsets; - - /* - var orderTime = Date.now(); - console.log("Reorder time: "+(orderTime-s)+"ms"); - console.log("Duplicated "+duplicatedVertices+" vertices."); - console.log("Compute Buffers time: "+(Date.now()-s)+"ms"); - console.log("Draw offsets: "+offsets.length); - */ - - return offsets; - }, + var normalMatrix, + vertexOffset = this.vertices.length, + vertices1 = this.vertices, + vertices2 = geometry.vertices, + faces1 = this.faces, + faces2 = geometry.faces, + uvs1 = this.faceVertexUvs[ 0 ], + uvs2 = geometry.faceVertexUvs[ 0 ]; - merge: function () { + if ( materialIndexOffset === undefined ) materialIndexOffset = 0; - console.log( 'BufferGeometry.merge(): TODO' ); + if ( matrix !== undefined ) { - }, + normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); - normalizeNormals: function () { + } - var normals = this.attributes[ 'normal' ].array; + // vertices - var x, y, z, n; + for ( var i = 0, il = vertices2.length; i < il; i ++ ) { - for ( var i = 0, il = normals.length; i < il; i += 3 ) { + var vertex = vertices2[ i ]; - x = normals[ i ]; - y = normals[ i + 1 ]; - z = normals[ i + 2 ]; + var vertexCopy = vertex.clone(); - n = 1.0 / Math.sqrt( x * x + y * y + z * z ); + if ( matrix !== undefined ) vertexCopy.applyMatrix4( matrix ); - normals[ i ] *= n; - normals[ i + 1 ] *= n; - normals[ i + 2 ] *= n; + vertices1.push( vertexCopy ); } - }, - - /* - reoderBuffers: - Reorder attributes based on a new indexBuffer and indexMap. - indexBuffer - Uint16Array of the new ordered indices. - indexMap - Int32Array where the position is the new vertex ID and the value the old vertex ID for each vertex. - vertexCount - Amount of total vertices considered in this reordering (in case you want to grow the vertice stack). - */ - reorderBuffers: function ( indexBuffer, indexMap, vertexCount ) { + // faces - /* Create a copy of all attributes for reordering. */ - var sortedAttributes = {}; - var types = [ Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array ]; - for ( var attr in this.attributes ) { - if ( attr == 'index' ) - continue; - var sourceArray = this.attributes[ attr ].array; - for ( var i = 0, il = types.length; i < il; i ++ ) { - var type = types[ i ]; - if ( sourceArray instanceof type ) { - sortedAttributes[ attr ] = new type( this.attributes[ attr ].itemSize * vertexCount ); - break; - } - } - } + for ( i = 0, il = faces2.length; i < il; i ++ ) { - /* Move attribute positions based on the new index map */ - for ( var new_vid = 0; new_vid < vertexCount; new_vid ++ ) { - var vid = indexMap[ new_vid ]; - for ( var attr in this.attributes ) { - if ( attr == 'index' ) - continue; - var attrArray = this.attributes[ attr ].array; - var attrSize = this.attributes[ attr ].itemSize; - var sortedAttr = sortedAttributes[ attr ]; - for ( var k = 0; k < attrSize; k ++ ) - sortedAttr[ new_vid * attrSize + k ] = attrArray[ vid * attrSize + k ]; - } - } + var face = faces2[ i ], faceCopy, normal, color, + faceVertexNormals = face.vertexNormals, + faceVertexColors = face.vertexColors; - /* Carry the new sorted buffers locally */ - this.attributes[ 'index' ].array = indexBuffer; - for ( var attr in this.attributes ) { - if ( attr == 'index' ) - continue; - this.attributes[ attr ].array = sortedAttributes[ attr ]; - this.attributes[ attr ].numItems = this.attributes[ attr ].itemSize * vertexCount; - } - }, + faceCopy = new THREE.Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset ); + faceCopy.normal.copy( face.normal ); - clone: function () { + if ( normalMatrix !== undefined ) { - var geometry = new THREE.BufferGeometry(); + faceCopy.normal.applyMatrix3( normalMatrix ).normalize(); - var types = [ Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array ]; + } - for ( var attr in this.attributes ) { + for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) { - var sourceAttr = this.attributes[ attr ]; - var sourceArray = sourceAttr.array; + normal = faceVertexNormals[ j ].clone(); - var attribute = { + if ( normalMatrix !== undefined ) { - itemSize: sourceAttr.itemSize, - array: null + normal.applyMatrix3( normalMatrix ).normalize(); - }; + } - for ( var i = 0, il = types.length; i < il; i ++ ) { + faceCopy.vertexNormals.push( normal ); - var type = types[ i ]; + } - if ( sourceArray instanceof type ) { + faceCopy.color.copy( face.color ); - attribute.array = new type( sourceArray ); - break; + for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) { - } + color = faceVertexColors[ j ]; + faceCopy.vertexColors.push( color.clone() ); } - geometry.attributes[ attr ] = attribute; + faceCopy.materialIndex = face.materialIndex + materialIndexOffset; + + faces1.push( faceCopy ); } - for ( var i = 0, il = this.offsets.length; i < il; i ++ ) { + // uvs - var offset = this.offsets[ i ]; + for ( i = 0, il = uvs2.length; i < il; i ++ ) { - geometry.offsets.push( { + var uv = uvs2[ i ], uvCopy = []; - start: offset.start, - index: offset.index, - count: offset.count + if ( uv === undefined ) { - } ); + continue; - } + } - return geometry; + for ( var j = 0, jl = uv.length; j < jl; j ++ ) { - }, + uvCopy.push( new THREE.Vector2( uv[ j ].x, uv[ j ].y ) ); - dispose: function () { + } - this.dispatchEvent( { type: 'dispose' } ); + uvs1.push( uvCopy ); - } + } -}; + }, -THREE.EventDispatcher.prototype.apply( THREE.BufferGeometry.prototype ); + /* + * Checks for duplicate vertices with hashmap. + * Duplicated vertices are removed + * and faces' vertices are updated. + */ -// File:src/core/Geometry.js + mergeVertices: function () { -/** - * @author mrdoob / http://mrdoob.com/ - * @author kile / http://kile.stravaganza.org/ - * @author alteredq / http://alteredqualia.com/ - * @author mikael emtinger / http://gomo.se/ - * @author zz85 / http://www.lab4games.net/zz85/blog - * @author bhouston / http://exocortex.com - */ + var verticesMap = {}; // Hashmap for looking up vertice by position coordinates (and making sure they are unique) + var unique = [], changes = []; -THREE.Geometry = function () { + var v, key; + var precisionPoints = 4; // number of decimal points, eg. 4 for epsilon of 0.0001 + var precision = Math.pow( 10, precisionPoints ); + var i,il, face; + var indices, k, j, jl, u; - this.id = THREE.GeometryIdCount ++; - this.uuid = THREE.Math.generateUUID(); + for ( i = 0, il = this.vertices.length; i < il; i ++ ) { - this.name = ''; + v = this.vertices[ i ]; + key = Math.round( v.x * precision ) + '_' + Math.round( v.y * precision ) + '_' + Math.round( v.z * precision ); - this.vertices = []; - this.colors = []; // one-to-one vertex colors, used in Points and Line + if ( verticesMap[ key ] === undefined ) { - this.faces = []; + verticesMap[ key ] = i; + unique.push( this.vertices[ i ] ); + changes[ i ] = unique.length - 1; - this.faceVertexUvs = [ [] ]; + } else { - this.morphTargets = []; - this.morphColors = []; - this.morphNormals = []; + //console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]); + changes[ i ] = changes[ verticesMap[ key ] ]; - this.skinWeights = []; - this.skinIndices = []; + } - this.lineDistances = []; + }; - this.boundingBox = null; - this.boundingSphere = null; - this.hasTangents = false; + // if faces are completely degenerate after merging vertices, we + // have to remove them from the geometry. + var faceIndicesToRemove = []; - this.dynamic = true; // the intermediate typed arrays will be deleted when set to false + for ( i = 0, il = this.faces.length; i < il; i ++ ) { - // update flags - - this.verticesNeedUpdate = false; - this.elementsNeedUpdate = false; - this.uvsNeedUpdate = false; - this.normalsNeedUpdate = false; - this.tangentsNeedUpdate = false; - this.colorsNeedUpdate = false; - this.lineDistancesNeedUpdate = false; - - this.buffersNeedUpdate = false; - this.groupsNeedUpdate = false; - -}; + face = this.faces[ i ]; -THREE.Geometry.prototype = { + face.a = changes[ face.a ]; + face.b = changes[ face.b ]; + face.c = changes[ face.c ]; - constructor: THREE.Geometry, + indices = [ face.a, face.b, face.c ]; - applyMatrix: function ( matrix ) { + var dupIndex = - 1; - var normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); + // if any duplicate vertices are found in a Face3 + // we have to remove the face as nothing can be saved + for ( var n = 0; n < 3; n ++ ) { + if ( indices[ n ] == indices[ ( n + 1 ) % 3 ] ) { - for ( var i = 0, il = this.vertices.length; i < il; i ++ ) { + dupIndex = n; + faceIndicesToRemove.push( i ); + break; - var vertex = this.vertices[ i ]; - vertex.applyMatrix4( matrix ); + } + } } - for ( var i = 0, il = this.faces.length; i < il; i ++ ) { + for ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) { + var idx = faceIndicesToRemove[ i ]; - var face = this.faces[ i ]; - face.normal.applyMatrix3( normalMatrix ).normalize(); + this.faces.splice( idx, 1 ); - for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { + for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) { - face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize(); + this.faceVertexUvs[ j ].splice( idx, 1 ); } } - if ( this.boundingBox instanceof THREE.Box3 ) { + // Use unique set of vertices - this.computeBoundingBox(); + var diff = this.vertices.length - unique.length; + this.vertices = unique; + return diff; - } + }, - if ( this.boundingSphere instanceof THREE.Sphere ) { + toJSON: function () { - this.computeBoundingSphere(); + var output = { + metadata: { + version: 4.0, + type: 'BufferGeometry', + generator: 'BufferGeometryExporter' + }, + uuid: this.uuid, + type: this.type + }; - } + if ( this.name !== "" ) output.name = this.name; - }, + if ( this.parameters !== undefined ) { - center: function () { + var parameters = this.parameters; - this.computeBoundingBox(); + for ( var key in parameters ) { - var offset = new THREE.Vector3(); + if ( parameters[ key ] !== undefined ) output[ key ] = parameters[ key ]; - offset.addVectors( this.boundingBox.min, this.boundingBox.max ); - offset.multiplyScalar( - 0.5 ); + } - this.applyMatrix( new THREE.Matrix4().makeTranslation( offset.x, offset.y, offset.z ) ); - this.computeBoundingBox(); + return output; - return offset; + } - }, + var vertices = []; - computeFaceNormals: function () { + for ( var i = 0; i < this.vertices.length; i ++ ) { - var cb = new THREE.Vector3(), ab = new THREE.Vector3(); + var vertex = this.vertices[ i ]; + vertices.push( vertex.x, vertex.y, vertex.z ); - for ( var f = 0, fl = this.faces.length; f < fl; f ++ ) { + } - var face = this.faces[ f ]; + var faces = []; + var normals = []; + var normalsHash = {}; + var colors = []; + var colorsHash = {}; + var uvs = []; + var uvsHash = {}; - var vA = this.vertices[ face.a ]; - var vB = this.vertices[ face.b ]; - var vC = this.vertices[ face.c ]; + for ( var i = 0; i < this.faces.length; i ++ ) { - cb.subVectors( vC, vB ); - ab.subVectors( vA, vB ); - cb.cross( ab ); + var face = this.faces[ i ]; - cb.normalize(); + var hasMaterial = false; // face.materialIndex !== undefined; + var hasFaceUv = false; // deprecated + var hasFaceVertexUv = this.faceVertexUvs[ 0 ][ i ] !== undefined; + var hasFaceNormal = face.normal.length() > 0; + var hasFaceVertexNormal = face.vertexNormals.length > 0; + var hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1; + var hasFaceVertexColor = face.vertexColors.length > 0; - face.normal.copy( cb ); + var faceType = 0; - } + faceType = setBit( faceType, 0, 0 ); + faceType = setBit( faceType, 1, hasMaterial ); + faceType = setBit( faceType, 2, hasFaceUv ); + faceType = setBit( faceType, 3, hasFaceVertexUv ); + faceType = setBit( faceType, 4, hasFaceNormal ); + faceType = setBit( faceType, 5, hasFaceVertexNormal ); + faceType = setBit( faceType, 6, hasFaceColor ); + faceType = setBit( faceType, 7, hasFaceVertexColor ); - }, + faces.push( faceType ); + faces.push( face.a, face.b, face.c ); - computeVertexNormals: function ( areaWeighted ) { - var v, vl, f, fl, face, vertices; + /* + if ( hasMaterial ) { - vertices = new Array( this.vertices.length ); + faces.push( face.materialIndex ); - for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { + } + */ - vertices[ v ] = new THREE.Vector3(); + if ( hasFaceVertexUv ) { - } + var faceVertexUvs = this.faceVertexUvs[ 0 ][ i ]; - if ( areaWeighted ) { + faces.push( + getUvIndex( faceVertexUvs[ 0 ] ), + getUvIndex( faceVertexUvs[ 1 ] ), + getUvIndex( faceVertexUvs[ 2 ] ) + ); - // vertex normals weighted by triangle areas - // http://www.iquilezles.org/www/articles/normals/normals.htm + } - var vA, vB, vC, vD; - var cb = new THREE.Vector3(), ab = new THREE.Vector3(), - db = new THREE.Vector3(), dc = new THREE.Vector3(), bc = new THREE.Vector3(); + if ( hasFaceNormal ) { - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + faces.push( getNormalIndex( face.normal ) ); - face = this.faces[ f ]; + } - vA = this.vertices[ face.a ]; - vB = this.vertices[ face.b ]; - vC = this.vertices[ face.c ]; + if ( hasFaceVertexNormal ) { - cb.subVectors( vC, vB ); - ab.subVectors( vA, vB ); - cb.cross( ab ); + var vertexNormals = face.vertexNormals; - vertices[ face.a ].add( cb ); - vertices[ face.b ].add( cb ); - vertices[ face.c ].add( cb ); + faces.push( + getNormalIndex( vertexNormals[ 0 ] ), + getNormalIndex( vertexNormals[ 1 ] ), + getNormalIndex( vertexNormals[ 2 ] ) + ); } - } else { + if ( hasFaceColor ) { - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + faces.push( getColorIndex( face.color ) ); - face = this.faces[ f ]; + } - vertices[ face.a ].add( face.normal ); - vertices[ face.b ].add( face.normal ); - vertices[ face.c ].add( face.normal ); + if ( hasFaceVertexColor ) { + + var vertexColors = face.vertexColors; + + faces.push( + getColorIndex( vertexColors[ 0 ] ), + getColorIndex( vertexColors[ 1 ] ), + getColorIndex( vertexColors[ 2 ] ) + ); } } - for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { + function setBit( value, position, enabled ) { - vertices[ v ].normalize(); + return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position) ); } - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - - face = this.faces[ f ]; - - face.vertexNormals[ 0 ] = vertices[ face.a ].clone(); - face.vertexNormals[ 1 ] = vertices[ face.b ].clone(); - face.vertexNormals[ 2 ] = vertices[ face.c ].clone(); + function getNormalIndex( normal ) { - } + var hash = normal.x.toString() + normal.y.toString() + normal.z.toString(); - }, + if ( normalsHash[ hash ] !== undefined ) { - computeMorphNormals: function () { + return normalsHash[ hash ]; - var i, il, f, fl, face; + } - // save original normals - // - create temp variables on first access - // otherwise just copy (for faster repeated calls) + normalsHash[ hash ] = normals.length / 3; + normals.push( normal.x, normal.y, normal.z ); - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + return normalsHash[ hash ]; - face = this.faces[ f ]; + } - if ( ! face.__originalFaceNormal ) { + function getColorIndex( color ) { - face.__originalFaceNormal = face.normal.clone(); + var hash = color.r.toString() + color.g.toString() + color.b.toString(); - } else { + if ( colorsHash[ hash ] !== undefined ) { - face.__originalFaceNormal.copy( face.normal ); + return colorsHash[ hash ]; } - if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = []; + colorsHash[ hash ] = colors.length; + colors.push( color.getHex() ); - for ( i = 0, il = face.vertexNormals.length; i < il; i ++ ) { + return colorsHash[ hash ]; - if ( ! face.__originalVertexNormals[ i ] ) { + } - face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone(); + function getUvIndex( uv ) { - } else { + var hash = uv.x.toString() + uv.y.toString(); - face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] ); + if ( uvsHash[ hash ] !== undefined ) { - } + return uvsHash[ hash ]; } - } - - // use temp geometry to compute face and vertex normals for each morph - - var tmpGeo = new THREE.Geometry(); - tmpGeo.faces = this.faces; + uvsHash[ hash ] = uvs.length / 2; + uvs.push( uv.x, uv.y ); - for ( i = 0, il = this.morphTargets.length; i < il; i ++ ) { - - // create on first access + return uvsHash[ hash ]; - if ( ! this.morphNormals[ i ] ) { + } - this.morphNormals[ i ] = {}; - this.morphNormals[ i ].faceNormals = []; - this.morphNormals[ i ].vertexNormals = []; + output.data = {}; - var dstNormalsFace = this.morphNormals[ i ].faceNormals; - var dstNormalsVertex = this.morphNormals[ i ].vertexNormals; + output.data.vertices = vertices; + output.data.normals = normals; + if ( colors.length > 0 ) output.data.colors = colors; + if ( uvs.length > 0 ) output.data.uvs = [ uvs ]; // temporal backward compatibility + output.data.faces = faces; - var faceNormal, vertexNormals; + // - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + return output; - faceNormal = new THREE.Vector3(); - vertexNormals = { a: new THREE.Vector3(), b: new THREE.Vector3(), c: new THREE.Vector3() }; + }, - dstNormalsFace.push( faceNormal ); - dstNormalsVertex.push( vertexNormals ); + clone: function () { - } + var geometry = new THREE.Geometry(); - } + var vertices = this.vertices; - var morphNormals = this.morphNormals[ i ]; + for ( var i = 0, il = vertices.length; i < il; i ++ ) { - // set vertices to morph target + geometry.vertices.push( vertices[ i ].clone() ); - tmpGeo.vertices = this.morphTargets[ i ].vertices; + } - // compute morph normals + var faces = this.faces; - tmpGeo.computeFaceNormals(); - tmpGeo.computeVertexNormals(); + for ( var i = 0, il = faces.length; i < il; i ++ ) { - // store morph normals + geometry.faces.push( faces[ i ].clone() ); - var faceNormal, vertexNormals; + } - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + var uvs = this.faceVertexUvs[ 0 ]; - face = this.faces[ f ]; + for ( var i = 0, il = uvs.length; i < il; i ++ ) { - faceNormal = morphNormals.faceNormals[ f ]; - vertexNormals = morphNormals.vertexNormals[ f ]; + var uv = uvs[ i ], uvCopy = []; - faceNormal.copy( face.normal ); + for ( var j = 0, jl = uv.length; j < jl; j ++ ) { - vertexNormals.a.copy( face.vertexNormals[ 0 ] ); - vertexNormals.b.copy( face.vertexNormals[ 1 ] ); - vertexNormals.c.copy( face.vertexNormals[ 2 ] ); + uvCopy.push( new THREE.Vector2( uv[ j ].x, uv[ j ].y ) ); } + geometry.faceVertexUvs[ 0 ].push( uvCopy ); + } - // restore original normals + return geometry; - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + }, - face = this.faces[ f ]; + dispose: function () { - face.normal = face.__originalFaceNormal; - face.vertexNormals = face.__originalVertexNormals; + this.dispatchEvent( { type: 'dispose' } ); - } + } - }, +}; - computeTangents: function () { +THREE.EventDispatcher.prototype.apply( THREE.Geometry.prototype ); - // based on http://www.terathon.com/code/tangent.html - // tangents go to vertices +THREE.GeometryIdCount = 0; - var f, fl, v, vl, i, il, vertexIndex, - face, uv, vA, vB, vC, uvA, uvB, uvC, - x1, x2, y1, y2, z1, z2, - s1, s2, t1, t2, r, t, test, - tan1 = [], tan2 = [], - sdir = new THREE.Vector3(), tdir = new THREE.Vector3(), - tmp = new THREE.Vector3(), tmp2 = new THREE.Vector3(), - n = new THREE.Vector3(), w; +// File:src/cameras/Camera.js - for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { +/** + * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + * @author WestLangley / http://github.com/WestLangley +*/ - tan1[ v ] = new THREE.Vector3(); - tan2[ v ] = new THREE.Vector3(); +THREE.Camera = function () { - } + THREE.Object3D.call( this ); - function handleTriangle( context, a, b, c, ua, ub, uc ) { + this.type = 'Camera'; - vA = context.vertices[ a ]; - vB = context.vertices[ b ]; - vC = context.vertices[ c ]; + this.matrixWorldInverse = new THREE.Matrix4(); + this.projectionMatrix = new THREE.Matrix4(); - uvA = uv[ ua ]; - uvB = uv[ ub ]; - uvC = uv[ uc ]; +}; - x1 = vB.x - vA.x; - x2 = vC.x - vA.x; - y1 = vB.y - vA.y; - y2 = vC.y - vA.y; - z1 = vB.z - vA.z; - z2 = vC.z - vA.z; +THREE.Camera.prototype = Object.create( THREE.Object3D.prototype ); - s1 = uvB.x - uvA.x; - s2 = uvC.x - uvA.x; - t1 = uvB.y - uvA.y; - t2 = uvC.y - uvA.y; +THREE.Camera.prototype.getWorldDirection = function () { - r = 1.0 / ( s1 * t2 - s2 * t1 ); - sdir.set( ( t2 * x1 - t1 * x2 ) * r, - ( t2 * y1 - t1 * y2 ) * r, - ( t2 * z1 - t1 * z2 ) * r ); - tdir.set( ( s1 * x2 - s2 * x1 ) * r, - ( s1 * y2 - s2 * y1 ) * r, - ( s1 * z2 - s2 * z1 ) * r ); + var quaternion = new THREE.Quaternion(); - tan1[ a ].add( sdir ); - tan1[ b ].add( sdir ); - tan1[ c ].add( sdir ); + return function ( optionalTarget ) { - tan2[ a ].add( tdir ); - tan2[ b ].add( tdir ); - tan2[ c ].add( tdir ); + var result = optionalTarget || new THREE.Vector3(); - } + this.getWorldQuaternion( quaternion ); - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + return result.set( 0, 0, - 1 ).applyQuaternion( quaternion ); - face = this.faces[ f ]; - uv = this.faceVertexUvs[ 0 ][ f ]; // use UV layer 0 for tangents + } - handleTriangle( this, face.a, face.b, face.c, 0, 1, 2 ); +}(); - } +THREE.Camera.prototype.lookAt = function () { - var faceIndex = [ 'a', 'b', 'c', 'd' ]; + // This routine does not support cameras with rotated and/or translated parent(s) - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + var m1 = new THREE.Matrix4(); - face = this.faces[ f ]; + return function ( vector ) { - for ( i = 0; i < Math.min( face.vertexNormals.length, 3 ); i ++ ) { + m1.lookAt( this.position, vector, this.up ); - n.copy( face.vertexNormals[ i ] ); + this.quaternion.setFromRotationMatrix( m1 ); - vertexIndex = face[ faceIndex[ i ] ]; + }; - t = tan1[ vertexIndex ]; +}(); - // Gram-Schmidt orthogonalize +THREE.Camera.prototype.clone = function ( camera ) { - tmp.copy( t ); - tmp.sub( n.multiplyScalar( n.dot( t ) ) ).normalize(); + if ( camera === undefined ) camera = new THREE.Camera(); - // Calculate handedness + THREE.Object3D.prototype.clone.call( this, camera ); - tmp2.crossVectors( face.vertexNormals[ i ], t ); - test = tmp2.dot( tan2[ vertexIndex ] ); - w = ( test < 0.0 ) ? - 1.0 : 1.0; + camera.matrixWorldInverse.copy( this.matrixWorldInverse ); + camera.projectionMatrix.copy( this.projectionMatrix ); - face.vertexTangents[ i ] = new THREE.Vector4( tmp.x, tmp.y, tmp.z, w ); + return camera; +}; - } +// File:src/cameras/CubeCamera.js - } +/** + * Camera for rendering cube maps + * - renders scene into axis-aligned cube + * + * @author alteredq / http://alteredqualia.com/ + */ - this.hasTangents = true; +THREE.CubeCamera = function ( near, far, cubeResolution ) { - }, + THREE.Object3D.call( this ); - computeLineDistances: function () { + this.type = 'CubeCamera'; - var d = 0; - var vertices = this.vertices; + var fov = 90, aspect = 1; - for ( var i = 0, il = vertices.length; i < il; i ++ ) { + var cameraPX = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraPX.up.set( 0, - 1, 0 ); + cameraPX.lookAt( new THREE.Vector3( 1, 0, 0 ) ); + this.add( cameraPX ); - if ( i > 0 ) { + var cameraNX = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraNX.up.set( 0, - 1, 0 ); + cameraNX.lookAt( new THREE.Vector3( - 1, 0, 0 ) ); + this.add( cameraNX ); - d += vertices[ i ].distanceTo( vertices[ i - 1 ] ); + var cameraPY = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraPY.up.set( 0, 0, 1 ); + cameraPY.lookAt( new THREE.Vector3( 0, 1, 0 ) ); + this.add( cameraPY ); - } + var cameraNY = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraNY.up.set( 0, 0, - 1 ); + cameraNY.lookAt( new THREE.Vector3( 0, - 1, 0 ) ); + this.add( cameraNY ); - this.lineDistances[ i ] = d; + var cameraPZ = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraPZ.up.set( 0, - 1, 0 ); + cameraPZ.lookAt( new THREE.Vector3( 0, 0, 1 ) ); + this.add( cameraPZ ); - } + var cameraNZ = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraNZ.up.set( 0, - 1, 0 ); + cameraNZ.lookAt( new THREE.Vector3( 0, 0, - 1 ) ); + this.add( cameraNZ ); - }, + this.renderTarget = new THREE.WebGLRenderTargetCube( cubeResolution, cubeResolution, { format: THREE.RGBFormat, magFilter: THREE.LinearFilter, minFilter: THREE.LinearFilter } ); - computeBoundingBox: function () { + this.updateCubeMap = function ( renderer, scene ) { - if ( this.boundingBox === null ) { + var renderTarget = this.renderTarget; + var generateMipmaps = renderTarget.generateMipmaps; - this.boundingBox = new THREE.Box3(); + renderTarget.generateMipmaps = false; - } + renderTarget.activeCubeFace = 0; + renderer.render( scene, cameraPX, renderTarget ); - this.boundingBox.setFromPoints( this.vertices ); + renderTarget.activeCubeFace = 1; + renderer.render( scene, cameraNX, renderTarget ); - }, + renderTarget.activeCubeFace = 2; + renderer.render( scene, cameraPY, renderTarget ); - computeBoundingSphere: function () { + renderTarget.activeCubeFace = 3; + renderer.render( scene, cameraNY, renderTarget ); - if ( this.boundingSphere === null ) { + renderTarget.activeCubeFace = 4; + renderer.render( scene, cameraPZ, renderTarget ); - this.boundingSphere = new THREE.Sphere(); + renderTarget.generateMipmaps = generateMipmaps; - } + renderTarget.activeCubeFace = 5; + renderer.render( scene, cameraNZ, renderTarget ); - this.boundingSphere.setFromPoints( this.vertices ); + }; - }, +}; - merge: function ( geometry, matrix, materialIndexOffset ) { +THREE.CubeCamera.prototype = Object.create( THREE.Object3D.prototype ); - if ( geometry instanceof THREE.Geometry === false ) { +// File:src/cameras/OrthographicCamera.js - console.error( 'THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry ); - return; +/** + * @author alteredq / http://alteredqualia.com/ + */ - } +THREE.OrthographicCamera = function ( left, right, top, bottom, near, far ) { - var normalMatrix, - vertexOffset = this.vertices.length, - uvPosition = this.faceVertexUvs[ 0 ].length, - vertices1 = this.vertices, - vertices2 = geometry.vertices, - faces1 = this.faces, - faces2 = geometry.faces, - uvs1 = this.faceVertexUvs[ 0 ], - uvs2 = geometry.faceVertexUvs[ 0 ]; + THREE.Camera.call( this ); - if ( materialIndexOffset === undefined ) materialIndexOffset = 0; + this.type = 'OrthographicCamera'; - if ( matrix !== undefined ) { + this.zoom = 1; - normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); + this.left = left; + this.right = right; + this.top = top; + this.bottom = bottom; - } + this.near = ( near !== undefined ) ? near : 0.1; + this.far = ( far !== undefined ) ? far : 2000; - // vertices + this.updateProjectionMatrix(); - for ( var i = 0, il = vertices2.length; i < il; i ++ ) { +}; - var vertex = vertices2[ i ]; +THREE.OrthographicCamera.prototype = Object.create( THREE.Camera.prototype ); - var vertexCopy = vertex.clone(); +THREE.OrthographicCamera.prototype.updateProjectionMatrix = function () { - if ( matrix !== undefined ) vertexCopy.applyMatrix4( matrix ); + var dx = ( this.right - this.left ) / ( 2 * this.zoom ); + var dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); + var cx = ( this.right + this.left ) / 2; + var cy = ( this.top + this.bottom ) / 2; - vertices1.push( vertexCopy ); + this.projectionMatrix.makeOrthographic( cx - dx, cx + dx, cy + dy, cy - dy, this.near, this.far ); - } +}; - // faces +THREE.OrthographicCamera.prototype.clone = function () { - for ( i = 0, il = faces2.length; i < il; i ++ ) { + var camera = new THREE.OrthographicCamera(); - var face = faces2[ i ], faceCopy, normal, color, - faceVertexNormals = face.vertexNormals, - faceVertexColors = face.vertexColors; + THREE.Camera.prototype.clone.call( this, camera ); - faceCopy = new THREE.Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset ); - faceCopy.normal.copy( face.normal ); + camera.zoom = this.zoom; - if ( normalMatrix !== undefined ) { + camera.left = this.left; + camera.right = this.right; + camera.top = this.top; + camera.bottom = this.bottom; - faceCopy.normal.applyMatrix3( normalMatrix ).normalize(); + camera.near = this.near; + camera.far = this.far; - } + camera.projectionMatrix.copy( this.projectionMatrix ); - for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) { + return camera; +}; - normal = faceVertexNormals[ j ].clone(); +// File:src/cameras/PerspectiveCamera.js - if ( normalMatrix !== undefined ) { +/** + * @author mrdoob / http://mrdoob.com/ + * @author greggman / http://games.greggman.com/ + * @author zz85 / http://www.lab4games.net/zz85/blog + */ - normal.applyMatrix3( normalMatrix ).normalize(); +THREE.PerspectiveCamera = function ( fov, aspect, near, far ) { - } + THREE.Camera.call( this ); - faceCopy.vertexNormals.push( normal ); + this.type = 'PerspectiveCamera'; - } + this.zoom = 1; - faceCopy.color.copy( face.color ); + this.fov = fov !== undefined ? fov : 50; + this.aspect = aspect !== undefined ? aspect : 1; + this.near = near !== undefined ? near : 0.1; + this.far = far !== undefined ? far : 2000; - for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) { + this.updateProjectionMatrix(); - color = faceVertexColors[ j ]; - faceCopy.vertexColors.push( color.clone() ); +}; - } +THREE.PerspectiveCamera.prototype = Object.create( THREE.Camera.prototype ); - faceCopy.materialIndex = face.materialIndex + materialIndexOffset; - faces1.push( faceCopy ); +/** + * Uses Focal Length (in mm) to estimate and set FOV + * 35mm (fullframe) camera is used if frame size is not specified; + * Formula based on http://www.bobatkins.com/photography/technical/field_of_view.html + */ - } +THREE.PerspectiveCamera.prototype.setLens = function ( focalLength, frameHeight ) { - // uvs + if ( frameHeight === undefined ) frameHeight = 24; - for ( i = 0, il = uvs2.length; i < il; i ++ ) { - - var uv = uvs2[ i ], uvCopy = []; + this.fov = 2 * THREE.Math.radToDeg( Math.atan( frameHeight / ( focalLength * 2 ) ) ); + this.updateProjectionMatrix(); - if ( uv === undefined ) { +} - continue; - } +/** + * Sets an offset in a larger frustum. This is useful for multi-window or + * multi-monitor/multi-machine setups. + * + * For example, if you have 3x2 monitors and each monitor is 1920x1080 and + * the monitors are in grid like this + * + * +---+---+---+ + * | A | B | C | + * +---+---+---+ + * | D | E | F | + * +---+---+---+ + * + * then for each monitor you would call it like this + * + * var w = 1920; + * var h = 1080; + * var fullWidth = w * 3; + * var fullHeight = h * 2; + * + * --A-- + * camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); + * --B-- + * camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); + * --C-- + * camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); + * --D-- + * camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); + * --E-- + * camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); + * --F-- + * camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); + * + * Note there is no reason monitors have to be the same size or in a grid. + */ - for ( var j = 0, jl = uv.length; j < jl; j ++ ) { +THREE.PerspectiveCamera.prototype.setViewOffset = function ( fullWidth, fullHeight, x, y, width, height ) { - uvCopy.push( new THREE.Vector2( uv[ j ].x, uv[ j ].y ) ); + this.fullWidth = fullWidth; + this.fullHeight = fullHeight; + this.x = x; + this.y = y; + this.width = width; + this.height = height; - } + this.updateProjectionMatrix(); - uvs1.push( uvCopy ); +}; - } - }, +THREE.PerspectiveCamera.prototype.updateProjectionMatrix = function () { - /* - * Checks for duplicate vertices with hashmap. - * Duplicated vertices are removed - * and faces' vertices are updated. - */ + var fov = THREE.Math.radToDeg( 2 * Math.atan( Math.tan( THREE.Math.degToRad( this.fov ) * 0.5 ) / this.zoom ) ); - mergeVertices: function () { + if ( this.fullWidth ) { - var verticesMap = {}; // Hashmap for looking up vertice by position coordinates (and making sure they are unique) - var unique = [], changes = []; + var aspect = this.fullWidth / this.fullHeight; + var top = Math.tan( THREE.Math.degToRad( fov * 0.5 ) ) * this.near; + var bottom = - top; + var left = aspect * bottom; + var right = aspect * top; + var width = Math.abs( right - left ); + var height = Math.abs( top - bottom ); - var v, key; - var precisionPoints = 4; // number of decimal points, eg. 4 for epsilon of 0.0001 - var precision = Math.pow( 10, precisionPoints ); - var i,il, face; - var indices, k, j, jl, u; + this.projectionMatrix.makeFrustum( + left + this.x * width / this.fullWidth, + left + ( this.x + this.width ) * width / this.fullWidth, + top - ( this.y + this.height ) * height / this.fullHeight, + top - this.y * height / this.fullHeight, + this.near, + this.far + ); - for ( i = 0, il = this.vertices.length; i < il; i ++ ) { + } else { - v = this.vertices[ i ]; - key = Math.round( v.x * precision ) + '_' + Math.round( v.y * precision ) + '_' + Math.round( v.z * precision ); + this.projectionMatrix.makePerspective( fov, this.aspect, this.near, this.far ); - if ( verticesMap[ key ] === undefined ) { + } - verticesMap[ key ] = i; - unique.push( this.vertices[ i ] ); - changes[ i ] = unique.length - 1; +}; - } else { +THREE.PerspectiveCamera.prototype.clone = function () { - //console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]); - changes[ i ] = changes[ verticesMap[ key ] ]; + var camera = new THREE.PerspectiveCamera(); - } + THREE.Camera.prototype.clone.call( this, camera ); - }; + camera.zoom = this.zoom; + camera.fov = this.fov; + camera.aspect = this.aspect; + camera.near = this.near; + camera.far = this.far; - // if faces are completely degenerate after merging vertices, we - // have to remove them from the geometry. - var faceIndicesToRemove = []; + camera.projectionMatrix.copy( this.projectionMatrix ); - for ( i = 0, il = this.faces.length; i < il; i ++ ) { + return camera; - face = this.faces[ i ]; +}; - face.a = changes[ face.a ]; - face.b = changes[ face.b ]; - face.c = changes[ face.c ]; +// File:src/lights/Light.js - indices = [ face.a, face.b, face.c ]; +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ - var dupIndex = - 1; +THREE.Light = function ( color ) { - // if any duplicate vertices are found in a Face3 - // we have to remove the face as nothing can be saved - for ( var n = 0; n < 3; n ++ ) { - if ( indices[ n ] == indices[ ( n + 1 ) % 3 ] ) { + THREE.Object3D.call( this ); - dupIndex = n; - faceIndicesToRemove.push( i ); - break; + this.type = 'Light'; + + this.color = new THREE.Color( color ); - } - } +}; - } +THREE.Light.prototype = Object.create( THREE.Object3D.prototype ); - for ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) { - var idx = faceIndicesToRemove[ i ]; +THREE.Light.prototype.clone = function ( light ) { - this.faces.splice( idx, 1 ); + if ( light === undefined ) light = new THREE.Light(); - for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) { + THREE.Object3D.prototype.clone.call( this, light ); - this.faceVertexUvs[ j ].splice( idx, 1 ); + light.color.copy( this.color ); - } + return light; - } +}; - // Use unique set of vertices +// File:src/lights/AmbientLight.js - var diff = this.vertices.length - unique.length; - this.vertices = unique; - return diff; +/** + * @author mrdoob / http://mrdoob.com/ + */ - }, +THREE.AmbientLight = function ( color ) { - // Geometry splitting + THREE.Light.call( this, color ); - makeGroups: ( function () { + this.type = 'AmbientLight'; - var geometryGroupCounter = 0; +}; - return function ( usesFaceMaterial, maxVerticesInGroup ) { +THREE.AmbientLight.prototype = Object.create( THREE.Light.prototype ); - var f, fl, face, materialIndex, - groupHash, hash_map = {},geometryGroup; +THREE.AmbientLight.prototype.clone = function () { - var numMorphTargets = this.morphTargets.length; - var numMorphNormals = this.morphNormals.length; + var light = new THREE.AmbientLight(); - this.geometryGroups = {}; - this.geometryGroupsList = []; + THREE.Light.prototype.clone.call( this, light ); - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + return light; - face = this.faces[ f ]; - materialIndex = usesFaceMaterial ? face.materialIndex : 0; +}; - if ( ! ( materialIndex in hash_map ) ) { +// File:src/lights/AreaLight.js - hash_map[ materialIndex ] = { 'hash': materialIndex, 'counter': 0 }; +/** + * @author MPanknin / http://www.redplant.de/ + * @author alteredq / http://alteredqualia.com/ + */ - } +THREE.AreaLight = function ( color, intensity ) { - groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter; + THREE.Light.call( this, color ); - if ( ! ( groupHash in this.geometryGroups ) ) { + this.type = 'AreaLight'; - geometryGroup = { 'id': geometryGroupCounter++, 'faces3': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets, 'numMorphNormals': numMorphNormals }; - this.geometryGroups[ groupHash ] = geometryGroup; - this.geometryGroupsList.push(geometryGroup); - } + this.normal = new THREE.Vector3( 0, - 1, 0 ); + this.right = new THREE.Vector3( 1, 0, 0 ); - if ( this.geometryGroups[ groupHash ].vertices + 3 > maxVerticesInGroup ) { + this.intensity = ( intensity !== undefined ) ? intensity : 1; - hash_map[ materialIndex ].counter += 1; - groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter; + this.width = 1.0; + this.height = 1.0; - if ( ! ( groupHash in this.geometryGroups ) ) { + this.constantAttenuation = 1.5; + this.linearAttenuation = 0.5; + this.quadraticAttenuation = 0.1; - geometryGroup = { 'id': geometryGroupCounter++, 'faces3': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets, 'numMorphNormals': numMorphNormals }; - this.geometryGroups[ groupHash ] = geometryGroup; - this.geometryGroupsList.push(geometryGroup); - - } +}; - } +THREE.AreaLight.prototype = Object.create( THREE.Light.prototype ); - this.geometryGroups[ groupHash ].faces3.push( f ); - this.geometryGroups[ groupHash ].vertices += 3; - } +// File:src/lights/DirectionalLight.js - }; +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ - } )(), +THREE.DirectionalLight = function ( color, intensity ) { - clone: function () { + THREE.Light.call( this, color ); - var geometry = new THREE.Geometry(); + this.type = 'DirectionalLight'; - var vertices = this.vertices; + this.position.set( 0, 1, 0 ); + this.target = new THREE.Object3D(); - for ( var i = 0, il = vertices.length; i < il; i ++ ) { + this.intensity = ( intensity !== undefined ) ? intensity : 1; - geometry.vertices.push( vertices[ i ].clone() ); + this.castShadow = false; + this.onlyShadow = false; - } + // - var faces = this.faces; + this.shadowCameraNear = 50; + this.shadowCameraFar = 5000; - for ( var i = 0, il = faces.length; i < il; i ++ ) { + this.shadowCameraLeft = - 500; + this.shadowCameraRight = 500; + this.shadowCameraTop = 500; + this.shadowCameraBottom = - 500; - geometry.faces.push( faces[ i ].clone() ); + this.shadowCameraVisible = false; - } + this.shadowBias = 0; + this.shadowDarkness = 0.5; - var uvs = this.faceVertexUvs[ 0 ]; + this.shadowMapWidth = 512; + this.shadowMapHeight = 512; - for ( var i = 0, il = uvs.length; i < il; i ++ ) { + // - var uv = uvs[ i ], uvCopy = []; + this.shadowCascade = false; - for ( var j = 0, jl = uv.length; j < jl; j ++ ) { + this.shadowCascadeOffset = new THREE.Vector3( 0, 0, - 1000 ); + this.shadowCascadeCount = 2; - uvCopy.push( new THREE.Vector2( uv[ j ].x, uv[ j ].y ) ); + this.shadowCascadeBias = [ 0, 0, 0 ]; + this.shadowCascadeWidth = [ 512, 512, 512 ]; + this.shadowCascadeHeight = [ 512, 512, 512 ]; - } + this.shadowCascadeNearZ = [ - 1.000, 0.990, 0.998 ]; + this.shadowCascadeFarZ = [ 0.990, 0.998, 1.000 ]; - geometry.faceVertexUvs[ 0 ].push( uvCopy ); + this.shadowCascadeArray = []; - } + // - return geometry; + this.shadowMap = null; + this.shadowMapSize = null; + this.shadowCamera = null; + this.shadowMatrix = null; - }, +}; - dispose: function () { +THREE.DirectionalLight.prototype = Object.create( THREE.Light.prototype ); - this.dispatchEvent( { type: 'dispose' } ); +THREE.DirectionalLight.prototype.clone = function () { - } + var light = new THREE.DirectionalLight(); -}; + THREE.Light.prototype.clone.call( this, light ); -THREE.EventDispatcher.prototype.apply( THREE.Geometry.prototype ); + light.target = this.target.clone(); -THREE.GeometryIdCount = 0; + light.intensity = this.intensity; -// File:src/cameras/Camera.js + light.castShadow = this.castShadow; + light.onlyShadow = this.onlyShadow; -/** - * @author mrdoob / http://mrdoob.com/ - * @author mikael emtinger / http://gomo.se/ - * @author WestLangley / http://github.com/WestLangley -*/ + // -THREE.Camera = function () { + light.shadowCameraNear = this.shadowCameraNear; + light.shadowCameraFar = this.shadowCameraFar; - THREE.Object3D.call( this ); + light.shadowCameraLeft = this.shadowCameraLeft; + light.shadowCameraRight = this.shadowCameraRight; + light.shadowCameraTop = this.shadowCameraTop; + light.shadowCameraBottom = this.shadowCameraBottom; - this.matrixWorldInverse = new THREE.Matrix4(); - this.projectionMatrix = new THREE.Matrix4(); + light.shadowCameraVisible = this.shadowCameraVisible; + + light.shadowBias = this.shadowBias; + light.shadowDarkness = this.shadowDarkness; + + light.shadowMapWidth = this.shadowMapWidth; + light.shadowMapHeight = this.shadowMapHeight; + + // + + light.shadowCascade = this.shadowCascade; + + light.shadowCascadeOffset.copy( this.shadowCascadeOffset ); + light.shadowCascadeCount = this.shadowCascadeCount; + + light.shadowCascadeBias = this.shadowCascadeBias.slice( 0 ); + light.shadowCascadeWidth = this.shadowCascadeWidth.slice( 0 ); + light.shadowCascadeHeight = this.shadowCascadeHeight.slice( 0 ); + + light.shadowCascadeNearZ = this.shadowCascadeNearZ.slice( 0 ); + light.shadowCascadeFarZ = this.shadowCascadeFarZ.slice( 0 ); + + return light; }; -THREE.Camera.prototype = Object.create( THREE.Object3D.prototype ); +// File:src/lights/HemisphereLight.js -THREE.Camera.prototype.lookAt = function () { +/** + * @author alteredq / http://alteredqualia.com/ + */ - // This routine does not support cameras with rotated and/or translated parent(s) +THREE.HemisphereLight = function ( skyColor, groundColor, intensity ) { - var m1 = new THREE.Matrix4(); + THREE.Light.call( this, skyColor ); - return function ( vector ) { + this.type = 'HemisphereLight'; - m1.lookAt( this.position, vector, this.up ); + this.position.set( 0, 100, 0 ); - this.quaternion.setFromRotationMatrix( m1 ); + this.groundColor = new THREE.Color( groundColor ); + this.intensity = ( intensity !== undefined ) ? intensity : 1; - }; +}; -}(); +THREE.HemisphereLight.prototype = Object.create( THREE.Light.prototype ); -THREE.Camera.prototype.clone = function ( camera ) { +THREE.HemisphereLight.prototype.clone = function () { - if ( camera === undefined ) camera = new THREE.Camera(); + var light = new THREE.HemisphereLight(); - THREE.Object3D.prototype.clone.call( this, camera ); + THREE.Light.prototype.clone.call( this, light ); - camera.matrixWorldInverse.copy( this.matrixWorldInverse ); - camera.projectionMatrix.copy( this.projectionMatrix ); + light.groundColor.copy( this.groundColor ); + light.intensity = this.intensity; + + return light; - return camera; }; -// File:src/cameras/CubeCamera.js +// File:src/lights/PointLight.js /** - * Camera for rendering cube maps - * - renders scene into axis-aligned cube - * - * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ */ -THREE.CubeCamera = function ( near, far, cubeResolution ) { +THREE.PointLight = function ( color, intensity, distance ) { - THREE.Object3D.call( this ); + THREE.Light.call( this, color ); - var fov = 90, aspect = 1; + this.type = 'PointLight'; - var cameraPX = new THREE.PerspectiveCamera( fov, aspect, near, far ); - cameraPX.up.set( 0, - 1, 0 ); - cameraPX.lookAt( new THREE.Vector3( 1, 0, 0 ) ); - this.add( cameraPX ); + this.intensity = ( intensity !== undefined ) ? intensity : 1; + this.distance = ( distance !== undefined ) ? distance : 0; - var cameraNX = new THREE.PerspectiveCamera( fov, aspect, near, far ); - cameraNX.up.set( 0, - 1, 0 ); - cameraNX.lookAt( new THREE.Vector3( - 1, 0, 0 ) ); - this.add( cameraNX ); +}; - var cameraPY = new THREE.PerspectiveCamera( fov, aspect, near, far ); - cameraPY.up.set( 0, 0, 1 ); - cameraPY.lookAt( new THREE.Vector3( 0, 1, 0 ) ); - this.add( cameraPY ); +THREE.PointLight.prototype = Object.create( THREE.Light.prototype ); - var cameraNY = new THREE.PerspectiveCamera( fov, aspect, near, far ); - cameraNY.up.set( 0, 0, - 1 ); - cameraNY.lookAt( new THREE.Vector3( 0, - 1, 0 ) ); - this.add( cameraNY ); +THREE.PointLight.prototype.clone = function () { - var cameraPZ = new THREE.PerspectiveCamera( fov, aspect, near, far ); - cameraPZ.up.set( 0, - 1, 0 ); - cameraPZ.lookAt( new THREE.Vector3( 0, 0, 1 ) ); - this.add( cameraPZ ); + var light = new THREE.PointLight(); - var cameraNZ = new THREE.PerspectiveCamera( fov, aspect, near, far ); - cameraNZ.up.set( 0, - 1, 0 ); - cameraNZ.lookAt( new THREE.Vector3( 0, 0, - 1 ) ); - this.add( cameraNZ ); + THREE.Light.prototype.clone.call( this, light ); - this.renderTarget = new THREE.WebGLRenderTargetCube( cubeResolution, cubeResolution, { format: THREE.RGBFormat, magFilter: THREE.LinearFilter, minFilter: THREE.LinearFilter } ); + light.intensity = this.intensity; + light.distance = this.distance; - this.updateCubeMap = function ( renderer, scene ) { + return light; - var renderTarget = this.renderTarget; - var generateMipmaps = renderTarget.generateMipmaps; +}; - renderTarget.generateMipmaps = false; +// File:src/lights/SpotLight.js - renderTarget.activeCubeFace = 0; - renderer.render( scene, cameraPX, renderTarget ); +/** + * @author alteredq / http://alteredqualia.com/ + */ - renderTarget.activeCubeFace = 1; - renderer.render( scene, cameraNX, renderTarget ); +THREE.SpotLight = function ( color, intensity, distance, angle, exponent ) { - renderTarget.activeCubeFace = 2; - renderer.render( scene, cameraPY, renderTarget ); + THREE.Light.call( this, color ); - renderTarget.activeCubeFace = 3; - renderer.render( scene, cameraNY, renderTarget ); + this.type = 'SpotLight'; - renderTarget.activeCubeFace = 4; - renderer.render( scene, cameraPZ, renderTarget ); + this.position.set( 0, 1, 0 ); + this.target = new THREE.Object3D(); - renderTarget.generateMipmaps = generateMipmaps; + this.intensity = ( intensity !== undefined ) ? intensity : 1; + this.distance = ( distance !== undefined ) ? distance : 0; + this.angle = ( angle !== undefined ) ? angle : Math.PI / 3; + this.exponent = ( exponent !== undefined ) ? exponent : 10; - renderTarget.activeCubeFace = 5; - renderer.render( scene, cameraNZ, renderTarget ); + this.castShadow = false; + this.onlyShadow = false; - }; + // -}; + this.shadowCameraNear = 50; + this.shadowCameraFar = 5000; + this.shadowCameraFov = 50; -THREE.CubeCamera.prototype = Object.create( THREE.Object3D.prototype ); + this.shadowCameraVisible = false; -// File:src/cameras/OrthographicCamera.js + this.shadowBias = 0; + this.shadowDarkness = 0.5; -/** - * @author alteredq / http://alteredqualia.com/ - */ + this.shadowMapWidth = 512; + this.shadowMapHeight = 512; -THREE.OrthographicCamera = function ( left, right, top, bottom, near, far ) { + // - THREE.Camera.call( this ); + this.shadowMap = null; + this.shadowMapSize = null; + this.shadowCamera = null; + this.shadowMatrix = null; - this.left = left; - this.right = right; - this.top = top; - this.bottom = bottom; +}; - this.near = ( near !== undefined ) ? near : 0.1; - this.far = ( far !== undefined ) ? far : 2000; +THREE.SpotLight.prototype = Object.create( THREE.Light.prototype ); - this.updateProjectionMatrix(); +THREE.SpotLight.prototype.clone = function () { -}; + var light = new THREE.SpotLight(); -THREE.OrthographicCamera.prototype = Object.create( THREE.Camera.prototype ); + THREE.Light.prototype.clone.call( this, light ); -THREE.OrthographicCamera.prototype.updateProjectionMatrix = function () { + light.target = this.target.clone(); - this.projectionMatrix.makeOrthographic( this.left, this.right, this.top, this.bottom, this.near, this.far ); + light.intensity = this.intensity; + light.distance = this.distance; + light.angle = this.angle; + light.exponent = this.exponent; -}; + light.castShadow = this.castShadow; + light.onlyShadow = this.onlyShadow; -THREE.OrthographicCamera.prototype.clone = function () { + // - var camera = new THREE.OrthographicCamera(); + light.shadowCameraNear = this.shadowCameraNear; + light.shadowCameraFar = this.shadowCameraFar; + light.shadowCameraFov = this.shadowCameraFov; - THREE.Camera.prototype.clone.call( this, camera ); + light.shadowCameraVisible = this.shadowCameraVisible; - camera.left = this.left; - camera.right = this.right; - camera.top = this.top; - camera.bottom = this.bottom; + light.shadowBias = this.shadowBias; + light.shadowDarkness = this.shadowDarkness; - camera.near = this.near; - camera.far = this.far; + light.shadowMapWidth = this.shadowMapWidth; + light.shadowMapHeight = this.shadowMapHeight; + + return light; - return camera; }; -// File:src/cameras/PerspectiveCamera.js +// File:src/loaders/Cache.js /** * @author mrdoob / http://mrdoob.com/ - * @author greggman / http://games.greggman.com/ - * @author zz85 / http://www.lab4games.net/zz85/blog */ -THREE.PerspectiveCamera = function ( fov, aspect, near, far ) { +THREE.Cache = function () { - THREE.Camera.call( this ); + this.files = {}; - this.fov = fov !== undefined ? fov : 50; - this.aspect = aspect !== undefined ? aspect : 1; - this.near = near !== undefined ? near : 0.1; - this.far = far !== undefined ? far : 2000; +}; - this.updateProjectionMatrix(); +THREE.Cache.prototype = { -}; + constructor: THREE.Cache, -THREE.PerspectiveCamera.prototype = Object.create( THREE.Camera.prototype ); + add: function ( key, file ) { + // console.log( 'THREE.Cache', 'Adding key:', key ); -/** - * Uses Focal Length (in mm) to estimate and set FOV - * 35mm (fullframe) camera is used if frame size is not specified; - * Formula based on http://www.bobatkins.com/photography/technical/field_of_view.html - */ + this.files[ key ] = file; -THREE.PerspectiveCamera.prototype.setLens = function ( focalLength, frameHeight ) { + }, - if ( frameHeight === undefined ) frameHeight = 24; + get: function ( key ) { - this.fov = 2 * THREE.Math.radToDeg( Math.atan( frameHeight / ( focalLength * 2 ) ) ); - this.updateProjectionMatrix(); + // console.log( 'THREE.Cache', 'Checking key:', key ); -} + return this.files[ key ]; + }, -/** - * Sets an offset in a larger frustum. This is useful for multi-window or - * multi-monitor/multi-machine setups. - * - * For example, if you have 3x2 monitors and each monitor is 1920x1080 and - * the monitors are in grid like this - * - * +---+---+---+ - * | A | B | C | - * +---+---+---+ - * | D | E | F | - * +---+---+---+ - * - * then for each monitor you would call it like this - * - * var w = 1920; - * var h = 1080; - * var fullWidth = w * 3; - * var fullHeight = h * 2; - * - * --A-- - * camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); - * --B-- - * camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); - * --C-- - * camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); - * --D-- - * camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); - * --E-- - * camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); - * --F-- - * camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); - * - * Note there is no reason monitors have to be the same size or in a grid. - */ + remove: function ( key ) { -THREE.PerspectiveCamera.prototype.setViewOffset = function ( fullWidth, fullHeight, x, y, width, height ) { + delete this.files[ key ]; - this.fullWidth = fullWidth; - this.fullHeight = fullHeight; - this.x = x; - this.y = y; - this.width = width; - this.height = height; + }, - this.updateProjectionMatrix(); + clear: function () { -}; + this.files = {} + } -THREE.PerspectiveCamera.prototype.updateProjectionMatrix = function () { +}; - if ( this.fullWidth ) { +// File:src/loaders/Loader.js - var aspect = this.fullWidth / this.fullHeight; - var top = Math.tan( THREE.Math.degToRad( this.fov * 0.5 ) ) * this.near; - var bottom = - top; - var left = aspect * bottom; - var right = aspect * top; - var width = Math.abs( right - left ); - var height = Math.abs( top - bottom ); +/** + * @author alteredq / http://alteredqualia.com/ + */ - this.projectionMatrix.makeFrustum( - left + this.x * width / this.fullWidth, - left + ( this.x + this.width ) * width / this.fullWidth, - top - ( this.y + this.height ) * height / this.fullHeight, - top - this.y * height / this.fullHeight, - this.near, - this.far - ); +THREE.Loader = function ( showStatus ) { - } else { + this.showStatus = showStatus; + this.statusDomElement = showStatus ? THREE.Loader.prototype.addStatusElement() : null; - this.projectionMatrix.makePerspective( this.fov, this.aspect, this.near, this.far ); + this.imageLoader = new THREE.ImageLoader(); - } + this.onLoadStart = function () {}; + this.onLoadProgress = function () {}; + this.onLoadComplete = function () {}; }; -THREE.PerspectiveCamera.prototype.clone = function () { +THREE.Loader.prototype = { - var camera = new THREE.PerspectiveCamera(); + constructor: THREE.Loader, - THREE.Camera.prototype.clone.call( this, camera ); + crossOrigin: undefined, - camera.fov = this.fov; - camera.aspect = this.aspect; - camera.near = this.near; - camera.far = this.far; + addStatusElement: function () { - return camera; -}; + var e = document.createElement( 'div' ); -// File:src/lights/Light.js + e.style.position = 'absolute'; + e.style.right = '0px'; + e.style.top = '0px'; + e.style.fontSize = '0.8em'; + e.style.textAlign = 'left'; + e.style.background = 'rgba(0,0,0,0.25)'; + e.style.color = '#fff'; + e.style.width = '120px'; + e.style.padding = '0.5em 0.5em 0.5em 0.5em'; + e.style.zIndex = 1000; -/** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ + e.innerHTML = 'Loading ...'; -THREE.Light = function ( color ) { + return e; - THREE.Object3D.call( this ); + }, - this.color = new THREE.Color( color ); + updateProgress: function ( progress ) { -}; + var message = 'Loaded '; -THREE.Light.prototype = Object.create( THREE.Object3D.prototype ); + if ( progress.total ) { -THREE.Light.prototype.clone = function ( light ) { + message += ( 100 * progress.loaded / progress.total ).toFixed( 0 ) + '%'; - if ( light === undefined ) light = new THREE.Light(); - THREE.Object3D.prototype.clone.call( this, light ); + } else { - light.color.copy( this.color ); + message += ( progress.loaded / 1024 ).toFixed( 2 ) + ' KB'; - return light; + } -}; + this.statusDomElement.innerHTML = message; -// File:src/lights/AmbientLight.js + }, -/** - * @author mrdoob / http://mrdoob.com/ - */ + extractUrlBase: function ( url ) { -THREE.AmbientLight = function ( color ) { + var parts = url.split( '/' ); - THREE.Light.call( this, color ); + if ( parts.length === 1 ) return './'; -}; + parts.pop(); -THREE.AmbientLight.prototype = Object.create( THREE.Light.prototype ); + return parts.join( '/' ) + '/'; -THREE.AmbientLight.prototype.clone = function () { + }, - var light = new THREE.AmbientLight(); + initMaterials: function ( materials, texturePath ) { - THREE.Light.prototype.clone.call( this, light ); + var array = []; - return light; + for ( var i = 0; i < materials.length; ++ i ) { -}; + array[ i ] = this.createMaterial( materials[ i ], texturePath ); -// File:src/lights/AreaLight.js + } -/** - * @author MPanknin / http://www.redplant.de/ - * @author alteredq / http://alteredqualia.com/ - */ + return array; -THREE.AreaLight = function ( color, intensity ) { + }, - THREE.Light.call( this, color ); + needsTangents: function ( materials ) { - this.normal = new THREE.Vector3( 0, - 1, 0 ); - this.right = new THREE.Vector3( 1, 0, 0 ); + for ( var i = 0, il = materials.length; i < il; i ++ ) { - this.intensity = ( intensity !== undefined ) ? intensity : 1; + var m = materials[ i ]; - this.width = 1.0; - this.height = 1.0; + if ( m instanceof THREE.ShaderMaterial ) return true; - this.constantAttenuation = 1.5; - this.linearAttenuation = 0.5; - this.quadraticAttenuation = 0.1; + } -}; + return false; -THREE.AreaLight.prototype = Object.create( THREE.Light.prototype ); + }, + createMaterial: function ( m, texturePath ) { -// File:src/lights/DirectionalLight.js + var scope = this; -/** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ + function nearest_pow2( n ) { -THREE.DirectionalLight = function ( color, intensity ) { + var l = Math.log( n ) / Math.LN2; + return Math.pow( 2, Math.round( l ) ); - THREE.Light.call( this, color ); + } - this.position.set( 0, 1, 0 ); - this.target = new THREE.Object3D(); + function create_texture( where, name, sourceFile, repeat, offset, wrap, anisotropy ) { - this.intensity = ( intensity !== undefined ) ? intensity : 1; + var fullPath = texturePath + sourceFile; - this.castShadow = false; - this.onlyShadow = false; + var texture; - // + var loader = THREE.Loader.Handlers.get( fullPath ); - this.shadowCameraNear = 50; - this.shadowCameraFar = 5000; + if ( loader !== null ) { - this.shadowCameraLeft = - 500; - this.shadowCameraRight = 500; - this.shadowCameraTop = 500; - this.shadowCameraBottom = - 500; + texture = loader.load( fullPath ); - this.shadowCameraVisible = false; + } else { - this.shadowBias = 0; - this.shadowDarkness = 0.5; + texture = new THREE.Texture(); - this.shadowMapWidth = 512; - this.shadowMapHeight = 512; + loader = scope.imageLoader; + loader.crossOrigin = scope.crossOrigin; + loader.load( fullPath, function ( image ) { - // + if ( THREE.Math.isPowerOfTwo( image.width ) === false || + THREE.Math.isPowerOfTwo( image.height ) === false ) { - this.shadowCascade = false; + var width = nearest_pow2( image.width ); + var height = nearest_pow2( image.height ); - this.shadowCascadeOffset = new THREE.Vector3( 0, 0, - 1000 ); - this.shadowCascadeCount = 2; + var canvas = document.createElement( 'canvas' ); + canvas.width = width; + canvas.height = height; - this.shadowCascadeBias = [ 0, 0, 0 ]; - this.shadowCascadeWidth = [ 512, 512, 512 ]; - this.shadowCascadeHeight = [ 512, 512, 512 ]; + var context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0, width, height ); - this.shadowCascadeNearZ = [ - 1.000, 0.990, 0.998 ]; - this.shadowCascadeFarZ = [ 0.990, 0.998, 1.000 ]; + texture.image = canvas; - this.shadowCascadeArray = []; + } else { - // + texture.image = image; - this.shadowMap = null; - this.shadowMapSize = null; - this.shadowCamera = null; - this.shadowMatrix = null; + } -}; + texture.needsUpdate = true; -THREE.DirectionalLight.prototype = Object.create( THREE.Light.prototype ); + } ); -THREE.DirectionalLight.prototype.clone = function () { + } - var light = new THREE.DirectionalLight(); + texture.sourceFile = sourceFile; - THREE.Light.prototype.clone.call( this, light ); + if ( repeat ) { - light.target = this.target.clone(); + texture.repeat.set( repeat[ 0 ], repeat[ 1 ] ); - light.intensity = this.intensity; + if ( repeat[ 0 ] !== 1 ) texture.wrapS = THREE.RepeatWrapping; + if ( repeat[ 1 ] !== 1 ) texture.wrapT = THREE.RepeatWrapping; - light.castShadow = this.castShadow; - light.onlyShadow = this.onlyShadow; + } - // + if ( offset ) { - light.shadowCameraNear = this.shadowCameraNear; - light.shadowCameraFar = this.shadowCameraFar; + texture.offset.set( offset[ 0 ], offset[ 1 ] ); - light.shadowCameraLeft = this.shadowCameraLeft; - light.shadowCameraRight = this.shadowCameraRight; - light.shadowCameraTop = this.shadowCameraTop; - light.shadowCameraBottom = this.shadowCameraBottom; + } - light.shadowCameraVisible = this.shadowCameraVisible; + if ( wrap ) { - light.shadowBias = this.shadowBias; - light.shadowDarkness = this.shadowDarkness; + var wrapMap = { + 'repeat': THREE.RepeatWrapping, + 'mirror': THREE.MirroredRepeatWrapping + } - light.shadowMapWidth = this.shadowMapWidth; - light.shadowMapHeight = this.shadowMapHeight; + if ( wrapMap[ wrap[ 0 ] ] !== undefined ) texture.wrapS = wrapMap[ wrap[ 0 ] ]; + if ( wrapMap[ wrap[ 1 ] ] !== undefined ) texture.wrapT = wrapMap[ wrap[ 1 ] ]; - // + } - light.shadowCascade = this.shadowCascade; + if ( anisotropy ) { - light.shadowCascadeOffset.copy( this.shadowCascadeOffset ); - light.shadowCascadeCount = this.shadowCascadeCount; + texture.anisotropy = anisotropy; - light.shadowCascadeBias = this.shadowCascadeBias.slice( 0 ); - light.shadowCascadeWidth = this.shadowCascadeWidth.slice( 0 ); - light.shadowCascadeHeight = this.shadowCascadeHeight.slice( 0 ); + } - light.shadowCascadeNearZ = this.shadowCascadeNearZ.slice( 0 ); - light.shadowCascadeFarZ = this.shadowCascadeFarZ.slice( 0 ); + where[ name ] = texture; - return light; + } -}; + function rgb2hex( rgb ) { -// File:src/lights/HemisphereLight.js + return ( rgb[ 0 ] * 255 << 16 ) + ( rgb[ 1 ] * 255 << 8 ) + rgb[ 2 ] * 255; -/** - * @author alteredq / http://alteredqualia.com/ - */ + } -THREE.HemisphereLight = function ( skyColor, groundColor, intensity ) { + // defaults - THREE.Light.call( this, skyColor ); + var mtype = 'MeshLambertMaterial'; + var mpars = { color: 0xeeeeee, opacity: 1.0, map: null, lightMap: null, normalMap: null, bumpMap: null, wireframe: false }; - this.position.set( 0, 100, 0 ); + // parameters from model file - this.groundColor = new THREE.Color( groundColor ); - this.intensity = ( intensity !== undefined ) ? intensity : 1; + if ( m.shading ) { -}; + var shading = m.shading.toLowerCase(); -THREE.HemisphereLight.prototype = Object.create( THREE.Light.prototype ); + if ( shading === 'phong' ) mtype = 'MeshPhongMaterial'; + else if ( shading === 'basic' ) mtype = 'MeshBasicMaterial'; -THREE.HemisphereLight.prototype.clone = function () { + } - var light = new THREE.HemisphereLight(); + if ( m.blending !== undefined && THREE[ m.blending ] !== undefined ) { - THREE.Light.prototype.clone.call( this, light ); + mpars.blending = THREE[ m.blending ]; - light.groundColor.copy( this.groundColor ); - light.intensity = this.intensity; + } - return light; + if ( m.transparent !== undefined || m.opacity < 1.0 ) { -}; + mpars.transparent = m.transparent; -// File:src/lights/PointLight.js + } -/** - * @author mrdoob / http://mrdoob.com/ - */ + if ( m.depthTest !== undefined ) { -THREE.PointLight = function ( color, intensity, distance ) { + mpars.depthTest = m.depthTest; - THREE.Light.call( this, color ); + } - this.intensity = ( intensity !== undefined ) ? intensity : 1; - this.distance = ( distance !== undefined ) ? distance : 0; + if ( m.depthWrite !== undefined ) { -}; + mpars.depthWrite = m.depthWrite; -THREE.PointLight.prototype = Object.create( THREE.Light.prototype ); + } -THREE.PointLight.prototype.clone = function () { + if ( m.visible !== undefined ) { - var light = new THREE.PointLight(); + mpars.visible = m.visible; - THREE.Light.prototype.clone.call( this, light ); + } - light.intensity = this.intensity; - light.distance = this.distance; + if ( m.flipSided !== undefined ) { - return light; + mpars.side = THREE.BackSide; -}; + } -// File:src/lights/SpotLight.js + if ( m.doubleSided !== undefined ) { -/** - * @author alteredq / http://alteredqualia.com/ - */ + mpars.side = THREE.DoubleSide; -THREE.SpotLight = function ( color, intensity, distance, angle, exponent ) { + } - THREE.Light.call( this, color ); + if ( m.wireframe !== undefined ) { - this.position.set( 0, 1, 0 ); - this.target = new THREE.Object3D(); + mpars.wireframe = m.wireframe; - this.intensity = ( intensity !== undefined ) ? intensity : 1; - this.distance = ( distance !== undefined ) ? distance : 0; - this.angle = ( angle !== undefined ) ? angle : Math.PI / 3; - this.exponent = ( exponent !== undefined ) ? exponent : 10; + } - this.castShadow = false; - this.onlyShadow = false; + if ( m.vertexColors !== undefined ) { - // + if ( m.vertexColors === 'face' ) { - this.shadowCameraNear = 50; - this.shadowCameraFar = 5000; - this.shadowCameraFov = 50; + mpars.vertexColors = THREE.FaceColors; - this.shadowCameraVisible = false; + } else if ( m.vertexColors ) { - this.shadowBias = 0; - this.shadowDarkness = 0.5; + mpars.vertexColors = THREE.VertexColors; - this.shadowMapWidth = 512; - this.shadowMapHeight = 512; + } - // + } - this.shadowMap = null; - this.shadowMapSize = null; - this.shadowCamera = null; - this.shadowMatrix = null; + // colors -}; + if ( m.colorDiffuse ) { -THREE.SpotLight.prototype = Object.create( THREE.Light.prototype ); + mpars.color = rgb2hex( m.colorDiffuse ); -THREE.SpotLight.prototype.clone = function () { + } else if ( m.DbgColor ) { - var light = new THREE.SpotLight(); + mpars.color = m.DbgColor; - THREE.Light.prototype.clone.call( this, light ); + } - light.target = this.target.clone(); + if ( m.colorSpecular ) { - light.intensity = this.intensity; - light.distance = this.distance; - light.angle = this.angle; - light.exponent = this.exponent; + mpars.specular = rgb2hex( m.colorSpecular ); - light.castShadow = this.castShadow; - light.onlyShadow = this.onlyShadow; + } - // + if ( m.colorAmbient ) { - light.shadowCameraNear = this.shadowCameraNear; - light.shadowCameraFar = this.shadowCameraFar; - light.shadowCameraFov = this.shadowCameraFov; + mpars.ambient = rgb2hex( m.colorAmbient ); - light.shadowCameraVisible = this.shadowCameraVisible; + } - light.shadowBias = this.shadowBias; - light.shadowDarkness = this.shadowDarkness; + if ( m.colorEmissive ) { - light.shadowMapWidth = this.shadowMapWidth; - light.shadowMapHeight = this.shadowMapHeight; + mpars.emissive = rgb2hex( m.colorEmissive ); - return light; + } + + // modifiers + + if ( m.transparency ) { + + mpars.opacity = m.transparency; + + } + + if ( m.specularCoef ) { + + mpars.shininess = m.specularCoef; + + } + + // textures + + if ( m.mapDiffuse && texturePath ) { + + create_texture( mpars, 'map', m.mapDiffuse, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy ); + + } + + if ( m.mapLight && texturePath ) { + + create_texture( mpars, 'lightMap', m.mapLight, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy ); + + } + + if ( m.mapBump && texturePath ) { + + create_texture( mpars, 'bumpMap', m.mapBump, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy ); + + } + + if ( m.mapNormal && texturePath ) { + + create_texture( mpars, 'normalMap', m.mapNormal, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy ); + + } + + if ( m.mapSpecular && texturePath ) { + + create_texture( mpars, 'specularMap', m.mapSpecular, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy ); + + } + + if ( m.mapAlpha && texturePath ) { + + create_texture( mpars, 'alphaMap', m.mapAlpha, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy ); + + } + + // + + if ( m.mapBumpScale ) { + + mpars.bumpScale = m.mapBumpScale; + + } + + // special case for normal mapped material + + if ( m.mapNormal ) { + + var shader = THREE.ShaderLib[ 'normalmap' ]; + var uniforms = THREE.UniformsUtils.clone( shader.uniforms ); + + uniforms[ 'tNormal' ].value = mpars.normalMap; + + if ( m.mapNormalFactor ) { + + uniforms[ 'uNormalScale' ].value.set( m.mapNormalFactor, m.mapNormalFactor ); + + } + + if ( mpars.map ) { + + uniforms[ 'tDiffuse' ].value = mpars.map; + uniforms[ 'enableDiffuse' ].value = true; + + } + + if ( mpars.specularMap ) { + + uniforms[ 'tSpecular' ].value = mpars.specularMap; + uniforms[ 'enableSpecular' ].value = true; + + } + + if ( mpars.lightMap ) { + + uniforms[ 'tAO' ].value = mpars.lightMap; + uniforms[ 'enableAO' ].value = true; + + } + + // for the moment don't handle displacement texture + + uniforms[ 'diffuse' ].value.setHex( mpars.color ); + uniforms[ 'specular' ].value.setHex( mpars.specular ); + uniforms[ 'ambient' ].value.setHex( mpars.ambient ); + + uniforms[ 'shininess' ].value = mpars.shininess; + + if ( mpars.opacity !== undefined ) { + + uniforms[ 'opacity' ].value = mpars.opacity; + + } + + var parameters = { fragmentShader: shader.fragmentShader, vertexShader: shader.vertexShader, uniforms: uniforms, lights: true, fog: true }; + var material = new THREE.ShaderMaterial( parameters ); + + if ( mpars.transparent ) { + + material.transparent = true; + + } + + } else { + + var material = new THREE[ mtype ]( mpars ); + + } + + if ( m.DbgName !== undefined ) material.name = m.DbgName; + + return material; + + } }; -// File:src/loaders/Cache.js +THREE.Loader.Handlers = { -/** - * @author mrdoob / http://mrdoob.com/ - */ - -THREE.Cache = function () { - - this.files = {}; - -}; - -THREE.Cache.prototype = { - - constructor: THREE.Cache, - - add: function ( key, file ) { - - // console.log( 'THREE.Cache', 'Adding key:', key ); - - this.files[ key ] = file; - - }, - - get: function ( key ) { - - // console.log( 'THREE.Cache', 'Checking key:', key ); - - return this.files[ key ]; - - }, - - remove: function ( key ) { - - delete this.files[ key ]; - - }, - - clear: function () { - - this.files = {} - - } - -}; + handlers: [], -// File:src/loaders/Loader.js + add: function ( regex, loader ) { + + this.handlers.push( regex, loader ); + + }, + + get: function ( file ) { + + for ( var i = 0, l = this.handlers.length; i < l; i += 2 ) { + + var regex = this.handlers[ i ]; + var loader = this.handlers[ i + 1 ]; + + if ( regex.test( file ) ) { + + return loader; + + } + + } + + return null; + + } -/** - * @author alteredq / http://alteredqualia.com/ - */ - -THREE.Loader = function ( showStatus ) { - - this.showStatus = showStatus; - this.statusDomElement = showStatus ? THREE.Loader.prototype.addStatusElement() : null; - - this.imageLoader = new THREE.ImageLoader(); - - this.onLoadStart = function () {}; - this.onLoadProgress = function () {}; - this.onLoadComplete = function () {}; - -}; - -THREE.Loader.prototype = { - - constructor: THREE.Loader, - - crossOrigin: undefined, - - addStatusElement: function () { - - var e = document.createElement( 'div' ); - - e.style.position = 'absolute'; - e.style.right = '0px'; - e.style.top = '0px'; - e.style.fontSize = '0.8em'; - e.style.textAlign = 'left'; - e.style.background = 'rgba(0,0,0,0.25)'; - e.style.color = '#fff'; - e.style.width = '120px'; - e.style.padding = '0.5em 0.5em 0.5em 0.5em'; - e.style.zIndex = 1000; - - e.innerHTML = 'Loading ...'; - - return e; - - }, - - updateProgress: function ( progress ) { - - var message = 'Loaded '; - - if ( progress.total ) { - - message += ( 100 * progress.loaded / progress.total ).toFixed( 0 ) + '%'; - - - } else { - - message += ( progress.loaded / 1024 ).toFixed( 2 ) + ' KB'; - - } - - this.statusDomElement.innerHTML = message; - - }, - - extractUrlBase: function ( url ) { - - var parts = url.split( '/' ); - - if ( parts.length === 1 ) return './'; - - parts.pop(); - - return parts.join( '/' ) + '/'; - - }, - - initMaterials: function ( materials, texturePath ) { - - var array = []; - - for ( var i = 0; i < materials.length; ++ i ) { - - array[ i ] = this.createMaterial( materials[ i ], texturePath ); - - } - - return array; - - }, - - needsTangents: function ( materials ) { - - for ( var i = 0, il = materials.length; i < il; i ++ ) { - - var m = materials[ i ]; - - if ( m instanceof THREE.ShaderMaterial ) return true; - - } - - return false; - - }, - - createMaterial: function ( m, texturePath ) { - - var scope = this; - - function nearest_pow2( n ) { - - var l = Math.log( n ) / Math.LN2; - return Math.pow( 2, Math.round( l ) ); - - } - - function create_texture( where, name, sourceFile, repeat, offset, wrap, anisotropy ) { - - var fullPath = texturePath + sourceFile; - - var texture; - - var loader = THREE.Loader.Handlers.get( fullPath ); - - if ( loader !== null ) { - - texture = loader.load( fullPath ); - - } else { - - texture = new THREE.Texture(); - - loader = scope.imageLoader; - loader.crossOrigin = scope.crossOrigin; - loader.load( fullPath, function ( image ) { - - if ( THREE.Math.isPowerOfTwo( image.width ) === false || - THREE.Math.isPowerOfTwo( image.height ) === false ) { - - var width = nearest_pow2( image.width ); - var height = nearest_pow2( image.height ); - - var canvas = document.createElement( 'canvas' ); - canvas.width = width; - canvas.height = height; - - var context = canvas.getContext( '2d' ); - context.drawImage( image, 0, 0, width, height ); - - texture.image = canvas; - - } else { - - texture.image = image; - - } - - texture.needsUpdate = true; - - } ); - - } - - texture.sourceFile = sourceFile; - - if ( repeat ) { - - texture.repeat.set( repeat[ 0 ], repeat[ 1 ] ); - - if ( repeat[ 0 ] !== 1 ) texture.wrapS = THREE.RepeatWrapping; - if ( repeat[ 1 ] !== 1 ) texture.wrapT = THREE.RepeatWrapping; - - } - - if ( offset ) { - - texture.offset.set( offset[ 0 ], offset[ 1 ] ); - - } - - if ( wrap ) { - - var wrapMap = { - 'repeat': THREE.RepeatWrapping, - 'mirror': THREE.MirroredRepeatWrapping - } - - if ( wrapMap[ wrap[ 0 ] ] !== undefined ) texture.wrapS = wrapMap[ wrap[ 0 ] ]; - if ( wrapMap[ wrap[ 1 ] ] !== undefined ) texture.wrapT = wrapMap[ wrap[ 1 ] ]; - - } - - if ( anisotropy ) { - - texture.anisotropy = anisotropy; - - } - - where[ name ] = texture; - - } - - function rgb2hex( rgb ) { - - return ( rgb[ 0 ] * 255 << 16 ) + ( rgb[ 1 ] * 255 << 8 ) + rgb[ 2 ] * 255; - - } - - // defaults - - var mtype = 'MeshLambertMaterial'; - var mpars = { color: 0xeeeeee, opacity: 1.0, map: null, lightMap: null, normalMap: null, bumpMap: null, wireframe: false }; - - // parameters from model file - - if ( m.shading ) { - - var shading = m.shading.toLowerCase(); - - if ( shading === 'phong' ) mtype = 'MeshPhongMaterial'; - else if ( shading === 'basic' ) mtype = 'MeshBasicMaterial'; - - } - - if ( m.blending !== undefined && THREE[ m.blending ] !== undefined ) { - - mpars.blending = THREE[ m.blending ]; - - } - - if ( m.transparent !== undefined || m.opacity < 1.0 ) { - - mpars.transparent = m.transparent; - - } - - if ( m.depthTest !== undefined ) { - - mpars.depthTest = m.depthTest; - - } - - if ( m.depthWrite !== undefined ) { - - mpars.depthWrite = m.depthWrite; - - } - - if ( m.visible !== undefined ) { - - mpars.visible = m.visible; - - } - - if ( m.flipSided !== undefined ) { - - mpars.side = THREE.BackSide; - - } - - if ( m.doubleSided !== undefined ) { - - mpars.side = THREE.DoubleSide; - - } - - if ( m.wireframe !== undefined ) { - - mpars.wireframe = m.wireframe; - - } - - if ( m.vertexColors !== undefined ) { - - if ( m.vertexColors === 'face' ) { - - mpars.vertexColors = THREE.FaceColors; - - } else if ( m.vertexColors ) { - - mpars.vertexColors = THREE.VertexColors; - - } - - } - - // colors - - if ( m.colorDiffuse ) { - - mpars.color = rgb2hex( m.colorDiffuse ); - - } else if ( m.DbgColor ) { - - mpars.color = m.DbgColor; - - } - - if ( m.colorSpecular ) { - - mpars.specular = rgb2hex( m.colorSpecular ); - - } - - if ( m.colorAmbient ) { - - mpars.ambient = rgb2hex( m.colorAmbient ); - - } - - if ( m.colorEmissive ) { - - mpars.emissive = rgb2hex( m.colorEmissive ); - - } - - // modifiers - - if ( m.transparency ) { - - mpars.opacity = m.transparency; - - } - - if ( m.specularCoef ) { - - mpars.shininess = m.specularCoef; - - } - - // textures - - if ( m.mapDiffuse && texturePath ) { - - create_texture( mpars, 'map', m.mapDiffuse, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy ); - - } - - if ( m.mapLight && texturePath ) { - - create_texture( mpars, 'lightMap', m.mapLight, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy ); - - } - - if ( m.mapBump && texturePath ) { - - create_texture( mpars, 'bumpMap', m.mapBump, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy ); - - } - - if ( m.mapNormal && texturePath ) { - - create_texture( mpars, 'normalMap', m.mapNormal, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy ); - - } - - if ( m.mapSpecular && texturePath ) { - - create_texture( mpars, 'specularMap', m.mapSpecular, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy ); - - } - - if ( m.mapAlpha && texturePath ) { - - create_texture( mpars, 'alphaMap', m.mapAlpha, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy ); - - } - - // - - if ( m.mapBumpScale ) { - - mpars.bumpScale = m.mapBumpScale; - - } - - // special case for normal mapped material - - if ( m.mapNormal ) { - - var shader = THREE.ShaderLib[ 'normalmap' ]; - var uniforms = THREE.UniformsUtils.clone( shader.uniforms ); - - uniforms[ 'tNormal' ].value = mpars.normalMap; - - if ( m.mapNormalFactor ) { - - uniforms[ 'uNormalScale' ].value.set( m.mapNormalFactor, m.mapNormalFactor ); - - } - - if ( mpars.map ) { - - uniforms[ 'tDiffuse' ].value = mpars.map; - uniforms[ 'enableDiffuse' ].value = true; - - } - - if ( mpars.specularMap ) { - - uniforms[ 'tSpecular' ].value = mpars.specularMap; - uniforms[ 'enableSpecular' ].value = true; - - } - - if ( mpars.lightMap ) { - - uniforms[ 'tAO' ].value = mpars.lightMap; - uniforms[ 'enableAO' ].value = true; - - } - - // for the moment don't handle displacement texture - - uniforms[ 'diffuse' ].value.setHex( mpars.color ); - uniforms[ 'specular' ].value.setHex( mpars.specular ); - uniforms[ 'ambient' ].value.setHex( mpars.ambient ); - - uniforms[ 'shininess' ].value = mpars.shininess; - - if ( mpars.opacity !== undefined ) { - - uniforms[ 'opacity' ].value = mpars.opacity; - - } - - var parameters = { fragmentShader: shader.fragmentShader, vertexShader: shader.vertexShader, uniforms: uniforms, lights: true, fog: true }; - var material = new THREE.ShaderMaterial( parameters ); - - if ( mpars.transparent ) { - - material.transparent = true; - - } - - } else { - - var material = new THREE[ mtype ]( mpars ); - - } - - if ( m.DbgName !== undefined ) material.name = m.DbgName; - - return material; - - } - -}; - -THREE.Loader.Handlers = { - - handlers: [], - - add: function ( regex, loader ) { - - this.handlers.push( regex, loader ); - - }, - - get: function ( file ) { - - for ( var i = 0, l = this.handlers.length; i < l; i += 2 ) { - - var regex = this.handlers[ i ]; - var loader = this.handlers[ i + 1 ]; - - if ( regex.test( file ) ) { - - return loader; - - } - - } - - return null; - - } - -}; +}; // File:src/loaders/XHRLoader.js @@ -11893,5140 +11633,4315 @@ THREE.ImageLoader.prototype = { // File:src/loaders/JSONLoader.js -/** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ - -THREE.JSONLoader = function ( showStatus ) { - - THREE.Loader.call( this, showStatus ); - - this.withCredentials = false; - -}; - -THREE.JSONLoader.prototype = Object.create( THREE.Loader.prototype ); - -THREE.JSONLoader.prototype.load = function ( url, callback, texturePath ) { - - var scope = this; - - // todo: unify load API to for easier SceneLoader use - - texturePath = texturePath && ( typeof texturePath === 'string' ) ? texturePath : this.extractUrlBase( url ); - - this.onLoadStart(); - this.loadAjaxJSON( this, url, callback, texturePath ); - -}; - -THREE.JSONLoader.prototype.loadAjaxJSON = function ( context, url, callback, texturePath, callbackProgress ) { - - var xhr = new XMLHttpRequest(); - - var length = 0; - - xhr.onreadystatechange = function () { - - if ( xhr.readyState === xhr.DONE ) { - - if ( xhr.status === 200 || xhr.status === 0 ) { - - if ( xhr.responseText ) { - - var json = JSON.parse( xhr.responseText ); - - if ( json.metadata !== undefined && json.metadata.type === 'scene' ) { - - console.error( 'THREE.JSONLoader: "' + url + '" seems to be a Scene. Use THREE.SceneLoader instead.' ); - return; - - } - - var result = context.parse( json, texturePath ); - callback( result.geometry, result.materials ); - - } else { - - console.error( 'THREE.JSONLoader: "' + url + '" seems to be unreachable or the file is empty.' ); - - } - - // in context of more complex asset initialization - // do not block on single failed file - // maybe should go even one more level up - - context.onLoadComplete(); - - } else { - - console.error( 'THREE.JSONLoader: Couldn\'t load "' + url + '" (' + xhr.status + ')' ); - - } - - } else if ( xhr.readyState === xhr.LOADING ) { - - if ( callbackProgress ) { - - if ( length === 0 ) { - - length = xhr.getResponseHeader( 'Content-Length' ); - - } - - callbackProgress( { total: length, loaded: xhr.responseText.length } ); - - } - - } else if ( xhr.readyState === xhr.HEADERS_RECEIVED ) { - - if ( callbackProgress !== undefined ) { - - length = xhr.getResponseHeader( 'Content-Length' ); - - } - - } - - }; - - xhr.open( 'GET', url, true ); - xhr.withCredentials = this.withCredentials; - xhr.send( null ); - -}; - -THREE.JSONLoader.prototype.parse = function ( json, texturePath ) { - - var scope = this, - geometry = new THREE.Geometry(), - scale = ( json.scale !== undefined ) ? 1.0 / json.scale : 1.0; - - parseModel( scale ); - - parseSkin(); - parseMorphing( scale ); - - geometry.computeFaceNormals(); - geometry.computeBoundingSphere(); - - function parseModel( scale ) { - - function isBitSet( value, position ) { - - return value & ( 1 << position ); - - } - - var i, j, fi, - - offset, zLength, - - colorIndex, normalIndex, uvIndex, materialIndex, - - type, - isQuad, - hasMaterial, - hasFaceVertexUv, - hasFaceNormal, hasFaceVertexNormal, - hasFaceColor, hasFaceVertexColor, - - vertex, face, faceA, faceB, color, hex, normal, - - uvLayer, uv, u, v, - - faces = json.faces, - vertices = json.vertices, - normals = json.normals, - colors = json.colors, - - nUvLayers = 0; - - if ( json.uvs !== undefined ) { - - // disregard empty arrays - - for ( i = 0; i < json.uvs.length; i ++ ) { - - if ( json.uvs[ i ].length ) nUvLayers ++; - - } - - for ( i = 0; i < nUvLayers; i ++ ) { - - geometry.faceVertexUvs[ i ] = []; - - } - - } - - offset = 0; - zLength = vertices.length; - - while ( offset < zLength ) { - - vertex = new THREE.Vector3(); - - vertex.x = vertices[ offset ++ ] * scale; - vertex.y = vertices[ offset ++ ] * scale; - vertex.z = vertices[ offset ++ ] * scale; - - geometry.vertices.push( vertex ); - - } - - offset = 0; - zLength = faces.length; - - while ( offset < zLength ) { - - type = faces[ offset ++ ]; - - - isQuad = isBitSet( type, 0 ); - hasMaterial = isBitSet( type, 1 ); - hasFaceVertexUv = isBitSet( type, 3 ); - hasFaceNormal = isBitSet( type, 4 ); - hasFaceVertexNormal = isBitSet( type, 5 ); - hasFaceColor = isBitSet( type, 6 ); - hasFaceVertexColor = isBitSet( type, 7 ); - - // console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor); - - if ( isQuad ) { - - faceA = new THREE.Face3(); - faceA.a = faces[ offset ]; - faceA.b = faces[ offset + 1 ]; - faceA.c = faces[ offset + 3 ]; - - faceB = new THREE.Face3(); - faceB.a = faces[ offset + 1 ]; - faceB.b = faces[ offset + 2 ]; - faceB.c = faces[ offset + 3 ]; - - offset += 4; - - if ( hasMaterial ) { - - materialIndex = faces[ offset ++ ]; - faceA.materialIndex = materialIndex; - faceB.materialIndex = materialIndex; - - } - - // to get face <=> uv index correspondence - - fi = geometry.faces.length; - - if ( hasFaceVertexUv ) { - - for ( i = 0; i < nUvLayers; i ++ ) { - - uvLayer = json.uvs[ i ]; - - geometry.faceVertexUvs[ i ][ fi ] = []; - geometry.faceVertexUvs[ i ][ fi + 1 ] = [] - - for ( j = 0; j < 4; j ++ ) { - - uvIndex = faces[ offset ++ ]; - - u = uvLayer[ uvIndex * 2 ]; - v = uvLayer[ uvIndex * 2 + 1 ]; - - uv = new THREE.Vector2( u, v ); - - if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv ); - if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv ); - - } - - } - - } - - if ( hasFaceNormal ) { - - normalIndex = faces[ offset ++ ] * 3; - - faceA.normal.set( - normals[ normalIndex ++ ], - normals[ normalIndex ++ ], - normals[ normalIndex ] - ); - - faceB.normal.copy( faceA.normal ); - - } - - if ( hasFaceVertexNormal ) { - - for ( i = 0; i < 4; i ++ ) { - - normalIndex = faces[ offset ++ ] * 3; - - normal = new THREE.Vector3( - normals[ normalIndex ++ ], - normals[ normalIndex ++ ], - normals[ normalIndex ] - ); - - - if ( i !== 2 ) faceA.vertexNormals.push( normal ); - if ( i !== 0 ) faceB.vertexNormals.push( normal ); - - } - - } - - - if ( hasFaceColor ) { - - colorIndex = faces[ offset ++ ]; - hex = colors[ colorIndex ]; - - faceA.color.setHex( hex ); - faceB.color.setHex( hex ); - - } - - - if ( hasFaceVertexColor ) { - - for ( i = 0; i < 4; i ++ ) { - - colorIndex = faces[ offset ++ ]; - hex = colors[ colorIndex ]; - - if ( i !== 2 ) faceA.vertexColors.push( new THREE.Color( hex ) ); - if ( i !== 0 ) faceB.vertexColors.push( new THREE.Color( hex ) ); - - } - - } - - geometry.faces.push( faceA ); - geometry.faces.push( faceB ); - - } else { - - face = new THREE.Face3(); - face.a = faces[ offset ++ ]; - face.b = faces[ offset ++ ]; - face.c = faces[ offset ++ ]; - - if ( hasMaterial ) { - - materialIndex = faces[ offset ++ ]; - face.materialIndex = materialIndex; - - } - - // to get face <=> uv index correspondence - - fi = geometry.faces.length; - - if ( hasFaceVertexUv ) { - - for ( i = 0; i < nUvLayers; i ++ ) { - - uvLayer = json.uvs[ i ]; - - geometry.faceVertexUvs[ i ][ fi ] = []; - - for ( j = 0; j < 3; j ++ ) { - - uvIndex = faces[ offset ++ ]; - - u = uvLayer[ uvIndex * 2 ]; - v = uvLayer[ uvIndex * 2 + 1 ]; - - uv = new THREE.Vector2( u, v ); - - geometry.faceVertexUvs[ i ][ fi ].push( uv ); - - } - - } - - } - - if ( hasFaceNormal ) { - - normalIndex = faces[ offset ++ ] * 3; - - face.normal.set( - normals[ normalIndex ++ ], - normals[ normalIndex ++ ], - normals[ normalIndex ] - ); - - } - - if ( hasFaceVertexNormal ) { - - for ( i = 0; i < 3; i ++ ) { - - normalIndex = faces[ offset ++ ] * 3; - - normal = new THREE.Vector3( - normals[ normalIndex ++ ], - normals[ normalIndex ++ ], - normals[ normalIndex ] - ); - - face.vertexNormals.push( normal ); - - } - - } - - - if ( hasFaceColor ) { - - colorIndex = faces[ offset ++ ]; - face.color.setHex( colors[ colorIndex ] ); - - } - - - if ( hasFaceVertexColor ) { - - for ( i = 0; i < 3; i ++ ) { - - colorIndex = faces[ offset ++ ]; - face.vertexColors.push( new THREE.Color( colors[ colorIndex ] ) ); - - } - - } - - geometry.faces.push( face ); - - } - - } - - }; - - function parseSkin() { - var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2; - - if ( json.skinWeights ) { - - for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) { - - var x = json.skinWeights[ i ]; - var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0; - var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0; - var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0; - - geometry.skinWeights.push( new THREE.Vector4( x, y, z, w ) ); - - } - - } - - if ( json.skinIndices ) { - - for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) { - - var a = json.skinIndices[ i ]; - var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0; - var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0; - var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0; - - geometry.skinIndices.push( new THREE.Vector4( a, b, c, d ) ); - - } - - } - - geometry.bones = json.bones; - - if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) { - - console.warn( 'When skinning, number of vertices (' + geometry.vertices.length + '), skinIndices (' + - geometry.skinIndices.length + '), and skinWeights (' + geometry.skinWeights.length + ') should match.' ); - - } - - - // could change this to json.animations[0] or remove completely - - geometry.animation = json.animation; - geometry.animations = json.animations; - - }; - - function parseMorphing( scale ) { - - if ( json.morphTargets !== undefined ) { - - var i, l, v, vl, dstVertices, srcVertices; - - for ( i = 0, l = json.morphTargets.length; i < l; i ++ ) { - - geometry.morphTargets[ i ] = {}; - geometry.morphTargets[ i ].name = json.morphTargets[ i ].name; - geometry.morphTargets[ i ].vertices = []; - - dstVertices = geometry.morphTargets[ i ].vertices; - srcVertices = json.morphTargets [ i ].vertices; - - for ( v = 0, vl = srcVertices.length; v < vl; v += 3 ) { - - var vertex = new THREE.Vector3(); - vertex.x = srcVertices[ v ] * scale; - vertex.y = srcVertices[ v + 1 ] * scale; - vertex.z = srcVertices[ v + 2 ] * scale; - - dstVertices.push( vertex ); - - } - - } - - } - - if ( json.morphColors !== undefined ) { - - var i, l, c, cl, dstColors, srcColors, color; - - for ( i = 0, l = json.morphColors.length; i < l; i ++ ) { - - geometry.morphColors[ i ] = {}; - geometry.morphColors[ i ].name = json.morphColors[ i ].name; - geometry.morphColors[ i ].colors = []; - - dstColors = geometry.morphColors[ i ].colors; - srcColors = json.morphColors [ i ].colors; - - for ( c = 0, cl = srcColors.length; c < cl; c += 3 ) { - - color = new THREE.Color( 0xffaa00 ); - color.setRGB( srcColors[ c ], srcColors[ c + 1 ], srcColors[ c + 2 ] ); - dstColors.push( color ); - - } - - } - - } - - }; - - if ( json.materials === undefined || json.materials.length === 0 ) { - - return { geometry: geometry }; - - } else { - - var materials = this.initMaterials( json.materials, texturePath ); - - if ( this.needsTangents( materials ) ) { - - geometry.computeTangents(); - - } - - return { geometry: geometry, materials: materials }; - - } - -}; - -// File:src/loaders/LoadingManager.js - /** * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ */ -THREE.LoadingManager = function ( onLoad, onProgress, onError ) { +THREE.JSONLoader = function ( showStatus ) { - var scope = this; + THREE.Loader.call( this, showStatus ); - var loaded = 0, total = 0; + this.withCredentials = false; - this.onLoad = onLoad; - this.onProgress = onProgress; - this.onError = onError; +}; - this.itemStart = function ( url ) { +THREE.JSONLoader.prototype = Object.create( THREE.Loader.prototype ); - total ++; +THREE.JSONLoader.prototype.load = function ( url, callback, texturePath ) { - }; + var scope = this; - this.itemEnd = function ( url ) { + // todo: unify load API to for easier SceneLoader use - loaded ++; + texturePath = texturePath && ( typeof texturePath === 'string' ) ? texturePath : this.extractUrlBase( url ); - if ( scope.onProgress !== undefined ) { + this.onLoadStart(); + this.loadAjaxJSON( this, url, callback, texturePath ); - scope.onProgress( url, loaded, total ); +}; - } +THREE.JSONLoader.prototype.loadAjaxJSON = function ( context, url, callback, texturePath, callbackProgress ) { - if ( loaded === total && scope.onLoad !== undefined ) { + var xhr = new XMLHttpRequest(); - scope.onLoad(); + var length = 0; - } + xhr.onreadystatechange = function () { - }; + if ( xhr.readyState === xhr.DONE ) { -}; + if ( xhr.status === 200 || xhr.status === 0 ) { -THREE.DefaultLoadingManager = new THREE.LoadingManager(); + if ( xhr.responseText ) { -// File:src/loaders/BufferGeometryLoader.js + var json = JSON.parse( xhr.responseText ); -/** - * @author mrdoob / http://mrdoob.com/ - */ - -THREE.BufferGeometryLoader = function ( manager ) { - - this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; - -}; - -THREE.BufferGeometryLoader.prototype = { - - constructor: THREE.BufferGeometryLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - var scope = this; - - var loader = new THREE.XHRLoader(); - loader.setCrossOrigin( this.crossOrigin ); - loader.load( url, function ( text ) { - - onLoad( scope.parse( JSON.parse( text ) ) ); - - }, onProgress, onError ); - - }, - - setCrossOrigin: function ( value ) { - - this.crossOrigin = value; - - }, - - parse: function ( json ) { - - var geometry = new THREE.BufferGeometry(); - - var attributes = json.attributes; - - for ( var key in attributes ) { - - var attribute = attributes[ key ]; - - geometry.attributes[ key ] = { - itemSize: attribute.itemSize, - array: new self[ attribute.type ]( attribute.array ) - } - - } - - var offsets = json.offsets; - - if ( offsets !== undefined ) { - - geometry.offsets = JSON.parse( JSON.stringify( offsets ) ); - - } - - var boundingSphere = json.boundingSphere; - - if ( boundingSphere !== undefined ) { - - geometry.boundingSphere = new THREE.Sphere( - new THREE.Vector3().fromArray( boundingSphere.center !== undefined ? boundingSphere.center : [ 0, 0, 0 ] ), - boundingSphere.radius - ); - - } - - return geometry; - - } - -}; + if ( json.metadata !== undefined && json.metadata.type === 'scene' ) { -// File:src/loaders/MaterialLoader.js + console.error( 'THREE.JSONLoader: "' + url + '" seems to be a Scene. Use THREE.SceneLoader instead.' ); + return; -/** - * @author mrdoob / http://mrdoob.com/ - */ - -THREE.MaterialLoader = function ( manager ) { - - this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; - -}; - -THREE.MaterialLoader.prototype = { - - constructor: THREE.MaterialLoader, - - load: function ( url, onLoad, onProgress, onError ) { - - var scope = this; - - var loader = new THREE.XHRLoader(); - loader.setCrossOrigin( this.crossOrigin ); - loader.load( url, function ( text ) { - - onLoad( scope.parse( JSON.parse( text ) ) ); - - }, onProgress, onError ); - - }, - - setCrossOrigin: function ( value ) { - - this.crossOrigin = value; - - }, - - parse: function ( json ) { - - var material = new THREE[ json.type ]; - - if ( json.color !== undefined ) material.color.setHex( json.color ); - if ( json.ambient !== undefined ) material.ambient.setHex( json.ambient ); - if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive ); - if ( json.specular !== undefined ) material.specular.setHex( json.specular ); - if ( json.shininess !== undefined ) material.shininess = json.shininess; - if ( json.uniforms !== undefined ) material.uniforms = json.uniforms; - if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader; - if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader; - if ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors; - if ( json.blending !== undefined ) material.blending = json.blending; - if ( json.side !== undefined ) material.side = json.side; - if ( json.opacity !== undefined ) material.opacity = json.opacity; - if ( json.transparent !== undefined ) material.transparent = json.transparent; - if ( json.wireframe !== undefined ) material.wireframe = json.wireframe; - - if ( json.materials !== undefined ) { - - for ( var i = 0, l = json.materials.length; i < l; i ++ ) { - - material.materials.push( this.parse( json.materials[ i ] ) ); - - } - - } - - return material; - - } - -}; + } -// File:src/loaders/ObjectLoader.js + var result = context.parse( json, texturePath ); + callback( result.geometry, result.materials ); -/** - * @author mrdoob / http://mrdoob.com/ - */ + } else { -THREE.ObjectLoader = function ( manager ) { + console.error( 'THREE.JSONLoader: "' + url + '" seems to be unreachable or the file is empty.' ); - this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + } -}; + // in context of more complex asset initialization + // do not block on single failed file + // maybe should go even one more level up -THREE.ObjectLoader.prototype = { + context.onLoadComplete(); - constructor: THREE.ObjectLoader, + } else { - load: function ( url, onLoad, onProgress, onError ) { + console.error( 'THREE.JSONLoader: Couldn\'t load "' + url + '" (' + xhr.status + ')' ); - var scope = this; + } - var loader = new THREE.XHRLoader( scope.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.load( url, function ( text ) { + } else if ( xhr.readyState === xhr.LOADING ) { - onLoad( scope.parse( JSON.parse( text ) ) ); + if ( callbackProgress ) { - }, onProgress, onError ); + if ( length === 0 ) { - }, + length = xhr.getResponseHeader( 'Content-Length' ); - setCrossOrigin: function ( value ) { + } - this.crossOrigin = value; + callbackProgress( { total: length, loaded: xhr.responseText.length } ); - }, + } - parse: function ( json ) { + } else if ( xhr.readyState === xhr.HEADERS_RECEIVED ) { - var geometries = this.parseGeometries( json.geometries ); - var materials = this.parseMaterials( json.materials ); - var object = this.parseObject( json.object, geometries, materials ); + if ( callbackProgress !== undefined ) { - return object; + length = xhr.getResponseHeader( 'Content-Length' ); - }, + } - parseGeometries: function ( json ) { + } - var geometries = {}; + }; - if ( json !== undefined ) { + xhr.open( 'GET', url, true ); + xhr.withCredentials = this.withCredentials; + xhr.send( null ); - var geometryLoader = new THREE.JSONLoader(); - var bufferGeometryLoader = new THREE.BufferGeometryLoader(); +}; - for ( var i = 0, l = json.length; i < l; i ++ ) { +THREE.JSONLoader.prototype.parse = function ( json, texturePath ) { - var geometry; - var data = json[ i ]; + var scope = this, + geometry = new THREE.Geometry(), + scale = ( json.scale !== undefined ) ? 1.0 / json.scale : 1.0; - switch ( data.type ) { + parseModel( scale ); - case 'PlaneGeometry': + parseSkin(); + parseMorphing( scale ); - geometry = new THREE.PlaneGeometry( - data.width, - data.height, - data.widthSegments, - data.heightSegments - ); + geometry.computeFaceNormals(); + geometry.computeBoundingSphere(); - break; + function parseModel( scale ) { - case 'BoxGeometry': - case 'CubeGeometry': // backwards compatible + function isBitSet( value, position ) { - geometry = new THREE.BoxGeometry( - data.width, - data.height, - data.depth, - data.widthSegments, - data.heightSegments, - data.depthSegments - ); + return value & ( 1 << position ); - break; + } - case 'CircleGeometry': + var i, j, fi, - geometry = new THREE.CircleGeometry( - data.radius, - data.segments - ); + offset, zLength, - break; + colorIndex, normalIndex, uvIndex, materialIndex, - case 'CylinderGeometry': + type, + isQuad, + hasMaterial, + hasFaceVertexUv, + hasFaceNormal, hasFaceVertexNormal, + hasFaceColor, hasFaceVertexColor, - geometry = new THREE.CylinderGeometry( - data.radiusTop, - data.radiusBottom, - data.height, - data.radialSegments, - data.heightSegments, - data.openEnded - ); + vertex, face, faceA, faceB, color, hex, normal, - break; + uvLayer, uv, u, v, - case 'SphereGeometry': + faces = json.faces, + vertices = json.vertices, + normals = json.normals, + colors = json.colors, - geometry = new THREE.SphereGeometry( - data.radius, - data.widthSegments, - data.heightSegments, - data.phiStart, - data.phiLength, - data.thetaStart, - data.thetaLength - ); + nUvLayers = 0; - break; + if ( json.uvs !== undefined ) { - case 'IcosahedronGeometry': + // disregard empty arrays - geometry = new THREE.IcosahedronGeometry( - data.radius, - data.detail - ); + for ( i = 0; i < json.uvs.length; i ++ ) { - break; + if ( json.uvs[ i ].length ) nUvLayers ++; - case 'TorusGeometry': + } - geometry = new THREE.TorusGeometry( - data.radius, - data.tube, - data.radialSegments, - data.tubularSegments, - data.arc - ); + for ( i = 0; i < nUvLayers; i ++ ) { - break; + geometry.faceVertexUvs[ i ] = []; - case 'TorusKnotGeometry': + } - geometry = new THREE.TorusKnotGeometry( - data.radius, - data.tube, - data.radialSegments, - data.tubularSegments, - data.p, - data.q, - data.heightScale - ); + } - break; + offset = 0; + zLength = vertices.length; - case 'BufferGeometry': + while ( offset < zLength ) { - geometry = bufferGeometryLoader.parse( data.data ); + vertex = new THREE.Vector3(); - break; + vertex.x = vertices[ offset ++ ] * scale; + vertex.y = vertices[ offset ++ ] * scale; + vertex.z = vertices[ offset ++ ] * scale; - case 'Geometry': + geometry.vertices.push( vertex ); - geometry = geometryLoader.parse( data.data ).geometry; + } - break; + offset = 0; + zLength = faces.length; - } + while ( offset < zLength ) { - geometry.uuid = data.uuid; + type = faces[ offset ++ ]; - if ( data.name !== undefined ) geometry.name = data.name; - geometries[ data.uuid ] = geometry; + isQuad = isBitSet( type, 0 ); + hasMaterial = isBitSet( type, 1 ); + hasFaceVertexUv = isBitSet( type, 3 ); + hasFaceNormal = isBitSet( type, 4 ); + hasFaceVertexNormal = isBitSet( type, 5 ); + hasFaceColor = isBitSet( type, 6 ); + hasFaceVertexColor = isBitSet( type, 7 ); - } + // console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor); - } + if ( isQuad ) { - return geometries; + faceA = new THREE.Face3(); + faceA.a = faces[ offset ]; + faceA.b = faces[ offset + 1 ]; + faceA.c = faces[ offset + 3 ]; - }, + faceB = new THREE.Face3(); + faceB.a = faces[ offset + 1 ]; + faceB.b = faces[ offset + 2 ]; + faceB.c = faces[ offset + 3 ]; - parseMaterials: function ( json ) { + offset += 4; - var materials = {}; + if ( hasMaterial ) { - if ( json !== undefined ) { + materialIndex = faces[ offset ++ ]; + faceA.materialIndex = materialIndex; + faceB.materialIndex = materialIndex; - var loader = new THREE.MaterialLoader(); + } - for ( var i = 0, l = json.length; i < l; i ++ ) { + // to get face <=> uv index correspondence - var data = json[ i ]; - var material = loader.parse( data ); + fi = geometry.faces.length; - material.uuid = data.uuid; + if ( hasFaceVertexUv ) { - if ( data.name !== undefined ) material.name = data.name; + for ( i = 0; i < nUvLayers; i ++ ) { - materials[ data.uuid ] = material; + uvLayer = json.uvs[ i ]; - } + geometry.faceVertexUvs[ i ][ fi ] = []; + geometry.faceVertexUvs[ i ][ fi + 1 ] = [] - } + for ( j = 0; j < 4; j ++ ) { - return materials; + uvIndex = faces[ offset ++ ]; - }, + u = uvLayer[ uvIndex * 2 ]; + v = uvLayer[ uvIndex * 2 + 1 ]; - parseObject: function () { + uv = new THREE.Vector2( u, v ); - var matrix = new THREE.Matrix4(); + if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv ); + if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv ); - return function ( data, geometries, materials ) { + } - var object; + } - switch ( data.type ) { + } - case 'Scene': + if ( hasFaceNormal ) { - object = new THREE.Scene(); + normalIndex = faces[ offset ++ ] * 3; - break; + faceA.normal.set( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); - case 'PerspectiveCamera': + faceB.normal.copy( faceA.normal ); - object = new THREE.PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); + } - break; + if ( hasFaceVertexNormal ) { - case 'OrthographicCamera': + for ( i = 0; i < 4; i ++ ) { - object = new THREE.OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); + normalIndex = faces[ offset ++ ] * 3; - break; + normal = new THREE.Vector3( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); - case 'AmbientLight': - object = new THREE.AmbientLight( data.color ); + if ( i !== 2 ) faceA.vertexNormals.push( normal ); + if ( i !== 0 ) faceB.vertexNormals.push( normal ); - break; + } - case 'DirectionalLight': + } - object = new THREE.DirectionalLight( data.color, data.intensity ); - break; + if ( hasFaceColor ) { - case 'PointLight': + colorIndex = faces[ offset ++ ]; + hex = colors[ colorIndex ]; - object = new THREE.PointLight( data.color, data.intensity, data.distance ); + faceA.color.setHex( hex ); + faceB.color.setHex( hex ); - break; + } - case 'SpotLight': - object = new THREE.SpotLight( data.color, data.intensity, data.distance, data.angle, data.exponent ); + if ( hasFaceVertexColor ) { - break; + for ( i = 0; i < 4; i ++ ) { - case 'HemisphereLight': + colorIndex = faces[ offset ++ ]; + hex = colors[ colorIndex ]; - object = new THREE.HemisphereLight( data.color, data.groundColor, data.intensity ); + if ( i !== 2 ) faceA.vertexColors.push( new THREE.Color( hex ) ); + if ( i !== 0 ) faceB.vertexColors.push( new THREE.Color( hex ) ); - break; + } - case 'Mesh': + } - var geometry = geometries[ data.geometry ]; - var material = materials[ data.material ]; + geometry.faces.push( faceA ); + geometry.faces.push( faceB ); - if ( geometry === undefined ) { + } else { - console.error( 'THREE.ObjectLoader: Undefined geometry ' + data.geometry ); + face = new THREE.Face3(); + face.a = faces[ offset ++ ]; + face.b = faces[ offset ++ ]; + face.c = faces[ offset ++ ]; - } + if ( hasMaterial ) { - if ( material === undefined ) { + materialIndex = faces[ offset ++ ]; + face.materialIndex = materialIndex; - console.error( 'THREE.ObjectLoader: Undefined material ' + data.material ); + } - } + // to get face <=> uv index correspondence - object = new THREE.Mesh( geometry, material ); + fi = geometry.faces.length; - break; + if ( hasFaceVertexUv ) { - case 'Sprite': + for ( i = 0; i < nUvLayers; i ++ ) { - var material = materials[ data.material ]; + uvLayer = json.uvs[ i ]; - if ( material === undefined ) { + geometry.faceVertexUvs[ i ][ fi ] = []; - console.error( 'THREE.ObjectLoader: Undefined material ' + data.material ); + for ( j = 0; j < 3; j ++ ) { - } + uvIndex = faces[ offset ++ ]; - object = new THREE.Sprite( material ); + u = uvLayer[ uvIndex * 2 ]; + v = uvLayer[ uvIndex * 2 + 1 ]; - break; + uv = new THREE.Vector2( u, v ); - default: + geometry.faceVertexUvs[ i ][ fi ].push( uv ); - object = new THREE.Object3D(); + } - } + } - object.uuid = data.uuid; + } - if ( data.name !== undefined ) object.name = data.name; - if ( data.matrix !== undefined ) { + if ( hasFaceNormal ) { - matrix.fromArray( data.matrix ); - matrix.decompose( object.position, object.quaternion, object.scale ); + normalIndex = faces[ offset ++ ] * 3; - } else { + face.normal.set( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); - if ( data.position !== undefined ) object.position.fromArray( data.position ); - if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation ); - if ( data.scale !== undefined ) object.scale.fromArray( data.scale ); + } - } + if ( hasFaceVertexNormal ) { - if ( data.visible !== undefined ) object.visible = data.visible; - if ( data.userData !== undefined ) object.userData = data.userData; + for ( i = 0; i < 3; i ++ ) { - if ( data.children !== undefined ) { + normalIndex = faces[ offset ++ ] * 3; - for ( var child in data.children ) { + normal = new THREE.Vector3( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); - object.add( this.parseObject( data.children[ child ], geometries, materials ) ); + face.vertexNormals.push( normal ); + + } } - } - return object; + if ( hasFaceColor ) { - } + colorIndex = faces[ offset ++ ]; + face.color.setHex( colors[ colorIndex ] ); - }() + } -}; -// File:src/loaders/TextureLoader.js + if ( hasFaceVertexColor ) { -/** - * @author mrdoob / http://mrdoob.com/ - */ + for ( i = 0; i < 3; i ++ ) { -THREE.TextureLoader = function ( manager ) { + colorIndex = faces[ offset ++ ]; + face.vertexColors.push( new THREE.Color( colors[ colorIndex ] ) ); - this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + } -}; + } -THREE.TextureLoader.prototype = { + geometry.faces.push( face ); - constructor: THREE.TextureLoader, + } - load: function ( url, onLoad, onProgress, onError ) { + } - var scope = this; + }; - var loader = new THREE.ImageLoader( scope.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.load( url, function ( image ) { + function parseSkin() { + var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2; - var texture = new THREE.Texture( image ); - texture.needsUpdate = true; + if ( json.skinWeights ) { - if ( onLoad !== undefined ) { + for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) { - onLoad( texture ); + var x = json.skinWeights[ i ]; + var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0; + var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0; + var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0; + + geometry.skinWeights.push( new THREE.Vector4( x, y, z, w ) ); } - }, onProgress, onError ); + } - }, + if ( json.skinIndices ) { - setCrossOrigin: function ( value ) { + for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) { - this.crossOrigin = value; + var a = json.skinIndices[ i ]; + var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0; + var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0; + var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0; - } + geometry.skinIndices.push( new THREE.Vector4( a, b, c, d ) ); -}; + } -// File:src/materials/Material.js + } -/** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ + geometry.bones = json.bones; -THREE.Material = function () { + if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) { - this.id = THREE.MaterialIdCount ++; - this.uuid = THREE.Math.generateUUID(); + console.warn( 'When skinning, number of vertices (' + geometry.vertices.length + '), skinIndices (' + + geometry.skinIndices.length + '), and skinWeights (' + geometry.skinWeights.length + ') should match.' ); - this.name = ''; + } - this.side = THREE.FrontSide; - this.opacity = 1; - this.transparent = false; + // could change this to json.animations[0] or remove completely - this.blending = THREE.NormalBlending; + geometry.animation = json.animation; + geometry.animations = json.animations; - this.blendSrc = THREE.SrcAlphaFactor; - this.blendDst = THREE.OneMinusSrcAlphaFactor; - this.blendEquation = THREE.AddEquation; + }; - this.depthTest = true; - this.depthWrite = true; + function parseMorphing( scale ) { - this.polygonOffset = false; - this.polygonOffsetFactor = 0; - this.polygonOffsetUnits = 0; + if ( json.morphTargets !== undefined ) { - this.alphaTest = 0; + var i, l, v, vl, dstVertices, srcVertices; - this.overdraw = 0; // Overdrawn pixels (typically between 0 and 1) for fixing antialiasing gaps in CanvasRenderer + for ( i = 0, l = json.morphTargets.length; i < l; i ++ ) { - this.visible = true; + geometry.morphTargets[ i ] = {}; + geometry.morphTargets[ i ].name = json.morphTargets[ i ].name; + geometry.morphTargets[ i ].vertices = []; - this.needsUpdate = true; + dstVertices = geometry.morphTargets[ i ].vertices; + srcVertices = json.morphTargets [ i ].vertices; -}; + for ( v = 0, vl = srcVertices.length; v < vl; v += 3 ) { -THREE.Material.prototype = { + var vertex = new THREE.Vector3(); + vertex.x = srcVertices[ v ] * scale; + vertex.y = srcVertices[ v + 1 ] * scale; + vertex.z = srcVertices[ v + 2 ] * scale; - constructor: THREE.Material, + dstVertices.push( vertex ); - setValues: function ( values ) { + } - if ( values === undefined ) return; + } - for ( var key in values ) { + } - var newValue = values[ key ]; + if ( json.morphColors !== undefined ) { - if ( newValue === undefined ) { + var i, l, c, cl, dstColors, srcColors, color; - console.warn( "THREE.Material: '" + key + "' parameter is undefined." ); - continue; + for ( i = 0, l = json.morphColors.length; i < l; i ++ ) { - } + geometry.morphColors[ i ] = {}; + geometry.morphColors[ i ].name = json.morphColors[ i ].name; + geometry.morphColors[ i ].colors = []; - if ( key in this ) { + dstColors = geometry.morphColors[ i ].colors; + srcColors = json.morphColors [ i ].colors; - var currentValue = this[ key ]; + for ( c = 0, cl = srcColors.length; c < cl; c += 3 ) { - if ( currentValue instanceof THREE.Color ) { + color = new THREE.Color( 0xffaa00 ); + color.setRGB( srcColors[ c ], srcColors[ c + 1 ], srcColors[ c + 2 ] ); + dstColors.push( color ); - currentValue.set( newValue ); + } - } else if ( currentValue instanceof THREE.Vector3 && newValue instanceof THREE.Vector3 ) { + } - currentValue.copy( newValue ); + } - } else if ( key == 'overdraw' ) { + }; - // ensure overdraw is backwards-compatable with legacy boolean type - this[ key ] = Number( newValue ); + if ( json.materials === undefined || json.materials.length === 0 ) { - } else { + return { geometry: geometry }; - this[ key ] = newValue; + } else { - } + var materials = this.initMaterials( json.materials, texturePath ); - } + if ( this.needsTangents( materials ) ) { + + geometry.computeTangents(); } - }, + return { geometry: geometry, materials: materials }; - clone: function ( material ) { + } - if ( material === undefined ) material = new THREE.Material(); +}; - material.name = this.name; +// File:src/loaders/LoadingManager.js - material.side = this.side; +/** + * @author mrdoob / http://mrdoob.com/ + */ - material.opacity = this.opacity; - material.transparent = this.transparent; +THREE.LoadingManager = function ( onLoad, onProgress, onError ) { - material.blending = this.blending; + var scope = this; - material.blendSrc = this.blendSrc; - material.blendDst = this.blendDst; - material.blendEquation = this.blendEquation; + var loaded = 0, total = 0; - material.depthTest = this.depthTest; - material.depthWrite = this.depthWrite; + this.onLoad = onLoad; + this.onProgress = onProgress; + this.onError = onError; - material.polygonOffset = this.polygonOffset; - material.polygonOffsetFactor = this.polygonOffsetFactor; - material.polygonOffsetUnits = this.polygonOffsetUnits; + this.itemStart = function ( url ) { - material.alphaTest = this.alphaTest; + total ++; - material.overdraw = this.overdraw; + }; - material.visible = this.visible; + this.itemEnd = function ( url ) { - return material; + loaded ++; - }, + if ( scope.onProgress !== undefined ) { - dispose: function () { + scope.onProgress( url, loaded, total ); - this.dispatchEvent( { type: 'dispose' } ); + } - } + if ( loaded === total && scope.onLoad !== undefined ) { -}; + scope.onLoad(); -THREE.EventDispatcher.prototype.apply( THREE.Material.prototype ); + } -THREE.MaterialIdCount = 0; + }; -// File:src/materials/LineBasicMaterial.js +}; + +THREE.DefaultLoadingManager = new THREE.LoadingManager(); + +// File:src/loaders/BufferGeometryLoader.js /** * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * opacity: , - * - * blending: THREE.NormalBlending, - * depthTest: , - * depthWrite: , - * - * linewidth: , - * linecap: "round", - * linejoin: "round", - * - * vertexColors: - * - * fog: - * } */ -THREE.LineBasicMaterial = function ( parameters ) { - - THREE.Material.call( this ); - - this.color = new THREE.Color( 0xffffff ); - - this.linewidth = 1; - this.linecap = 'round'; - this.linejoin = 'round'; - - this.vertexColors = THREE.NoColors; - - this.fog = true; +THREE.BufferGeometryLoader = function ( manager ) { - this.setValues( parameters ); + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; }; -THREE.LineBasicMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.BufferGeometryLoader.prototype = { -THREE.LineBasicMaterial.prototype.clone = function () { + constructor: THREE.BufferGeometryLoader, - var material = new THREE.LineBasicMaterial(); + load: function ( url, onLoad, onProgress, onError ) { - THREE.Material.prototype.clone.call( this, material ); + var scope = this; - material.color.copy( this.color ); + var loader = new THREE.XHRLoader(); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { - material.linewidth = this.linewidth; - material.linecap = this.linecap; - material.linejoin = this.linejoin; + onLoad( scope.parse( JSON.parse( text ) ) ); - material.vertexColors = this.vertexColors; + }, onProgress, onError ); - material.fog = this.fog; + }, - return material; + setCrossOrigin: function ( value ) { -}; + this.crossOrigin = value; -// File:src/materials/LineDashedMaterial.js + }, -/** - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * opacity: , - * - * blending: THREE.NormalBlending, - * depthTest: , - * depthWrite: , - * - * linewidth: , - * - * scale: , - * dashSize: , - * gapSize: , - * - * vertexColors: - * - * fog: - * } - */ + parse: function ( json ) { -THREE.LineDashedMaterial = function ( parameters ) { + var geometry = new THREE.BufferGeometry(); - THREE.Material.call( this ); + var attributes = json.attributes; - this.color = new THREE.Color( 0xffffff ); + for ( var key in attributes ) { - this.linewidth = 1; + var attribute = attributes[ key ]; + var typedArray = new self[ attribute.type ]( attribute.array ); - this.scale = 1; - this.dashSize = 3; - this.gapSize = 1; + geometry.addAttribute( key, new THREE.BufferAttribute( typedArray, attribute.itemSize ) ); - this.vertexColors = false; + } - this.fog = true; + var offsets = json.offsets; - this.setValues( parameters ); + if ( offsets !== undefined ) { -}; + geometry.offsets = JSON.parse( JSON.stringify( offsets ) ); -THREE.LineDashedMaterial.prototype = Object.create( THREE.Material.prototype ); + } -THREE.LineDashedMaterial.prototype.clone = function () { + var boundingSphere = json.boundingSphere; - var material = new THREE.LineDashedMaterial(); + if ( boundingSphere !== undefined ) { - THREE.Material.prototype.clone.call( this, material ); + var center = new THREE.Vector3(); - material.color.copy( this.color ); + if ( boundingSphere.center !== undefined ) { - material.linewidth = this.linewidth; + center.fromArray( boundingSphere.center ); - material.scale = this.scale; - material.dashSize = this.dashSize; - material.gapSize = this.gapSize; + } - material.vertexColors = this.vertexColors; + geometry.boundingSphere = new THREE.Sphere( center, boundingSphere.radius ); - material.fog = this.fog; + } - return material; + return geometry; + + } }; -// File:src/materials/MeshBasicMaterial.js +// File:src/loaders/MaterialLoader.js /** * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * opacity: , - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.Multiply, - * reflectivity: , - * refractionRatio: , - * - * shading: THREE.SmoothShading, - * blending: THREE.NormalBlending, - * depthTest: , - * depthWrite: , - * - * wireframe: , - * wireframeLinewidth: , - * - * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, - * - * skinning: , - * morphTargets: , - * - * fog: - * } */ -THREE.MeshBasicMaterial = function ( parameters ) { +THREE.MaterialLoader = function ( manager ) { - THREE.Material.call( this ); + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; - this.color = new THREE.Color( 0xffffff ); // emissive +}; - this.map = null; +THREE.MaterialLoader.prototype = { - this.lightMap = null; + constructor: THREE.MaterialLoader, - this.specularMap = null; + load: function ( url, onLoad, onProgress, onError ) { - this.alphaMap = null; + var scope = this; - this.envMap = null; - this.combine = THREE.MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; + var loader = new THREE.XHRLoader(); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { - this.fog = true; + onLoad( scope.parse( JSON.parse( text ) ) ); - this.shading = THREE.SmoothShading; + }, onProgress, onError ); - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; + }, - this.vertexColors = THREE.NoColors; + setCrossOrigin: function ( value ) { - this.skinning = false; - this.morphTargets = false; + this.crossOrigin = value; - this.setValues( parameters ); + }, -}; + parse: function ( json ) { -THREE.MeshBasicMaterial.prototype = Object.create( THREE.Material.prototype ); + var material = new THREE[ json.type ]; -THREE.MeshBasicMaterial.prototype.clone = function () { + if ( json.color !== undefined ) material.color.setHex( json.color ); + if ( json.ambient !== undefined ) material.ambient.setHex( json.ambient ); + if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive ); + if ( json.specular !== undefined ) material.specular.setHex( json.specular ); + if ( json.shininess !== undefined ) material.shininess = json.shininess; + if ( json.uniforms !== undefined ) material.uniforms = json.uniforms; + if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader; + if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader; + if ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors; + if ( json.shading !== undefined ) material.shading = json.shading; + if ( json.blending !== undefined ) material.blending = json.blending; + if ( json.side !== undefined ) material.side = json.side; + if ( json.opacity !== undefined ) material.opacity = json.opacity; + if ( json.transparent !== undefined ) material.transparent = json.transparent; + if ( json.wireframe !== undefined ) material.wireframe = json.wireframe; - var material = new THREE.MeshBasicMaterial(); + if ( json.materials !== undefined ) { - THREE.Material.prototype.clone.call( this, material ); + for ( var i = 0, l = json.materials.length; i < l; i ++ ) { - material.color.copy( this.color ); + material.materials.push( this.parse( json.materials[ i ] ) ); - material.map = this.map; + } - material.lightMap = this.lightMap; + } - material.specularMap = this.specularMap; + return material; - material.alphaMap = this.alphaMap; + } - material.envMap = this.envMap; - material.combine = this.combine; - material.reflectivity = this.reflectivity; - material.refractionRatio = this.refractionRatio; +}; - material.fog = this.fog; +// File:src/loaders/ObjectLoader.js - material.shading = this.shading; +/** + * @author mrdoob / http://mrdoob.com/ + */ - material.wireframe = this.wireframe; - material.wireframeLinewidth = this.wireframeLinewidth; - material.wireframeLinecap = this.wireframeLinecap; - material.wireframeLinejoin = this.wireframeLinejoin; +THREE.ObjectLoader = function ( manager ) { - material.vertexColors = this.vertexColors; + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; - material.skinning = this.skinning; - material.morphTargets = this.morphTargets; +}; - return material; +THREE.ObjectLoader.prototype = { -}; + constructor: THREE.ObjectLoader, -// File:src/materials/MeshLambertMaterial.js + load: function ( url, onLoad, onProgress, onError ) { -/** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * ambient: , - * emissive: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.Multiply, - * reflectivity: , - * refractionRatio: , - * - * shading: THREE.SmoothShading, - * blending: THREE.NormalBlending, - * depthTest: , - * depthWrite: , - * - * wireframe: , - * wireframeLinewidth: , - * - * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, - * - * skinning: , - * morphTargets: , - * morphNormals: , - * - * fog: - * } - */ + var scope = this; -THREE.MeshLambertMaterial = function ( parameters ) { + var loader = new THREE.XHRLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { - THREE.Material.call( this ); + onLoad( scope.parse( JSON.parse( text ) ) ); - this.color = new THREE.Color( 0xffffff ); // diffuse - this.ambient = new THREE.Color( 0xffffff ); - this.emissive = new THREE.Color( 0x000000 ); + }, onProgress, onError ); - this.wrapAround = false; - this.wrapRGB = new THREE.Vector3( 1, 1, 1 ); + }, - this.map = null; + setCrossOrigin: function ( value ) { - this.lightMap = null; + this.crossOrigin = value; - this.specularMap = null; + }, - this.alphaMap = null; + parse: function ( json ) { - this.envMap = null; - this.combine = THREE.MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; + var geometries = this.parseGeometries( json.geometries ); + var materials = this.parseMaterials( json.materials ); + var object = this.parseObject( json.object, geometries, materials ); - this.fog = true; + return object; - this.shading = THREE.SmoothShading; + }, - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; + parseGeometries: function ( json ) { - this.vertexColors = THREE.NoColors; + var geometries = {}; - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + if ( json !== undefined ) { - this.setValues( parameters ); + var geometryLoader = new THREE.JSONLoader(); + var bufferGeometryLoader = new THREE.BufferGeometryLoader(); -}; + for ( var i = 0, l = json.length; i < l; i ++ ) { -THREE.MeshLambertMaterial.prototype = Object.create( THREE.Material.prototype ); + var geometry; + var data = json[ i ]; -THREE.MeshLambertMaterial.prototype.clone = function () { + switch ( data.type ) { - var material = new THREE.MeshLambertMaterial(); + case 'PlaneGeometry': - THREE.Material.prototype.clone.call( this, material ); + geometry = new THREE.PlaneGeometry( + data.width, + data.height, + data.widthSegments, + data.heightSegments + ); - material.color.copy( this.color ); - material.ambient.copy( this.ambient ); - material.emissive.copy( this.emissive ); + break; - material.wrapAround = this.wrapAround; - material.wrapRGB.copy( this.wrapRGB ); + case 'BoxGeometry': + case 'CubeGeometry': // backwards compatible - material.map = this.map; + geometry = new THREE.BoxGeometry( + data.width, + data.height, + data.depth, + data.widthSegments, + data.heightSegments, + data.depthSegments + ); - material.lightMap = this.lightMap; + break; - material.specularMap = this.specularMap; + case 'CircleGeometry': - material.alphaMap = this.alphaMap; + geometry = new THREE.CircleGeometry( + data.radius, + data.segments + ); - material.envMap = this.envMap; - material.combine = this.combine; - material.reflectivity = this.reflectivity; - material.refractionRatio = this.refractionRatio; + break; - material.fog = this.fog; + case 'CylinderGeometry': - material.shading = this.shading; + geometry = new THREE.CylinderGeometry( + data.radiusTop, + data.radiusBottom, + data.height, + data.radialSegments, + data.heightSegments, + data.openEnded + ); - material.wireframe = this.wireframe; - material.wireframeLinewidth = this.wireframeLinewidth; - material.wireframeLinecap = this.wireframeLinecap; - material.wireframeLinejoin = this.wireframeLinejoin; + break; - material.vertexColors = this.vertexColors; + case 'SphereGeometry': - material.skinning = this.skinning; - material.morphTargets = this.morphTargets; - material.morphNormals = this.morphNormals; + geometry = new THREE.SphereGeometry( + data.radius, + data.widthSegments, + data.heightSegments, + data.phiStart, + data.phiLength, + data.thetaStart, + data.thetaLength + ); - return material; + break; -}; + case 'IcosahedronGeometry': -// File:src/materials/MeshPhongMaterial.js + geometry = new THREE.IcosahedronGeometry( + data.radius, + data.detail + ); -/** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * ambient: , - * emissive: , - * specular: , - * shininess: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalScale: , - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.Multiply, - * reflectivity: , - * refractionRatio: , - * - * shading: THREE.SmoothShading, - * blending: THREE.NormalBlending, - * depthTest: , - * depthWrite: , - * - * wireframe: , - * wireframeLinewidth: , - * - * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, - * - * skinning: , - * morphTargets: , - * morphNormals: , - * - * fog: - * } - */ + break; -THREE.MeshPhongMaterial = function ( parameters ) { + case 'TorusGeometry': - THREE.Material.call( this ); + geometry = new THREE.TorusGeometry( + data.radius, + data.tube, + data.radialSegments, + data.tubularSegments, + data.arc + ); - this.color = new THREE.Color( 0xffffff ); // diffuse - this.ambient = new THREE.Color( 0xffffff ); - this.emissive = new THREE.Color( 0x000000 ); - this.specular = new THREE.Color( 0x111111 ); - this.shininess = 30; + break; - this.metal = false; + case 'TorusKnotGeometry': - this.wrapAround = false; - this.wrapRGB = new THREE.Vector3( 1, 1, 1 ); + geometry = new THREE.TorusKnotGeometry( + data.radius, + data.tube, + data.radialSegments, + data.tubularSegments, + data.p, + data.q, + data.heightScale + ); - this.map = null; + break; - this.lightMap = null; + case 'BufferGeometry': - this.bumpMap = null; - this.bumpScale = 1; + geometry = bufferGeometryLoader.parse( data.data ); - this.normalMap = null; - this.normalScale = new THREE.Vector2( 1, 1 ); + break; - this.specularMap = null; + case 'Geometry': - this.alphaMap = null; + geometry = geometryLoader.parse( data.data ).geometry; - this.envMap = null; - this.combine = THREE.MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; + break; - this.fog = true; + } - this.shading = THREE.SmoothShading; + geometry.uuid = data.uuid; - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; + if ( data.name !== undefined ) geometry.name = data.name; - this.vertexColors = THREE.NoColors; + geometries[ data.uuid ] = geometry; - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + } - this.setValues( parameters ); + } -}; + return geometries; -THREE.MeshPhongMaterial.prototype = Object.create( THREE.Material.prototype ); + }, -THREE.MeshPhongMaterial.prototype.clone = function () { + parseMaterials: function ( json ) { - var material = new THREE.MeshPhongMaterial(); + var materials = {}; - THREE.Material.prototype.clone.call( this, material ); + if ( json !== undefined ) { - material.color.copy( this.color ); - material.ambient.copy( this.ambient ); - material.emissive.copy( this.emissive ); - material.specular.copy( this.specular ); - material.shininess = this.shininess; + var loader = new THREE.MaterialLoader(); - material.metal = this.metal; + for ( var i = 0, l = json.length; i < l; i ++ ) { - material.wrapAround = this.wrapAround; - material.wrapRGB.copy( this.wrapRGB ); + var data = json[ i ]; + var material = loader.parse( data ); - material.map = this.map; + material.uuid = data.uuid; - material.lightMap = this.lightMap; + if ( data.name !== undefined ) material.name = data.name; - material.bumpMap = this.bumpMap; - material.bumpScale = this.bumpScale; + materials[ data.uuid ] = material; - material.normalMap = this.normalMap; - material.normalScale.copy( this.normalScale ); + } - material.specularMap = this.specularMap; + } - material.alphaMap = this.alphaMap; + return materials; - material.envMap = this.envMap; - material.combine = this.combine; - material.reflectivity = this.reflectivity; - material.refractionRatio = this.refractionRatio; + }, - material.fog = this.fog; + parseObject: function () { - material.shading = this.shading; + var matrix = new THREE.Matrix4(); - material.wireframe = this.wireframe; - material.wireframeLinewidth = this.wireframeLinewidth; - material.wireframeLinecap = this.wireframeLinecap; - material.wireframeLinejoin = this.wireframeLinejoin; + return function ( data, geometries, materials ) { - material.vertexColors = this.vertexColors; + var object; - material.skinning = this.skinning; - material.morphTargets = this.morphTargets; - material.morphNormals = this.morphNormals; + switch ( data.type ) { - return material; + case 'Scene': -}; + object = new THREE.Scene(); -// File:src/materials/MeshDepthMaterial.js + break; -/** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * opacity: , - * - * blending: THREE.NormalBlending, - * depthTest: , - * depthWrite: , - * - * wireframe: , - * wireframeLinewidth: - * } - */ + case 'PerspectiveCamera': -THREE.MeshDepthMaterial = function ( parameters ) { + object = new THREE.PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); - THREE.Material.call( this ); + break; - this.morphTargets = false; - this.wireframe = false; - this.wireframeLinewidth = 1; + case 'OrthographicCamera': - this.setValues( parameters ); + object = new THREE.OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); -}; + break; -THREE.MeshDepthMaterial.prototype = Object.create( THREE.Material.prototype ); + case 'AmbientLight': -THREE.MeshDepthMaterial.prototype.clone = function () { + object = new THREE.AmbientLight( data.color ); - var material = new THREE.MeshDepthMaterial(); + break; - THREE.Material.prototype.clone.call( this, material ); + case 'DirectionalLight': - material.wireframe = this.wireframe; - material.wireframeLinewidth = this.wireframeLinewidth; + object = new THREE.DirectionalLight( data.color, data.intensity ); - return material; + break; -}; + case 'PointLight': -// File:src/materials/MeshNormalMaterial.js + object = new THREE.PointLight( data.color, data.intensity, data.distance ); -/** - * @author mrdoob / http://mrdoob.com/ - * - * parameters = { - * opacity: , - * - * shading: THREE.FlatShading, - * blending: THREE.NormalBlending, - * depthTest: , - * depthWrite: , - * - * wireframe: , - * wireframeLinewidth: - * } - */ + break; -THREE.MeshNormalMaterial = function ( parameters ) { + case 'SpotLight': - THREE.Material.call( this, parameters ); + object = new THREE.SpotLight( data.color, data.intensity, data.distance, data.angle, data.exponent ); - this.shading = THREE.FlatShading; + break; - this.wireframe = false; - this.wireframeLinewidth = 1; + case 'HemisphereLight': - this.morphTargets = false; + object = new THREE.HemisphereLight( data.color, data.groundColor, data.intensity ); - this.setValues( parameters ); + break; -}; + case 'Mesh': -THREE.MeshNormalMaterial.prototype = Object.create( THREE.Material.prototype ); + var geometry = geometries[ data.geometry ]; + var material = materials[ data.material ]; -THREE.MeshNormalMaterial.prototype.clone = function () { + if ( geometry === undefined ) { - var material = new THREE.MeshNormalMaterial(); + console.warn( 'THREE.ObjectLoader: Undefined geometry', data.geometry ); - THREE.Material.prototype.clone.call( this, material ); + } - material.shading = this.shading; + if ( material === undefined ) { - material.wireframe = this.wireframe; - material.wireframeLinewidth = this.wireframeLinewidth; + console.warn( 'THREE.ObjectLoader: Undefined material', data.material ); - return material; + } -}; + object = new THREE.Mesh( geometry, material ); -// File:src/materials/MeshFaceMaterial.js + break; -/** - * @author mrdoob / http://mrdoob.com/ - */ + case 'Line': -THREE.MeshFaceMaterial = function ( materials ) { + var geometry = geometries[ data.geometry ]; + var material = materials[ data.material ]; - this.materials = materials instanceof Array ? materials : []; + if ( geometry === undefined ) { -}; + console.warn( 'THREE.ObjectLoader: Undefined geometry', data.geometry ); -THREE.MeshFaceMaterial.prototype.clone = function () { + } - var material = new THREE.MeshFaceMaterial(); + if ( material === undefined ) { - for ( var i = 0; i < this.materials.length; i ++ ) { + console.warn( 'THREE.ObjectLoader: Undefined material', data.material ); - material.materials.push( this.materials[ i ].clone() ); + } - } + object = new THREE.Line( geometry, material ); - return material; + break; -}; + case 'Sprite': -// File:src/materials/PointCloudMaterial.js - -/** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * opacity: , - * map: new THREE.Texture( ), - * - * size: , - * - * blending: THREE.NormalBlending, - * depthTest: , - * depthWrite: , - * - * vertexColors: , - * - * fog: - * } - */ + var material = materials[ data.material ]; -THREE.PointCloudMaterial = function ( parameters ) { + if ( material === undefined ) { - THREE.Material.call( this ); + console.warn( 'THREE.ObjectLoader: Undefined material', data.material ); - this.color = new THREE.Color( 0xffffff ); + } - this.map = null; + object = new THREE.Sprite( material ); - this.size = 1; - this.sizeAttenuation = true; + break; - this.vertexColors = THREE.NoColors; + case 'Group': - this.fog = true; + object = new THREE.Group(); - this.setValues( parameters ); + break; -}; + default: -THREE.PointCloudMaterial.prototype = Object.create( THREE.Material.prototype ); + object = new THREE.Object3D(); -THREE.PointCloudMaterial.prototype.clone = function () { + } - var material = new THREE.PointCloudMaterial(); + object.uuid = data.uuid; - THREE.Material.prototype.clone.call( this, material ); + if ( data.name !== undefined ) object.name = data.name; + if ( data.matrix !== undefined ) { - material.color.copy( this.color ); + matrix.fromArray( data.matrix ); + matrix.decompose( object.position, object.quaternion, object.scale ); - material.map = this.map; + } else { - material.size = this.size; - material.sizeAttenuation = this.sizeAttenuation; + if ( data.position !== undefined ) object.position.fromArray( data.position ); + if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation ); + if ( data.scale !== undefined ) object.scale.fromArray( data.scale ); - material.vertexColors = this.vertexColors; + } - material.fog = this.fog; + if ( data.visible !== undefined ) object.visible = data.visible; + if ( data.userData !== undefined ) object.userData = data.userData; - return material; + if ( data.children !== undefined ) { -}; + for ( var child in data.children ) { -// backwards compatibility + object.add( this.parseObject( data.children[ child ], geometries, materials ) ); -THREE.ParticleBasicMaterial = function ( parameters ) { + } - console.warn( 'THREE.ParticleBasicMaterial has been renamed to THREE.PointCloudMaterial.' ); - return new THREE.PointCloudMaterial( parameters ); + } -}; + return object; -THREE.ParticleSystemMaterial = function ( parameters ) { + } - console.warn( 'THREE.ParticleSystemMaterial has been renamed to THREE.PointCloudMaterial.' ); - return new THREE.PointCloudMaterial( parameters ); + }() }; -// File:src/materials/ShaderMaterial.js +// File:src/loaders/TextureLoader.js /** - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * defines: { "label" : "value" }, - * uniforms: { "parameter1": { type: "f", value: 1.0 }, "parameter2": { type: "i" value2: 2 } }, - * - * fragmentShader: , - * vertexShader: , - * - * shading: THREE.SmoothShading, - * blending: THREE.NormalBlending, - * depthTest: , - * depthWrite: , - * - * wireframe: , - * wireframeLinewidth: , - * - * lights: , - * - * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, - * - * skinning: , - * morphTargets: , - * morphNormals: , - * - * fog: - * } + * @author mrdoob / http://mrdoob.com/ */ -THREE.ShaderMaterial = function ( parameters ) { +THREE.TextureLoader = function ( manager ) { - THREE.Material.call( this ); + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; - this.defines = {}; - this.uniforms = {}; - this.attributes = null; +}; - this.vertexShader = 'void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}'; - this.fragmentShader = 'void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}'; +THREE.TextureLoader.prototype = { - this.shading = THREE.SmoothShading; + constructor: THREE.TextureLoader, - this.linewidth = 1; + load: function ( url, onLoad, onProgress, onError ) { - this.wireframe = false; - this.wireframeLinewidth = 1; + var scope = this; - this.fog = false; // set to use scene fog + var loader = new THREE.ImageLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( image ) { - this.lights = false; // set to use scene lights + var texture = new THREE.Texture( image ); + texture.needsUpdate = true; - this.vertexColors = THREE.NoColors; // set to use "color" attribute stream + if ( onLoad !== undefined ) { - this.skinning = false; // set to use skinning attribute streams + onLoad( texture ); - this.morphTargets = false; // set to use morph targets - this.morphNormals = false; // set to use morph normals + } - // When rendered geometry doesn't include these attributes but the material does, - // use these default values in WebGL. This avoids errors when buffer data is missing. - this.defaultAttributeValues = { - 'color': [ 1, 1, 1 ], - 'uv': [ 0, 0 ], - 'uv2': [ 0, 0 ] - }; + }, onProgress, onError ); - this.index0AttributeName = undefined; + }, - this.setValues( parameters ); + setCrossOrigin: function ( value ) { -}; + this.crossOrigin = value; -THREE.ShaderMaterial.prototype = Object.create( THREE.Material.prototype ); + } -THREE.ShaderMaterial.prototype.clone = function () { +}; - var material = new THREE.ShaderMaterial(); +// File:src/loaders/CompressedTextureLoader.js - THREE.Material.prototype.clone.call( this, material ); +/** + * @author mrdoob / http://mrdoob.com/ + * + * Abstract Base class to block based textures loader (dds, pvr, ...) + */ - material.fragmentShader = this.fragmentShader; - material.vertexShader = this.vertexShader; +THREE.CompressedTextureLoader = function () { - material.uniforms = THREE.UniformsUtils.clone( this.uniforms ); + // override in sub classes + this._parser = null; - material.attributes = this.attributes; - material.defines = this.defines; +}; - material.shading = this.shading; - material.wireframe = this.wireframe; - material.wireframeLinewidth = this.wireframeLinewidth; +THREE.CompressedTextureLoader.prototype = { - material.fog = this.fog; + constructor: THREE.CompressedTextureLoader, - material.lights = this.lights; + load: function ( url, onLoad, onError ) { - material.vertexColors = this.vertexColors; + var scope = this; - material.skinning = this.skinning; + var images = []; - material.morphTargets = this.morphTargets; - material.morphNormals = this.morphNormals; + var texture = new THREE.CompressedTexture(); + texture.image = images; - return material; + var loader = new THREE.XHRLoader(); + loader.setResponseType( 'arraybuffer' ); -}; + if ( url instanceof Array ) { -// File:src/materials/RawShaderMaterial.js + var loaded = 0; -/** - * @author mrdoob / http://mrdoob.com/ - */ + var loadTexture = function ( i ) { -THREE.RawShaderMaterial = function ( parameters ) { + loader.load( url[ i ], function ( buffer ) { - THREE.ShaderMaterial.call( this, parameters ); + var texDatas = scope._parser( buffer, true ); -}; + images[ i ] = { + width: texDatas.width, + height: texDatas.height, + format: texDatas.format, + mipmaps: texDatas.mipmaps + }; -THREE.RawShaderMaterial.prototype = Object.create( THREE.ShaderMaterial.prototype ); + loaded += 1; -THREE.RawShaderMaterial.prototype.clone = function () { + if ( loaded === 6 ) { - var material = new THREE.RawShaderMaterial(); + if (texDatas.mipmapCount == 1) + texture.minFilter = THREE.LinearFilter; - THREE.ShaderMaterial.prototype.clone.call( this, material ); + texture.format = texDatas.format; + texture.needsUpdate = true; - return material; + if ( onLoad ) onLoad( texture ); -}; + } -// File:src/materials/SpriteMaterial.js + } ); -/** - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * opacity: , - * map: new THREE.Texture( ), - * - * blending: THREE.NormalBlending, - * depthTest: , - * depthWrite: , - * - * uvOffset: new THREE.Vector2(), - * uvScale: new THREE.Vector2(), - * - * fog: - * } - */ + }; -THREE.SpriteMaterial = function ( parameters ) { + for ( var i = 0, il = url.length; i < il; ++ i ) { - THREE.Material.call( this ); + loadTexture( i ); - // defaults + } - this.color = new THREE.Color( 0xffffff ); - this.map = null; + } else { - this.rotation = 0; + // compressed cubemap texture stored in a single DDS file - this.fog = false; + loader.load( url, function ( buffer ) { - // set parameters + var texDatas = scope._parser( buffer, true ); - this.setValues( parameters ); + if ( texDatas.isCubemap ) { -}; + var faces = texDatas.mipmaps.length / texDatas.mipmapCount; -THREE.SpriteMaterial.prototype = Object.create( THREE.Material.prototype ); + for ( var f = 0; f < faces; f ++ ) { -THREE.SpriteMaterial.prototype.clone = function () { + images[ f ] = { mipmaps : [] }; - var material = new THREE.SpriteMaterial(); + for ( var i = 0; i < texDatas.mipmapCount; i ++ ) { - THREE.Material.prototype.clone.call( this, material ); + images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] ); + images[ f ].format = texDatas.format; + images[ f ].width = texDatas.width; + images[ f ].height = texDatas.height; - material.color.copy( this.color ); - material.map = this.map; + } - material.rotation = this.rotation; + } - material.fog = this.fog; + } else { - return material; + texture.image.width = texDatas.width; + texture.image.height = texDatas.height; + texture.mipmaps = texDatas.mipmaps; -}; + } -// File:src/materials/SpriteCanvasMaterial.js + if ( texDatas.mipmapCount === 1 ) { -/** - * @author mrdoob / http://mrdoob.com/ - * - * parameters = { - * color: , - * program: , - * opacity: , - * blending: THREE.NormalBlending - * } - */ + texture.minFilter = THREE.LinearFilter; -THREE.SpriteCanvasMaterial = function ( parameters ) { - - THREE.Material.call( this ); - - this.color = new THREE.Color( 0xffffff ); - this.program = function ( context, color ) {}; - - this.setValues( parameters ); - -}; + } -THREE.SpriteCanvasMaterial.prototype = Object.create( THREE.Material.prototype ); + texture.format = texDatas.format; + texture.needsUpdate = true; -THREE.SpriteCanvasMaterial.prototype.clone = function () { + if ( onLoad ) onLoad( texture ); - var material = new THREE.SpriteCanvasMaterial(); + } ); - THREE.Material.prototype.clone.call( this, material ); + } - material.color.copy( this.color ); - material.program = this.program; + return texture; - return material; + } }; -// backwards compatibility - -THREE.ParticleCanvasMaterial = THREE.SpriteCanvasMaterial; - -// File:src/textures/Texture.js +// File:src/materials/Material.js /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ - * @author szimek / https://github.com/szimek/ */ -THREE.Texture = function ( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { +THREE.Material = function () { + + Object.defineProperty( this, 'id', { value: THREE.MaterialIdCount ++ } ); - this.id = THREE.TextureIdCount ++; this.uuid = THREE.Math.generateUUID(); this.name = ''; + this.type = 'Material'; - this.image = image !== undefined ? image : THREE.Texture.DEFAULT_IMAGE; - this.mipmaps = []; + this.side = THREE.FrontSide; - this.mapping = mapping !== undefined ? mapping : THREE.Texture.DEFAULT_MAPPING; + this.opacity = 1; + this.transparent = false; - this.wrapS = wrapS !== undefined ? wrapS : THREE.ClampToEdgeWrapping; - this.wrapT = wrapT !== undefined ? wrapT : THREE.ClampToEdgeWrapping; + this.blending = THREE.NormalBlending; - this.magFilter = magFilter !== undefined ? magFilter : THREE.LinearFilter; - this.minFilter = minFilter !== undefined ? minFilter : THREE.LinearMipMapLinearFilter; + this.blendSrc = THREE.SrcAlphaFactor; + this.blendDst = THREE.OneMinusSrcAlphaFactor; + this.blendEquation = THREE.AddEquation; - this.anisotropy = anisotropy !== undefined ? anisotropy : 1; + this.depthTest = true; + this.depthWrite = true; - this.format = format !== undefined ? format : THREE.RGBAFormat; - this.type = type !== undefined ? type : THREE.UnsignedByteType; + this.polygonOffset = false; + this.polygonOffsetFactor = 0; + this.polygonOffsetUnits = 0; - this.offset = new THREE.Vector2( 0, 0 ); - this.repeat = new THREE.Vector2( 1, 1 ); + this.alphaTest = 0; - this.generateMipmaps = true; - this.premultiplyAlpha = false; - this.flipY = true; - this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) + this.overdraw = 0; // Overdrawn pixels (typically between 0 and 1) for fixing antialiasing gaps in CanvasRenderer - this._needsUpdate = false; - this.onUpdate = null; + this.visible = true; + + this.needsUpdate = true; }; -THREE.Texture.DEFAULT_IMAGE = undefined; -THREE.Texture.DEFAULT_MAPPING = new THREE.UVMapping(); +THREE.Material.prototype = { -THREE.Texture.prototype = { + constructor: THREE.Material, - constructor: THREE.Texture, + setValues: function ( values ) { - get needsUpdate () { + if ( values === undefined ) return; - return this._needsUpdate; + for ( var key in values ) { - }, + var newValue = values[ key ]; - set needsUpdate ( value ) { + if ( newValue === undefined ) { - if ( value === true ) this.update(); + console.warn( "THREE.Material: '" + key + "' parameter is undefined." ); + continue; - this._needsUpdate = value; + } - }, + if ( key in this ) { - clone: function ( texture ) { + var currentValue = this[ key ]; - if ( texture === undefined ) texture = new THREE.Texture(); + if ( currentValue instanceof THREE.Color ) { - texture.image = this.image; - texture.mipmaps = this.mipmaps.slice( 0 ); + currentValue.set( newValue ); - texture.mapping = this.mapping; + } else if ( currentValue instanceof THREE.Vector3 && newValue instanceof THREE.Vector3 ) { - texture.wrapS = this.wrapS; - texture.wrapT = this.wrapT; + currentValue.copy( newValue ); - texture.magFilter = this.magFilter; - texture.minFilter = this.minFilter; + } else if ( key == 'overdraw' ) { - texture.anisotropy = this.anisotropy; + // ensure overdraw is backwards-compatable with legacy boolean type + this[ key ] = Number( newValue ); - texture.format = this.format; - texture.type = this.type; + } else { - texture.offset.copy( this.offset ); - texture.repeat.copy( this.repeat ); + this[ key ] = newValue; - texture.generateMipmaps = this.generateMipmaps; - texture.premultiplyAlpha = this.premultiplyAlpha; - texture.flipY = this.flipY; - texture.unpackAlignment = this.unpackAlignment; + } - return texture; + } + + } }, - update: function () { + toJSON: function () { - this.dispatchEvent( { type: 'update' } ); + var output = { + metadata: { + version: 4.2, + type: 'material', + generator: 'MaterialExporter' + }, + uuid: this.uuid, + type: this.type + }; - }, + if ( this.name !== "" ) output.name = this.name; - dispose: function () { + if ( this instanceof THREE.MeshBasicMaterial ) { - this.dispatchEvent( { type: 'dispose' } ); + output.color = this.color.getHex(); + if ( this.vertexColors !== THREE.NoColors ) output.vertexColors = this.vertexColors; + if ( this.blending !== THREE.NormalBlending ) output.blending = this.blending; + if ( this.side !== THREE.FrontSide ) output.side = this.side; - } + } else if ( this instanceof THREE.MeshLambertMaterial ) { -}; + output.color = this.color.getHex(); + output.ambient = this.ambient.getHex(); + output.emissive = this.emissive.getHex(); + if ( this.vertexColors !== THREE.NoColors ) output.vertexColors = this.vertexColors; + if ( this.blending !== THREE.NormalBlending ) output.blending = this.blending; + if ( this.side !== THREE.FrontSide ) output.side = this.side; -THREE.EventDispatcher.prototype.apply( THREE.Texture.prototype ); + } else if ( this instanceof THREE.MeshPhongMaterial ) { -THREE.TextureIdCount = 0; + output.color = this.color.getHex(); + output.ambient = this.ambient.getHex(); + output.emissive = this.emissive.getHex(); + output.specular = this.specular.getHex(); + output.shininess = this.shininess; + if ( this.vertexColors !== THREE.NoColors ) output.vertexColors = this.vertexColors; + if ( this.blending !== THREE.NormalBlending ) output.blending = this.blending; + if ( this.side !== THREE.FrontSide ) output.side = this.side; -// File:src/textures/CubeTexture.js + } else if ( this instanceof THREE.MeshNormalMaterial ) { -/** - * @author mrdoob / http://mrdoob.com/ - */ + if ( this.shading !== THREE.FlatShading ) output.shading = this.shading; + if ( this.blending !== THREE.NormalBlending ) output.blending = this.blending; + if ( this.side !== THREE.FrontSide ) output.side = this.side; -THREE.CubeTexture = function ( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { + } else if ( this instanceof THREE.MeshDepthMaterial ) { - THREE.Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + if ( this.blending !== THREE.NormalBlending ) output.blending = this.blending; + if ( this.side !== THREE.FrontSide ) output.side = this.side; - this.images = images; + } else if ( this instanceof THREE.ShaderMaterial ) { -}; + output.uniforms = this.uniforms; + output.vertexShader = this.vertexShader; + output.fragmentShader = this.fragmentShader; -THREE.CubeTexture.prototype = Object.create( THREE.Texture.prototype ); + } else if ( this instanceof THREE.SpriteMaterial ) { -THREE.CubeTexture.clone = function ( texture ) { + output.color = this.color.getHex(); - if ( texture === undefined ) texture = new THREE.CubeTexture(); + } - THREE.Texture.prototype.clone.call( this, texture ); + if ( this.opacity < 1 ) output.opacity = this.opacity; + if ( this.transparent !== false ) output.transparent = this.transparent; + if ( this.wireframe !== false ) output.wireframe = this.wireframe; - texture.images = this.images; + return output; - return texture; + }, -}; + clone: function ( material ) { -// File:src/textures/CompressedTexture.js + if ( material === undefined ) material = new THREE.Material(); -/** - * @author alteredq / http://alteredqualia.com/ - */ + material.name = this.name; -THREE.CompressedTexture = function ( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) { + material.side = this.side; - THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + material.opacity = this.opacity; + material.transparent = this.transparent; - this.image = { width: width, height: height }; - this.mipmaps = mipmaps; + material.blending = this.blending; + + material.blendSrc = this.blendSrc; + material.blendDst = this.blendDst; + material.blendEquation = this.blendEquation; - this.generateMipmaps = false; // WebGL currently can't generate mipmaps for compressed textures, they must be embedded in DDS file + material.depthTest = this.depthTest; + material.depthWrite = this.depthWrite; -}; + material.polygonOffset = this.polygonOffset; + material.polygonOffsetFactor = this.polygonOffsetFactor; + material.polygonOffsetUnits = this.polygonOffsetUnits; -THREE.CompressedTexture.prototype = Object.create( THREE.Texture.prototype ); + material.alphaTest = this.alphaTest; -THREE.CompressedTexture.prototype.clone = function () { + material.overdraw = this.overdraw; - var texture = new THREE.CompressedTexture(); + material.visible = this.visible; - THREE.Texture.prototype.clone.call( this, texture ); + return material; - return texture; + }, + + dispose: function () { + + this.dispatchEvent( { type: 'dispose' } ); + + } }; -// File:src/textures/DataTexture.js +THREE.EventDispatcher.prototype.apply( THREE.Material.prototype ); + +THREE.MaterialIdCount = 0; + +// File:src/materials/LineBasicMaterial.js /** + * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * linewidth: , + * linecap: "round", + * linejoin: "round", + * + * vertexColors: + * + * fog: + * } */ -THREE.DataTexture = function ( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) { - - THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); +THREE.LineBasicMaterial = function ( parameters ) { - this.image = { data: data, width: width, height: height }; + THREE.Material.call( this ); -}; + this.type = 'LineBasicMaterial'; -THREE.DataTexture.prototype = Object.create( THREE.Texture.prototype ); + this.color = new THREE.Color( 0xffffff ); -THREE.DataTexture.prototype.clone = function () { + this.linewidth = 1; + this.linecap = 'round'; + this.linejoin = 'round'; - var texture = new THREE.DataTexture(); + this.vertexColors = THREE.NoColors; - THREE.Texture.prototype.clone.call( this, texture ); + this.fog = true; - return texture; + this.setValues( parameters ); }; -// File:src/objects/PointCloud.js +THREE.LineBasicMaterial.prototype = Object.create( THREE.Material.prototype ); -/** - * @author alteredq / http://alteredqualia.com/ - */ +THREE.LineBasicMaterial.prototype.clone = function () { -THREE.PointCloud = function ( geometry, material ) { + var material = new THREE.LineBasicMaterial(); - THREE.Object3D.call( this ); + THREE.Material.prototype.clone.call( this, material ); - this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); - this.material = material !== undefined ? material : new THREE.PointCloudMaterial( { color: Math.random() * 0xffffff } ); + material.color.copy( this.color ); - this.sortParticles = false; + material.linewidth = this.linewidth; + material.linecap = this.linecap; + material.linejoin = this.linejoin; -}; + material.vertexColors = this.vertexColors; -THREE.PointCloud.prototype = Object.create( THREE.Object3D.prototype ); + material.fog = this.fog; -THREE.PointCloud.prototype.raycast = ( function () { + return material; - var inverseMatrix = new THREE.Matrix4(); - var ray = new THREE.Ray(); +}; - return function ( raycaster, intersects ) { +// File:src/materials/LineDashedMaterial.js - var object = this; - var geometry = object.geometry; - var threshold = raycaster.params.PointCloud.threshold; - - inverseMatrix.getInverse( this.matrixWorld ); - ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); - - if ( geometry.boundingBox !== null ) { +/** + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * linewidth: , + * + * scale: , + * dashSize: , + * gapSize: , + * + * vertexColors: + * + * fog: + * } + */ - if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) { +THREE.LineDashedMaterial = function ( parameters ) { - return; + THREE.Material.call( this ); - } + this.type = 'LineDashedMaterial'; - } + this.color = new THREE.Color( 0xffffff ); - var localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); - var position = new THREE.Vector3(); + this.linewidth = 1; - var testPoint = function ( point, index ) { + this.scale = 1; + this.dashSize = 3; + this.gapSize = 1; - var rayPointDistance = ray.distanceToPoint( point ); + this.vertexColors = false; - if ( rayPointDistance < localThreshold ) { + this.fog = true; - var intersectPoint = ray.closestPointToPoint( point ); - intersectPoint.applyMatrix4( object.matrixWorld ); + this.setValues( parameters ); - var distance = raycaster.ray.origin.distanceTo( intersectPoint ); +}; - intersects.push( { +THREE.LineDashedMaterial.prototype = Object.create( THREE.Material.prototype ); - distance: distance, - distanceToRay: rayPointDistance, - point: intersectPoint.clone(), - index: index, - face: null, - object: object +THREE.LineDashedMaterial.prototype.clone = function () { - } ); + var material = new THREE.LineDashedMaterial(); - } + THREE.Material.prototype.clone.call( this, material ); - }; + material.color.copy( this.color ); - if ( geometry instanceof THREE.BufferGeometry ) { + material.linewidth = this.linewidth; - var attributes = geometry.attributes; - var positions = attributes.position.array; + material.scale = this.scale; + material.dashSize = this.dashSize; + material.gapSize = this.gapSize; - if ( attributes.index !== undefined ) { + material.vertexColors = this.vertexColors; - var indices = attributes.index.array; - var offsets = geometry.offsets; + material.fog = this.fog; - if ( offsets.length === 0 ) { + return material; - var offset = { - start: 0, - count: indices.length, - index: 0 - }; +}; - offsets = [ offset ]; +// File:src/materials/MeshBasicMaterial.js - } +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * map: new THREE.Texture( ), + * + * lightMap: new THREE.Texture( ), + * + * specularMap: new THREE.Texture( ), + * + * alphaMap: new THREE.Texture( ), + * + * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.Multiply, + * reflectivity: , + * refractionRatio: , + * + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: , + * + * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, + * + * skinning: , + * morphTargets: , + * + * fog: + * } + */ - for ( var oi = 0, ol = offsets.length; oi < ol; ++oi ) { +THREE.MeshBasicMaterial = function ( parameters ) { - var start = offsets[ oi ].start; - var count = offsets[ oi ].count; - var index = offsets[ oi ].index; + THREE.Material.call( this ); - for ( var i = start, il = start + count; i < il; i ++ ) { + this.type = 'MeshBasicMaterial'; - var a = index + indices[ i ]; + this.color = new THREE.Color( 0xffffff ); // emissive - position.set( - positions[ a * 3 ], - positions[ a * 3 + 1 ], - positions[ a * 3 + 2 ] - ); + this.map = null; - testPoint( position, a ); + this.lightMap = null; - } + this.specularMap = null; - } + this.alphaMap = null; - } else { + this.envMap = null; + this.combine = THREE.MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; - var pointCount = positions.length / 3; + this.fog = true; - for ( var i = 0; i < pointCount; i ++ ) { + this.shading = THREE.SmoothShading; - position.set( - positions[ 3 * i ], - positions[ 3 * i + 1 ], - positions[ 3 * i + 2 ] - ); + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; - testPoint( position, i ); + this.vertexColors = THREE.NoColors; - } + this.skinning = false; + this.morphTargets = false; - } + this.setValues( parameters ); - } else { +}; - var vertices = this.geometry.vertices; +THREE.MeshBasicMaterial.prototype = Object.create( THREE.Material.prototype ); - for ( var i = 0; i < vertices.length; i ++ ) { +THREE.MeshBasicMaterial.prototype.clone = function () { - testPoint( vertices[ i ], i ); + var material = new THREE.MeshBasicMaterial(); - } + THREE.Material.prototype.clone.call( this, material ); - } + material.color.copy( this.color ); - }; + material.map = this.map; -}() ); + material.lightMap = this.lightMap; -THREE.PointCloud.prototype.clone = function ( object ) { + material.specularMap = this.specularMap; - if ( object === undefined ) object = new THREE.PointCloud( this.geometry, this.material ); + material.alphaMap = this.alphaMap; - object.sortParticles = this.sortParticles; + material.envMap = this.envMap; + material.combine = this.combine; + material.reflectivity = this.reflectivity; + material.refractionRatio = this.refractionRatio; - THREE.Object3D.prototype.clone.call( this, object ); + material.fog = this.fog; - return object; + material.shading = this.shading; -}; + material.wireframe = this.wireframe; + material.wireframeLinewidth = this.wireframeLinewidth; + material.wireframeLinecap = this.wireframeLinecap; + material.wireframeLinejoin = this.wireframeLinejoin; -// Backwards compatibility + material.vertexColors = this.vertexColors; -THREE.ParticleSystem = function ( geometry, material ) { + material.skinning = this.skinning; + material.morphTargets = this.morphTargets; - console.warn( 'THREE.ParticleSystem has been renamed to THREE.PointCloud.' ); - return new THREE.PointCloud( geometry, material ); + return material; }; -// File:src/objects/Line.js +// File:src/materials/MeshLambertMaterial.js /** * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * ambient: , + * emissive: , + * opacity: , + * + * map: new THREE.Texture( ), + * + * lightMap: new THREE.Texture( ), + * + * specularMap: new THREE.Texture( ), + * + * alphaMap: new THREE.Texture( ), + * + * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.Multiply, + * reflectivity: , + * refractionRatio: , + * + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: , + * + * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, + * + * skinning: , + * morphTargets: , + * morphNormals: , + * + * fog: + * } */ -THREE.Line = function ( geometry, material, type ) { +THREE.MeshLambertMaterial = function ( parameters ) { - THREE.Object3D.call( this ); + THREE.Material.call( this ); - this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); - this.material = material !== undefined ? material : new THREE.LineBasicMaterial( { color: Math.random() * 0xffffff } ); + this.type = 'MeshLambertMaterial'; - this.type = ( type !== undefined ) ? type : THREE.LineStrip; + this.color = new THREE.Color( 0xffffff ); // diffuse + this.ambient = new THREE.Color( 0xffffff ); + this.emissive = new THREE.Color( 0x000000 ); -}; - -THREE.LineStrip = 0; -THREE.LinePieces = 1; - -THREE.Line.prototype = Object.create( THREE.Object3D.prototype ); - -THREE.Line.prototype.raycast = ( function () { - - var inverseMatrix = new THREE.Matrix4(); - var ray = new THREE.Ray(); - var sphere = new THREE.Sphere(); + this.wrapAround = false; + this.wrapRGB = new THREE.Vector3( 1, 1, 1 ); - return function ( raycaster, intersects ) { + this.map = null; - var precision = raycaster.linePrecision; - var precisionSq = precision * precision; + this.lightMap = null; - var geometry = this.geometry; + this.specularMap = null; - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + this.alphaMap = null; - // Checking boundingSphere distance to ray + this.envMap = null; + this.combine = THREE.MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; - sphere.copy( geometry.boundingSphere ); - sphere.applyMatrix4( this.matrixWorld ); + this.fog = true; - if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) { + this.shading = THREE.SmoothShading; - return; + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; - } + this.vertexColors = THREE.NoColors; - inverseMatrix.getInverse( this.matrixWorld ); - ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); + this.skinning = false; + this.morphTargets = false; + this.morphNormals = false; - /* if ( geometry instanceof THREE.BufferGeometry ) { + this.setValues( parameters ); - } else */ if ( geometry instanceof THREE.Geometry ) { +}; - var vertices = geometry.vertices; - var nbVertices = vertices.length; - var interSegment = new THREE.Vector3(); - var interRay = new THREE.Vector3(); - var step = this.type === THREE.LineStrip ? 1 : 2; +THREE.MeshLambertMaterial.prototype = Object.create( THREE.Material.prototype ); - for ( var i = 0; i < nbVertices - 1; i = i + step ) { +THREE.MeshLambertMaterial.prototype.clone = function () { - var distSq = ray.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment ); + var material = new THREE.MeshLambertMaterial(); - if ( distSq > precisionSq ) continue; + THREE.Material.prototype.clone.call( this, material ); - var distance = ray.origin.distanceTo( interRay ); + material.color.copy( this.color ); + material.ambient.copy( this.ambient ); + material.emissive.copy( this.emissive ); - if ( distance < raycaster.near || distance > raycaster.far ) continue; + material.wrapAround = this.wrapAround; + material.wrapRGB.copy( this.wrapRGB ); - intersects.push( { + material.map = this.map; - distance: distance, - // What do we want? intersection point on the ray or on the segment?? - // point: raycaster.ray.at( distance ), - point: interSegment.clone().applyMatrix4( this.matrixWorld ), - face: null, - faceIndex: null, - object: this + material.lightMap = this.lightMap; - } ); + material.specularMap = this.specularMap; - } + material.alphaMap = this.alphaMap; - } + material.envMap = this.envMap; + material.combine = this.combine; + material.reflectivity = this.reflectivity; + material.refractionRatio = this.refractionRatio; - }; + material.fog = this.fog; -}() ); + material.shading = this.shading; -THREE.Line.prototype.clone = function ( object ) { + material.wireframe = this.wireframe; + material.wireframeLinewidth = this.wireframeLinewidth; + material.wireframeLinecap = this.wireframeLinecap; + material.wireframeLinejoin = this.wireframeLinejoin; - if ( object === undefined ) object = new THREE.Line( this.geometry, this.material, this.type ); + material.vertexColors = this.vertexColors; - THREE.Object3D.prototype.clone.call( this, object ); + material.skinning = this.skinning; + material.morphTargets = this.morphTargets; + material.morphNormals = this.morphNormals; - return object; + return material; }; -// File:src/objects/Mesh.js +// File:src/materials/MeshPhongMaterial.js /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ - * @author mikael emtinger / http://gomo.se/ - * @author jonobr1 / http://jonobr1.com/ + * + * parameters = { + * color: , + * ambient: , + * emissive: , + * specular: , + * shininess: , + * opacity: , + * + * map: new THREE.Texture( ), + * + * lightMap: new THREE.Texture( ), + * + * bumpMap: new THREE.Texture( ), + * bumpScale: , + * + * normalMap: new THREE.Texture( ), + * normalScale: , + * + * specularMap: new THREE.Texture( ), + * + * alphaMap: new THREE.Texture( ), + * + * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.Multiply, + * reflectivity: , + * refractionRatio: , + * + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: , + * + * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, + * + * skinning: , + * morphTargets: , + * morphNormals: , + * + * fog: + * } */ -THREE.Mesh = function ( geometry, material ) { - - THREE.Object3D.call( this ); +THREE.MeshPhongMaterial = function ( parameters ) { - this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); - this.material = material !== undefined ? material : new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff } ); + THREE.Material.call( this ); - this.updateMorphTargets(); + this.type = 'MeshPhongMaterial'; -}; + this.color = new THREE.Color( 0xffffff ); // diffuse + this.ambient = new THREE.Color( 0xffffff ); + this.emissive = new THREE.Color( 0x000000 ); + this.specular = new THREE.Color( 0x111111 ); + this.shininess = 30; -THREE.Mesh.prototype = Object.create( THREE.Object3D.prototype ); + this.metal = false; -THREE.Mesh.prototype.updateMorphTargets = function () { + this.wrapAround = false; + this.wrapRGB = new THREE.Vector3( 1, 1, 1 ); - if ( this.geometry.morphTargets !== undefined && this.geometry.morphTargets.length > 0 ) { + this.map = null; - this.morphTargetBase = - 1; - this.morphTargetForcedOrder = []; - this.morphTargetInfluences = []; - this.morphTargetDictionary = {}; + this.lightMap = null; - for ( var m = 0, ml = this.geometry.morphTargets.length; m < ml; m ++ ) { + this.bumpMap = null; + this.bumpScale = 1; - this.morphTargetInfluences.push( 0 ); - this.morphTargetDictionary[ this.geometry.morphTargets[ m ].name ] = m; + this.normalMap = null; + this.normalScale = new THREE.Vector2( 1, 1 ); - } + this.specularMap = null; - } + this.alphaMap = null; -}; + this.envMap = null; + this.combine = THREE.MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; -THREE.Mesh.prototype.getMorphTargetIndexByName = function ( name ) { + this.fog = true; - if ( this.morphTargetDictionary[ name ] !== undefined ) { + this.shading = THREE.SmoothShading; - return this.morphTargetDictionary[ name ]; + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; - } + this.vertexColors = THREE.NoColors; - console.log( 'THREE.Mesh.getMorphTargetIndexByName: morph target ' + name + ' does not exist. Returning 0.' ); + this.skinning = false; + this.morphTargets = false; + this.morphNormals = false; - return 0; + this.setValues( parameters ); }; +THREE.MeshPhongMaterial.prototype = Object.create( THREE.Material.prototype ); -THREE.Mesh.prototype.raycast = ( function () { +THREE.MeshPhongMaterial.prototype.clone = function () { - var inverseMatrix = new THREE.Matrix4(); - var ray = new THREE.Ray(); - var sphere = new THREE.Sphere(); + var material = new THREE.MeshPhongMaterial(); - var vA = new THREE.Vector3(); - var vB = new THREE.Vector3(); - var vC = new THREE.Vector3(); + THREE.Material.prototype.clone.call( this, material ); - return function ( raycaster, intersects ) { + material.color.copy( this.color ); + material.ambient.copy( this.ambient ); + material.emissive.copy( this.emissive ); + material.specular.copy( this.specular ); + material.shininess = this.shininess; - var geometry = this.geometry; + material.metal = this.metal; - // Checking boundingSphere distance to ray + material.wrapAround = this.wrapAround; + material.wrapRGB.copy( this.wrapRGB ); - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + material.map = this.map; - sphere.copy( geometry.boundingSphere ); - sphere.applyMatrix4( this.matrixWorld ); + material.lightMap = this.lightMap; - if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) { + material.bumpMap = this.bumpMap; + material.bumpScale = this.bumpScale; - return; + material.normalMap = this.normalMap; + material.normalScale.copy( this.normalScale ); - } + material.specularMap = this.specularMap; - // Check boundingBox before continuing + material.alphaMap = this.alphaMap; - inverseMatrix.getInverse( this.matrixWorld ); - ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); + material.envMap = this.envMap; + material.combine = this.combine; + material.reflectivity = this.reflectivity; + material.refractionRatio = this.refractionRatio; - if ( geometry.boundingBox !== null ) { + material.fog = this.fog; - if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) { + material.shading = this.shading; - return; + material.wireframe = this.wireframe; + material.wireframeLinewidth = this.wireframeLinewidth; + material.wireframeLinecap = this.wireframeLinecap; + material.wireframeLinejoin = this.wireframeLinejoin; - } + material.vertexColors = this.vertexColors; - } + material.skinning = this.skinning; + material.morphTargets = this.morphTargets; + material.morphNormals = this.morphNormals; - if ( geometry instanceof THREE.BufferGeometry ) { + return material; - var material = this.material; +}; - if ( material === undefined ) return; +// File:src/materials/MeshDepthMaterial.js - var attributes = geometry.attributes; +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * opacity: , + * + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: + * } + */ - var a, b, c; - var precision = raycaster.precision; +THREE.MeshDepthMaterial = function ( parameters ) { - if ( attributes.index !== undefined ) { + THREE.Material.call( this ); - var indices = attributes.index.array; - var positions = attributes.position.array; - var offsets = geometry.offsets; + this.type = 'MeshDepthMaterial'; - if ( offsets.length === 0 ) { + this.morphTargets = false; + this.wireframe = false; + this.wireframeLinewidth = 1; - offsets = [ { start: 0, count: indices.length, index: 0 } ]; + this.setValues( parameters ); - } +}; - for ( var oi = 0, ol = offsets.length; oi < ol; ++oi ) { +THREE.MeshDepthMaterial.prototype = Object.create( THREE.Material.prototype ); - var start = offsets[ oi ].start; - var count = offsets[ oi ].count; - var index = offsets[ oi ].index; +THREE.MeshDepthMaterial.prototype.clone = function () { - for ( var i = start, il = start + count; i < il; i += 3 ) { + var material = new THREE.MeshDepthMaterial(); - a = index + indices[ i ]; - b = index + indices[ i + 1 ]; - c = index + indices[ i + 2 ]; + THREE.Material.prototype.clone.call( this, material ); - vA.set( - positions[ a * 3 ], - positions[ a * 3 + 1 ], - positions[ a * 3 + 2 ] - ); - vB.set( - positions[ b * 3 ], - positions[ b * 3 + 1 ], - positions[ b * 3 + 2 ] - ); - vC.set( - positions[ c * 3 ], - positions[ c * 3 + 1 ], - positions[ c * 3 + 2 ] - ); + material.wireframe = this.wireframe; + material.wireframeLinewidth = this.wireframeLinewidth; + return material; - if ( material.side === THREE.BackSide ) { +}; - var intersectionPoint = ray.intersectTriangle( vC, vB, vA, true ); +// File:src/materials/MeshNormalMaterial.js - } else { +/** + * @author mrdoob / http://mrdoob.com/ + * + * parameters = { + * opacity: , + * + * shading: THREE.FlatShading, + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: + * } + */ - var intersectionPoint = ray.intersectTriangle( vA, vB, vC, material.side !== THREE.DoubleSide ); +THREE.MeshNormalMaterial = function ( parameters ) { - } + THREE.Material.call( this, parameters ); - if ( intersectionPoint === null ) continue; + this.type = 'MeshNormalMaterial'; - intersectionPoint.applyMatrix4( this.matrixWorld ); + this.shading = THREE.FlatShading; - var distance = raycaster.ray.origin.distanceTo( intersectionPoint ); + this.wireframe = false; + this.wireframeLinewidth = 1; - if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue; + this.morphTargets = false; - intersects.push( { + this.setValues( parameters ); - distance: distance, - point: intersectionPoint, - indices: [ a, b, c ], - face: null, - faceIndex: null, - object: this +}; - } ); +THREE.MeshNormalMaterial.prototype = Object.create( THREE.Material.prototype ); - } +THREE.MeshNormalMaterial.prototype.clone = function () { - } + var material = new THREE.MeshNormalMaterial(); - } else { + THREE.Material.prototype.clone.call( this, material ); - var positions = attributes.position.array; + material.shading = this.shading; - for ( var i = 0, j = 0, il = positions.length; i < il; i += 3, j += 9 ) { + material.wireframe = this.wireframe; + material.wireframeLinewidth = this.wireframeLinewidth; - a = i; - b = i + 1; - c = i + 2; + return material; - vA.set( - positions[ j ], - positions[ j + 1 ], - positions[ j + 2 ] - ); - vB.set( - positions[ j + 3 ], - positions[ j + 4 ], - positions[ j + 5 ] - ); - vC.set( - positions[ j + 6 ], - positions[ j + 7 ], - positions[ j + 8 ] - ); +}; +// File:src/materials/MeshFaceMaterial.js - if ( material.side === THREE.BackSide ) { +/** + * @author mrdoob / http://mrdoob.com/ + */ - var intersectionPoint = ray.intersectTriangle( vC, vB, vA, true ); +THREE.MeshFaceMaterial = function ( materials ) { - } else { + this.uuid = THREE.Math.generateUUID(); - var intersectionPoint = ray.intersectTriangle( vA, vB, vC, material.side !== THREE.DoubleSide ); + this.type = 'MeshFaceMaterial'; + + this.materials = materials instanceof Array ? materials : []; - } +}; - if ( intersectionPoint === null ) continue; +THREE.MeshFaceMaterial.prototype = { - intersectionPoint.applyMatrix4( this.matrixWorld ); + constructor: THREE.MeshFaceMaterial, - var distance = raycaster.ray.origin.distanceTo( intersectionPoint ); + toJSON: function () { - if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue; + var output = { + metadata: { + version: 4.2, + type: 'material', + generator: 'MaterialExporter' + }, + uuid: this.uuid, + type: this.type, + materials: [] + }; - intersects.push( { + for ( var i = 0, l = this.materials.length; i < l; i ++ ) { - distance: distance, - point: intersectionPoint, - indices: [ a, b, c ], - face: null, - faceIndex: null, - object: this + output.materials.push( this.materials[ i ].toJSON() ); - } ); + } - } + return output; - } + }, - } else if ( geometry instanceof THREE.Geometry ) { + clone: function () { - var isFaceMaterial = this.material instanceof THREE.MeshFaceMaterial; - var objectMaterials = isFaceMaterial === true ? this.material.materials : null; + var material = new THREE.MeshFaceMaterial(); - var a, b, c, d; - var precision = raycaster.precision; + for ( var i = 0; i < this.materials.length; i ++ ) { - var vertices = geometry.vertices; + material.materials.push( this.materials[ i ].clone() ); - for ( var f = 0, fl = geometry.faces.length; f < fl; f ++ ) { + } - var face = geometry.faces[ f ]; + return material; - var material = isFaceMaterial === true ? objectMaterials[ face.materialIndex ] : this.material; + } - if ( material === undefined ) continue; +}; - a = vertices[ face.a ]; - b = vertices[ face.b ]; - c = vertices[ face.c ]; +// File:src/materials/PointCloudMaterial.js - if ( material.morphTargets === true ) { +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * map: new THREE.Texture( ), + * + * size: , + * + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * vertexColors: , + * + * fog: + * } + */ - var morphTargets = geometry.morphTargets; - var morphInfluences = this.morphTargetInfluences; +THREE.PointCloudMaterial = function ( parameters ) { - vA.set( 0, 0, 0 ); - vB.set( 0, 0, 0 ); - vC.set( 0, 0, 0 ); + THREE.Material.call( this ); - for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) { + this.type = 'PointCloudMaterial'; - var influence = morphInfluences[ t ]; + this.color = new THREE.Color( 0xffffff ); - if ( influence === 0 ) continue; + this.map = null; - var targets = morphTargets[ t ].vertices; + this.size = 1; + this.sizeAttenuation = true; - vA.x += ( targets[ face.a ].x - a.x ) * influence; - vA.y += ( targets[ face.a ].y - a.y ) * influence; - vA.z += ( targets[ face.a ].z - a.z ) * influence; + this.vertexColors = THREE.NoColors; - vB.x += ( targets[ face.b ].x - b.x ) * influence; - vB.y += ( targets[ face.b ].y - b.y ) * influence; - vB.z += ( targets[ face.b ].z - b.z ) * influence; + this.fog = true; - vC.x += ( targets[ face.c ].x - c.x ) * influence; - vC.y += ( targets[ face.c ].y - c.y ) * influence; - vC.z += ( targets[ face.c ].z - c.z ) * influence; + this.setValues( parameters ); - } +}; - vA.add( a ); - vB.add( b ); - vC.add( c ); +THREE.PointCloudMaterial.prototype = Object.create( THREE.Material.prototype ); - a = vA; - b = vB; - c = vC; +THREE.PointCloudMaterial.prototype.clone = function () { - } + var material = new THREE.PointCloudMaterial(); - if ( material.side === THREE.BackSide ) { + THREE.Material.prototype.clone.call( this, material ); - var intersectionPoint = ray.intersectTriangle( c, b, a, true ); + material.color.copy( this.color ); - } else { + material.map = this.map; - var intersectionPoint = ray.intersectTriangle( a, b, c, material.side !== THREE.DoubleSide ); + material.size = this.size; + material.sizeAttenuation = this.sizeAttenuation; - } + material.vertexColors = this.vertexColors; - if ( intersectionPoint === null ) continue; + material.fog = this.fog; - intersectionPoint.applyMatrix4( this.matrixWorld ); + return material; - var distance = raycaster.ray.origin.distanceTo( intersectionPoint ); +}; - if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue; +// backwards compatibility - intersects.push( { +THREE.ParticleBasicMaterial = function ( parameters ) { - distance: distance, - point: intersectionPoint, - face: face, - faceIndex: f, - object: this - - } ); - - } - - } - - }; - -}() ); - -THREE.Mesh.prototype.clone = function ( object, recursive ) { + console.warn( 'THREE.ParticleBasicMaterial has been renamed to THREE.PointCloudMaterial.' ); + return new THREE.PointCloudMaterial( parameters ); - if ( object === undefined ) object = new THREE.Mesh( this.geometry, this.material ); +}; - THREE.Object3D.prototype.clone.call( this, object, recursive ); +THREE.ParticleSystemMaterial = function ( parameters ) { - return object; + console.warn( 'THREE.ParticleSystemMaterial has been renamed to THREE.PointCloudMaterial.' ); + return new THREE.PointCloudMaterial( parameters ); }; -// File:src/objects/Bone.js +// File:src/materials/ShaderMaterial.js /** - * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ - * @author ikerr / http://verold.com + * + * parameters = { + * defines: { "label" : "value" }, + * uniforms: { "parameter1": { type: "f", value: 1.0 }, "parameter2": { type: "i" value2: 2 } }, + * + * fragmentShader: , + * vertexShader: , + * + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: , + * + * lights: , + * + * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, + * + * skinning: , + * morphTargets: , + * morphNormals: , + * + * fog: + * } */ -THREE.Bone = function ( belongsToSkin ) { +THREE.ShaderMaterial = function ( parameters ) { - THREE.Object3D.call( this ); + THREE.Material.call( this ); - this.skin = belongsToSkin; + this.type = 'ShaderMaterial'; - this.accumulatedRotWeight = 0; - this.accumulatedPosWeight = 0; - this.accumulatedSclWeight = 0; + this.defines = {}; + this.uniforms = {}; + this.attributes = null; -}; + this.vertexShader = 'void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}'; + this.fragmentShader = 'void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}'; -THREE.Bone.prototype = Object.create( THREE.Object3D.prototype ); + this.shading = THREE.SmoothShading; -THREE.Bone.prototype.updateMatrixWorld = function ( force ) { + this.linewidth = 1; - THREE.Object3D.prototype.updateMatrixWorld.call( this, force ); + this.wireframe = false; + this.wireframeLinewidth = 1; - // Reset weights to be re-accumulated in the next frame + this.fog = false; // set to use scene fog - this.accumulatedRotWeight = 0; - this.accumulatedPosWeight = 0; - this.accumulatedSclWeight = 0; + this.lights = false; // set to use scene lights -}; + this.vertexColors = THREE.NoColors; // set to use "color" attribute stream + this.skinning = false; // set to use skinning attribute streams -// File:src/objects/Skeleton.js + this.morphTargets = false; // set to use morph targets + this.morphNormals = false; // set to use morph normals -/** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - * @author michael guerrero / http://realitymeltdown.com - * @author ikerr / http://verold.com - */ + // When rendered geometry doesn't include these attributes but the material does, + // use these default values in WebGL. This avoids errors when buffer data is missing. + this.defaultAttributeValues = { + 'color': [ 1, 1, 1 ], + 'uv': [ 0, 0 ], + 'uv2': [ 0, 0 ] + }; -THREE.Skeleton = function ( bones, boneInverses, useVertexTexture ) { + this.index0AttributeName = undefined; - this.useVertexTexture = useVertexTexture !== undefined ? useVertexTexture : true; + this.setValues( parameters ); - this.identityMatrix = new THREE.Matrix4(); +}; - // copy the bone array +THREE.ShaderMaterial.prototype = Object.create( THREE.Material.prototype ); - bones = bones || []; +THREE.ShaderMaterial.prototype.clone = function () { - this.bones = bones.slice( 0 ); + var material = new THREE.ShaderMaterial(); - // create a bone texture or an array of floats + THREE.Material.prototype.clone.call( this, material ); - if ( this.useVertexTexture ) { + material.fragmentShader = this.fragmentShader; + material.vertexShader = this.vertexShader; - // layout (1 matrix = 4 pixels) - // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) - // with 8x8 pixel texture max 16 bones (8 * 8 / 4) - // 16x16 pixel texture max 64 bones (16 * 16 / 4) - // 32x32 pixel texture max 256 bones (32 * 32 / 4) - // 64x64 pixel texture max 1024 bones (64 * 64 / 4) + material.uniforms = THREE.UniformsUtils.clone( this.uniforms ); - var size; + material.attributes = this.attributes; + material.defines = this.defines; - if ( this.bones.length > 256 ) - size = 64; - else if ( this.bones.length > 64 ) - size = 32; - else if ( this.bones.length > 16 ) - size = 16; - else - size = 8; + material.shading = this.shading; - this.boneTextureWidth = size; - this.boneTextureHeight = size; + material.wireframe = this.wireframe; + material.wireframeLinewidth = this.wireframeLinewidth; - this.boneMatrices = new Float32Array( this.boneTextureWidth * this.boneTextureHeight * 4 ); // 4 floats per RGBA pixel - this.boneTexture = new THREE.DataTexture( this.boneMatrices, this.boneTextureWidth, this.boneTextureHeight, THREE.RGBAFormat, THREE.FloatType ); - this.boneTexture.minFilter = THREE.NearestFilter; - this.boneTexture.magFilter = THREE.NearestFilter; - this.boneTexture.generateMipmaps = false; - this.boneTexture.flipY = false; + material.fog = this.fog; - } else { + material.lights = this.lights; - this.boneMatrices = new Float32Array( 16 * this.bones.length ); + material.vertexColors = this.vertexColors; - } + material.skinning = this.skinning; - // use the supplied bone inverses or calculate the inverses + material.morphTargets = this.morphTargets; + material.morphNormals = this.morphNormals; - if ( boneInverses === undefined ) { + return material; - this.calculateInverses(); +}; - } else { +// File:src/materials/RawShaderMaterial.js - if ( this.bones.length === boneInverses.length ) { +/** + * @author mrdoob / http://mrdoob.com/ + */ - this.boneInverses = boneInverses.slice( 0 ); +THREE.RawShaderMaterial = function ( parameters ) { - } else { + THREE.ShaderMaterial.call( this, parameters ); - console.warn( 'THREE.Skeleton bonInverses is the wrong length.' ); + this.type = 'RawShaderMaterial'; - this.boneInverses = []; +}; - for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { +THREE.RawShaderMaterial.prototype = Object.create( THREE.ShaderMaterial.prototype ); - this.boneInverses.push( new THREE.Matrix4() ); +THREE.RawShaderMaterial.prototype.clone = function () { - } + var material = new THREE.RawShaderMaterial(); - } + THREE.ShaderMaterial.prototype.clone.call( this, material ); - } + return material; }; -THREE.Skeleton.prototype.calculateInverses = function () { +// File:src/materials/SpriteMaterial.js - this.boneInverses = []; +/** + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * map: new THREE.Texture( ), + * + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * uvOffset: new THREE.Vector2(), + * uvScale: new THREE.Vector2(), + * + * fog: + * } + */ - for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { +THREE.SpriteMaterial = function ( parameters ) { - var inverse = new THREE.Matrix4(); + THREE.Material.call( this ); - if ( this.bones[ b ] ) { + this.type = 'SpriteMaterial'; - inverse.getInverse( this.bones[ b ].matrixWorld ); + this.color = new THREE.Color( 0xffffff ); + this.map = null; - } + this.rotation = 0; - this.boneInverses.push( inverse ); + this.fog = false; - } + // set parameters + + this.setValues( parameters ); }; -THREE.Skeleton.prototype.pose = function () { +THREE.SpriteMaterial.prototype = Object.create( THREE.Material.prototype ); - var bone; +THREE.SpriteMaterial.prototype.clone = function () { - // recover the bind-time world matrices + var material = new THREE.SpriteMaterial(); - for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + THREE.Material.prototype.clone.call( this, material ); - bone = this.bones[ b ]; + material.color.copy( this.color ); + material.map = this.map; - if ( bone ) { + material.rotation = this.rotation; - bone.matrixWorld.getInverse( this.boneInverses[ b ] ); + material.fog = this.fog; - } + return material; - } +}; - // compute the local matrices, positions, rotations and scales +// File:src/textures/Texture.js - for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author szimek / https://github.com/szimek/ + */ - bone = this.bones[ b ]; +THREE.Texture = function ( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { - if ( bone ) { + Object.defineProperty( this, 'id', { value: THREE.TextureIdCount ++ } ); - if ( bone.parent ) { + this.uuid = THREE.Math.generateUUID(); - bone.matrix.getInverse( bone.parent.matrixWorld ); - bone.matrix.multiply( bone.matrixWorld ); + this.name = ''; - } - else { + this.image = image !== undefined ? image : THREE.Texture.DEFAULT_IMAGE; + this.mipmaps = []; - bone.matrix.copy( bone.matrixWorld ); + this.mapping = mapping !== undefined ? mapping : THREE.Texture.DEFAULT_MAPPING; - } + this.wrapS = wrapS !== undefined ? wrapS : THREE.ClampToEdgeWrapping; + this.wrapT = wrapT !== undefined ? wrapT : THREE.ClampToEdgeWrapping; - bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); + this.magFilter = magFilter !== undefined ? magFilter : THREE.LinearFilter; + this.minFilter = minFilter !== undefined ? minFilter : THREE.LinearMipMapLinearFilter; - } + this.anisotropy = anisotropy !== undefined ? anisotropy : 1; - } + this.format = format !== undefined ? format : THREE.RGBAFormat; + this.type = type !== undefined ? type : THREE.UnsignedByteType; -}; + this.offset = new THREE.Vector2( 0, 0 ); + this.repeat = new THREE.Vector2( 1, 1 ); -THREE.Skeleton.prototype.update = function () { + this.generateMipmaps = true; + this.premultiplyAlpha = false; + this.flipY = true; + this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) - var offsetMatrix = new THREE.Matrix4(); + this._needsUpdate = false; + this.onUpdate = null; - // flatten bone matrices to array +}; - for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { +THREE.Texture.DEFAULT_IMAGE = undefined; +THREE.Texture.DEFAULT_MAPPING = new THREE.UVMapping(); - // compute the offset between the current and the original transform +THREE.Texture.prototype = { - var matrix = this.bones[ b ] ? this.bones[ b ].matrixWorld : this.identityMatrix; + constructor: THREE.Texture, - offsetMatrix.multiplyMatrices( matrix, this.boneInverses[ b ] ); - offsetMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 ); + get needsUpdate () { - } + return this._needsUpdate; - if ( this.useVertexTexture ) { + }, - this.boneTexture.needsUpdate = true; + set needsUpdate ( value ) { - } + if ( value === true ) this.update(); -}; + this._needsUpdate = value; + }, -// File:src/objects/SkinnedMesh.js + clone: function ( texture ) { -/** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - * @author ikerr / http://verold.com - */ + if ( texture === undefined ) texture = new THREE.Texture(); -THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) { + texture.image = this.image; + texture.mipmaps = this.mipmaps.slice( 0 ); - THREE.Mesh.call( this, geometry, material ); + texture.mapping = this.mapping; - this.bindMode = "attached"; - this.bindMatrix = new THREE.Matrix4(); - this.bindMatrixInverse = new THREE.Matrix4(); + texture.wrapS = this.wrapS; + texture.wrapT = this.wrapT; - // init bones + texture.magFilter = this.magFilter; + texture.minFilter = this.minFilter; - // TODO: remove bone creation as there is no reason (other than - // convenience) for THREE.SkinnedMesh to do this. + texture.anisotropy = this.anisotropy; - var bones = []; + texture.format = this.format; + texture.type = this.type; - if ( this.geometry && this.geometry.bones !== undefined ) { + texture.offset.copy( this.offset ); + texture.repeat.copy( this.repeat ); - var bone, gbone, p, q, s; + texture.generateMipmaps = this.generateMipmaps; + texture.premultiplyAlpha = this.premultiplyAlpha; + texture.flipY = this.flipY; + texture.unpackAlignment = this.unpackAlignment; - for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++b ) { + return texture; - gbone = this.geometry.bones[ b ]; + }, - p = gbone.pos; - q = gbone.rotq; - s = gbone.scl; + update: function () { - bone = new THREE.Bone( this ); - bones.push( bone ); + this.dispatchEvent( { type: 'update' } ); - bone.name = gbone.name; - bone.position.set( p[ 0 ], p[ 1 ], p[ 2 ] ); - bone.quaternion.set( q[ 0 ], q[ 1 ], q[ 2 ], q[ 3 ] ); + }, - if ( s !== undefined ) { + dispose: function () { - bone.scale.set( s[ 0 ], s[ 1 ], s[ 2 ] ); + this.dispatchEvent( { type: 'dispose' } ); - } else { + } - bone.scale.set( 1, 1, 1 ); +}; - } +THREE.EventDispatcher.prototype.apply( THREE.Texture.prototype ); - } +THREE.TextureIdCount = 0; - for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++b ) { +// File:src/textures/CubeTexture.js - gbone = this.geometry.bones[ b ]; +/** + * @author mrdoob / http://mrdoob.com/ + */ - if ( gbone.parent !== - 1 ) { +THREE.CubeTexture = function ( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { - bones[ gbone.parent ].add( bones[ b ] ); + THREE.Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - } else { + this.images = images; - this.add( bones[ b ] ); +}; - } +THREE.CubeTexture.prototype = Object.create( THREE.Texture.prototype ); - } +THREE.CubeTexture.clone = function ( texture ) { - } + if ( texture === undefined ) texture = new THREE.CubeTexture(); - this.normalizeSkinWeights(); + THREE.Texture.prototype.clone.call( this, texture ); - this.updateMatrixWorld( true ); - this.bind( new THREE.Skeleton( bones, undefined, useVertexTexture ) ); + texture.images = this.images; + + return texture; }; +// File:src/textures/CompressedTexture.js -THREE.SkinnedMesh.prototype = Object.create( THREE.Mesh.prototype ); +/** + * @author alteredq / http://alteredqualia.com/ + */ -THREE.SkinnedMesh.prototype.bind = function( skeleton, bindMatrix ) { +THREE.CompressedTexture = function ( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) { - this.skeleton = skeleton; + THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - if ( bindMatrix === undefined ) { + this.image = { width: width, height: height }; + this.mipmaps = mipmaps; - this.updateMatrixWorld( true ); + // no flipping for cube textures + // (also flipping doesn't work for compressed textures ) - bindMatrix = this.matrixWorld; + this.flipY = false; - } + // can't generate mipmaps for compressed textures + // mips must be embedded in DDS files - this.bindMatrix.copy( bindMatrix ); - this.bindMatrixInverse.getInverse( bindMatrix ); + this.generateMipmaps = false; }; -THREE.SkinnedMesh.prototype.pose = function () { +THREE.CompressedTexture.prototype = Object.create( THREE.Texture.prototype ); - this.skeleton.pose(); +THREE.CompressedTexture.prototype.clone = function () { -}; + var texture = new THREE.CompressedTexture(); -THREE.SkinnedMesh.prototype.normalizeSkinWeights = function () { + THREE.Texture.prototype.clone.call( this, texture ); - if ( this.geometry instanceof THREE.Geometry ) { + return texture; - for ( var i = 0; i < this.geometry.skinIndices.length; i ++ ) { +}; - var sw = this.geometry.skinWeights[ i ]; +// File:src/textures/DataTexture.js - var scale = 1.0 / sw.lengthManhattan(); +/** + * @author alteredq / http://alteredqualia.com/ + */ - if ( scale !== Infinity ) { +THREE.DataTexture = function ( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) { - sw.multiplyScalar( scale ); + THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - } else { + this.image = { data: data, width: width, height: height }; - sw.set( 1 ); // this will be normalized by the shader anyway +}; - } +THREE.DataTexture.prototype = Object.create( THREE.Texture.prototype ); - } +THREE.DataTexture.prototype.clone = function () { - } else { + var texture = new THREE.DataTexture(); - // skinning weights assumed to be normalized for THREE.BufferGeometry + THREE.Texture.prototype.clone.call( this, texture ); - } + return texture; }; -THREE.SkinnedMesh.prototype.updateMatrixWorld = function( force ) { +// File:src/textures/VideoTexture.js - THREE.Mesh.prototype.updateMatrixWorld.call( this, true ); +/** + * @author mrdoob / http://mrdoob.com/ + */ - if ( this.bindMode === "attached" ) { +THREE.VideoTexture = function ( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { - this.bindMatrixInverse.getInverse( this.matrixWorld ); + THREE.Texture.call( this, video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - } else if ( this.bindMode === "detached" ) { + this.generateMipmaps = false; - this.bindMatrixInverse.getInverse( this.bindMatrix ); + var scope = this; - } else { + var update = function () { - console.warn( 'THREE.SkinnedMesh unreckognized bindMode: ' + this.bindMode ); + requestAnimationFrame( update ); - } + if ( video.readyState === video.HAVE_ENOUGH_DATA ) { + + scope.needsUpdate = true; + + } + + }; + + update(); }; -THREE.SkinnedMesh.prototype.clone = function( object ) { +THREE.VideoTexture.prototype = Object.create( THREE.Texture.prototype ); - if ( object === undefined ) { +// File:src/objects/Group.js - object = new THREE.SkinnedMesh( this.geometry, this.material, this.useVertexTexture ); +/** + * @author mrdoob / http://mrdoob.com/ + */ - } +THREE.Group = function () { - THREE.Mesh.prototype.clone.call( this, object ); + THREE.Object3D.call( this ); - return object; + this.type = 'Group'; }; +THREE.Group.prototype = Object.create( THREE.Object3D.prototype ); -// File:src/objects/MorphAnimMesh.js +// File:src/objects/PointCloud.js /** * @author alteredq / http://alteredqualia.com/ */ -THREE.MorphAnimMesh = function ( geometry, material ) { +THREE.PointCloud = function ( geometry, material ) { - THREE.Mesh.call( this, geometry, material ); + THREE.Object3D.call( this ); - // API + this.type = 'PointCloud'; - this.duration = 1000; // milliseconds - this.mirroredLoop = false; - this.time = 0; + this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); + this.material = material !== undefined ? material : new THREE.PointCloudMaterial( { color: Math.random() * 0xffffff } ); - // internals + this.sortParticles = false; - this.lastKeyframe = 0; - this.currentKeyframe = 0; +}; - this.direction = 1; - this.directionBackwards = false; +THREE.PointCloud.prototype = Object.create( THREE.Object3D.prototype ); - this.setFrameRange( 0, this.geometry.morphTargets.length - 1 ); +THREE.PointCloud.prototype.raycast = ( function () { -}; + var inverseMatrix = new THREE.Matrix4(); + var ray = new THREE.Ray(); -THREE.MorphAnimMesh.prototype = Object.create( THREE.Mesh.prototype ); + return function ( raycaster, intersects ) { -THREE.MorphAnimMesh.prototype.setFrameRange = function ( start, end ) { + var object = this; + var geometry = object.geometry; + var threshold = raycaster.params.PointCloud.threshold; - this.startKeyframe = start; - this.endKeyframe = end; + inverseMatrix.getInverse( this.matrixWorld ); + ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); - this.length = this.endKeyframe - this.startKeyframe + 1; + if ( geometry.boundingBox !== null ) { -}; + if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) { -THREE.MorphAnimMesh.prototype.setDirectionForward = function () { + return; - this.direction = 1; - this.directionBackwards = false; + } -}; + } -THREE.MorphAnimMesh.prototype.setDirectionBackward = function () { + var localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); + var position = new THREE.Vector3(); - this.direction = - 1; - this.directionBackwards = true; - -}; - -THREE.MorphAnimMesh.prototype.parseAnimations = function () { + var testPoint = function ( point, index ) { - var geometry = this.geometry; + var rayPointDistance = ray.distanceToPoint( point ); - if ( ! geometry.animations ) geometry.animations = {}; + if ( rayPointDistance < localThreshold ) { - var firstAnimation, animations = geometry.animations; + var intersectPoint = ray.closestPointToPoint( point ); + intersectPoint.applyMatrix4( object.matrixWorld ); - var pattern = /([a-z]+)_?(\d+)/; + var distance = raycaster.ray.origin.distanceTo( intersectPoint ); - for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) { + intersects.push( { - var morph = geometry.morphTargets[ i ]; - var parts = morph.name.match( pattern ); + distance: distance, + distanceToRay: rayPointDistance, + point: intersectPoint.clone(), + index: index, + face: null, + object: object - if ( parts && parts.length > 1 ) { + } ); - var label = parts[ 1 ]; - var num = parts[ 2 ]; + } - if ( ! animations[ label ] ) animations[ label ] = { start: Infinity, end: - Infinity }; + }; - var animation = animations[ label ]; + if ( geometry instanceof THREE.BufferGeometry ) { - if ( i < animation.start ) animation.start = i; - if ( i > animation.end ) animation.end = i; + var attributes = geometry.attributes; + var positions = attributes.position.array; - if ( ! firstAnimation ) firstAnimation = label; + if ( attributes.index !== undefined ) { - } + var indices = attributes.index.array; + var offsets = geometry.offsets; - } + if ( offsets.length === 0 ) { - geometry.firstAnimation = firstAnimation; + var offset = { + start: 0, + count: indices.length, + index: 0 + }; -}; + offsets = [ offset ]; -THREE.MorphAnimMesh.prototype.setAnimationLabel = function ( label, start, end ) { + } - if ( ! this.geometry.animations ) this.geometry.animations = {}; + for ( var oi = 0, ol = offsets.length; oi < ol; ++oi ) { - this.geometry.animations[ label ] = { start: start, end: end }; + var start = offsets[ oi ].start; + var count = offsets[ oi ].count; + var index = offsets[ oi ].index; -}; + for ( var i = start, il = start + count; i < il; i ++ ) { -THREE.MorphAnimMesh.prototype.playAnimation = function ( label, fps ) { + var a = index + indices[ i ]; - var animation = this.geometry.animations[ label ]; + position.fromArray( positions, a * 3 ); - if ( animation ) { + testPoint( position, a ); - this.setFrameRange( animation.start, animation.end ); - this.duration = 1000 * ( ( animation.end - animation.start ) / fps ); - this.time = 0; + } - } else { + } - console.warn( 'animation[' + label + '] undefined' ); + } else { - } + var pointCount = positions.length / 3; -}; + for ( var i = 0; i < pointCount; i ++ ) { -THREE.MorphAnimMesh.prototype.updateAnimation = function ( delta ) { + position.set( + positions[ 3 * i ], + positions[ 3 * i + 1 ], + positions[ 3 * i + 2 ] + ); - var frameTime = this.duration / this.length; + testPoint( position, i ); - this.time += this.direction * delta; + } - if ( this.mirroredLoop ) { + } - if ( this.time > this.duration || this.time < 0 ) { + } else { - this.direction *= - 1; + var vertices = this.geometry.vertices; - if ( this.time > this.duration ) { + for ( var i = 0; i < vertices.length; i ++ ) { - this.time = this.duration; - this.directionBackwards = true; + testPoint( vertices[ i ], i ); } - if ( this.time < 0 ) { + } - this.time = 0; - this.directionBackwards = false; + }; - } +}() ); - } +THREE.PointCloud.prototype.clone = function ( object ) { - } else { + if ( object === undefined ) object = new THREE.PointCloud( this.geometry, this.material ); - this.time = this.time % this.duration; + object.sortParticles = this.sortParticles; - if ( this.time < 0 ) this.time += this.duration; + THREE.Object3D.prototype.clone.call( this, object ); - } + return object; - var keyframe = this.startKeyframe + THREE.Math.clamp( Math.floor( this.time / frameTime ), 0, this.length - 1 ); +}; - if ( keyframe !== this.currentKeyframe ) { +// Backwards compatibility - this.morphTargetInfluences[ this.lastKeyframe ] = 0; - this.morphTargetInfluences[ this.currentKeyframe ] = 1; +THREE.ParticleSystem = function ( geometry, material ) { - this.morphTargetInfluences[ keyframe ] = 0; + console.warn( 'THREE.ParticleSystem has been renamed to THREE.PointCloud.' ); + return new THREE.PointCloud( geometry, material ); - this.lastKeyframe = this.currentKeyframe; - this.currentKeyframe = keyframe; +}; - } +// File:src/objects/Line.js - var mix = ( this.time % frameTime ) / frameTime; +/** + * @author mrdoob / http://mrdoob.com/ + */ - if ( this.directionBackwards ) { +THREE.Line = function ( geometry, material, mode ) { - mix = 1 - mix; + THREE.Object3D.call( this ); - } + this.type = 'Line'; - this.morphTargetInfluences[ this.currentKeyframe ] = mix; - this.morphTargetInfluences[ this.lastKeyframe ] = 1 - mix; + this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); + this.material = material !== undefined ? material : new THREE.LineBasicMaterial( { color: Math.random() * 0xffffff } ); + + this.mode = ( mode !== undefined ) ? mode : THREE.LineStrip; }; -THREE.MorphAnimMesh.prototype.interpolateTargets = function ( a, b, t ) { +THREE.LineStrip = 0; +THREE.LinePieces = 1; - var influences = this.morphTargetInfluences; +THREE.Line.prototype = Object.create( THREE.Object3D.prototype ); - for ( var i = 0, l = influences.length; i < l; i ++ ) { +THREE.Line.prototype.raycast = ( function () { - influences[ i ] = 0; + var inverseMatrix = new THREE.Matrix4(); + var ray = new THREE.Ray(); + var sphere = new THREE.Sphere(); - } + return function ( raycaster, intersects ) { - if ( a > -1 ) influences[ a ] = 1 - t; - if ( b > -1 ) influences[ b ] = t; + var precision = raycaster.linePrecision; + var precisionSq = precision * precision; -}; + var geometry = this.geometry; -THREE.MorphAnimMesh.prototype.clone = function ( object ) { + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - if ( object === undefined ) object = new THREE.MorphAnimMesh( this.geometry, this.material ); + // Checking boundingSphere distance to ray - object.duration = this.duration; - object.mirroredLoop = this.mirroredLoop; - object.time = this.time; + sphere.copy( geometry.boundingSphere ); + sphere.applyMatrix4( this.matrixWorld ); - object.lastKeyframe = this.lastKeyframe; - object.currentKeyframe = this.currentKeyframe; + if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) { - object.direction = this.direction; - object.directionBackwards = this.directionBackwards; + return; - THREE.Mesh.prototype.clone.call( this, object ); + } - return object; + inverseMatrix.getInverse( this.matrixWorld ); + ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); -}; + /* if ( geometry instanceof THREE.BufferGeometry ) { -// File:src/objects/LOD.js + } else */ if ( geometry instanceof THREE.Geometry ) { -/** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - */ + var vertices = geometry.vertices; + var nbVertices = vertices.length; + var interSegment = new THREE.Vector3(); + var interRay = new THREE.Vector3(); + var step = this.mode === THREE.LineStrip ? 1 : 2; -THREE.LOD = function () { + for ( var i = 0; i < nbVertices - 1; i = i + step ) { - THREE.Object3D.call( this ); + var distSq = ray.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment ); - this.objects = []; + if ( distSq > precisionSq ) continue; -}; + var distance = ray.origin.distanceTo( interRay ); + if ( distance < raycaster.near || distance > raycaster.far ) continue; -THREE.LOD.prototype = Object.create( THREE.Object3D.prototype ); + intersects.push( { -THREE.LOD.prototype.addLevel = function ( object, distance ) { + distance: distance, + // What do we want? intersection point on the ray or on the segment?? + // point: raycaster.ray.at( distance ), + point: interSegment.clone().applyMatrix4( this.matrixWorld ), + face: null, + faceIndex: null, + object: this - if ( distance === undefined ) distance = 0; + } ); - distance = Math.abs( distance ); + } - for ( var l = 0; l < this.objects.length; l ++ ) { + } - if ( distance < this.objects[ l ].distance ) { + }; - break; +}() ); - } +THREE.Line.prototype.clone = function ( object ) { - } + if ( object === undefined ) object = new THREE.Line( this.geometry, this.material, this.mode ); - this.objects.splice( l, 0, { distance: distance, object: object } ); - this.add( object ); + THREE.Object3D.prototype.clone.call( this, object ); -}; + return object; -THREE.LOD.prototype.getObjectForDistance = function ( distance ) { +}; - for ( var i = 1, l = this.objects.length; i < l; i ++ ) { +// File:src/objects/Mesh.js - if ( distance < this.objects[ i ].distance ) { +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author mikael emtinger / http://gomo.se/ + * @author jonobr1 / http://jonobr1.com/ + */ - break; +THREE.Mesh = function ( geometry, material ) { - } + THREE.Object3D.call( this ); - } + this.type = 'Mesh'; + + this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); + this.material = material !== undefined ? material : new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff } ); - return this.objects[ i - 1 ].object; + this.updateMorphTargets(); }; -THREE.LOD.prototype.raycast = ( function () { +THREE.Mesh.prototype = Object.create( THREE.Object3D.prototype ); - var matrixPosition = new THREE.Vector3(); +THREE.Mesh.prototype.updateMorphTargets = function () { - return function ( raycaster, intersects ) { + if ( this.geometry.morphTargets !== undefined && this.geometry.morphTargets.length > 0 ) { - matrixPosition.setFromMatrixPosition( this.matrixWorld ); + this.morphTargetBase = - 1; + this.morphTargetForcedOrder = []; + this.morphTargetInfluences = []; + this.morphTargetDictionary = {}; - var distance = raycaster.ray.origin.distanceTo( matrixPosition ); + for ( var m = 0, ml = this.geometry.morphTargets.length; m < ml; m ++ ) { - this.getObjectForDistance( distance ).raycast( raycaster, intersects ); + this.morphTargetInfluences.push( 0 ); + this.morphTargetDictionary[ this.geometry.morphTargets[ m ].name ] = m; - }; + } -}() ); + } -THREE.LOD.prototype.update = function () { +}; - var v1 = new THREE.Vector3(); - var v2 = new THREE.Vector3(); +THREE.Mesh.prototype.getMorphTargetIndexByName = function ( name ) { - return function ( camera ) { + if ( this.morphTargetDictionary[ name ] !== undefined ) { - if ( this.objects.length > 1 ) { + return this.morphTargetDictionary[ name ]; - v1.setFromMatrixPosition( camera.matrixWorld ); - v2.setFromMatrixPosition( this.matrixWorld ); + } - var distance = v1.distanceTo( v2 ); + console.log( 'THREE.Mesh.getMorphTargetIndexByName: morph target ' + name + ' does not exist. Returning 0.' ); - this.objects[ 0 ].object.visible = true; + return 0; - for ( var i = 1, l = this.objects.length; i < l; i ++ ) { +}; - if ( distance >= this.objects[ i ].distance ) { - this.objects[ i - 1 ].object.visible = false; - this.objects[ i ].object.visible = true; +THREE.Mesh.prototype.raycast = ( function () { - } else { + var inverseMatrix = new THREE.Matrix4(); + var ray = new THREE.Ray(); + var sphere = new THREE.Sphere(); - break; + var vA = new THREE.Vector3(); + var vB = new THREE.Vector3(); + var vC = new THREE.Vector3(); - } + return function ( raycaster, intersects ) { - } + var geometry = this.geometry; - for ( ; i < l; i ++ ) { + // Checking boundingSphere distance to ray - this.objects[ i ].object.visible = false; + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - } + sphere.copy( geometry.boundingSphere ); + sphere.applyMatrix4( this.matrixWorld ); - } + if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) { - }; + return; -}(); + } -THREE.LOD.prototype.clone = function ( object ) { + // Check boundingBox before continuing - if ( object === undefined ) object = new THREE.LOD(); + inverseMatrix.getInverse( this.matrixWorld ); + ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); - THREE.Object3D.prototype.clone.call( this, object ); + if ( geometry.boundingBox !== null ) { - for ( var i = 0, l = this.objects.length; i < l; i ++ ) { - var x = this.objects[ i ].object.clone(); - x.visible = i === 0; - object.addLevel( x, this.objects[ i ].distance ); - } + if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) { - return object; + return; -}; + } -// File:src/objects/Sprite.js + } -/** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - */ + if ( geometry instanceof THREE.BufferGeometry ) { -THREE.Sprite = ( function () { + var material = this.material; - var vertices = new Float32Array( [ - 0.5, - 0.5, 0, 0.5, - 0.5, 0, 0.5, 0.5, 0 ] ); + if ( material === undefined ) return; - var geometry = new THREE.BufferGeometry(); - geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); + var attributes = geometry.attributes; - return function ( material ) { + var a, b, c; + var precision = raycaster.precision; - THREE.Object3D.call( this ); + if ( attributes.index !== undefined ) { - this.geometry = geometry; - this.material = ( material !== undefined ) ? material : new THREE.SpriteMaterial(); + var indices = attributes.index.array; + var positions = attributes.position.array; + var offsets = geometry.offsets; - }; + if ( offsets.length === 0 ) { -} )(); + offsets = [ { start: 0, count: indices.length, index: 0 } ]; -THREE.Sprite.prototype = Object.create( THREE.Object3D.prototype ); + } -THREE.Sprite.prototype.raycast = ( function () { + for ( var oi = 0, ol = offsets.length; oi < ol; ++oi ) { - var matrixPosition = new THREE.Vector3(); + var start = offsets[ oi ].start; + var count = offsets[ oi ].count; + var index = offsets[ oi ].index; - return function ( raycaster, intersects ) { + for ( var i = start, il = start + count; i < il; i += 3 ) { - matrixPosition.setFromMatrixPosition( this.matrixWorld ); + a = index + indices[ i ]; + b = index + indices[ i + 1 ]; + c = index + indices[ i + 2 ]; - var distance = raycaster.ray.distanceToPoint( matrixPosition ); + vA.fromArray( positions, a * 3 ); + vB.fromArray( positions, b * 3 ); + vC.fromArray( positions, c * 3 ); - if ( distance > this.scale.x ) { + if ( material.side === THREE.BackSide ) { - return; + var intersectionPoint = ray.intersectTriangle( vC, vB, vA, true ); - } + } else { - intersects.push( { + var intersectionPoint = ray.intersectTriangle( vA, vB, vC, material.side !== THREE.DoubleSide ); - distance: distance, - point: this.position, - face: null, - object: this + } - } ); + if ( intersectionPoint === null ) continue; - }; + intersectionPoint.applyMatrix4( this.matrixWorld ); -}() ); + var distance = raycaster.ray.origin.distanceTo( intersectionPoint ); -THREE.Sprite.prototype.updateMatrix = function () { + if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue; - this.matrix.compose( this.position, this.quaternion, this.scale ); + intersects.push( { - this.matrixWorldNeedsUpdate = true; + distance: distance, + point: intersectionPoint, + face: new THREE.Face3( a, b, c, THREE.Triangle.normal( vA, vB, vC ) ), + faceIndex: null, + object: this -}; + } ); -THREE.Sprite.prototype.clone = function ( object ) { + } - if ( object === undefined ) object = new THREE.Sprite( this.material ); + } - THREE.Object3D.prototype.clone.call( this, object ); + } else { - return object; + var positions = attributes.position.array; -}; + for ( var i = 0, j = 0, il = positions.length; i < il; i += 3, j += 9 ) { -// Backwards compatibility + a = i; + b = i + 1; + c = i + 2; -THREE.Particle = THREE.Sprite; + vA.fromArray( positions, j ); + vB.fromArray( positions, j + 3 ); + vC.fromArray( positions, j + 6 ); -// File:src/scenes/Scene.js + if ( material.side === THREE.BackSide ) { -/** - * @author mrdoob / http://mrdoob.com/ - */ + var intersectionPoint = ray.intersectTriangle( vC, vB, vA, true ); -THREE.Scene = function () { + } else { - THREE.Object3D.call( this ); + var intersectionPoint = ray.intersectTriangle( vA, vB, vC, material.side !== THREE.DoubleSide ); - this.fog = null; - this.overrideMaterial = null; + } - this.autoUpdate = true; // checked by the renderer - this.matrixAutoUpdate = false; + if ( intersectionPoint === null ) continue; - this.__lights = []; + intersectionPoint.applyMatrix4( this.matrixWorld ); - this.__objectsAdded = []; - this.__objectsRemoved = []; + var distance = raycaster.ray.origin.distanceTo( intersectionPoint ); -}; + if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue; -THREE.Scene.prototype = Object.create( THREE.Object3D.prototype ); + intersects.push( { -THREE.Scene.prototype.__addObject = function ( object ) { + distance: distance, + point: intersectionPoint, + face: new THREE.Face3( a, b, c, THREE.Triangle.normal( vA, vB, vC ) ), + faceIndex: null, + object: this - if ( object instanceof THREE.Light ) { + } ); - if ( this.__lights.indexOf( object ) === - 1 ) { + } - this.__lights.push( object ); + } - } + } else if ( geometry instanceof THREE.Geometry ) { - if ( object.target && object.target.parent === undefined ) { + var isFaceMaterial = this.material instanceof THREE.MeshFaceMaterial; + var objectMaterials = isFaceMaterial === true ? this.material.materials : null; - this.add( object.target ); + var a, b, c, d; + var precision = raycaster.precision; - } + var vertices = geometry.vertices; - } else if ( ! ( object instanceof THREE.Camera || object instanceof THREE.Bone ) ) { + for ( var f = 0, fl = geometry.faces.length; f < fl; f ++ ) { - this.__objectsAdded.push( object ); + var face = geometry.faces[ f ]; - // check if previously removed + var material = isFaceMaterial === true ? objectMaterials[ face.materialIndex ] : this.material; - var i = this.__objectsRemoved.indexOf( object ); + if ( material === undefined ) continue; - if ( i !== - 1 ) { + a = vertices[ face.a ]; + b = vertices[ face.b ]; + c = vertices[ face.c ]; - this.__objectsRemoved.splice( i, 1 ); + if ( material.morphTargets === true ) { - } + var morphTargets = geometry.morphTargets; + var morphInfluences = this.morphTargetInfluences; - } + vA.set( 0, 0, 0 ); + vB.set( 0, 0, 0 ); + vC.set( 0, 0, 0 ); - this.dispatchEvent( { type: 'objectAdded', object: object } ); - object.dispatchEvent( { type: 'addedToScene', scene: this } ); + for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) { - for ( var c = 0; c < object.children.length; c ++ ) { + var influence = morphInfluences[ t ]; - this.__addObject( object.children[ c ] ); + if ( influence === 0 ) continue; - } + var targets = morphTargets[ t ].vertices; -}; + vA.x += ( targets[ face.a ].x - a.x ) * influence; + vA.y += ( targets[ face.a ].y - a.y ) * influence; + vA.z += ( targets[ face.a ].z - a.z ) * influence; -THREE.Scene.prototype.__removeObject = function ( object ) { + vB.x += ( targets[ face.b ].x - b.x ) * influence; + vB.y += ( targets[ face.b ].y - b.y ) * influence; + vB.z += ( targets[ face.b ].z - b.z ) * influence; - if ( object instanceof THREE.Light ) { + vC.x += ( targets[ face.c ].x - c.x ) * influence; + vC.y += ( targets[ face.c ].y - c.y ) * influence; + vC.z += ( targets[ face.c ].z - c.z ) * influence; - var i = this.__lights.indexOf( object ); + } - if ( i !== - 1 ) { + vA.add( a ); + vB.add( b ); + vC.add( c ); - this.__lights.splice( i, 1 ); + a = vA; + b = vB; + c = vC; - } + } - if ( object.shadowCascadeArray ) { + if ( material.side === THREE.BackSide ) { - for ( var x = 0; x < object.shadowCascadeArray.length; x ++ ) { + var intersectionPoint = ray.intersectTriangle( c, b, a, true ); - this.__removeObject( object.shadowCascadeArray[ x ] ); + } else { - } + var intersectionPoint = ray.intersectTriangle( a, b, c, material.side !== THREE.DoubleSide ); - } + } - } else if ( ! ( object instanceof THREE.Camera ) ) { + if ( intersectionPoint === null ) continue; - this.__objectsRemoved.push( object ); + intersectionPoint.applyMatrix4( this.matrixWorld ); - // check if previously added + var distance = raycaster.ray.origin.distanceTo( intersectionPoint ); - var i = this.__objectsAdded.indexOf( object ); + if ( distance < precision || distance < raycaster.near || distance > raycaster.far ) continue; - if ( i !== - 1 ) { + intersects.push( { - this.__objectsAdded.splice( i, 1 ); - - } - - } - - this.dispatchEvent( { type: 'objectRemoved', object: object } ); - object.dispatchEvent( { type: 'removedFromScene', scene: this } ); - - for ( var c = 0; c < object.children.length; c ++ ) { + distance: distance, + point: intersectionPoint, + face: face, + faceIndex: f, + object: this - this.__removeObject( object.children[ c ] ); + } ); - } + } -}; + } -THREE.Scene.prototype.clone = function ( object ) { + }; - if ( object === undefined ) object = new THREE.Scene(); +}() ); - THREE.Object3D.prototype.clone.call( this, object ); +THREE.Mesh.prototype.clone = function ( object, recursive ) { - if ( this.fog !== null ) object.fog = this.fog.clone(); - if ( this.overrideMaterial !== null ) object.overrideMaterial = this.overrideMaterial.clone(); + if ( object === undefined ) object = new THREE.Mesh( this.geometry, this.material ); - object.autoUpdate = this.autoUpdate; - object.matrixAutoUpdate = this.matrixAutoUpdate; + THREE.Object3D.prototype.clone.call( this, object, recursive ); return object; }; -// File:src/scenes/Fog.js +// File:src/objects/Bone.js /** - * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ + * @author ikerr / http://verold.com */ -THREE.Fog = function ( color, near, far ) { - - this.name = ''; +THREE.Bone = function ( belongsToSkin ) { - this.color = new THREE.Color( color ); + THREE.Object3D.call( this ); - this.near = ( near !== undefined ) ? near : 1; - this.far = ( far !== undefined ) ? far : 1000; + this.skin = belongsToSkin; }; -THREE.Fog.prototype.clone = function () { - - return new THREE.Fog( this.color.getHex(), this.near, this.far ); +THREE.Bone.prototype = Object.create( THREE.Object3D.prototype ); -}; -// File:src/scenes/FogExp2.js +// File:src/objects/Skeleton.js /** - * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ + * @author michael guerrero / http://realitymeltdown.com + * @author ikerr / http://verold.com */ -THREE.FogExp2 = function ( color, density ) { - - this.name = ''; - - this.color = new THREE.Color( color ); - this.density = ( density !== undefined ) ? density : 0.00025; - -}; - -THREE.FogExp2.prototype.clone = function () { - - return new THREE.FogExp2( this.color.getHex(), this.density ); +THREE.Skeleton = function ( bones, boneInverses, useVertexTexture ) { -}; + this.useVertexTexture = useVertexTexture !== undefined ? useVertexTexture : true; -// File:src/renderers/CanvasRenderer.js + this.identityMatrix = new THREE.Matrix4(); -/** - * @author mrdoob / http://mrdoob.com/ - */ + // copy the bone array -THREE.CanvasRenderer = function ( parameters ) { + bones = bones || []; - console.log( 'THREE.CanvasRenderer', THREE.REVISION ); + this.bones = bones.slice( 0 ); - var smoothstep = THREE.Math.smoothstep; + // create a bone texture or an array of floats - parameters = parameters || {}; + if ( this.useVertexTexture ) { - var _this = this, - _renderData, _elements, _lights, - _projector = new THREE.Projector(), + // layout (1 matrix = 4 pixels) + // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) + // with 8x8 pixel texture max 16 bones (8 * 8 / 4) + // 16x16 pixel texture max 64 bones (16 * 16 / 4) + // 32x32 pixel texture max 256 bones (32 * 32 / 4) + // 64x64 pixel texture max 1024 bones (64 * 64 / 4) - _canvas = parameters.canvas !== undefined - ? parameters.canvas - : document.createElement( 'canvas' ), + var size; - _canvasWidth = _canvas.width, - _canvasHeight = _canvas.height, - _canvasWidthHalf = Math.floor( _canvasWidth / 2 ), - _canvasHeightHalf = Math.floor( _canvasHeight / 2 ), + if ( this.bones.length > 256 ) + size = 64; + else if ( this.bones.length > 64 ) + size = 32; + else if ( this.bones.length > 16 ) + size = 16; + else + size = 8; - _viewportX = 0, - _viewportY = 0, - _viewportWidth = _canvasWidth, - _viewportHeight = _canvasHeight, + this.boneTextureWidth = size; + this.boneTextureHeight = size; - _context = _canvas.getContext( '2d', { - alpha: parameters.alpha === true - } ), + this.boneMatrices = new Float32Array( this.boneTextureWidth * this.boneTextureHeight * 4 ); // 4 floats per RGBA pixel + this.boneTexture = new THREE.DataTexture( this.boneMatrices, this.boneTextureWidth, this.boneTextureHeight, THREE.RGBAFormat, THREE.FloatType ); + this.boneTexture.minFilter = THREE.NearestFilter; + this.boneTexture.magFilter = THREE.NearestFilter; + this.boneTexture.generateMipmaps = false; + this.boneTexture.flipY = false; - _clearColor = new THREE.Color( 0x000000 ), - _clearAlpha = 0, + } else { - _contextGlobalAlpha = 1, - _contextGlobalCompositeOperation = 0, - _contextStrokeStyle = null, - _contextFillStyle = null, - _contextLineWidth = null, - _contextLineCap = null, - _contextLineJoin = null, - _contextLineDash = [], + this.boneMatrices = new Float32Array( 16 * this.bones.length ); - _camera, + } - _v1, _v2, _v3, _v4, - _v5 = new THREE.RenderableVertex(), - _v6 = new THREE.RenderableVertex(), + // use the supplied bone inverses or calculate the inverses - _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, - _v4x, _v4y, _v5x, _v5y, _v6x, _v6y, + if ( boneInverses === undefined ) { - _color = new THREE.Color(), - _color1 = new THREE.Color(), - _color2 = new THREE.Color(), - _color3 = new THREE.Color(), - _color4 = new THREE.Color(), + this.calculateInverses(); - _diffuseColor = new THREE.Color(), - _emissiveColor = new THREE.Color(), + } else { - _lightColor = new THREE.Color(), + if ( this.bones.length === boneInverses.length ) { - _patterns = {}, + this.boneInverses = boneInverses.slice( 0 ); - _image, _uvs, - _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, + } else { - _clipBox = new THREE.Box2(), - _clearBox = new THREE.Box2(), - _elemBox = new THREE.Box2(), + console.warn( 'THREE.Skeleton bonInverses is the wrong length.' ); - _ambientLight = new THREE.Color(), - _directionalLights = new THREE.Color(), - _pointLights = new THREE.Color(), + this.boneInverses = []; - _vector3 = new THREE.Vector3(), // Needed for PointLight - _centroid = new THREE.Vector3(), - _normal = new THREE.Vector3(), - _normalViewMatrix = new THREE.Matrix3(); + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { - // dash+gap fallbacks for Firefox and everything else + this.boneInverses.push( new THREE.Matrix4() ); - if ( _context.setLineDash === undefined ) { + } - _context.setLineDash = function () {} + } } - this.domElement = _canvas; +}; - this.devicePixelRatio = parameters.devicePixelRatio !== undefined - ? parameters.devicePixelRatio - : self.devicePixelRatio !== undefined - ? self.devicePixelRatio - : 1; +THREE.Skeleton.prototype.calculateInverses = function () { - this.autoClear = true; - this.sortObjects = true; - this.sortElements = true; + this.boneInverses = []; - this.info = { + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { - render: { + var inverse = new THREE.Matrix4(); - vertices: 0, - faces: 0 + if ( this.bones[ b ] ) { + + inverse.getInverse( this.bones[ b ].matrixWorld ); } + this.boneInverses.push( inverse ); + } - // WebGLRenderer compatibility +}; - this.supportsVertexTextures = function () {}; - this.setFaceCulling = function () {}; +THREE.Skeleton.prototype.pose = function () { - this.setSize = function ( width, height, updateStyle ) { + var bone; - _canvasWidth = width * this.devicePixelRatio; - _canvasHeight = height * this.devicePixelRatio; + // recover the bind-time world matrices - _canvas.width = _canvasWidth; - _canvas.height = _canvasHeight; + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { - _canvasWidthHalf = Math.floor( _canvasWidth / 2 ); - _canvasHeightHalf = Math.floor( _canvasHeight / 2 ); + bone = this.bones[ b ]; - if ( updateStyle !== false ) { + if ( bone ) { - _canvas.style.width = width + 'px'; - _canvas.style.height = height + 'px'; + bone.matrixWorld.getInverse( this.boneInverses[ b ] ); } - _clipBox.min.set( -_canvasWidthHalf, -_canvasHeightHalf ), - _clipBox.max.set( _canvasWidthHalf, _canvasHeightHalf ); - - _clearBox.min.set( - _canvasWidthHalf, - _canvasHeightHalf ); - _clearBox.max.set( _canvasWidthHalf, _canvasHeightHalf ); + } - _contextGlobalAlpha = 1; - _contextGlobalCompositeOperation = 0; - _contextStrokeStyle = null; - _contextFillStyle = null; - _contextLineWidth = null; - _contextLineCap = null; - _contextLineJoin = null; + // compute the local matrices, positions, rotations and scales - this.setViewport( 0, 0, width, height ); + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { - }; + bone = this.bones[ b ]; - this.setViewport = function ( x, y, width, height ) { + if ( bone ) { - _viewportX = x * this.devicePixelRatio; - _viewportY = y * this.devicePixelRatio; + if ( bone.parent ) { - _viewportWidth = width * this.devicePixelRatio; - _viewportHeight = height * this.devicePixelRatio; + bone.matrix.getInverse( bone.parent.matrixWorld ); + bone.matrix.multiply( bone.matrixWorld ); - }; + } else { - this.setScissor = function () {}; - this.enableScissorTest = function () {}; + bone.matrix.copy( bone.matrixWorld ); - this.setClearColor = function ( color, alpha ) { + } - _clearColor.set( color ); - _clearAlpha = alpha !== undefined ? alpha : 1; + bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); - _clearBox.min.set( - _canvasWidthHalf, - _canvasHeightHalf ); - _clearBox.max.set( _canvasWidthHalf, _canvasHeightHalf ); + } - }; + } - this.setClearColorHex = function ( hex, alpha ) { +}; - console.warn( 'THREE.CanvasRenderer: .setClearColorHex() is being removed. Use .setClearColor() instead.' ); - this.setClearColor( hex, alpha ); +THREE.Skeleton.prototype.update = ( function () { - }; + var offsetMatrix = new THREE.Matrix4(); - this.getClearColor = function () { + return function () { - return _clearColor; + // flatten bone matrices to array - }; + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { - this.getClearAlpha = function () { + // compute the offset between the current and the original transform - return _clearAlpha; + var matrix = this.bones[ b ] ? this.bones[ b ].matrixWorld : this.identityMatrix; - }; + offsetMatrix.multiplyMatrices( matrix, this.boneInverses[ b ] ); + offsetMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 ); - this.getMaxAnisotropy = function () { + } - return 0; + if ( this.useVertexTexture ) { - }; + this.boneTexture.needsUpdate = true; - this.clear = function () { + } + + }; - if ( _clearBox.empty() === false ) { +} )(); - _clearBox.intersect( _clipBox ); - _clearBox.expandByScalar( 2 ); - _clearBox.min.x = _clearBox.min.x + _canvasWidthHalf; - _clearBox.min.y = - _clearBox.min.y + _canvasHeightHalf; - _clearBox.max.x = _clearBox.max.x + _canvasWidthHalf; - _clearBox.max.y = - _clearBox.max.y + _canvasHeightHalf; +// File:src/objects/SkinnedMesh.js - if ( _clearAlpha < 1 ) { +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author ikerr / http://verold.com + */ - _context.clearRect( - _clearBox.min.x | 0, - _clearBox.min.y | 0, - ( _clearBox.max.x - _clearBox.min.x ) | 0, - ( _clearBox.max.y - _clearBox.min.y ) | 0 - ); +THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) { - } + THREE.Mesh.call( this, geometry, material ); - if ( _clearAlpha > 0 ) { + this.type = 'SkinnedMesh'; - setBlending( THREE.NormalBlending ); - setOpacity( 1 ); + this.bindMode = "attached"; + this.bindMatrix = new THREE.Matrix4(); + this.bindMatrixInverse = new THREE.Matrix4(); - setFillStyle( 'rgba(' + Math.floor( _clearColor.r * 255 ) + ',' + Math.floor( _clearColor.g * 255 ) + ',' + Math.floor( _clearColor.b * 255 ) + ',' + _clearAlpha + ')' ); + // init bones - _context.fillRect( - _clearBox.min.x | 0, - _clearBox.min.y | 0, - ( _clearBox.max.x - _clearBox.min.x ) | 0, - ( _clearBox.max.y - _clearBox.min.y ) | 0 - ); + // TODO: remove bone creation as there is no reason (other than + // convenience) for THREE.SkinnedMesh to do this. - } + var bones = []; - _clearBox.makeEmpty(); + if ( this.geometry && this.geometry.bones !== undefined ) { - } + var bone, gbone, p, q, s; - }; + for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++b ) { - // compatibility + gbone = this.geometry.bones[ b ]; - this.clearColor = function () {}; - this.clearDepth = function () {}; - this.clearStencil = function () {}; + p = gbone.pos; + q = gbone.rotq; + s = gbone.scl; - this.render = function ( scene, camera ) { + bone = new THREE.Bone( this ); + bones.push( bone ); - if ( camera instanceof THREE.Camera === false ) { + bone.name = gbone.name; + bone.position.set( p[ 0 ], p[ 1 ], p[ 2 ] ); + bone.quaternion.set( q[ 0 ], q[ 1 ], q[ 2 ], q[ 3 ] ); - console.error( 'THREE.CanvasRenderer.render: camera is not an instance of THREE.Camera.' ); - return; + if ( s !== undefined ) { - } + bone.scale.set( s[ 0 ], s[ 1 ], s[ 2 ] ); - if ( this.autoClear === true ) this.clear(); + } else { - _this.info.render.vertices = 0; - _this.info.render.faces = 0; + bone.scale.set( 1, 1, 1 ); - _context.setTransform( _viewportWidth / _canvasWidth, 0, 0, - _viewportHeight / _canvasHeight, _viewportX, _canvasHeight - _viewportY ); - _context.translate( _canvasWidthHalf, _canvasHeightHalf ); + } - _renderData = _projector.projectScene( scene, camera, this.sortObjects, this.sortElements ); - _elements = _renderData.elements; - _lights = _renderData.lights; - _camera = camera; + } - _normalViewMatrix.getNormalMatrix( camera.matrixWorldInverse ); + for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++b ) { - /* DEBUG - setFillStyle( 'rgba( 0, 255, 255, 0.5 )' ); - _context.fillRect( _clipBox.min.x, _clipBox.min.y, _clipBox.max.x - _clipBox.min.x, _clipBox.max.y - _clipBox.min.y ); - */ + gbone = this.geometry.bones[ b ]; - calculateLights(); + if ( gbone.parent !== - 1 ) { - for ( var e = 0, el = _elements.length; e < el; e ++ ) { + bones[ gbone.parent ].add( bones[ b ] ); - var element = _elements[ e ]; + } else { - var material = element.material; + this.add( bones[ b ] ); - if ( material === undefined || material.opacity === 0 ) continue; + } - _elemBox.makeEmpty(); + } - if ( element instanceof THREE.RenderableSprite ) { + } - _v1 = element; - _v1.x *= _canvasWidthHalf; _v1.y *= _canvasHeightHalf; + this.normalizeSkinWeights(); - renderSprite( _v1, element, material ); - - } else if ( element instanceof THREE.RenderableLine ) { - - _v1 = element.v1; _v2 = element.v2; - - _v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf; - _v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf; + this.updateMatrixWorld( true ); + this.bind( new THREE.Skeleton( bones, undefined, useVertexTexture ) ); - _elemBox.setFromPoints( [ - _v1.positionScreen, - _v2.positionScreen - ] ); +}; - if ( _clipBox.isIntersectionBox( _elemBox ) === true ) { - renderLine( _v1, _v2, element, material ); +THREE.SkinnedMesh.prototype = Object.create( THREE.Mesh.prototype ); - } +THREE.SkinnedMesh.prototype.bind = function( skeleton, bindMatrix ) { - } else if ( element instanceof THREE.RenderableFace ) { + this.skeleton = skeleton; - _v1 = element.v1; _v2 = element.v2; _v3 = element.v3; + if ( bindMatrix === undefined ) { - if ( _v1.positionScreen.z < - 1 || _v1.positionScreen.z > 1 ) continue; - if ( _v2.positionScreen.z < - 1 || _v2.positionScreen.z > 1 ) continue; - if ( _v3.positionScreen.z < - 1 || _v3.positionScreen.z > 1 ) continue; + this.updateMatrixWorld( true ); - _v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf; - _v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf; - _v3.positionScreen.x *= _canvasWidthHalf; _v3.positionScreen.y *= _canvasHeightHalf; + bindMatrix = this.matrixWorld; - if ( material.overdraw > 0 ) { + } - expand( _v1.positionScreen, _v2.positionScreen, material.overdraw ); - expand( _v2.positionScreen, _v3.positionScreen, material.overdraw ); - expand( _v3.positionScreen, _v1.positionScreen, material.overdraw ); + this.bindMatrix.copy( bindMatrix ); + this.bindMatrixInverse.getInverse( bindMatrix ); - } +}; - _elemBox.setFromPoints( [ - _v1.positionScreen, - _v2.positionScreen, - _v3.positionScreen - ] ); +THREE.SkinnedMesh.prototype.pose = function () { - if ( _clipBox.isIntersectionBox( _elemBox ) === true ) { + this.skeleton.pose(); - renderFace3( _v1, _v2, _v3, 0, 1, 2, element, material ); +}; - } +THREE.SkinnedMesh.prototype.normalizeSkinWeights = function () { - } + if ( this.geometry instanceof THREE.Geometry ) { - /* DEBUG - setLineWidth( 1 ); - setStrokeStyle( 'rgba( 0, 255, 0, 0.5 )' ); - _context.strokeRect( _elemBox.min.x, _elemBox.min.y, _elemBox.max.x - _elemBox.min.x, _elemBox.max.y - _elemBox.min.y ); - */ + for ( var i = 0; i < this.geometry.skinIndices.length; i ++ ) { - _clearBox.union( _elemBox ); + var sw = this.geometry.skinWeights[ i ]; - } + var scale = 1.0 / sw.lengthManhattan(); - /* DEBUG - setLineWidth( 1 ); - setStrokeStyle( 'rgba( 255, 0, 0, 0.5 )' ); - _context.strokeRect( _clearBox.min.x, _clearBox.min.y, _clearBox.max.x - _clearBox.min.x, _clearBox.max.y - _clearBox.min.y ); - */ + if ( scale !== Infinity ) { - _context.setTransform( 1, 0, 0, 1, 0, 0 ); + sw.multiplyScalar( scale ); - }; + } else { - // + sw.set( 1 ); // this will be normalized by the shader anyway - function calculateLights() { + } - _ambientLight.setRGB( 0, 0, 0 ); - _directionalLights.setRGB( 0, 0, 0 ); - _pointLights.setRGB( 0, 0, 0 ); + } - for ( var l = 0, ll = _lights.length; l < ll; l ++ ) { + } else { - var light = _lights[ l ]; - var lightColor = light.color; + // skinning weights assumed to be normalized for THREE.BufferGeometry - if ( light instanceof THREE.AmbientLight ) { + } - _ambientLight.add( lightColor ); +}; - } else if ( light instanceof THREE.DirectionalLight ) { +THREE.SkinnedMesh.prototype.updateMatrixWorld = function( force ) { - // for sprites + THREE.Mesh.prototype.updateMatrixWorld.call( this, true ); - _directionalLights.add( lightColor ); + if ( this.bindMode === "attached" ) { - } else if ( light instanceof THREE.PointLight ) { + this.bindMatrixInverse.getInverse( this.matrixWorld ); - // for sprites + } else if ( this.bindMode === "detached" ) { - _pointLights.add( lightColor ); + this.bindMatrixInverse.getInverse( this.bindMatrix ); - } + } else { - } + console.warn( 'THREE.SkinnedMesh unreckognized bindMode: ' + this.bindMode ); } - function calculateLight( position, normal, color ) { +}; - for ( var l = 0, ll = _lights.length; l < ll; l ++ ) { +THREE.SkinnedMesh.prototype.clone = function( object ) { - var light = _lights[ l ]; + if ( object === undefined ) { - _lightColor.copy( light.color ); + object = new THREE.SkinnedMesh( this.geometry, this.material, this.useVertexTexture ); - if ( light instanceof THREE.DirectionalLight ) { + } - var lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld ).normalize(); + THREE.Mesh.prototype.clone.call( this, object ); - var amount = normal.dot( lightPosition ); + return object; - if ( amount <= 0 ) continue; +}; - amount *= light.intensity; - color.add( _lightColor.multiplyScalar( amount ) ); +// File:src/objects/MorphAnimMesh.js - } else if ( light instanceof THREE.PointLight ) { +/** + * @author alteredq / http://alteredqualia.com/ + */ - var lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld ); +THREE.MorphAnimMesh = function ( geometry, material ) { - var amount = normal.dot( _vector3.subVectors( lightPosition, position ).normalize() ); + THREE.Mesh.call( this, geometry, material ); - if ( amount <= 0 ) continue; + this.type = 'MorphAnimMesh'; - amount *= light.distance == 0 ? 1 : 1 - Math.min( position.distanceTo( lightPosition ) / light.distance, 1 ); + // API - if ( amount == 0 ) continue; + this.duration = 1000; // milliseconds + this.mirroredLoop = false; + this.time = 0; - amount *= light.intensity; + // internals - color.add( _lightColor.multiplyScalar( amount ) ); + this.lastKeyframe = 0; + this.currentKeyframe = 0; - } + this.direction = 1; + this.directionBackwards = false; - } + this.setFrameRange( 0, this.geometry.morphTargets.length - 1 ); - } +}; - function renderSprite( v1, element, material ) { +THREE.MorphAnimMesh.prototype = Object.create( THREE.Mesh.prototype ); - setOpacity( material.opacity ); - setBlending( material.blending ); +THREE.MorphAnimMesh.prototype.setFrameRange = function ( start, end ) { - var scaleX = element.scale.x * _canvasWidthHalf; - var scaleY = element.scale.y * _canvasHeightHalf; + this.startKeyframe = start; + this.endKeyframe = end; - var dist = 0.5 * Math.sqrt( scaleX * scaleX + scaleY * scaleY ); // allow for rotated sprite - _elemBox.min.set( v1.x - dist, v1.y - dist ); - _elemBox.max.set( v1.x + dist, v1.y + dist ); + this.length = this.endKeyframe - this.startKeyframe + 1; - if ( material instanceof THREE.SpriteMaterial ) { +}; - var texture = material.map; +THREE.MorphAnimMesh.prototype.setDirectionForward = function () { - if ( texture !== null && texture.image !== undefined ) { + this.direction = 1; + this.directionBackwards = false; - if ( texture.hasEventListener( 'update', onTextureUpdate ) === false ) { +}; - if ( texture.image.width > 0 ) { +THREE.MorphAnimMesh.prototype.setDirectionBackward = function () { - textureToPattern( texture ); + this.direction = - 1; + this.directionBackwards = true; - } +}; - texture.addEventListener( 'update', onTextureUpdate ); +THREE.MorphAnimMesh.prototype.parseAnimations = function () { - } + var geometry = this.geometry; - var pattern = _patterns[ texture.id ]; + if ( ! geometry.animations ) geometry.animations = {}; - if ( pattern !== undefined ) { + var firstAnimation, animations = geometry.animations; - setFillStyle( pattern ); + var pattern = /([a-z]+)_?(\d+)/; - } else { + for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) { - setFillStyle( 'rgba( 0, 0, 0, 1 )' ); + var morph = geometry.morphTargets[ i ]; + var parts = morph.name.match( pattern ); - } + if ( parts && parts.length > 1 ) { - // + var label = parts[ 1 ]; + var num = parts[ 2 ]; - var bitmap = texture.image; + if ( ! animations[ label ] ) animations[ label ] = { start: Infinity, end: - Infinity }; - var ox = bitmap.width * texture.offset.x; - var oy = bitmap.height * texture.offset.y; + var animation = animations[ label ]; - var sx = bitmap.width * texture.repeat.x; - var sy = bitmap.height * texture.repeat.y; + if ( i < animation.start ) animation.start = i; + if ( i > animation.end ) animation.end = i; - var cx = scaleX / sx; - var cy = scaleY / sy; + if ( ! firstAnimation ) firstAnimation = label; - _context.save(); - _context.translate( v1.x, v1.y ); - if ( material.rotation !== 0 ) _context.rotate( material.rotation ); - _context.translate( - scaleX / 2, - scaleY / 2 ); - _context.scale( cx, cy ); - _context.translate( - ox, - oy ); - _context.fillRect( ox, oy, sx, sy ); - _context.restore(); + } - } else { + } - // no texture + geometry.firstAnimation = firstAnimation; - setFillStyle( material.color.getStyle() ); +}; - _context.save(); - _context.translate( v1.x, v1.y ); - if ( material.rotation !== 0 ) _context.rotate( material.rotation ); - _context.scale( scaleX, - scaleY ); - _context.fillRect( - 0.5, - 0.5, 1, 1 ); - _context.restore(); +THREE.MorphAnimMesh.prototype.setAnimationLabel = function ( label, start, end ) { - } + if ( ! this.geometry.animations ) this.geometry.animations = {}; - } else if ( material instanceof THREE.SpriteCanvasMaterial ) { + this.geometry.animations[ label ] = { start: start, end: end }; - setStrokeStyle( material.color.getStyle() ); - setFillStyle( material.color.getStyle() ); +}; - _context.save(); - _context.translate( v1.x, v1.y ); - if ( material.rotation !== 0 ) _context.rotate( material.rotation ); - _context.scale( scaleX, scaleY ); +THREE.MorphAnimMesh.prototype.playAnimation = function ( label, fps ) { - material.program( _context ); + var animation = this.geometry.animations[ label ]; - _context.restore(); + if ( animation ) { - } + this.setFrameRange( animation.start, animation.end ); + this.duration = 1000 * ( ( animation.end - animation.start ) / fps ); + this.time = 0; - /* DEBUG - setStrokeStyle( 'rgb(255,255,0)' ); - _context.beginPath(); - _context.moveTo( v1.x - 10, v1.y ); - _context.lineTo( v1.x + 10, v1.y ); - _context.moveTo( v1.x, v1.y - 10 ); - _context.lineTo( v1.x, v1.y + 10 ); - _context.stroke(); - */ + } else { + + console.warn( 'animation[' + label + '] undefined' ); } - function renderLine( v1, v2, element, material ) { +}; - setOpacity( material.opacity ); - setBlending( material.blending ); +THREE.MorphAnimMesh.prototype.updateAnimation = function ( delta ) { - _context.beginPath(); - _context.moveTo( v1.positionScreen.x, v1.positionScreen.y ); - _context.lineTo( v2.positionScreen.x, v2.positionScreen.y ); + var frameTime = this.duration / this.length; - if ( material instanceof THREE.LineBasicMaterial ) { + this.time += this.direction * delta; - setLineWidth( material.linewidth ); - setLineCap( material.linecap ); - setLineJoin( material.linejoin ); + if ( this.mirroredLoop ) { - if ( material.vertexColors !== THREE.VertexColors ) { + if ( this.time > this.duration || this.time < 0 ) { - setStrokeStyle( material.color.getStyle() ); + this.direction *= - 1; - } else { + if ( this.time > this.duration ) { - var colorStyle1 = element.vertexColors[ 0 ].getStyle(); - var colorStyle2 = element.vertexColors[ 1 ].getStyle(); + this.time = this.duration; + this.directionBackwards = true; - if ( colorStyle1 === colorStyle2 ) { + } - setStrokeStyle( colorStyle1 ); + if ( this.time < 0 ) { - } else { + this.time = 0; + this.directionBackwards = false; - try { + } - var grad = _context.createLinearGradient( - v1.positionScreen.x, - v1.positionScreen.y, - v2.positionScreen.x, - v2.positionScreen.y - ); - grad.addColorStop( 0, colorStyle1 ); - grad.addColorStop( 1, colorStyle2 ); + } - } catch ( exception ) { + } else { - grad = colorStyle1; + this.time = this.time % this.duration; - } + if ( this.time < 0 ) this.time += this.duration; - setStrokeStyle( grad ); + } - } + var keyframe = this.startKeyframe + THREE.Math.clamp( Math.floor( this.time / frameTime ), 0, this.length - 1 ); - } + if ( keyframe !== this.currentKeyframe ) { - _context.stroke(); - _elemBox.expandByScalar( material.linewidth * 2 ); + this.morphTargetInfluences[ this.lastKeyframe ] = 0; + this.morphTargetInfluences[ this.currentKeyframe ] = 1; - } else if ( material instanceof THREE.LineDashedMaterial ) { + this.morphTargetInfluences[ keyframe ] = 0; - setLineWidth( material.linewidth ); - setLineCap( material.linecap ); - setLineJoin( material.linejoin ); - setStrokeStyle( material.color.getStyle() ); - setLineDash( [ material.dashSize, material.gapSize ] ); + this.lastKeyframe = this.currentKeyframe; + this.currentKeyframe = keyframe; - _context.stroke(); + } - _elemBox.expandByScalar( material.linewidth * 2 ); + var mix = ( this.time % frameTime ) / frameTime; - setLineDash( [] ); + if ( this.directionBackwards ) { - } + mix = 1 - mix; } - function renderFace3( v1, v2, v3, uv1, uv2, uv3, element, material ) { + this.morphTargetInfluences[ this.currentKeyframe ] = mix; + this.morphTargetInfluences[ this.lastKeyframe ] = 1 - mix; - _this.info.render.vertices += 3; - _this.info.render.faces ++; +}; - setOpacity( material.opacity ); - setBlending( material.blending ); +THREE.MorphAnimMesh.prototype.interpolateTargets = function ( a, b, t ) { - _v1x = v1.positionScreen.x; _v1y = v1.positionScreen.y; - _v2x = v2.positionScreen.x; _v2y = v2.positionScreen.y; - _v3x = v3.positionScreen.x; _v3y = v3.positionScreen.y; + var influences = this.morphTargetInfluences; - drawTriangle( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y ); + for ( var i = 0, l = influences.length; i < l; i ++ ) { - if ( ( material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) && material.map === null ) { + influences[ i ] = 0; - _diffuseColor.copy( material.color ); - _emissiveColor.copy( material.emissive ); + } - if ( material.vertexColors === THREE.FaceColors ) { + if ( a > -1 ) influences[ a ] = 1 - t; + if ( b > -1 ) influences[ b ] = t; - _diffuseColor.multiply( element.color ); +}; - } +THREE.MorphAnimMesh.prototype.clone = function ( object ) { - _color.copy( _ambientLight ); + if ( object === undefined ) object = new THREE.MorphAnimMesh( this.geometry, this.material ); - _centroid.copy( v1.positionWorld ).add( v2.positionWorld ).add( v3.positionWorld ).divideScalar( 3 ); + object.duration = this.duration; + object.mirroredLoop = this.mirroredLoop; + object.time = this.time; - calculateLight( _centroid, element.normalModel, _color ); + object.lastKeyframe = this.lastKeyframe; + object.currentKeyframe = this.currentKeyframe; - _color.multiply( _diffuseColor ).add( _emissiveColor ); + object.direction = this.direction; + object.directionBackwards = this.directionBackwards; - material.wireframe === true - ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) - : fillPath( _color ); + THREE.Mesh.prototype.clone.call( this, object ); - } else if ( material instanceof THREE.MeshBasicMaterial || - material instanceof THREE.MeshLambertMaterial || - material instanceof THREE.MeshPhongMaterial ) { + return object; - if ( material.map !== null ) { +}; - if ( material.map.mapping instanceof THREE.UVMapping ) { +// File:src/objects/LOD.js - _uvs = element.uvs; - patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uvs[ uv1 ].x, _uvs[ uv1 ].y, _uvs[ uv2 ].x, _uvs[ uv2 ].y, _uvs[ uv3 ].x, _uvs[ uv3 ].y, material.map ); +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ - } +THREE.LOD = function () { - } else if ( material.envMap !== null ) { + THREE.Object3D.call( this ); - if ( material.envMap.mapping instanceof THREE.SphericalReflectionMapping ) { + this.objects = []; - _normal.copy( element.vertexNormalsModel[ uv1 ] ).applyMatrix3( _normalViewMatrix ); - _uv1x = 0.5 * _normal.x + 0.5; - _uv1y = 0.5 * _normal.y + 0.5; +}; - _normal.copy( element.vertexNormalsModel[ uv2 ] ).applyMatrix3( _normalViewMatrix ); - _uv2x = 0.5 * _normal.x + 0.5; - _uv2y = 0.5 * _normal.y + 0.5; - _normal.copy( element.vertexNormalsModel[ uv3 ] ).applyMatrix3( _normalViewMatrix ); - _uv3x = 0.5 * _normal.x + 0.5; - _uv3y = 0.5 * _normal.y + 0.5; +THREE.LOD.prototype = Object.create( THREE.Object3D.prototype ); - patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, material.envMap ); +THREE.LOD.prototype.addLevel = function ( object, distance ) { - } else if ( material.envMap.mapping instanceof THREE.SphericalRefractionMapping ) { + if ( distance === undefined ) distance = 0; - _normal.copy( element.vertexNormalsModel[ uv1 ] ).applyMatrix3( _normalViewMatrix ); - _uv1x = - 0.5 * _normal.x + 0.5; - _uv1y = - 0.5 * _normal.y + 0.5; + distance = Math.abs( distance ); - _normal.copy( element.vertexNormalsModel[ uv2 ] ).applyMatrix3( _normalViewMatrix ); - _uv2x = - 0.5 * _normal.x + 0.5; - _uv2y = - 0.5 * _normal.y + 0.5; + for ( var l = 0; l < this.objects.length; l ++ ) { - _normal.copy( element.vertexNormalsModel[ uv3 ] ).applyMatrix3( _normalViewMatrix ); - _uv3x = - 0.5 * _normal.x + 0.5; - _uv3y = - 0.5 * _normal.y + 0.5; + if ( distance < this.objects[ l ].distance ) { - patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, material.envMap ); + break; - } + } + } - } else { + this.objects.splice( l, 0, { distance: distance, object: object } ); + this.add( object ); - _color.copy( material.color ); +}; - if ( material.vertexColors === THREE.FaceColors ) { +THREE.LOD.prototype.getObjectForDistance = function ( distance ) { - _color.multiply( element.color ); + for ( var i = 1, l = this.objects.length; i < l; i ++ ) { - } + if ( distance < this.objects[ i ].distance ) { - material.wireframe === true - ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) - : fillPath( _color ); + break; - } + } - } else if ( material instanceof THREE.MeshDepthMaterial ) { + } - _color.r = _color.g = _color.b = 1 - smoothstep( v1.positionScreen.z * v1.positionScreen.w, _camera.near, _camera.far ); + return this.objects[ i - 1 ].object; - material.wireframe === true - ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) - : fillPath( _color ); +}; - } else if ( material instanceof THREE.MeshNormalMaterial ) { +THREE.LOD.prototype.raycast = ( function () { - _normal.copy( element.normalModel ).applyMatrix3( _normalViewMatrix ); + var matrixPosition = new THREE.Vector3(); - _color.setRGB( _normal.x, _normal.y, _normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 ); + return function ( raycaster, intersects ) { - material.wireframe === true - ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) - : fillPath( _color ); + matrixPosition.setFromMatrixPosition( this.matrixWorld ); - } else { + var distance = raycaster.ray.origin.distanceTo( matrixPosition ); - _color.setRGB( 1, 1, 1 ); + this.getObjectForDistance( distance ).raycast( raycaster, intersects ); - material.wireframe === true - ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) - : fillPath( _color ); + }; - } +}() ); - } +THREE.LOD.prototype.update = function () { - // + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); - function drawTriangle( x0, y0, x1, y1, x2, y2 ) { + return function ( camera ) { - _context.beginPath(); - _context.moveTo( x0, y0 ); - _context.lineTo( x1, y1 ); - _context.lineTo( x2, y2 ); - _context.closePath(); + if ( this.objects.length > 1 ) { - } + v1.setFromMatrixPosition( camera.matrixWorld ); + v2.setFromMatrixPosition( this.matrixWorld ); - function strokePath( color, linewidth, linecap, linejoin ) { + var distance = v1.distanceTo( v2 ); - setLineWidth( linewidth ); - setLineCap( linecap ); - setLineJoin( linejoin ); - setStrokeStyle( color.getStyle() ); + this.objects[ 0 ].object.visible = true; - _context.stroke(); + for ( var i = 1, l = this.objects.length; i < l; i ++ ) { - _elemBox.expandByScalar( linewidth * 2 ); + if ( distance >= this.objects[ i ].distance ) { - } + this.objects[ i - 1 ].object.visible = false; + this.objects[ i ].object.visible = true; - function fillPath( color ) { + } else { - setFillStyle( color.getStyle() ); - _context.fill(); + break; - } + } - function onTextureUpdate ( event ) { + } - textureToPattern( event.target ); + for ( ; i < l; i ++ ) { - } + this.objects[ i ].object.visible = false; - function textureToPattern( texture ) { + } - if ( texture instanceof THREE.CompressedTexture ) return; + } - var repeatX = texture.wrapS === THREE.RepeatWrapping; - var repeatY = texture.wrapT === THREE.RepeatWrapping; + }; - var image = texture.image; +}(); - var canvas = document.createElement( 'canvas' ); - canvas.width = image.width; - canvas.height = image.height; +THREE.LOD.prototype.clone = function ( object ) { - var context = canvas.getContext( '2d' ); - context.setTransform( 1, 0, 0, - 1, 0, image.height ); - context.drawImage( image, 0, 0 ); + if ( object === undefined ) object = new THREE.LOD(); - _patterns[ texture.id ] = _context.createPattern( - canvas, repeatX === true && repeatY === true - ? 'repeat' - : repeatX === true && repeatY === false - ? 'repeat-x' - : repeatX === false && repeatY === true - ? 'repeat-y' - : 'no-repeat' - ); + THREE.Object3D.prototype.clone.call( this, object ); + for ( var i = 0, l = this.objects.length; i < l; i ++ ) { + var x = this.objects[ i ].object.clone(); + x.visible = i === 0; + object.addLevel( x, this.objects[ i ].distance ); } - function patternPath( x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2, texture ) { - - if ( texture instanceof THREE.DataTexture ) return; - - if ( texture.hasEventListener( 'update', onTextureUpdate ) === false ) { - - if ( texture.image !== undefined && texture.image.width > 0 ) { + return object; - textureToPattern( texture ); +}; - } +// File:src/objects/Sprite.js - texture.addEventListener( 'update', onTextureUpdate ); +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ - } +THREE.Sprite = ( function () { - var pattern = _patterns[ texture.id ]; + var indices = new Uint16Array( [ 0, 1, 2, 0, 2, 3 ] ); + var vertices = new Float32Array( [ - 0.5, - 0.5, 0, 0.5, - 0.5, 0, 0.5, 0.5, 0, - 0.5, 0.5, 0 ] ); + var uvs = new Float32Array( [ 0, 0, 1, 0, 1, 1, 0, 1 ] ); - if ( pattern !== undefined ) { + var geometry = new THREE.BufferGeometry(); + geometry.addAttribute( 'index', new THREE.BufferAttribute( indices, 1 ) ); + geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); + geometry.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); - setFillStyle( pattern ); + return function ( material ) { - } else { + THREE.Object3D.call( this ); - setFillStyle( 'rgba(0,0,0,1)' ); - _context.fill(); + this.type = 'Sprite'; - return; + this.geometry = geometry; + this.material = ( material !== undefined ) ? material : new THREE.SpriteMaterial(); - } + }; - // http://extremelysatisfactorytotalitarianism.com/blog/?p=2120 +} )(); - var a, b, c, d, e, f, det, idet, - offsetX = texture.offset.x / texture.repeat.x, - offsetY = texture.offset.y / texture.repeat.y, - width = texture.image.width * texture.repeat.x, - height = texture.image.height * texture.repeat.y; +THREE.Sprite.prototype = Object.create( THREE.Object3D.prototype ); - u0 = ( u0 + offsetX ) * width; - v0 = ( v0 + offsetY ) * height; +THREE.Sprite.prototype.raycast = ( function () { - u1 = ( u1 + offsetX ) * width; - v1 = ( v1 + offsetY ) * height; + var matrixPosition = new THREE.Vector3(); - u2 = ( u2 + offsetX ) * width; - v2 = ( v2 + offsetY ) * height; + return function ( raycaster, intersects ) { - x1 -= x0; y1 -= y0; - x2 -= x0; y2 -= y0; + matrixPosition.setFromMatrixPosition( this.matrixWorld ); - u1 -= u0; v1 -= v0; - u2 -= u0; v2 -= v0; + var distance = raycaster.ray.distanceToPoint( matrixPosition ); - det = u1 * v2 - u2 * v1; + if ( distance > this.scale.x ) { - if ( det === 0 ) return; + return; - idet = 1 / det; + } - a = ( v2 * x1 - v1 * x2 ) * idet; - b = ( v2 * y1 - v1 * y2 ) * idet; - c = ( u1 * x2 - u2 * x1 ) * idet; - d = ( u1 * y2 - u2 * y1 ) * idet; + intersects.push( { - e = x0 - a * u0 - c * v0; - f = y0 - b * u0 - d * v0; + distance: distance, + point: this.position, + face: null, + object: this - _context.save(); - _context.transform( a, b, c, d, e, f ); - _context.fill(); - _context.restore(); + } ); - } + }; - function clipImage( x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2, image ) { +}() ); - // http://extremelysatisfactorytotalitarianism.com/blog/?p=2120 +THREE.Sprite.prototype.clone = function ( object ) { - var a, b, c, d, e, f, det, idet, - width = image.width - 1, - height = image.height - 1; + if ( object === undefined ) object = new THREE.Sprite( this.material ); - u0 *= width; v0 *= height; - u1 *= width; v1 *= height; - u2 *= width; v2 *= height; + THREE.Object3D.prototype.clone.call( this, object ); - x1 -= x0; y1 -= y0; - x2 -= x0; y2 -= y0; + return object; - u1 -= u0; v1 -= v0; - u2 -= u0; v2 -= v0; +}; - det = u1 * v2 - u2 * v1; +// Backwards compatibility - idet = 1 / det; +THREE.Particle = THREE.Sprite; - a = ( v2 * x1 - v1 * x2 ) * idet; - b = ( v2 * y1 - v1 * y2 ) * idet; - c = ( u1 * x2 - u2 * x1 ) * idet; - d = ( u1 * y2 - u2 * y1 ) * idet; +// File:src/objects/LensFlare.js - e = x0 - a * u0 - c * v0; - f = y0 - b * u0 - d * v0; +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ - _context.save(); - _context.transform( a, b, c, d, e, f ); - _context.clip(); - _context.drawImage( image, 0, 0 ); - _context.restore(); +THREE.LensFlare = function ( texture, size, distance, blending, color ) { - } + THREE.Object3D.call( this ); - // Hide anti-alias gaps + this.lensFlares = []; - function expand( v1, v2, pixels ) { + this.positionScreen = new THREE.Vector3(); + this.customUpdateCallback = undefined; - var x = v2.x - v1.x, y = v2.y - v1.y, - det = x * x + y * y, idet; + if( texture !== undefined ) { - if ( det === 0 ) return; + this.add( texture, size, distance, blending, color ); - idet = pixels / Math.sqrt( det ); + } - x *= idet; y *= idet; +}; - v2.x += x; v2.y += y; - v1.x -= x; v1.y -= y; +THREE.LensFlare.prototype = Object.create( THREE.Object3D.prototype ); - } - // Context cached methods. +/* + * Add: adds another flare + */ - function setOpacity( value ) { +THREE.LensFlare.prototype.add = function ( texture, size, distance, blending, color, opacity ) { - if ( _contextGlobalAlpha !== value ) { + if ( size === undefined ) size = - 1; + if ( distance === undefined ) distance = 0; + if ( opacity === undefined ) opacity = 1; + if ( color === undefined ) color = new THREE.Color( 0xffffff ); + if ( blending === undefined ) blending = THREE.NormalBlending; - _context.globalAlpha = value; - _contextGlobalAlpha = value; + distance = Math.min( distance, Math.max( 0, distance ) ); - } + this.lensFlares.push( { + texture: texture, // THREE.Texture + size: size, // size in pixels (-1 = use texture.width) + distance: distance, // distance (0-1) from light source (0=at light source) + x: 0, y: 0, z: 0, // screen position (-1 => 1) z = 0 is ontop z = 1 is back + scale: 1, // scale + rotation: 1, // rotation + opacity: opacity, // opacity + color: color, // color + blending: blending // blending + } ); - } +}; - function setBlending( value ) { +/* + * Update lens flares update positions on all flares based on the screen position + * Set myLensFlare.customUpdateCallback to alter the flares in your project specific way. + */ - if ( _contextGlobalCompositeOperation !== value ) { +THREE.LensFlare.prototype.updateLensFlares = function () { - if ( value === THREE.NormalBlending ) { + var f, fl = this.lensFlares.length; + var flare; + var vecX = - this.positionScreen.x * 2; + var vecY = - this.positionScreen.y * 2; - _context.globalCompositeOperation = 'source-over'; + for( f = 0; f < fl; f ++ ) { - } else if ( value === THREE.AdditiveBlending ) { + flare = this.lensFlares[ f ]; - _context.globalCompositeOperation = 'lighter'; + flare.x = this.positionScreen.x + vecX * flare.distance; + flare.y = this.positionScreen.y + vecY * flare.distance; - } else if ( value === THREE.SubtractiveBlending ) { + flare.wantedRotation = flare.x * Math.PI * 0.25; + flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25; - _context.globalCompositeOperation = 'darker'; + } - } +}; - _contextGlobalCompositeOperation = value; - } +// File:src/scenes/Scene.js - } +/** + * @author mrdoob / http://mrdoob.com/ + */ - function setLineWidth( value ) { +THREE.Scene = function () { - if ( _contextLineWidth !== value ) { + THREE.Object3D.call( this ); - _context.lineWidth = value; - _contextLineWidth = value; + this.type = 'Scene'; - } + this.fog = null; + this.overrideMaterial = null; - } + this.autoUpdate = true; // checked by the renderer - function setLineCap( value ) { +}; - // "butt", "round", "square" +THREE.Scene.prototype = Object.create( THREE.Object3D.prototype ); - if ( _contextLineCap !== value ) { +THREE.Scene.prototype.clone = function ( object ) { - _context.lineCap = value; - _contextLineCap = value; + if ( object === undefined ) object = new THREE.Scene(); - } + THREE.Object3D.prototype.clone.call( this, object ); - } + if ( this.fog !== null ) object.fog = this.fog.clone(); + if ( this.overrideMaterial !== null ) object.overrideMaterial = this.overrideMaterial.clone(); - function setLineJoin( value ) { + object.autoUpdate = this.autoUpdate; + object.matrixAutoUpdate = this.matrixAutoUpdate; - // "round", "bevel", "miter" + return object; - if ( _contextLineJoin !== value ) { +}; - _context.lineJoin = value; - _contextLineJoin = value; +// File:src/scenes/Fog.js - } +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ - } +THREE.Fog = function ( color, near, far ) { - function setStrokeStyle( value ) { + this.name = ''; - if ( _contextStrokeStyle !== value ) { + this.color = new THREE.Color( color ); - _context.strokeStyle = value; - _contextStrokeStyle = value; + this.near = ( near !== undefined ) ? near : 1; + this.far = ( far !== undefined ) ? far : 1000; - } +}; - } +THREE.Fog.prototype.clone = function () { - function setFillStyle( value ) { + return new THREE.Fog( this.color.getHex(), this.near, this.far ); - if ( _contextFillStyle !== value ) { +}; - _context.fillStyle = value; - _contextFillStyle = value; +// File:src/scenes/FogExp2.js - } +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ - } +THREE.FogExp2 = function ( color, density ) { - function setLineDash( value ) { + this.name = ''; - if ( _contextLineDash.length !== value.length ) { + this.color = new THREE.Color( color ); + this.density = ( density !== undefined ) ? density : 0.00025; - _context.setLineDash( value ); - _contextLineDash = value; +}; - } +THREE.FogExp2.prototype.clone = function () { - } + return new THREE.FogExp2( this.color.getHex(), this.density ); }; @@ -17080,7 +15995,7 @@ THREE.ShaderChunk[ 'morphnormal_vertex'] = "#ifdef USE_MORPHNORMALS\n\n vec3 mor // File:src/renderers/shaders/ShaderChunk/envmap_pars_fragment.glsl -THREE.ShaderChunk[ 'envmap_pars_fragment'] = "#ifdef USE_ENVMAP\n\n uniform float reflectivity;\n uniform samplerCube envMap;\n uniform float flipEnvMap;\n uniform int combine;\n\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\n\n uniform bool useRefract;\n uniform float refractionRatio;\n\n #else\n\n varying vec3 vReflect;\n\n #endif\n\n#endif"; +THREE.ShaderChunk[ 'envmap_pars_fragment'] = "#ifdef USE_ENVMAP\n\n uniform float reflectivity;\n uniform samplerCube envMap;\n uniform float flipEnvMap;\n uniform int combine;\n\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n uniform bool useRefract;\n uniform float refractionRatio;\n\n #else\n\n varying vec3 vReflect;\n\n #endif\n\n#endif"; // File:src/renderers/shaders/ShaderChunk/logdepthbuf_fragment.glsl @@ -17132,7 +16047,7 @@ THREE.ShaderChunk[ 'skinning_vertex'] = "#ifdef USE_SKINNING\n\n #ifdef USE_MORP // File:src/renderers/shaders/ShaderChunk/envmap_pars_vertex.glsl -THREE.ShaderChunk[ 'envmap_pars_vertex'] = "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP )\n\n varying vec3 vReflect;\n\n uniform float refractionRatio;\n uniform bool useRefract;\n\n#endif\n"; +THREE.ShaderChunk[ 'envmap_pars_vertex'] = "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n varying vec3 vReflect;\n\n uniform float refractionRatio;\n uniform bool useRefract;\n\n#endif\n"; // File:src/renderers/shaders/ShaderChunk/linear_to_gamma_fragment.glsl @@ -17152,7 +16067,7 @@ THREE.ShaderChunk[ 'map_pars_vertex'] = "#if defined( USE_MAP ) || defined( USE_ // File:src/renderers/shaders/ShaderChunk/envmap_fragment.glsl -THREE.ShaderChunk[ 'envmap_fragment'] = "#ifdef USE_ENVMAP\n\n vec3 reflectVec;\n\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\n\n vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\n // http://en.wikibooks.org/wiki/GLSL_Programming/Applying_Matrix_Transformations\n // Transforming Normal Vectors with the Inverse Transformation\n\n vec3 worldNormal = normalize( vec3( vec4( normal, 0.0 ) * viewMatrix ) );\n\n if ( useRefract ) {\n\n reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\n } else { \n\n reflectVec = reflect( cameraToVertex, worldNormal );\n\n }\n\n #else\n\n reflectVec = vReflect;\n\n #endif\n\n #ifdef DOUBLE_SIDED\n\n float flipNormal = ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n vec4 cubeColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\n #else\n\n vec4 cubeColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\n #endif\n\n #ifdef GAMMA_INPUT\n\n cubeColor.xyz *= cubeColor.xyz;\n\n #endif\n\n if ( combine == 1 ) {\n\n gl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularStrength * reflectivity );\n\n } else if ( combine == 2 ) {\n\n gl_FragColor.xyz += cubeColor.xyz * specularStrength * reflectivity;\n\n } else {\n\n gl_FragColor.xyz = mix( gl_FragColor.xyz, gl_FragColor.xyz * cubeColor.xyz, specularStrength * reflectivity );\n\n }\n\n#endif"; +THREE.ShaderChunk[ 'envmap_fragment'] = "#ifdef USE_ENVMAP\n\n vec3 reflectVec;\n\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\n // http://en.wikibooks.org/wiki/GLSL_Programming/Applying_Matrix_Transformations\n // Transforming Normal Vectors with the Inverse Transformation\n\n vec3 worldNormal = normalize( vec3( vec4( normal, 0.0 ) * viewMatrix ) );\n\n if ( useRefract ) {\n\n reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\n } else { \n\n reflectVec = reflect( cameraToVertex, worldNormal );\n\n }\n\n #else\n\n reflectVec = vReflect;\n\n #endif\n\n #ifdef DOUBLE_SIDED\n\n float flipNormal = ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n vec4 cubeColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\n #else\n\n vec4 cubeColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\n #endif\n\n #ifdef GAMMA_INPUT\n\n cubeColor.xyz *= cubeColor.xyz;\n\n #endif\n\n if ( combine == 1 ) {\n\n gl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularStrength * reflectivity );\n\n } else if ( combine == 2 ) {\n\n gl_FragColor.xyz += cubeColor.xyz * specularStrength * reflectivity;\n\n } else {\n\n gl_FragColor.xyz = mix( gl_FragColor.xyz, gl_FragColor.xyz * cubeColor.xyz, specularStrength * reflectivity );\n\n }\n\n#endif"; // File:src/renderers/shaders/ShaderChunk/specularmap_pars_fragment.glsl @@ -17212,7 +16127,7 @@ THREE.ShaderChunk[ 'morphtarget_vertex'] = "#ifdef USE_MORPHTARGETS\n\n vec3 mor // File:src/renderers/shaders/ShaderChunk/envmap_vertex.glsl -THREE.ShaderChunk[ 'envmap_vertex'] = "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP )\n\n vec3 worldNormal = mat3( modelMatrix[ 0 ].xyz, modelMatrix[ 1 ].xyz, modelMatrix[ 2 ].xyz ) * objectNormal;\n worldNormal = normalize( worldNormal );\n\n vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\n if ( useRefract ) {\n\n vReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\n } else {\n\n vReflect = reflect( cameraToVertex, worldNormal );\n\n }\n\n#endif"; +THREE.ShaderChunk[ 'envmap_vertex'] = "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n vec3 worldNormal = mat3( modelMatrix[ 0 ].xyz, modelMatrix[ 1 ].xyz, modelMatrix[ 2 ].xyz ) * objectNormal;\n worldNormal = normalize( worldNormal );\n\n vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\n if ( useRefract ) {\n\n vReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\n } else {\n\n vReflect = reflect( cameraToVertex, worldNormal );\n\n }\n\n#endif"; // File:src/renderers/shaders/ShaderChunk/shadowmap_fragment.glsl @@ -17252,13 +16167,13 @@ THREE.UniformsUtils = { merge: function ( uniforms ) { - var u, p, tmp, merged = {}; + var merged = {}; - for ( u = 0; u < uniforms.length; u ++ ) { + for ( var u = 0; u < uniforms.length; u ++ ) { - tmp = this.clone( uniforms[ u ] ); + var tmp = this.clone( uniforms[ u ] ); - for ( p in tmp ) { + for ( var p in tmp ) { merged[ p ] = tmp[ p ]; @@ -17272,15 +16187,15 @@ THREE.UniformsUtils = { clone: function ( uniforms_src ) { - var u, p, parameter, parameter_src, uniforms_dst = {}; + var uniforms_dst = {}; - for ( u in uniforms_src ) { + for ( var u in uniforms_src ) { uniforms_dst[ u ] = {}; - for ( p in uniforms_src[ u ] ) { + for ( var p in uniforms_src[ u ] ) { - parameter_src = uniforms_src[ u ][ p ]; + var parameter_src = uniforms_src[ u ][ p ]; if ( parameter_src instanceof THREE.Color || parameter_src instanceof THREE.Vector2 || @@ -17714,6 +16629,8 @@ THREE.ShaderLib = { fragmentShader: [ + "#define PHONG", + "uniform vec3 diffuse;", "uniform float opacity;", @@ -18778,10 +17695,18 @@ THREE.WebGLRenderer = function ( parameters ) { _clearColor = new THREE.Color( 0x000000 ), _clearAlpha = 0; - + + var lights = []; + + var _webglObjects = {}; + var _webglObjectsImmediate = []; + var opaqueObjects = []; var transparentObjects = []; + var sprites = []; + var lensFlares = []; + // public properties this.domElement = _canvas; @@ -18811,7 +17736,6 @@ THREE.WebGLRenderer = function ( parameters ) { // shadow map this.shadowMapEnabled = false; - this.shadowMapAutoUpdate = true; this.shadowMapType = THREE.PCFShadowMap; this.shadowMapCullFace = THREE.CullFaceFront; this.shadowMapDebug = false; @@ -18826,11 +17750,6 @@ THREE.WebGLRenderer = function ( parameters ) { this.autoScaleCubemaps = true; - // custom render plugins - - this.renderPluginsPre = []; - this.renderPluginsPost = []; - // info this.info = { @@ -18865,7 +17784,7 @@ THREE.WebGLRenderer = function ( parameters ) { _currentProgram = null, _currentFramebuffer = null, _currentMaterialId = - 1, - _currentGeometryGroupHash = null, + _currentGeometryGroupHash = - 1, _currentCamera = null, _usedTextureUnits = 0, @@ -18931,111 +17850,247 @@ THREE.WebGLRenderer = function ( parameters ) { var _gl; - var _glExtensionTextureFloat; - var _glExtensionTextureFloatLinear; - var _glExtensionStandardDerivatives; - var _glExtensionTextureFilterAnisotropic; - var _glExtensionCompressedTextureS3TC; - var _glExtensionElementIndexUint; - var _glExtensionFragDepth; + try { + var attributes = { + alpha: _alpha, + depth: _depth, + stencil: _stencil, + antialias: _antialias, + premultipliedAlpha: _premultipliedAlpha, + preserveDrawingBuffer: _preserveDrawingBuffer + }; - initGL(); - - setDefaultGLState(); - - this.context = _gl; - - // GPU capabilities - - var _maxTextures = _gl.getParameter( _gl.MAX_TEXTURE_IMAGE_UNITS ); - var _maxVertexTextures = _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ); - var _maxTextureSize = _gl.getParameter( _gl.MAX_TEXTURE_SIZE ); - var _maxCubemapSize = _gl.getParameter( _gl.MAX_CUBE_MAP_TEXTURE_SIZE ); + _gl = _context || _canvas.getContext( 'webgl', attributes ) || _canvas.getContext( 'experimental-webgl', attributes ); - var _maxAnisotropy = _glExtensionTextureFilterAnisotropic ? _gl.getParameter( _glExtensionTextureFilterAnisotropic.MAX_TEXTURE_MAX_ANISOTROPY_EXT ) : 0; + if ( _gl === null ) { - var _supportsVertexTextures = ( _maxVertexTextures > 0 ); - var _supportsBoneTextures = _supportsVertexTextures && _glExtensionTextureFloat; + if ( _canvas.getContext( 'webgl') !== null ) { - var _compressedTextureFormats = _glExtensionCompressedTextureS3TC ? _gl.getParameter( _gl.COMPRESSED_TEXTURE_FORMATS ) : []; + throw 'Error creating WebGL context with your selected attributes.'; - // + } else { - var _vertexShaderPrecisionHighpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.HIGH_FLOAT ); - var _vertexShaderPrecisionMediumpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.MEDIUM_FLOAT ); - var _vertexShaderPrecisionLowpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.LOW_FLOAT ); + throw 'Error creating WebGL context.'; - var _fragmentShaderPrecisionHighpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.HIGH_FLOAT ); - var _fragmentShaderPrecisionMediumpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.MEDIUM_FLOAT ); - var _fragmentShaderPrecisionLowpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.LOW_FLOAT ); + } - // clamp precision to maximum available + } - var highpAvailable = _vertexShaderPrecisionHighpFloat.precision > 0 && _fragmentShaderPrecisionHighpFloat.precision > 0; - var mediumpAvailable = _vertexShaderPrecisionMediumpFloat.precision > 0 && _fragmentShaderPrecisionMediumpFloat.precision > 0; + } catch ( error ) { - if ( _precision === 'highp' && ! highpAvailable ) { + console.error( error ); - if ( mediumpAvailable ) { + } - _precision = 'mediump'; - console.warn( 'THREE.WebGLRenderer: highp not supported, using mediump.' ); + if ( _gl.getShaderPrecisionFormat === undefined ) { - } else { + _gl.getShaderPrecisionFormat = function () { - _precision = 'lowp'; - console.warn( 'THREE.WebGLRenderer: highp and mediump not supported, using lowp.' ); + return { + 'rangeMin': 1, + 'rangeMax': 1, + 'precision': 1 + }; } } - if ( _precision === 'mediump' && ! mediumpAvailable ) { + var extensions = new THREE.WebGLExtensions( _gl ); - _precision = 'lowp'; - console.warn( 'THREE.WebGLRenderer: mediump not supported, using lowp.' ); + extensions.get( 'OES_texture_float' ); + extensions.get( 'OES_texture_float_linear' ); + extensions.get( 'OES_standard_derivatives' ); - } + if ( _logarithmicDepthBuffer ) { - // API + extensions.get( 'EXT_frag_depth' ); - this.getContext = function () { + } - return _gl; + // - }; + function setDefaultGLState() { - this.supportsVertexTextures = function () { + _gl.clearColor( 0, 0, 0, 1 ); + _gl.clearDepth( 1 ); + _gl.clearStencil( 0 ); - return _supportsVertexTextures; + _gl.enable( _gl.DEPTH_TEST ); + _gl.depthFunc( _gl.LEQUAL ); - }; + _gl.frontFace( _gl.CCW ); + _gl.cullFace( _gl.BACK ); + _gl.enable( _gl.CULL_FACE ); + + _gl.enable( _gl.BLEND ); + _gl.blendEquation( _gl.FUNC_ADD ); + _gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA ); + + _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight ); + + _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); + + } + + setDefaultGLState(); + + this.context = _gl; + + // GPU capabilities + + var _maxTextures = _gl.getParameter( _gl.MAX_TEXTURE_IMAGE_UNITS ); + var _maxVertexTextures = _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ); + var _maxTextureSize = _gl.getParameter( _gl.MAX_TEXTURE_SIZE ); + var _maxCubemapSize = _gl.getParameter( _gl.MAX_CUBE_MAP_TEXTURE_SIZE ); + + var _supportsVertexTextures = _maxVertexTextures > 0; + var _supportsBoneTextures = _supportsVertexTextures && extensions.get( 'OES_texture_float' ); + + // + + var _vertexShaderPrecisionHighpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.HIGH_FLOAT ); + var _vertexShaderPrecisionMediumpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.MEDIUM_FLOAT ); + var _vertexShaderPrecisionLowpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.LOW_FLOAT ); + + var _fragmentShaderPrecisionHighpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.HIGH_FLOAT ); + var _fragmentShaderPrecisionMediumpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.MEDIUM_FLOAT ); + var _fragmentShaderPrecisionLowpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.LOW_FLOAT ); + + var getCompressedTextureFormats = ( function () { + + var array; + + return function () { + + if ( array !== undefined ) { + + return array; + + } + + array = []; + + if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) || extensions.get( 'WEBGL_compressed_texture_s3tc' ) ) { + + var formats = _gl.getParameter( _gl.COMPRESSED_TEXTURE_FORMATS ); + + for ( var i = 0; i < formats.length; i ++ ){ + + array.push( formats[ i ] ); + + } + + } + + return array; + + }; + + } )(); + + // clamp precision to maximum available + + var highpAvailable = _vertexShaderPrecisionHighpFloat.precision > 0 && _fragmentShaderPrecisionHighpFloat.precision > 0; + var mediumpAvailable = _vertexShaderPrecisionMediumpFloat.precision > 0 && _fragmentShaderPrecisionMediumpFloat.precision > 0; + + if ( _precision === 'highp' && ! highpAvailable ) { + + if ( mediumpAvailable ) { + + _precision = 'mediump'; + console.warn( 'THREE.WebGLRenderer: highp not supported, using mediump.' ); + + } else { + + _precision = 'lowp'; + console.warn( 'THREE.WebGLRenderer: highp and mediump not supported, using lowp.' ); + + } + + } + + if ( _precision === 'mediump' && ! mediumpAvailable ) { + + _precision = 'lowp'; + console.warn( 'THREE.WebGLRenderer: mediump not supported, using lowp.' ); + + } + + // Plugins + + var shadowMapPlugin = new THREE.ShadowMapPlugin( this, lights, _webglObjects, _webglObjectsImmediate ); + + var spritePlugin = new THREE.SpritePlugin( this, sprites ); + var lensFlarePlugin = new THREE.LensFlarePlugin( this, lensFlares ); + + // API + + this.getContext = function () { + + return _gl; + + }; + + this.supportsVertexTextures = function () { + + return _supportsVertexTextures; + + }; this.supportsFloatTextures = function () { - return _glExtensionTextureFloat; + return extensions.get( 'OES_texture_float' ); }; this.supportsStandardDerivatives = function () { - return _glExtensionStandardDerivatives; + return extensions.get( 'OES_standard_derivatives' ); }; this.supportsCompressedTextureS3TC = function () { - return _glExtensionCompressedTextureS3TC; + return extensions.get( 'WEBGL_compressed_texture_s3tc' ); + + }; + + this.supportsCompressedTexturePVRTC = function () { + + return extensions.get( 'WEBGL_compressed_texture_pvrtc' ); }; - this.getMaxAnisotropy = function () { + this.supportsBlendMinMax = function () { - return _maxAnisotropy; + return extensions.get( 'EXT_blend_minmax' ); }; + this.getMaxAnisotropy = ( function () { + + var value; + + return function () { + + if ( value !== undefined ) { + + return value; + + } + + var extension = extensions.get( 'EXT_texture_filter_anisotropic' ); + + value = extension !== null ? _gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ) : 0; + + return value; + + } + + } )(); + this.getPrecision = function () { return _precision; @@ -19154,44 +18209,25 @@ THREE.WebGLRenderer = function ( parameters ) { }; - // Plugins - - this.addPostPlugin = function ( plugin ) { - - plugin.init( this ); - this.renderPluginsPost.push( plugin ); - - }; - - this.addPrePlugin = function ( plugin ) { - - plugin.init( this ); - this.renderPluginsPre.push( plugin ); - - }; - - // Rendering + // Reset - this.updateShadowMap = function ( scene, camera ) { + this.resetGLState = function () { _currentProgram = null; + _currentCamera = null; + _oldBlending = - 1; _oldDepthTest = - 1; _oldDepthWrite = - 1; - _currentGeometryGroupHash = - 1; - _currentMaterialId = - 1; - _lightsNeedUpdate = true; _oldDoubleSided = - 1; _oldFlipSided = - 1; + _currentGeometryGroupHash = - 1; + _currentMaterialId = - 1; - initObjects( scene ); - - this.shadowMapPlugin.update( scene, camera ); + _lightsNeedUpdate = true; }; - // Internal functions - // Buffer allocation function createParticleBuffers ( geometry ) { @@ -19260,6 +18296,20 @@ THREE.WebGLRenderer = function ( parameters ) { // Events + var onObjectRemoved = function ( event ) { + + var object = event.target; + + object.traverse( function ( child ) { + + child.removeEventListener( 'remove', onObjectRemoved ); + + removeObject( child ); + + } ); + + }; + var onGeometryDispose = function ( event ) { var geometry = event.target; @@ -19308,31 +18358,50 @@ THREE.WebGLRenderer = function ( parameters ) { // Buffer deallocation var deleteBuffers = function ( geometry ) { + + var buffers = [ + '__webglVertexBuffer', + '__webglNormalBuffer', + '__webglTangentBuffer', + '__webglColorBuffer', + '__webglUVBuffer', + '__webglUV2Buffer', + + '__webglSkinIndicesBuffer', + '__webglSkinWeightsBuffer', + + '__webglFaceBuffer', + '__webglLineBuffer', + + '__webglLineDistanceBuffer' + ]; + + for ( var i = 0, l = buffers.length; i < l; i ++ ) { + + var name = buffers[ i ]; + + if ( geometry[ name ] !== undefined ) { - if ( geometry.__webglVertexBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglVertexBuffer ); - if ( geometry.__webglNormalBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglNormalBuffer ); - if ( geometry.__webglTangentBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglTangentBuffer ); - if ( geometry.__webglColorBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglColorBuffer ); - if ( geometry.__webglUVBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglUVBuffer ); - if ( geometry.__webglUV2Buffer !== undefined ) _gl.deleteBuffer( geometry.__webglUV2Buffer ); + _gl.deleteBuffer( geometry[ name ] ); - if ( geometry.__webglSkinIndicesBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglSkinIndicesBuffer ); - if ( geometry.__webglSkinWeightsBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglSkinWeightsBuffer ); + delete geometry[ name ]; - if ( geometry.__webglFaceBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglFaceBuffer ); - if ( geometry.__webglLineBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglLineBuffer ); + } + + } - if ( geometry.__webglLineDistanceBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglLineDistanceBuffer ); // custom attributes if ( geometry.__webglCustomAttributesList !== undefined ) { - for ( var id in geometry.__webglCustomAttributesList ) { + for ( var name in geometry.__webglCustomAttributesList ) { - _gl.deleteBuffer( geometry.__webglCustomAttributesList[ id ].buffer ); + _gl.deleteBuffer( geometry.__webglCustomAttributesList[ name ].buffer ); } + delete geometry.__webglCustomAttributesList; + } _this.info.memory.geometries --; @@ -19341,17 +18410,19 @@ THREE.WebGLRenderer = function ( parameters ) { var deallocateGeometry = function ( geometry ) { - geometry.__webglInit = undefined; + delete geometry.__webglInit; if ( geometry instanceof THREE.BufferGeometry ) { - var attributes = geometry.attributes; + for ( var name in geometry.attributes ) { + + var attribute = geometry.attributes[ name ]; - for ( var key in attributes ) { + if ( attribute.buffer !== undefined ) { - if ( attributes[ key ].buffer !== undefined ) { + _gl.deleteBuffer( attribute.buffer ); - _gl.deleteBuffer( attributes[ key ].buffer ); + delete attribute.buffer; } @@ -19361,11 +18432,13 @@ THREE.WebGLRenderer = function ( parameters ) { } else { - if ( geometry.geometryGroups !== undefined ) { + var geometryGroupsList = geometryGroups[ geometry.id ]; - for ( var i = 0,l = geometry.geometryGroupsList.length; i 1 ) { - geometryGroup.__normalArray = new Float32Array( nvertices * 3 ); + geometryGroup.__uv2Array = new Float32Array( nvertices * 2 ); } @@ -19619,28 +18709,6 @@ THREE.WebGLRenderer = function ( parameters ) { } - if ( vertexColorType ) { - - geometryGroup.__colorArray = new Float32Array( nvertices * 3 ); - - } - - if ( uvType ) { - - if ( geometry.faceVertexUvs.length > 0 ) { - - geometryGroup.__uvArray = new Float32Array( nvertices * 2 ); - - } - - if ( geometry.faceVertexUvs.length > 1 ) { - - geometryGroup.__uv2Array = new Float32Array( nvertices * 2 ); - - } - - } - if ( object.geometry.skinWeights.length && object.geometry.skinIndices.length ) { geometryGroup.__skinIndexArray = new Float32Array( nvertices * 4 ); @@ -19648,7 +18716,7 @@ THREE.WebGLRenderer = function ( parameters ) { } - var UintArray = _glExtensionElementIndexUint !== null && ntris > 21845 ? Uint32Array : Uint16Array; // 65535 / 3 + var UintArray = extensions.get( 'OES_element_index_uint' ) !== null && ntris > 21845 ? Uint32Array : Uint16Array; // 65535 / 3 geometryGroup.__typeArray = UintArray; geometryGroup.__faceArray = new UintArray( ntris * 3 ); @@ -19694,12 +18762,12 @@ THREE.WebGLRenderer = function ( parameters ) { } - for ( var a in material.attributes ) { + for ( var name in material.attributes ) { // Do a shallow copy of the attribute object so different geometryGroup chunks use different // attribute buffers which are correctly indexed in the setMeshBuffers function - var originalAttribute = material.attributes[ a ]; + var originalAttribute = material.attributes[ name ]; var attribute = {}; @@ -19725,7 +18793,7 @@ THREE.WebGLRenderer = function ( parameters ) { attribute.array = new Float32Array( nvertices * size ); attribute.buffer = _gl.createBuffer(); - attribute.buffer.belongsToAttribute = a; + attribute.buffer.belongsToAttribute = name; originalAttribute.needsUpdate = true; attribute.__original = originalAttribute; @@ -19756,78 +18824,6 @@ THREE.WebGLRenderer = function ( parameters ) { }; - function bufferGuessNormalType ( material ) { - - // only MeshBasicMaterial and MeshDepthMaterial don't need normals - - if ( ( material instanceof THREE.MeshBasicMaterial && ! material.envMap ) || material instanceof THREE.MeshDepthMaterial ) { - - return false; - - } - - if ( materialNeedsSmoothNormals( material ) ) { - - return THREE.SmoothShading; - - } else { - - return THREE.FlatShading; - - } - - }; - - function bufferGuessVertexColorType( material ) { - - if ( material.vertexColors ) { - - return material.vertexColors; - - } - - return false; - - }; - - function bufferGuessUVType( material ) { - - // material must use some texture to require uvs - - if ( material.map || - material.lightMap || - material.bumpMap || - material.normalMap || - material.specularMap || - material.alphaMap || - material instanceof THREE.ShaderMaterial ) { - - return true; - - } - - return false; - - }; - - // - - function initDirectBuffers( geometry ) { - - for ( var name in geometry.attributes ) { - - var bufferType = ( name === 'index' ) ? _gl.ELEMENT_ARRAY_BUFFER : _gl.ARRAY_BUFFER; - - var attribute = geometry.attributes[ name ]; - attribute.buffer = _gl.createBuffer(); - - _gl.bindBuffer( bufferType, attribute.buffer ); - _gl.bufferData( bufferType, attribute.array, _gl.STATIC_DRAW ); - - } - - } - // Buffer setting function setParticleBuffers ( geometry, hint, object ) { @@ -20331,11 +19327,7 @@ THREE.WebGLRenderer = function ( parameters ) { } - var normalType = bufferGuessNormalType( material ), - vertexColorType = bufferGuessVertexColorType( material ), - uvType = bufferGuessUVType( material ), - - needsSmoothNormals = ( normalType === THREE.SmoothShading ); + var needsSmoothNormals = materialNeedsSmoothNormals( material ); var f, fl, fi, face, vertexNormals, faceNormal, normal, @@ -20596,7 +19588,7 @@ THREE.WebGLRenderer = function ( parameters ) { } - if ( dirtyColors && vertexColorType ) { + if ( dirtyColors ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { @@ -20605,7 +19597,7 @@ THREE.WebGLRenderer = function ( parameters ) { vertexColors = face.vertexColors; faceColor = face.color; - if ( vertexColors.length === 3 && vertexColorType === THREE.VertexColors ) { + if ( vertexColors.length === 3 && material.vertexColors === THREE.VertexColors ) { c1 = vertexColors[ 0 ]; c2 = vertexColors[ 1 ]; @@ -20680,7 +19672,7 @@ THREE.WebGLRenderer = function ( parameters ) { } - if ( dirtyNormals && normalType ) { + if ( dirtyNormals ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { @@ -20724,7 +19716,7 @@ THREE.WebGLRenderer = function ( parameters ) { } - if ( dirtyUvs && obj_uvs && uvType ) { + if ( dirtyUvs && obj_uvs ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { @@ -20756,7 +19748,7 @@ THREE.WebGLRenderer = function ( parameters ) { } - if ( dirtyUvs && obj_uvs2 && uvType ) { + if ( dirtyUvs && obj_uvs2 ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { @@ -21126,31 +20118,31 @@ THREE.WebGLRenderer = function ( parameters ) { }; - function setDirectBuffers( geometry, hint ) { + function setDirectBuffers( geometry ) { var attributes = geometry.attributes; + var attributesKeys = geometry.attributesKeys; - var attributeName, attributeItem; + for ( var i = 0, l = attributesKeys.length; i < l; i ++ ) { - for ( attributeName in attributes ) { + var key = attributesKeys[ i ]; + var attribute = attributes[ key ]; - attributeItem = attributes[ attributeName ]; + if ( attribute.buffer === undefined ) { - if ( attributeItem.needsUpdate ) { + attribute.buffer = _gl.createBuffer(); + attribute.needsUpdate = true; - if ( attributeName === 'index' ) { - - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, attributeItem.buffer ); - _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, attributeItem.array, hint ); + } - } else { + if ( attribute.needsUpdate === true ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, attributeItem.array, hint ); + var bufferType = ( key === 'index' ) ? _gl.ELEMENT_ARRAY_BUFFER : _gl.ARRAY_BUFFER; - } + _gl.bindBuffer( bufferType, attribute.buffer ); + _gl.bufferData( bufferType, attribute.array, _gl.STATIC_DRAW ); - attributeItem.needsUpdate = false; + attribute.needsUpdate = false; } @@ -21257,32 +20249,41 @@ THREE.WebGLRenderer = function ( parameters ) { }; - function setupVertexAttributes( material, programAttributes, geometryAttributes, startIndex ) { + function setupVertexAttributes( material, program, geometry, startIndex ) { - for ( var attributeName in programAttributes ) { + var geometryAttributes = geometry.attributes; + + var programAttributes = program.attributes; + var programAttributesKeys = program.attributesKeys; + + for ( var i = 0, l = programAttributesKeys.length; i < l; i ++ ) { - var attributePointer = programAttributes[ attributeName ]; - var attributeItem = geometryAttributes[ attributeName ]; + var key = programAttributesKeys[ i ]; + var programAttribute = programAttributes[ key ]; - if ( attributePointer >= 0 ) { + if ( programAttribute >= 0 ) { - if ( attributeItem ) { + var geometryAttribute = geometryAttributes[ key ]; - var attributeSize = attributeItem.itemSize; + if ( geometryAttribute !== undefined ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer ); - enableAttribute( attributePointer ); - _gl.vertexAttribPointer( attributePointer, attributeSize, _gl.FLOAT, false, 0, startIndex * attributeSize * 4 ); // 4 bytes per Float32 + var size = geometryAttribute.itemSize; - } else if ( material.defaultAttributeValues ) { + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryAttribute.buffer ); - if ( material.defaultAttributeValues[ attributeName ].length === 2 ) { + enableAttribute( programAttribute ); - _gl.vertexAttrib2fv( attributePointer, material.defaultAttributeValues[ attributeName ] ); + _gl.vertexAttribPointer( programAttribute, size, _gl.FLOAT, false, 0, startIndex * size * 4 ); // 4 bytes per Float32 - } else if ( material.defaultAttributeValues[ attributeName ].length === 3 ) { + } else if ( material.defaultAttributeValues !== undefined ) { - _gl.vertexAttrib3fv( attributePointer, material.defaultAttributeValues[ attributeName ] ); + if ( material.defaultAttributeValues[ key ].length === 2 ) { + + _gl.vertexAttrib2fv( programAttribute, material.defaultAttributeValues[ key ] ); + + } else if ( material.defaultAttributeValues[ key ].length === 3 ) { + + _gl.vertexAttrib3fv( programAttribute, material.defaultAttributeValues[ key ] ); } @@ -21300,14 +20301,8 @@ THREE.WebGLRenderer = function ( parameters ) { if ( material.visible === false ) return; - var linewidth, a, attribute; - var attributeItem, attributeName, attributePointer, attributeSize; - var program = setProgram( camera, lights, fog, material, object ); - var programAttributes = program.attributes; - var geometryAttributes = geometry.attributes; - var updateBuffers = false, wireframeBit = material.wireframe ? 1 : 0, geometryHash = ( geometry.id * 0xffffff ) + ( program.id * 2 ) + wireframeBit; @@ -21329,7 +20324,9 @@ THREE.WebGLRenderer = function ( parameters ) { if ( object instanceof THREE.Mesh ) { - var index = geometryAttributes[ 'index' ]; + var mode = material.wireframe === true ? _gl.LINES : _gl.TRIANGLES; + + var index = geometry.attributes.index; if ( index ) { @@ -21337,7 +20334,7 @@ THREE.WebGLRenderer = function ( parameters ) { var type, size; - if ( index.array instanceof Uint32Array ) { + if ( index.array instanceof Uint32Array && extensions.get( 'OES_element_index_uint' ) ) { type = _gl.UNSIGNED_INT; size = 4; @@ -21355,12 +20352,12 @@ THREE.WebGLRenderer = function ( parameters ) { if ( updateBuffers ) { - setupVertexAttributes( material, programAttributes, geometryAttributes, 0 ); + setupVertexAttributes( material, program, geometry, 0 ); _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); } - _gl.drawElements( _gl.TRIANGLES, index.array.length, type, 0 ); + _gl.drawElements( mode, index.array.length, type, 0 ); _this.info.render.calls ++; _this.info.render.vertices += index.array.length; // not really true, here vertices can be shared @@ -21380,14 +20377,14 @@ THREE.WebGLRenderer = function ( parameters ) { if ( updateBuffers ) { - setupVertexAttributes( material, programAttributes, geometryAttributes, startIndex ); + setupVertexAttributes( material, program, geometry, startIndex ); _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); } // render indexed triangles - _gl.drawElements( _gl.TRIANGLES, offsets[ i ].count, type, offsets[ i ].start * size ); + _gl.drawElements( mode, offsets[ i ].count, type, offsets[ i ].start * size ); _this.info.render.calls ++; _this.info.render.vertices += offsets[ i ].count; // not really true, here vertices can be shared @@ -21403,7 +20400,7 @@ THREE.WebGLRenderer = function ( parameters ) { if ( updateBuffers ) { - setupVertexAttributes( material, programAttributes, geometryAttributes, 0 ); + setupVertexAttributes( material, program, geometry, 0 ); } @@ -21411,7 +20408,7 @@ THREE.WebGLRenderer = function ( parameters ) { // render non-indexed triangles - _gl.drawArrays( _gl.TRIANGLES, 0, position.array.length / 3 ); + _gl.drawArrays( mode, 0, position.array.length / 3 ); _this.info.render.calls ++; _this.info.render.vertices += position.array.length / 3; @@ -21425,11 +20422,11 @@ THREE.WebGLRenderer = function ( parameters ) { if ( updateBuffers ) { - setupVertexAttributes( material, programAttributes, geometryAttributes, 0 ); + setupVertexAttributes( material, program, geometry, 0 ); } - var position = geometryAttributes[ 'position' ]; + var position = geometry.attributes.position; // render particles @@ -21440,11 +20437,11 @@ THREE.WebGLRenderer = function ( parameters ) { } else if ( object instanceof THREE.Line ) { - var mode = ( object.type === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES; + var mode = ( object.mode === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES; setLineWidth( material.linewidth ); - var index = geometryAttributes[ 'index' ]; + var index = geometry.attributes.index; if ( index ) { @@ -21470,7 +20467,7 @@ THREE.WebGLRenderer = function ( parameters ) { if ( updateBuffers ) { - setupVertexAttributes( material, programAttributes, geometryAttributes, 0 ); + setupVertexAttributes( material, program, geometry, 0 ); _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); } @@ -21494,7 +20491,7 @@ THREE.WebGLRenderer = function ( parameters ) { if ( updateBuffers ) { - setupVertexAttributes( material, programAttributes, geometryAttributes, startIndex ); + setupVertexAttributes( material, program, geometry, startIndex ); _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); } @@ -21516,11 +20513,11 @@ THREE.WebGLRenderer = function ( parameters ) { if ( updateBuffers ) { - setupVertexAttributes( material, programAttributes, geometryAttributes, 0 ); + setupVertexAttributes( material, program, geometry, 0 ); } - var position = geometryAttributes[ 'position' ]; + var position = geometry.attributes.position; _gl.drawArrays( mode, 0, position.array.length / 3 ); @@ -21537,8 +20534,6 @@ THREE.WebGLRenderer = function ( parameters ) { if ( material.visible === false ) return; - var linewidth, a, attribute, i, il; - var program = setProgram( camera, lights, fog, material, object ); var attributes = program.attributes; @@ -21591,9 +20586,9 @@ THREE.WebGLRenderer = function ( parameters ) { if ( geometryGroup.__webglCustomAttributesList ) { - for ( i = 0, il = geometryGroup.__webglCustomAttributesList.length; i < il; i ++ ) { + for ( var i = 0, il = geometryGroup.__webglCustomAttributesList.length; i < il; i ++ ) { - attribute = geometryGroup.__webglCustomAttributesList[ i ]; + var attribute = geometryGroup.__webglCustomAttributesList[ i ]; if ( attributes[ attribute.buffer.belongsToAttribute ] >= 0 ) { @@ -21618,7 +20613,7 @@ THREE.WebGLRenderer = function ( parameters ) { enableAttribute( attributes.color ); _gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 ); - } else if ( material.defaultAttributeValues ) { + } else if ( material.defaultAttributeValues !== undefined ) { _gl.vertexAttrib3fv( attributes.color, material.defaultAttributeValues.color ); @@ -21657,7 +20652,7 @@ THREE.WebGLRenderer = function ( parameters ) { enableAttribute( attributes.uv ); _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); - } else if ( material.defaultAttributeValues ) { + } else if ( material.defaultAttributeValues !== undefined ) { _gl.vertexAttrib2fv( attributes.uv, material.defaultAttributeValues.uv ); @@ -21674,7 +20669,7 @@ THREE.WebGLRenderer = function ( parameters ) { enableAttribute( attributes.uv2 ); _gl.vertexAttribPointer( attributes.uv2, 2, _gl.FLOAT, false, 0, 0 ); - } else if ( material.defaultAttributeValues ) { + } else if ( material.defaultAttributeValues !== undefined ) { _gl.vertexAttrib2fv( attributes.uv2, material.defaultAttributeValues.uv2 ); @@ -21741,7 +20736,7 @@ THREE.WebGLRenderer = function ( parameters ) { } else if ( object instanceof THREE.Line ) { - var mode = ( object.type === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES; + var mode = ( object.mode === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES; setLineWidth( material.linewidth ); @@ -21943,13 +20938,17 @@ THREE.WebGLRenderer = function ( parameters ) { } - }; + } // Sorting function painterSortStable ( a, b ) { - if ( a.z !== b.z ) { + if ( a.material.id !== b.material.id ) { + + return b.material.id - a.material.id; + + } else if ( a.z !== b.z ) { return b.z - a.z; @@ -21959,7 +20958,7 @@ THREE.WebGLRenderer = function ( parameters ) { } - }; + } function reversePainterSortStable ( a, b ) { @@ -21973,14 +20972,13 @@ THREE.WebGLRenderer = function ( parameters ) { } - }; + } function numericalSort ( a, b ) { return b[ 0 ] - a[ 0 ]; - }; - + } // Rendering @@ -21993,16 +20991,11 @@ THREE.WebGLRenderer = function ( parameters ) { } - var i, il, - - webglObject, object, - renderList, - - lights = scene.__lights, - fog = scene.fog; + var fog = scene.fog; // reset caching for this frame + _currentGeometryGroupHash = - 1; _currentMaterialId = - 1; _currentCamera = null; _lightsNeedUpdate = true; @@ -22016,7 +21009,8 @@ THREE.WebGLRenderer = function ( parameters ) { if ( camera.parent === undefined ) camera.updateMatrixWorld(); // update Skeleton objects - function updateSkeletons( object ) { + + scene.traverse( function ( object ) { if ( object instanceof THREE.SkinnedMesh ) { @@ -22024,27 +21018,21 @@ THREE.WebGLRenderer = function ( parameters ) { } - for ( var i = 0, l = object.children.length; i < l; i ++ ) { - - updateSkeletons( object.children[ i ] ); - - } - - } - - updateSkeletons( scene ); + } ); camera.matrixWorldInverse.getInverse( camera.matrixWorld ); _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); _frustum.setFromMatrix( _projScreenMatrix ); - initObjects( scene ); - + lights.length = 0; opaqueObjects.length = 0; transparentObjects.length = 0; - - projectObject( scene, scene, camera ); + + sprites.length = 0; + lensFlares.length = 0; + + projectObject( scene, scene ); if ( _this.sortObjects === true ) { @@ -22054,8 +21042,8 @@ THREE.WebGLRenderer = function ( parameters ) { } // custom render plugins (pre pass) - - renderPlugins( this.renderPluginsPre, scene, camera ); + + shadowMapPlugin.render( scene, camera ); // @@ -22072,19 +21060,12 @@ THREE.WebGLRenderer = function ( parameters ) { } - // set matrices for regular objects (frustum culled) - - - - // set matrices for immediate objects - renderList = scene.__webglObjectsImmediate; - - for ( i = 0, il = renderList.length; i < il; i ++ ) { + for ( var i = 0, il = _webglObjectsImmediate.length; i < il; i ++ ) { - webglObject = renderList[ i ]; - object = webglObject.object; + var webglObject = _webglObjectsImmediate[ i ]; + var object = webglObject.object; if ( object.visible ) { @@ -22107,7 +21088,7 @@ THREE.WebGLRenderer = function ( parameters ) { renderObjects( opaqueObjects, camera, lights, fog, true, material ); renderObjects( transparentObjects, camera, lights, fog, true, material ); - renderObjectsImmediate( scene.__webglObjectsImmediate, '', camera, lights, fog, false, material ); + renderObjectsImmediate( _webglObjectsImmediate, '', camera, lights, fog, false, material ); } else { @@ -22118,19 +21099,19 @@ THREE.WebGLRenderer = function ( parameters ) { this.setBlending( THREE.NoBlending ); renderObjects( opaqueObjects, camera, lights, fog, false, material ); - renderObjectsImmediate( scene.__webglObjectsImmediate, 'opaque', camera, lights, fog, false, material ); + renderObjectsImmediate( _webglObjectsImmediate, 'opaque', camera, lights, fog, false, material ); // transparent pass (back-to-front order) renderObjects( transparentObjects, camera, lights, fog, true, material ); - renderObjectsImmediate( scene.__webglObjectsImmediate, 'transparent', camera, lights, fog, true, material ); + renderObjectsImmediate( _webglObjectsImmediate, 'transparent', camera, lights, fog, true, material ); } // custom render plugins (post pass) - renderPlugins( this.renderPluginsPost, scene, camera ); - + spritePlugin.render( scene, camera ); + lensFlarePlugin.render( scene, camera, _currentWidth, _currentHeight ); // Generate mipmap if we're using any kind of mipmap filtering @@ -22148,107 +21129,91 @@ THREE.WebGLRenderer = function ( parameters ) { // _gl.finish(); }; - - function projectObject(scene, object,camera){ - + + function projectObject( scene, object ) { + if ( object.visible === false ) return; - - var webglObjects = scene.__webglObjects[ object.id ]; - - if ( webglObjects && ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) ) { - - updateObject( scene, object ); - - for ( var i = 0, l = webglObjects.length; i < l; i ++ ) { - - var webglObject = webglObjects[i]; - - unrollBufferMaterial( webglObject ); - webglObject.render = true; + if ( object instanceof THREE.Scene || object instanceof THREE.Group ) { - if ( _this.sortObjects === true ) { + // skip - if ( object.renderDepth !== null ) { + } else { - webglObject.z = object.renderDepth; + initObject( object, scene ); - } else { + if ( object instanceof THREE.Light ) { - _vector3.setFromMatrixPosition( object.matrixWorld ); - _vector3.applyProjection( _projScreenMatrix ); + lights.push( object ); - webglObject.z = _vector3.z; + } else if ( object instanceof THREE.Sprite ) { - } + sprites.push( object ); - } + } else if ( object instanceof THREE.LensFlare ) { - } + lensFlares.push( object ); - } - - for ( var i = 0, l = object.children.length; i < l; i ++ ) { + } else { - projectObject( scene, object.children[ i ], camera ); + var webglObjects = _webglObjects[ object.id ]; - } - - } + if ( webglObjects && ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) ) { + + updateObject( object, scene ); + + for ( var i = 0, l = webglObjects.length; i < l; i ++ ) { + + var webglObject = webglObjects[i]; - function renderPlugins( plugins, scene, camera ) { + unrollBufferMaterial( webglObject ); - if ( plugins.length === 0 ) return; + webglObject.render = true; - for ( var i = 0, il = plugins.length; i < il; i ++ ) { + if ( _this.sortObjects === true ) { - // reset state for plugin (to start from clean slate) + if ( object.renderDepth !== null ) { - _currentProgram = null; - _currentCamera = null; + webglObject.z = object.renderDepth; - _oldBlending = - 1; - _oldDepthTest = - 1; - _oldDepthWrite = - 1; - _oldDoubleSided = - 1; - _oldFlipSided = - 1; - _currentGeometryGroupHash = - 1; - _currentMaterialId = - 1; + } else { + + _vector3.setFromMatrixPosition( object.matrixWorld ); + _vector3.applyProjection( _projScreenMatrix ); - _lightsNeedUpdate = true; + webglObject.z = _vector3.z; - plugins[ i ].render( scene, camera, _currentWidth, _currentHeight ); + } - // reset state after plugin (anything could have changed) + } - _currentProgram = null; - _currentCamera = null; + } - _oldBlending = - 1; - _oldDepthTest = - 1; - _oldDepthWrite = - 1; - _oldDoubleSided = - 1; - _oldFlipSided = - 1; - _currentGeometryGroupHash = - 1; - _currentMaterialId = - 1; + } - _lightsNeedUpdate = true; + } } - }; + for ( var i = 0, l = object.children.length; i < l; i ++ ) { + + projectObject( scene, object.children[ i ] ); + + } + + } function renderObjects( renderList, camera, lights, fog, useBlending, overrideMaterial ) { - var webglObject, object, buffer, material; + var material; for ( var i = renderList.length - 1; i !== - 1; i -- ) { - webglObject = renderList[ i ]; + var webglObject = renderList[ i ]; + + var object = webglObject.object; + var buffer = webglObject.buffer; - object = webglObject.object; - buffer = webglObject.buffer; - setupMatrices( object, camera ); if ( overrideMaterial ) { @@ -22283,16 +21248,16 @@ THREE.WebGLRenderer = function ( parameters ) { } - }; + } function renderObjectsImmediate ( renderList, materialType, camera, lights, fog, useBlending, overrideMaterial ) { - var webglObject, object, material, program; + var material; for ( var i = 0, il = renderList.length; i < il; i ++ ) { - webglObject = renderList[ i ]; - object = webglObject.object; + var webglObject = renderList[ i ]; + var object = webglObject.object; if ( object.visible ) { @@ -22320,7 +21285,7 @@ THREE.WebGLRenderer = function ( parameters ) { } - }; + } this.renderImmediateObject = function ( camera, lights, fog, material, object ) { @@ -22359,7 +21324,7 @@ THREE.WebGLRenderer = function ( parameters ) { } - }; + } function unrollBufferMaterial ( globject ) { @@ -22375,84 +21340,50 @@ THREE.WebGLRenderer = function ( parameters ) { material = material.materials[ materialIndex ]; + globject.material = material; + if ( material.transparent ) { - globject.material = material; transparentObjects.push( globject ); } else { - globject.material = material; opaqueObjects.push( globject ); } - } else { - - if ( material ) { + } else if ( material ) { - if ( material.transparent ) { + globject.material = material; - globject.material = material; - transparentObjects.push( globject ); + if ( material.transparent ) { - } else { + transparentObjects.push( globject ); - globject.material = material; - opaqueObjects.push( globject ); + } else { - } + opaqueObjects.push( globject ); } } - }; - - // Objects refresh - - var initObjects = function ( scene ) { - - if ( ! scene.__webglObjects ) { - - scene.__webglObjects = {}; - scene.__webglObjectsImmediate = []; - - } - - while ( scene.__objectsAdded.length ) { - - addObject( scene.__objectsAdded[ 0 ], scene ); - scene.__objectsAdded.splice( 0, 1 ); - - } - - while ( scene.__objectsRemoved.length ) { - - removeObject( scene.__objectsRemoved[ 0 ], scene ); - scene.__objectsRemoved.splice( 0, 1 ); - - } - - }; - - // Objects adding - - function addObject( object, scene ) { + } - var g, geometry, geometryGroup; + function initObject( object, scene ) { if ( object.__webglInit === undefined ) { object.__webglInit = true; - object._modelViewMatrix = new THREE.Matrix4(); object._normalMatrix = new THREE.Matrix3(); + object.addEventListener( 'removed', onObjectRemoved ); + } - - geometry = object.geometry; - + + var geometry = object.geometry; + if ( geometry === undefined ) { // ImmediateRenderObject @@ -22464,21 +21395,15 @@ THREE.WebGLRenderer = function ( parameters ) { if ( geometry instanceof THREE.BufferGeometry ) { - initDirectBuffers( geometry ); + // } else if ( object instanceof THREE.Mesh ) { - - if ( object.__webglActive !== undefined ) { - - removeObject( object, scene ); - } - - initGeometryGroups(scene, object, geometry); + initGeometryGroups( scene, object, geometry ); } else if ( object instanceof THREE.Line ) { - if ( ! geometry.__webglVertexBuffer ) { + if ( geometry.__webglVertexBuffer === undefined ) { createLineBuffers( geometry ); initLineBuffers( geometry, object ); @@ -22491,7 +21416,7 @@ THREE.WebGLRenderer = function ( parameters ) { } else if ( object instanceof THREE.PointCloud ) { - if ( ! geometry.__webglVertexBuffer ) { + if ( geometry.__webglVertexBuffer === undefined ) { createParticleBuffers( geometry ); initParticleBuffers( geometry, object ); @@ -22507,64 +21432,144 @@ THREE.WebGLRenderer = function ( parameters ) { if ( object.__webglActive === undefined) { - if ( object instanceof THREE.Mesh ) { + object.__webglActive = true; - geometry = object.geometry; + if ( object instanceof THREE.Mesh ) { if ( geometry instanceof THREE.BufferGeometry ) { - addBuffer( scene.__webglObjects, geometry, object ); + addBuffer( _webglObjects, geometry, object ); } else if ( geometry instanceof THREE.Geometry ) { - for ( var i = 0,l = geometry.geometryGroupsList.length; i maxVerticesInGroup ) { + + hash_map[ materialIndex ].counter += 1; + groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter; + + if ( ! ( groupHash in groups ) ) { + + group = { + id: geometryGroupCounter ++, + faces3: [], + materialIndex: materialIndex, + vertices: 0, + numMorphTargets: numMorphTargets, + numMorphNormals: numMorphNormals + }; + + groups[ groupHash ] = group; + groupsList.push( group ); + + } + + } + + groups[ groupHash ].faces3.push( f ); + groups[ groupHash ].vertices += 3; } - }; - + return groupsList; + + } + function initGeometryGroups( scene, object, geometry ) { - - var g, geometryGroup, material,addBuffers = false; - material = object.material; - if ( geometry.geometryGroups === undefined || geometry.groupsNeedUpdate ) { - - delete scene.__webglObjects[object.id]; - geometry.makeGroups( material instanceof THREE.MeshFaceMaterial, _glExtensionElementIndexUint ? 4294967296 : 65535 ); + var material = object.material, addBuffers = false; + + if ( geometryGroups[ geometry.id ] === undefined || geometry.groupsNeedUpdate === true ) { + + delete _webglObjects[ object.id ]; + + geometryGroups[ geometry.id ] = makeGroups( geometry, material instanceof THREE.MeshFaceMaterial ); + geometry.groupsNeedUpdate = false; } + var geometryGroupsList = geometryGroups[ geometry.id ]; + // create separate VBOs per geometry chunk - for ( var i = 0, il = geometry.geometryGroupsList.length; i < il; i ++ ) { + for ( var i = 0, il = geometryGroupsList.length; i < il; i ++ ) { - geometryGroup = geometry.geometryGroupsList[ i ]; + var geometryGroup = geometryGroupsList[ i ]; // initialise VBO on the first access - if ( ! geometryGroup.__webglVertexBuffer ) { + if ( geometryGroup.__webglVertexBuffer === undefined ) { createMeshBuffers( geometryGroup ); initMeshBuffers( geometryGroup, object ); @@ -22576,17 +21581,19 @@ THREE.WebGLRenderer = function ( parameters ) { geometry.normalsNeedUpdate = true; geometry.tangentsNeedUpdate = true; geometry.colorsNeedUpdate = true; - + addBuffers = true; - + } else { - + addBuffers = false; - + } - + if ( addBuffers || object.__webglActive === undefined ) { - addBuffer( scene.__webglObjects, geometryGroup, object ); + + addBuffer( _webglObjects, geometryGroup, object ); + } } @@ -22594,7 +21601,7 @@ THREE.WebGLRenderer = function ( parameters ) { object.__webglActive = true; } - + function addBuffer( objlist, buffer, object ) { var id = object.id; @@ -22627,39 +21634,33 @@ THREE.WebGLRenderer = function ( parameters ) { // Objects updates - function updateObject(scene, object ) { + function updateObject( object, scene ) { - var geometry = object.geometry, - geometryGroup, customAttributesDirty, material; + var geometry = object.geometry, customAttributesDirty, material; if ( geometry instanceof THREE.BufferGeometry ) { - setDirectBuffers( geometry, _gl.DYNAMIC_DRAW ); + setDirectBuffers( geometry ); } else if ( object instanceof THREE.Mesh ) { // check all geometry groups - if ( geometry.buffersNeedUpdate || geometry.groupsNeedUpdate ) { - - if ( geometry instanceof THREE.BufferGeometry ) { - initDirectBuffers( geometry ); + if ( geometry.groupsNeedUpdate === true ) { + + initGeometryGroups( scene, object, geometry ); - } else if ( object instanceof THREE.Mesh ) { - - initGeometryGroups(scene, object,geometry); - - } - } - for ( var i = 0, il = geometry.geometryGroupsList.length; i < il; i ++ ) { + var geometryGroupsList = geometryGroups[ geometry.id ]; - geometryGroup = geometry.geometryGroupsList[ i ]; + for ( var i = 0, il = geometryGroupsList.length; i < il; i ++ ) { + + var geometryGroup = geometryGroupsList[ i ]; material = getBufferMaterial( object, geometryGroup ); - if ( geometry.buffersNeedUpdate || geometry.groupsNeedUpdate) { + if ( geometry.groupsNeedUpdate === true ) { initMeshBuffers( geometryGroup, object ); @@ -22685,8 +21686,6 @@ THREE.WebGLRenderer = function ( parameters ) { geometry.colorsNeedUpdate = false; geometry.tangentsNeedUpdate = false; - geometry.buffersNeedUpdate = false; - material.attributes && clearCustomAttributes( material ); } else if ( object instanceof THREE.Line ) { @@ -22727,59 +21726,55 @@ THREE.WebGLRenderer = function ( parameters ) { } - }; + } // Objects updates - custom attributes check function areCustomAttributesDirty( material ) { - for ( var a in material.attributes ) { + for ( var name in material.attributes ) { - if ( material.attributes[ a ].needsUpdate ) return true; + if ( material.attributes[ name ].needsUpdate ) return true; } return false; - }; + } function clearCustomAttributes( material ) { - for ( var a in material.attributes ) { + for ( var name in material.attributes ) { - material.attributes[ a ].needsUpdate = false; + material.attributes[ name ].needsUpdate = false; } - }; + } // Objects removal - function removeObject( object, scene ) { + function removeObject( object ) { if ( object instanceof THREE.Mesh || object instanceof THREE.PointCloud || object instanceof THREE.Line ) { - removeInstancesWebglObjects( scene.__webglObjects, object ); + delete _webglObjects[ object.id ]; } else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) { - removeInstances( scene.__webglObjectsImmediate, object ); + removeInstances( _webglObjectsImmediate, object ); } - delete object.__webglActive; - - }; - - - - function removeInstancesWebglObjects( objlist, object ) { + delete object.__webglInit; + delete object._modelViewMatrix; + delete object._normalMatrix; - delete objlist[ object.id ]; + delete object.__webglActive; - }; + } function removeInstances( objlist, object ) { @@ -22793,15 +21788,15 @@ THREE.WebGLRenderer = function ( parameters ) { } - }; + } // Materials - this.initMaterial = function ( material, lights, fog, object ) { + function initMaterial( material, lights, fog, object ) { material.addEventListener( 'dispose', onMaterialDispose ); - var u, a, identifiers, i, parameters, maxLightCount, maxBones, maxShadows, shaderID; + var shaderID; if ( material instanceof THREE.MeshDepthMaterial ) { @@ -22860,13 +21855,11 @@ THREE.WebGLRenderer = function ( parameters ) { // heuristics to create shader parameters according to lights in the scene // (not to blow over maxLights budget) - maxLightCount = allocateLights( lights ); - - maxShadows = allocateShadows( lights ); + var maxLightCount = allocateLights( lights ); + var maxShadows = allocateShadows( lights ); + var maxBones = allocateBones( object ); - maxBones = allocateBones( object ); - - parameters = { + var parameters = { precision: _precision, supportsVertexTextures: _supportsVertexTextures, @@ -22894,8 +21887,8 @@ THREE.WebGLRenderer = function ( parameters ) { morphTargets: material.morphTargets, morphNormals: material.morphNormals, - maxMorphTargets: this.maxMorphTargets, - maxMorphNormals: this.maxMorphNormals, + maxMorphTargets: _this.maxMorphTargets, + maxMorphNormals: _this.maxMorphNormals, maxDirLights: maxLightCount.directional, maxPointLights: maxLightCount.point, @@ -22903,10 +21896,10 @@ THREE.WebGLRenderer = function ( parameters ) { maxHemiLights: maxLightCount.hemi, maxShadows: maxShadows, - shadowMapEnabled: this.shadowMapEnabled && object.receiveShadow && maxShadows > 0, - shadowMapType: this.shadowMapType, - shadowMapDebug: this.shadowMapDebug, - shadowMapCascade: this.shadowMapCascade, + shadowMapEnabled: _this.shadowMapEnabled && object.receiveShadow && maxShadows > 0, + shadowMapType: _this.shadowMapType, + shadowMapDebug: _this.shadowMapDebug, + shadowMapCascade: _this.shadowMapCascade, alphaTest: material.alphaTest, metal: material.metal, @@ -22931,17 +21924,21 @@ THREE.WebGLRenderer = function ( parameters ) { } - for ( var d in material.defines ) { + if ( material.defines !== undefined ) { + + for ( var name in material.defines ) { - chunks.push( d ); - chunks.push( material.defines[ d ] ); + chunks.push( name ); + chunks.push( material.defines[ name ] ); + + } } - for ( var p in parameters ) { + for ( var name in parameters ) { - chunks.push( p ); - chunks.push( parameters[ p ] ); + chunks.push( name ); + chunks.push( parameters[ name ] ); } @@ -22968,7 +21965,7 @@ THREE.WebGLRenderer = function ( parameters ) { if ( program === undefined ) { - program = new THREE.WebGLProgram( this, code, material, parameters ); + program = new THREE.WebGLProgram( _this, code, material, parameters ); _programs.push( program ); _this.info.memory.programs = _programs.length; @@ -22977,7 +21974,7 @@ THREE.WebGLRenderer = function ( parameters ) { material.program = program; - var attributes = material.program.attributes; + var attributes = program.attributes; if ( material.morphTargets ) { @@ -22985,7 +21982,7 @@ THREE.WebGLRenderer = function ( parameters ) { var id, base = 'morphTarget'; - for ( i = 0; i < this.maxMorphTargets; i ++ ) { + for ( var i = 0; i < _this.maxMorphTargets; i ++ ) { id = base + i; @@ -23005,7 +22002,7 @@ THREE.WebGLRenderer = function ( parameters ) { var id, base = 'morphNormal'; - for ( i = 0; i < this.maxMorphNormals; i ++ ) { + for ( i = 0; i < _this.maxMorphNormals; i ++ ) { id = base + i; @@ -23021,7 +22018,7 @@ THREE.WebGLRenderer = function ( parameters ) { material.uniformsList = []; - for ( u in material.__webglShader.uniforms ) { + for ( var u in material.__webglShader.uniforms ) { var location = material.program.uniforms[ u ]; @@ -23031,7 +22028,7 @@ THREE.WebGLRenderer = function ( parameters ) { } - }; + } function setProgram( camera, lights, fog, material, object ) { @@ -23041,7 +22038,7 @@ THREE.WebGLRenderer = function ( parameters ) { if ( material.program ) deallocateMaterial( material ); - _this.initMaterial( material, lights, fog, object ); + initMaterial( material, lights, fog, object ); material.needsUpdate = false; } @@ -23276,7 +22273,7 @@ THREE.WebGLRenderer = function ( parameters ) { return program; - }; + } // Uniforms (refresh uniforms objects) @@ -23371,14 +22368,14 @@ THREE.WebGLRenderer = function ( parameters ) { uniforms.combine.value = material.combine; uniforms.useRefract.value = material.envMap && material.envMap.mapping instanceof THREE.CubeRefractionMapping; - }; + } function refreshUniformsLine ( uniforms, material ) { uniforms.diffuse.value = material.color; uniforms.opacity.value = material.opacity; - }; + } function refreshUniformsDash ( uniforms, material ) { @@ -23386,7 +22383,7 @@ THREE.WebGLRenderer = function ( parameters ) { uniforms.totalSize.value = material.dashSize + material.gapSize; uniforms.scale.value = material.scale; - }; + } function refreshUniformsParticle ( uniforms, material ) { @@ -23397,7 +22394,7 @@ THREE.WebGLRenderer = function ( parameters ) { uniforms.map.value = material.map; - }; + } function refreshUniformsFog ( uniforms, fog ) { @@ -23414,7 +22411,7 @@ THREE.WebGLRenderer = function ( parameters ) { } - }; + } function refreshUniformsPhong ( uniforms, material ) { @@ -23440,7 +22437,7 @@ THREE.WebGLRenderer = function ( parameters ) { } - }; + } function refreshUniformsLambert ( uniforms, material ) { @@ -23462,7 +22459,7 @@ THREE.WebGLRenderer = function ( parameters ) { } - }; + } function refreshUniformsLights ( uniforms, lights ) { @@ -23486,7 +22483,7 @@ THREE.WebGLRenderer = function ( parameters ) { uniforms.hemisphereLightGroundColor.value = lights.hemi.groundColors; uniforms.hemisphereLightDirection.value = lights.hemi.positions; - }; + } // If uniforms are marked as clean, they don't need to be loaded to the GPU. @@ -23512,7 +22509,7 @@ THREE.WebGLRenderer = function ( parameters ) { uniforms.hemisphereLightGroundColor.needsUpdate = boolean; uniforms.hemisphereLightDirection.needsUpdate = boolean; - }; + } function refreshUniformsShadow ( uniforms, lights ) { @@ -23544,7 +22541,7 @@ THREE.WebGLRenderer = function ( parameters ) { } - }; + } // Uniforms (load to GPU) @@ -23558,7 +22555,7 @@ THREE.WebGLRenderer = function ( parameters ) { } - }; + } function getTextureUnit() { @@ -23574,7 +22571,7 @@ THREE.WebGLRenderer = function ( parameters ) { return textureUnit; - }; + } function loadUniformsGeneric ( uniforms ) { @@ -23647,7 +22644,7 @@ THREE.WebGLRenderer = function ( parameters ) { // - case 'i': + case 'i': // single integer _gl.uniform1i( location, value ); @@ -23675,7 +22672,7 @@ THREE.WebGLRenderer = function ( parameters ) { break; - case 'v4': + case 'v4': // single THREE.Vector4 _gl.uniform4f( location, value.x, value.y, value.z, value.w ); @@ -23910,14 +22907,14 @@ THREE.WebGLRenderer = function ( parameters ) { } - }; + } function setupMatrices ( object, camera ) { object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); object._normalMatrix.getNormalMatrix( object._modelViewMatrix ); - }; + } // @@ -23927,7 +22924,7 @@ THREE.WebGLRenderer = function ( parameters ) { array[ offset + 1 ] = color.g * color.g * intensitySq; array[ offset + 2 ] = color.b * color.b * intensitySq; - }; + } function setColorLinear( array, offset, color, intensity ) { @@ -23935,7 +22932,7 @@ THREE.WebGLRenderer = function ( parameters ) { array[ offset + 1 ] = color.g * intensity; array[ offset + 2 ] = color.b * intensity; - }; + } function setupLights ( lights ) { @@ -24084,15 +23081,14 @@ THREE.WebGLRenderer = function ( parameters ) { } - _vector3.setFromMatrixPosition( light.matrixWorld ); + _direction.setFromMatrixPosition( light.matrixWorld ); - spotPositions[ spotOffset ] = _vector3.x; - spotPositions[ spotOffset + 1 ] = _vector3.y; - spotPositions[ spotOffset + 2 ] = _vector3.z; + spotPositions[ spotOffset ] = _direction.x; + spotPositions[ spotOffset + 1 ] = _direction.y; + spotPositions[ spotOffset + 2 ] = _direction.z; spotDistances[ spotLength ] = distance; - _direction.copy( _vector3 ); _vector3.setFromMatrixPosition( light.target.matrixWorld ); _direction.sub( _vector3 ); _direction.normalize(); @@ -24162,7 +23158,7 @@ THREE.WebGLRenderer = function ( parameters ) { zlights.ambient[ 1 ] = g; zlights.ambient[ 2 ] = b; - }; + } // GL state setting @@ -24284,7 +23280,7 @@ THREE.WebGLRenderer = function ( parameters ) { } - }; + } function setPolygonOffset ( polygonoffset, factor, units ) { @@ -24313,7 +23309,7 @@ THREE.WebGLRenderer = function ( parameters ) { } - }; + } this.setBlending = function ( blending, blendEquation, blendSrc, blendDst ) { @@ -24392,6 +23388,8 @@ THREE.WebGLRenderer = function ( parameters ) { function setTextureParameters ( textureType, texture, isImagePowerOfTwo ) { + var extension; + if ( isImagePowerOfTwo ) { _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, paramThreeToGL( texture.wrapS ) ); @@ -24410,121 +23408,144 @@ THREE.WebGLRenderer = function ( parameters ) { } - if ( _glExtensionTextureFilterAnisotropic && texture.type !== THREE.FloatType ) { + extension = extensions.get( 'EXT_texture_filter_anisotropic' ); + + if ( extension && texture.type !== THREE.FloatType ) { if ( texture.anisotropy > 1 || texture.__oldAnisotropy ) { - _gl.texParameterf( textureType, _glExtensionTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, _maxAnisotropy ) ); + _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, _this.getMaxAnisotropy() ) ); texture.__oldAnisotropy = texture.anisotropy; } } - }; + } - this.setTexture = function ( texture, slot ) { + this.uploadTexture = function ( texture ) { - if ( texture.needsUpdate ) { + if ( texture.__webglInit === undefined ) { - if ( ! texture.__webglInit ) { + texture.__webglInit = true; - texture.__webglInit = true; + texture.addEventListener( 'dispose', onTextureDispose ); - texture.addEventListener( 'dispose', onTextureDispose ); + texture.__webglTexture = _gl.createTexture(); - texture.__webglTexture = _gl.createTexture(); + _this.info.memory.textures ++; - _this.info.memory.textures ++; + } - } + _gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture ); - _gl.activeTexture( _gl.TEXTURE0 + slot ); - _gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture ); + _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); + _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha ); + _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment ); - _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); - _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha ); - _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment ); + texture.image = clampToMaxSize( texture.image, _maxTextureSize ); - var image = texture.image, - isImagePowerOfTwo = THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height ), - glFormat = paramThreeToGL( texture.format ), - glType = paramThreeToGL( texture.type ); + var image = texture.image, + isImagePowerOfTwo = THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height ), + glFormat = paramThreeToGL( texture.format ), + glType = paramThreeToGL( texture.type ); - setTextureParameters( _gl.TEXTURE_2D, texture, isImagePowerOfTwo ); + setTextureParameters( _gl.TEXTURE_2D, texture, isImagePowerOfTwo ); - var mipmap, mipmaps = texture.mipmaps; + var mipmap, mipmaps = texture.mipmaps; - if ( texture instanceof THREE.DataTexture ) { + if ( texture instanceof THREE.DataTexture ) { - // use manually created mipmaps if available - // if there are no manual mipmaps - // set 0 level mipmap and then use GL to generate other mipmap levels + // use manually created mipmaps if available + // if there are no manual mipmaps + // set 0 level mipmap and then use GL to generate other mipmap levels - if ( mipmaps.length > 0 && isImagePowerOfTwo ) { + if ( mipmaps.length > 0 && isImagePowerOfTwo ) { - for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { + for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { - mipmap = mipmaps[ i ]; - _gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + mipmap = mipmaps[ i ]; + _gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - } + } - texture.generateMipmaps = false; + texture.generateMipmaps = false; - } else { + } else { - _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data ); + _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data ); - } + } - } else if ( texture instanceof THREE.CompressedTexture ) { + } else if ( texture instanceof THREE.CompressedTexture ) { - for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { + for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { + + mipmap = mipmaps[ i ]; + + if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) { + + if ( getCompressedTextureFormats().indexOf( glFormat ) > -1 ) { - mipmap = mipmaps[ i ]; - if ( texture.format !== THREE.RGBAFormat ) { _gl.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + } else { - _gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + + console.warn( "Attempt to load unsupported compressed texture format" ); + } + } else { + + _gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + } - } else { // regular Texture (image, video, canvas) + } - // use manually created mipmaps if available - // if there are no manual mipmaps - // set 0 level mipmap and then use GL to generate other mipmap levels + } else { // regular Texture (image, video, canvas) - if ( mipmaps.length > 0 && isImagePowerOfTwo ) { + // use manually created mipmaps if available + // if there are no manual mipmaps + // set 0 level mipmap and then use GL to generate other mipmap levels - for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { + if ( mipmaps.length > 0 && isImagePowerOfTwo ) { - mipmap = mipmaps[ i ]; - _gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap ); + for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { - } + mipmap = mipmaps[ i ]; + _gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap ); - texture.generateMipmaps = false; + } - } else { + texture.generateMipmaps = false; - _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, texture.image ); + } else { - } + _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, texture.image ); } - if ( texture.generateMipmaps && isImagePowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); + } + + if ( texture.generateMipmaps && isImagePowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); + + texture.needsUpdate = false; + + if ( texture.onUpdate ) texture.onUpdate(); + + }; + + this.setTexture = function ( texture, slot ) { + + _gl.activeTexture( _gl.TEXTURE0 + slot ); - texture.needsUpdate = false; + if ( texture.needsUpdate ) { - if ( texture.onUpdate ) texture.onUpdate(); + _this.uploadTexture( texture ); } else { - _gl.activeTexture( _gl.TEXTURE0 + slot ); _gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture ); } @@ -24533,27 +23554,27 @@ THREE.WebGLRenderer = function ( parameters ) { function clampToMaxSize ( image, maxSize ) { - if ( image.width <= maxSize && image.height <= maxSize ) { + if ( image.width > maxSize || image.height > maxSize ) { - return image; + // Warning: Scaling through the canvas will only work with images that use + // premultiplied alpha. - } + var scale = maxSize / Math.max( image.width, image.height ); - // Warning: Scaling through the canvas will only work with images that use - // premultiplied alpha. + var canvas = document.createElement( 'canvas' ); + canvas.width = Math.floor( image.width * scale ); + canvas.height = Math.floor( image.height * scale ); - var maxDimension = Math.max( image.width, image.height ); - var newWidth = Math.floor( image.width * maxSize / maxDimension ); - var newHeight = Math.floor( image.height * maxSize / maxDimension ); + var context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height ); - var canvas = document.createElement( 'canvas' ); - canvas.width = newWidth; - canvas.height = newHeight; + console.log( 'THREE.WebGLRenderer:', image, 'is too big (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height + '.' ); - var ctx = canvas.getContext( '2d' ); - ctx.drawImage( image, 0, 0, image.width, image.height, 0, 0, newWidth, newHeight ); + return canvas; - return canvas; + } + + return image; } @@ -24579,18 +23600,19 @@ THREE.WebGLRenderer = function ( parameters ) { _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); var isCompressed = texture instanceof THREE.CompressedTexture; + var isDataTexture = texture.image[ 0 ] instanceof THREE.DataTexture; var cubeImage = []; for ( var i = 0; i < 6; i ++ ) { - if ( _this.autoScaleCubemaps && ! isCompressed ) { + if ( _this.autoScaleCubemaps && ! isCompressed && ! isDataTexture ) { cubeImage[ i ] = clampToMaxSize( texture.image[ i ], _maxCubemapSize ); } else { - cubeImage[ i ] = texture.image[ i ]; + cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; } @@ -24607,7 +23629,15 @@ THREE.WebGLRenderer = function ( parameters ) { if ( ! isCompressed ) { - _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] ); + if ( isDataTexture ) { + + _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); + + } else { + + _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] ); + + } } else { @@ -24616,16 +23646,29 @@ THREE.WebGLRenderer = function ( parameters ) { for ( var j = 0, jl = mipmaps.length; j < jl; j ++ ) { mipmap = mipmaps[ j ]; - if ( texture.format !== THREE.RGBAFormat ) { - _gl.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) { + + if ( getCompressedTextureFormats().indexOf( glFormat ) > -1 ) { + + _gl.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + + } else { + + console.warn( "Attempt to load unsupported compressed texture format" ); + + } } else { + _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + } } + } + } if ( texture.generateMipmaps && isImagePowerOfTwo ) { @@ -24647,14 +23690,14 @@ THREE.WebGLRenderer = function ( parameters ) { } - }; + } function setCubeTextureDynamic ( texture, slot ) { _gl.activeTexture( _gl.TEXTURE0 + slot ); _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.__webglTexture ); - }; + } // Render targets @@ -24663,7 +23706,7 @@ THREE.WebGLRenderer = function ( parameters ) { _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureTarget, renderTarget.__webglTexture, 0 ); - }; + } function setupRenderBuffer ( renderbuffer, renderTarget ) { @@ -24691,13 +23734,13 @@ THREE.WebGLRenderer = function ( parameters ) { } - }; + } this.setRenderTarget = function ( renderTarget ) { var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube ); - if ( renderTarget && ! renderTarget.__webglFramebuffer ) { + if ( renderTarget && renderTarget.__webglFramebuffer === undefined ) { if ( renderTarget.depthBuffer === undefined ) renderTarget.depthBuffer = true; if ( renderTarget.stencilBuffer === undefined ) renderTarget.stencilBuffer = true; @@ -24858,7 +23901,7 @@ THREE.WebGLRenderer = function ( parameters ) { } - }; + } // Fallback filters for non-power-of-2 textures @@ -24872,12 +23915,14 @@ THREE.WebGLRenderer = function ( parameters ) { return _gl.LINEAR; - }; + } // Map three.js constants to WebGL constants function paramThreeToGL ( p ) { + var extension; + if ( p === THREE.RepeatWrapping ) return _gl.REPEAT; if ( p === THREE.ClampToEdgeWrapping ) return _gl.CLAMP_TO_EDGE; if ( p === THREE.MirroredRepeatWrapping ) return _gl.MIRRORED_REPEAT; @@ -24925,18 +23970,40 @@ THREE.WebGLRenderer = function ( parameters ) { if ( p === THREE.OneMinusDstColorFactor ) return _gl.ONE_MINUS_DST_COLOR; if ( p === THREE.SrcAlphaSaturateFactor ) return _gl.SRC_ALPHA_SATURATE; - if ( _glExtensionCompressedTextureS3TC !== undefined ) { + extension = extensions.get( 'WEBGL_compressed_texture_s3tc' ); + + if ( extension !== null ) { - if ( p === THREE.RGB_S3TC_DXT1_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGB_S3TC_DXT1_EXT; - if ( p === THREE.RGBA_S3TC_DXT1_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGBA_S3TC_DXT1_EXT; - if ( p === THREE.RGBA_S3TC_DXT3_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGBA_S3TC_DXT3_EXT; - if ( p === THREE.RGBA_S3TC_DXT5_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGBA_S3TC_DXT5_EXT; + if ( p === THREE.RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; + if ( p === THREE.RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; + if ( p === THREE.RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; + if ( p === THREE.RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; + + } + + extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' ); + + if ( extension !== null ) { + + if ( p === THREE.RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + if ( p === THREE.RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; + if ( p === THREE.RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + if ( p === THREE.RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; + + } + + extension = extensions.get( 'EXT_blend_minmax' ); + + if ( extension !== null ) { + + if ( p === THREE.MinEquation ) return extension.MIN_EXT; + if ( p === THREE.MaxEquation ) return extension.MAX_EXT; } return 0; - }; + } // Allocations @@ -24977,7 +24044,7 @@ THREE.WebGLRenderer = function ( parameters ) { } - }; + } function allocateLights( lights ) { @@ -25001,7 +24068,7 @@ THREE.WebGLRenderer = function ( parameters ) { return { 'directional': dirLights, 'point': pointLights, 'spot': spotLights, 'hemi': hemiLights }; - }; + } function allocateShadows( lights ) { @@ -25020,129 +24087,33 @@ THREE.WebGLRenderer = function ( parameters ) { return maxShadows; - }; - - // Initialization - - function initGL() { - - try { - - var attributes = { - alpha: _alpha, - depth: _depth, - stencil: _stencil, - antialias: _antialias, - premultipliedAlpha: _premultipliedAlpha, - preserveDrawingBuffer: _preserveDrawingBuffer - }; - - _gl = _context || _canvas.getContext( 'webgl', attributes ) || _canvas.getContext( 'experimental-webgl', attributes ); - - if ( _gl === null ) { - - throw 'Error creating WebGL context.'; - - } - - } catch ( error ) { - - console.error( error ); - - } - - _glExtensionTextureFloat = _gl.getExtension( 'OES_texture_float' ); - _glExtensionTextureFloatLinear = _gl.getExtension( 'OES_texture_float_linear' ); - _glExtensionStandardDerivatives = _gl.getExtension( 'OES_standard_derivatives' ); - - _glExtensionTextureFilterAnisotropic = _gl.getExtension( 'EXT_texture_filter_anisotropic' ) || _gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || _gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' ); - - _glExtensionCompressedTextureS3TC = _gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || _gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || _gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' ); - - _glExtensionElementIndexUint = _gl.getExtension( 'OES_element_index_uint' ); - - - if ( _glExtensionTextureFloat === null ) { - - console.log( 'THREE.WebGLRenderer: Float textures not supported.' ); - - } - - if ( _glExtensionStandardDerivatives === null ) { - - console.log( 'THREE.WebGLRenderer: Standard derivatives not supported.' ); - - } - - if ( _glExtensionTextureFilterAnisotropic === null ) { - - console.log( 'THREE.WebGLRenderer: Anisotropic texture filtering not supported.' ); - - } - - if ( _glExtensionCompressedTextureS3TC === null ) { - - console.log( 'THREE.WebGLRenderer: S3TC compressed textures not supported.' ); - - } - - if ( _glExtensionElementIndexUint === null ) { - - console.log( 'THREE.WebGLRenderer: elementindex as unsigned integer not supported.' ); - - } - - if ( _gl.getShaderPrecisionFormat === undefined ) { - - _gl.getShaderPrecisionFormat = function () { - - return { - 'rangeMin': 1, - 'rangeMax': 1, - 'precision': 1 - }; - - } - } - - if ( _logarithmicDepthBuffer ) { + } - _glExtensionFragDepth = _gl.getExtension( 'EXT_frag_depth' ); + // DEPRECATED + + this.initMaterial = function () { - } + console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' ); }; - function setDefaultGLState () { - - _gl.clearColor( 0, 0, 0, 1 ); - _gl.clearDepth( 1 ); - _gl.clearStencil( 0 ); - - _gl.enable( _gl.DEPTH_TEST ); - _gl.depthFunc( _gl.LEQUAL ); + this.addPrePlugin = function () { - _gl.frontFace( _gl.CCW ); - _gl.cullFace( _gl.BACK ); - _gl.enable( _gl.CULL_FACE ); + console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' ); - _gl.enable( _gl.BLEND ); - _gl.blendEquation( _gl.FUNC_ADD ); - _gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA ); + }; - _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight ); + this.addPostPlugin = function () { - _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); + console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' ); }; - // default plugins (order is important) + this.updateShadowMap = function () { - this.shadowMapPlugin = new THREE.ShadowMapPlugin(); - this.addPrePlugin( this.shadowMapPlugin ); + console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' ); - this.addPostPlugin( new THREE.SpritePlugin() ); - this.addPostPlugin( new THREE.LensFlarePlugin() ); + }; }; @@ -25249,6 +24220,76 @@ THREE.WebGLRenderTargetCube = function ( width, height, options ) { THREE.WebGLRenderTargetCube.prototype = Object.create( THREE.WebGLRenderTarget.prototype ); +// File:src/renderers/webgl/WebGLExtensions.js + +THREE.WebGLExtensions = function ( gl ) { + + var extensions = {}; + + this.get = function ( name ) { + + if ( extensions[ name ] !== undefined ) { + + return extensions[ name ]; + + } + + var extension; + + switch ( name ) { + + case 'OES_texture_float': + extension = gl.getExtension( 'OES_texture_float' ); + break; + + case 'OES_texture_float_linear': + extension = gl.getExtension( 'OES_texture_float_linear' ); + break; + + case 'OES_standard_derivatives': + extension = gl.getExtension( 'OES_standard_derivatives' ); + break; + + case 'EXT_texture_filter_anisotropic': + extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' ); + break; + + case 'WEBGL_compressed_texture_s3tc': + extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' ); + break; + + case 'WEBGL_compressed_texture_pvrtc': + extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ); + break; + + case 'OES_element_index_uint': + extension = gl.getExtension( 'OES_element_index_uint' ); + break; + + case 'EXT_blend_minmax': + extension = gl.getExtension( 'EXT_blend_minmax' ); + break; + + case 'EXT_frag_depth': + extension = gl.getExtension( 'EXT_frag_depth' ); + break; + + } + + if ( extension === null ) { + + console.log( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' ); + + } + + extensions[ name ] = extension; + + return extension; + + }; + +}; + // File:src/renderers/webgl/WebGLProgram.js THREE.WebGLProgram = ( function () { @@ -25614,6 +24655,7 @@ THREE.WebGLProgram = ( function () { } this.attributes = cacheAttributeLocations( _gl, program, identifiers ); + this.attributesKeys = Object.keys( this.attributes ); // @@ -25677,5071 +24719,5210 @@ THREE.WebGLShader = ( function () { } )(); -// File:src/renderers/renderables/RenderableVertex.js +// File:src/renderers/webgl/plugins/LensFlarePlugin.js /** - * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ */ -THREE.RenderableVertex = function () { +THREE.LensFlarePlugin = function ( renderer, flares ) { - this.position = new THREE.Vector3(); - this.positionWorld = new THREE.Vector3(); - this.positionScreen = new THREE.Vector4(); + var gl = renderer.context; - this.visible = true; + var vertexBuffer, elementBuffer; + var program, attributes, uniforms; + var hasVertexTexture; -}; + var tempTexture, occlusionTexture; -THREE.RenderableVertex.prototype.copy = function ( vertex ) { + var init = function () { - this.positionWorld.copy( vertex.positionWorld ); - this.positionScreen.copy( vertex.positionScreen ); + var vertices = new Float32Array( [ + -1, -1, 0, 0, + 1, -1, 1, 0, + 1, 1, 1, 1, + -1, 1, 0, 1 + ] ); -}; + var faces = new Uint16Array( [ + 0, 1, 2, + 0, 2, 3 + ] ); -// File:src/renderers/renderables/RenderableFace.js + // buffers -/** - * @author mrdoob / http://mrdoob.com/ - */ + vertexBuffer = gl.createBuffer(); + elementBuffer = gl.createBuffer(); -THREE.RenderableFace = function () { + gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); + gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW ); - this.id = 0; + gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); + gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW ); - this.v1 = new THREE.RenderableVertex(); - this.v2 = new THREE.RenderableVertex(); - this.v3 = new THREE.RenderableVertex(); + // textures - this.normalModel = new THREE.Vector3(); + tempTexture = gl.createTexture(); + occlusionTexture = gl.createTexture(); - this.vertexNormalsModel = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ]; - this.vertexNormalsLength = 0; + gl.bindTexture( gl.TEXTURE_2D, tempTexture ); + gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGB, 16, 16, 0, gl.RGB, gl.UNSIGNED_BYTE, null ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); - this.color = new THREE.Color(); - this.material = null; - this.uvs = [ new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() ]; + gl.bindTexture( gl.TEXTURE_2D, occlusionTexture ); + gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); + gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); - this.z = 0; + hasVertexTexture = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) > 0; -}; + var shader; -// File:src/renderers/renderables/RenderableObject.js + if ( hasVertexTexture ) { -/** - * @author mrdoob / http://mrdoob.com/ - */ + shader = { -THREE.RenderableObject = function () { + vertexShader: [ - this.id = 0; + "uniform lowp int renderType;", - this.object = null; - this.z = 0; - -}; + "uniform vec3 screenPosition;", + "uniform vec2 scale;", + "uniform float rotation;", -// File:src/renderers/renderables/RenderableSprite.js + "uniform sampler2D occlusionMap;", -/** - * @author mrdoob / http://mrdoob.com/ - */ + "attribute vec2 position;", + "attribute vec2 uv;", -THREE.RenderableSprite = function () { + "varying vec2 vUV;", + "varying float vVisibility;", - this.id = 0; + "void main() {", - this.object = null; + "vUV = uv;", - this.x = 0; - this.y = 0; - this.z = 0; + "vec2 pos = position;", - this.rotation = 0; - this.scale = new THREE.Vector2(); + "if( renderType == 2 ) {", - this.material = null; + "vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );", -}; + "vVisibility = visibility.r / 9.0;", + "vVisibility *= 1.0 - visibility.g / 9.0;", + "vVisibility *= visibility.b / 9.0;", + "vVisibility *= 1.0 - visibility.a / 9.0;", -// File:src/renderers/renderables/RenderableLine.js + "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", + "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", -/** - * @author mrdoob / http://mrdoob.com/ - */ + "}", -THREE.RenderableLine = function () { + "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", - this.id = 0; + "}" - this.v1 = new THREE.RenderableVertex(); - this.v2 = new THREE.RenderableVertex(); + ].join( "\n" ), - this.vertexColors = [ new THREE.Color(), new THREE.Color() ]; - this.material = null; + fragmentShader: [ - this.z = 0; + "uniform lowp int renderType;", -}; + "uniform sampler2D map;", + "uniform float opacity;", + "uniform vec3 color;", -// File:src/extras/GeometryUtils.js + "varying vec2 vUV;", + "varying float vVisibility;", -/** - * @author mrdoob / http://mrdoob.com/ - */ + "void main() {", -THREE.GeometryUtils = { + // pink square - merge: function ( geometry1, geometry2, materialIndexOffset ) { + "if( renderType == 0 ) {", - console.warn( 'THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.' ); + "gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );", - var matrix; + // restore - if ( geometry2 instanceof THREE.Mesh ) { + "} else if( renderType == 1 ) {", - geometry2.matrixAutoUpdate && geometry2.updateMatrix(); + "gl_FragColor = texture2D( map, vUV );", - matrix = geometry2.matrix; - geometry2 = geometry2.geometry; + // flare - } + "} else {", - geometry1.merge( geometry2, matrix, materialIndexOffset ); + "vec4 texture = texture2D( map, vUV );", + "texture.a *= opacity * vVisibility;", + "gl_FragColor = texture;", + "gl_FragColor.rgb *= color;", - }, + "}", - center: function ( geometry ) { + "}" - console.warn( 'THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.' ); - return geometry.center(); + ].join( "\n" ) - } + }; -}; + } else { -// File:src/extras/ImageUtils.js + shader = { -/** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - * @author Daosheng Mu / https://github.com/DaoshengMu/ - */ + vertexShader: [ -THREE.ImageUtils = { + "uniform lowp int renderType;", - crossOrigin: undefined, + "uniform vec3 screenPosition;", + "uniform vec2 scale;", + "uniform float rotation;", - loadTexture: function ( url, mapping, onLoad, onError ) { + "attribute vec2 position;", + "attribute vec2 uv;", - var loader = new THREE.ImageLoader(); - loader.crossOrigin = this.crossOrigin; + "varying vec2 vUV;", - var texture = new THREE.Texture( undefined, mapping ); + "void main() {", - loader.load( url, function ( image ) { + "vUV = uv;", - texture.image = image; - texture.needsUpdate = true; + "vec2 pos = position;", - if ( onLoad ) onLoad( texture ); + "if( renderType == 2 ) {", - }, undefined, function ( event ) { + "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", + "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", - if ( onError ) onError( event ); + "}", - } ); + "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", - texture.sourceFile = url; + "}" - return texture; + ].join( "\n" ), - }, + fragmentShader: [ - loadTextureCube: function ( array, mapping, onLoad, onError ) { + "precision mediump float;", - var images = []; + "uniform lowp int renderType;", - var loader = new THREE.ImageLoader(); - loader.crossOrigin = this.crossOrigin; + "uniform sampler2D map;", + "uniform sampler2D occlusionMap;", + "uniform float opacity;", + "uniform vec3 color;", - var texture = new THREE.CubeTexture( images, mapping ); + "varying vec2 vUV;", - // no flipping needed for cube textures + "void main() {", - texture.flipY = false; + // pink square - var loaded = 0; + "if( renderType == 0 ) {", - var loadTexture = function ( i ) { + "gl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );", - loader.load( array[ i ], function ( image ) { + // restore - texture.images[ i ] = image; + "} else if( renderType == 1 ) {", - loaded += 1; + "gl_FragColor = texture2D( map, vUV );", - if ( loaded === 6 ) { + // flare - texture.needsUpdate = true; + "} else {", - if ( onLoad ) onLoad( texture ); + "float visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 ) ).a;", + "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a;", + "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a;", + "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) ).a;", + "visibility = ( 1.0 - visibility / 4.0 );", - } + "vec4 texture = texture2D( map, vUV );", + "texture.a *= opacity * visibility;", + "gl_FragColor = texture;", + "gl_FragColor.rgb *= color;", - } ); + "}", - } + "}" - for ( var i = 0, il = array.length; i < il; ++ i ) { + ].join( "\n" ) - loadTexture( i ); + }; } - return texture; + program = createProgram( shader ); - }, + attributes = { + vertex: gl.getAttribLocation ( program, "position" ), + uv: gl.getAttribLocation ( program, "uv" ) + } - loadCompressedTexture: function () { + uniforms = { + renderType: gl.getUniformLocation( program, "renderType" ), + map: gl.getUniformLocation( program, "map" ), + occlusionMap: gl.getUniformLocation( program, "occlusionMap" ), + opacity: gl.getUniformLocation( program, "opacity" ), + color: gl.getUniformLocation( program, "color" ), + scale: gl.getUniformLocation( program, "scale" ), + rotation: gl.getUniformLocation( program, "rotation" ), + screenPosition: gl.getUniformLocation( program, "screenPosition" ) + }; - console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' ) + }; - }, + /* + * Render lens flares + * Method: renders 16x16 0xff00ff-colored points scattered over the light source area, + * reads these back and calculates occlusion. + */ - loadCompressedTextureCube: function () { + this.render = function ( scene, camera, viewportWidth, viewportHeight ) { - console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' ) + if ( flares.length === 0 ) return; - }, + var tempPosition = new THREE.Vector3(); - getNormalMap: function ( image, depth ) { + var invAspect = viewportHeight / viewportWidth, + halfViewportWidth = viewportWidth * 0.5, + halfViewportHeight = viewportHeight * 0.5; - // Adapted from http://www.paulbrunt.co.uk/lab/heightnormal/ + var size = 16 / viewportHeight, + scale = new THREE.Vector2( size * invAspect, size ); - var cross = function ( a, b ) { + var screenPosition = new THREE.Vector3( 1, 1, 0 ), + screenPositionPixels = new THREE.Vector2( 1, 1 ); - return [ a[ 1 ] * b[ 2 ] - a[ 2 ] * b[ 1 ], a[ 2 ] * b[ 0 ] - a[ 0 ] * b[ 2 ], a[ 0 ] * b[ 1 ] - a[ 1 ] * b[ 0 ] ]; + if ( program === undefined ) { + + init(); } - var subtract = function ( a, b ) { + gl.useProgram( program ); - return [ a[ 0 ] - b[ 0 ], a[ 1 ] - b[ 1 ], a[ 2 ] - b[ 2 ] ]; + gl.enableVertexAttribArray( attributes.vertex ); + gl.enableVertexAttribArray( attributes.uv ); - } + // loop through all lens flares to update their occlusion and positions + // setup gl and common used attribs/unforms - var normalize = function ( a ) { + gl.uniform1i( uniforms.occlusionMap, 0 ); + gl.uniform1i( uniforms.map, 1 ); - var l = Math.sqrt( a[ 0 ] * a[ 0 ] + a[ 1 ] * a[ 1 ] + a[ 2 ] * a[ 2 ] ); - return [ a[ 0 ] / l, a[ 1 ] / l, a[ 2 ] / l ]; + gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); + gl.vertexAttribPointer( attributes.vertex, 2, gl.FLOAT, false, 2 * 8, 0 ); + gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 ); - } + gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); - depth = depth | 1; + gl.disable( gl.CULL_FACE ); + gl.depthMask( false ); - var width = image.width; - var height = image.height; + for ( var i = 0, l = flares.length; i < l; i ++ ) { - var canvas = document.createElement( 'canvas' ); - canvas.width = width; - canvas.height = height; + size = 16 / viewportHeight; + scale.set( size * invAspect, size ); - var context = canvas.getContext( '2d' ); - context.drawImage( image, 0, 0 ); + // calc object screen position - var data = context.getImageData( 0, 0, width, height ).data; - var imageData = context.createImageData( width, height ); - var output = imageData.data; + var flare = flares[ i ]; + + tempPosition.set( flare.matrixWorld.elements[12], flare.matrixWorld.elements[13], flare.matrixWorld.elements[14] ); - for ( var x = 0; x < width; x ++ ) { + tempPosition.applyMatrix4( camera.matrixWorldInverse ); + tempPosition.applyProjection( camera.projectionMatrix ); - for ( var y = 0; y < height; y ++ ) { + // setup arrays for gl programs - var ly = y - 1 < 0 ? 0 : y - 1; - var uy = y + 1 > height - 1 ? height - 1 : y + 1; - var lx = x - 1 < 0 ? 0 : x - 1; - var ux = x + 1 > width - 1 ? width - 1 : x + 1; + screenPosition.copy( tempPosition ) - var points = []; - var origin = [ 0, 0, data[ ( y * width + x ) * 4 ] / 255 * depth ]; - points.push( [ - 1, 0, data[ ( y * width + lx ) * 4 ] / 255 * depth ] ); - points.push( [ - 1, - 1, data[ ( ly * width + lx ) * 4 ] / 255 * depth ] ); - points.push( [ 0, - 1, data[ ( ly * width + x ) * 4 ] / 255 * depth ] ); - points.push( [ 1, - 1, data[ ( ly * width + ux ) * 4 ] / 255 * depth ] ); - points.push( [ 1, 0, data[ ( y * width + ux ) * 4 ] / 255 * depth ] ); - points.push( [ 1, 1, data[ ( uy * width + ux ) * 4 ] / 255 * depth ] ); - points.push( [ 0, 1, data[ ( uy * width + x ) * 4 ] / 255 * depth ] ); - points.push( [ - 1, 1, data[ ( uy * width + lx ) * 4 ] / 255 * depth ] ); + screenPositionPixels.x = screenPosition.x * halfViewportWidth + halfViewportWidth; + screenPositionPixels.y = screenPosition.y * halfViewportHeight + halfViewportHeight; - var normals = []; - var num_points = points.length; + // screen cull - for ( var i = 0; i < num_points; i ++ ) { + if ( hasVertexTexture || ( + screenPositionPixels.x > 0 && + screenPositionPixels.x < viewportWidth && + screenPositionPixels.y > 0 && + screenPositionPixels.y < viewportHeight ) ) { - var v1 = points[ i ]; - var v2 = points[ ( i + 1 ) % num_points ]; - v1 = subtract( v1, origin ); - v2 = subtract( v2, origin ); - normals.push( normalize( cross( v1, v2 ) ) ); + // save current RGB to temp texture - } + gl.activeTexture( gl.TEXTURE1 ); + gl.bindTexture( gl.TEXTURE_2D, tempTexture ); + gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGB, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 ); - var normal = [ 0, 0, 0 ]; - for ( var i = 0; i < normals.length; i ++ ) { + // render pink quad - normal[ 0 ] += normals[ i ][ 0 ]; - normal[ 1 ] += normals[ i ][ 1 ]; - normal[ 2 ] += normals[ i ][ 2 ]; + gl.uniform1i( uniforms.renderType, 0 ); + gl.uniform2f( uniforms.scale, scale.x, scale.y ); + gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); - } + gl.disable( gl.BLEND ); + gl.enable( gl.DEPTH_TEST ); - normal[ 0 ] /= normals.length; - normal[ 1 ] /= normals.length; - normal[ 2 ] /= normals.length; + gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - var idx = ( y * width + x ) * 4; - output[ idx ] = ( ( normal[ 0 ] + 1.0 ) / 2.0 * 255 ) | 0; - output[ idx + 1 ] = ( ( normal[ 1 ] + 1.0 ) / 2.0 * 255 ) | 0; - output[ idx + 2 ] = ( normal[ 2 ] * 255 ) | 0; - output[ idx + 3 ] = 255; + // copy result to occlusionMap - } + gl.activeTexture( gl.TEXTURE0 ); + gl.bindTexture( gl.TEXTURE_2D, occlusionTexture ); + gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGBA, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 ); - } - context.putImageData( imageData, 0, 0 ); + // restore graphics - return canvas; + gl.uniform1i( uniforms.renderType, 1 ); + gl.disable( gl.DEPTH_TEST ); - }, + gl.activeTexture( gl.TEXTURE1 ); + gl.bindTexture( gl.TEXTURE_2D, tempTexture ); + gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - generateDataTexture: function ( width, height, color ) { - var size = width * height; - var data = new Uint8Array( 3 * size ); + // update object positions - var r = Math.floor( color.r * 255 ); - var g = Math.floor( color.g * 255 ); - var b = Math.floor( color.b * 255 ); + flare.positionScreen.copy( screenPosition ) - for ( var i = 0; i < size; i ++ ) { + if ( flare.customUpdateCallback ) { - data[ i * 3 ] = r; - data[ i * 3 + 1 ] = g; - data[ i * 3 + 2 ] = b; + flare.customUpdateCallback( flare ); - } + } else { - var texture = new THREE.DataTexture( data, width, height, THREE.RGBFormat ); - texture.needsUpdate = true; + flare.updateLensFlares(); - return texture; + } - } + // render flares -}; + gl.uniform1i( uniforms.renderType, 2 ); + gl.enable( gl.BLEND ); -// File:src/extras/SceneUtils.js + for ( var j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) { -/** - * @author alteredq / http://alteredqualia.com/ - */ + var sprite = flare.lensFlares[ j ]; -THREE.SceneUtils = { + if ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) { - createMultiMaterialObject: function ( geometry, materials ) { + screenPosition.x = sprite.x; + screenPosition.y = sprite.y; + screenPosition.z = sprite.z; - var group = new THREE.Object3D(); + size = sprite.size * sprite.scale / viewportHeight; - for ( var i = 0, l = materials.length; i < l; i ++ ) { + scale.x = size * invAspect; + scale.y = size; - group.add( new THREE.Mesh( geometry, materials[ i ] ) ); + gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); + gl.uniform2f( uniforms.scale, scale.x, scale.y ); + gl.uniform1f( uniforms.rotation, sprite.rotation ); - } + gl.uniform1f( uniforms.opacity, sprite.opacity ); + gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b ); - return group; + renderer.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst ); + renderer.setTexture( sprite.texture, 1 ); - }, + gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - detach: function ( child, parent, scene ) { + } - child.applyMatrix( parent.matrixWorld ); - parent.remove( child ); - scene.add( child ); + } - }, + } - attach: function ( child, scene, parent ) { + } - var matrixWorldInverse = new THREE.Matrix4(); - matrixWorldInverse.getInverse( parent.matrixWorld ); - child.applyMatrix( matrixWorldInverse ); + // restore gl - scene.remove( child ); - parent.add( child ); + gl.enable( gl.CULL_FACE ); + gl.enable( gl.DEPTH_TEST ); + gl.depthMask( true ); - } + renderer.resetGLState(); -}; + }; -// File:src/extras/FontUtils.js + function createProgram ( shader ) { -/** - * @author zz85 / http://www.lab4games.net/zz85/blog - * @author alteredq / http://alteredqualia.com/ - * - * For Text operations in three.js (See TextGeometry) - * - * It uses techniques used in: - * - * typeface.js and canvastext - * For converting fonts and rendering with javascript - * http://typeface.neocracy.org - * - * Triangulation ported from AS3 - * Simple Polygon Triangulation - * http://actionsnippet.com/?p=1462 - * - * A Method to triangulate shapes with holes - * http://www.sakri.net/blog/2009/06/12/an-approach-to-triangulating-polygons-with-holes/ - * - */ + var program = gl.createProgram(); -THREE.FontUtils = { + var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER ); + var vertexShader = gl.createShader( gl.VERTEX_SHADER ); - faces: {}, + var prefix = "precision " + renderer.getPrecision() + " float;\n"; - // Just for now. face[weight][style] + gl.shaderSource( fragmentShader, prefix + shader.fragmentShader ); + gl.shaderSource( vertexShader, prefix + shader.vertexShader ); - face: 'helvetiker', - weight: 'normal', - style: 'normal', - size: 150, - divisions: 10, + gl.compileShader( fragmentShader ); + gl.compileShader( vertexShader ); - getFace: function () { + gl.attachShader( program, fragmentShader ); + gl.attachShader( program, vertexShader ); - try { + gl.linkProgram( program ); - return this.faces[ this.face ][ this.weight ][ this.style ]; + return program; - } catch (e) { + } - throw "The font " + this.face + " with " + this.weight + " weight and " + this.style + " style is missing." +}; - }; +// File:src/renderers/webgl/plugins/ShadowMapPlugin.js - }, +/** + * @author alteredq / http://alteredqualia.com/ + */ - loadFace: function ( data ) { +THREE.ShadowMapPlugin = function ( _renderer, _lights, _webglObjects, _webglObjectsImmediate ) { - var family = data.familyName.toLowerCase(); + var _gl = _renderer.context; - var ThreeFont = this; + var _depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin, - ThreeFont.faces[ family ] = ThreeFont.faces[ family ] || {}; + _frustum = new THREE.Frustum(), + _projScreenMatrix = new THREE.Matrix4(), - ThreeFont.faces[ family ][ data.cssFontWeight ] = ThreeFont.faces[ family ][ data.cssFontWeight ] || {}; - ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data; + _min = new THREE.Vector3(), + _max = new THREE.Vector3(), - var face = ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data; + _matrixPosition = new THREE.Vector3(), + + _renderList = []; - return data; + // init - }, + var depthShader = THREE.ShaderLib[ "depthRGBA" ]; + var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms ); - drawText: function ( text ) { + _depthMaterial = new THREE.ShaderMaterial( { + uniforms: depthUniforms, + vertexShader: depthShader.vertexShader, + fragmentShader: depthShader.fragmentShader + } ); - var characterPts = [], allPts = []; + _depthMaterialMorph = new THREE.ShaderMaterial( { + uniforms: depthUniforms, + vertexShader: depthShader.vertexShader, + fragmentShader: depthShader.fragmentShader, + morphTargets: true + } ); - // RenderText + _depthMaterialSkin = new THREE.ShaderMaterial( { + uniforms: depthUniforms, + vertexShader: depthShader.vertexShader, + fragmentShader: depthShader.fragmentShader, + skinning: true + } ); - var i, p, - face = this.getFace(), - scale = this.size / face.resolution, - offset = 0, - chars = String( text ).split( '' ), - length = chars.length; + _depthMaterialMorphSkin = new THREE.ShaderMaterial( { + uniforms: depthUniforms, + vertexShader: depthShader.vertexShader, + fragmentShader: depthShader.fragmentShader, + morphTargets: true, + skinning: true + } ); - var fontPaths = []; + _depthMaterial._shadowPass = true; + _depthMaterialMorph._shadowPass = true; + _depthMaterialSkin._shadowPass = true; + _depthMaterialMorphSkin._shadowPass = true; - for ( i = 0; i < length; i ++ ) { + this.render = function ( scene, camera ) { - var path = new THREE.Path(); + if ( _renderer.shadowMapEnabled === false ) return; - var ret = this.extractGlyphPoints( chars[ i ], face, scale, offset, path ); - offset += ret.offset; + var i, il, j, jl, n, - fontPaths.push( ret.path ); + shadowMap, shadowMatrix, shadowCamera, + program, buffer, material, + webglObject, object, light, - } + lights = [], + k = 0, - // get the width + fog = null; - var width = offset / 2; - // - // for ( p = 0; p < allPts.length; p++ ) { - // - // allPts[ p ].x -= width; - // - // } + // set GL state for depth map - //var extract = this.extractPoints( allPts, characterPts ); - //extract.contour = allPts; + _gl.clearColor( 1, 1, 1, 1 ); + _gl.disable( _gl.BLEND ); - //extract.paths = fontPaths; - //extract.offset = width; + _gl.enable( _gl.CULL_FACE ); + _gl.frontFace( _gl.CCW ); - return { paths: fontPaths, offset: width }; + if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) { - }, + _gl.cullFace( _gl.FRONT ); + } else { + _gl.cullFace( _gl.BACK ); + } - extractGlyphPoints: function ( c, face, scale, offset, path ) { + _renderer.setDepthTest( true ); - var pts = []; + // preprocess lights + // - skip lights that are not casting shadows + // - create virtual lights for cascaded shadow maps - var i, i2, divisions, - outline, action, length, - scaleX, scaleY, - x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2, - laste, - glyph = face.glyphs[ c ] || face.glyphs[ '?' ]; + for ( i = 0, il = _lights.length; i < il; i ++ ) { - if ( ! glyph ) return; + light = _lights[ i ]; - if ( glyph.o ) { + if ( ! light.castShadow ) continue; - outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) ); - length = outline.length; + if ( ( light instanceof THREE.DirectionalLight ) && light.shadowCascade ) { - scaleX = scale; - scaleY = scale; + for ( n = 0; n < light.shadowCascadeCount; n ++ ) { - for ( i = 0; i < length; ) { + var virtualLight; - action = outline[ i ++ ]; + if ( ! light.shadowCascadeArray[ n ] ) { - //console.log( action ); + virtualLight = createVirtualLight( light, n ); + virtualLight.originalCamera = camera; - switch ( action ) { + var gyro = new THREE.Gyroscope(); + gyro.position.copy( light.shadowCascadeOffset ); - case 'm': + gyro.add( virtualLight ); + gyro.add( virtualLight.target ); - // Move To + camera.add( gyro ); - x = outline[ i ++ ] * scaleX + offset; - y = outline[ i ++ ] * scaleY; + light.shadowCascadeArray[ n ] = virtualLight; - path.moveTo( x, y ); - break; + console.log( "Created virtualLight", virtualLight ); - case 'l': + } else { - // Line To + virtualLight = light.shadowCascadeArray[ n ]; - x = outline[ i ++ ] * scaleX + offset; - y = outline[ i ++ ] * scaleY; - path.lineTo( x,y ); - break; + } - case 'q': + updateVirtualLight( light, n ); - // QuadraticCurveTo + lights[ k ] = virtualLight; + k ++; - cpx = outline[ i ++ ] * scaleX + offset; - cpy = outline[ i ++ ] * scaleY; - cpx1 = outline[ i ++ ] * scaleX + offset; - cpy1 = outline[ i ++ ] * scaleY; + } - path.quadraticCurveTo( cpx1, cpy1, cpx, cpy ); + } else { - laste = pts[ pts.length - 1 ]; + lights[ k ] = light; + k ++; - if ( laste ) { + } - cpx0 = laste.x; - cpy0 = laste.y; + } - for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) { + // render depth map - var t = i2 / divisions; - var tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx ); - var ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy ); - } + for ( i = 0, il = lights.length; i < il; i ++ ) { - } + light = lights[ i ]; - break; + if ( ! light.shadowMap ) { - case 'b': + var shadowFilter = THREE.LinearFilter; - // Cubic Bezier Curve + if ( _renderer.shadowMapType === THREE.PCFSoftShadowMap ) { - cpx = outline[ i ++ ] * scaleX + offset; - cpy = outline[ i ++ ] * scaleY; - cpx1 = outline[ i ++ ] * scaleX + offset; - cpy1 = outline[ i ++ ] * scaleY; - cpx2 = outline[ i ++ ] * scaleX + offset; - cpy2 = outline[ i ++ ] * scaleY; + shadowFilter = THREE.NearestFilter; - path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy ); + } - laste = pts[ pts.length - 1 ]; + var pars = { minFilter: shadowFilter, magFilter: shadowFilter, format: THREE.RGBAFormat }; - if ( laste ) { + light.shadowMap = new THREE.WebGLRenderTarget( light.shadowMapWidth, light.shadowMapHeight, pars ); + light.shadowMapSize = new THREE.Vector2( light.shadowMapWidth, light.shadowMapHeight ); - cpx0 = laste.x; - cpy0 = laste.y; + light.shadowMatrix = new THREE.Matrix4(); - for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) { + } - var t = i2 / divisions; - var tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx ); - var ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy ); + if ( ! light.shadowCamera ) { - } + if ( light instanceof THREE.SpotLight ) { - } + light.shadowCamera = new THREE.PerspectiveCamera( light.shadowCameraFov, light.shadowMapWidth / light.shadowMapHeight, light.shadowCameraNear, light.shadowCameraFar ); - break; + } else if ( light instanceof THREE.DirectionalLight ) { - } + light.shadowCamera = new THREE.OrthographicCamera( light.shadowCameraLeft, light.shadowCameraRight, light.shadowCameraTop, light.shadowCameraBottom, light.shadowCameraNear, light.shadowCameraFar ); - } - } + } else { + console.error( "Unsupported light type for shadow" ); + continue; + } - return { offset: glyph.ha * scale, path:path }; - } + scene.add( light.shadowCamera ); -}; + if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); + } -THREE.FontUtils.generateShapes = function ( text, parameters ) { + if ( light.shadowCameraVisible && ! light.cameraHelper ) { - // Parameters + light.cameraHelper = new THREE.CameraHelper( light.shadowCamera ); + scene.add( light.cameraHelper ); - parameters = parameters || {}; + } - var size = parameters.size !== undefined ? parameters.size : 100; - var curveSegments = parameters.curveSegments !== undefined ? parameters.curveSegments : 4; + if ( light.isVirtual && virtualLight.originalCamera == camera ) { - var font = parameters.font !== undefined ? parameters.font : 'helvetiker'; - var weight = parameters.weight !== undefined ? parameters.weight : 'normal'; - var style = parameters.style !== undefined ? parameters.style : 'normal'; - - THREE.FontUtils.size = size; - THREE.FontUtils.divisions = curveSegments; + updateShadowCamera( camera, light ); - THREE.FontUtils.face = font; - THREE.FontUtils.weight = weight; - THREE.FontUtils.style = style; + } - // Get a Font data json object + shadowMap = light.shadowMap; + shadowMatrix = light.shadowMatrix; + shadowCamera = light.shadowCamera; - var data = THREE.FontUtils.drawText( text ); + // - var paths = data.paths; - var shapes = []; + shadowCamera.position.setFromMatrixPosition( light.matrixWorld ); + _matrixPosition.setFromMatrixPosition( light.target.matrixWorld ); + shadowCamera.lookAt( _matrixPosition ); + shadowCamera.updateMatrixWorld(); - for ( var p = 0, pl = paths.length; p < pl; p ++ ) { + shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld ); - Array.prototype.push.apply( shapes, paths[ p ].toShapes() ); + // - } + if ( light.cameraHelper ) light.cameraHelper.visible = light.shadowCameraVisible; + if ( light.shadowCameraVisible ) light.cameraHelper.update(); - return shapes; + // compute shadow matrix -}; + shadowMatrix.set( + 0.5, 0.0, 0.0, 0.5, + 0.0, 0.5, 0.0, 0.5, + 0.0, 0.0, 0.5, 0.5, + 0.0, 0.0, 0.0, 1.0 + ); + shadowMatrix.multiply( shadowCamera.projectionMatrix ); + shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); -/** - * This code is a quick port of code written in C++ which was submitted to - * flipcode.com by John W. Ratcliff // July 22, 2000 - * See original code and more information here: - * http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml - * - * ported to actionscript by Zevan Rosser - * www.actionsnippet.com - * - * ported to javascript by Joshua Koo - * http://www.lab4games.net/zz85/blog - * - */ + // update camera matrices and frustum + _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); + _frustum.setFromMatrix( _projScreenMatrix ); -( function ( namespace ) { + // render shadow map - var EPSILON = 0.0000000001; + _renderer.setRenderTarget( shadowMap ); + _renderer.clear(); - // takes in an contour array and returns + // set object matrices & frustum culling - var process = function ( contour, indices ) { + _renderList.length = 0; - var n = contour.length; + projectObject( scene, scene, shadowCamera ); - if ( n < 3 ) return null; - var result = [], - verts = [], - vertIndices = []; + // render regular objects - /* we want a counter-clockwise polygon in verts */ + var objectMaterial, useMorphing, useSkinning; - var u, v, w; + for ( j = 0, jl = _renderList.length; j < jl; j ++ ) { - if ( area( contour ) > 0.0 ) { + webglObject = _renderList[ j ]; - for ( v = 0; v < n; v ++ ) verts[ v ] = v; + object = webglObject.object; + buffer = webglObject.buffer; - } else { + // culling is overriden globally for all objects + // while rendering depth map - for ( v = 0; v < n; v ++ ) verts[ v ] = ( n - 1 ) - v; + // need to deal with MeshFaceMaterial somehow + // in that case just use the first of material.materials for now + // (proper solution would require to break objects by materials + // similarly to regular rendering and then set corresponding + // depth materials per each chunk instead of just once per object) - } + objectMaterial = getObjectMaterial( object ); - var nv = n; + useMorphing = object.geometry.morphTargets !== undefined && object.geometry.morphTargets.length > 0 && objectMaterial.morphTargets; + useSkinning = object instanceof THREE.SkinnedMesh && objectMaterial.skinning; - /* remove nv - 2 vertices, creating 1 triangle every time */ + if ( object.customDepthMaterial ) { - var count = 2 * nv; /* error detection */ + material = object.customDepthMaterial; - for ( v = nv - 1; nv > 2; ) { + } else if ( useSkinning ) { - /* if we loop, it is probably a non-simple polygon */ + material = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin; - if ( ( count -- ) <= 0 ) { + } else if ( useMorphing ) { - //** Triangulate: ERROR - probable bad polygon! + material = _depthMaterialMorph; - //throw ( "Warning, unable to triangulate polygon!" ); - //return null; - // Sometimes warning is fine, especially polygons are triangulated in reverse. - console.log( 'Warning, unable to triangulate polygon!' ); + } else { - if ( indices ) return vertIndices; - return result; + material = _depthMaterial; - } + } - /* three consecutive vertices in current polygon, */ + _renderer.setMaterialFaces( objectMaterial ); - u = v; if ( nv <= u ) u = 0; /* previous */ - v = u + 1; if ( nv <= v ) v = 0; /* new v */ - w = v + 1; if ( nv <= w ) w = 0; /* next */ + if ( buffer instanceof THREE.BufferGeometry ) { - if ( snip( contour, u, v, w, nv, verts ) ) { + _renderer.renderBufferDirect( shadowCamera, _lights, fog, material, buffer, object ); - var a, b, c, s, t; + } else { - /* true names of the vertices */ + _renderer.renderBuffer( shadowCamera, _lights, fog, material, buffer, object ); - a = verts[ u ]; - b = verts[ v ]; - c = verts[ w ]; + } - /* output Triangle */ + } - result.push( [ contour[ a ], - contour[ b ], - contour[ c ] ] ); + // set matrices and render immediate objects + for ( j = 0, jl = _webglObjectsImmediate.length; j < jl; j ++ ) { - vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] ); + webglObject = _webglObjectsImmediate[ j ]; + object = webglObject.object; - /* remove v from the remaining polygon */ + if ( object.visible && object.castShadow ) { - for ( s = v, t = v + 1; t < nv; s++, t++ ) { + object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); - verts[ s ] = verts[ t ]; + _renderer.renderImmediateObject( shadowCamera, _lights, fog, _depthMaterial, object ); } - nv --; + } - /* reset error detection counter */ + } - count = 2 * nv; + // restore GL state - } + var clearColor = _renderer.getClearColor(), + clearAlpha = _renderer.getClearAlpha(); + + _gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha ); + _gl.enable( _gl.BLEND ); + + if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) { + + _gl.cullFace( _gl.BACK ); } - if ( indices ) return vertIndices; - return result; + _renderer.resetGLState(); }; - // calculate area of the contour polygon + function projectObject( scene, object, shadowCamera ){ - var area = function ( contour ) { + if ( object.visible ) { - var n = contour.length; - var a = 0.0; + var webglObjects = _webglObjects[ object.id ]; - for ( var p = n - 1, q = 0; q < n; p = q ++ ) { + if ( webglObjects && object.castShadow && (object.frustumCulled === false || _frustum.intersectsObject( object ) === true) ) { - a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; + for ( var i = 0, l = webglObjects.length; i < l; i ++ ) { - } + var webglObject = webglObjects[ i ]; - return a * 0.5; + object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); + _renderList.push( webglObject ); - }; + } - var snip = function ( contour, u, v, w, n, verts ) { + } - var p; - var ax, ay, bx, by; - var cx, cy, px, py; + for ( var i = 0, l = object.children.length; i < l; i ++ ) { - ax = contour[ verts[ u ] ].x; - ay = contour[ verts[ u ] ].y; + projectObject( scene, object.children[ i ], shadowCamera ); - bx = contour[ verts[ v ] ].x; - by = contour[ verts[ v ] ].y; + } - cx = contour[ verts[ w ] ].x; - cy = contour[ verts[ w ] ].y; + } - if ( EPSILON > ( ( ( bx - ax ) * ( cy - ay ) ) - ( ( by - ay ) * ( cx - ax ) ) ) ) return false; + } - var aX, aY, bX, bY, cX, cY; - var apx, apy, bpx, bpy, cpx, cpy; - var cCROSSap, bCROSScp, aCROSSbp; + function createVirtualLight( light, cascade ) { - aX = cx - bx; aY = cy - by; - bX = ax - cx; bY = ay - cy; - cX = bx - ax; cY = by - ay; + var virtualLight = new THREE.DirectionalLight(); - for ( p = 0; p < n; p ++ ) { + virtualLight.isVirtual = true; - px = contour[ verts[ p ] ].x - py = contour[ verts[ p ] ].y + virtualLight.onlyShadow = true; + virtualLight.castShadow = true; - if ( ( ( px === ax ) && ( py === ay ) ) || - ( ( px === bx ) && ( py === by ) ) || - ( ( px === cx ) && ( py === cy ) ) ) continue; + virtualLight.shadowCameraNear = light.shadowCameraNear; + virtualLight.shadowCameraFar = light.shadowCameraFar; - apx = px - ax; apy = py - ay; - bpx = px - bx; bpy = py - by; - cpx = px - cx; cpy = py - cy; + virtualLight.shadowCameraLeft = light.shadowCameraLeft; + virtualLight.shadowCameraRight = light.shadowCameraRight; + virtualLight.shadowCameraBottom = light.shadowCameraBottom; + virtualLight.shadowCameraTop = light.shadowCameraTop; - // see if p is inside triangle abc + virtualLight.shadowCameraVisible = light.shadowCameraVisible; - aCROSSbp = aX * bpy - aY * bpx; - cCROSSap = cX * apy - cY * apx; - bCROSScp = bX * cpy - bY * cpx; + virtualLight.shadowDarkness = light.shadowDarkness; - if ( ( aCROSSbp >= - EPSILON ) && ( bCROSScp >= - EPSILON ) && ( cCROSSap >= - EPSILON ) ) return false; + virtualLight.shadowBias = light.shadowCascadeBias[ cascade ]; + virtualLight.shadowMapWidth = light.shadowCascadeWidth[ cascade ]; + virtualLight.shadowMapHeight = light.shadowCascadeHeight[ cascade ]; + + virtualLight.pointsWorld = []; + virtualLight.pointsFrustum = []; + + var pointsWorld = virtualLight.pointsWorld, + pointsFrustum = virtualLight.pointsFrustum; + + for ( var i = 0; i < 8; i ++ ) { + + pointsWorld[ i ] = new THREE.Vector3(); + pointsFrustum[ i ] = new THREE.Vector3(); } - return true; + var nearZ = light.shadowCascadeNearZ[ cascade ]; + var farZ = light.shadowCascadeFarZ[ cascade ]; - }; + pointsFrustum[ 0 ].set( - 1, - 1, nearZ ); + pointsFrustum[ 1 ].set( 1, - 1, nearZ ); + pointsFrustum[ 2 ].set( - 1, 1, nearZ ); + pointsFrustum[ 3 ].set( 1, 1, nearZ ); + pointsFrustum[ 4 ].set( - 1, - 1, farZ ); + pointsFrustum[ 5 ].set( 1, - 1, farZ ); + pointsFrustum[ 6 ].set( - 1, 1, farZ ); + pointsFrustum[ 7 ].set( 1, 1, farZ ); - namespace.Triangulate = process; - namespace.Triangulate.area = area; + return virtualLight; - return namespace; + } -} )( THREE.FontUtils ); + // Synchronize virtual light with the original light -// To use the typeface.js face files, hook up the API -self._typeface_js = { faces: THREE.FontUtils.faces, loadFace: THREE.FontUtils.loadFace }; -THREE.typeface_js = self._typeface_js; + function updateVirtualLight( light, cascade ) { -// File:src/extras/core/Curve.js + var virtualLight = light.shadowCascadeArray[ cascade ]; -/** - * @author zz85 / http://www.lab4games.net/zz85/blog - * Extensible curve object - * - * Some common of Curve methods - * .getPoint(t), getTangent(t) - * .getPointAt(u), getTagentAt(u) - * .getPoints(), .getSpacedPoints() - * .getLength() - * .updateArcLengths() - * - * This following classes subclasses THREE.Curve: - * - * -- 2d classes -- - * THREE.LineCurve - * THREE.QuadraticBezierCurve - * THREE.CubicBezierCurve - * THREE.SplineCurve - * THREE.ArcCurve - * THREE.EllipseCurve - * - * -- 3d classes -- - * THREE.LineCurve3 - * THREE.QuadraticBezierCurve3 - * THREE.CubicBezierCurve3 - * THREE.SplineCurve3 - * THREE.ClosedSplineCurve3 - * - * A series of curves can be represented as a THREE.CurvePath - * - **/ - -/************************************************************** - * Abstract Curve base class - **************************************************************/ + virtualLight.position.copy( light.position ); + virtualLight.target.position.copy( light.target.position ); + virtualLight.lookAt( virtualLight.target ); -THREE.Curve = function () { + virtualLight.shadowCameraVisible = light.shadowCameraVisible; + virtualLight.shadowDarkness = light.shadowDarkness; -}; + virtualLight.shadowBias = light.shadowCascadeBias[ cascade ]; -// Virtual base class method to overwrite and implement in subclasses -// - t [0 .. 1] + var nearZ = light.shadowCascadeNearZ[ cascade ]; + var farZ = light.shadowCascadeFarZ[ cascade ]; -THREE.Curve.prototype.getPoint = function ( t ) { + var pointsFrustum = virtualLight.pointsFrustum; - console.log( "Warning, getPoint() not implemented!" ); - return null; + pointsFrustum[ 0 ].z = nearZ; + pointsFrustum[ 1 ].z = nearZ; + pointsFrustum[ 2 ].z = nearZ; + pointsFrustum[ 3 ].z = nearZ; -}; + pointsFrustum[ 4 ].z = farZ; + pointsFrustum[ 5 ].z = farZ; + pointsFrustum[ 6 ].z = farZ; + pointsFrustum[ 7 ].z = farZ; -// Get point at relative position in curve according to arc length -// - u [0 .. 1] + } -THREE.Curve.prototype.getPointAt = function ( u ) { + // Fit shadow camera's ortho frustum to camera frustum - var t = this.getUtoTmapping( u ); - return this.getPoint( t ); + function updateShadowCamera( camera, light ) { -}; + var shadowCamera = light.shadowCamera, + pointsFrustum = light.pointsFrustum, + pointsWorld = light.pointsWorld; -// Get sequence of points using getPoint( t ) + _min.set( Infinity, Infinity, Infinity ); + _max.set( - Infinity, - Infinity, - Infinity ); -THREE.Curve.prototype.getPoints = function ( divisions ) { + for ( var i = 0; i < 8; i ++ ) { - if ( ! divisions ) divisions = 5; + var p = pointsWorld[ i ]; - var d, pts = []; + p.copy( pointsFrustum[ i ] ); + p.unproject( camera ); - for ( d = 0; d <= divisions; d ++ ) { + p.applyMatrix4( shadowCamera.matrixWorldInverse ); - pts.push( this.getPoint( d / divisions ) ); + if ( p.x < _min.x ) _min.x = p.x; + if ( p.x > _max.x ) _max.x = p.x; - } + if ( p.y < _min.y ) _min.y = p.y; + if ( p.y > _max.y ) _max.y = p.y; - return pts; + if ( p.z < _min.z ) _min.z = p.z; + if ( p.z > _max.z ) _max.z = p.z; -}; + } -// Get sequence of points using getPointAt( u ) + shadowCamera.left = _min.x; + shadowCamera.right = _max.x; + shadowCamera.top = _max.y; + shadowCamera.bottom = _min.y; -THREE.Curve.prototype.getSpacedPoints = function ( divisions ) { + // can't really fit near/far + //shadowCamera.near = _min.z; + //shadowCamera.far = _max.z; - if ( ! divisions ) divisions = 5; + shadowCamera.updateProjectionMatrix(); - var d, pts = []; + } - for ( d = 0; d <= divisions; d ++ ) { + // For the moment just ignore objects that have multiple materials with different animation methods + // Only the first material will be taken into account for deciding which depth material to use for shadow maps - pts.push( this.getPointAt( d / divisions ) ); + function getObjectMaterial( object ) { - } + return object.material instanceof THREE.MeshFaceMaterial + ? object.material.materials[ 0 ] + : object.material; - return pts; + }; }; -// Get total curve arc length +// File:src/renderers/webgl/plugins/SpritePlugin.js -THREE.Curve.prototype.getLength = function () { +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ - var lengths = this.getLengths(); - return lengths[ lengths.length - 1 ]; +THREE.SpritePlugin = function ( renderer, sprites ) { -}; + var gl = renderer.context; -// Get list of cumulative segment lengths + var vertexBuffer, elementBuffer; + var program, attributes, uniforms; -THREE.Curve.prototype.getLengths = function ( divisions ) { + var texture; + + var init = function () { - if ( ! divisions ) divisions = (this.__arcLengthDivisions) ? (this.__arcLengthDivisions): 200; + var vertices = new Float32Array( [ + - 0.5, - 0.5, 0, 0, + 0.5, - 0.5, 1, 0, + 0.5, 0.5, 1, 1, + - 0.5, 0.5, 0, 1 + ] ); - if ( this.cacheArcLengths - && ( this.cacheArcLengths.length == divisions + 1 ) - && ! this.needsUpdate) { + var faces = new Uint16Array( [ + 0, 1, 2, + 0, 2, 3 + ] ); - //console.log( "cached", this.cacheArcLengths ); - return this.cacheArcLengths; + vertexBuffer = gl.createBuffer(); + elementBuffer = gl.createBuffer(); - } + gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); + gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW ); - this.needsUpdate = false; + gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); + gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW ); - var cache = []; - var current, last = this.getPoint( 0 ); - var p, sum = 0; + program = createProgram(); - cache.push( 0 ); + attributes = { + position: gl.getAttribLocation ( program, 'position' ), + uv: gl.getAttribLocation ( program, 'uv' ) + }; - for ( p = 1; p <= divisions; p ++ ) { + uniforms = { + uvOffset: gl.getUniformLocation( program, 'uvOffset' ), + uvScale: gl.getUniformLocation( program, 'uvScale' ), - current = this.getPoint ( p / divisions ); - sum += current.distanceTo( last ); - cache.push( sum ); - last = current; + rotation: gl.getUniformLocation( program, 'rotation' ), + scale: gl.getUniformLocation( program, 'scale' ), - } + color: gl.getUniformLocation( program, 'color' ), + map: gl.getUniformLocation( program, 'map' ), + opacity: gl.getUniformLocation( program, 'opacity' ), - this.cacheArcLengths = cache; + modelViewMatrix: gl.getUniformLocation( program, 'modelViewMatrix' ), + projectionMatrix: gl.getUniformLocation( program, 'projectionMatrix' ), - return cache; // { sums: cache, sum:sum }; Sum is in the last element. + fogType: gl.getUniformLocation( program, 'fogType' ), + fogDensity: gl.getUniformLocation( program, 'fogDensity' ), + fogNear: gl.getUniformLocation( program, 'fogNear' ), + fogFar: gl.getUniformLocation( program, 'fogFar' ), + fogColor: gl.getUniformLocation( program, 'fogColor' ), -}; + alphaTest: gl.getUniformLocation( program, 'alphaTest' ) + }; + var canvas = document.createElement( 'canvas' ); + canvas.width = 8; + canvas.height = 8; -THREE.Curve.prototype.updateArcLengths = function() { - this.needsUpdate = true; - this.getLengths(); -}; + var context = canvas.getContext( '2d' ); + context.fillStyle = 'white'; + context.fillRect( 0, 0, 8, 8 ); -// Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equi distance + texture = new THREE.Texture( canvas ); + texture.needsUpdate = true; -THREE.Curve.prototype.getUtoTmapping = function ( u, distance ) { + }; - var arcLengths = this.getLengths(); + this.render = function ( scene, camera ) { - var i = 0, il = arcLengths.length; + if ( sprites.length === 0 ) return; - var targetArcLength; // The targeted u distance value to get + // setup gl - if ( distance ) { + if ( program === undefined ) { - targetArcLength = distance; + init(); - } else { + } - targetArcLength = u * arcLengths[ il - 1 ]; + gl.useProgram( program ); - } + gl.enableVertexAttribArray( attributes.position ); + gl.enableVertexAttribArray( attributes.uv ); - //var time = Date.now(); + gl.disable( gl.CULL_FACE ); + gl.enable( gl.BLEND ); - // binary search for the index with largest value smaller than target u distance + gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); + gl.vertexAttribPointer( attributes.position, 2, gl.FLOAT, false, 2 * 8, 0 ); + gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 ); - var low = 0, high = il - 1, comparison; + gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); - while ( low <= high ) { + gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); - i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats + gl.activeTexture( gl.TEXTURE0 ); + gl.uniform1i( uniforms.map, 0 ); - comparison = arcLengths[ i ] - targetArcLength; + var oldFogType = 0; + var sceneFogType = 0; + var fog = scene.fog; - if ( comparison < 0 ) { + if ( fog ) { - low = i + 1; - continue; + gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b ); - } else if ( comparison > 0 ) { + if ( fog instanceof THREE.Fog ) { - high = i - 1; - continue; + gl.uniform1f( uniforms.fogNear, fog.near ); + gl.uniform1f( uniforms.fogFar, fog.far ); - } else { + gl.uniform1i( uniforms.fogType, 1 ); + oldFogType = 1; + sceneFogType = 1; - high = i; - break; + } else if ( fog instanceof THREE.FogExp2 ) { - // DONE + gl.uniform1f( uniforms.fogDensity, fog.density ); - } + gl.uniform1i( uniforms.fogType, 2 ); + oldFogType = 2; + sceneFogType = 2; - } + } - i = high; + } else { - //console.log('b' , i, low, high, Date.now()- time); + gl.uniform1i( uniforms.fogType, 0 ); + oldFogType = 0; + sceneFogType = 0; - if ( arcLengths[ i ] == targetArcLength ) { + } - var t = i / ( il - 1 ); - return t; - } + // update positions and sort - // we could get finer grain at lengths, or use simple interpolatation between two points + for ( var i = 0, l = sprites.length; i < l; i ++ ) { - var lengthBefore = arcLengths[ i ]; - var lengthAfter = arcLengths[ i + 1 ]; + var sprite = sprites[ i ]; - var segmentLength = lengthAfter - lengthBefore; + sprite._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld ); - // determine where we are between the 'before' and 'after' points + if ( sprite.renderDepth === null ) { - var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; + sprite.z = - sprite._modelViewMatrix.elements[ 14 ]; - // add that fractional amount to t + } else { - var t = ( i + segmentFraction ) / ( il -1 ); + sprite.z = sprite.renderDepth; - return t; + } -}; + } -// Returns a unit vector tangent at t -// In case any sub curve does not implement its tangent derivation, -// 2 points a small delta apart will be used to find its gradient -// which seems to give a reasonable approximation + sprites.sort( painterSortStable ); -THREE.Curve.prototype.getTangent = function( t ) { + // render all sprites - var delta = 0.0001; - var t1 = t - delta; - var t2 = t + delta; + var scale = []; - // Capping in case of danger + for ( var i = 0, l = sprites.length; i < l; i ++ ) { - if ( t1 < 0 ) t1 = 0; - if ( t2 > 1 ) t2 = 1; - - var pt1 = this.getPoint( t1 ); - var pt2 = this.getPoint( t2 ); + var sprite = sprites[ i ]; + var material = sprite.material; - var vec = pt2.clone().sub(pt1); - return vec.normalize(); + gl.uniform1f( uniforms.alphaTest, material.alphaTest ); + gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite._modelViewMatrix.elements ); -}; + scale[ 0 ] = sprite.scale.x; + scale[ 1 ] = sprite.scale.y; + var fogType = 0; -THREE.Curve.prototype.getTangentAt = function ( u ) { + if ( scene.fog && material.fog ) { - var t = this.getUtoTmapping( u ); - return this.getTangent( t ); + fogType = sceneFogType; -}; + } + if ( oldFogType !== fogType ) { + gl.uniform1i( uniforms.fogType, fogType ); + oldFogType = fogType; + } + if ( material.map !== null ) { -/************************************************************** - * Utils - **************************************************************/ + gl.uniform2f( uniforms.uvOffset, material.map.offset.x, material.map.offset.y ); + gl.uniform2f( uniforms.uvScale, material.map.repeat.x, material.map.repeat.y ); -THREE.Curve.Utils = { + } else { - tangentQuadraticBezier: function ( t, p0, p1, p2 ) { + gl.uniform2f( uniforms.uvOffset, 0, 0 ); + gl.uniform2f( uniforms.uvScale, 1, 1 ); - return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 ); + } - }, + gl.uniform1f( uniforms.opacity, material.opacity ); + gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b ); - // Puay Bing, thanks for helping with this derivative! + gl.uniform1f( uniforms.rotation, material.rotation ); + gl.uniform2fv( uniforms.scale, scale ); - tangentCubicBezier: function (t, p0, p1, p2, p3 ) { + renderer.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); + renderer.setDepthTest( material.depthTest ); + renderer.setDepthWrite( material.depthWrite ); - return - 3 * p0 * (1 - t) * (1 - t) + - 3 * p1 * (1 - t) * (1-t) - 6 *t *p1 * (1-t) + - 6 * t * p2 * (1-t) - 3 * t * t * p2 + - 3 * t * t * p3; - }, + if ( material.map && material.map.image && material.map.image.width ) { + renderer.setTexture( material.map, 0 ); - tangentSpline: function ( t, p0, p1, p2, p3 ) { + } else { - // To check if my formulas are correct + renderer.setTexture( texture, 0 ); - var h00 = 6 * t * t - 6 * t; // derived from 2t^3 − 3t^2 + 1 - var h10 = 3 * t * t - 4 * t + 1; // t^3 − 2t^2 + t - var h01 = - 6 * t * t + 6 * t; // − 2t3 + 3t2 - var h11 = 3 * t * t - 2 * t; // t3 − t2 + } - return h00 + h10 + h01 + h11; + gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - }, + } - // Catmull-Rom + // restore gl - interpolate: function( p0, p1, p2, p3, t ) { + gl.enable( gl.CULL_FACE ); + + renderer.resetGLState(); - var v0 = ( p2 - p0 ) * 0.5; - var v1 = ( p3 - p1 ) * 0.5; - var t2 = t * t; - var t3 = t * t2; - return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; + }; - } + function createProgram () { -}; + var program = gl.createProgram(); + var vertexShader = gl.createShader( gl.VERTEX_SHADER ); + var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER ); -// TODO: Transformation for Curves? + gl.shaderSource( vertexShader, [ -/************************************************************** - * 3D Curves - **************************************************************/ + 'precision ' + renderer.getPrecision() + ' float;', -// A Factory method for creating new curve subclasses + 'uniform mat4 modelViewMatrix;', + 'uniform mat4 projectionMatrix;', + 'uniform float rotation;', + 'uniform vec2 scale;', + 'uniform vec2 uvOffset;', + 'uniform vec2 uvScale;', -THREE.Curve.create = function ( constructor, getPointFunc ) { + 'attribute vec2 position;', + 'attribute vec2 uv;', - constructor.prototype = Object.create( THREE.Curve.prototype ); - constructor.prototype.getPoint = getPointFunc; + 'varying vec2 vUV;', - return constructor; + 'void main() {', -}; + 'vUV = uvOffset + uv * uvScale;', -// File:src/extras/core/CurvePath.js + 'vec2 alignedPosition = position * scale;', -/** - * @author zz85 / http://www.lab4games.net/zz85/blog - * - **/ + 'vec2 rotatedPosition;', + 'rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;', + 'rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;', -/************************************************************** - * Curved Path - a curve path is simply a array of connected - * curves, but retains the api of a curve - **************************************************************/ + 'vec4 finalPosition;', -THREE.CurvePath = function () { + 'finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );', + 'finalPosition.xy += rotatedPosition;', + 'finalPosition = projectionMatrix * finalPosition;', - this.curves = []; - this.bends = []; - - this.autoClose = false; // Automatically closes the path -}; + 'gl_Position = finalPosition;', -THREE.CurvePath.prototype = Object.create( THREE.Curve.prototype ); + '}' -THREE.CurvePath.prototype.add = function ( curve ) { + ].join( '\n' ) ); - this.curves.push( curve ); + gl.shaderSource( fragmentShader, [ -}; + 'precision ' + renderer.getPrecision() + ' float;', -THREE.CurvePath.prototype.checkConnection = function() { - // TODO - // If the ending of curve is not connected to the starting - // or the next curve, then, this is not a real path -}; + 'uniform vec3 color;', + 'uniform sampler2D map;', + 'uniform float opacity;', -THREE.CurvePath.prototype.closePath = function() { - // TODO Test - // and verify for vector3 (needs to implement equals) - // Add a line curve if start and end of lines are not connected - var startPoint = this.curves[0].getPoint(0); - var endPoint = this.curves[this.curves.length-1].getPoint(1); - - if (! startPoint.equals(endPoint)) { - this.curves.push( new THREE.LineCurve(endPoint, startPoint) ); - } - -}; + 'uniform int fogType;', + 'uniform vec3 fogColor;', + 'uniform float fogDensity;', + 'uniform float fogNear;', + 'uniform float fogFar;', + 'uniform float alphaTest;', -// To get accurate point with reference to -// entire path distance at time t, -// following has to be done: + 'varying vec2 vUV;', -// 1. Length of each sub path have to be known -// 2. Locate and identify type of curve -// 3. Get t for the curve -// 4. Return curve.getPointAt(t') + 'void main() {', -THREE.CurvePath.prototype.getPoint = function( t ) { + 'vec4 texture = texture2D( map, vUV );', - var d = t * this.getLength(); - var curveLengths = this.getCurveLengths(); - var i = 0, diff, curve; + 'if ( texture.a < alphaTest ) discard;', - // To think about boundaries points. + 'gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );', - while ( i < curveLengths.length ) { + 'if ( fogType > 0 ) {', - if ( curveLengths[ i ] >= d ) { + 'float depth = gl_FragCoord.z / gl_FragCoord.w;', + 'float fogFactor = 0.0;', - diff = curveLengths[ i ] - d; - curve = this.curves[ i ]; + 'if ( fogType == 1 ) {', - var u = 1 - diff / curve.getLength(); + 'fogFactor = smoothstep( fogNear, fogFar, depth );', - return curve.getPointAt( u ); + '} else {', - break; - } + 'const float LOG2 = 1.442695;', + 'float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );', + 'fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );', - i ++; + '}', - } + 'gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );', - return null; + '}', - // loop where sum != 0, sum > d , sum+1 maxX ) maxX = p.x; - else if ( p.x < minX ) minX = p.x; +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author Daosheng Mu / https://github.com/DaoshengMu/ + */ - if ( p.y > maxY ) maxY = p.y; - else if ( p.y < minY ) minY = p.y; +THREE.ImageUtils = { - if ( v3 ) { + crossOrigin: undefined, - if ( p.z > maxZ ) maxZ = p.z; - else if ( p.z < minZ ) minZ = p.z; + loadTexture: function ( url, mapping, onLoad, onError ) { - } + var loader = new THREE.ImageLoader(); + loader.crossOrigin = this.crossOrigin; - sum.add( p ); + var texture = new THREE.Texture( undefined, mapping ); - } + loader.load( url, function ( image ) { - var ret = { + texture.image = image; + texture.needsUpdate = true; - minX: minX, - minY: minY, - maxX: maxX, - maxY: maxY + if ( onLoad ) onLoad( texture ); - }; + }, undefined, function ( event ) { - if ( v3 ) { + if ( onError ) onError( event ); - ret.maxZ = maxZ; - ret.minZ = minZ; + } ); - } + texture.sourceFile = url; - return ret; + return texture; -}; + }, -/************************************************************** - * Create Geometries Helpers - **************************************************************/ + loadTextureCube: function ( array, mapping, onLoad, onError ) { -/// Generate geometry from path points (for Line or Points objects) + var images = []; -THREE.CurvePath.prototype.createPointsGeometry = function( divisions ) { + var loader = new THREE.ImageLoader(); + loader.crossOrigin = this.crossOrigin; - var pts = this.getPoints( divisions, true ); - return this.createGeometry( pts ); + var texture = new THREE.CubeTexture( images, mapping ); -}; + // no flipping needed for cube textures -// Generate geometry from equidistance sampling along the path + texture.flipY = false; -THREE.CurvePath.prototype.createSpacedPointsGeometry = function( divisions ) { + var loaded = 0; - var pts = this.getSpacedPoints( divisions, true ); - return this.createGeometry( pts ); + var loadTexture = function ( i ) { -}; + loader.load( array[ i ], function ( image ) { -THREE.CurvePath.prototype.createGeometry = function( points ) { + texture.images[ i ] = image; - var geometry = new THREE.Geometry(); + loaded += 1; - for ( var i = 0; i < points.length; i ++ ) { + if ( loaded === 6 ) { - geometry.vertices.push( new THREE.Vector3( points[ i ].x, points[ i ].y, points[ i ].z || 0) ); + texture.needsUpdate = true; - } + if ( onLoad ) onLoad( texture ); - return geometry; + } -}; + } ); + } -/************************************************************** - * Bend / Wrap Helper Methods - **************************************************************/ + for ( var i = 0, il = array.length; i < il; ++ i ) { -// Wrap path / Bend modifiers? + loadTexture( i ); -THREE.CurvePath.prototype.addWrapPath = function ( bendpath ) { + } - this.bends.push( bendpath ); + return texture; -}; + }, -THREE.CurvePath.prototype.getTransformedPoints = function( segments, bends ) { + loadCompressedTexture: function () { - var oldPts = this.getPoints( segments ); // getPoints getSpacedPoints - var i, il; + console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' ) - if ( ! bends ) { + }, - bends = this.bends; + loadCompressedTextureCube: function () { - } + console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' ) - for ( i = 0, il = bends.length; i < il; i ++ ) { + }, - oldPts = this.getWrapPoints( oldPts, bends[ i ] ); + getNormalMap: function ( image, depth ) { - } + // Adapted from http://www.paulbrunt.co.uk/lab/heightnormal/ - return oldPts; + var cross = function ( a, b ) { -}; + return [ a[ 1 ] * b[ 2 ] - a[ 2 ] * b[ 1 ], a[ 2 ] * b[ 0 ] - a[ 0 ] * b[ 2 ], a[ 0 ] * b[ 1 ] - a[ 1 ] * b[ 0 ] ]; -THREE.CurvePath.prototype.getTransformedSpacedPoints = function( segments, bends ) { + } - var oldPts = this.getSpacedPoints( segments ); + var subtract = function ( a, b ) { - var i, il; + return [ a[ 0 ] - b[ 0 ], a[ 1 ] - b[ 1 ], a[ 2 ] - b[ 2 ] ]; - if ( ! bends ) { + } - bends = this.bends; + var normalize = function ( a ) { - } + var l = Math.sqrt( a[ 0 ] * a[ 0 ] + a[ 1 ] * a[ 1 ] + a[ 2 ] * a[ 2 ] ); + return [ a[ 0 ] / l, a[ 1 ] / l, a[ 2 ] / l ]; - for ( i = 0, il = bends.length; i < il; i ++ ) { + } - oldPts = this.getWrapPoints( oldPts, bends[ i ] ); + depth = depth | 1; - } + var width = image.width; + var height = image.height; - return oldPts; + var canvas = document.createElement( 'canvas' ); + canvas.width = width; + canvas.height = height; -}; + var context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0 ); -// This returns getPoints() bend/wrapped around the contour of a path. -// Read http://www.planetclegg.com/projects/WarpingTextToSplines.html + var data = context.getImageData( 0, 0, width, height ).data; + var imageData = context.createImageData( width, height ); + var output = imageData.data; -THREE.CurvePath.prototype.getWrapPoints = function ( oldPts, path ) { + for ( var x = 0; x < width; x ++ ) { - var bounds = this.getBoundingBox(); + for ( var y = 0; y < height; y ++ ) { - var i, il, p, oldX, oldY, xNorm; + var ly = y - 1 < 0 ? 0 : y - 1; + var uy = y + 1 > height - 1 ? height - 1 : y + 1; + var lx = x - 1 < 0 ? 0 : x - 1; + var ux = x + 1 > width - 1 ? width - 1 : x + 1; - for ( i = 0, il = oldPts.length; i < il; i ++ ) { + var points = []; + var origin = [ 0, 0, data[ ( y * width + x ) * 4 ] / 255 * depth ]; + points.push( [ - 1, 0, data[ ( y * width + lx ) * 4 ] / 255 * depth ] ); + points.push( [ - 1, - 1, data[ ( ly * width + lx ) * 4 ] / 255 * depth ] ); + points.push( [ 0, - 1, data[ ( ly * width + x ) * 4 ] / 255 * depth ] ); + points.push( [ 1, - 1, data[ ( ly * width + ux ) * 4 ] / 255 * depth ] ); + points.push( [ 1, 0, data[ ( y * width + ux ) * 4 ] / 255 * depth ] ); + points.push( [ 1, 1, data[ ( uy * width + ux ) * 4 ] / 255 * depth ] ); + points.push( [ 0, 1, data[ ( uy * width + x ) * 4 ] / 255 * depth ] ); + points.push( [ - 1, 1, data[ ( uy * width + lx ) * 4 ] / 255 * depth ] ); - p = oldPts[ i ]; + var normals = []; + var num_points = points.length; - oldX = p.x; - oldY = p.y; + for ( var i = 0; i < num_points; i ++ ) { - xNorm = oldX / bounds.maxX; + var v1 = points[ i ]; + var v2 = points[ ( i + 1 ) % num_points ]; + v1 = subtract( v1, origin ); + v2 = subtract( v2, origin ); + normals.push( normalize( cross( v1, v2 ) ) ); - // If using actual distance, for length > path, requires line extrusions - //xNorm = path.getUtoTmapping(xNorm, oldX); // 3 styles. 1) wrap stretched. 2) wrap stretch by arc length 3) warp by actual distance + } - xNorm = path.getUtoTmapping( xNorm, oldX ); + var normal = [ 0, 0, 0 ]; - // check for out of bounds? + for ( var i = 0; i < normals.length; i ++ ) { - var pathPt = path.getPoint( xNorm ); - var normal = path.getTangent( xNorm ); - normal.set( - normal.y, normal.x ).multiplyScalar( oldY ); + normal[ 0 ] += normals[ i ][ 0 ]; + normal[ 1 ] += normals[ i ][ 1 ]; + normal[ 2 ] += normals[ i ][ 2 ]; - p.x = pathPt.x + normal.x; - p.y = pathPt.y + normal.y; + } - } + normal[ 0 ] /= normals.length; + normal[ 1 ] /= normals.length; + normal[ 2 ] /= normals.length; - return oldPts; + var idx = ( y * width + x ) * 4; -}; + output[ idx ] = ( ( normal[ 0 ] + 1.0 ) / 2.0 * 255 ) | 0; + output[ idx + 1 ] = ( ( normal[ 1 ] + 1.0 ) / 2.0 * 255 ) | 0; + output[ idx + 2 ] = ( normal[ 2 ] * 255 ) | 0; + output[ idx + 3 ] = 255; + } -// File:src/extras/core/Gyroscope.js + } -/** - * @author alteredq / http://alteredqualia.com/ - */ - -THREE.Gyroscope = function () { - - THREE.Object3D.call( this ); - -}; - -THREE.Gyroscope.prototype = Object.create( THREE.Object3D.prototype ); - -THREE.Gyroscope.prototype.updateMatrixWorld = function ( force ) { - - this.matrixAutoUpdate && this.updateMatrix(); - - // update matrixWorld - - if ( this.matrixWorldNeedsUpdate || force ) { - - if ( this.parent ) { - - this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); - - this.matrixWorld.decompose( this.translationWorld, this.quaternionWorld, this.scaleWorld ); - this.matrix.decompose( this.translationObject, this.quaternionObject, this.scaleObject ); - - this.matrixWorld.compose( this.translationWorld, this.quaternionObject, this.scaleWorld ); - - - } else { - - this.matrixWorld.copy( this.matrix ); - - } - - - this.matrixWorldNeedsUpdate = false; - - force = true; - - } - - // update children - - for ( var i = 0, l = this.children.length; i < l; i ++ ) { - - this.children[ i ].updateMatrixWorld( force ); - - } - -}; - -THREE.Gyroscope.prototype.translationWorld = new THREE.Vector3(); -THREE.Gyroscope.prototype.translationObject = new THREE.Vector3(); -THREE.Gyroscope.prototype.quaternionWorld = new THREE.Quaternion(); -THREE.Gyroscope.prototype.quaternionObject = new THREE.Quaternion(); -THREE.Gyroscope.prototype.scaleWorld = new THREE.Vector3(); -THREE.Gyroscope.prototype.scaleObject = new THREE.Vector3(); - + context.putImageData( imageData, 0, 0 ); -// File:src/extras/core/Path.js + return canvas; -/** - * @author zz85 / http://www.lab4games.net/zz85/blog - * Creates free form 2d path using series of points, lines or curves. - * - **/ + }, -THREE.Path = function ( points ) { + generateDataTexture: function ( width, height, color ) { - THREE.CurvePath.call(this); + var size = width * height; + var data = new Uint8Array( 3 * size ); - this.actions = []; + var r = Math.floor( color.r * 255 ); + var g = Math.floor( color.g * 255 ); + var b = Math.floor( color.b * 255 ); - if ( points ) { + for ( var i = 0; i < size; i ++ ) { - this.fromPoints( points ); + data[ i * 3 ] = r; + data[ i * 3 + 1 ] = g; + data[ i * 3 + 2 ] = b; - } + } -}; + var texture = new THREE.DataTexture( data, width, height, THREE.RGBFormat ); + texture.needsUpdate = true; -THREE.Path.prototype = Object.create( THREE.CurvePath.prototype ); + return texture; -THREE.PathActions = { + } - MOVE_TO: 'moveTo', - LINE_TO: 'lineTo', - QUADRATIC_CURVE_TO: 'quadraticCurveTo', // Bezier quadratic curve - BEZIER_CURVE_TO: 'bezierCurveTo', // Bezier cubic curve - CSPLINE_THRU: 'splineThru', // Catmull-rom spline - ARC: 'arc', // Circle - ELLIPSE: 'ellipse' }; -// TODO Clean up PATH API +// File:src/extras/SceneUtils.js -// Create path using straight lines to connect all points -// - vectors: array of Vector2 +/** + * @author alteredq / http://alteredqualia.com/ + */ -THREE.Path.prototype.fromPoints = function ( vectors ) { - - this.moveTo( vectors[ 0 ].x, vectors[ 0 ].y ); +THREE.SceneUtils = { - for ( var v = 1, vlen = vectors.length; v < vlen; v ++ ) { + createMultiMaterialObject: function ( geometry, materials ) { - this.lineTo( vectors[ v ].x, vectors[ v ].y ); + var group = new THREE.Object3D(); - }; + for ( var i = 0, l = materials.length; i < l; i ++ ) { -}; + group.add( new THREE.Mesh( geometry, materials[ i ] ) ); -// startPath() endPath()? + } -THREE.Path.prototype.moveTo = function ( x, y ) { + return group; - var args = Array.prototype.slice.call( arguments ); - this.actions.push( { action: THREE.PathActions.MOVE_TO, args: args } ); + }, -}; + detach: function ( child, parent, scene ) { -THREE.Path.prototype.lineTo = function ( x, y ) { + child.applyMatrix( parent.matrixWorld ); + parent.remove( child ); + scene.add( child ); - var args = Array.prototype.slice.call( arguments ); + }, - var lastargs = this.actions[ this.actions.length - 1 ].args; + attach: function ( child, scene, parent ) { - var x0 = lastargs[ lastargs.length - 2 ]; - var y0 = lastargs[ lastargs.length - 1 ]; + var matrixWorldInverse = new THREE.Matrix4(); + matrixWorldInverse.getInverse( parent.matrixWorld ); + child.applyMatrix( matrixWorldInverse ); - var curve = new THREE.LineCurve( new THREE.Vector2( x0, y0 ), new THREE.Vector2( x, y ) ); - this.curves.push( curve ); + scene.remove( child ); + parent.add( child ); - this.actions.push( { action: THREE.PathActions.LINE_TO, args: args } ); + } }; -THREE.Path.prototype.quadraticCurveTo = function( aCPx, aCPy, aX, aY ) { +// File:src/extras/FontUtils.js - var args = Array.prototype.slice.call( arguments ); +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * @author alteredq / http://alteredqualia.com/ + * + * For Text operations in three.js (See TextGeometry) + * + * It uses techniques used in: + * + * typeface.js and canvastext + * For converting fonts and rendering with javascript + * http://typeface.neocracy.org + * + * Triangulation ported from AS3 + * Simple Polygon Triangulation + * http://actionsnippet.com/?p=1462 + * + * A Method to triangulate shapes with holes + * http://www.sakri.net/blog/2009/06/12/an-approach-to-triangulating-polygons-with-holes/ + * + */ - var lastargs = this.actions[ this.actions.length - 1 ].args; +THREE.FontUtils = { - var x0 = lastargs[ lastargs.length - 2 ]; - var y0 = lastargs[ lastargs.length - 1 ]; + faces: {}, - var curve = new THREE.QuadraticBezierCurve( new THREE.Vector2( x0, y0 ), - new THREE.Vector2( aCPx, aCPy ), - new THREE.Vector2( aX, aY ) ); - this.curves.push( curve ); + // Just for now. face[weight][style] - this.actions.push( { action: THREE.PathActions.QUADRATIC_CURVE_TO, args: args } ); + face: 'helvetiker', + weight: 'normal', + style: 'normal', + size: 150, + divisions: 10, -}; + getFace: function () { -THREE.Path.prototype.bezierCurveTo = function( aCP1x, aCP1y, - aCP2x, aCP2y, - aX, aY ) { + try { - var args = Array.prototype.slice.call( arguments ); + return this.faces[ this.face ][ this.weight ][ this.style ]; - var lastargs = this.actions[ this.actions.length - 1 ].args; + } catch (e) { - var x0 = lastargs[ lastargs.length - 2 ]; - var y0 = lastargs[ lastargs.length - 1 ]; + throw "The font " + this.face + " with " + this.weight + " weight and " + this.style + " style is missing." - var curve = new THREE.CubicBezierCurve( new THREE.Vector2( x0, y0 ), - new THREE.Vector2( aCP1x, aCP1y ), - new THREE.Vector2( aCP2x, aCP2y ), - new THREE.Vector2( aX, aY ) ); - this.curves.push( curve ); + }; - this.actions.push( { action: THREE.PathActions.BEZIER_CURVE_TO, args: args } ); + }, -}; + loadFace: function ( data ) { -THREE.Path.prototype.splineThru = function( pts /*Array of Vector*/ ) { + var family = data.familyName.toLowerCase(); - var args = Array.prototype.slice.call( arguments ); - var lastargs = this.actions[ this.actions.length - 1 ].args; + var ThreeFont = this; - var x0 = lastargs[ lastargs.length - 2 ]; - var y0 = lastargs[ lastargs.length - 1 ]; -//--- - var npts = [ new THREE.Vector2( x0, y0 ) ]; - Array.prototype.push.apply( npts, pts ); + ThreeFont.faces[ family ] = ThreeFont.faces[ family ] || {}; - var curve = new THREE.SplineCurve( npts ); - this.curves.push( curve ); + ThreeFont.faces[ family ][ data.cssFontWeight ] = ThreeFont.faces[ family ][ data.cssFontWeight ] || {}; + ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data; - this.actions.push( { action: THREE.PathActions.CSPLINE_THRU, args: args } ); + var face = ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data; -}; + return data; -// FUTURE: Change the API or follow canvas API? + }, -THREE.Path.prototype.arc = function ( aX, aY, aRadius, - aStartAngle, aEndAngle, aClockwise ) { + drawText: function ( text ) { - var lastargs = this.actions[ this.actions.length - 1].args; - var x0 = lastargs[ lastargs.length - 2 ]; - var y0 = lastargs[ lastargs.length - 1 ]; + var characterPts = [], allPts = []; - this.absarc(aX + x0, aY + y0, aRadius, - aStartAngle, aEndAngle, aClockwise ); + // RenderText - }; + var i, p, + face = this.getFace(), + scale = this.size / face.resolution, + offset = 0, + chars = String( text ).split( '' ), + length = chars.length; - THREE.Path.prototype.absarc = function ( aX, aY, aRadius, - aStartAngle, aEndAngle, aClockwise ) { - this.absellipse(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise); - }; + var fontPaths = []; -THREE.Path.prototype.ellipse = function ( aX, aY, xRadius, yRadius, - aStartAngle, aEndAngle, aClockwise ) { + for ( i = 0; i < length; i ++ ) { - var lastargs = this.actions[ this.actions.length - 1].args; - var x0 = lastargs[ lastargs.length - 2 ]; - var y0 = lastargs[ lastargs.length - 1 ]; + var path = new THREE.Path(); - this.absellipse(aX + x0, aY + y0, xRadius, yRadius, - aStartAngle, aEndAngle, aClockwise ); + var ret = this.extractGlyphPoints( chars[ i ], face, scale, offset, path ); + offset += ret.offset; - }; + fontPaths.push( ret.path ); + } -THREE.Path.prototype.absellipse = function ( aX, aY, xRadius, yRadius, - aStartAngle, aEndAngle, aClockwise ) { + // get the width - var args = Array.prototype.slice.call( arguments ); - var curve = new THREE.EllipseCurve( aX, aY, xRadius, yRadius, - aStartAngle, aEndAngle, aClockwise ); - this.curves.push( curve ); + var width = offset / 2; + // + // for ( p = 0; p < allPts.length; p++ ) { + // + // allPts[ p ].x -= width; + // + // } - var lastPoint = curve.getPoint(1); - args.push(lastPoint.x); - args.push(lastPoint.y); + //var extract = this.extractPoints( allPts, characterPts ); + //extract.contour = allPts; - this.actions.push( { action: THREE.PathActions.ELLIPSE, args: args } ); + //extract.paths = fontPaths; + //extract.offset = width; - }; + return { paths: fontPaths, offset: width }; -THREE.Path.prototype.getSpacedPoints = function ( divisions, closedPath ) { + }, - if ( ! divisions ) divisions = 40; - var points = []; - for ( var i = 0; i < divisions; i ++ ) { - points.push( this.getPoint( i / divisions ) ); + extractGlyphPoints: function ( c, face, scale, offset, path ) { - //if( !this.getPoint( i / divisions ) ) throw "DIE"; + var pts = []; - } + var i, i2, divisions, + outline, action, length, + scaleX, scaleY, + x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2, + laste, + glyph = face.glyphs[ c ] || face.glyphs[ '?' ]; - // if ( closedPath ) { - // - // points.push( points[ 0 ] ); - // - // } + if ( ! glyph ) return; - return points; + if ( glyph.o ) { -}; + outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) ); + length = outline.length; -/* Return an array of vectors based on contour of the path */ + scaleX = scale; + scaleY = scale; -THREE.Path.prototype.getPoints = function( divisions, closedPath ) { + for ( i = 0; i < length; ) { - if (this.useSpacedPoints) { - console.log('tata'); - return this.getSpacedPoints( divisions, closedPath ); - } + action = outline[ i ++ ]; - divisions = divisions || 12; + //console.log( action ); - var points = []; + switch ( action ) { - var i, il, item, action, args; - var cpx, cpy, cpx2, cpy2, cpx1, cpy1, cpx0, cpy0, - laste, j, - t, tx, ty; + case 'm': - for ( i = 0, il = this.actions.length; i < il; i ++ ) { + // Move To - item = this.actions[ i ]; + x = outline[ i ++ ] * scaleX + offset; + y = outline[ i ++ ] * scaleY; - action = item.action; - args = item.args; + path.moveTo( x, y ); + break; - switch( action ) { + case 'l': - case THREE.PathActions.MOVE_TO: + // Line To - points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) ); + x = outline[ i ++ ] * scaleX + offset; + y = outline[ i ++ ] * scaleY; + path.lineTo( x,y ); + break; - break; + case 'q': - case THREE.PathActions.LINE_TO: + // QuadraticCurveTo - points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) ); + cpx = outline[ i ++ ] * scaleX + offset; + cpy = outline[ i ++ ] * scaleY; + cpx1 = outline[ i ++ ] * scaleX + offset; + cpy1 = outline[ i ++ ] * scaleY; - break; + path.quadraticCurveTo( cpx1, cpy1, cpx, cpy ); - case THREE.PathActions.QUADRATIC_CURVE_TO: + laste = pts[ pts.length - 1 ]; - cpx = args[ 2 ]; - cpy = args[ 3 ]; + if ( laste ) { - cpx1 = args[ 0 ]; - cpy1 = args[ 1 ]; + cpx0 = laste.x; + cpy0 = laste.y; - if ( points.length > 0 ) { + for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) { - laste = points[ points.length - 1 ]; + var t = i2 / divisions; + var tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx ); + var ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy ); + } - cpx0 = laste.x; - cpy0 = laste.y; + } - } else { + break; - laste = this.actions[ i - 1 ].args; + case 'b': - cpx0 = laste[ laste.length - 2 ]; - cpy0 = laste[ laste.length - 1 ]; + // Cubic Bezier Curve - } + cpx = outline[ i ++ ] * scaleX + offset; + cpy = outline[ i ++ ] * scaleY; + cpx1 = outline[ i ++ ] * scaleX + offset; + cpy1 = outline[ i ++ ] * scaleY; + cpx2 = outline[ i ++ ] * scaleX + offset; + cpy2 = outline[ i ++ ] * scaleY; - for ( j = 1; j <= divisions; j ++ ) { + path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy ); - t = j / divisions; + laste = pts[ pts.length - 1 ]; - tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx ); - ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy ); + if ( laste ) { - points.push( new THREE.Vector2( tx, ty ) ); + cpx0 = laste.x; + cpy0 = laste.y; - } + for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) { - break; + var t = i2 / divisions; + var tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx ); + var ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy ); - case THREE.PathActions.BEZIER_CURVE_TO: + } - cpx = args[ 4 ]; - cpy = args[ 5 ]; + } - cpx1 = args[ 0 ]; - cpy1 = args[ 1 ]; + break; - cpx2 = args[ 2 ]; - cpy2 = args[ 3 ]; + } - if ( points.length > 0 ) { + } + } - laste = points[ points.length - 1 ]; - cpx0 = laste.x; - cpy0 = laste.y; - } else { + return { offset: glyph.ha * scale, path:path }; + } - laste = this.actions[ i - 1 ].args; +}; - cpx0 = laste[ laste.length - 2 ]; - cpy0 = laste[ laste.length - 1 ]; - } +THREE.FontUtils.generateShapes = function ( text, parameters ) { + // Parameters - for ( j = 1; j <= divisions; j ++ ) { + parameters = parameters || {}; - t = j / divisions; + var size = parameters.size !== undefined ? parameters.size : 100; + var curveSegments = parameters.curveSegments !== undefined ? parameters.curveSegments : 4; - tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx ); - ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy ); + var font = parameters.font !== undefined ? parameters.font : 'helvetiker'; + var weight = parameters.weight !== undefined ? parameters.weight : 'normal'; + var style = parameters.style !== undefined ? parameters.style : 'normal'; - points.push( new THREE.Vector2( tx, ty ) ); + THREE.FontUtils.size = size; + THREE.FontUtils.divisions = curveSegments; - } + THREE.FontUtils.face = font; + THREE.FontUtils.weight = weight; + THREE.FontUtils.style = style; - break; + // Get a Font data json object - case THREE.PathActions.CSPLINE_THRU: + var data = THREE.FontUtils.drawText( text ); - laste = this.actions[ i - 1 ].args; + var paths = data.paths; + var shapes = []; - var last = new THREE.Vector2( laste[ laste.length - 2 ], laste[ laste.length - 1 ] ); - var spts = [ last ]; + for ( var p = 0, pl = paths.length; p < pl; p ++ ) { - var n = divisions * args[ 0 ].length; + Array.prototype.push.apply( shapes, paths[ p ].toShapes() ); - spts = spts.concat( args[ 0 ] ); + } - var spline = new THREE.SplineCurve( spts ); + return shapes; - for ( j = 1; j <= n; j ++ ) { +}; - points.push( spline.getPointAt( j / n ) ) ; - } +/** + * This code is a quick port of code written in C++ which was submitted to + * flipcode.com by John W. Ratcliff // July 22, 2000 + * See original code and more information here: + * http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml + * + * ported to actionscript by Zevan Rosser + * www.actionsnippet.com + * + * ported to javascript by Joshua Koo + * http://www.lab4games.net/zz85/blog + * + */ - break; - case THREE.PathActions.ARC: +( function ( namespace ) { - var aX = args[ 0 ], aY = args[ 1 ], - aRadius = args[ 2 ], - aStartAngle = args[ 3 ], aEndAngle = args[ 4 ], - aClockwise = !! args[ 5 ]; + var EPSILON = 0.0000000001; - var deltaAngle = aEndAngle - aStartAngle; - var angle; - var tdivisions = divisions * 2; + // takes in an contour array and returns - for ( j = 1; j <= tdivisions; j ++ ) { + var process = function ( contour, indices ) { - t = j / tdivisions; + var n = contour.length; - if ( ! aClockwise ) { + if ( n < 3 ) return null; - t = 1 - t; + var result = [], + verts = [], + vertIndices = []; - } + /* we want a counter-clockwise polygon in verts */ - angle = aStartAngle + t * deltaAngle; + var u, v, w; - tx = aX + aRadius * Math.cos( angle ); - ty = aY + aRadius * Math.sin( angle ); + if ( area( contour ) > 0.0 ) { - //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty); + for ( v = 0; v < n; v ++ ) verts[ v ] = v; - points.push( new THREE.Vector2( tx, ty ) ); + } else { - } + for ( v = 0; v < n; v ++ ) verts[ v ] = ( n - 1 ) - v; - //console.log(points); + } - break; - - case THREE.PathActions.ELLIPSE: + var nv = n; - var aX = args[ 0 ], aY = args[ 1 ], - xRadius = args[ 2 ], - yRadius = args[ 3 ], - aStartAngle = args[ 4 ], aEndAngle = args[ 5 ], - aClockwise = !! args[ 6 ]; + /* remove nv - 2 vertices, creating 1 triangle every time */ + var count = 2 * nv; /* error detection */ - var deltaAngle = aEndAngle - aStartAngle; - var angle; - var tdivisions = divisions * 2; + for ( v = nv - 1; nv > 2; ) { - for ( j = 1; j <= tdivisions; j ++ ) { + /* if we loop, it is probably a non-simple polygon */ - t = j / tdivisions; + if ( ( count -- ) <= 0 ) { - if ( ! aClockwise ) { + //** Triangulate: ERROR - probable bad polygon! - t = 1 - t; + //throw ( "Warning, unable to triangulate polygon!" ); + //return null; + // Sometimes warning is fine, especially polygons are triangulated in reverse. + console.log( 'Warning, unable to triangulate polygon!' ); - } + if ( indices ) return vertIndices; + return result; - angle = aStartAngle + t * deltaAngle; + } - tx = aX + xRadius * Math.cos( angle ); - ty = aY + yRadius * Math.sin( angle ); + /* three consecutive vertices in current polygon, */ - //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty); + u = v; if ( nv <= u ) u = 0; /* previous */ + v = u + 1; if ( nv <= v ) v = 0; /* new v */ + w = v + 1; if ( nv <= w ) w = 0; /* next */ - points.push( new THREE.Vector2( tx, ty ) ); + if ( snip( contour, u, v, w, nv, verts ) ) { - } + var a, b, c, s, t; - //console.log(points); + /* true names of the vertices */ - break; + a = verts[ u ]; + b = verts[ v ]; + c = verts[ w ]; - } // end switch + /* output Triangle */ - } + result.push( [ contour[ a ], + contour[ b ], + contour[ c ] ] ); + vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] ); - // Normalize to remove the closing point by default. - var lastPoint = points[ points.length - 1]; - var EPSILON = 0.0000000001; - if ( Math.abs(lastPoint.x - points[ 0 ].x) < EPSILON && - Math.abs(lastPoint.y - points[ 0 ].y) < EPSILON) - points.splice( points.length - 1, 1); - if ( closedPath ) { + /* remove v from the remaining polygon */ - points.push( points[ 0 ] ); + for ( s = v, t = v + 1; t < nv; s++, t++ ) { - } + verts[ s ] = verts[ t ]; - return points; + } -}; + nv --; -// -// Breaks path into shapes -// -// Assumptions (if parameter isCCW==true the opposite holds): -// - solid shapes are defined clockwise (CW) -// - holes are defined counterclockwise (CCW) -// -// If parameter noHoles==true: -// - all subPaths are regarded as solid shapes -// - definition order CW/CCW has no relevance -// + /* reset error detection counter */ -THREE.Path.prototype.toShapes = function( isCCW, noHoles ) { + count = 2 * nv; - function extractSubpaths( inActions ) { + } - var i, il, item, action, args; + } - var subPaths = [], lastPath = new THREE.Path(); + if ( indices ) return vertIndices; + return result; - for ( i = 0, il = inActions.length; i < il; i ++ ) { - - item = inActions[ i ]; + }; - args = item.args; - action = item.action; + // calculate area of the contour polygon - if ( action == THREE.PathActions.MOVE_TO ) { + var area = function ( contour ) { - if ( lastPath.actions.length != 0 ) { + var n = contour.length; + var a = 0.0; - subPaths.push( lastPath ); - lastPath = new THREE.Path(); + for ( var p = n - 1, q = 0; q < n; p = q ++ ) { - } + a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; - } + } - lastPath[ action ].apply( lastPath, args ); + return a * 0.5; - } + }; - if ( lastPath.actions.length != 0 ) { + var snip = function ( contour, u, v, w, n, verts ) { - subPaths.push( lastPath ); + var p; + var ax, ay, bx, by; + var cx, cy, px, py; - } + ax = contour[ verts[ u ] ].x; + ay = contour[ verts[ u ] ].y; - // console.log(subPaths); + bx = contour[ verts[ v ] ].x; + by = contour[ verts[ v ] ].y; - return subPaths; - } + cx = contour[ verts[ w ] ].x; + cy = contour[ verts[ w ] ].y; - function toShapesNoHoles( inSubpaths ) { + if ( EPSILON > ( ( ( bx - ax ) * ( cy - ay ) ) - ( ( by - ay ) * ( cx - ax ) ) ) ) return false; - var shapes = []; + var aX, aY, bX, bY, cX, cY; + var apx, apy, bpx, bpy, cpx, cpy; + var cCROSSap, bCROSScp, aCROSSbp; - for ( var i = 0, il = inSubpaths.length; i < il; i ++ ) { + aX = cx - bx; aY = cy - by; + bX = ax - cx; bY = ay - cy; + cX = bx - ax; cY = by - ay; - var tmpPath = inSubpaths[ i ]; + for ( p = 0; p < n; p ++ ) { - var tmpShape = new THREE.Shape(); - tmpShape.actions = tmpPath.actions; - tmpShape.curves = tmpPath.curves; + px = contour[ verts[ p ] ].x + py = contour[ verts[ p ] ].y - shapes.push( tmpShape ); - } + if ( ( ( px === ax ) && ( py === ay ) ) || + ( ( px === bx ) && ( py === by ) ) || + ( ( px === cx ) && ( py === cy ) ) ) continue; - //console.log("shape", shapes); + apx = px - ax; apy = py - ay; + bpx = px - bx; bpy = py - by; + cpx = px - cx; cpy = py - cy; - return shapes; - }; + // see if p is inside triangle abc - function isPointInsidePolygon( inPt, inPolygon ) { - var EPSILON = 0.0000000001; + aCROSSbp = aX * bpy - aY * bpx; + cCROSSap = cX * apy - cY * apx; + bCROSScp = bX * cpy - bY * cpx; - var polyLen = inPolygon.length; + if ( ( aCROSSbp >= - EPSILON ) && ( bCROSScp >= - EPSILON ) && ( cCROSSap >= - EPSILON ) ) return false; - // inPt on polygon contour => immediate success or - // toggling of inside/outside at every single! intersection point of an edge - // with the horizontal line through inPt, left of inPt - // not counting lowerY endpoints of edges and whole edges on that line - var inside = false; - for( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) { - var edgeLowPt = inPolygon[ p ]; - var edgeHighPt = inPolygon[ q ]; + } - var edgeDx = edgeHighPt.x - edgeLowPt.x; - var edgeDy = edgeHighPt.y - edgeLowPt.y; + return true; - if ( Math.abs(edgeDy) > EPSILON ) { // not parallel - if ( edgeDy < 0 ) { - edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx; - edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy; - } - if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue; + }; - if ( inPt.y == edgeLowPt.y ) { - if ( inPt.x == edgeLowPt.x ) return true; // inPt is on contour ? - // continue; // no intersection or edgeLowPt => doesn't count !!! - } else { - var perpEdge = edgeDy * (inPt.x - edgeLowPt.x) - edgeDx * (inPt.y - edgeLowPt.y); - if ( perpEdge == 0 ) return true; // inPt is on contour ? - if ( perpEdge < 0 ) continue; - inside = ! inside; // true intersection left of inPt - } - } else { // parallel or colinear - if ( inPt.y != edgeLowPt.y ) continue; // parallel - // egde lies on the same horizontal line as inPt - if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) || - ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour ! - // continue; - } - } - return inside; - } + namespace.Triangulate = process; + namespace.Triangulate.area = area; + return namespace; - var subPaths = extractSubpaths( this.actions ); - if ( subPaths.length == 0 ) return []; +} )( THREE.FontUtils ); - if ( noHoles === true ) return toShapesNoHoles( subPaths ); +// To use the typeface.js face files, hook up the API +self._typeface_js = { faces: THREE.FontUtils.faces, loadFace: THREE.FontUtils.loadFace }; +THREE.typeface_js = self._typeface_js; +// File:src/extras/audio/Audio.js - var solid, tmpPath, tmpShape, shapes = []; +/** + * @author mrdoob / http://mrdoob.com/ + */ - if ( subPaths.length == 1) { +THREE.Audio = function ( listener ) { - tmpPath = subPaths[0]; - tmpShape = new THREE.Shape(); - tmpShape.actions = tmpPath.actions; - tmpShape.curves = tmpPath.curves; - shapes.push( tmpShape ); - return shapes; + THREE.Object3D.call( this ); - } + this.type = 'Audio'; - var holesFirst = ! THREE.Shape.Utils.isClockWise( subPaths[ 0 ].getPoints() ); - holesFirst = isCCW ? ! holesFirst : holesFirst; + this.context = listener.context; + this.source = this.context.createBufferSource(); - // console.log("Holes first", holesFirst); - - var betterShapeHoles = []; - var newShapes = []; - var newShapeHoles = []; - var mainIdx = 0; - var tmpPoints; + this.gain = this.context.createGain(); + this.gain.connect( this.context.destination ); - newShapes[mainIdx] = undefined; - newShapeHoles[mainIdx] = []; + this.panner = this.context.createPanner(); + this.panner.connect( this.gain ); - var i, il; +}; - for ( i = 0, il = subPaths.length; i < il; i ++ ) { +THREE.Audio.prototype = Object.create( THREE.Object3D.prototype ); - tmpPath = subPaths[ i ]; - tmpPoints = tmpPath.getPoints(); - solid = THREE.Shape.Utils.isClockWise( tmpPoints ); - solid = isCCW ? ! solid : solid; +THREE.Audio.prototype.load = function ( file ) { - if ( solid ) { + var scope = this; - if ( (! holesFirst ) && ( newShapes[mainIdx] ) ) mainIdx ++; + var request = new XMLHttpRequest(); + request.open( 'GET', file, true ); + request.responseType = 'arraybuffer'; + request.onload = function ( e ) { - newShapes[mainIdx] = { s: new THREE.Shape(), p: tmpPoints }; - newShapes[mainIdx].s.actions = tmpPath.actions; - newShapes[mainIdx].s.curves = tmpPath.curves; - - if ( holesFirst ) mainIdx ++; - newShapeHoles[mainIdx] = []; + scope.context.decodeAudioData( this.response, function ( buffer ) { - //console.log('cw', i); + scope.source.buffer = buffer; + scope.source.connect( scope.panner ); + scope.source.start( 0 ); - } else { + } ); - newShapeHoles[mainIdx].push( { h: tmpPath, p: tmpPoints[0] } ); + }; + request.send(); - //console.log('ccw', i); + return this; - } +}; - } +THREE.Audio.prototype.setLoop = function ( value ) { - // only Holes? -> probably all Shapes with wrong orientation - if ( ! newShapes[0] ) return toShapesNoHoles( subPaths ); + this.source.loop = value; +}; - if ( newShapes.length > 1 ) { - var ambigious = false; - var toChange = []; +THREE.Audio.prototype.setRefDistance = function ( value ) { - for (var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { - betterShapeHoles[sIdx] = []; - } - for (var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { - var sh = newShapes[sIdx]; - var sho = newShapeHoles[sIdx]; - for (var hIdx = 0; hIdx < sho.length; hIdx ++ ) { - var ho = sho[hIdx]; - var hole_unassigned = true; - for (var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) { - if ( isPointInsidePolygon( ho.p, newShapes[s2Idx].p ) ) { - if ( sIdx != s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } ); - if ( hole_unassigned ) { - hole_unassigned = false; - betterShapeHoles[s2Idx].push( ho ); - } else { - ambigious = true; - } - } - } - if ( hole_unassigned ) { betterShapeHoles[sIdx].push( ho ); } - } - } - // console.log("ambigious: ", ambigious); - if ( toChange.length > 0 ) { - // console.log("to change: ", toChange); - if (! ambigious) newShapeHoles = betterShapeHoles; - } - } + this.panner.refDistance = value; - var tmpHoles, j, jl; - for ( i = 0, il = newShapes.length; i < il; i ++ ) { - tmpShape = newShapes[i].s; - shapes.push( tmpShape ); - tmpHoles = newShapeHoles[i]; - for ( j = 0, jl = tmpHoles.length; j < jl; j ++ ) { - tmpShape.holes.push( tmpHoles[j].h ); - } - } +}; - //console.log("shape", shapes); +THREE.Audio.prototype.setRolloffFactor = function ( value ) { - return shapes; + this.panner.rolloffFactor = value; }; -// File:src/extras/core/Shape.js +THREE.Audio.prototype.updateMatrixWorld = ( function () { -/** - * @author zz85 / http://www.lab4games.net/zz85/blog - * Defines a 2d shape plane using paths. - **/ + var position = new THREE.Vector3(); -// STEP 1 Create a path. -// STEP 2 Turn path into shape. -// STEP 3 ExtrudeGeometry takes in Shape/Shapes -// STEP 3a - Extract points from each shape, turn to vertices -// STEP 3b - Triangulate each shape, add faces. + return function ( force ) { -THREE.Shape = function () { + THREE.Object3D.prototype.updateMatrixWorld.call( this, force ); - THREE.Path.apply( this, arguments ); - this.holes = []; + position.setFromMatrixPosition( this.matrixWorld ); -}; + this.panner.setPosition( position.x, position.y, position.z ); -THREE.Shape.prototype = Object.create( THREE.Path.prototype ); + }; -// Convenience method to return ExtrudeGeometry +} )(); -THREE.Shape.prototype.extrude = function ( options ) { +// File:src/extras/audio/AudioListener.js - var extruded = new THREE.ExtrudeGeometry( this, options ); - return extruded; +/** + * @author mrdoob / http://mrdoob.com/ + */ -}; +THREE.AudioListener = function () { -// Convenience method to return ShapeGeometry + THREE.Object3D.call( this ); -THREE.Shape.prototype.makeGeometry = function ( options ) { + this.type = 'AudioListener'; - var geometry = new THREE.ShapeGeometry( this, options ); - return geometry; + this.context = new ( window.AudioContext || window.webkitAudioContext )(); }; -// Get points of holes +THREE.AudioListener.prototype = Object.create( THREE.Object3D.prototype ); -THREE.Shape.prototype.getPointsHoles = function ( divisions ) { +THREE.AudioListener.prototype.updateMatrixWorld = ( function () { - var i, il = this.holes.length, holesPts = []; + var position = new THREE.Vector3(); + var quaternion = new THREE.Quaternion(); + var scale = new THREE.Vector3(); - for ( i = 0; i < il; i ++ ) { + var orientation = new THREE.Vector3(); + var velocity = new THREE.Vector3(); - holesPts[ i ] = this.holes[ i ].getTransformedPoints( divisions, this.bends ); + var positionPrev = new THREE.Vector3(); - } + return function ( force ) { - return holesPts; + THREE.Object3D.prototype.updateMatrixWorld.call( this, force ); + + var listener = this.context.listener; + + this.matrixWorld.decompose( position, quaternion, scale ); + + orientation.set( 0, 0, -1 ).applyQuaternion( quaternion ); + velocity.subVectors( position, positionPrev ); + + listener.setPosition( position.x, position.y, position.z ); + listener.setOrientation( orientation.x, orientation.y, orientation.z, this.up.x, this.up.y, this.up.z ); + listener.setVelocity( velocity.x, velocity.y, velocity.z ); + + positionPrev.copy( position ); + + }; + +} )(); + +// File:src/extras/core/Curve.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * Extensible curve object + * + * Some common of Curve methods + * .getPoint(t), getTangent(t) + * .getPointAt(u), getTagentAt(u) + * .getPoints(), .getSpacedPoints() + * .getLength() + * .updateArcLengths() + * + * This following classes subclasses THREE.Curve: + * + * -- 2d classes -- + * THREE.LineCurve + * THREE.QuadraticBezierCurve + * THREE.CubicBezierCurve + * THREE.SplineCurve + * THREE.ArcCurve + * THREE.EllipseCurve + * + * -- 3d classes -- + * THREE.LineCurve3 + * THREE.QuadraticBezierCurve3 + * THREE.CubicBezierCurve3 + * THREE.SplineCurve3 + * THREE.ClosedSplineCurve3 + * + * A series of curves can be represented as a THREE.CurvePath + * + **/ + +/************************************************************** + * Abstract Curve base class + **************************************************************/ + +THREE.Curve = function () { }; -// Get points of holes (spaced by regular distance) +// Virtual base class method to overwrite and implement in subclasses +// - t [0 .. 1] -THREE.Shape.prototype.getSpacedPointsHoles = function ( divisions ) { +THREE.Curve.prototype.getPoint = function ( t ) { - var i, il = this.holes.length, holesPts = []; + console.log( "Warning, getPoint() not implemented!" ); + return null; - for ( i = 0; i < il; i ++ ) { +}; - holesPts[ i ] = this.holes[ i ].getTransformedSpacedPoints( divisions, this.bends ); +// Get point at relative position in curve according to arc length +// - u [0 .. 1] - } +THREE.Curve.prototype.getPointAt = function ( u ) { - return holesPts; + var t = this.getUtoTmapping( u ); + return this.getPoint( t ); }; +// Get sequence of points using getPoint( t ) -// Get points of shape and holes (keypoints based on segments parameter) +THREE.Curve.prototype.getPoints = function ( divisions ) { -THREE.Shape.prototype.extractAllPoints = function ( divisions ) { + if ( ! divisions ) divisions = 5; - return { + var d, pts = []; - shape: this.getTransformedPoints( divisions ), - holes: this.getPointsHoles( divisions ) + for ( d = 0; d <= divisions; d ++ ) { - }; + pts.push( this.getPoint( d / divisions ) ); + + } + + return pts; }; -THREE.Shape.prototype.extractPoints = function ( divisions ) { +// Get sequence of points using getPointAt( u ) + +THREE.Curve.prototype.getSpacedPoints = function ( divisions ) { + + if ( ! divisions ) divisions = 5; + + var d, pts = []; + + for ( d = 0; d <= divisions; d ++ ) { + + pts.push( this.getPointAt( d / divisions ) ); - if (this.useSpacedPoints) { - return this.extractAllSpacedPoints(divisions); } - return this.extractAllPoints(divisions); + return pts; }; -// -// THREE.Shape.prototype.extractAllPointsWithBend = function ( divisions, bend ) { -// -// return { -// -// shape: this.transform( bend, divisions ), -// holes: this.getPointsHoles( divisions, bend ) -// -// }; -// -// }; +// Get total curve arc length -// Get points of shape and holes (spaced by regular distance) +THREE.Curve.prototype.getLength = function () { -THREE.Shape.prototype.extractAllSpacedPoints = function ( divisions ) { + var lengths = this.getLengths(); + return lengths[ lengths.length - 1 ]; - return { +}; - shape: this.getTransformedSpacedPoints( divisions ), - holes: this.getSpacedPointsHoles( divisions ) +// Get list of cumulative segment lengths - }; +THREE.Curve.prototype.getLengths = function ( divisions ) { + + if ( ! divisions ) divisions = (this.__arcLengthDivisions) ? (this.__arcLengthDivisions): 200; + + if ( this.cacheArcLengths + && ( this.cacheArcLengths.length == divisions + 1 ) + && ! this.needsUpdate) { + + //console.log( "cached", this.cacheArcLengths ); + return this.cacheArcLengths; + + } + + this.needsUpdate = false; + + var cache = []; + var current, last = this.getPoint( 0 ); + var p, sum = 0; + + cache.push( 0 ); + + for ( p = 1; p <= divisions; p ++ ) { + + current = this.getPoint ( p / divisions ); + sum += current.distanceTo( last ); + cache.push( sum ); + last = current; + + } + + this.cacheArcLengths = cache; + + return cache; // { sums: cache, sum:sum }; Sum is in the last element. }; -/************************************************************** - * Utils - **************************************************************/ -THREE.Shape.Utils = { +THREE.Curve.prototype.updateArcLengths = function() { + this.needsUpdate = true; + this.getLengths(); +}; - triangulateShape: function ( contour, holes ) { +// Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equi distance - function point_in_segment_2D_colin( inSegPt1, inSegPt2, inOtherPt ) { - // inOtherPt needs to be colinear to the inSegment - if ( inSegPt1.x != inSegPt2.x ) { - if ( inSegPt1.x < inSegPt2.x ) { - return ( ( inSegPt1.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt2.x ) ); - } else { - return ( ( inSegPt2.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt1.x ) ); - } - } else { - if ( inSegPt1.y < inSegPt2.y ) { - return ( ( inSegPt1.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt2.y ) ); - } else { - return ( ( inSegPt2.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt1.y ) ); - } - } - } +THREE.Curve.prototype.getUtoTmapping = function ( u, distance ) { - function intersect_segments_2D( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1, inSeg2Pt2, inExcludeAdjacentSegs ) { - var EPSILON = 0.0000000001; + var arcLengths = this.getLengths(); - var seg1dx = inSeg1Pt2.x - inSeg1Pt1.x, seg1dy = inSeg1Pt2.y - inSeg1Pt1.y; - var seg2dx = inSeg2Pt2.x - inSeg2Pt1.x, seg2dy = inSeg2Pt2.y - inSeg2Pt1.y; + var i = 0, il = arcLengths.length; - var seg1seg2dx = inSeg1Pt1.x - inSeg2Pt1.x; - var seg1seg2dy = inSeg1Pt1.y - inSeg2Pt1.y; + var targetArcLength; // The targeted u distance value to get - var limit = seg1dy * seg2dx - seg1dx * seg2dy; - var perpSeg1 = seg1dy * seg1seg2dx - seg1dx * seg1seg2dy; + if ( distance ) { - if ( Math.abs(limit) > EPSILON ) { // not parallel + targetArcLength = distance; - var perpSeg2; - if ( limit > 0 ) { - if ( ( perpSeg1 < 0 ) || ( perpSeg1 > limit ) ) return []; - perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy; - if ( ( perpSeg2 < 0 ) || ( perpSeg2 > limit ) ) return []; - } else { - if ( ( perpSeg1 > 0 ) || ( perpSeg1 < limit ) ) return []; - perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy; - if ( ( perpSeg2 > 0 ) || ( perpSeg2 < limit ) ) return []; - } + } else { - // i.e. to reduce rounding errors - // intersection at endpoint of segment#1? - if ( perpSeg2 == 0 ) { - if ( ( inExcludeAdjacentSegs ) && - ( ( perpSeg1 == 0 ) || ( perpSeg1 == limit ) ) ) return []; - return [ inSeg1Pt1 ]; - } - if ( perpSeg2 == limit ) { - if ( ( inExcludeAdjacentSegs ) && - ( ( perpSeg1 == 0 ) || ( perpSeg1 == limit ) ) ) return []; - return [ inSeg1Pt2 ]; - } - // intersection at endpoint of segment#2? - if ( perpSeg1 == 0 ) return [ inSeg2Pt1 ]; - if ( perpSeg1 == limit ) return [ inSeg2Pt2 ]; + targetArcLength = u * arcLengths[ il - 1 ]; - // return real intersection point - var factorSeg1 = perpSeg2 / limit; - return [ { x: inSeg1Pt1.x + factorSeg1 * seg1dx, - y: inSeg1Pt1.y + factorSeg1 * seg1dy } ]; + } - } else { // parallel or colinear - if ( ( perpSeg1 != 0 ) || - ( seg2dy * seg1seg2dx != seg2dx * seg1seg2dy ) ) return []; + //var time = Date.now(); - // they are collinear or degenerate - var seg1Pt = ( (seg1dx == 0) && (seg1dy == 0) ); // segment1 ist just a point? - var seg2Pt = ( (seg2dx == 0) && (seg2dy == 0) ); // segment2 ist just a point? - // both segments are points - if ( seg1Pt && seg2Pt ) { - if ( (inSeg1Pt1.x != inSeg2Pt1.x) || - (inSeg1Pt1.y != inSeg2Pt1.y) ) return []; // they are distinct points - return [ inSeg1Pt1 ]; // they are the same point - } - // segment#1 is a single point - if ( seg1Pt ) { - if (! point_in_segment_2D_colin( inSeg2Pt1, inSeg2Pt2, inSeg1Pt1 ) ) return []; // but not in segment#2 - return [ inSeg1Pt1 ]; - } - // segment#2 is a single point - if ( seg2Pt ) { - if (! point_in_segment_2D_colin( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1 ) ) return []; // but not in segment#1 - return [ inSeg2Pt1 ]; - } + // binary search for the index with largest value smaller than target u distance - // they are collinear segments, which might overlap - var seg1min, seg1max, seg1minVal, seg1maxVal; - var seg2min, seg2max, seg2minVal, seg2maxVal; - if (seg1dx != 0) { // the segments are NOT on a vertical line - if ( inSeg1Pt1.x < inSeg1Pt2.x ) { - seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.x; - seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.x; - } else { - seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.x; - seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.x; - } - if ( inSeg2Pt1.x < inSeg2Pt2.x ) { - seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.x; - seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.x; - } else { - seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.x; - seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.x; - } - } else { // the segments are on a vertical line - if ( inSeg1Pt1.y < inSeg1Pt2.y ) { - seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.y; - seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.y; - } else { - seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.y; - seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.y; - } - if ( inSeg2Pt1.y < inSeg2Pt2.y ) { - seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.y; - seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.y; - } else { - seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.y; - seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.y; - } - } - if ( seg1minVal <= seg2minVal ) { - if ( seg1maxVal < seg2minVal ) return []; - if ( seg1maxVal == seg2minVal ) { - if ( inExcludeAdjacentSegs ) return []; - return [ seg2min ]; - } - if ( seg1maxVal <= seg2maxVal ) return [ seg2min, seg1max ]; - return [ seg2min, seg2max ]; - } else { - if ( seg1minVal > seg2maxVal ) return []; - if ( seg1minVal == seg2maxVal ) { - if ( inExcludeAdjacentSegs ) return []; - return [ seg1min ]; - } - if ( seg1maxVal <= seg2maxVal ) return [ seg1min, seg1max ]; - return [ seg1min, seg2max ]; - } - } - } + var low = 0, high = il - 1, comparison; - function isPointInsideAngle( inVertex, inLegFromPt, inLegToPt, inOtherPt ) { - // The order of legs is important + while ( low <= high ) { - var EPSILON = 0.0000000001; + i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats - // translation of all points, so that Vertex is at (0,0) - var legFromPtX = inLegFromPt.x - inVertex.x, legFromPtY = inLegFromPt.y - inVertex.y; - var legToPtX = inLegToPt.x - inVertex.x, legToPtY = inLegToPt.y - inVertex.y; - var otherPtX = inOtherPt.x - inVertex.x, otherPtY = inOtherPt.y - inVertex.y; + comparison = arcLengths[ i ] - targetArcLength; - // main angle >0: < 180 deg.; 0: 180 deg.; <0: > 180 deg. - var from2toAngle = legFromPtX * legToPtY - legFromPtY * legToPtX; - var from2otherAngle = legFromPtX * otherPtY - legFromPtY * otherPtX; + if ( comparison < 0 ) { - if ( Math.abs(from2toAngle) > EPSILON ) { // angle != 180 deg. + low = i + 1; + continue; - var other2toAngle = otherPtX * legToPtY - otherPtY * legToPtX; - // console.log( "from2to: " + from2toAngle + ", from2other: " + from2otherAngle + ", other2to: " + other2toAngle ); + } else if ( comparison > 0 ) { + + high = i - 1; + continue; + + } else { + + high = i; + break; + + // DONE - if ( from2toAngle > 0 ) { // main angle < 180 deg. - return ( ( from2otherAngle >= 0 ) && ( other2toAngle >= 0 ) ); - } else { // main angle > 180 deg. - return ( ( from2otherAngle >= 0 ) || ( other2toAngle >= 0 ) ); - } - } else { // angle == 180 deg. - // console.log( "from2to: 180 deg., from2other: " + from2otherAngle ); - return ( from2otherAngle > 0 ); - } } + } - function removeHoles( contour, holes ) { + i = high; - var shape = contour.concat(); // work on this shape - var hole; + //console.log('b' , i, low, high, Date.now()- time); - function isCutLineInsideAngles( inShapeIdx, inHoleIdx ) { - // Check if hole point lies within angle around shape point - var lastShapeIdx = shape.length - 1; + if ( arcLengths[ i ] == targetArcLength ) { - var prevShapeIdx = inShapeIdx - 1; - if ( prevShapeIdx < 0 ) prevShapeIdx = lastShapeIdx; + var t = i / ( il - 1 ); + return t; - var nextShapeIdx = inShapeIdx + 1; - if ( nextShapeIdx > lastShapeIdx ) nextShapeIdx = 0; + } - var insideAngle = isPointInsideAngle( shape[inShapeIdx], shape[ prevShapeIdx ], shape[ nextShapeIdx ], hole[inHoleIdx] ); - if (! insideAngle ) { - // console.log( "Vertex (Shape): " + inShapeIdx + ", Point: " + hole[inHoleIdx].x + "/" + hole[inHoleIdx].y ); - return false; - } + // we could get finer grain at lengths, or use simple interpolatation between two points - // Check if shape point lies within angle around hole point - var lastHoleIdx = hole.length - 1; + var lengthBefore = arcLengths[ i ]; + var lengthAfter = arcLengths[ i + 1 ]; - var prevHoleIdx = inHoleIdx - 1; - if ( prevHoleIdx < 0 ) prevHoleIdx = lastHoleIdx; + var segmentLength = lengthAfter - lengthBefore; - var nextHoleIdx = inHoleIdx + 1; - if ( nextHoleIdx > lastHoleIdx ) nextHoleIdx = 0; + // determine where we are between the 'before' and 'after' points - insideAngle = isPointInsideAngle( hole[inHoleIdx], hole[ prevHoleIdx ], hole[ nextHoleIdx ], shape[inShapeIdx] ); - if (! insideAngle ) { - // console.log( "Vertex (Hole): " + inHoleIdx + ", Point: " + shape[inShapeIdx].x + "/" + shape[inShapeIdx].y ); - return false; - } + var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; - return true; - } + // add that fractional amount to t - function intersectsShapeEdge( inShapePt, inHolePt ) { - // checks for intersections with shape edges - var sIdx, nextIdx, intersection; - for ( sIdx = 0; sIdx < shape.length; sIdx ++ ) { - nextIdx = sIdx+1; nextIdx %= shape.length; - intersection = intersect_segments_2D( inShapePt, inHolePt, shape[sIdx], shape[nextIdx], true ); - if ( intersection.length > 0 ) return true; - } + var t = ( i + segmentFraction ) / ( il -1 ); - return false; - } + return t; - var indepHoles = []; +}; - function intersectsHoleEdge( inShapePt, inHolePt ) { - // checks for intersections with hole edges - var ihIdx, chkHole, - hIdx, nextIdx, intersection; - for ( ihIdx = 0; ihIdx < indepHoles.length; ihIdx ++ ) { - chkHole = holes[indepHoles[ihIdx]]; - for ( hIdx = 0; hIdx < chkHole.length; hIdx ++ ) { - nextIdx = hIdx+1; nextIdx %= chkHole.length; - intersection = intersect_segments_2D( inShapePt, inHolePt, chkHole[hIdx], chkHole[nextIdx], true ); - if ( intersection.length > 0 ) return true; - } - } - return false; - } +// Returns a unit vector tangent at t +// In case any sub curve does not implement its tangent derivation, +// 2 points a small delta apart will be used to find its gradient +// which seems to give a reasonable approximation - var holeIndex, shapeIndex, - shapePt, holePt, - holeIdx, cutKey, failedCuts = [], - tmpShape1, tmpShape2, - tmpHole1, tmpHole2; +THREE.Curve.prototype.getTangent = function( t ) { - for ( var h = 0, hl = holes.length; h < hl; h ++ ) { + var delta = 0.0001; + var t1 = t - delta; + var t2 = t + delta; - indepHoles.push( h ); + // Capping in case of danger - } + if ( t1 < 0 ) t1 = 0; + if ( t2 > 1 ) t2 = 1; - var minShapeIndex = 0; - var counter = indepHoles.length * 2; - while ( indepHoles.length > 0 ) { - counter --; - if ( counter < 0 ) { - console.log( "Infinite Loop! Holes left:" + indepHoles.length + ", Probably Hole outside Shape!" ); - break; - } + var pt1 = this.getPoint( t1 ); + var pt2 = this.getPoint( t2 ); - // search for shape-vertex and hole-vertex, - // which can be connected without intersections - for ( shapeIndex = minShapeIndex; shapeIndex < shape.length; shapeIndex ++ ) { + var vec = pt2.clone().sub(pt1); + return vec.normalize(); - shapePt = shape[ shapeIndex ]; - holeIndex = - 1; +}; - // search for hole which can be reached without intersections - for ( var h = 0; h < indepHoles.length; h ++ ) { - holeIdx = indepHoles[h]; - // prevent multiple checks - cutKey = shapePt.x + ":" + shapePt.y + ":" + holeIdx; - if ( failedCuts[cutKey] !== undefined ) continue; +THREE.Curve.prototype.getTangentAt = function ( u ) { - hole = holes[holeIdx]; - for ( var h2 = 0; h2 < hole.length; h2 ++ ) { - holePt = hole[ h2 ]; - if (! isCutLineInsideAngles( shapeIndex, h2 ) ) continue; - if ( intersectsShapeEdge( shapePt, holePt ) ) continue; - if ( intersectsHoleEdge( shapePt, holePt ) ) continue; + var t = this.getUtoTmapping( u ); + return this.getTangent( t ); - holeIndex = h2; - indepHoles.splice(h,1); +}; - tmpShape1 = shape.slice( 0, shapeIndex+1 ); - tmpShape2 = shape.slice( shapeIndex ); - tmpHole1 = hole.slice( holeIndex ); - tmpHole2 = hole.slice( 0, holeIndex+1 ); - shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 ); - minShapeIndex = shapeIndex; - // Debug only, to show the selected cuts - // glob_CutLines.push( [ shapePt, holePt ] ); - break; - } - if ( holeIndex >= 0 ) break; // hole-vertex found +/************************************************************** + * Utils + **************************************************************/ - failedCuts[cutKey] = true; // remember failure - } - if ( holeIndex >= 0 ) break; // hole-vertex found - } - } +THREE.Curve.Utils = { + + tangentQuadraticBezier: function ( t, p0, p1, p2 ) { + + return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 ); + + }, + + // Puay Bing, thanks for helping with this derivative! + + tangentCubicBezier: function (t, p0, p1, p2, p3 ) { + + return - 3 * p0 * (1 - t) * (1 - t) + + 3 * p1 * (1 - t) * (1-t) - 6 *t *p1 * (1-t) + + 6 * t * p2 * (1-t) - 3 * t * t * p2 + + 3 * t * t * p3; + + }, + + tangentSpline: function ( t, p0, p1, p2, p3 ) { + + // To check if my formulas are correct + + var h00 = 6 * t * t - 6 * t; // derived from 2t^3 − 3t^2 + 1 + var h10 = 3 * t * t - 4 * t + 1; // t^3 − 2t^2 + t + var h01 = - 6 * t * t + 6 * t; // − 2t3 + 3t2 + var h11 = 3 * t * t - 2 * t; // t3 − t2 + + return h00 + h10 + h01 + h11; + + }, + + // Catmull-Rom + + interpolate: function( p0, p1, p2, p3, t ) { + + var v0 = ( p2 - p0 ) * 0.5; + var v1 = ( p3 - p1 ) * 0.5; + var t2 = t * t; + var t3 = t * t2; + return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; + + } + +}; + + +// TODO: Transformation for Curves? + +/************************************************************** + * 3D Curves + **************************************************************/ + +// A Factory method for creating new curve subclasses + +THREE.Curve.create = function ( constructor, getPointFunc ) { + + constructor.prototype = Object.create( THREE.Curve.prototype ); + constructor.prototype.getPoint = getPointFunc; + + return constructor; + +}; + +// File:src/extras/core/CurvePath.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * + **/ + +/************************************************************** + * Curved Path - a curve path is simply a array of connected + * curves, but retains the api of a curve + **************************************************************/ + +THREE.CurvePath = function () { + + this.curves = []; + this.bends = []; + + this.autoClose = false; // Automatically closes the path +}; + +THREE.CurvePath.prototype = Object.create( THREE.Curve.prototype ); + +THREE.CurvePath.prototype.add = function ( curve ) { + + this.curves.push( curve ); + +}; + +THREE.CurvePath.prototype.checkConnection = function() { + // TODO + // If the ending of curve is not connected to the starting + // or the next curve, then, this is not a real path +}; + +THREE.CurvePath.prototype.closePath = function() { + // TODO Test + // and verify for vector3 (needs to implement equals) + // Add a line curve if start and end of lines are not connected + var startPoint = this.curves[0].getPoint(0); + var endPoint = this.curves[this.curves.length-1].getPoint(1); + + if (! startPoint.equals(endPoint)) { + this.curves.push( new THREE.LineCurve(endPoint, startPoint) ); + } + +}; + +// To get accurate point with reference to +// entire path distance at time t, +// following has to be done: + +// 1. Length of each sub path have to be known +// 2. Locate and identify type of curve +// 3. Get t for the curve +// 4. Return curve.getPointAt(t') + +THREE.CurvePath.prototype.getPoint = function( t ) { + + var d = t * this.getLength(); + var curveLengths = this.getCurveLengths(); + var i = 0, diff, curve; + + // To think about boundaries points. + + while ( i < curveLengths.length ) { + + if ( curveLengths[ i ] >= d ) { + + diff = curveLengths[ i ] - d; + curve = this.curves[ i ]; + + var u = 1 - diff / curve.getLength(); - return shape; /* shape with no holes */ + return curve.getPointAt( u ); + + break; } + i ++; - var i, il, f, face, - key, index, - allPointsMap = {}; + } - // To maintain reference to old shape, one must match coordinates, or offset the indices from original arrays. It's probably easier to do the first. + return null; - var allpoints = contour.concat(); + // loop where sum != 0, sum > d , sum+1 maxX ) maxX = p.x; + else if ( p.x < minX ) minX = p.x; - }, + if ( p.y > maxY ) maxY = p.y; + else if ( p.y < minY ) minY = p.y; - b2p1: function ( t, p ) { + if ( v3 ) { - return 2 * ( 1 - t ) * t * p; + if ( p.z > maxZ ) maxZ = p.z; + else if ( p.z < minZ ) minZ = p.z; - }, + } - b2p2: function ( t, p ) { + sum.add( p ); - return t * t * p; + } - }, + var ret = { - b2: function ( t, p0, p1, p2 ) { + minX: minX, + minY: minY, + maxX: maxX, + maxY: maxY - return this.b2p0( t, p0 ) + this.b2p1( t, p1 ) + this.b2p2( t, p2 ); + }; - }, + if ( v3 ) { - // Cubic Bezier Functions + ret.maxZ = maxZ; + ret.minZ = minZ; - b3p0: function ( t, p ) { + } - var k = 1 - t; - return k * k * k * p; + return ret; - }, +}; - b3p1: function ( t, p ) { +/************************************************************** + * Create Geometries Helpers + **************************************************************/ - var k = 1 - t; - return 3 * k * k * t * p; +/// Generate geometry from path points (for Line or Points objects) - }, +THREE.CurvePath.prototype.createPointsGeometry = function( divisions ) { - b3p2: function ( t, p ) { + var pts = this.getPoints( divisions, true ); + return this.createGeometry( pts ); - var k = 1 - t; - return 3 * k * t * t * p; +}; - }, +// Generate geometry from equidistance sampling along the path - b3p3: function ( t, p ) { +THREE.CurvePath.prototype.createSpacedPointsGeometry = function( divisions ) { - return t * t * t * p; + var pts = this.getSpacedPoints( divisions, true ); + return this.createGeometry( pts ); - }, +}; - b3: function ( t, p0, p1, p2, p3 ) { +THREE.CurvePath.prototype.createGeometry = function( points ) { - return this.b3p0( t, p0 ) + this.b3p1( t, p1 ) + this.b3p2( t, p2 ) + this.b3p3( t, p3 ); + var geometry = new THREE.Geometry(); + + for ( var i = 0; i < points.length; i ++ ) { + + geometry.vertices.push( new THREE.Vector3( points[ i ].x, points[ i ].y, points[ i ].z || 0) ); } -}; + return geometry; +}; -// File:src/extras/curves/LineCurve.js /************************************************************** - * Line + * Bend / Wrap Helper Methods **************************************************************/ -THREE.LineCurve = function ( v1, v2 ) { - - this.v1 = v1; - this.v2 = v2; - -}; +// Wrap path / Bend modifiers? -THREE.LineCurve.prototype = Object.create( THREE.Curve.prototype ); +THREE.CurvePath.prototype.addWrapPath = function ( bendpath ) { -THREE.LineCurve.prototype.getPoint = function ( t ) { + this.bends.push( bendpath ); - var point = this.v2.clone().sub(this.v1); - point.multiplyScalar( t ).add( this.v1 ); +}; - return point; +THREE.CurvePath.prototype.getTransformedPoints = function( segments, bends ) { -}; + var oldPts = this.getPoints( segments ); // getPoints getSpacedPoints + var i, il; -// Line curve is linear, so we can overwrite default getPointAt + if ( ! bends ) { -THREE.LineCurve.prototype.getPointAt = function ( u ) { + bends = this.bends; - return this.getPoint( u ); + } -}; + for ( i = 0, il = bends.length; i < il; i ++ ) { -THREE.LineCurve.prototype.getTangent = function( t ) { + oldPts = this.getWrapPoints( oldPts, bends[ i ] ); - var tangent = this.v2.clone().sub(this.v1); + } - return tangent.normalize(); + return oldPts; }; -// File:src/extras/curves/QuadraticBezierCurve.js +THREE.CurvePath.prototype.getTransformedSpacedPoints = function( segments, bends ) { -/************************************************************** - * Quadratic Bezier curve - **************************************************************/ + var oldPts = this.getSpacedPoints( segments ); + + var i, il; + if ( ! bends ) { -THREE.QuadraticBezierCurve = function ( v0, v1, v2 ) { + bends = this.bends; - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; + } -}; + for ( i = 0, il = bends.length; i < il; i ++ ) { -THREE.QuadraticBezierCurve.prototype = Object.create( THREE.Curve.prototype ); + oldPts = this.getWrapPoints( oldPts, bends[ i ] ); + } -THREE.QuadraticBezierCurve.prototype.getPoint = function ( t ) { + return oldPts; - var tx, ty; +}; - tx = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x ); - ty = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y ); +// This returns getPoints() bend/wrapped around the contour of a path. +// Read http://www.planetclegg.com/projects/WarpingTextToSplines.html - return new THREE.Vector2( tx, ty ); +THREE.CurvePath.prototype.getWrapPoints = function ( oldPts, path ) { -}; + var bounds = this.getBoundingBox(); + var i, il, p, oldX, oldY, xNorm; -THREE.QuadraticBezierCurve.prototype.getTangent = function( t ) { + for ( i = 0, il = oldPts.length; i < il; i ++ ) { - var tx, ty; + p = oldPts[ i ]; - tx = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.x, this.v1.x, this.v2.x ); - ty = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.y, this.v1.y, this.v2.y ); + oldX = p.x; + oldY = p.y; - // returns unit vector + xNorm = oldX / bounds.maxX; - var tangent = new THREE.Vector2( tx, ty ); - tangent.normalize(); + // If using actual distance, for length > path, requires line extrusions + //xNorm = path.getUtoTmapping(xNorm, oldX); // 3 styles. 1) wrap stretched. 2) wrap stretch by arc length 3) warp by actual distance - return tangent; + xNorm = path.getUtoTmapping( xNorm, oldX ); -}; + // check for out of bounds? -// File:src/extras/curves/CubicBezierCurve.js + var pathPt = path.getPoint( xNorm ); + var normal = path.getTangent( xNorm ); + normal.set( - normal.y, normal.x ).multiplyScalar( oldY ); -/************************************************************** - * Cubic Bezier curve - **************************************************************/ + p.x = pathPt.x + normal.x; + p.y = pathPt.y + normal.y; -THREE.CubicBezierCurve = function ( v0, v1, v2, v3 ) { + } - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; - this.v3 = v3; + return oldPts; }; -THREE.CubicBezierCurve.prototype = Object.create( THREE.Curve.prototype ); -THREE.CubicBezierCurve.prototype.getPoint = function ( t ) { +// File:src/extras/core/Gyroscope.js - var tx, ty; +/** + * @author alteredq / http://alteredqualia.com/ + */ - tx = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ); - ty = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ); +THREE.Gyroscope = function () { - return new THREE.Vector2( tx, ty ); + THREE.Object3D.call( this ); }; -THREE.CubicBezierCurve.prototype.getTangent = function( t ) { +THREE.Gyroscope.prototype = Object.create( THREE.Object3D.prototype ); - var tx, ty; +THREE.Gyroscope.prototype.updateMatrixWorld = ( function () { - tx = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ); - ty = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ); + var translationObject = new THREE.Vector3(); + var quaternionObject = new THREE.Quaternion(); + var scaleObject = new THREE.Vector3(); - var tangent = new THREE.Vector2( tx, ty ); - tangent.normalize(); + var translationWorld = new THREE.Vector3(); + var quaternionWorld = new THREE.Quaternion(); + var scaleWorld = new THREE.Vector3(); - return tangent; + return function ( force ) { -}; + this.matrixAutoUpdate && this.updateMatrix(); -// File:src/extras/curves/SplineCurve.js + // update matrixWorld -/************************************************************** - * Spline curve - **************************************************************/ + if ( this.matrixWorldNeedsUpdate || force ) { -THREE.SplineCurve = function ( points /* array of Vector2 */ ) { + if ( this.parent ) { - this.points = (points == undefined) ? [] : points; + this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); -}; + this.matrixWorld.decompose( translationWorld, quaternionWorld, scaleWorld ); + this.matrix.decompose( translationObject, quaternionObject, scaleObject ); -THREE.SplineCurve.prototype = Object.create( THREE.Curve.prototype ); + this.matrixWorld.compose( translationWorld, quaternionObject, scaleWorld ); -THREE.SplineCurve.prototype.getPoint = function ( t ) { - var v = new THREE.Vector2(); - var c = []; - var points = this.points, point, intPoint, weight; - point = ( points.length - 1 ) * t; + } else { - intPoint = Math.floor( point ); - weight = point - intPoint; + this.matrixWorld.copy( this.matrix ); - c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1; - c[ 1 ] = intPoint; - c[ 2 ] = intPoint > points.length - 2 ? points.length -1 : intPoint + 1; - c[ 3 ] = intPoint > points.length - 3 ? points.length -1 : intPoint + 2; + } - v.x = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].x, points[ c[ 1 ] ].x, points[ c[ 2 ] ].x, points[ c[ 3 ] ].x, weight ); - v.y = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].y, points[ c[ 1 ] ].y, points[ c[ 2 ] ].y, points[ c[ 3 ] ].y, weight ); - return v; + this.matrixWorldNeedsUpdate = false; -}; + force = true; -// File:src/extras/curves/EllipseCurve.js + } -/************************************************************** - * Ellipse curve - **************************************************************/ + // update children -THREE.EllipseCurve = function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise ) { + for ( var i = 0, l = this.children.length; i < l; i ++ ) { - this.aX = aX; - this.aY = aY; + this.children[ i ].updateMatrixWorld( force ); - this.xRadius = xRadius; - this.yRadius = yRadius; + } - this.aStartAngle = aStartAngle; - this.aEndAngle = aEndAngle; + }; + +}() ); - this.aClockwise = aClockwise; +// File:src/extras/core/Path.js -}; +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * Creates free form 2d path using series of points, lines or curves. + * + **/ -THREE.EllipseCurve.prototype = Object.create( THREE.Curve.prototype ); +THREE.Path = function ( points ) { -THREE.EllipseCurve.prototype.getPoint = function ( t ) { + THREE.CurvePath.call(this); - var angle; - var deltaAngle = this.aEndAngle - this.aStartAngle; + this.actions = []; - if ( deltaAngle < 0 ) deltaAngle += Math.PI * 2; - if ( deltaAngle > Math.PI * 2 ) deltaAngle -= Math.PI * 2; + if ( points ) { - if ( this.aClockwise === true ) { + this.fromPoints( points ); - angle = this.aEndAngle + ( 1 - t ) * ( Math.PI * 2 - deltaAngle ); + } - } else { +}; - angle = this.aStartAngle + t * deltaAngle; +THREE.Path.prototype = Object.create( THREE.CurvePath.prototype ); - } +THREE.PathActions = { + + MOVE_TO: 'moveTo', + LINE_TO: 'lineTo', + QUADRATIC_CURVE_TO: 'quadraticCurveTo', // Bezier quadratic curve + BEZIER_CURVE_TO: 'bezierCurveTo', // Bezier cubic curve + CSPLINE_THRU: 'splineThru', // Catmull-rom spline + ARC: 'arc', // Circle + ELLIPSE: 'ellipse' +}; - var tx = this.aX + this.xRadius * Math.cos( angle ); - var ty = this.aY + this.yRadius * Math.sin( angle ); +// TODO Clean up PATH API - return new THREE.Vector2( tx, ty ); +// Create path using straight lines to connect all points +// - vectors: array of Vector2 -}; +THREE.Path.prototype.fromPoints = function ( vectors ) { -// File:src/extras/curves/ArcCurve.js + this.moveTo( vectors[ 0 ].x, vectors[ 0 ].y ); -/************************************************************** - * Arc curve - **************************************************************/ + for ( var v = 1, vlen = vectors.length; v < vlen; v ++ ) { -THREE.ArcCurve = function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { + this.lineTo( vectors[ v ].x, vectors[ v ].y ); + + }; - THREE.EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); }; -THREE.ArcCurve.prototype = Object.create( THREE.EllipseCurve.prototype ); +// startPath() endPath()? -// File:src/extras/curves/LineCurve3.js +THREE.Path.prototype.moveTo = function ( x, y ) { -/************************************************************** - * Line3D - **************************************************************/ + var args = Array.prototype.slice.call( arguments ); + this.actions.push( { action: THREE.PathActions.MOVE_TO, args: args } ); -THREE.LineCurve3 = THREE.Curve.create( +}; - function ( v1, v2 ) { +THREE.Path.prototype.lineTo = function ( x, y ) { - this.v1 = v1; - this.v2 = v2; + var args = Array.prototype.slice.call( arguments ); - }, + var lastargs = this.actions[ this.actions.length - 1 ].args; - function ( t ) { + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; - var r = new THREE.Vector3(); + var curve = new THREE.LineCurve( new THREE.Vector2( x0, y0 ), new THREE.Vector2( x, y ) ); + this.curves.push( curve ); + this.actions.push( { action: THREE.PathActions.LINE_TO, args: args } ); - r.subVectors( this.v2, this.v1 ); // diff - r.multiplyScalar( t ); - r.add( this.v1 ); +}; - return r; +THREE.Path.prototype.quadraticCurveTo = function( aCPx, aCPy, aX, aY ) { - } + var args = Array.prototype.slice.call( arguments ); -); + var lastargs = this.actions[ this.actions.length - 1 ].args; -// File:src/extras/curves/QuadraticBezierCurve3.js + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; -/************************************************************** - * Quadratic Bezier 3D curve - **************************************************************/ + var curve = new THREE.QuadraticBezierCurve( new THREE.Vector2( x0, y0 ), + new THREE.Vector2( aCPx, aCPy ), + new THREE.Vector2( aX, aY ) ); + this.curves.push( curve ); -THREE.QuadraticBezierCurve3 = THREE.Curve.create( + this.actions.push( { action: THREE.PathActions.QUADRATIC_CURVE_TO, args: args } ); - function ( v0, v1, v2 ) { +}; - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; +THREE.Path.prototype.bezierCurveTo = function( aCP1x, aCP1y, + aCP2x, aCP2y, + aX, aY ) { - }, + var args = Array.prototype.slice.call( arguments ); - function ( t ) { + var lastargs = this.actions[ this.actions.length - 1 ].args; - var tx, ty, tz; + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; - tx = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x ); - ty = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y ); - tz = THREE.Shape.Utils.b2( t, this.v0.z, this.v1.z, this.v2.z ); + var curve = new THREE.CubicBezierCurve( new THREE.Vector2( x0, y0 ), + new THREE.Vector2( aCP1x, aCP1y ), + new THREE.Vector2( aCP2x, aCP2y ), + new THREE.Vector2( aX, aY ) ); + this.curves.push( curve ); - return new THREE.Vector3( tx, ty, tz ); + this.actions.push( { action: THREE.PathActions.BEZIER_CURVE_TO, args: args } ); - } +}; -); +THREE.Path.prototype.splineThru = function( pts /*Array of Vector*/ ) { -// File:src/extras/curves/CubicBezierCurve3.js + var args = Array.prototype.slice.call( arguments ); + var lastargs = this.actions[ this.actions.length - 1 ].args; -/************************************************************** - * Cubic Bezier 3D curve - **************************************************************/ + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; +//--- + var npts = [ new THREE.Vector2( x0, y0 ) ]; + Array.prototype.push.apply( npts, pts ); -THREE.CubicBezierCurve3 = THREE.Curve.create( + var curve = new THREE.SplineCurve( npts ); + this.curves.push( curve ); - function ( v0, v1, v2, v3 ) { + this.actions.push( { action: THREE.PathActions.CSPLINE_THRU, args: args } ); - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; - this.v3 = v3; +}; - }, +// FUTURE: Change the API or follow canvas API? - function ( t ) { +THREE.Path.prototype.arc = function ( aX, aY, aRadius, + aStartAngle, aEndAngle, aClockwise ) { - var tx, ty, tz; + var lastargs = this.actions[ this.actions.length - 1].args; + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; - tx = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ); - ty = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ); - tz = THREE.Shape.Utils.b3( t, this.v0.z, this.v1.z, this.v2.z, this.v3.z ); + this.absarc(aX + x0, aY + y0, aRadius, + aStartAngle, aEndAngle, aClockwise ); - return new THREE.Vector3( tx, ty, tz ); + }; - } + THREE.Path.prototype.absarc = function ( aX, aY, aRadius, + aStartAngle, aEndAngle, aClockwise ) { + this.absellipse(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise); + }; -); +THREE.Path.prototype.ellipse = function ( aX, aY, xRadius, yRadius, + aStartAngle, aEndAngle, aClockwise ) { -// File:src/extras/curves/SplineCurve3.js + var lastargs = this.actions[ this.actions.length - 1].args; + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; -/************************************************************** - * Spline 3D curve - **************************************************************/ + this.absellipse(aX + x0, aY + y0, xRadius, yRadius, + aStartAngle, aEndAngle, aClockwise ); + }; -THREE.SplineCurve3 = THREE.Curve.create( - function ( points /* array of Vector3 */) { +THREE.Path.prototype.absellipse = function ( aX, aY, xRadius, yRadius, + aStartAngle, aEndAngle, aClockwise ) { + + var args = Array.prototype.slice.call( arguments ); + var curve = new THREE.EllipseCurve( aX, aY, xRadius, yRadius, + aStartAngle, aEndAngle, aClockwise ); + this.curves.push( curve ); - this.points = (points == undefined) ? [] : points; + var lastPoint = curve.getPoint(1); + args.push(lastPoint.x); + args.push(lastPoint.y); - }, + this.actions.push( { action: THREE.PathActions.ELLIPSE, args: args } ); - function ( t ) { + }; - var v = new THREE.Vector3(); - var c = []; - var points = this.points, point, intPoint, weight; - point = ( points.length - 1 ) * t; +THREE.Path.prototype.getSpacedPoints = function ( divisions, closedPath ) { - intPoint = Math.floor( point ); - weight = point - intPoint; + if ( ! divisions ) divisions = 40; - c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1; - c[ 1 ] = intPoint; - c[ 2 ] = intPoint > points.length - 2 ? points.length - 1 : intPoint + 1; - c[ 3 ] = intPoint > points.length - 3 ? points.length - 1 : intPoint + 2; + var points = []; - var pt0 = points[ c[0] ], - pt1 = points[ c[1] ], - pt2 = points[ c[2] ], - pt3 = points[ c[3] ]; + for ( var i = 0; i < divisions; i ++ ) { - v.x = THREE.Curve.Utils.interpolate(pt0.x, pt1.x, pt2.x, pt3.x, weight); - v.y = THREE.Curve.Utils.interpolate(pt0.y, pt1.y, pt2.y, pt3.y, weight); - v.z = THREE.Curve.Utils.interpolate(pt0.z, pt1.z, pt2.z, pt3.z, weight); + points.push( this.getPoint( i / divisions ) ); - return v; + //if( !this.getPoint( i / divisions ) ) throw "DIE"; } -); + // if ( closedPath ) { + // + // points.push( points[ 0 ] ); + // + // } + return points; -// THREE.SplineCurve3.prototype.getTangent = function(t) { -// var v = new THREE.Vector3(); -// var c = []; -// var points = this.points, point, intPoint, weight; -// point = ( points.length - 1 ) * t; +}; -// intPoint = Math.floor( point ); -// weight = point - intPoint; +/* Return an array of vectors based on contour of the path */ -// c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1; -// c[ 1 ] = intPoint; -// c[ 2 ] = intPoint > points.length - 2 ? points.length - 1 : intPoint + 1; -// c[ 3 ] = intPoint > points.length - 3 ? points.length - 1 : intPoint + 2; +THREE.Path.prototype.getPoints = function( divisions, closedPath ) { -// var pt0 = points[ c[0] ], -// pt1 = points[ c[1] ], -// pt2 = points[ c[2] ], -// pt3 = points[ c[3] ]; + if (this.useSpacedPoints) { + console.log('tata'); + return this.getSpacedPoints( divisions, closedPath ); + } -// // t = weight; -// v.x = THREE.Curve.Utils.tangentSpline( t, pt0.x, pt1.x, pt2.x, pt3.x ); -// v.y = THREE.Curve.Utils.tangentSpline( t, pt0.y, pt1.y, pt2.y, pt3.y ); -// v.z = THREE.Curve.Utils.tangentSpline( t, pt0.z, pt1.z, pt2.z, pt3.z ); + divisions = divisions || 12; -// return v; + var points = []; -// } + var i, il, item, action, args; + var cpx, cpy, cpx2, cpy2, cpx1, cpy1, cpx0, cpy0, + laste, j, + t, tx, ty; -// File:src/extras/curves/ClosedSplineCurve3.js + for ( i = 0, il = this.actions.length; i < il; i ++ ) { -/************************************************************** - * Closed Spline 3D curve - **************************************************************/ + item = this.actions[ i ]; + action = item.action; + args = item.args; -THREE.ClosedSplineCurve3 = THREE.Curve.create( + switch( action ) { - function ( points /* array of Vector3 */) { + case THREE.PathActions.MOVE_TO: - this.points = (points == undefined) ? [] : points; + points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) ); - }, + break; - function ( t ) { + case THREE.PathActions.LINE_TO: - var v = new THREE.Vector3(); - var c = []; - var points = this.points, point, intPoint, weight; - point = ( points.length - 0 ) * t; - // This needs to be from 0-length +1 + points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) ); - intPoint = Math.floor( point ); - weight = point - intPoint; + break; - intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length; - c[ 0 ] = ( intPoint - 1 ) % points.length; - c[ 1 ] = ( intPoint ) % points.length; - c[ 2 ] = ( intPoint + 1 ) % points.length; - c[ 3 ] = ( intPoint + 2 ) % points.length; + case THREE.PathActions.QUADRATIC_CURVE_TO: - v.x = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].x, points[ c[ 1 ] ].x, points[ c[ 2 ] ].x, points[ c[ 3 ] ].x, weight ); - v.y = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].y, points[ c[ 1 ] ].y, points[ c[ 2 ] ].y, points[ c[ 3 ] ].y, weight ); - v.z = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].z, points[ c[ 1 ] ].z, points[ c[ 2 ] ].z, points[ c[ 3 ] ].z, weight ); + cpx = args[ 2 ]; + cpy = args[ 3 ]; - return v; + cpx1 = args[ 0 ]; + cpy1 = args[ 1 ]; - } + if ( points.length > 0 ) { -); + laste = points[ points.length - 1 ]; -// File:src/extras/animation/AnimationHandler.js + cpx0 = laste.x; + cpy0 = laste.y; -/** - * @author mikael emtinger / http://gomo.se/ - */ + } else { -THREE.AnimationHandler = { + laste = this.actions[ i - 1 ].args; - LINEAR: 0, - CATMULLROM: 1, - CATMULLROM_FORWARD: 2, + cpx0 = laste[ laste.length - 2 ]; + cpy0 = laste[ laste.length - 1 ]; - // + } - add: function () { console.warn( 'THREE.AnimationHandler.add() has been deprecated.' ); }, - get: function () { console.warn( 'THREE.AnimationHandler.get() has been deprecated.' ); }, - remove: function () { console.warn( 'THREE.AnimationHandler.remove() has been deprecated.' ); }, + for ( j = 1; j <= divisions; j ++ ) { - // + t = j / divisions; - animations: [], + tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx ); + ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy ); - init: function ( data ) { + points.push( new THREE.Vector2( tx, ty ) ); - if ( data.initialized === true ) return; + } - // loop through all keys + break; - for ( var h = 0; h < data.hierarchy.length; h ++ ) { + case THREE.PathActions.BEZIER_CURVE_TO: - for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { + cpx = args[ 4 ]; + cpy = args[ 5 ]; - // remove minus times + cpx1 = args[ 0 ]; + cpy1 = args[ 1 ]; - if ( data.hierarchy[ h ].keys[ k ].time < 0 ) { + cpx2 = args[ 2 ]; + cpy2 = args[ 3 ]; - data.hierarchy[ h ].keys[ k ].time = 0; + if ( points.length > 0 ) { - } + laste = points[ points.length - 1 ]; - // create quaternions + cpx0 = laste.x; + cpy0 = laste.y; - if ( data.hierarchy[ h ].keys[ k ].rot !== undefined && - ! ( data.hierarchy[ h ].keys[ k ].rot instanceof THREE.Quaternion ) ) { + } else { - var quat = data.hierarchy[ h ].keys[ k ].rot; - data.hierarchy[ h ].keys[ k ].rot = new THREE.Quaternion().fromArray( quat ); + laste = this.actions[ i - 1 ].args; - } + cpx0 = laste[ laste.length - 2 ]; + cpy0 = laste[ laste.length - 1 ]; } - // prepare morph target keys - - if ( data.hierarchy[ h ].keys.length && data.hierarchy[ h ].keys[ 0 ].morphTargets !== undefined ) { - // get all used + for ( j = 1; j <= divisions; j ++ ) { - var usedMorphTargets = {}; + t = j / divisions; - for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { + tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx ); + ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy ); - for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) { + points.push( new THREE.Vector2( tx, ty ) ); - var morphTargetName = data.hierarchy[ h ].keys[ k ].morphTargets[ m ]; - usedMorphTargets[ morphTargetName ] = - 1; + } - } + break; - } + case THREE.PathActions.CSPLINE_THRU: - data.hierarchy[ h ].usedMorphTargets = usedMorphTargets; + laste = this.actions[ i - 1 ].args; + var last = new THREE.Vector2( laste[ laste.length - 2 ], laste[ laste.length - 1 ] ); + var spts = [ last ]; - // set all used on all frames + var n = divisions * args[ 0 ].length; - for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { + spts = spts.concat( args[ 0 ] ); - var influences = {}; + var spline = new THREE.SplineCurve( spts ); - for ( var morphTargetName in usedMorphTargets ) { + for ( j = 1; j <= n; j ++ ) { - for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) { + points.push( spline.getPointAt( j / n ) ) ; - if ( data.hierarchy[ h ].keys[ k ].morphTargets[ m ] === morphTargetName ) { + } - influences[ morphTargetName ] = data.hierarchy[ h ].keys[ k ].morphTargetsInfluences[ m ]; - break; + break; - } + case THREE.PathActions.ARC: - } + var aX = args[ 0 ], aY = args[ 1 ], + aRadius = args[ 2 ], + aStartAngle = args[ 3 ], aEndAngle = args[ 4 ], + aClockwise = !! args[ 5 ]; - if ( m === data.hierarchy[ h ].keys[ k ].morphTargets.length ) { + var deltaAngle = aEndAngle - aStartAngle; + var angle; + var tdivisions = divisions * 2; - influences[ morphTargetName ] = 0; + for ( j = 1; j <= tdivisions; j ++ ) { - } + t = j / tdivisions; - } + if ( ! aClockwise ) { - data.hierarchy[ h ].keys[ k ].morphTargetsInfluences = influences; + t = 1 - t; } - } + angle = aStartAngle + t * deltaAngle; + tx = aX + aRadius * Math.cos( angle ); + ty = aY + aRadius * Math.sin( angle ); - // remove all keys that are on the same time + //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty); - for ( var k = 1; k < data.hierarchy[ h ].keys.length; k ++ ) { + points.push( new THREE.Vector2( tx, ty ) ); - if ( data.hierarchy[ h ].keys[ k ].time === data.hierarchy[ h ].keys[ k - 1 ].time ) { + } - data.hierarchy[ h ].keys.splice( k, 1 ); - k --; + //console.log(points); - } + break; + + case THREE.PathActions.ELLIPSE: - } + var aX = args[ 0 ], aY = args[ 1 ], + xRadius = args[ 2 ], + yRadius = args[ 3 ], + aStartAngle = args[ 4 ], aEndAngle = args[ 5 ], + aClockwise = !! args[ 6 ]; - // set index + var deltaAngle = aEndAngle - aStartAngle; + var angle; + var tdivisions = divisions * 2; - for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { + for ( j = 1; j <= tdivisions; j ++ ) { - data.hierarchy[ h ].keys[ k ].index = k; + t = j / tdivisions; - } + if ( ! aClockwise ) { - } + t = 1 - t; - data.initialized = true; + } - return data; + angle = aStartAngle + t * deltaAngle; - }, + tx = aX + xRadius * Math.cos( angle ); + ty = aY + yRadius * Math.sin( angle ); - parse: function ( root ) { + //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty); - var parseRecurseHierarchy = function ( root, hierarchy ) { + points.push( new THREE.Vector2( tx, ty ) ); - hierarchy.push( root ); + } - for ( var c = 0; c < root.children.length; c ++ ) - parseRecurseHierarchy( root.children[ c ], hierarchy ); + //console.log(points); - }; + break; - // setup hierarchy + } // end switch - var hierarchy = []; + } - if ( root instanceof THREE.SkinnedMesh ) { - for ( var b = 0; b < root.skeleton.bones.length; b ++ ) { - hierarchy.push( root.skeleton.bones[ b ] ); + // Normalize to remove the closing point by default. + var lastPoint = points[ points.length - 1]; + var EPSILON = 0.0000000001; + if ( Math.abs(lastPoint.x - points[ 0 ].x) < EPSILON && + Math.abs(lastPoint.y - points[ 0 ].y) < EPSILON) + points.splice( points.length - 1, 1); + if ( closedPath ) { - } + points.push( points[ 0 ] ); - } else { + } - parseRecurseHierarchy( root, hierarchy ); + return points; - } +}; - return hierarchy; +// +// Breaks path into shapes +// +// Assumptions (if parameter isCCW==true the opposite holds): +// - solid shapes are defined clockwise (CW) +// - holes are defined counterclockwise (CCW) +// +// If parameter noHoles==true: +// - all subPaths are regarded as solid shapes +// - definition order CW/CCW has no relevance +// - }, +THREE.Path.prototype.toShapes = function( isCCW, noHoles ) { - play: function ( animation ) { + function extractSubpaths( inActions ) { + + var i, il, item, action, args; - if ( this.animations.indexOf( animation ) === - 1 ) { + var subPaths = [], lastPath = new THREE.Path(); - this.animations.push( animation ); + for ( i = 0, il = inActions.length; i < il; i ++ ) { - } + item = inActions[ i ]; - }, + args = item.args; + action = item.action; - stop: function ( animation ) { + if ( action == THREE.PathActions.MOVE_TO ) { - var index = this.animations.indexOf( animation ); + if ( lastPath.actions.length != 0 ) { - if ( index !== - 1 ) { + subPaths.push( lastPath ); + lastPath = new THREE.Path(); - this.animations.splice( index, 1 ); + } - } + } - }, + lastPath[ action ].apply( lastPath, args ); - update: function ( deltaTimeMS ) { + } - for ( var i = 0; i < this.animations.length; i ++ ) { + if ( lastPath.actions.length != 0 ) { - this.animations[ i ].update( deltaTimeMS ); + subPaths.push( lastPath ); } - } + // console.log(subPaths); -}; + return subPaths; + } -// File:src/extras/animation/Animation.js + function toShapesNoHoles( inSubpaths ) { -/** - * @author mikael emtinger / http://gomo.se/ - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ + var shapes = []; -THREE.Animation = function ( root, data ) { + for ( var i = 0, il = inSubpaths.length; i < il; i ++ ) { - this.root = root; - this.data = THREE.AnimationHandler.init( data ); - this.hierarchy = THREE.AnimationHandler.parse( root ); + var tmpPath = inSubpaths[ i ]; - this.currentTime = 0; - this.timeScale = 1; + var tmpShape = new THREE.Shape(); + tmpShape.actions = tmpPath.actions; + tmpShape.curves = tmpPath.curves; - this.isPlaying = false; - this.loop = true; - this.weight = 0; + shapes.push( tmpShape ); + } - this.interpolationType = THREE.AnimationHandler.LINEAR; + //console.log("shape", shapes); -}; + return shapes; + }; + function isPointInsidePolygon( inPt, inPolygon ) { + var EPSILON = 0.0000000001; -THREE.Animation.prototype.keyTypes = [ "pos", "rot", "scl" ]; + var polyLen = inPolygon.length; + // inPt on polygon contour => immediate success or + // toggling of inside/outside at every single! intersection point of an edge + // with the horizontal line through inPt, left of inPt + // not counting lowerY endpoints of edges and whole edges on that line + var inside = false; + for( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) { + var edgeLowPt = inPolygon[ p ]; + var edgeHighPt = inPolygon[ q ]; -THREE.Animation.prototype.play = function ( startTime, weight ) { + var edgeDx = edgeHighPt.x - edgeLowPt.x; + var edgeDy = edgeHighPt.y - edgeLowPt.y; - this.currentTime = startTime !== undefined ? startTime : 0; - this.weight = weight !== undefined ? weight: 1; + if ( Math.abs(edgeDy) > EPSILON ) { // not parallel + if ( edgeDy < 0 ) { + edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx; + edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy; + } + if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue; - this.isPlaying = true; + if ( inPt.y == edgeLowPt.y ) { + if ( inPt.x == edgeLowPt.x ) return true; // inPt is on contour ? + // continue; // no intersection or edgeLowPt => doesn't count !!! + } else { + var perpEdge = edgeDy * (inPt.x - edgeLowPt.x) - edgeDx * (inPt.y - edgeLowPt.y); + if ( perpEdge == 0 ) return true; // inPt is on contour ? + if ( perpEdge < 0 ) continue; + inside = ! inside; // true intersection left of inPt + } + } else { // parallel or colinear + if ( inPt.y != edgeLowPt.y ) continue; // parallel + // egde lies on the same horizontal line as inPt + if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) || + ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour ! + // continue; + } + } - this.reset(); + return inside; + } - THREE.AnimationHandler.play( this ); -}; + var subPaths = extractSubpaths( this.actions ); + if ( subPaths.length == 0 ) return []; + if ( noHoles === true ) return toShapesNoHoles( subPaths ); -THREE.Animation.prototype.stop = function() { - this.isPlaying = false; + var solid, tmpPath, tmpShape, shapes = []; - THREE.AnimationHandler.stop( this ); + if ( subPaths.length == 1) { -}; + tmpPath = subPaths[0]; + tmpShape = new THREE.Shape(); + tmpShape.actions = tmpPath.actions; + tmpShape.curves = tmpPath.curves; + shapes.push( tmpShape ); + return shapes; -THREE.Animation.prototype.reset = function () { + } - for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { + var holesFirst = ! THREE.Shape.Utils.isClockWise( subPaths[ 0 ].getPoints() ); + holesFirst = isCCW ? ! holesFirst : holesFirst; - var object = this.hierarchy[ h ]; + // console.log("Holes first", holesFirst); + + var betterShapeHoles = []; + var newShapes = []; + var newShapeHoles = []; + var mainIdx = 0; + var tmpPoints; - object.matrixAutoUpdate = true; + newShapes[mainIdx] = undefined; + newShapeHoles[mainIdx] = []; - if ( object.animationCache === undefined ) { + var i, il; - object.animationCache = {}; + for ( i = 0, il = subPaths.length; i < il; i ++ ) { - } + tmpPath = subPaths[ i ]; + tmpPoints = tmpPath.getPoints(); + solid = THREE.Shape.Utils.isClockWise( tmpPoints ); + solid = isCCW ? ! solid : solid; - if ( object.animationCache[this.data.name] === undefined ) { + if ( solid ) { - object.animationCache[this.data.name] = {}; - object.animationCache[this.data.name].prevKey = { pos: 0, rot: 0, scl: 0 }; - object.animationCache[this.data.name].nextKey = { pos: 0, rot: 0, scl: 0 }; - object.animationCache[this.data.name].originalMatrix = object.matrix; + if ( (! holesFirst ) && ( newShapes[mainIdx] ) ) mainIdx ++; - } + newShapes[mainIdx] = { s: new THREE.Shape(), p: tmpPoints }; + newShapes[mainIdx].s.actions = tmpPath.actions; + newShapes[mainIdx].s.curves = tmpPath.curves; + + if ( holesFirst ) mainIdx ++; + newShapeHoles[mainIdx] = []; - var animationCache = object.animationCache[this.data.name]; + //console.log('cw', i); - // Get keys to match our current time + } else { - for ( var t = 0; t < 3; t ++ ) { + newShapeHoles[mainIdx].push( { h: tmpPath, p: tmpPoints[0] } ); - var type = this.keyTypes[ t ]; + //console.log('ccw', i); - var prevKey = this.data.hierarchy[ h ].keys[ 0 ]; - var nextKey = this.getNextKeyWith( type, h, 1 ); + } - while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) { + } - prevKey = nextKey; - nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 ); + // only Holes? -> probably all Shapes with wrong orientation + if ( ! newShapes[0] ) return toShapesNoHoles( subPaths ); - } - animationCache.prevKey[ type ] = prevKey; - animationCache.nextKey[ type ] = nextKey; + if ( newShapes.length > 1 ) { + var ambigious = false; + var toChange = []; + for (var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { + betterShapeHoles[sIdx] = []; + } + for (var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { + var sh = newShapes[sIdx]; + var sho = newShapeHoles[sIdx]; + for (var hIdx = 0; hIdx < sho.length; hIdx ++ ) { + var ho = sho[hIdx]; + var hole_unassigned = true; + for (var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) { + if ( isPointInsidePolygon( ho.p, newShapes[s2Idx].p ) ) { + if ( sIdx != s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } ); + if ( hole_unassigned ) { + hole_unassigned = false; + betterShapeHoles[s2Idx].push( ho ); + } else { + ambigious = true; + } + } + } + if ( hole_unassigned ) { betterShapeHoles[sIdx].push( ho ); } + } + } + // console.log("ambigious: ", ambigious); + if ( toChange.length > 0 ) { + // console.log("to change: ", toChange); + if (! ambigious) newShapeHoles = betterShapeHoles; } + } + var tmpHoles, j, jl; + for ( i = 0, il = newShapes.length; i < il; i ++ ) { + tmpShape = newShapes[i].s; + shapes.push( tmpShape ); + tmpHoles = newShapeHoles[i]; + for ( j = 0, jl = tmpHoles.length; j < jl; j ++ ) { + tmpShape.holes.push( tmpHoles[j].h ); + } } + //console.log("shape", shapes); + + return shapes; + }; +// File:src/extras/core/Shape.js -THREE.Animation.prototype.update = (function(){ +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * Defines a 2d shape plane using paths. + **/ - var points = []; - var target = new THREE.Vector3(); - var newVector = new THREE.Vector3(); - var newQuat = new THREE.Quaternion(); +// STEP 1 Create a path. +// STEP 2 Turn path into shape. +// STEP 3 ExtrudeGeometry takes in Shape/Shapes +// STEP 3a - Extract points from each shape, turn to vertices +// STEP 3b - Triangulate each shape, add faces. - // Catmull-Rom spline +THREE.Shape = function () { - var interpolateCatmullRom = function ( points, scale ) { + THREE.Path.apply( this, arguments ); + this.holes = []; - var c = [], v3 = [], - point, intPoint, weight, w2, w3, - pa, pb, pc, pd; +}; - point = ( points.length - 1 ) * scale; - intPoint = Math.floor( point ); - weight = point - intPoint; +THREE.Shape.prototype = Object.create( THREE.Path.prototype ); - c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1; - c[ 1 ] = intPoint; - c[ 2 ] = intPoint > points.length - 2 ? intPoint : intPoint + 1; - c[ 3 ] = intPoint > points.length - 3 ? intPoint : intPoint + 2; +// Convenience method to return ExtrudeGeometry + +THREE.Shape.prototype.extrude = function ( options ) { + + var extruded = new THREE.ExtrudeGeometry( this, options ); + return extruded; - pa = points[ c[ 0 ] ]; - pb = points[ c[ 1 ] ]; - pc = points[ c[ 2 ] ]; - pd = points[ c[ 3 ] ]; +}; - w2 = weight * weight; - w3 = weight * w2; +// Convenience method to return ShapeGeometry - v3[ 0 ] = interpolate( pa[ 0 ], pb[ 0 ], pc[ 0 ], pd[ 0 ], weight, w2, w3 ); - v3[ 1 ] = interpolate( pa[ 1 ], pb[ 1 ], pc[ 1 ], pd[ 1 ], weight, w2, w3 ); - v3[ 2 ] = interpolate( pa[ 2 ], pb[ 2 ], pc[ 2 ], pd[ 2 ], weight, w2, w3 ); +THREE.Shape.prototype.makeGeometry = function ( options ) { - return v3; + var geometry = new THREE.ShapeGeometry( this, options ); + return geometry; - }; +}; - var interpolate = function ( p0, p1, p2, p3, t, t2, t3 ) { +// Get points of holes - var v0 = ( p2 - p0 ) * 0.5, - v1 = ( p3 - p1 ) * 0.5; +THREE.Shape.prototype.getPointsHoles = function ( divisions ) { - return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1; + var i, il = this.holes.length, holesPts = []; - }; + for ( i = 0; i < il; i ++ ) { - return function ( delta ) { + holesPts[ i ] = this.holes[ i ].getTransformedPoints( divisions, this.bends ); - if ( this.isPlaying === false ) return; + } - this.currentTime += delta * this.timeScale; + return holesPts; - if ( this.weight === 0 ) - return; +}; - // +// Get points of holes (spaced by regular distance) - var duration = this.data.length; +THREE.Shape.prototype.getSpacedPointsHoles = function ( divisions ) { - if ( this.loop === true && this.currentTime > duration ) { + var i, il = this.holes.length, holesPts = []; - this.currentTime %= duration; - this.reset(); + for ( i = 0; i < il; i ++ ) { - } else if ( this.loop === false && this.currentTime > duration ) { + holesPts[ i ] = this.holes[ i ].getTransformedSpacedPoints( divisions, this.bends ); - this.stop(); - return; + } - } + return holesPts; - for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { +}; - var object = this.hierarchy[ h ]; - var animationCache = object.animationCache[this.data.name]; - // loop through pos/rot/scl +// Get points of shape and holes (keypoints based on segments parameter) - for ( var t = 0; t < 3; t ++ ) { +THREE.Shape.prototype.extractAllPoints = function ( divisions ) { - // get keys + return { - var type = this.keyTypes[ t ]; - var prevKey = animationCache.prevKey[ type ]; - var nextKey = animationCache.nextKey[ type ]; + shape: this.getTransformedPoints( divisions ), + holes: this.getPointsHoles( divisions ) - if ( nextKey.time <= this.currentTime ) { + }; - prevKey = this.data.hierarchy[ h ].keys[ 0 ]; - nextKey = this.getNextKeyWith( type, h, 1 ); +}; - while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) { +THREE.Shape.prototype.extractPoints = function ( divisions ) { - prevKey = nextKey; - nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 ); + if (this.useSpacedPoints) { + return this.extractAllSpacedPoints(divisions); + } - } + return this.extractAllPoints(divisions); - animationCache.prevKey[ type ] = prevKey; - animationCache.nextKey[ type ] = nextKey; +}; - } +// +// THREE.Shape.prototype.extractAllPointsWithBend = function ( divisions, bend ) { +// +// return { +// +// shape: this.transform( bend, divisions ), +// holes: this.getPointsHoles( divisions, bend ) +// +// }; +// +// }; - object.matrixAutoUpdate = true; - object.matrixWorldNeedsUpdate = true; +// Get points of shape and holes (spaced by regular distance) - var scale = ( this.currentTime - prevKey.time ) / ( nextKey.time - prevKey.time ); +THREE.Shape.prototype.extractAllSpacedPoints = function ( divisions ) { - var prevXYZ = prevKey[ type ]; - var nextXYZ = nextKey[ type ]; + return { - if ( scale < 0 ) scale = 0; - if ( scale > 1 ) scale = 1; + shape: this.getTransformedSpacedPoints( divisions ), + holes: this.getSpacedPointsHoles( divisions ) - // interpolate + }; - if ( type === "pos" ) { +}; - if ( this.interpolationType === THREE.AnimationHandler.LINEAR ) { +/************************************************************** + * Utils + **************************************************************/ - newVector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale; - newVector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale; - newVector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale; +THREE.Shape.Utils = { - // blend - if ( object instanceof THREE.Bone ) { + triangulateShape: function ( contour, holes ) { + + function point_in_segment_2D_colin( inSegPt1, inSegPt2, inOtherPt ) { + // inOtherPt needs to be colinear to the inSegment + if ( inSegPt1.x != inSegPt2.x ) { + if ( inSegPt1.x < inSegPt2.x ) { + return ( ( inSegPt1.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt2.x ) ); + } else { + return ( ( inSegPt2.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt1.x ) ); + } + } else { + if ( inSegPt1.y < inSegPt2.y ) { + return ( ( inSegPt1.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt2.y ) ); + } else { + return ( ( inSegPt2.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt1.y ) ); + } + } + } - var proportionalWeight = this.weight / ( this.weight + object.accumulatedPosWeight ); - object.position.lerp( newVector, proportionalWeight ); - object.accumulatedPosWeight += this.weight; + function intersect_segments_2D( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1, inSeg2Pt2, inExcludeAdjacentSegs ) { + var EPSILON = 0.0000000001; - } else { + var seg1dx = inSeg1Pt2.x - inSeg1Pt1.x, seg1dy = inSeg1Pt2.y - inSeg1Pt1.y; + var seg2dx = inSeg2Pt2.x - inSeg2Pt1.x, seg2dy = inSeg2Pt2.y - inSeg2Pt1.y; - object.position.copy( newVector ); + var seg1seg2dx = inSeg1Pt1.x - inSeg2Pt1.x; + var seg1seg2dy = inSeg1Pt1.y - inSeg2Pt1.y; - } + var limit = seg1dy * seg2dx - seg1dx * seg2dy; + var perpSeg1 = seg1dy * seg1seg2dx - seg1dx * seg1seg2dy; - } else if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || - this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + if ( Math.abs(limit) > EPSILON ) { // not parallel - points[ 0 ] = this.getPrevKeyWith( "pos", h, prevKey.index - 1 )[ "pos" ]; - points[ 1 ] = prevXYZ; - points[ 2 ] = nextXYZ; - points[ 3 ] = this.getNextKeyWith( "pos", h, nextKey.index + 1 )[ "pos" ]; + var perpSeg2; + if ( limit > 0 ) { + if ( ( perpSeg1 < 0 ) || ( perpSeg1 > limit ) ) return []; + perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy; + if ( ( perpSeg2 < 0 ) || ( perpSeg2 > limit ) ) return []; + } else { + if ( ( perpSeg1 > 0 ) || ( perpSeg1 < limit ) ) return []; + perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy; + if ( ( perpSeg2 > 0 ) || ( perpSeg2 < limit ) ) return []; + } - scale = scale * 0.33 + 0.33; + // i.e. to reduce rounding errors + // intersection at endpoint of segment#1? + if ( perpSeg2 == 0 ) { + if ( ( inExcludeAdjacentSegs ) && + ( ( perpSeg1 == 0 ) || ( perpSeg1 == limit ) ) ) return []; + return [ inSeg1Pt1 ]; + } + if ( perpSeg2 == limit ) { + if ( ( inExcludeAdjacentSegs ) && + ( ( perpSeg1 == 0 ) || ( perpSeg1 == limit ) ) ) return []; + return [ inSeg1Pt2 ]; + } + // intersection at endpoint of segment#2? + if ( perpSeg1 == 0 ) return [ inSeg2Pt1 ]; + if ( perpSeg1 == limit ) return [ inSeg2Pt2 ]; - var currentPoint = interpolateCatmullRom( points, scale ); - var proportionalWeight = 1; - - if ( object instanceof THREE.Bone ) { + // return real intersection point + var factorSeg1 = perpSeg2 / limit; + return [ { x: inSeg1Pt1.x + factorSeg1 * seg1dx, + y: inSeg1Pt1.y + factorSeg1 * seg1dy } ]; - proportionalWeight = this.weight / ( this.weight + object.accumulatedPosWeight ); - object.accumulatedPosWeight += this.weight; + } else { // parallel or colinear + if ( ( perpSeg1 != 0 ) || + ( seg2dy * seg1seg2dx != seg2dx * seg1seg2dy ) ) return []; - } + // they are collinear or degenerate + var seg1Pt = ( (seg1dx == 0) && (seg1dy == 0) ); // segment1 ist just a point? + var seg2Pt = ( (seg2dx == 0) && (seg2dy == 0) ); // segment2 ist just a point? + // both segments are points + if ( seg1Pt && seg2Pt ) { + if ( (inSeg1Pt1.x != inSeg2Pt1.x) || + (inSeg1Pt1.y != inSeg2Pt1.y) ) return []; // they are distinct points + return [ inSeg1Pt1 ]; // they are the same point + } + // segment#1 is a single point + if ( seg1Pt ) { + if (! point_in_segment_2D_colin( inSeg2Pt1, inSeg2Pt2, inSeg1Pt1 ) ) return []; // but not in segment#2 + return [ inSeg1Pt1 ]; + } + // segment#2 is a single point + if ( seg2Pt ) { + if (! point_in_segment_2D_colin( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1 ) ) return []; // but not in segment#1 + return [ inSeg2Pt1 ]; + } - // blend + // they are collinear segments, which might overlap + var seg1min, seg1max, seg1minVal, seg1maxVal; + var seg2min, seg2max, seg2minVal, seg2maxVal; + if (seg1dx != 0) { // the segments are NOT on a vertical line + if ( inSeg1Pt1.x < inSeg1Pt2.x ) { + seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.x; + seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.x; + } else { + seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.x; + seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.x; + } + if ( inSeg2Pt1.x < inSeg2Pt2.x ) { + seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.x; + seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.x; + } else { + seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.x; + seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.x; + } + } else { // the segments are on a vertical line + if ( inSeg1Pt1.y < inSeg1Pt2.y ) { + seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.y; + seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.y; + } else { + seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.y; + seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.y; + } + if ( inSeg2Pt1.y < inSeg2Pt2.y ) { + seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.y; + seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.y; + } else { + seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.y; + seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.y; + } + } + if ( seg1minVal <= seg2minVal ) { + if ( seg1maxVal < seg2minVal ) return []; + if ( seg1maxVal == seg2minVal ) { + if ( inExcludeAdjacentSegs ) return []; + return [ seg2min ]; + } + if ( seg1maxVal <= seg2maxVal ) return [ seg2min, seg1max ]; + return [ seg2min, seg2max ]; + } else { + if ( seg1minVal > seg2maxVal ) return []; + if ( seg1minVal == seg2maxVal ) { + if ( inExcludeAdjacentSegs ) return []; + return [ seg1min ]; + } + if ( seg1maxVal <= seg2maxVal ) return [ seg1min, seg1max ]; + return [ seg1min, seg2max ]; + } + } + } - var vector = object.position; - - vector.x = vector.x + ( currentPoint[ 0 ] - vector.x ) * proportionalWeight; - vector.y = vector.y + ( currentPoint[ 1 ] - vector.y ) * proportionalWeight; - vector.z = vector.z + ( currentPoint[ 2 ] - vector.z ) * proportionalWeight; + function isPointInsideAngle( inVertex, inLegFromPt, inLegToPt, inOtherPt ) { + // The order of legs is important - if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + var EPSILON = 0.0000000001; - var forwardPoint = interpolateCatmullRom( points, scale * 1.01 ); + // translation of all points, so that Vertex is at (0,0) + var legFromPtX = inLegFromPt.x - inVertex.x, legFromPtY = inLegFromPt.y - inVertex.y; + var legToPtX = inLegToPt.x - inVertex.x, legToPtY = inLegToPt.y - inVertex.y; + var otherPtX = inOtherPt.x - inVertex.x, otherPtY = inOtherPt.y - inVertex.y; - target.set( forwardPoint[ 0 ], forwardPoint[ 1 ], forwardPoint[ 2 ] ); - target.sub( vector ); - target.y = 0; - target.normalize(); + // main angle >0: < 180 deg.; 0: 180 deg.; <0: > 180 deg. + var from2toAngle = legFromPtX * legToPtY - legFromPtY * legToPtX; + var from2otherAngle = legFromPtX * otherPtY - legFromPtY * otherPtX; - var angle = Math.atan2( target.x, target.z ); - object.rotation.set( 0, angle, 0 ); + if ( Math.abs(from2toAngle) > EPSILON ) { // angle != 180 deg. - } + var other2toAngle = otherPtX * legToPtY - otherPtY * legToPtX; + // console.log( "from2to: " + from2toAngle + ", from2other: " + from2otherAngle + ", other2to: " + other2toAngle ); - } + if ( from2toAngle > 0 ) { // main angle < 180 deg. + return ( ( from2otherAngle >= 0 ) && ( other2toAngle >= 0 ) ); + } else { // main angle > 180 deg. + return ( ( from2otherAngle >= 0 ) || ( other2toAngle >= 0 ) ); + } + } else { // angle == 180 deg. + // console.log( "from2to: 180 deg., from2other: " + from2otherAngle ); + return ( from2otherAngle > 0 ); + } + } - } else if ( type === "rot" ) { - THREE.Quaternion.slerp( prevXYZ, nextXYZ, newQuat, scale ); + function removeHoles( contour, holes ) { - // Avoid paying the cost of an additional slerp if we don't have to - if ( ! ( object instanceof THREE.Bone ) ) { + var shape = contour.concat(); // work on this shape + var hole; - object.quaternion.copy(newQuat); + function isCutLineInsideAngles( inShapeIdx, inHoleIdx ) { + // Check if hole point lies within angle around shape point + var lastShapeIdx = shape.length - 1; - } else if ( object.accumulatedRotWeight === 0 ) { + var prevShapeIdx = inShapeIdx - 1; + if ( prevShapeIdx < 0 ) prevShapeIdx = lastShapeIdx; - object.quaternion.copy(newQuat); - object.accumulatedRotWeight = this.weight; + var nextShapeIdx = inShapeIdx + 1; + if ( nextShapeIdx > lastShapeIdx ) nextShapeIdx = 0; - } else { + var insideAngle = isPointInsideAngle( shape[inShapeIdx], shape[ prevShapeIdx ], shape[ nextShapeIdx ], hole[inHoleIdx] ); + if (! insideAngle ) { + // console.log( "Vertex (Shape): " + inShapeIdx + ", Point: " + hole[inHoleIdx].x + "/" + hole[inHoleIdx].y ); + return false; + } - var proportionalWeight = this.weight / ( this.weight + object.accumulatedRotWeight ); - THREE.Quaternion.slerp( object.quaternion, newQuat, object.quaternion, proportionalWeight ); - object.accumulatedRotWeight += this.weight; + // Check if shape point lies within angle around hole point + var lastHoleIdx = hole.length - 1; - } + var prevHoleIdx = inHoleIdx - 1; + if ( prevHoleIdx < 0 ) prevHoleIdx = lastHoleIdx; - } else if ( type === "scl" ) { + var nextHoleIdx = inHoleIdx + 1; + if ( nextHoleIdx > lastHoleIdx ) nextHoleIdx = 0; - newVector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale; - newVector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale; - newVector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale; + insideAngle = isPointInsideAngle( hole[inHoleIdx], hole[ prevHoleIdx ], hole[ nextHoleIdx ], shape[inShapeIdx] ); + if (! insideAngle ) { + // console.log( "Vertex (Hole): " + inHoleIdx + ", Point: " + shape[inShapeIdx].x + "/" + shape[inShapeIdx].y ); + return false; + } - if ( object instanceof THREE.Bone ) { + return true; + } - var proportionalWeight = this.weight / ( this.weight + object.accumulatedSclWeight); - object.scale.lerp( newVector, proportionalWeight ); - object.accumulatedSclWeight += this.weight; + function intersectsShapeEdge( inShapePt, inHolePt ) { + // checks for intersections with shape edges + var sIdx, nextIdx, intersection; + for ( sIdx = 0; sIdx < shape.length; sIdx ++ ) { + nextIdx = sIdx+1; nextIdx %= shape.length; + intersection = intersect_segments_2D( inShapePt, inHolePt, shape[sIdx], shape[nextIdx], true ); + if ( intersection.length > 0 ) return true; + } - } else { + return false; + } - object.scale.copy( newVector ); + var indepHoles = []; + function intersectsHoleEdge( inShapePt, inHolePt ) { + // checks for intersections with hole edges + var ihIdx, chkHole, + hIdx, nextIdx, intersection; + for ( ihIdx = 0; ihIdx < indepHoles.length; ihIdx ++ ) { + chkHole = holes[indepHoles[ihIdx]]; + for ( hIdx = 0; hIdx < chkHole.length; hIdx ++ ) { + nextIdx = hIdx+1; nextIdx %= chkHole.length; + intersection = intersect_segments_2D( inShapePt, inHolePt, chkHole[hIdx], chkHole[nextIdx], true ); + if ( intersection.length > 0 ) return true; } - } - + return false; } - } - - return true; - - }; + var holeIndex, shapeIndex, + shapePt, holePt, + holeIdx, cutKey, failedCuts = [], + tmpShape1, tmpShape2, + tmpHole1, tmpHole2; -})(); + for ( var h = 0, hl = holes.length; h < hl; h ++ ) { + indepHoles.push( h ); + } + var minShapeIndex = 0; + var counter = indepHoles.length * 2; + while ( indepHoles.length > 0 ) { + counter --; + if ( counter < 0 ) { + console.log( "Infinite Loop! Holes left:" + indepHoles.length + ", Probably Hole outside Shape!" ); + break; + } + // search for shape-vertex and hole-vertex, + // which can be connected without intersections + for ( shapeIndex = minShapeIndex; shapeIndex < shape.length; shapeIndex ++ ) { -// Get next key with + shapePt = shape[ shapeIndex ]; + holeIndex = - 1; -THREE.Animation.prototype.getNextKeyWith = function ( type, h, key ) { + // search for hole which can be reached without intersections + for ( var h = 0; h < indepHoles.length; h ++ ) { + holeIdx = indepHoles[h]; - var keys = this.data.hierarchy[ h ].keys; + // prevent multiple checks + cutKey = shapePt.x + ":" + shapePt.y + ":" + holeIdx; + if ( failedCuts[cutKey] !== undefined ) continue; - if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || - this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + hole = holes[holeIdx]; + for ( var h2 = 0; h2 < hole.length; h2 ++ ) { + holePt = hole[ h2 ]; + if (! isCutLineInsideAngles( shapeIndex, h2 ) ) continue; + if ( intersectsShapeEdge( shapePt, holePt ) ) continue; + if ( intersectsHoleEdge( shapePt, holePt ) ) continue; - key = key < keys.length - 1 ? key : keys.length - 1; + holeIndex = h2; + indepHoles.splice(h,1); - } else { + tmpShape1 = shape.slice( 0, shapeIndex+1 ); + tmpShape2 = shape.slice( shapeIndex ); + tmpHole1 = hole.slice( holeIndex ); + tmpHole2 = hole.slice( 0, holeIndex+1 ); - key = key % keys.length; + shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 ); - } + minShapeIndex = shapeIndex; - for ( ; key < keys.length; key ++ ) { + // Debug only, to show the selected cuts + // glob_CutLines.push( [ shapePt, holePt ] ); - if ( keys[ key ][ type ] !== undefined ) { + break; + } + if ( holeIndex >= 0 ) break; // hole-vertex found - return keys[ key ]; + failedCuts[cutKey] = true; // remember failure + } + if ( holeIndex >= 0 ) break; // hole-vertex found + } + } + return shape; /* shape with no holes */ } - } - return this.data.hierarchy[ h ].keys[ 0 ]; + var i, il, f, face, + key, index, + allPointsMap = {}; -}; + // To maintain reference to old shape, one must match coordinates, or offset the indices from original arrays. It's probably easier to do the first. -// Get previous key with + var allpoints = contour.concat(); -THREE.Animation.prototype.getPrevKeyWith = function ( type, h, key ) { + for ( var h = 0, hl = holes.length; h < hl; h ++ ) { - var keys = this.data.hierarchy[ h ].keys; + Array.prototype.push.apply( allpoints, holes[h] ); - if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || - this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + } - key = key > 0 ? key : 0; + //console.log( "allpoints",allpoints, allpoints.length ); - } else { + // prepare all points map - key = key >= 0 ? key : key + keys.length; + for ( i = 0, il = allpoints.length; i < il; i ++ ) { - } + key = allpoints[ i ].x + ":" + allpoints[ i ].y; + if ( allPointsMap[ key ] !== undefined ) { - for ( ; key >= 0; key -- ) { + console.log( "Duplicate point", key ); - if ( keys[ key ][ type ] !== undefined ) { + } - return keys[ key ]; + allPointsMap[ key ] = i; } - } + // remove holes by cutting paths to holes and adding them to the shape + var shapeWithoutHoles = removeHoles( contour, holes ); - return this.data.hierarchy[ h ].keys[ keys.length - 1 ]; + var triangles = THREE.FontUtils.Triangulate( shapeWithoutHoles, false ); // True returns indices for points of spooled shape + //console.log( "triangles",triangles, triangles.length ); -}; + // check all face vertices against all points map -// File:src/extras/animation/KeyFrameAnimation.js + for ( i = 0, il = triangles.length; i < il; i ++ ) { -/** - * @author mikael emtinger / http://gomo.se/ - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * @author khang duong - * @author erik kitson - */ + face = triangles[ i ]; -THREE.KeyFrameAnimation = function ( data ) { + for ( f = 0; f < 3; f ++ ) { - this.root = data.node; - this.data = THREE.AnimationHandler.init( data ); - this.hierarchy = THREE.AnimationHandler.parse( this.root ); - this.currentTime = 0; - this.timeScale = 0.001; - this.isPlaying = false; - this.isPaused = true; - this.loop = true; + key = face[ f ].x + ":" + face[ f ].y; - // initialize to first keyframes + index = allPointsMap[ key ]; - for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { + if ( index !== undefined ) { - var keys = this.data.hierarchy[h].keys, - sids = this.data.hierarchy[h].sids, - obj = this.hierarchy[h]; + face[ f ] = index; - if ( keys.length && sids ) { + } - for ( var s = 0; s < sids.length; s ++ ) { + } - var sid = sids[ s ], - next = this.getNextKeyWith( sid, h, 0 ); + } + + return triangles.concat(); + + }, + + isClockWise: function ( pts ) { - if ( next ) { + return THREE.FontUtils.Triangulate.area( pts ) < 0; - next.apply( sid ); + }, - } + // Bezier Curves formulas obtained from + // http://en.wikipedia.org/wiki/B%C3%A9zier_curve - } + // Quad Bezier Functions - obj.matrixAutoUpdate = false; - this.data.hierarchy[h].node.updateMatrix(); - obj.matrixWorldNeedsUpdate = true; + b2p0: function ( t, p ) { - } + var k = 1 - t; + return k * k * p; - } + }, -}; + b2p1: function ( t, p ) { + return 2 * ( 1 - t ) * t * p; -THREE.KeyFrameAnimation.prototype.play = function ( startTime ) { + }, - this.currentTime = startTime !== undefined ? startTime : 0; + b2p2: function ( t, p ) { - if ( this.isPlaying === false ) { + return t * t * p; - this.isPlaying = true; + }, - // reset key cache + b2: function ( t, p0, p1, p2 ) { - var h, hl = this.hierarchy.length, - object, - node; + return this.b2p0( t, p0 ) + this.b2p1( t, p1 ) + this.b2p2( t, p2 ); - for ( h = 0; h < hl; h ++ ) { + }, - object = this.hierarchy[ h ]; - node = this.data.hierarchy[ h ]; + // Cubic Bezier Functions - if ( node.animationCache === undefined ) { + b3p0: function ( t, p ) { - node.animationCache = {}; - node.animationCache.prevKey = null; - node.animationCache.nextKey = null; - node.animationCache.originalMatrix = object.matrix; + var k = 1 - t; + return k * k * k * p; - } + }, - var keys = this.data.hierarchy[h].keys; + b3p1: function ( t, p ) { - if (keys.length) { + var k = 1 - t; + return 3 * k * k * t * p; - node.animationCache.prevKey = keys[ 0 ]; - node.animationCache.nextKey = keys[ 1 ]; + }, - this.startTime = Math.min( keys[0].time, this.startTime ); - this.endTime = Math.max( keys[keys.length - 1].time, this.endTime ); + b3p2: function ( t, p ) { - } + var k = 1 - t; + return 3 * k * t * t * p; - } + }, - this.update( 0 ); + b3p3: function ( t, p ) { - } + return t * t * t * p; - this.isPaused = false; + }, - THREE.AnimationHandler.play( this ); + b3: function ( t, p0, p1, p2, p3 ) { -}; + return this.b3p0( t, p0 ) + this.b3p1( t, p1 ) + this.b3p2( t, p2 ) + this.b3p3( t, p3 ); + } -THREE.KeyFrameAnimation.prototype.stop = function() { +}; - this.isPlaying = false; - this.isPaused = false; - THREE.AnimationHandler.stop( this ); +// File:src/extras/curves/LineCurve.js - // reset JIT matrix and remove cache +/************************************************************** + * Line + **************************************************************/ - for ( var h = 0; h < this.data.hierarchy.length; h ++ ) { - - var obj = this.hierarchy[ h ]; - var node = this.data.hierarchy[ h ]; +THREE.LineCurve = function ( v1, v2 ) { - if ( node.animationCache !== undefined ) { + this.v1 = v1; + this.v2 = v2; - var original = node.animationCache.originalMatrix; +}; - original.copy( obj.matrix ); - obj.matrix = original; +THREE.LineCurve.prototype = Object.create( THREE.Curve.prototype ); - delete node.animationCache; +THREE.LineCurve.prototype.getPoint = function ( t ) { - } + var point = this.v2.clone().sub(this.v1); + point.multiplyScalar( t ).add( this.v1 ); - } + return point; }; +// Line curve is linear, so we can overwrite default getPointAt -// Update +THREE.LineCurve.prototype.getPointAt = function ( u ) { -THREE.KeyFrameAnimation.prototype.update = function ( delta ) { + return this.getPoint( u ); - if ( this.isPlaying === false ) return; +}; - this.currentTime += delta * this.timeScale; +THREE.LineCurve.prototype.getTangent = function( t ) { - // + var tangent = this.v2.clone().sub(this.v1); - var duration = this.data.length; + return tangent.normalize(); - if ( this.loop === true && this.currentTime > duration ) { +}; - this.currentTime %= duration; +// File:src/extras/curves/QuadraticBezierCurve.js - } +/************************************************************** + * Quadratic Bezier curve + **************************************************************/ - this.currentTime = Math.min( this.currentTime, duration ); - for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { +THREE.QuadraticBezierCurve = function ( v0, v1, v2 ) { - var object = this.hierarchy[ h ]; - var node = this.data.hierarchy[ h ]; + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; - var keys = node.keys, - animationCache = node.animationCache; +}; +THREE.QuadraticBezierCurve.prototype = Object.create( THREE.Curve.prototype ); - if ( keys.length ) { - var prevKey = animationCache.prevKey; - var nextKey = animationCache.nextKey; +THREE.QuadraticBezierCurve.prototype.getPoint = function ( t ) { - if ( nextKey.time <= this.currentTime ) { + var vector = new THREE.Vector2(); - while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) { + vector.x = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x ); + vector.y = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y ); - prevKey = nextKey; - nextKey = keys[ prevKey.index + 1 ]; + return vector; - } +}; - animationCache.prevKey = prevKey; - animationCache.nextKey = nextKey; - } +THREE.QuadraticBezierCurve.prototype.getTangent = function( t ) { - if ( nextKey.time >= this.currentTime ) { + var vector = new THREE.Vector2(); - prevKey.interpolate( nextKey, this.currentTime ); + vector.x = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.x, this.v1.x, this.v2.x ); + vector.y = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.y, this.v1.y, this.v2.y ); - } else { + // returns unit vector - prevKey.interpolate( nextKey, nextKey.time ); + return vector.normalize(); - } +}; - this.data.hierarchy[ h ].node.updateMatrix(); - object.matrixWorldNeedsUpdate = true; +// File:src/extras/curves/CubicBezierCurve.js - } +/************************************************************** + * Cubic Bezier curve + **************************************************************/ - } +THREE.CubicBezierCurve = function ( v0, v1, v2, v3 ) { + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; }; -// Get next key with +THREE.CubicBezierCurve.prototype = Object.create( THREE.Curve.prototype ); -THREE.KeyFrameAnimation.prototype.getNextKeyWith = function( sid, h, key ) { +THREE.CubicBezierCurve.prototype.getPoint = function ( t ) { - var keys = this.data.hierarchy[ h ].keys; - key = key % keys.length; + var tx, ty; - for ( ; key < keys.length; key ++ ) { + tx = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ); + ty = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ); - if ( keys[ key ].hasTarget( sid ) ) { + return new THREE.Vector2( tx, ty ); - return keys[ key ]; +}; - } +THREE.CubicBezierCurve.prototype.getTangent = function( t ) { - } + var tx, ty; - return keys[ 0 ]; + tx = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ); + ty = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ); -}; + var tangent = new THREE.Vector2( tx, ty ); + tangent.normalize(); -// Get previous key with + return tangent; -THREE.KeyFrameAnimation.prototype.getPrevKeyWith = function( sid, h, key ) { +}; - var keys = this.data.hierarchy[ h ].keys; - key = key >= 0 ? key : key + keys.length; +// File:src/extras/curves/SplineCurve.js - for ( ; key >= 0; key -- ) { +/************************************************************** + * Spline curve + **************************************************************/ - if ( keys[ key ].hasTarget( sid ) ) { +THREE.SplineCurve = function ( points /* array of Vector2 */ ) { - return keys[ key ]; + this.points = ( points == undefined ) ? [] : points; - } +}; - } +THREE.SplineCurve.prototype = Object.create( THREE.Curve.prototype ); - return keys[ keys.length - 1 ]; +THREE.SplineCurve.prototype.getPoint = function ( t ) { -}; + var points = this.points; + var point = ( points.length - 1 ) * t; -// File:src/extras/animation/MorphAnimation.js + var intPoint = Math.floor( point ); + var weight = point - intPoint; -/** - * @author mrdoob / http://mrdoob.com - */ + var point0 = points[ intPoint == 0 ? intPoint : intPoint - 1 ] + var point1 = points[ intPoint ] + var point2 = points[ intPoint > points.length - 2 ? points.length -1 : intPoint + 1 ] + var point3 = points[ intPoint > points.length - 3 ? points.length -1 : intPoint + 2 ] -THREE.MorphAnimation = function ( mesh ) { + var vector = new THREE.Vector2(); - this.mesh = mesh; - this.frames = mesh.morphTargetInfluences.length; - this.currentTime = 0; - this.duration = 1000; - this.loop = true; + vector.x = THREE.Curve.Utils.interpolate( point0.x, point1.x, point2.x, point3.x, weight ); + vector.y = THREE.Curve.Utils.interpolate( point0.y, point1.y, point2.y, point3.y, weight ); - this.isPlaying = false; + return vector; }; -THREE.MorphAnimation.prototype = { +// File:src/extras/curves/EllipseCurve.js - play: function () { +/************************************************************** + * Ellipse curve + **************************************************************/ - this.isPlaying = true; +THREE.EllipseCurve = function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise ) { - }, + this.aX = aX; + this.aY = aY; - pause: function () { + this.xRadius = xRadius; + this.yRadius = yRadius; - this.isPlaying = false; + this.aStartAngle = aStartAngle; + this.aEndAngle = aEndAngle; - }, + this.aClockwise = aClockwise; - update: ( function () { +}; - var lastFrame = 0; - var currentFrame = 0; +THREE.EllipseCurve.prototype = Object.create( THREE.Curve.prototype ); - return function ( delta ) { +THREE.EllipseCurve.prototype.getPoint = function ( t ) { - if ( this.isPlaying === false ) return; + var deltaAngle = this.aEndAngle - this.aStartAngle; - this.currentTime += delta; + if ( deltaAngle < 0 ) deltaAngle += Math.PI * 2; + if ( deltaAngle > Math.PI * 2 ) deltaAngle -= Math.PI * 2; - if ( this.loop === true && this.currentTime > this.duration ) { + var angle; - this.currentTime %= this.duration; + if ( this.aClockwise === true ) { - } + angle = this.aEndAngle + ( 1 - t ) * ( Math.PI * 2 - deltaAngle ); - this.currentTime = Math.min( this.currentTime, this.duration ); + } else { - var interpolation = this.duration / this.frames; - var frame = Math.floor( this.currentTime / interpolation ); + angle = this.aStartAngle + t * deltaAngle; - if ( frame != currentFrame ) { + } + + var vector = new THREE.Vector2(); - this.mesh.morphTargetInfluences[ lastFrame ] = 0; - this.mesh.morphTargetInfluences[ currentFrame ] = 1; - this.mesh.morphTargetInfluences[ frame ] = 0; + vector.x = this.aX + this.xRadius * Math.cos( angle ); + vector.y = this.aY + this.yRadius * Math.sin( angle ); - lastFrame = currentFrame; - currentFrame = frame; + return vector; - } +}; - this.mesh.morphTargetInfluences[ frame ] = ( this.currentTime % interpolation ) / interpolation; - this.mesh.morphTargetInfluences[ lastFrame ] = 1 - this.mesh.morphTargetInfluences[ frame ]; +// File:src/extras/curves/ArcCurve.js - } +/************************************************************** + * Arc curve + **************************************************************/ - } )() +THREE.ArcCurve = function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { + THREE.EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); }; -// File:src/extras/geometries/BoxGeometry.js - -/** - * @author mrdoob / http://mrdoob.com/ - * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Cube.as - */ +THREE.ArcCurve.prototype = Object.create( THREE.EllipseCurve.prototype ); -THREE.BoxGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) { +// File:src/extras/curves/LineCurve3.js - THREE.Geometry.call( this ); +/************************************************************** + * Line3D + **************************************************************/ - this.parameters = { - width: width, - height: height, - depth: depth, - widthSegments: widthSegments, - heightSegments: heightSegments, - depthSegments: depthSegments - }; +THREE.LineCurve3 = THREE.Curve.create( - this.widthSegments = widthSegments || 1; - this.heightSegments = heightSegments || 1; - this.depthSegments = depthSegments || 1; + function ( v1, v2 ) { - var scope = this; + this.v1 = v1; + this.v2 = v2; - var width_half = width / 2; - var height_half = height / 2; - var depth_half = depth / 2; + }, - buildPlane( 'z', 'y', - 1, - 1, depth, height, width_half, 0 ); // px - buildPlane( 'z', 'y', 1, - 1, depth, height, - width_half, 1 ); // nx - buildPlane( 'x', 'z', 1, 1, width, depth, height_half, 2 ); // py - buildPlane( 'x', 'z', 1, - 1, width, depth, - height_half, 3 ); // ny - buildPlane( 'x', 'y', 1, - 1, width, height, depth_half, 4 ); // pz - buildPlane( 'x', 'y', - 1, - 1, width, height, - depth_half, 5 ); // nz + function ( t ) { - function buildPlane( u, v, udir, vdir, width, height, depth, materialIndex ) { + var vector = new THREE.Vector3(); - var w, ix, iy, - gridX = scope.widthSegments, - gridY = scope.heightSegments, - width_half = width / 2, - height_half = height / 2, - offset = scope.vertices.length; + vector.subVectors( this.v2, this.v1 ); // diff + vector.multiplyScalar( t ); + vector.add( this.v1 ); - if ( ( u === 'x' && v === 'y' ) || ( u === 'y' && v === 'x' ) ) { + return vector; - w = 'z'; + } - } else if ( ( u === 'x' && v === 'z' ) || ( u === 'z' && v === 'x' ) ) { +); - w = 'y'; - gridY = scope.depthSegments; +// File:src/extras/curves/QuadraticBezierCurve3.js - } else if ( ( u === 'z' && v === 'y' ) || ( u === 'y' && v === 'z' ) ) { +/************************************************************** + * Quadratic Bezier 3D curve + **************************************************************/ - w = 'x'; - gridX = scope.depthSegments; +THREE.QuadraticBezierCurve3 = THREE.Curve.create( - } + function ( v0, v1, v2 ) { - var gridX1 = gridX + 1, - gridY1 = gridY + 1, - segment_width = width / gridX, - segment_height = height / gridY, - normal = new THREE.Vector3(); + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; - normal[ w ] = depth > 0 ? 1 : - 1; + }, - for ( iy = 0; iy < gridY1; iy ++ ) { + function ( t ) { - for ( ix = 0; ix < gridX1; ix ++ ) { + var vector = new THREE.Vector3(); - var vector = new THREE.Vector3(); - vector[ u ] = ( ix * segment_width - width_half ) * udir; - vector[ v ] = ( iy * segment_height - height_half ) * vdir; - vector[ w ] = depth; + vector.x = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x ); + vector.y = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y ); + vector.z = THREE.Shape.Utils.b2( t, this.v0.z, this.v1.z, this.v2.z ); - scope.vertices.push( vector ); + return vector; - } + } - } +); - for ( iy = 0; iy < gridY; iy ++ ) { +// File:src/extras/curves/CubicBezierCurve3.js - for ( ix = 0; ix < gridX; ix ++ ) { +/************************************************************** + * Cubic Bezier 3D curve + **************************************************************/ - var a = ix + gridX1 * iy; - var b = ix + gridX1 * ( iy + 1 ); - var c = ( ix + 1 ) + gridX1 * ( iy + 1 ); - var d = ( ix + 1 ) + gridX1 * iy; +THREE.CubicBezierCurve3 = THREE.Curve.create( - var uva = new THREE.Vector2( ix / gridX, 1 - iy / gridY ); - var uvb = new THREE.Vector2( ix / gridX, 1 - ( iy + 1 ) / gridY ); - var uvc = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - ( iy + 1 ) / gridY ); - var uvd = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - iy / gridY ); + function ( v0, v1, v2, v3 ) { - var face = new THREE.Face3( a + offset, b + offset, d + offset ); - face.normal.copy( normal ); - face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() ); - face.materialIndex = materialIndex; + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; - scope.faces.push( face ); - scope.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); + }, - face = new THREE.Face3( b + offset, c + offset, d + offset ); - face.normal.copy( normal ); - face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() ); - face.materialIndex = materialIndex; + function ( t ) { - scope.faces.push( face ); - scope.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); + var vector = new THREE.Vector3(); - } + vector.x = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ); + vector.y = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ); + vector.z = THREE.Shape.Utils.b3( t, this.v0.z, this.v1.z, this.v2.z, this.v3.z ); - } + return vector; } - this.mergeVertices(); - -}; +); -THREE.BoxGeometry.prototype = Object.create( THREE.Geometry.prototype ); +// File:src/extras/curves/SplineCurve3.js -// File:src/extras/geometries/CircleGeometry.js +/************************************************************** + * Spline 3D curve + **************************************************************/ -/** - * @author hughes - */ -THREE.CircleGeometry = function ( radius, segments, thetaStart, thetaLength ) { +THREE.SplineCurve3 = THREE.Curve.create( - THREE.Geometry.call( this ); + function ( points /* array of Vector3 */) { - this.parameters = { - radius: radius, - segments: segments, - thetaStart: thetaStart, - thetaLength: thetaLength - }; + this.points = ( points == undefined ) ? [] : points; - radius = radius || 50; - segments = segments !== undefined ? Math.max( 3, segments ) : 8; + }, - thetaStart = thetaStart !== undefined ? thetaStart : 0; - thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; + function ( t ) { - var i, uvs = [], - center = new THREE.Vector3(), centerUV = new THREE.Vector2( 0.5, 0.5 ); + var points = this.points; + var point = ( points.length - 1 ) * t; - this.vertices.push(center); - uvs.push( centerUV ); + var intPoint = Math.floor( point ); + var weight = point - intPoint; - for ( i = 0; i <= segments; i ++ ) { + var point0 = points[ intPoint == 0 ? intPoint : intPoint - 1 ]; + var point1 = points[ intPoint ]; + var point2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; + var point3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; - var vertex = new THREE.Vector3(); - var segment = thetaStart + i / segments * thetaLength; + var vector = new THREE.Vector3(); - vertex.x = radius * Math.cos( segment ); - vertex.y = radius * Math.sin( segment ); + vector.x = THREE.Curve.Utils.interpolate( point0.x, point1.x, point2.x, point3.x, weight ); + vector.y = THREE.Curve.Utils.interpolate( point0.y, point1.y, point2.y, point3.y, weight ); + vector.z = THREE.Curve.Utils.interpolate( point0.z, point1.z, point2.z, point3.z, weight ); - this.vertices.push( vertex ); - uvs.push( new THREE.Vector2( ( vertex.x / radius + 1 ) / 2, ( vertex.y / radius + 1 ) / 2 ) ); + return vector; } - var n = new THREE.Vector3( 0, 0, 1 ); +); - for ( i = 1; i <= segments; i ++ ) { +// File:src/extras/curves/ClosedSplineCurve3.js - this.faces.push( new THREE.Face3( i, i + 1, 0, [ n.clone(), n.clone(), n.clone() ] ) ); - this.faceVertexUvs[ 0 ].push( [ uvs[ i ].clone(), uvs[ i + 1 ].clone(), centerUV.clone() ] ); +/************************************************************** + * Closed Spline 3D curve + **************************************************************/ - } - this.computeFaceNormals(); +THREE.ClosedSplineCurve3 = THREE.Curve.create( - this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); + function ( points /* array of Vector3 */) { -}; + this.points = ( points == undefined ) ? [] : points; -THREE.CircleGeometry.prototype = Object.create( THREE.Geometry.prototype ); + }, -// File:src/extras/geometries/CubeGeometry.js + function ( t ) { -/** - * @author mrdoob / http://mrdoob.com/ - */ + var points = this.points; + var point = ( points.length - 0 ) * t; // This needs to be from 0-length +1 + var intPoint = Math.floor( point ); + var weight = point - intPoint; -THREE.CubeGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) { + intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length; - console.warn( 'THEE.CubeGeometry has been renamed to THREE.BoxGeometry.' ); - return new THREE.BoxGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ); + var point0 = points[ ( intPoint - 1 ) % points.length ]; + var point1 = points[ ( intPoint ) % points.length ]; + var point2 = points[ ( intPoint + 1 ) % points.length ]; + var point3 = points[ ( intPoint + 2 ) % points.length ]; - }; + var vector = new THREE.Vector3(); -// File:src/extras/geometries/CylinderGeometry.js + vector.x = THREE.Curve.Utils.interpolate( point0.x, point1.x, point2.x, point3.x, weight ); + vector.y = THREE.Curve.Utils.interpolate( point0.y, point1.y, point2.y, point3.y, weight ); + vector.z = THREE.Curve.Utils.interpolate( point0.z, point1.z, point2.z, point3.z, weight ); + + return vector; + + } + +); + +// File:src/extras/animation/AnimationHandler.js /** - * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ */ -THREE.CylinderGeometry = function ( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded ) { +THREE.AnimationHandler = { - THREE.Geometry.call( this ); + LINEAR: 0, + CATMULLROM: 1, + CATMULLROM_FORWARD: 2, - this.parameters = { - radiusTop: radiusTop, - radiusBottom: radiusBottom, - height: height, - radialSegments: radialSegments, - heightSegments: heightSegments, - openEnded: openEnded - }; + // + + add: function () { console.warn( 'THREE.AnimationHandler.add() has been deprecated.' ); }, + get: function () { console.warn( 'THREE.AnimationHandler.get() has been deprecated.' ); }, + remove: function () { console.warn( 'THREE.AnimationHandler.remove() has been deprecated.' ); }, - radiusTop = radiusTop !== undefined ? radiusTop : 20; - radiusBottom = radiusBottom !== undefined ? radiusBottom : 20; - height = height !== undefined ? height : 100; + // - radialSegments = radialSegments || 8; - heightSegments = heightSegments || 1; + animations: [], - openEnded = openEnded !== undefined ? openEnded : false; + init: function ( data ) { - var heightHalf = height / 2; + if ( data.initialized === true ) return; - var x, y, vertices = [], uvs = []; + // loop through all keys - for ( y = 0; y <= heightSegments; y ++ ) { + for ( var h = 0; h < data.hierarchy.length; h ++ ) { - var verticesRow = []; - var uvsRow = []; + for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { - var v = y / heightSegments; - var radius = v * ( radiusBottom - radiusTop ) + radiusTop; + // remove minus times - for ( x = 0; x <= radialSegments; x ++ ) { + if ( data.hierarchy[ h ].keys[ k ].time < 0 ) { - var u = x / radialSegments; + data.hierarchy[ h ].keys[ k ].time = 0; - var vertex = new THREE.Vector3(); - vertex.x = radius * Math.sin( u * Math.PI * 2 ); - vertex.y = - v * height + heightHalf; - vertex.z = radius * Math.cos( u * Math.PI * 2 ); + } - this.vertices.push( vertex ); + // create quaternions - verticesRow.push( this.vertices.length - 1 ); - uvsRow.push( new THREE.Vector2( u, 1 - v ) ); + if ( data.hierarchy[ h ].keys[ k ].rot !== undefined && + ! ( data.hierarchy[ h ].keys[ k ].rot instanceof THREE.Quaternion ) ) { - } + var quat = data.hierarchy[ h ].keys[ k ].rot; + data.hierarchy[ h ].keys[ k ].rot = new THREE.Quaternion().fromArray( quat ); - vertices.push( verticesRow ); - uvs.push( uvsRow ); + } - } + } - var tanTheta = ( radiusBottom - radiusTop ) / height; - var na, nb; + // prepare morph target keys - for ( x = 0; x < radialSegments; x ++ ) { + if ( data.hierarchy[ h ].keys.length && data.hierarchy[ h ].keys[ 0 ].morphTargets !== undefined ) { - if ( radiusTop !== 0 ) { + // get all used - na = this.vertices[ vertices[ 0 ][ x ] ].clone(); - nb = this.vertices[ vertices[ 0 ][ x + 1 ] ].clone(); + var usedMorphTargets = {}; - } else { + for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { - na = this.vertices[ vertices[ 1 ][ x ] ].clone(); - nb = this.vertices[ vertices[ 1 ][ x + 1 ] ].clone(); + for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) { - } + var morphTargetName = data.hierarchy[ h ].keys[ k ].morphTargets[ m ]; + usedMorphTargets[ morphTargetName ] = - 1; - na.setY( Math.sqrt( na.x * na.x + na.z * na.z ) * tanTheta ).normalize(); - nb.setY( Math.sqrt( nb.x * nb.x + nb.z * nb.z ) * tanTheta ).normalize(); + } - for ( y = 0; y < heightSegments; y ++ ) { + } - var v1 = vertices[ y ][ x ]; - var v2 = vertices[ y + 1 ][ x ]; - var v3 = vertices[ y + 1 ][ x + 1 ]; - var v4 = vertices[ y ][ x + 1 ]; + data.hierarchy[ h ].usedMorphTargets = usedMorphTargets; - var n1 = na.clone(); - var n2 = na.clone(); - var n3 = nb.clone(); - var n4 = nb.clone(); - var uv1 = uvs[ y ][ x ].clone(); - var uv2 = uvs[ y + 1 ][ x ].clone(); - var uv3 = uvs[ y + 1 ][ x + 1 ].clone(); - var uv4 = uvs[ y ][ x + 1 ].clone(); + // set all used on all frames - this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) ); - this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] ); + for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { - this.faces.push( new THREE.Face3( v2, v3, v4, [ n2.clone(), n3, n4.clone() ] ) ); - this.faceVertexUvs[ 0 ].push( [ uv2.clone(), uv3, uv4.clone() ] ); + var influences = {}; - } + for ( var morphTargetName in usedMorphTargets ) { - } + for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) { - // top cap + if ( data.hierarchy[ h ].keys[ k ].morphTargets[ m ] === morphTargetName ) { - if ( openEnded === false && radiusTop > 0 ) { + influences[ morphTargetName ] = data.hierarchy[ h ].keys[ k ].morphTargetsInfluences[ m ]; + break; - this.vertices.push( new THREE.Vector3( 0, heightHalf, 0 ) ); + } - for ( x = 0; x < radialSegments; x ++ ) { + } - var v1 = vertices[ 0 ][ x ]; - var v2 = vertices[ 0 ][ x + 1 ]; - var v3 = this.vertices.length - 1; + if ( m === data.hierarchy[ h ].keys[ k ].morphTargets.length ) { - var n1 = new THREE.Vector3( 0, 1, 0 ); - var n2 = new THREE.Vector3( 0, 1, 0 ); - var n3 = new THREE.Vector3( 0, 1, 0 ); + influences[ morphTargetName ] = 0; - var uv1 = uvs[ 0 ][ x ].clone(); - var uv2 = uvs[ 0 ][ x + 1 ].clone(); - var uv3 = new THREE.Vector2( uv2.x, 0 ); + } - this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) ); - this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] ); + } - } + data.hierarchy[ h ].keys[ k ].morphTargetsInfluences = influences; - } + } - // bottom cap + } - if ( openEnded === false && radiusBottom > 0 ) { - this.vertices.push( new THREE.Vector3( 0, - heightHalf, 0 ) ); + // remove all keys that are on the same time - for ( x = 0; x < radialSegments; x ++ ) { + for ( var k = 1; k < data.hierarchy[ h ].keys.length; k ++ ) { - var v1 = vertices[ y ][ x + 1 ]; - var v2 = vertices[ y ][ x ]; - var v3 = this.vertices.length - 1; + if ( data.hierarchy[ h ].keys[ k ].time === data.hierarchy[ h ].keys[ k - 1 ].time ) { - var n1 = new THREE.Vector3( 0, - 1, 0 ); - var n2 = new THREE.Vector3( 0, - 1, 0 ); - var n3 = new THREE.Vector3( 0, - 1, 0 ); + data.hierarchy[ h ].keys.splice( k, 1 ); + k --; - var uv1 = uvs[ y ][ x + 1 ].clone(); - var uv2 = uvs[ y ][ x ].clone(); - var uv3 = new THREE.Vector2( uv2.x, 1 ); + } - this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) ); - this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] ); + } - } - } + // set index - this.computeFaceNormals(); + for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { -} + data.hierarchy[ h ].keys[ k ].index = k; -THREE.CylinderGeometry.prototype = Object.create( THREE.Geometry.prototype ); + } -// File:src/extras/geometries/ExtrudeGeometry.js + } -/** - * @author zz85 / http://www.lab4games.net/zz85/blog - * - * Creates extruded geometry from a path shape. - * - * parameters = { - * - * curveSegments: , // number of points on the curves - * steps: , // number of points for z-side extrusions / used for subdividing segements of extrude spline too - * amount: , // Depth to extrude the shape - * - * bevelEnabled: , // turn on bevel - * bevelThickness: , // how deep into the original shape bevel goes - * bevelSize: , // how far from shape outline is bevel - * bevelSegments: , // number of bevel layers - * - * extrudePath: // 3d spline path to extrude shape along. (creates Frames if .frames aren't defined) - * frames: // containing arrays of tangents, normals, binormals - * - * material: // material index for front and back faces - * extrudeMaterial: // material index for extrusion and beveled faces - * uvGenerator: // object that provides UV generator functions - * - * } - **/ + data.initialized = true; -THREE.ExtrudeGeometry = function ( shapes, options ) { + return data; - if ( typeof( shapes ) === "undefined" ) { - shapes = []; - return; - } + }, - THREE.Geometry.call( this ); + parse: function ( root ) { - shapes = shapes instanceof Array ? shapes : [ shapes ]; + var parseRecurseHierarchy = function ( root, hierarchy ) { - this.addShapeList( shapes, options ); + hierarchy.push( root ); - this.computeFaceNormals(); + for ( var c = 0; c < root.children.length; c ++ ) + parseRecurseHierarchy( root.children[ c ], hierarchy ); - // can't really use automatic vertex normals - // as then front and back sides get smoothed too - // should do separate smoothing just for sides + }; - //this.computeVertexNormals(); + // setup hierarchy - //console.log( "took", ( Date.now() - startTime ) ); + var hierarchy = []; -}; + if ( root instanceof THREE.SkinnedMesh ) { -THREE.ExtrudeGeometry.prototype = Object.create( THREE.Geometry.prototype ); + for ( var b = 0; b < root.skeleton.bones.length; b ++ ) { -THREE.ExtrudeGeometry.prototype.addShapeList = function ( shapes, options ) { - var sl = shapes.length; + hierarchy.push( root.skeleton.bones[ b ] ); - for ( var s = 0; s < sl; s ++ ) { - var shape = shapes[ s ]; - this.addShape( shape, options ); - } -}; + } -THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { + } else { - var amount = options.amount !== undefined ? options.amount : 100; + parseRecurseHierarchy( root, hierarchy ); - var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10 - var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8 - var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; + } - var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false + return hierarchy; - var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; + }, - var steps = options.steps !== undefined ? options.steps : 1; + play: function ( animation ) { - var extrudePath = options.extrudePath; - var extrudePts, extrudeByPath = false; + if ( this.animations.indexOf( animation ) === - 1 ) { - var material = options.material; - var extrudeMaterial = options.extrudeMaterial; + this.animations.push( animation ); - // Use default WorldUVGenerator if no UV generators are specified. - var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : THREE.ExtrudeGeometry.WorldUVGenerator; + } - var splineTube, binormal, normal, position2; - if ( extrudePath ) { + }, - extrudePts = extrudePath.getSpacedPoints( steps ); + stop: function ( animation ) { - extrudeByPath = true; - bevelEnabled = false; // bevels not supported for path extrusion + var index = this.animations.indexOf( animation ); - // SETUP TNB variables + if ( index !== - 1 ) { - // Reuse TNB from TubeGeomtry for now. - // TODO1 - have a .isClosed in spline? + this.animations.splice( index, 1 ); + + } - splineTube = options.frames !== undefined ? options.frames : new THREE.TubeGeometry.FrenetFrames(extrudePath, steps, false); + }, - // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); + update: function ( deltaTimeMS ) { - binormal = new THREE.Vector3(); - normal = new THREE.Vector3(); - position2 = new THREE.Vector3(); + for ( var i = 0; i < this.animations.length; i ++ ) { - } + this.animations[ i ].resetBlendWeights( ); - // Safeguards if bevels are not enabled + } - if ( ! bevelEnabled ) { + for ( var i = 0; i < this.animations.length; i ++ ) { - bevelSegments = 0; - bevelThickness = 0; - bevelSize = 0; + this.animations[ i ].update( deltaTimeMS ); + + } } - // Variables initalization +}; - var ahole, h, hl; // looping of holes - var scope = this; - var bevelPoints = []; +// File:src/extras/animation/Animation.js - var shapesOffset = this.vertices.length; +/** + * @author mikael emtinger / http://gomo.se/ + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ - var shapePoints = shape.extractPoints( curveSegments ); +THREE.Animation = function ( root, data ) { - var vertices = shapePoints.shape; - var holes = shapePoints.holes; + this.root = root; + this.data = THREE.AnimationHandler.init( data ); + this.hierarchy = THREE.AnimationHandler.parse( root ); - var reverse = ! THREE.Shape.Utils.isClockWise( vertices ) ; + this.currentTime = 0; + this.timeScale = 1; - if ( reverse ) { + this.isPlaying = false; + this.loop = true; + this.weight = 0; - vertices = vertices.reverse(); + this.interpolationType = THREE.AnimationHandler.LINEAR; - // Maybe we should also check if holes are in the opposite direction, just to be safe ... +}; - for ( h = 0, hl = holes.length; h < hl; h ++ ) { - ahole = holes[ h ]; +THREE.Animation.prototype.keyTypes = [ "pos", "rot", "scl" ]; - if ( THREE.Shape.Utils.isClockWise( ahole ) ) { - holes[ h ] = ahole.reverse(); +THREE.Animation.prototype.play = function ( startTime, weight ) { - } + this.currentTime = startTime !== undefined ? startTime : 0; + this.weight = weight !== undefined ? weight: 1; - } + this.isPlaying = true; - reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)! + this.reset(); - } + THREE.AnimationHandler.play( this ); +}; - var faces = THREE.Shape.Utils.triangulateShape ( vertices, holes ); - /* Vertices */ +THREE.Animation.prototype.stop = function() { - var contour = vertices; // vertices has all points but contour has only points of circumference + this.isPlaying = false; - for ( h = 0, hl = holes.length; h < hl; h ++ ) { + THREE.AnimationHandler.stop( this ); - ahole = holes[ h ]; +}; - vertices = vertices.concat( ahole ); +THREE.Animation.prototype.reset = function () { - } + for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { + var object = this.hierarchy[ h ]; - function scalePt2 ( pt, vec, size ) { + object.matrixAutoUpdate = true; - if ( ! vec ) console.log( "die" ); + if ( object.animationCache === undefined ) { - return vec.clone().multiplyScalar( size ).add( pt ); + object.animationCache = { + animations: {}, + blending: { + positionWeight: 0.0, + quaternionWeight: 0.0, + scaleWeight: 0.0 + } + }; + } - } + if ( object.animationCache.animations[this.data.name] === undefined ) { - var b, bs, t, z, - vert, vlen = vertices.length, - face, flen = faces.length, - cont, clen = contour.length; + object.animationCache.animations[this.data.name] = {}; + object.animationCache.animations[this.data.name].prevKey = { pos: 0, rot: 0, scl: 0 }; + object.animationCache.animations[this.data.name].nextKey = { pos: 0, rot: 0, scl: 0 }; + object.animationCache.animations[this.data.name].originalMatrix = object.matrix; + } - // Find directions for point movement + var animationCache = object.animationCache.animations[this.data.name]; - var RAD_TO_DEGREES = 180 / Math.PI; + // Get keys to match our current time + for ( var t = 0; t < 3; t ++ ) { - function getBevelVec( inPt, inPrev, inNext ) { + var type = this.keyTypes[ t ]; - var EPSILON = 0.0000000001; - var sign = THREE.Math.sign; - - // computes for inPt the corresponding point inPt' on a new contour - // shiftet by 1 unit (length of normalized vector) to the left - // if we walk along contour clockwise, this new contour is outside the old one - // - // inPt' is the intersection of the two lines parallel to the two - // adjacent edges of inPt at a distance of 1 unit on the left side. - - var v_trans_x, v_trans_y, shrink_by = 1; // resulting translation vector for inPt + var prevKey = this.data.hierarchy[ h ].keys[ 0 ]; + var nextKey = this.getNextKeyWith( type, h, 1 ); - // good reading for geometry algorithms (here: line-line intersection) - // http://geomalgorithms.com/a05-_intersect-1.html + while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) { - var v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y; - var v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y; - - var v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); - - // check for colinear edges - var colinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); - - if ( Math.abs( colinear0 ) > EPSILON ) { // not colinear - - // length of vectors for normalizing - - var v_prev_len = Math.sqrt( v_prev_lensq ); - var v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); - - // shift adjacent points by unit vectors to the left - - var ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); - var ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); - - var ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); - var ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); - - // scaling factor for v_prev to intersection point - - var sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y - - ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) / - ( v_prev_x * v_next_y - v_prev_y * v_next_x ); - - // vector from inPt to intersection point - - v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); - v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); - - // Don't normalize!, otherwise sharp corners become ugly - // but prevent crazy spikes - var v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ) - if ( v_trans_lensq <= 2 ) { - return new THREE.Vector2( v_trans_x, v_trans_y ); - } else { - shrink_by = Math.sqrt( v_trans_lensq / 2 ); - } - - } else { // handle special case of colinear edges + prevKey = nextKey; + nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 ); - var direction_eq = false; // assumes: opposite - if ( v_prev_x > EPSILON ) { - if ( v_next_x > EPSILON ) { direction_eq = true; } - } else { - if ( v_prev_x < - EPSILON ) { - if ( v_next_x < - EPSILON ) { direction_eq = true; } - } else { - if ( sign(v_prev_y) == sign(v_next_y) ) { direction_eq = true; } - } } - if ( direction_eq ) { - // console.log("Warning: lines are a straight sequence"); - v_trans_x = - v_prev_y; - v_trans_y = v_prev_x; - shrink_by = Math.sqrt( v_prev_lensq ); - } else { - // console.log("Warning: lines are a straight spike"); - v_trans_x = v_prev_x; - v_trans_y = v_prev_y; - shrink_by = Math.sqrt( v_prev_lensq / 2 ); - } + animationCache.prevKey[ type ] = prevKey; + animationCache.nextKey[ type ] = nextKey; } - return new THREE.Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); - } +}; - var contourMovements = []; +THREE.Animation.prototype.resetBlendWeights = function () { - for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { + for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { - if ( j === il ) j = 0; - if ( k === il ) k = 0; + var object = this.hierarchy[ h ]; - // (j)---(i)---(k) - // console.log('i,j,k', i, j , k) + if ( object.animationCache !== undefined ) { - var pt_i = contour[ i ]; - var pt_j = contour[ j ]; - var pt_k = contour[ k ]; + object.animationCache.blending.positionWeight = 0.0; + object.animationCache.blending.quaternionWeight = 0.0; + object.animationCache.blending.scaleWeight = 0.0; - contourMovements[ i ]= getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); + } } - var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat(); +}; - for ( h = 0, hl = holes.length; h < hl; h ++ ) { +THREE.Animation.prototype.update = (function(){ - ahole = holes[ h ]; + var points = []; + var target = new THREE.Vector3(); + var newVector = new THREE.Vector3(); + var newQuat = new THREE.Quaternion(); - oneHoleMovements = []; + // Catmull-Rom spline - for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { + var interpolateCatmullRom = function ( points, scale ) { - if ( j === il ) j = 0; - if ( k === il ) k = 0; + var c = [], v3 = [], + point, intPoint, weight, w2, w3, + pa, pb, pc, pd; - // (j)---(i)---(k) - oneHoleMovements[ i ]= getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); + point = ( points.length - 1 ) * scale; + intPoint = Math.floor( point ); + weight = point - intPoint; - } + c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1; + c[ 1 ] = intPoint; + c[ 2 ] = intPoint > points.length - 2 ? intPoint : intPoint + 1; + c[ 3 ] = intPoint > points.length - 3 ? intPoint : intPoint + 2; - holesMovements.push( oneHoleMovements ); - verticesMovements = verticesMovements.concat( oneHoleMovements ); + pa = points[ c[ 0 ] ]; + pb = points[ c[ 1 ] ]; + pc = points[ c[ 2 ] ]; + pd = points[ c[ 3 ] ]; - } + w2 = weight * weight; + w3 = weight * w2; + v3[ 0 ] = interpolate( pa[ 0 ], pb[ 0 ], pc[ 0 ], pd[ 0 ], weight, w2, w3 ); + v3[ 1 ] = interpolate( pa[ 1 ], pb[ 1 ], pc[ 1 ], pd[ 1 ], weight, w2, w3 ); + v3[ 2 ] = interpolate( pa[ 2 ], pb[ 2 ], pc[ 2 ], pd[ 2 ], weight, w2, w3 ); - // Loop bevelSegments, 1 for the front, 1 for the back + return v3; - for ( b = 0; b < bevelSegments; b ++ ) { - //for ( b = bevelSegments; b > 0; b -- ) { + }; - t = b / bevelSegments; - z = bevelThickness * ( 1 - t ); + var interpolate = function ( p0, p1, p2, p3, t, t2, t3 ) { - //z = bevelThickness * t; - bs = bevelSize * ( Math.sin ( t * Math.PI/2 ) ) ; // curved - //bs = bevelSize * t ; // linear + var v0 = ( p2 - p0 ) * 0.5, + v1 = ( p3 - p1 ) * 0.5; + + return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1; + + }; + + return function ( delta ) { - // contract shape + if ( this.isPlaying === false ) return; - for ( i = 0, il = contour.length; i < il; i ++ ) { + this.currentTime += delta * this.timeScale; - vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); + if ( this.weight === 0 ) + return; - v( vert.x, vert.y, - z ); + // - } + var duration = this.data.length; - // expand holes + if ( this.currentTime > duration || this.currentTime < 0 ) { - for ( h = 0, hl = holes.length; h < hl; h ++ ) { + if ( this.loop ) { - ahole = holes[ h ]; - oneHoleMovements = holesMovements[ h ]; + this.currentTime %= duration; - for ( i = 0, il = ahole.length; i < il; i ++ ) { + if ( this.currentTime < 0 ) + this.currentTime += duration; - vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); + this.reset(); - v( vert.x, vert.y, - z ); + } else { + + this.stop(); + return; } } - } - - bs = bevelSize; - - // Back facing vertices + for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { - for ( i = 0; i < vlen; i ++ ) { + var object = this.hierarchy[ h ]; + var animationCache = object.animationCache.animations[this.data.name]; + var blending = object.animationCache.blending; - vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; + // loop through pos/rot/scl - if ( ! extrudeByPath ) { + for ( var t = 0; t < 3; t ++ ) { - v( vert.x, vert.y, 0 ); + // get keys - } else { + var type = this.keyTypes[ t ]; + var prevKey = animationCache.prevKey[ type ]; + var nextKey = animationCache.nextKey[ type ]; - // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); + if ( ( this.timeScale > 0 && nextKey.time <= this.currentTime ) || + ( this.timeScale < 0 && prevKey.time >= this.currentTime ) ) { - normal.copy( splineTube.normals[0] ).multiplyScalar(vert.x); - binormal.copy( splineTube.binormals[0] ).multiplyScalar(vert.y); + prevKey = this.data.hierarchy[ h ].keys[ 0 ]; + nextKey = this.getNextKeyWith( type, h, 1 ); - position2.copy( extrudePts[0] ).add(normal).add(binormal); + while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) { - v( position2.x, position2.y, position2.z ); + prevKey = nextKey; + nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 ); - } + } - } + animationCache.prevKey[ type ] = prevKey; + animationCache.nextKey[ type ] = nextKey; - // Add stepped vertices... - // Including front facing vertices + } - var s; + object.matrixAutoUpdate = true; + object.matrixWorldNeedsUpdate = true; - for ( s = 1; s <= steps; s ++ ) { + var scale = ( this.currentTime - prevKey.time ) / ( nextKey.time - prevKey.time ); - for ( i = 0; i < vlen; i ++ ) { + var prevXYZ = prevKey[ type ]; + var nextXYZ = nextKey[ type ]; - vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; + if ( scale < 0 ) scale = 0; + if ( scale > 1 ) scale = 1; - if ( ! extrudeByPath ) { + // interpolate - v( vert.x, vert.y, amount / steps * s ); + if ( type === "pos" ) { - } else { + if ( this.interpolationType === THREE.AnimationHandler.LINEAR ) { - // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); + newVector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale; + newVector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale; + newVector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale; - normal.copy( splineTube.normals[s] ).multiplyScalar( vert.x ); - binormal.copy( splineTube.binormals[s] ).multiplyScalar( vert.y ); + // blend + var proportionalWeight = this.weight / ( this.weight + blending.positionWeight ); + object.position.lerp( newVector, proportionalWeight ); + blending.positionWeight += this.weight; - position2.copy( extrudePts[s] ).add( normal ).add( binormal ); + } else if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || + this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { - v( position2.x, position2.y, position2.z ); + points[ 0 ] = this.getPrevKeyWith( "pos", h, prevKey.index - 1 )[ "pos" ]; + points[ 1 ] = prevXYZ; + points[ 2 ] = nextXYZ; + points[ 3 ] = this.getNextKeyWith( "pos", h, nextKey.index + 1 )[ "pos" ]; - } + scale = scale * 0.33 + 0.33; - } + var currentPoint = interpolateCatmullRom( points, scale ); + var proportionalWeight = this.weight / ( this.weight + blending.positionWeight ); + blending.positionWeight += this.weight; - } + // blend + var vector = object.position; + + vector.x = vector.x + ( currentPoint[ 0 ] - vector.x ) * proportionalWeight; + vector.y = vector.y + ( currentPoint[ 1 ] - vector.y ) * proportionalWeight; + vector.z = vector.z + ( currentPoint[ 2 ] - vector.z ) * proportionalWeight; - // Add bevel segments planes + if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { - //for ( b = 1; b <= bevelSegments; b ++ ) { - for ( b = bevelSegments - 1; b >= 0; b -- ) { + var forwardPoint = interpolateCatmullRom( points, scale * 1.01 ); - t = b / bevelSegments; - z = bevelThickness * ( 1 - t ); - //bs = bevelSize * ( 1-Math.sin ( ( 1 - t ) * Math.PI/2 ) ); - bs = bevelSize * Math.sin ( t * Math.PI/2 ) ; + target.set( forwardPoint[ 0 ], forwardPoint[ 1 ], forwardPoint[ 2 ] ); + target.sub( vector ); + target.y = 0; + target.normalize(); - // contract shape + var angle = Math.atan2( target.x, target.z ); + object.rotation.set( 0, angle, 0 ); - for ( i = 0, il = contour.length; i < il; i ++ ) { + } - vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); - v( vert.x, vert.y, amount + z ); + } - } + } else if ( type === "rot" ) { - // expand holes + THREE.Quaternion.slerp( prevXYZ, nextXYZ, newQuat, scale ); - for ( h = 0, hl = holes.length; h < hl; h ++ ) { + // Avoid paying the cost of an additional slerp if we don't have to + if ( blending.quaternionWeight === 0 ) { - ahole = holes[ h ]; - oneHoleMovements = holesMovements[ h ]; + object.quaternion.copy(newQuat); + blending.quaternionWeight = this.weight; - for ( i = 0, il = ahole.length; i < il; i ++ ) { + } else { - vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); + var proportionalWeight = this.weight / ( this.weight + blending.quaternionWeight ); + THREE.Quaternion.slerp( object.quaternion, newQuat, object.quaternion, proportionalWeight ); + blending.quaternionWeight += this.weight; - if ( ! extrudeByPath ) { + } - v( vert.x, vert.y, amount + z ); + } else if ( type === "scl" ) { - } else { + newVector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale; + newVector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale; + newVector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale; - v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); + var proportionalWeight = this.weight / ( this.weight + blending.scaleWeight ); + object.scale.lerp( newVector, proportionalWeight ); + blending.scaleWeight += this.weight; } @@ -30749,656 +29930,635 @@ THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { } - } + return true; - /* Faces */ + }; - // Top and bottom faces +})(); - buildLidFaces(); - // Sides faces - buildSideFaces(); - ///// Internal functions +// Get next key with - function buildLidFaces() { +THREE.Animation.prototype.getNextKeyWith = function ( type, h, key ) { - if ( bevelEnabled ) { + var keys = this.data.hierarchy[ h ].keys; - var layer = 0 ; // steps + 1 - var offset = vlen * layer; + if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || + this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { - // Bottom faces + key = key < keys.length - 1 ? key : keys.length - 1; - for ( i = 0; i < flen; i ++ ) { + } else { - face = faces[ i ]; - f3( face[ 2 ]+ offset, face[ 1 ]+ offset, face[ 0 ] + offset, true ); + key = key % keys.length; - } + } - layer = steps + bevelSegments * 2; - offset = vlen * layer; + for ( ; key < keys.length; key ++ ) { - // Top faces + if ( keys[ key ][ type ] !== undefined ) { - for ( i = 0; i < flen; i ++ ) { + return keys[ key ]; - face = faces[ i ]; - f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset, false ); + } - } + } - } else { + return this.data.hierarchy[ h ].keys[ 0 ]; - // Bottom faces +}; - for ( i = 0; i < flen; i ++ ) { +// Get previous key with - face = faces[ i ]; - f3( face[ 2 ], face[ 1 ], face[ 0 ], true ); +THREE.Animation.prototype.getPrevKeyWith = function ( type, h, key ) { - } + var keys = this.data.hierarchy[ h ].keys; - // Top faces + if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || + this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { - for ( i = 0; i < flen; i ++ ) { + key = key > 0 ? key : 0; - face = faces[ i ]; - f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps, false ); + } else { - } - } + key = key >= 0 ? key : key + keys.length; } - // Create faces for the z-sides of the shape - - function buildSideFaces() { - - var layeroffset = 0; - sidewalls( contour, layeroffset ); - layeroffset += contour.length; - for ( h = 0, hl = holes.length; h < hl; h ++ ) { + for ( ; key >= 0; key -- ) { - ahole = holes[ h ]; - sidewalls( ahole, layeroffset ); + if ( keys[ key ][ type ] !== undefined ) { - //, true - layeroffset += ahole.length; + return keys[ key ]; } } - function sidewalls( contour, layeroffset ) { + return this.data.hierarchy[ h ].keys[ keys.length - 1 ]; - var j, k; - i = contour.length; +}; - while ( --i >= 0 ) { +// File:src/extras/animation/KeyFrameAnimation.js - j = i; - k = i - 1; - if ( k < 0 ) k = contour.length - 1; +/** + * @author mikael emtinger / http://gomo.se/ + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author khang duong + * @author erik kitson + */ - //console.log('b', i,j, i-1, k,vertices.length); +THREE.KeyFrameAnimation = function ( data ) { - var s = 0, sl = steps + bevelSegments * 2; + this.root = data.node; + this.data = THREE.AnimationHandler.init( data ); + this.hierarchy = THREE.AnimationHandler.parse( this.root ); + this.currentTime = 0; + this.timeScale = 0.001; + this.isPlaying = false; + this.isPaused = true; + this.loop = true; + + // initialize to first keyframes + + for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { + + var keys = this.data.hierarchy[h].keys, + sids = this.data.hierarchy[h].sids, + obj = this.hierarchy[h]; + + if ( keys.length && sids ) { + + for ( var s = 0; s < sids.length; s ++ ) { - for ( s = 0; s < sl; s ++ ) { + var sid = sids[ s ], + next = this.getNextKeyWith( sid, h, 0 ); - var slen1 = vlen * s; - var slen2 = vlen * ( s + 1 ); + if ( next ) { - var a = layeroffset + j + slen1, - b = layeroffset + k + slen1, - c = layeroffset + k + slen2, - d = layeroffset + j + slen2; + next.apply( sid ); - f4( a, b, c, d, contour, s, sl, j, k ); + } } + + obj.matrixAutoUpdate = false; + this.data.hierarchy[h].node.updateMatrix(); + obj.matrixWorldNeedsUpdate = true; + } } +}; - function v( x, y, z ) { - scope.vertices.push( new THREE.Vector3( x, y, z ) ); +THREE.KeyFrameAnimation.prototype.play = function ( startTime ) { - } + this.currentTime = startTime !== undefined ? startTime : 0; - function f3( a, b, c, isBottom ) { + if ( this.isPlaying === false ) { - a += shapesOffset; - b += shapesOffset; - c += shapesOffset; + this.isPlaying = true; - // normal, color, material - scope.faces.push( new THREE.Face3( a, b, c, null, null, material ) ); + // reset key cache - var uvs = isBottom ? uvgen.generateBottomUV( scope, shape, options, a, b, c ) : uvgen.generateTopUV( scope, shape, options, a, b, c ); + var h, hl = this.hierarchy.length, + object, + node; - scope.faceVertexUvs[ 0 ].push( uvs ); + for ( h = 0; h < hl; h ++ ) { - } + object = this.hierarchy[ h ]; + node = this.data.hierarchy[ h ]; - function f4( a, b, c, d, wallContour, stepIndex, stepsLength, contourIndex1, contourIndex2 ) { + if ( node.animationCache === undefined ) { - a += shapesOffset; - b += shapesOffset; - c += shapesOffset; - d += shapesOffset; + node.animationCache = {}; + node.animationCache.prevKey = null; + node.animationCache.nextKey = null; + node.animationCache.originalMatrix = object.matrix; - scope.faces.push( new THREE.Face3( a, b, d, null, null, extrudeMaterial ) ); - scope.faces.push( new THREE.Face3( b, c, d, null, null, extrudeMaterial ) ); + } - var uvs = uvgen.generateSideWallUV( scope, shape, wallContour, options, a, b, c, d, - stepIndex, stepsLength, contourIndex1, contourIndex2 ); + var keys = this.data.hierarchy[h].keys; - scope.faceVertexUvs[ 0 ].push( [ uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] ] ); - scope.faceVertexUvs[ 0 ].push( [ uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] ] ); + if (keys.length) { - } + node.animationCache.prevKey = keys[ 0 ]; + node.animationCache.nextKey = keys[ 1 ]; -}; + this.startTime = Math.min( keys[0].time, this.startTime ); + this.endTime = Math.max( keys[keys.length - 1].time, this.endTime ); -THREE.ExtrudeGeometry.WorldUVGenerator = { + } - generateTopUV: function( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC ) { - var ax = geometry.vertices[ indexA ].x, - ay = geometry.vertices[ indexA ].y, + } - bx = geometry.vertices[ indexB ].x, - by = geometry.vertices[ indexB ].y, + this.update( 0 ); - cx = geometry.vertices[ indexC ].x, - cy = geometry.vertices[ indexC ].y; + } - return [ - new THREE.Vector2( ax, ay ), - new THREE.Vector2( bx, by ), - new THREE.Vector2( cx, cy ) - ]; + this.isPaused = false; - }, + THREE.AnimationHandler.play( this ); - generateBottomUV: function( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC ) { +}; - return this.generateTopUV( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC ); - }, +THREE.KeyFrameAnimation.prototype.stop = function() { - generateSideWallUV: function( geometry, extrudedShape, wallContour, extrudeOptions, - indexA, indexB, indexC, indexD, stepIndex, stepsLength, - contourIndex1, contourIndex2 ) { + this.isPlaying = false; + this.isPaused = false; - var ax = geometry.vertices[ indexA ].x, - ay = geometry.vertices[ indexA ].y, - az = geometry.vertices[ indexA ].z, + THREE.AnimationHandler.stop( this ); - bx = geometry.vertices[ indexB ].x, - by = geometry.vertices[ indexB ].y, - bz = geometry.vertices[ indexB ].z, + // reset JIT matrix and remove cache - cx = geometry.vertices[ indexC ].x, - cy = geometry.vertices[ indexC ].y, - cz = geometry.vertices[ indexC ].z, + for ( var h = 0; h < this.data.hierarchy.length; h ++ ) { + + var obj = this.hierarchy[ h ]; + var node = this.data.hierarchy[ h ]; - dx = geometry.vertices[ indexD ].x, - dy = geometry.vertices[ indexD ].y, - dz = geometry.vertices[ indexD ].z; + if ( node.animationCache !== undefined ) { - if ( Math.abs( ay - by ) < 0.01 ) { - return [ - new THREE.Vector2( ax, 1 - az ), - new THREE.Vector2( bx, 1 - bz ), - new THREE.Vector2( cx, 1 - cz ), - new THREE.Vector2( dx, 1 - dz ) - ]; - } else { - return [ - new THREE.Vector2( ay, 1 - az ), - new THREE.Vector2( by, 1 - bz ), - new THREE.Vector2( cy, 1 - cz ), - new THREE.Vector2( dy, 1 - dz ) - ]; - } - } -}; + var original = node.animationCache.originalMatrix; -THREE.ExtrudeGeometry.__v1 = new THREE.Vector2(); -THREE.ExtrudeGeometry.__v2 = new THREE.Vector2(); -THREE.ExtrudeGeometry.__v3 = new THREE.Vector2(); -THREE.ExtrudeGeometry.__v4 = new THREE.Vector2(); -THREE.ExtrudeGeometry.__v5 = new THREE.Vector2(); -THREE.ExtrudeGeometry.__v6 = new THREE.Vector2(); + original.copy( obj.matrix ); + obj.matrix = original; -// File:src/extras/geometries/ShapeGeometry.js + delete node.animationCache; -/** - * @author jonobr1 / http://jonobr1.com - * - * Creates a one-sided polygonal geometry from a path shape. Similar to - * ExtrudeGeometry. - * - * parameters = { - * - * curveSegments: , // number of points on the curves. NOT USED AT THE MOMENT. - * - * material: // material index for front and back faces - * uvGenerator: // object that provides UV generator functions - * - * } - **/ + } -THREE.ShapeGeometry = function ( shapes, options ) { + } - THREE.Geometry.call( this ); +}; - if ( shapes instanceof Array === false ) shapes = [ shapes ]; - this.addShapeList( shapes, options ); +// Update - this.computeFaceNormals(); +THREE.KeyFrameAnimation.prototype.update = function ( delta ) { -}; + if ( this.isPlaying === false ) return; -THREE.ShapeGeometry.prototype = Object.create( THREE.Geometry.prototype ); + this.currentTime += delta * this.timeScale; -/** - * Add an array of shapes to THREE.ShapeGeometry. - */ -THREE.ShapeGeometry.prototype.addShapeList = function ( shapes, options ) { + // - for ( var i = 0, l = shapes.length; i < l; i ++ ) { + var duration = this.data.length; - this.addShape( shapes[ i ], options ); + if ( this.loop === true && this.currentTime > duration ) { + + this.currentTime %= duration; } - return this; + this.currentTime = Math.min( this.currentTime, duration ); -}; + for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { -/** - * Adds a shape to THREE.ShapeGeometry, based on THREE.ExtrudeGeometry. - */ -THREE.ShapeGeometry.prototype.addShape = function ( shape, options ) { + var object = this.hierarchy[ h ]; + var node = this.data.hierarchy[ h ]; - if ( options === undefined ) options = {}; - var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; + var keys = node.keys, + animationCache = node.animationCache; - var material = options.material; - var uvgen = options.UVGenerator === undefined ? THREE.ExtrudeGeometry.WorldUVGenerator : options.UVGenerator; - // + if ( keys.length ) { - var i, l, hole, s; + var prevKey = animationCache.prevKey; + var nextKey = animationCache.nextKey; - var shapesOffset = this.vertices.length; - var shapePoints = shape.extractPoints( curveSegments ); + if ( nextKey.time <= this.currentTime ) { - var vertices = shapePoints.shape; - var holes = shapePoints.holes; + while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) { - var reverse = ! THREE.Shape.Utils.isClockWise( vertices ); + prevKey = nextKey; + nextKey = keys[ prevKey.index + 1 ]; - if ( reverse ) { + } - vertices = vertices.reverse(); + animationCache.prevKey = prevKey; + animationCache.nextKey = nextKey; - // Maybe we should also check if holes are in the opposite direction, just to be safe... + } - for ( i = 0, l = holes.length; i < l; i ++ ) { + if ( nextKey.time >= this.currentTime ) { - hole = holes[ i ]; + prevKey.interpolate( nextKey, this.currentTime ); - if ( THREE.Shape.Utils.isClockWise( hole ) ) { + } else { - holes[ i ] = hole.reverse(); + prevKey.interpolate( nextKey, nextKey.time ); } - } + this.data.hierarchy[ h ].node.updateMatrix(); + object.matrixWorldNeedsUpdate = true; - reverse = false; + } } - var faces = THREE.Shape.Utils.triangulateShape( vertices, holes ); +}; - // Vertices +// Get next key with - var contour = vertices; +THREE.KeyFrameAnimation.prototype.getNextKeyWith = function( sid, h, key ) { - for ( i = 0, l = holes.length; i < l; i ++ ) { + var keys = this.data.hierarchy[ h ].keys; + key = key % keys.length; - hole = holes[ i ]; - vertices = vertices.concat( hole ); + for ( ; key < keys.length; key ++ ) { - } + if ( keys[ key ].hasTarget( sid ) ) { - // + return keys[ key ]; - var vert, vlen = vertices.length; - var face, flen = faces.length; - var cont, clen = contour.length; + } - for ( i = 0; i < vlen; i ++ ) { + } - vert = vertices[ i ]; + return keys[ 0 ]; - this.vertices.push( new THREE.Vector3( vert.x, vert.y, 0 ) ); +}; - } +// Get previous key with - for ( i = 0; i < flen; i ++ ) { +THREE.KeyFrameAnimation.prototype.getPrevKeyWith = function( sid, h, key ) { - face = faces[ i ]; + var keys = this.data.hierarchy[ h ].keys; + key = key >= 0 ? key : key + keys.length; - var a = face[ 0 ] + shapesOffset; - var b = face[ 1 ] + shapesOffset; - var c = face[ 2 ] + shapesOffset; + for ( ; key >= 0; key -- ) { - this.faces.push( new THREE.Face3( a, b, c, null, null, material ) ); - this.faceVertexUvs[ 0 ].push( uvgen.generateBottomUV( this, shape, options, a, b, c ) ); + if ( keys[ key ].hasTarget( sid ) ) { + + return keys[ key ]; + + } } + return keys[ keys.length - 1 ]; + }; -// File:src/extras/geometries/LatheGeometry.js +// File:src/extras/animation/MorphAnimation.js /** - * @author astrodud / http://astrodud.isgreat.org/ - * @author zz85 / https://github.com/zz85 - * @author bhouston / http://exocortex.com + * @author mrdoob / http://mrdoob.com */ -// points - to create a closed torus, one must use a set of points -// like so: [ a, b, c, d, a ], see first is the same as last. -// segments - the number of circumference segments to create -// phiStart - the starting radian -// phiLength - the radian (0 to 2*PI) range of the lathed section -// 2*pi is a closed lathe, less than 2PI is a portion. -THREE.LatheGeometry = function ( points, segments, phiStart, phiLength ) { - - THREE.Geometry.call( this ); - - segments = segments || 12; - phiStart = phiStart || 0; - phiLength = phiLength || 2 * Math.PI; +THREE.MorphAnimation = function ( mesh ) { - var inversePointLength = 1.0 / ( points.length - 1 ); - var inverseSegments = 1.0 / segments; + this.mesh = mesh; + this.frames = mesh.morphTargetInfluences.length; + this.currentTime = 0; + this.duration = 1000; + this.loop = true; - for ( var i = 0, il = segments; i <= il; i ++ ) { + this.isPlaying = false; - var phi = phiStart + i * inverseSegments * phiLength; +}; - var c = Math.cos( phi ), - s = Math.sin( phi ); +THREE.MorphAnimation.prototype = { - for ( var j = 0, jl = points.length; j < jl; j ++ ) { + play: function () { - var pt = points[ j ]; + this.isPlaying = true; - var vertex = new THREE.Vector3(); + }, - vertex.x = c * pt.x - s * pt.y; - vertex.y = s * pt.x + c * pt.y; - vertex.z = pt.z; + pause: function () { - this.vertices.push( vertex ); + this.isPlaying = false; - } + }, - } + update: ( function () { - var np = points.length; + var lastFrame = 0; + var currentFrame = 0; - for ( var i = 0, il = segments; i < il; i ++ ) { + return function ( delta ) { - for ( var j = 0, jl = points.length - 1; j < jl; j ++ ) { + if ( this.isPlaying === false ) return; - var base = j + np * i; - var a = base; - var b = base + np; - var c = base + 1 + np; - var d = base + 1; + this.currentTime += delta; - var u0 = i * inverseSegments; - var v0 = j * inversePointLength; - var u1 = u0 + inverseSegments; - var v1 = v0 + inversePointLength; + if ( this.loop === true && this.currentTime > this.duration ) { - this.faces.push( new THREE.Face3( a, b, d ) ); + this.currentTime %= this.duration; - this.faceVertexUvs[ 0 ].push( [ + } - new THREE.Vector2( u0, v0 ), - new THREE.Vector2( u1, v0 ), - new THREE.Vector2( u0, v1 ) + this.currentTime = Math.min( this.currentTime, this.duration ); - ] ); + var interpolation = this.duration / this.frames; + var frame = Math.floor( this.currentTime / interpolation ); - this.faces.push( new THREE.Face3( b, c, d ) ); + if ( frame != currentFrame ) { - this.faceVertexUvs[ 0 ].push( [ + this.mesh.morphTargetInfluences[ lastFrame ] = 0; + this.mesh.morphTargetInfluences[ currentFrame ] = 1; + this.mesh.morphTargetInfluences[ frame ] = 0; - new THREE.Vector2( u1, v0 ), - new THREE.Vector2( u1, v1 ), - new THREE.Vector2( u0, v1 ) + lastFrame = currentFrame; + currentFrame = frame; - ] ); + } + this.mesh.morphTargetInfluences[ frame ] = ( this.currentTime % interpolation ) / interpolation; + this.mesh.morphTargetInfluences[ lastFrame ] = 1 - this.mesh.morphTargetInfluences[ frame ]; } - } - - this.mergeVertices(); - this.computeFaceNormals(); - this.computeVertexNormals(); + } )() }; -THREE.LatheGeometry.prototype = Object.create( THREE.Geometry.prototype ); - -// File:src/extras/geometries/PlaneGeometry.js +// File:src/extras/geometries/BoxGeometry.js /** * @author mrdoob / http://mrdoob.com/ - * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as + * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Cube.as */ -THREE.PlaneGeometry = function ( width, height, widthSegments, heightSegments ) { +THREE.BoxGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) { THREE.Geometry.call( this ); + this.type = 'BoxGeometry'; + this.parameters = { width: width, height: height, + depth: depth, widthSegments: widthSegments, - heightSegments: heightSegments + heightSegments: heightSegments, + depthSegments: depthSegments }; - var ix, iz; + this.widthSegments = widthSegments || 1; + this.heightSegments = heightSegments || 1; + this.depthSegments = depthSegments || 1; + + var scope = this; + var width_half = width / 2; var height_half = height / 2; + var depth_half = depth / 2; - var gridX = widthSegments || 1; - var gridZ = heightSegments || 1; + buildPlane( 'z', 'y', - 1, - 1, depth, height, width_half, 0 ); // px + buildPlane( 'z', 'y', 1, - 1, depth, height, - width_half, 1 ); // nx + buildPlane( 'x', 'z', 1, 1, width, depth, height_half, 2 ); // py + buildPlane( 'x', 'z', 1, - 1, width, depth, - height_half, 3 ); // ny + buildPlane( 'x', 'y', 1, - 1, width, height, depth_half, 4 ); // pz + buildPlane( 'x', 'y', - 1, - 1, width, height, - depth_half, 5 ); // nz - var gridX1 = gridX + 1; - var gridZ1 = gridZ + 1; + function buildPlane( u, v, udir, vdir, width, height, depth, materialIndex ) { - var segment_width = width / gridX; - var segment_height = height / gridZ; + var w, ix, iy, + gridX = scope.widthSegments, + gridY = scope.heightSegments, + width_half = width / 2, + height_half = height / 2, + offset = scope.vertices.length; - var normal = new THREE.Vector3( 0, 0, 1 ); + if ( ( u === 'x' && v === 'y' ) || ( u === 'y' && v === 'x' ) ) { - for ( iz = 0; iz < gridZ1; iz ++ ) { + w = 'z'; - var y = iz * segment_height - height_half; + } else if ( ( u === 'x' && v === 'z' ) || ( u === 'z' && v === 'x' ) ) { - for ( ix = 0; ix < gridX1; ix ++ ) { + w = 'y'; + gridY = scope.depthSegments; - var x = ix * segment_width - width_half; + } else if ( ( u === 'z' && v === 'y' ) || ( u === 'y' && v === 'z' ) ) { - this.vertices.push( new THREE.Vector3( x, - y, 0 ) ); + w = 'x'; + gridX = scope.depthSegments; } - } + var gridX1 = gridX + 1, + gridY1 = gridY + 1, + segment_width = width / gridX, + segment_height = height / gridY, + normal = new THREE.Vector3(); - for ( iz = 0; iz < gridZ; iz ++ ) { + normal[ w ] = depth > 0 ? 1 : - 1; + + for ( iy = 0; iy < gridY1; iy ++ ) { - for ( ix = 0; ix < gridX; ix ++ ) { + for ( ix = 0; ix < gridX1; ix ++ ) { - var a = ix + gridX1 * iz; - var b = ix + gridX1 * ( iz + 1 ); - var c = ( ix + 1 ) + gridX1 * ( iz + 1 ); - var d = ( ix + 1 ) + gridX1 * iz; + var vector = new THREE.Vector3(); + vector[ u ] = ( ix * segment_width - width_half ) * udir; + vector[ v ] = ( iy * segment_height - height_half ) * vdir; + vector[ w ] = depth; - var uva = new THREE.Vector2( ix / gridX, 1 - iz / gridZ ); - var uvb = new THREE.Vector2( ix / gridX, 1 - ( iz + 1 ) / gridZ ); - var uvc = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - ( iz + 1 ) / gridZ ); - var uvd = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - iz / gridZ ); + scope.vertices.push( vector ); - var face = new THREE.Face3( a, b, d ); - face.normal.copy( normal ); - face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() ); + } - this.faces.push( face ); - this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); + } - face = new THREE.Face3( b, c, d ); - face.normal.copy( normal ); - face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() ); + for ( iy = 0; iy < gridY; iy ++ ) { - this.faces.push( face ); - this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); + for ( ix = 0; ix < gridX; ix ++ ) { + + var a = ix + gridX1 * iy; + var b = ix + gridX1 * ( iy + 1 ); + var c = ( ix + 1 ) + gridX1 * ( iy + 1 ); + var d = ( ix + 1 ) + gridX1 * iy; + + var uva = new THREE.Vector2( ix / gridX, 1 - iy / gridY ); + var uvb = new THREE.Vector2( ix / gridX, 1 - ( iy + 1 ) / gridY ); + var uvc = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - ( iy + 1 ) / gridY ); + var uvd = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - iy / gridY ); + + var face = new THREE.Face3( a + offset, b + offset, d + offset ); + face.normal.copy( normal ); + face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() ); + face.materialIndex = materialIndex; + + scope.faces.push( face ); + scope.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); + + face = new THREE.Face3( b + offset, c + offset, d + offset ); + face.normal.copy( normal ); + face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() ); + face.materialIndex = materialIndex; + + scope.faces.push( face ); + scope.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); + + } } } + this.mergeVertices(); + }; -THREE.PlaneGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.BoxGeometry.prototype = Object.create( THREE.Geometry.prototype ); -// File:src/extras/geometries/RingGeometry.js +// File:src/extras/geometries/CircleGeometry.js /** - * @author Kaleb Murphy + * @author hughes */ -THREE.RingGeometry = function ( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { +THREE.CircleGeometry = function ( radius, segments, thetaStart, thetaLength ) { THREE.Geometry.call( this ); - innerRadius = innerRadius || 0; - outerRadius = outerRadius || 50; + this.type = 'CircleGeometry'; + + this.parameters = { + radius: radius, + segments: segments, + thetaStart: thetaStart, + thetaLength: thetaLength + }; + + radius = radius || 50; + segments = segments !== undefined ? Math.max( 3, segments ) : 8; thetaStart = thetaStart !== undefined ? thetaStart : 0; thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; - thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8; - phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 8; - - var i, o, uvs = [], radius = innerRadius, radiusStep = ( ( outerRadius - innerRadius ) / phiSegments ); + var i, uvs = [], + center = new THREE.Vector3(), centerUV = new THREE.Vector2( 0.5, 0.5 ); - for ( i = 0; i < phiSegments + 1; i ++ ) { // concentric circles inside ring + this.vertices.push(center); + uvs.push( centerUV ); - for ( o = 0; o < thetaSegments + 1; o ++ ) { // number of segments per circle + for ( i = 0; i <= segments; i ++ ) { - var vertex = new THREE.Vector3(); - var segment = thetaStart + o / thetaSegments * thetaLength; - vertex.x = radius * Math.cos( segment ); - vertex.y = radius * Math.sin( segment ); + var vertex = new THREE.Vector3(); + var segment = thetaStart + i / segments * thetaLength; - this.vertices.push( vertex ); - uvs.push( new THREE.Vector2( ( vertex.x / outerRadius + 1 ) / 2, ( vertex.y / outerRadius + 1 ) / 2 ) ); - } + vertex.x = radius * Math.cos( segment ); + vertex.y = radius * Math.sin( segment ); - radius += radiusStep; + this.vertices.push( vertex ); + uvs.push( new THREE.Vector2( ( vertex.x / radius + 1 ) / 2, ( vertex.y / radius + 1 ) / 2 ) ); } var n = new THREE.Vector3( 0, 0, 1 ); - for ( i = 0; i < phiSegments; i ++ ) { // concentric circles inside ring - - var thetaSegment = i * (thetaSegments + 1); + for ( i = 1; i <= segments; i ++ ) { - for ( o = 0; o < thetaSegments ; o ++ ) { // number of segments per circle + this.faces.push( new THREE.Face3( i, i + 1, 0, [ n.clone(), n.clone(), n.clone() ] ) ); + this.faceVertexUvs[ 0 ].push( [ uvs[ i ].clone(), uvs[ i + 1 ].clone(), centerUV.clone() ] ); - var segment = o + thetaSegment; + } - var v1 = segment; - var v2 = segment + thetaSegments + 1; - var v3 = segment + thetaSegments + 2; + this.computeFaceNormals(); - this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) ); - this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ]); + this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); - v1 = segment; - v2 = segment + thetaSegments + 2; - v3 = segment + 1; +}; - this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) ); - this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ]); +THREE.CircleGeometry.prototype = Object.create( THREE.Geometry.prototype ); - } - } +// File:src/extras/geometries/CubeGeometry.js - this.computeFaceNormals(); +/** + * @author mrdoob / http://mrdoob.com/ + */ - this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); -}; +THREE.CubeGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) { -THREE.RingGeometry.prototype = Object.create( THREE.Geometry.prototype ); + console.warn( 'THREE.CubeGeometry has been renamed to THREE.BoxGeometry.' ); + return new THREE.BoxGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ); + }; -// File:src/extras/geometries/SphereGeometry.js +// File:src/extras/geometries/CylinderGeometry.js /** * @author mrdoob / http://mrdoob.com/ */ -THREE.SphereGeometry = function ( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { +THREE.CylinderGeometry = function ( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded ) { THREE.Geometry.call( this ); + this.type = 'CylinderGeometry'; + this.parameters = { - radius: radius, - widthSegments: widthSegments, + radiusTop: radiusTop, + radiusBottom: radiusBottom, + height: height, + radialSegments: radialSegments, heightSegments: heightSegments, - phiStart: phiStart, - phiLength: phiLength, - thetaStart: thetaStart, - thetaLength: thetaLength + openEnded: openEnded }; - radius = radius || 50; + radiusTop = radiusTop !== undefined ? radiusTop : 20; + radiusBottom = radiusBottom !== undefined ? radiusBottom : 20; + height = height !== undefined ? height : 100; - widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 ); - heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 ); + radialSegments = radialSegments || 8; + heightSegments = heightSegments || 1; - phiStart = phiStart !== undefined ? phiStart : 0; - phiLength = phiLength !== undefined ? phiLength : Math.PI * 2; + openEnded = openEnded !== undefined ? openEnded : false; - thetaStart = thetaStart !== undefined ? thetaStart : 0; - thetaLength = thetaLength !== undefined ? thetaLength : Math.PI; + var heightHalf = height / 2; var x, y, vertices = [], uvs = []; @@ -31407,15 +30567,17 @@ THREE.SphereGeometry = function ( radius, widthSegments, heightSegments, phiStar var verticesRow = []; var uvsRow = []; - for ( x = 0; x <= widthSegments; x ++ ) { + var v = y / heightSegments; + var radius = v * ( radiusBottom - radiusTop ) + radiusTop; - var u = x / widthSegments; - var v = y / heightSegments; + for ( x = 0; x <= radialSegments; x ++ ) { + + var u = x / radialSegments; var vertex = new THREE.Vector3(); - vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); - vertex.y = radius * Math.cos( thetaStart + v * thetaLength ); - vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); + vertex.x = radius * Math.sin( u * Math.PI * 2 ); + vertex.y = - v * height + heightHalf; + vertex.z = radius * Math.cos( u * Math.PI * 2 ); this.vertices.push( vertex ); @@ -31429,4412 +30591,4138 @@ THREE.SphereGeometry = function ( radius, widthSegments, heightSegments, phiStar } - for ( y = 0; y < heightSegments; y ++ ) { + var tanTheta = ( radiusBottom - radiusTop ) / height; + var na, nb; - for ( x = 0; x < widthSegments; x ++ ) { + for ( x = 0; x < radialSegments; x ++ ) { - var v1 = vertices[ y ][ x + 1 ]; - var v2 = vertices[ y ][ x ]; - var v3 = vertices[ y + 1 ][ x ]; - var v4 = vertices[ y + 1 ][ x + 1 ]; + if ( radiusTop !== 0 ) { - var n1 = this.vertices[ v1 ].clone().normalize(); - var n2 = this.vertices[ v2 ].clone().normalize(); - var n3 = this.vertices[ v3 ].clone().normalize(); - var n4 = this.vertices[ v4 ].clone().normalize(); + na = this.vertices[ vertices[ 0 ][ x ] ].clone(); + nb = this.vertices[ vertices[ 0 ][ x + 1 ] ].clone(); - var uv1 = uvs[ y ][ x + 1 ].clone(); - var uv2 = uvs[ y ][ x ].clone(); - var uv3 = uvs[ y + 1 ][ x ].clone(); - var uv4 = uvs[ y + 1 ][ x + 1 ].clone(); + } else { - if ( Math.abs( this.vertices[ v1 ].y ) === radius ) { + na = this.vertices[ vertices[ 1 ][ x ] ].clone(); + nb = this.vertices[ vertices[ 1 ][ x + 1 ] ].clone(); - uv1.x = ( uv1.x + uv2.x ) / 2; - this.faces.push( new THREE.Face3( v1, v3, v4, [ n1, n3, n4 ] ) ); - this.faceVertexUvs[ 0 ].push( [ uv1, uv3, uv4 ] ); + } - } else if ( Math.abs( this.vertices[ v3 ].y ) === radius ) { + na.setY( Math.sqrt( na.x * na.x + na.z * na.z ) * tanTheta ).normalize(); + nb.setY( Math.sqrt( nb.x * nb.x + nb.z * nb.z ) * tanTheta ).normalize(); - uv3.x = ( uv3.x + uv4.x ) / 2; - this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) ); - this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] ); + for ( y = 0; y < heightSegments; y ++ ) { - } else { + var v1 = vertices[ y ][ x ]; + var v2 = vertices[ y + 1 ][ x ]; + var v3 = vertices[ y + 1 ][ x + 1 ]; + var v4 = vertices[ y ][ x + 1 ]; - this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) ); - this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] ); + var n1 = na.clone(); + var n2 = na.clone(); + var n3 = nb.clone(); + var n4 = nb.clone(); - this.faces.push( new THREE.Face3( v2, v3, v4, [ n2.clone(), n3, n4.clone() ] ) ); - this.faceVertexUvs[ 0 ].push( [ uv2.clone(), uv3, uv4.clone() ] ); + var uv1 = uvs[ y ][ x ].clone(); + var uv2 = uvs[ y + 1 ][ x ].clone(); + var uv3 = uvs[ y + 1 ][ x + 1 ].clone(); + var uv4 = uvs[ y ][ x + 1 ].clone(); - } + this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] ); + + this.faces.push( new THREE.Face3( v2, v3, v4, [ n2.clone(), n3, n4.clone() ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv2.clone(), uv3, uv4.clone() ] ); } } - this.computeFaceNormals(); + // top cap - this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); + if ( openEnded === false && radiusTop > 0 ) { -}; + this.vertices.push( new THREE.Vector3( 0, heightHalf, 0 ) ); -THREE.SphereGeometry.prototype = Object.create( THREE.Geometry.prototype ); + for ( x = 0; x < radialSegments; x ++ ) { -// File:src/extras/geometries/TextGeometry.js + var v1 = vertices[ 0 ][ x ]; + var v2 = vertices[ 0 ][ x + 1 ]; + var v3 = this.vertices.length - 1; -/** - * @author zz85 / http://www.lab4games.net/zz85/blog - * @author alteredq / http://alteredqualia.com/ - * - * For creating 3D text geometry in three.js - * - * Text = 3D Text - * - * parameters = { - * size: , // size of the text - * height: , // thickness to extrude text - * curveSegments: , // number of points on the curves - * - * font: , // font name - * weight: , // font weight (normal, bold) - * style: , // font style (normal, italics) - * - * bevelEnabled: , // turn on bevel - * bevelThickness: , // how deep into text bevel goes - * bevelSize: , // how far from text outline is bevel - * } - * - */ + var n1 = new THREE.Vector3( 0, 1, 0 ); + var n2 = new THREE.Vector3( 0, 1, 0 ); + var n3 = new THREE.Vector3( 0, 1, 0 ); -/* Usage Examples + var uv1 = uvs[ 0 ][ x ].clone(); + var uv2 = uvs[ 0 ][ x + 1 ].clone(); + var uv3 = new THREE.Vector2( uv2.x, 0 ); - // TextGeometry wrapper + this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] ); - var text3d = new TextGeometry( text, options ); + } - // Complete manner + } - var textShapes = THREE.FontUtils.generateShapes( text, options ); - var text3d = new ExtrudeGeometry( textShapes, options ); + // bottom cap -*/ + if ( openEnded === false && radiusBottom > 0 ) { + this.vertices.push( new THREE.Vector3( 0, - heightHalf, 0 ) ); -THREE.TextGeometry = function ( text, parameters ) { + for ( x = 0; x < radialSegments; x ++ ) { - parameters = parameters || {}; + var v1 = vertices[ y ][ x + 1 ]; + var v2 = vertices[ y ][ x ]; + var v3 = this.vertices.length - 1; - var textShapes = THREE.FontUtils.generateShapes( text, parameters ); + var n1 = new THREE.Vector3( 0, - 1, 0 ); + var n2 = new THREE.Vector3( 0, - 1, 0 ); + var n3 = new THREE.Vector3( 0, - 1, 0 ); - // translate parameters to ExtrudeGeometry API + var uv1 = uvs[ y ][ x + 1 ].clone(); + var uv2 = uvs[ y ][ x ].clone(); + var uv3 = new THREE.Vector2( uv2.x, 1 ); - parameters.amount = parameters.height !== undefined ? parameters.height : 50; + this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] ); - // defaults + } - if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10; - if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8; - if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false; + } - THREE.ExtrudeGeometry.call( this, textShapes, parameters ); + this.computeFaceNormals(); -}; +} -THREE.TextGeometry.prototype = Object.create( THREE.ExtrudeGeometry.prototype ); +THREE.CylinderGeometry.prototype = Object.create( THREE.Geometry.prototype ); -// File:src/extras/geometries/TorusGeometry.js +// File:src/extras/geometries/ExtrudeGeometry.js /** - * @author oosmoxiecode - * @author mrdoob / http://mrdoob.com/ - * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3DLite/src/away3dlite/primitives/Torus.as?r=2888 - */ - -THREE.TorusGeometry = function ( radius, tube, radialSegments, tubularSegments, arc ) { - - THREE.Geometry.call( this ); - - this.parameters = { - radius: radius, - tube: tube, - radialSegments: radialSegments, - tubularSegments: tubularSegments, - arc: arc - }; - - radius = radius || 100; - tube = tube || 40; - radialSegments = radialSegments || 8; - tubularSegments = tubularSegments || 6; - arc = arc || Math.PI * 2; - - var center = new THREE.Vector3(), uvs = [], normals = []; - - for ( var j = 0; j <= radialSegments; j ++ ) { - - for ( var i = 0; i <= tubularSegments; i ++ ) { - - var u = i / tubularSegments * arc; - var v = j / radialSegments * Math.PI * 2; - - center.x = radius * Math.cos( u ); - center.y = radius * Math.sin( u ); - - var vertex = new THREE.Vector3(); - vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u ); - vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u ); - vertex.z = tube * Math.sin( v ); - - this.vertices.push( vertex ); - - uvs.push( new THREE.Vector2( i / tubularSegments, j / radialSegments ) ); - normals.push( vertex.clone().sub( center ).normalize() ); + * @author zz85 / http://www.lab4games.net/zz85/blog + * + * Creates extruded geometry from a path shape. + * + * parameters = { + * + * curveSegments: , // number of points on the curves + * steps: , // number of points for z-side extrusions / used for subdividing segements of extrude spline too + * amount: , // Depth to extrude the shape + * + * bevelEnabled: , // turn on bevel + * bevelThickness: , // how deep into the original shape bevel goes + * bevelSize: , // how far from shape outline is bevel + * bevelSegments: , // number of bevel layers + * + * extrudePath: // 3d spline path to extrude shape along. (creates Frames if .frames aren't defined) + * frames: // containing arrays of tangents, normals, binormals + * + * material: // material index for front and back faces + * extrudeMaterial: // material index for extrusion and beveled faces + * uvGenerator: // object that provides UV generator functions + * + * } + **/ - } +THREE.ExtrudeGeometry = function ( shapes, options ) { + if ( typeof( shapes ) === "undefined" ) { + shapes = []; + return; } - for ( var j = 1; j <= radialSegments; j ++ ) { - - for ( var i = 1; i <= tubularSegments; i ++ ) { - - var a = ( tubularSegments + 1 ) * j + i - 1; - var b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1; - var c = ( tubularSegments + 1 ) * ( j - 1 ) + i; - var d = ( tubularSegments + 1 ) * j + i; - - var face = new THREE.Face3( a, b, d, [ normals[ a ].clone(), normals[ b ].clone(), normals[ d ].clone() ] ); - this.faces.push( face ); - this.faceVertexUvs[ 0 ].push( [ uvs[ a ].clone(), uvs[ b ].clone(), uvs[ d ].clone() ] ); + THREE.Geometry.call( this ); - face = new THREE.Face3( b, c, d, [ normals[ b ].clone(), normals[ c ].clone(), normals[ d ].clone() ] ); - this.faces.push( face ); - this.faceVertexUvs[ 0 ].push( [ uvs[ b ].clone(), uvs[ c ].clone(), uvs[ d ].clone() ] ); + this.type = 'ExtrudeGeometry'; - } + shapes = shapes instanceof Array ? shapes : [ shapes ]; - } + this.addShapeList( shapes, options ); this.computeFaceNormals(); -}; + // can't really use automatic vertex normals + // as then front and back sides get smoothed too + // should do separate smoothing just for sides -THREE.TorusGeometry.prototype = Object.create( THREE.Geometry.prototype ); + //this.computeVertexNormals(); -// File:src/extras/geometries/TorusKnotGeometry.js + //console.log( "took", ( Date.now() - startTime ) ); -/** - * @author oosmoxiecode - * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3D/src/away3d/primitives/TorusKnot.as?spec=svn2473&r=2473 - */ +}; -THREE.TorusKnotGeometry = function ( radius, tube, radialSegments, tubularSegments, p, q, heightScale ) { +THREE.ExtrudeGeometry.prototype = Object.create( THREE.Geometry.prototype ); - THREE.Geometry.call( this ); +THREE.ExtrudeGeometry.prototype.addShapeList = function ( shapes, options ) { + var sl = shapes.length; - this.parameters = { - radius: radius, - tube: tube, - radialSegments: radialSegments, - tubularSegments: tubularSegments, - p: p, - q: q, - heightScale: heightScale - }; + for ( var s = 0; s < sl; s ++ ) { + var shape = shapes[ s ]; + this.addShape( shape, options ); + } +}; - radius = radius || 100; - tube = tube || 40; - radialSegments = radialSegments || 64; - tubularSegments = tubularSegments || 8; - p = p || 2; - q = q || 3; - heightScale = heightScale || 1; - - var grid = new Array( radialSegments ); - var tang = new THREE.Vector3(); - var n = new THREE.Vector3(); - var bitan = new THREE.Vector3(); +THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { - for ( var i = 0; i < radialSegments; ++ i ) { + var amount = options.amount !== undefined ? options.amount : 100; - grid[ i ] = new Array( tubularSegments ); - var u = i / radialSegments * 2 * p * Math.PI; - var p1 = getPos( u, q, p, radius, heightScale ); - var p2 = getPos( u + 0.01, q, p, radius, heightScale ); - tang.subVectors( p2, p1 ); - n.addVectors( p2, p1 ); + var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10 + var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8 + var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; - bitan.crossVectors( tang, n ); - n.crossVectors( bitan, tang ); - bitan.normalize(); - n.normalize(); + var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false - for ( var j = 0; j < tubularSegments; ++ j ) { + var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; - var v = j / tubularSegments * 2 * Math.PI; - var cx = - tube * Math.cos( v ); // TODO: Hack: Negating it so it faces outside. - var cy = tube * Math.sin( v ); + var steps = options.steps !== undefined ? options.steps : 1; - var pos = new THREE.Vector3(); - pos.x = p1.x + cx * n.x + cy * bitan.x; - pos.y = p1.y + cx * n.y + cy * bitan.y; - pos.z = p1.z + cx * n.z + cy * bitan.z; + var extrudePath = options.extrudePath; + var extrudePts, extrudeByPath = false; - grid[ i ][ j ] = this.vertices.push( pos ) - 1; + var material = options.material; + var extrudeMaterial = options.extrudeMaterial; - } + // Use default WorldUVGenerator if no UV generators are specified. + var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : THREE.ExtrudeGeometry.WorldUVGenerator; - } + var splineTube, binormal, normal, position2; + if ( extrudePath ) { - for ( var i = 0; i < radialSegments; ++ i ) { + extrudePts = extrudePath.getSpacedPoints( steps ); - for ( var j = 0; j < tubularSegments; ++ j ) { + extrudeByPath = true; + bevelEnabled = false; // bevels not supported for path extrusion - var ip = ( i + 1 ) % radialSegments; - var jp = ( j + 1 ) % tubularSegments; + // SETUP TNB variables - var a = grid[ i ][ j ]; - var b = grid[ ip ][ j ]; - var c = grid[ ip ][ jp ]; - var d = grid[ i ][ jp ]; + // Reuse TNB from TubeGeomtry for now. + // TODO1 - have a .isClosed in spline? - var uva = new THREE.Vector2( i / radialSegments, j / tubularSegments ); - var uvb = new THREE.Vector2( ( i + 1 ) / radialSegments, j / tubularSegments ); - var uvc = new THREE.Vector2( ( i + 1 ) / radialSegments, ( j + 1 ) / tubularSegments ); - var uvd = new THREE.Vector2( i / radialSegments, ( j + 1 ) / tubularSegments ); + splineTube = options.frames !== undefined ? options.frames : new THREE.TubeGeometry.FrenetFrames(extrudePath, steps, false); - this.faces.push( new THREE.Face3( a, b, d ) ); - this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); + // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); - this.faces.push( new THREE.Face3( b, c, d ) ); - this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); + binormal = new THREE.Vector3(); + normal = new THREE.Vector3(); + position2 = new THREE.Vector3(); - } } - this.computeFaceNormals(); - this.computeVertexNormals(); - - function getPos( u, in_q, in_p, radius, heightScale ) { - - var cu = Math.cos( u ); - var su = Math.sin( u ); - var quOverP = in_q / in_p * u; - var cs = Math.cos( quOverP ); + // Safeguards if bevels are not enabled - var tx = radius * ( 2 + cs ) * 0.5 * cu; - var ty = radius * ( 2 + cs ) * su * 0.5; - var tz = heightScale * radius * Math.sin( quOverP ) * 0.5; + if ( ! bevelEnabled ) { - return new THREE.Vector3( tx, ty, tz ); + bevelSegments = 0; + bevelThickness = 0; + bevelSize = 0; } -}; - -THREE.TorusKnotGeometry.prototype = Object.create( THREE.Geometry.prototype ); - -// File:src/extras/geometries/TubeGeometry.js + // Variables initalization -/** - * @author WestLangley / https://github.com/WestLangley - * @author zz85 / https://github.com/zz85 - * @author miningold / https://github.com/miningold - * - * Modified from the TorusKnotGeometry by @oosmoxiecode - * - * Creates a tube which extrudes along a 3d spline - * - * Uses parallel transport frames as described in - * http://www.cs.indiana.edu/pub/techreports/TR425.pdf - */ + var ahole, h, hl; // looping of holes + var scope = this; + var bevelPoints = []; -THREE.TubeGeometry = function ( path, segments, radius, radialSegments, closed ) { + var shapesOffset = this.vertices.length; - THREE.Geometry.call( this ); + var shapePoints = shape.extractPoints( curveSegments ); - this.parameters = { - path: path, - segments: segments, - radius: radius, - radialSegments: radialSegments, - closed: closed - }; + var vertices = shapePoints.shape; + var holes = shapePoints.holes; - segments = segments || 64; - radius = radius || 1; - radialSegments = radialSegments || 8; - closed = closed || false; + var reverse = ! THREE.Shape.Utils.isClockWise( vertices ) ; - var grid = []; + if ( reverse ) { - var scope = this, + vertices = vertices.reverse(); - tangent, - normal, - binormal, + // Maybe we should also check if holes are in the opposite direction, just to be safe ... - numpoints = segments + 1, + for ( h = 0, hl = holes.length; h < hl; h ++ ) { - x, y, z, - tx, ty, tz, - u, v, + ahole = holes[ h ]; - cx, cy, - pos, pos2 = new THREE.Vector3(), - i, j, - ip, jp, - a, b, c, d, - uva, uvb, uvc, uvd; + if ( THREE.Shape.Utils.isClockWise( ahole ) ) { - var frames = new THREE.TubeGeometry.FrenetFrames( path, segments, closed ), - tangents = frames.tangents, - normals = frames.normals, - binormals = frames.binormals; + holes[ h ] = ahole.reverse(); - // proxy internals - this.tangents = tangents; - this.normals = normals; - this.binormals = binormals; + } - function vert( x, y, z ) { + } - return scope.vertices.push( new THREE.Vector3( x, y, z ) ) - 1; + reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)! } - // consruct the grid - - for ( i = 0; i < numpoints; i ++ ) { - - grid[ i ] = []; - u = i / ( numpoints - 1 ); + var faces = THREE.Shape.Utils.triangulateShape ( vertices, holes ); - pos = path.getPointAt( u ); + /* Vertices */ - tangent = tangents[ i ]; - normal = normals[ i ]; - binormal = binormals[ i ]; + var contour = vertices; // vertices has all points but contour has only points of circumference - for ( j = 0; j < radialSegments; j ++ ) { + for ( h = 0, hl = holes.length; h < hl; h ++ ) { - v = j / radialSegments * 2 * Math.PI; + ahole = holes[ h ]; - cx = - radius * Math.cos( v ); // TODO: Hack: Negating it so it faces outside. - cy = radius * Math.sin( v ); + vertices = vertices.concat( ahole ); - pos2.copy( pos ); - pos2.x += cx * normal.x + cy * binormal.x; - pos2.y += cx * normal.y + cy * binormal.y; - pos2.z += cx * normal.z + cy * binormal.z; + } - grid[ i ][ j ] = vert( pos2.x, pos2.y, pos2.z ); - } - } + function scalePt2 ( pt, vec, size ) { + if ( ! vec ) console.log( "die" ); - // construct the mesh + return vec.clone().multiplyScalar( size ).add( pt ); - for ( i = 0; i < segments; i ++ ) { + } - for ( j = 0; j < radialSegments; j ++ ) { + var b, bs, t, z, + vert, vlen = vertices.length, + face, flen = faces.length, + cont, clen = contour.length; - ip = ( closed ) ? (i + 1) % segments : i + 1; - jp = (j + 1) % radialSegments; - a = grid[ i ][ j ]; // *** NOT NECESSARILY PLANAR ! *** - b = grid[ ip ][ j ]; - c = grid[ ip ][ jp ]; - d = grid[ i ][ jp ]; + // Find directions for point movement - uva = new THREE.Vector2( i / segments, j / radialSegments ); - uvb = new THREE.Vector2( ( i + 1 ) / segments, j / radialSegments ); - uvc = new THREE.Vector2( ( i + 1 ) / segments, ( j + 1 ) / radialSegments ); - uvd = new THREE.Vector2( i / segments, ( j + 1 ) / radialSegments ); + var RAD_TO_DEGREES = 180 / Math.PI; - this.faces.push( new THREE.Face3( a, b, d ) ); - this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); - this.faces.push( new THREE.Face3( b, c, d ) ); - this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); + function getBevelVec( inPt, inPrev, inNext ) { - } - } + var EPSILON = 0.0000000001; + + // computes for inPt the corresponding point inPt' on a new contour + // shiftet by 1 unit (length of normalized vector) to the left + // if we walk along contour clockwise, this new contour is outside the old one + // + // inPt' is the intersection of the two lines parallel to the two + // adjacent edges of inPt at a distance of 1 unit on the left side. + + var v_trans_x, v_trans_y, shrink_by = 1; // resulting translation vector for inPt - this.computeFaceNormals(); - this.computeVertexNormals(); + // good reading for geometry algorithms (here: line-line intersection) + // http://geomalgorithms.com/a05-_intersect-1.html -}; + var v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y; + var v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y; + + var v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); + + // check for colinear edges + var colinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); + + if ( Math.abs( colinear0 ) > EPSILON ) { // not colinear + + // length of vectors for normalizing + + var v_prev_len = Math.sqrt( v_prev_lensq ); + var v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); + + // shift adjacent points by unit vectors to the left + + var ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); + var ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); + + var ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); + var ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); + + // scaling factor for v_prev to intersection point + + var sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y - + ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) / + ( v_prev_x * v_next_y - v_prev_y * v_next_x ); + + // vector from inPt to intersection point + + v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); + v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); + + // Don't normalize!, otherwise sharp corners become ugly + // but prevent crazy spikes + var v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ) + if ( v_trans_lensq <= 2 ) { + return new THREE.Vector2( v_trans_x, v_trans_y ); + } else { + shrink_by = Math.sqrt( v_trans_lensq / 2 ); + } + + } else { // handle special case of colinear edges -THREE.TubeGeometry.prototype = Object.create( THREE.Geometry.prototype ); + var direction_eq = false; // assumes: opposite + if ( v_prev_x > EPSILON ) { + if ( v_next_x > EPSILON ) { direction_eq = true; } + } else { + if ( v_prev_x < - EPSILON ) { + if ( v_next_x < - EPSILON ) { direction_eq = true; } + } else { + if ( Math.sign(v_prev_y) == Math.sign(v_next_y) ) { direction_eq = true; } + } + } + if ( direction_eq ) { + // console.log("Warning: lines are a straight sequence"); + v_trans_x = - v_prev_y; + v_trans_y = v_prev_x; + shrink_by = Math.sqrt( v_prev_lensq ); + } else { + // console.log("Warning: lines are a straight spike"); + v_trans_x = v_prev_x; + v_trans_y = v_prev_y; + shrink_by = Math.sqrt( v_prev_lensq / 2 ); + } -// For computing of Frenet frames, exposing the tangents, normals and binormals the spline -THREE.TubeGeometry.FrenetFrames = function ( path, segments, closed ) { + } - var tangent = new THREE.Vector3(), - normal = new THREE.Vector3(), - binormal = new THREE.Vector3(), + return new THREE.Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); - tangents = [], - normals = [], - binormals = [], + } - vec = new THREE.Vector3(), - mat = new THREE.Matrix4(), - numpoints = segments + 1, - theta, - epsilon = 0.0001, - smallest, + var contourMovements = []; - tx, ty, tz, - i, u, v; + for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { + if ( j === il ) j = 0; + if ( k === il ) k = 0; - // expose internals - this.tangents = tangents; - this.normals = normals; - this.binormals = binormals; + // (j)---(i)---(k) + // console.log('i,j,k', i, j , k) - // compute the tangent vectors for each segment on the path + var pt_i = contour[ i ]; + var pt_j = contour[ j ]; + var pt_k = contour[ k ]; - for ( i = 0; i < numpoints; i ++ ) { + contourMovements[ i ]= getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); - u = i / ( numpoints - 1 ); + } - tangents[ i ] = path.getTangentAt( u ); - tangents[ i ].normalize(); + var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat(); - } + for ( h = 0, hl = holes.length; h < hl; h ++ ) { - initialNormal3(); + ahole = holes[ h ]; - /* - function initialNormal1(lastBinormal) { - // fixed start binormal. Has dangers of 0 vectors - normals[ 0 ] = new THREE.Vector3(); - binormals[ 0 ] = new THREE.Vector3(); - if (lastBinormal===undefined) lastBinormal = new THREE.Vector3( 0, 0, 1 ); - normals[ 0 ].crossVectors( lastBinormal, tangents[ 0 ] ).normalize(); - binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize(); - } + oneHoleMovements = []; - function initialNormal2() { + for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { - // This uses the Frenet-Serret formula for deriving binormal - var t2 = path.getTangentAt( epsilon ); + if ( j === il ) j = 0; + if ( k === il ) k = 0; - normals[ 0 ] = new THREE.Vector3().subVectors( t2, tangents[ 0 ] ).normalize(); - binormals[ 0 ] = new THREE.Vector3().crossVectors( tangents[ 0 ], normals[ 0 ] ); + // (j)---(i)---(k) + oneHoleMovements[ i ]= getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); - normals[ 0 ].crossVectors( binormals[ 0 ], tangents[ 0 ] ).normalize(); // last binormal x tangent - binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize(); + } + + holesMovements.push( oneHoleMovements ); + verticesMovements = verticesMovements.concat( oneHoleMovements ); } - */ - function initialNormal3() { - // select an initial normal vector perpenicular to the first tangent vector, - // and in the direction of the smallest tangent xyz component - normals[ 0 ] = new THREE.Vector3(); - binormals[ 0 ] = new THREE.Vector3(); - smallest = Number.MAX_VALUE; - tx = Math.abs( tangents[ 0 ].x ); - ty = Math.abs( tangents[ 0 ].y ); - tz = Math.abs( tangents[ 0 ].z ); + // Loop bevelSegments, 1 for the front, 1 for the back - if ( tx <= smallest ) { - smallest = tx; - normal.set( 1, 0, 0 ); - } + for ( b = 0; b < bevelSegments; b ++ ) { + //for ( b = bevelSegments; b > 0; b -- ) { - if ( ty <= smallest ) { - smallest = ty; - normal.set( 0, 1, 0 ); - } + t = b / bevelSegments; + z = bevelThickness * ( 1 - t ); - if ( tz <= smallest ) { - normal.set( 0, 0, 1 ); - } + //z = bevelThickness * t; + bs = bevelSize * ( Math.sin ( t * Math.PI/2 ) ) ; // curved + //bs = bevelSize * t ; // linear - vec.crossVectors( tangents[ 0 ], normal ).normalize(); + // contract shape - normals[ 0 ].crossVectors( tangents[ 0 ], vec ); - binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); - } + for ( i = 0, il = contour.length; i < il; i ++ ) { + vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); - // compute the slowly-varying normal and binormal vectors for each segment on the path + v( vert.x, vert.y, - z ); - for ( i = 1; i < numpoints; i ++ ) { + } - normals[ i ] = normals[ i-1 ].clone(); + // expand holes - binormals[ i ] = binormals[ i-1 ].clone(); + for ( h = 0, hl = holes.length; h < hl; h ++ ) { - vec.crossVectors( tangents[ i-1 ], tangents[ i ] ); + ahole = holes[ h ]; + oneHoleMovements = holesMovements[ h ]; - if ( vec.length() > epsilon ) { + for ( i = 0, il = ahole.length; i < il; i ++ ) { - vec.normalize(); + vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); - theta = Math.acos( THREE.Math.clamp( tangents[ i-1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors + v( vert.x, vert.y, - z ); - normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); + } } - binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); - } + bs = bevelSize; - // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same + // Back facing vertices - if ( closed ) { + for ( i = 0; i < vlen; i ++ ) { - theta = Math.acos( THREE.Math.clamp( normals[ 0 ].dot( normals[ numpoints-1 ] ), - 1, 1 ) ); - theta /= ( numpoints - 1 ); + vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; - if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ numpoints-1 ] ) ) > 0 ) { + if ( ! extrudeByPath ) { - theta = - theta; + v( vert.x, vert.y, 0 ); - } + } else { - for ( i = 1; i < numpoints; i ++ ) { + // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); + + normal.copy( splineTube.normals[0] ).multiplyScalar(vert.x); + binormal.copy( splineTube.binormals[0] ).multiplyScalar(vert.y); + + position2.copy( extrudePts[0] ).add(normal).add(binormal); - // twist a little... - normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); - binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); + v( position2.x, position2.y, position2.z ); } } -}; -// File:src/extras/geometries/PolyhedronGeometry.js + // Add stepped vertices... + // Including front facing vertices -/** - * @author clockworkgeek / https://github.com/clockworkgeek - * @author timothypratley / https://github.com/timothypratley - * @author WestLangley / http://github.com/WestLangley -*/ + var s; -THREE.PolyhedronGeometry = function ( vertices, indices, radius, detail ) { + for ( s = 1; s <= steps; s ++ ) { - THREE.Geometry.call( this ); + for ( i = 0; i < vlen; i ++ ) { - radius = radius || 1; - detail = detail || 0; + vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; - var that = this; + if ( ! extrudeByPath ) { - for ( var i = 0, l = vertices.length; i < l; i += 3 ) { + v( vert.x, vert.y, amount / steps * s ); - prepare( new THREE.Vector3( vertices[ i ], vertices[ i + 1 ], vertices[ i + 2 ] ) ); + } else { - } + // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); - var midpoints = [], p = this.vertices; + normal.copy( splineTube.normals[s] ).multiplyScalar( vert.x ); + binormal.copy( splineTube.binormals[s] ).multiplyScalar( vert.y ); - var faces = []; + position2.copy( extrudePts[s] ).add( normal ).add( binormal ); - for ( var i = 0, j = 0, l = indices.length; i < l; i += 3, j ++ ) { + v( position2.x, position2.y, position2.z ); - var v1 = p[ indices[ i ] ]; - var v2 = p[ indices[ i + 1 ] ]; - var v3 = p[ indices[ i + 2 ] ]; + } - faces[ j ] = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] ); + } } - var centroid = new THREE.Vector3(); - for ( var i = 0, l = faces.length; i < l; i ++ ) { + // Add bevel segments planes - subdivide( faces[ i ], detail ); + //for ( b = 1; b <= bevelSegments; b ++ ) { + for ( b = bevelSegments - 1; b >= 0; b -- ) { - } + t = b / bevelSegments; + z = bevelThickness * ( 1 - t ); + //bs = bevelSize * ( 1-Math.sin ( ( 1 - t ) * Math.PI/2 ) ); + bs = bevelSize * Math.sin ( t * Math.PI/2 ) ; + // contract shape - // Handle case when face straddles the seam + for ( i = 0, il = contour.length; i < il; i ++ ) { - for ( var i = 0, l = this.faceVertexUvs[ 0 ].length; i < l; i ++ ) { + vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); + v( vert.x, vert.y, amount + z ); - var uvs = this.faceVertexUvs[ 0 ][ i ]; + } - var x0 = uvs[ 0 ].x; - var x1 = uvs[ 1 ].x; - var x2 = uvs[ 2 ].x; + // expand holes - var max = Math.max( x0, Math.max( x1, x2 ) ); - var min = Math.min( x0, Math.min( x1, x2 ) ); + for ( h = 0, hl = holes.length; h < hl; h ++ ) { - if ( max > 0.9 && min < 0.1 ) { // 0.9 is somewhat arbitrary + ahole = holes[ h ]; + oneHoleMovements = holesMovements[ h ]; - if ( x0 < 0.2 ) uvs[ 0 ].x += 1; - if ( x1 < 0.2 ) uvs[ 1 ].x += 1; - if ( x2 < 0.2 ) uvs[ 2 ].x += 1; + for ( i = 0, il = ahole.length; i < il; i ++ ) { - } + vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); - } + if ( ! extrudeByPath ) { + v( vert.x, vert.y, amount + z ); - // Apply radius + } else { - for ( var i = 0, l = this.vertices.length; i < l; i ++ ) { + v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); - this.vertices[ i ].multiplyScalar( radius ); + } - } + } + } - // Merge vertices + } - this.mergeVertices(); + /* Faces */ - this.computeFaceNormals(); + // Top and bottom faces - this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); + buildLidFaces(); + // Sides faces - // Project vector onto sphere's surface + buildSideFaces(); - function prepare( vector ) { - var vertex = vector.normalize().clone(); - vertex.index = that.vertices.push( vertex ) - 1; + ///// Internal functions - // Texture coords are equivalent to map coords, calculate angle and convert to fraction of a circle. + function buildLidFaces() { - var u = azimuth( vector ) / 2 / Math.PI + 0.5; - var v = inclination( vector ) / Math.PI + 0.5; - vertex.uv = new THREE.Vector2( u, 1 - v ); + if ( bevelEnabled ) { - return vertex; + var layer = 0 ; // steps + 1 + var offset = vlen * layer; - } + // Bottom faces + for ( i = 0; i < flen; i ++ ) { - // Approximate a curved face with recursively sub-divided triangles. + face = faces[ i ]; + f3( face[ 2 ]+ offset, face[ 1 ]+ offset, face[ 0 ] + offset ); - function make( v1, v2, v3 ) { + } - var face = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] ); - that.faces.push( face ); + layer = steps + bevelSegments * 2; + offset = vlen * layer; - centroid.copy( v1 ).add( v2 ).add( v3 ).divideScalar( 3 ); + // Top faces - var azi = azimuth( centroid ); + for ( i = 0; i < flen; i ++ ) { - that.faceVertexUvs[ 0 ].push( [ - correctUV( v1.uv, v1, azi ), - correctUV( v2.uv, v2, azi ), - correctUV( v3.uv, v3, azi ) - ] ); + face = faces[ i ]; + f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset ); - } + } + } else { - // Analytically subdivide a face to the required detail level. + // Bottom faces - function subdivide( face, detail ) { + for ( i = 0; i < flen; i ++ ) { - var cols = Math.pow(2, detail); - var cells = Math.pow(4, detail); - var a = prepare( that.vertices[ face.a ] ); - var b = prepare( that.vertices[ face.b ] ); - var c = prepare( that.vertices[ face.c ] ); - var v = []; + face = faces[ i ]; + f3( face[ 2 ], face[ 1 ], face[ 0 ] ); - // Construct all of the vertices for this subdivision. + } - for ( var i = 0 ; i <= cols; i ++ ) { + // Top faces - v[ i ] = []; + for ( i = 0; i < flen; i ++ ) { - var aj = prepare( a.clone().lerp( c, i / cols ) ); - var bj = prepare( b.clone().lerp( c, i / cols ) ); - var rows = cols - i; + face = faces[ i ]; + f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); - for ( var j = 0; j <= rows; j ++) { + } + } - if ( j == 0 && i == cols ) { + } - v[ i ][ j ] = aj; + // Create faces for the z-sides of the shape - } else { + function buildSideFaces() { - v[ i ][ j ] = prepare( aj.clone().lerp( bj, j / rows ) ); + var layeroffset = 0; + sidewalls( contour, layeroffset ); + layeroffset += contour.length; - } + for ( h = 0, hl = holes.length; h < hl; h ++ ) { - } + ahole = holes[ h ]; + sidewalls( ahole, layeroffset ); + + //, true + layeroffset += ahole.length; } - // Construct all of the faces. + } - for ( var i = 0; i < cols ; i ++ ) { + function sidewalls( contour, layeroffset ) { - for ( var j = 0; j < 2 * (cols - i) - 1; j ++ ) { + var j, k; + i = contour.length; - var k = Math.floor( j / 2 ); + while ( --i >= 0 ) { - if ( j % 2 == 0 ) { + j = i; + k = i - 1; + if ( k < 0 ) k = contour.length - 1; - make( - v[ i ][ k + 1], - v[ i + 1 ][ k ], - v[ i ][ k ] - ); + //console.log('b', i,j, i-1, k,vertices.length); - } else { + var s = 0, sl = steps + bevelSegments * 2; - make( - v[ i ][ k + 1 ], - v[ i + 1][ k + 1], - v[ i + 1 ][ k ] - ); + for ( s = 0; s < sl; s ++ ) { - } + var slen1 = vlen * s; + var slen2 = vlen * ( s + 1 ); - } + var a = layeroffset + j + slen1, + b = layeroffset + k + slen1, + c = layeroffset + k + slen2, + d = layeroffset + j + slen2; + + f4( a, b, c, d, contour, s, sl, j, k ); + } } } - // Angle around the Y axis, counter-clockwise when looking from above. - - function azimuth( vector ) { + function v( x, y, z ) { - return Math.atan2( vector.z, - vector.x ); + scope.vertices.push( new THREE.Vector3( x, y, z ) ); } + function f3( a, b, c ) { - // Angle above the XZ plane. + a += shapesOffset; + b += shapesOffset; + c += shapesOffset; - function inclination( vector ) { + // normal, color, material + scope.faces.push( new THREE.Face3( a, b, c, null, null, material ) ); - return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) ); + var uvs = uvgen.generateTopUV( scope, a, b, c ); + + scope.faceVertexUvs[ 0 ].push( uvs ); } + function f4( a, b, c, d, wallContour, stepIndex, stepsLength, contourIndex1, contourIndex2 ) { - // Texture fixing helper. Spheres have some odd behaviours. + a += shapesOffset; + b += shapesOffset; + c += shapesOffset; + d += shapesOffset; - function correctUV( uv, vector, azimuth ) { + scope.faces.push( new THREE.Face3( a, b, d, null, null, extrudeMaterial ) ); + scope.faces.push( new THREE.Face3( b, c, d, null, null, extrudeMaterial ) ); - if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) uv = new THREE.Vector2( uv.x - 1, uv.y ); - if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) uv = new THREE.Vector2( azimuth / 2 / Math.PI + 0.5, uv.y ); - return uv.clone(); + var uvs = uvgen.generateSideWallUV( scope, a, b, c, d ); - } + scope.faceVertexUvs[ 0 ].push( [ uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] ] ); + scope.faceVertexUvs[ 0 ].push( [ uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] ] ); + } }; -THREE.PolyhedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.ExtrudeGeometry.WorldUVGenerator = { -// File:src/extras/geometries/IcosahedronGeometry.js + generateTopUV: function ( geometry, indexA, indexB, indexC ) { -/** - * @author timothypratley / https://github.com/timothypratley - */ + var vertices = geometry.vertices; -THREE.IcosahedronGeometry = function ( radius, detail ) { + var a = vertices[ indexA ]; + var b = vertices[ indexB ]; + var c = vertices[ indexC ]; - this.parameters = { - radius: radius, - detail: detail - }; + return [ + new THREE.Vector2( a.x, a.y ), + new THREE.Vector2( b.x, b.y ), + new THREE.Vector2( c.x, c.y ) + ]; - var t = ( 1 + Math.sqrt( 5 ) ) / 2; + }, - var vertices = [ - - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, 0, - 0, - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, - t, 0, - 1, t, 0, 1, - t, 0, - 1, - t, 0, 1 - ]; + generateSideWallUV: function ( geometry, indexA, indexB, indexC, indexD ) { - var indices = [ - 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, - 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, - 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, - 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1 - ]; + var vertices = geometry.vertices; - THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); + var a = vertices[ indexA ]; + var b = vertices[ indexB ]; + var c = vertices[ indexC ]; + var d = vertices[ indexD ]; + if ( Math.abs( a.y - b.y ) < 0.01 ) { + return [ + new THREE.Vector2( a.x, 1 - a.z ), + new THREE.Vector2( b.x, 1 - b.z ), + new THREE.Vector2( c.x, 1 - c.z ), + new THREE.Vector2( d.x, 1 - d.z ) + ]; + } else { + return [ + new THREE.Vector2( a.y, 1 - a.z ), + new THREE.Vector2( b.y, 1 - b.z ), + new THREE.Vector2( c.y, 1 - c.z ), + new THREE.Vector2( d.y, 1 - d.z ) + ]; + } + } }; -THREE.IcosahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); - -// File:src/extras/geometries/OctahedronGeometry.js +// File:src/extras/geometries/ShapeGeometry.js /** - * @author timothypratley / https://github.com/timothypratley - */ + * @author jonobr1 / http://jonobr1.com + * + * Creates a one-sided polygonal geometry from a path shape. Similar to + * ExtrudeGeometry. + * + * parameters = { + * + * curveSegments: , // number of points on the curves. NOT USED AT THE MOMENT. + * + * material: // material index for front and back faces + * uvGenerator: // object that provides UV generator functions + * + * } + **/ -THREE.OctahedronGeometry = function ( radius, detail ) { +THREE.ShapeGeometry = function ( shapes, options ) { - this.parameters = { - radius: radius, - detail: detail - }; + THREE.Geometry.call( this ); - var vertices = [ - 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0,- 1, 0, 0, 0, 1, 0, 0,- 1 - ]; + this.type = 'ShapeGeometry'; - var indices = [ - 0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2 - ]; + if ( shapes instanceof Array === false ) shapes = [ shapes ]; - THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); -}; + this.addShapeList( shapes, options ); -THREE.OctahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); + this.computeFaceNormals(); -// File:src/extras/geometries/TetrahedronGeometry.js +}; + +THREE.ShapeGeometry.prototype = Object.create( THREE.Geometry.prototype ); /** - * @author timothypratley / https://github.com/timothypratley + * Add an array of shapes to THREE.ShapeGeometry. */ +THREE.ShapeGeometry.prototype.addShapeList = function ( shapes, options ) { -THREE.TetrahedronGeometry = function ( radius, detail ) { + for ( var i = 0, l = shapes.length; i < l; i ++ ) { - var vertices = [ - 1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1 - ]; + this.addShape( shapes[ i ], options ); - var indices = [ - 2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1 - ]; + } - THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); + return this; }; -THREE.TetrahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); - -// File:src/extras/geometries/ParametricGeometry.js - /** - * @author zz85 / https://github.com/zz85 - * Parametric Surfaces Geometry - * based on the brilliant article by @prideout http://prideout.net/blog/?p=44 - * - * new THREE.ParametricGeometry( parametricFunction, uSegments, ySegements ); - * + * Adds a shape to THREE.ShapeGeometry, based on THREE.ExtrudeGeometry. */ +THREE.ShapeGeometry.prototype.addShape = function ( shape, options ) { -THREE.ParametricGeometry = function ( func, slices, stacks ) { + if ( options === undefined ) options = {}; + var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; - THREE.Geometry.call( this ); + var material = options.material; + var uvgen = options.UVGenerator === undefined ? THREE.ExtrudeGeometry.WorldUVGenerator : options.UVGenerator; - var verts = this.vertices; - var faces = this.faces; - var uvs = this.faceVertexUvs[ 0 ]; + // - var i, il, j, p; - var u, v; + var i, l, hole, s; - var stackCount = stacks + 1; - var sliceCount = slices + 1; + var shapesOffset = this.vertices.length; + var shapePoints = shape.extractPoints( curveSegments ); - for ( i = 0; i <= stacks; i ++ ) { + var vertices = shapePoints.shape; + var holes = shapePoints.holes; - v = i / stacks; + var reverse = ! THREE.Shape.Utils.isClockWise( vertices ); - for ( j = 0; j <= slices; j ++ ) { + if ( reverse ) { - u = j / slices; + vertices = vertices.reverse(); - p = func( u, v ); - verts.push( p ); + // Maybe we should also check if holes are in the opposite direction, just to be safe... - } - } + for ( i = 0, l = holes.length; i < l; i ++ ) { - var a, b, c, d; - var uva, uvb, uvc, uvd; + hole = holes[ i ]; - for ( i = 0; i < stacks; i ++ ) { + if ( THREE.Shape.Utils.isClockWise( hole ) ) { - for ( j = 0; j < slices; j ++ ) { + holes[ i ] = hole.reverse(); - a = i * sliceCount + j; - b = i * sliceCount + j + 1; - c = (i + 1) * sliceCount + j + 1; - d = (i + 1) * sliceCount + j; + } - uva = new THREE.Vector2( j / slices, i / stacks ); - uvb = new THREE.Vector2( ( j + 1 ) / slices, i / stacks ); - uvc = new THREE.Vector2( ( j + 1 ) / slices, ( i + 1 ) / stacks ); - uvd = new THREE.Vector2( j / slices, ( i + 1 ) / stacks ); + } - faces.push( new THREE.Face3( a, b, d ) ); - uvs.push( [ uva, uvb, uvd ] ); + reverse = false; - faces.push( new THREE.Face3( b, c, d ) ); - uvs.push( [ uvb.clone(), uvc, uvd.clone() ] ); + } - } + var faces = THREE.Shape.Utils.triangulateShape( vertices, holes ); - } + // Vertices - // console.log(this); + var contour = vertices; - // magic bullet - // var diff = this.mergeVertices(); - // console.log('removed ', diff, ' vertices by merging'); + for ( i = 0, l = holes.length; i < l; i ++ ) { - this.computeFaceNormals(); - this.computeVertexNormals(); + hole = holes[ i ]; + vertices = vertices.concat( hole ); -}; + } -THREE.ParametricGeometry.prototype = Object.create( THREE.Geometry.prototype ); + // -// File:src/extras/helpers/AxisHelper.js + var vert, vlen = vertices.length; + var face, flen = faces.length; + var cont, clen = contour.length; -/** - * @author sroucheray / http://sroucheray.org/ - * @author mrdoob / http://mrdoob.com/ - */ + for ( i = 0; i < vlen; i ++ ) { -THREE.AxisHelper = function ( size ) { + vert = vertices[ i ]; - size = size || 1; + this.vertices.push( new THREE.Vector3( vert.x, vert.y, 0 ) ); - var vertices = new Float32Array( [ - 0, 0, 0, size, 0, 0, - 0, 0, 0, 0, size, 0, - 0, 0, 0, 0, 0, size - ] ); + } - var colors = new Float32Array( [ - 1, 0, 0, 1, 0.6, 0, - 0, 1, 0, 0.6, 1, 0, - 0, 0, 1, 0, 0.6, 1 - ] ); + for ( i = 0; i < flen; i ++ ) { - var geometry = new THREE.BufferGeometry(); - geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); - geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) ); + face = faces[ i ]; - var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } ); + var a = face[ 0 ] + shapesOffset; + var b = face[ 1 ] + shapesOffset; + var c = face[ 2 ] + shapesOffset; - THREE.Line.call( this, geometry, material, THREE.LinePieces ); + this.faces.push( new THREE.Face3( a, b, c, null, null, material ) ); + this.faceVertexUvs[ 0 ].push( uvgen.generateTopUV( this, a, b, c ) ); -}; + } -THREE.AxisHelper.prototype = Object.create( THREE.Line.prototype ); +}; -// File:src/extras/helpers/ArrowHelper.js +// File:src/extras/geometries/LatheGeometry.js /** - * @author WestLangley / http://github.com/WestLangley - * @author zz85 / http://github.com/zz85 + * @author astrodud / http://astrodud.isgreat.org/ + * @author zz85 / https://github.com/zz85 * @author bhouston / http://exocortex.com - * - * Creates an arrow for visualizing directions - * - * Parameters: - * dir - Vector3 - * origin - Vector3 - * length - Number - * color - color in hex value - * headLength - Number - * headWidth - Number */ -THREE.ArrowHelper = ( function () { +// points - to create a closed torus, one must use a set of points +// like so: [ a, b, c, d, a ], see first is the same as last. +// segments - the number of circumference segments to create +// phiStart - the starting radian +// phiLength - the radian (0 to 2*PI) range of the lathed section +// 2*pi is a closed lathe, less than 2PI is a portion. - var lineGeometry = new THREE.Geometry(); - lineGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 1, 0 ) ); +THREE.LatheGeometry = function ( points, segments, phiStart, phiLength ) { - var coneGeometry = new THREE.CylinderGeometry( 0, 0.5, 1, 5, 1 ); - coneGeometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, - 0.5, 0 ) ); + THREE.Geometry.call( this ); + + this.type = 'LatheGeometry'; + + this.parameters = { + points: points, + segments: segments, + phiStart: phiStart, + phiLength: phiLength + }; + + segments = segments || 12; + phiStart = phiStart || 0; + phiLength = phiLength || 2 * Math.PI; + + var inversePointLength = 1.0 / ( points.length - 1 ); + var inverseSegments = 1.0 / segments; + + for ( var i = 0, il = segments; i <= il; i ++ ) { - return function ( dir, origin, length, color, headLength, headWidth ) { + var phi = phiStart + i * inverseSegments * phiLength; - // dir is assumed to be normalized + var c = Math.cos( phi ), + s = Math.sin( phi ); - THREE.Object3D.call( this ); + for ( var j = 0, jl = points.length; j < jl; j ++ ) { - if ( color === undefined ) color = 0xffff00; - if ( length === undefined ) length = 1; - if ( headLength === undefined ) headLength = 0.2 * length; - if ( headWidth === undefined ) headWidth = 0.2 * headLength; + var pt = points[ j ]; - this.position.copy( origin ); + var vertex = new THREE.Vector3(); - this.line = new THREE.Line( lineGeometry, new THREE.LineBasicMaterial( { color: color } ) ); - this.line.matrixAutoUpdate = false; - this.add( this.line ); + vertex.x = c * pt.x - s * pt.y; + vertex.y = s * pt.x + c * pt.y; + vertex.z = pt.z; - this.cone = new THREE.Mesh( coneGeometry, new THREE.MeshBasicMaterial( { color: color } ) ); - this.cone.matrixAutoUpdate = false; - this.add( this.cone ); + this.vertices.push( vertex ); - this.setDirection( dir ); - this.setLength( length, headLength, headWidth ); + } } -}() ); + var np = points.length; -THREE.ArrowHelper.prototype = Object.create( THREE.Object3D.prototype ); + for ( var i = 0, il = segments; i < il; i ++ ) { -THREE.ArrowHelper.prototype.setDirection = ( function () { + for ( var j = 0, jl = points.length - 1; j < jl; j ++ ) { - var axis = new THREE.Vector3(); - var radians; + var base = j + np * i; + var a = base; + var b = base + np; + var c = base + 1 + np; + var d = base + 1; - return function ( dir ) { + var u0 = i * inverseSegments; + var v0 = j * inversePointLength; + var u1 = u0 + inverseSegments; + var v1 = v0 + inversePointLength; - // dir is assumed to be normalized + this.faces.push( new THREE.Face3( a, b, d ) ); - if ( dir.y > 0.99999 ) { + this.faceVertexUvs[ 0 ].push( [ - this.quaternion.set( 0, 0, 0, 1 ); + new THREE.Vector2( u0, v0 ), + new THREE.Vector2( u1, v0 ), + new THREE.Vector2( u0, v1 ) - } else if ( dir.y < - 0.99999 ) { + ] ); - this.quaternion.set( 1, 0, 0, 0 ); + this.faces.push( new THREE.Face3( b, c, d ) ); - } else { + this.faceVertexUvs[ 0 ].push( [ - axis.set( dir.z, 0, - dir.x ).normalize(); + new THREE.Vector2( u1, v0 ), + new THREE.Vector2( u1, v1 ), + new THREE.Vector2( u0, v1 ) - radians = Math.acos( dir.y ); + ] ); - this.quaternion.setFromAxisAngle( axis, radians ); } - }; + } -}() ); + this.mergeVertices(); + this.computeFaceNormals(); + this.computeVertexNormals(); -THREE.ArrowHelper.prototype.setLength = function ( length, headLength, headWidth ) { +}; - if ( headLength === undefined ) headLength = 0.2 * length; - if ( headWidth === undefined ) headWidth = 0.2 * headLength; +THREE.LatheGeometry.prototype = Object.create( THREE.Geometry.prototype ); - this.line.scale.set( 1, length, 1 ); - this.line.updateMatrix(); +// File:src/extras/geometries/PlaneGeometry.js - this.cone.scale.set( headWidth, headLength, headWidth ); - this.cone.position.y = length; - this.cone.updateMatrix(); +/** + * @author mrdoob / http://mrdoob.com/ + * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as + */ -}; +THREE.PlaneGeometry = function ( width, height, widthSegments, heightSegments ) { -THREE.ArrowHelper.prototype.setColor = function ( color ) { + console.info( 'THREE.PlaneGeometry: Consider using THREE.PlaneBufferGeometry for lower memory footprint.' ); - this.line.material.color.set( color ); - this.cone.material.color.set( color ); + THREE.Geometry.call( this ); + + this.type = 'PlaneGeometry'; + + this.parameters = { + width: width, + height: height, + widthSegments: widthSegments, + heightSegments: heightSegments + }; + + this.fromBufferGeometry( new THREE.PlaneBufferGeometry( width, height, widthSegments, heightSegments ) ); }; -// File:src/extras/helpers/BoxHelper.js +THREE.PlaneGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +// File:src/extras/geometries/PlaneBufferGeometry.js /** * @author mrdoob / http://mrdoob.com/ + * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as */ -THREE.BoxHelper = function ( object ) { - - var geometry = new THREE.BufferGeometry(); - geometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( 72 ), 3 ) ); - - THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: 0xffff00 } ), THREE.LinePieces ); - - if ( object !== undefined ) { +THREE.PlaneBufferGeometry = function ( width, height, widthSegments, heightSegments ) { - this.update( object ); + THREE.BufferGeometry.call( this ); - } + this.type = 'PlaneBufferGeometry'; -}; + this.parameters = { + width: width, + height: height, + widthSegments: widthSegments, + heightSegments: heightSegments + }; -THREE.BoxHelper.prototype = Object.create( THREE.Line.prototype ); + var width_half = width / 2; + var height_half = height / 2; -THREE.BoxHelper.prototype.update = function ( object ) { + var gridX = widthSegments || 1; + var gridY = heightSegments || 1; - var geometry = object.geometry; + var gridX1 = gridX + 1; + var gridY1 = gridY + 1; - if ( geometry.boundingBox === null ) { + var segment_width = width / gridX; + var segment_height = height / gridY; - geometry.computeBoundingBox(); + var vertices = new Float32Array( gridX1 * gridY1 * 3 ); + var normals = new Float32Array( gridX1 * gridY1 * 3 ); + var uvs = new Float32Array( gridX1 * gridY1 * 2 ); - } + var offset = 0; + var offset2 = 0; - var min = geometry.boundingBox.min; - var max = geometry.boundingBox.max; + for ( var iy = 0; iy < gridY1; iy ++ ) { - /* - 5____4 - 1/___0/| - | 6__|_7 - 2/___3/ + var y = iy * segment_height - height_half; - 0: max.x, max.y, max.z - 1: min.x, max.y, max.z - 2: min.x, min.y, max.z - 3: max.x, min.y, max.z - 4: max.x, max.y, min.z - 5: min.x, max.y, min.z - 6: min.x, min.y, min.z - 7: max.x, min.y, min.z - */ + for ( var ix = 0; ix < gridX1; ix ++ ) { - var vertices = this.geometry.attributes.position.array; + var x = ix * segment_width - width_half; - vertices[ 0 ] = max.x; vertices[ 1 ] = max.y; vertices[ 2 ] = max.z; - vertices[ 3 ] = min.x; vertices[ 4 ] = max.y; vertices[ 5 ] = max.z; + vertices[ offset ] = x; + vertices[ offset + 1 ] = - y; - vertices[ 6 ] = min.x; vertices[ 7 ] = max.y; vertices[ 8 ] = max.z; - vertices[ 9 ] = min.x; vertices[ 10 ] = min.y; vertices[ 11 ] = max.z; + normals[ offset + 2 ] = 1; - vertices[ 12 ] = min.x; vertices[ 13 ] = min.y; vertices[ 14 ] = max.z; - vertices[ 15 ] = max.x; vertices[ 16 ] = min.y; vertices[ 17 ] = max.z; + uvs[ offset2 ] = ix / gridX; + uvs[ offset2 + 1 ] = 1 - ( iy / gridY ); - vertices[ 18 ] = max.x; vertices[ 19 ] = min.y; vertices[ 20 ] = max.z; - vertices[ 21 ] = max.x; vertices[ 22 ] = max.y; vertices[ 23 ] = max.z; + offset += 3; + offset2 += 2; - // + } - vertices[ 24 ] = max.x; vertices[ 25 ] = max.y; vertices[ 26 ] = min.z; - vertices[ 27 ] = min.x; vertices[ 28 ] = max.y; vertices[ 29 ] = min.z; + } - vertices[ 30 ] = min.x; vertices[ 31 ] = max.y; vertices[ 32 ] = min.z; - vertices[ 33 ] = min.x; vertices[ 34 ] = min.y; vertices[ 35 ] = min.z; + offset = 0; - vertices[ 36 ] = min.x; vertices[ 37 ] = min.y; vertices[ 38 ] = min.z; - vertices[ 39 ] = max.x; vertices[ 40 ] = min.y; vertices[ 41 ] = min.z; + var indices = new ( ( vertices.length / 3 ) > 65535 ? Uint32Array : Uint16Array )( gridX * gridY * 6 ); - vertices[ 42 ] = max.x; vertices[ 43 ] = min.y; vertices[ 44 ] = min.z; - vertices[ 45 ] = max.x; vertices[ 46 ] = max.y; vertices[ 47 ] = min.z; + for ( var iy = 0; iy < gridY; iy ++ ) { - // + for ( var ix = 0; ix < gridX; ix ++ ) { - vertices[ 48 ] = max.x; vertices[ 49 ] = max.y; vertices[ 50 ] = max.z; - vertices[ 51 ] = max.x; vertices[ 52 ] = max.y; vertices[ 53 ] = min.z; + var a = ix + gridX1 * iy; + var b = ix + gridX1 * ( iy + 1 ); + var c = ( ix + 1 ) + gridX1 * ( iy + 1 ); + var d = ( ix + 1 ) + gridX1 * iy; - vertices[ 54 ] = min.x; vertices[ 55 ] = max.y; vertices[ 56 ] = max.z; - vertices[ 57 ] = min.x; vertices[ 58 ] = max.y; vertices[ 59 ] = min.z; + indices[ offset ] = a; + indices[ offset + 1 ] = b; + indices[ offset + 2 ] = d; - vertices[ 60 ] = min.x; vertices[ 61 ] = min.y; vertices[ 62 ] = max.z; - vertices[ 63 ] = min.x; vertices[ 64 ] = min.y; vertices[ 65 ] = min.z; + indices[ offset + 3 ] = b; + indices[ offset + 4 ] = c; + indices[ offset + 5 ] = d; - vertices[ 66 ] = max.x; vertices[ 67 ] = min.y; vertices[ 68 ] = max.z; - vertices[ 69 ] = max.x; vertices[ 70 ] = min.y; vertices[ 71 ] = min.z; + offset += 6; - this.geometry.attributes.position.needsUpdate = true; + } - this.geometry.computeBoundingSphere(); + } - this.matrixAutoUpdate = false; - this.matrixWorld = object.matrixWorld; + this.addAttribute( 'index', new THREE.BufferAttribute( indices, 1 ) ); + this.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); + this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) ); + this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); }; -// File:src/extras/helpers/BoundingBoxHelper.js +THREE.PlaneBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); + +// File:src/extras/geometries/RingGeometry.js /** - * @author WestLangley / http://github.com/WestLangley + * @author Kaleb Murphy */ -// a helper to show the world-axis-aligned bounding box for an object +THREE.RingGeometry = function ( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { -THREE.BoundingBoxHelper = function ( object, hex ) { + THREE.Geometry.call( this ); - var color = ( hex !== undefined ) ? hex : 0x888888; + this.type = 'RingGeometry'; - this.object = object; + this.parameters = { + innerRadius: innerRadius, + outerRadius: outerRadius, + thetaSegments: thetaSegments, + phiSegments: phiSegments, + thetaStart: thetaStart, + thetaLength: thetaLength + }; - this.box = new THREE.Box3(); + innerRadius = innerRadius || 0; + outerRadius = outerRadius || 50; - THREE.Mesh.call( this, new THREE.BoxGeometry( 1, 1, 1 ), new THREE.MeshBasicMaterial( { color: color, wireframe: true } ) ); + thetaStart = thetaStart !== undefined ? thetaStart : 0; + thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; -}; + thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8; + phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 8; -THREE.BoundingBoxHelper.prototype = Object.create( THREE.Mesh.prototype ); + var i, o, uvs = [], radius = innerRadius, radiusStep = ( ( outerRadius - innerRadius ) / phiSegments ); -THREE.BoundingBoxHelper.prototype.update = function () { + for ( i = 0; i < phiSegments + 1; i ++ ) { // concentric circles inside ring - this.box.setFromObject( this.object ); + for ( o = 0; o < thetaSegments + 1; o ++ ) { // number of segments per circle - this.box.size( this.scale ); + var vertex = new THREE.Vector3(); + var segment = thetaStart + o / thetaSegments * thetaLength; + vertex.x = radius * Math.cos( segment ); + vertex.y = radius * Math.sin( segment ); - this.box.center( this.position ); + this.vertices.push( vertex ); + uvs.push( new THREE.Vector2( ( vertex.x / outerRadius + 1 ) / 2, ( vertex.y / outerRadius + 1 ) / 2 ) ); + } -}; + radius += radiusStep; -// File:src/extras/helpers/CameraHelper.js + } -/** - * @author alteredq / http://alteredqualia.com/ - * - * - shows frustum, line of sight and up of the camera - * - suitable for fast updates - * - based on frustum visualization in lightgl.js shadowmap example - * http://evanw.github.com/lightgl.js/tests/shadowmap.html - */ + var n = new THREE.Vector3( 0, 0, 1 ); -THREE.CameraHelper = function ( camera ) { + for ( i = 0; i < phiSegments; i ++ ) { // concentric circles inside ring - var geometry = new THREE.Geometry(); - var material = new THREE.LineBasicMaterial( { color: 0xffffff, vertexColors: THREE.FaceColors } ); + var thetaSegment = i * (thetaSegments + 1); + + for ( o = 0; o < thetaSegments ; o ++ ) { // number of segments per circle + + var segment = o + thetaSegment; + + var v1 = segment; + var v2 = segment + thetaSegments + 1; + var v3 = segment + thetaSegments + 2; + + this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) ); + this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ]); + + v1 = segment; + v2 = segment + thetaSegments + 2; + v3 = segment + 1; - var pointMap = {}; + this.faces.push( new THREE.Face3( v1, v2, v3, [ n.clone(), n.clone(), n.clone() ] ) ); + this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ].clone(), uvs[ v2 ].clone(), uvs[ v3 ].clone() ]); - // colors + } + } - var hexFrustum = 0xffaa00; - var hexCone = 0xff0000; - var hexUp = 0x00aaff; - var hexTarget = 0xffffff; - var hexCross = 0x333333; + this.computeFaceNormals(); - // near + this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); - addLine( "n1", "n2", hexFrustum ); - addLine( "n2", "n4", hexFrustum ); - addLine( "n4", "n3", hexFrustum ); - addLine( "n3", "n1", hexFrustum ); +}; - // far +THREE.RingGeometry.prototype = Object.create( THREE.Geometry.prototype ); - addLine( "f1", "f2", hexFrustum ); - addLine( "f2", "f4", hexFrustum ); - addLine( "f4", "f3", hexFrustum ); - addLine( "f3", "f1", hexFrustum ); - // sides +// File:src/extras/geometries/SphereGeometry.js - addLine( "n1", "f1", hexFrustum ); - addLine( "n2", "f2", hexFrustum ); - addLine( "n3", "f3", hexFrustum ); - addLine( "n4", "f4", hexFrustum ); +/** + * @author mrdoob / http://mrdoob.com/ + */ - // cone +THREE.SphereGeometry = function ( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { - addLine( "p", "n1", hexCone ); - addLine( "p", "n2", hexCone ); - addLine( "p", "n3", hexCone ); - addLine( "p", "n4", hexCone ); + THREE.Geometry.call( this ); - // up + this.type = 'SphereGeometry'; - addLine( "u1", "u2", hexUp ); - addLine( "u2", "u3", hexUp ); - addLine( "u3", "u1", hexUp ); + this.parameters = { + radius: radius, + widthSegments: widthSegments, + heightSegments: heightSegments, + phiStart: phiStart, + phiLength: phiLength, + thetaStart: thetaStart, + thetaLength: thetaLength + }; - // target + radius = radius || 50; - addLine( "c", "t", hexTarget ); - addLine( "p", "c", hexCross ); + widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 ); + heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 ); - // cross + phiStart = phiStart !== undefined ? phiStart : 0; + phiLength = phiLength !== undefined ? phiLength : Math.PI * 2; - addLine( "cn1", "cn2", hexCross ); - addLine( "cn3", "cn4", hexCross ); + thetaStart = thetaStart !== undefined ? thetaStart : 0; + thetaLength = thetaLength !== undefined ? thetaLength : Math.PI; - addLine( "cf1", "cf2", hexCross ); - addLine( "cf3", "cf4", hexCross ); + var x, y, vertices = [], uvs = []; - function addLine( a, b, hex ) { + for ( y = 0; y <= heightSegments; y ++ ) { - addPoint( a, hex ); - addPoint( b, hex ); + var verticesRow = []; + var uvsRow = []; - } + for ( x = 0; x <= widthSegments; x ++ ) { - function addPoint( id, hex ) { + var u = x / widthSegments; + var v = y / heightSegments; - geometry.vertices.push( new THREE.Vector3() ); - geometry.colors.push( new THREE.Color( hex ) ); + var vertex = new THREE.Vector3(); + vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); + vertex.y = radius * Math.cos( thetaStart + v * thetaLength ); + vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); - if ( pointMap[ id ] === undefined ) { + this.vertices.push( vertex ); - pointMap[ id ] = []; + verticesRow.push( this.vertices.length - 1 ); + uvsRow.push( new THREE.Vector2( u, 1 - v ) ); } - pointMap[ id ].push( geometry.vertices.length - 1 ); + vertices.push( verticesRow ); + uvs.push( uvsRow ); } - THREE.Line.call( this, geometry, material, THREE.LinePieces ); + for ( y = 0; y < heightSegments; y ++ ) { - this.camera = camera; - this.matrixWorld = camera.matrixWorld; - this.matrixAutoUpdate = false; + for ( x = 0; x < widthSegments; x ++ ) { - this.pointMap = pointMap; + var v1 = vertices[ y ][ x + 1 ]; + var v2 = vertices[ y ][ x ]; + var v3 = vertices[ y + 1 ][ x ]; + var v4 = vertices[ y + 1 ][ x + 1 ]; - this.update(); + var n1 = this.vertices[ v1 ].clone().normalize(); + var n2 = this.vertices[ v2 ].clone().normalize(); + var n3 = this.vertices[ v3 ].clone().normalize(); + var n4 = this.vertices[ v4 ].clone().normalize(); -}; + var uv1 = uvs[ y ][ x + 1 ].clone(); + var uv2 = uvs[ y ][ x ].clone(); + var uv3 = uvs[ y + 1 ][ x ].clone(); + var uv4 = uvs[ y + 1 ][ x + 1 ].clone(); -THREE.CameraHelper.prototype = Object.create( THREE.Line.prototype ); + if ( Math.abs( this.vertices[ v1 ].y ) === radius ) { -THREE.CameraHelper.prototype.update = function () { + uv1.x = ( uv1.x + uv2.x ) / 2; + this.faces.push( new THREE.Face3( v1, v3, v4, [ n1, n3, n4 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv3, uv4 ] ); - var vector = new THREE.Vector3(); - var camera = new THREE.Camera(); - var projector = new THREE.Projector(); + } else if ( Math.abs( this.vertices[ v3 ].y ) === radius ) { - return function () { + uv3.x = ( uv3.x + uv4.x ) / 2; + this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] ); - var scope = this; + } else { - var w = 1, h = 1; + this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] ); - // we need just camera projection matrix - // world matrix must be identity + this.faces.push( new THREE.Face3( v2, v3, v4, [ n2.clone(), n3, n4.clone() ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv2.clone(), uv3, uv4.clone() ] ); - camera.projectionMatrix.copy( this.camera.projectionMatrix ); + } - // center / target + } - setPoint( "c", 0, 0, - 1 ); - setPoint( "t", 0, 0, 1 ); + } - // near + this.computeFaceNormals(); - setPoint( "n1", - w, - h, - 1 ); - setPoint( "n2", w, - h, - 1 ); - setPoint( "n3", - w, h, - 1 ); - setPoint( "n4", w, h, - 1 ); + this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); - // far +}; - setPoint( "f1", - w, - h, 1 ); - setPoint( "f2", w, - h, 1 ); - setPoint( "f3", - w, h, 1 ); - setPoint( "f4", w, h, 1 ); +THREE.SphereGeometry.prototype = Object.create( THREE.Geometry.prototype ); - // up +// File:src/extras/geometries/TextGeometry.js - setPoint( "u1", w * 0.7, h * 1.1, - 1 ); - setPoint( "u2", - w * 0.7, h * 1.1, - 1 ); - setPoint( "u3", 0, h * 2, - 1 ); +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * @author alteredq / http://alteredqualia.com/ + * + * For creating 3D text geometry in three.js + * + * Text = 3D Text + * + * parameters = { + * size: , // size of the text + * height: , // thickness to extrude text + * curveSegments: , // number of points on the curves + * + * font: , // font name + * weight: , // font weight (normal, bold) + * style: , // font style (normal, italics) + * + * bevelEnabled: , // turn on bevel + * bevelThickness: , // how deep into text bevel goes + * bevelSize: , // how far from text outline is bevel + * } + * + */ - // cross +/* Usage Examples - setPoint( "cf1", - w, 0, 1 ); - setPoint( "cf2", w, 0, 1 ); - setPoint( "cf3", 0, - h, 1 ); - setPoint( "cf4", 0, h, 1 ); + // TextGeometry wrapper - setPoint( "cn1", - w, 0, - 1 ); - setPoint( "cn2", w, 0, - 1 ); - setPoint( "cn3", 0, - h, - 1 ); - setPoint( "cn4", 0, h, - 1 ); + var text3d = new TextGeometry( text, options ); - function setPoint( point, x, y, z ) { + // Complete manner - vector.set( x, y, z ); - projector.unprojectVector( vector, camera ); + var textShapes = THREE.FontUtils.generateShapes( text, options ); + var text3d = new ExtrudeGeometry( textShapes, options ); - var points = scope.pointMap[ point ]; +*/ - if ( points !== undefined ) { - for ( var i = 0, il = points.length; i < il; i ++ ) { +THREE.TextGeometry = function ( text, parameters ) { - scope.geometry.vertices[ points[ i ] ].copy( vector ); + parameters = parameters || {}; - } + var textShapes = THREE.FontUtils.generateShapes( text, parameters ); - } + // translate parameters to ExtrudeGeometry API - } + parameters.amount = parameters.height !== undefined ? parameters.height : 50; - this.geometry.verticesNeedUpdate = true; + // defaults - }; + if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10; + if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8; + if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false; -}(); + THREE.ExtrudeGeometry.call( this, textShapes, parameters ); -// File:src/extras/helpers/DirectionalLightHelper.js + this.type = 'TextGeometry'; + +}; + +THREE.TextGeometry.prototype = Object.create( THREE.ExtrudeGeometry.prototype ); + +// File:src/extras/geometries/TorusGeometry.js /** - * @author alteredq / http://alteredqualia.com/ + * @author oosmoxiecode * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley + * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3DLite/src/away3dlite/primitives/Torus.as?r=2888 */ -THREE.DirectionalLightHelper = function ( light, size ) { +THREE.TorusGeometry = function ( radius, tube, radialSegments, tubularSegments, arc ) { - THREE.Object3D.call( this ); + THREE.Geometry.call( this ); - this.light = light; - this.light.updateMatrixWorld(); + this.type = 'TorusGeometry'; - this.matrixWorld = light.matrixWorld; - this.matrixAutoUpdate = false; + this.parameters = { + radius: radius, + tube: tube, + radialSegments: radialSegments, + tubularSegments: tubularSegments, + arc: arc + }; - size = size || 1; + radius = radius || 100; + tube = tube || 40; + radialSegments = radialSegments || 8; + tubularSegments = tubularSegments || 6; + arc = arc || Math.PI * 2; - var geometry = new THREE.Geometry(); - geometry.vertices.push( - new THREE.Vector3( - size, size, 0 ), - new THREE.Vector3( size, size, 0 ), - new THREE.Vector3( size, - size, 0 ), - new THREE.Vector3( - size, - size, 0 ), - new THREE.Vector3( - size, size, 0 ) - ); + var center = new THREE.Vector3(), uvs = [], normals = []; - var material = new THREE.LineBasicMaterial( { fog: false } ); - material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + for ( var j = 0; j <= radialSegments; j ++ ) { - this.lightPlane = new THREE.Line( geometry, material ); - this.add( this.lightPlane ); + for ( var i = 0; i <= tubularSegments; i ++ ) { - geometry = new THREE.Geometry(); - geometry.vertices.push( - new THREE.Vector3(), - new THREE.Vector3() - ); + var u = i / tubularSegments * arc; + var v = j / radialSegments * Math.PI * 2; - material = new THREE.LineBasicMaterial( { fog: false } ); - material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + center.x = radius * Math.cos( u ); + center.y = radius * Math.sin( u ); - this.targetLine = new THREE.Line( geometry, material ); - this.add( this.targetLine ); + var vertex = new THREE.Vector3(); + vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u ); + vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u ); + vertex.z = tube * Math.sin( v ); - this.update(); + this.vertices.push( vertex ); -}; + uvs.push( new THREE.Vector2( i / tubularSegments, j / radialSegments ) ); + normals.push( vertex.clone().sub( center ).normalize() ); -THREE.DirectionalLightHelper.prototype = Object.create( THREE.Object3D.prototype ); + } -THREE.DirectionalLightHelper.prototype.dispose = function () { - - this.lightPlane.geometry.dispose(); - this.lightPlane.material.dispose(); - this.targetLine.geometry.dispose(); - this.targetLine.material.dispose(); -}; + } -THREE.DirectionalLightHelper.prototype.update = function () { + for ( var j = 1; j <= radialSegments; j ++ ) { - var v1 = new THREE.Vector3(); - var v2 = new THREE.Vector3(); - var v3 = new THREE.Vector3(); + for ( var i = 1; i <= tubularSegments; i ++ ) { - return function () { + var a = ( tubularSegments + 1 ) * j + i - 1; + var b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1; + var c = ( tubularSegments + 1 ) * ( j - 1 ) + i; + var d = ( tubularSegments + 1 ) * j + i; - v1.setFromMatrixPosition( this.light.matrixWorld ); - v2.setFromMatrixPosition( this.light.target.matrixWorld ); - v3.subVectors( v2, v1 ); + var face = new THREE.Face3( a, b, d, [ normals[ a ].clone(), normals[ b ].clone(), normals[ d ].clone() ] ); + this.faces.push( face ); + this.faceVertexUvs[ 0 ].push( [ uvs[ a ].clone(), uvs[ b ].clone(), uvs[ d ].clone() ] ); - this.lightPlane.lookAt( v3 ); - this.lightPlane.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + face = new THREE.Face3( b, c, d, [ normals[ b ].clone(), normals[ c ].clone(), normals[ d ].clone() ] ); + this.faces.push( face ); + this.faceVertexUvs[ 0 ].push( [ uvs[ b ].clone(), uvs[ c ].clone(), uvs[ d ].clone() ] ); - this.targetLine.geometry.vertices[ 1 ].copy( v3 ); - this.targetLine.geometry.verticesNeedUpdate = true; - this.targetLine.material.color.copy( this.lightPlane.material.color ); + } } -}(); + this.computeFaceNormals(); +}; -// File:src/extras/helpers/EdgesHelper.js +THREE.TorusGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +// File:src/extras/geometries/TorusKnotGeometry.js /** - * @author WestLangley / http://github.com/WestLangley + * @author oosmoxiecode + * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3D/src/away3d/primitives/TorusKnot.as?spec=svn2473&r=2473 */ -THREE.EdgesHelper = function ( object, hex ) { - - var color = ( hex !== undefined ) ? hex : 0xffffff; +THREE.TorusKnotGeometry = function ( radius, tube, radialSegments, tubularSegments, p, q, heightScale ) { - var edge = [ 0, 0 ], hash = {}; - var sortFunction = function ( a, b ) { return a - b }; + THREE.Geometry.call( this ); - var keys = [ 'a', 'b', 'c' ]; - var geometry = new THREE.BufferGeometry(); + this.type = 'TorusKnotGeometry'; - var geometry2 = object.geometry.clone(); + this.parameters = { + radius: radius, + tube: tube, + radialSegments: radialSegments, + tubularSegments: tubularSegments, + p: p, + q: q, + heightScale: heightScale + }; - geometry2.mergeVertices(); - geometry2.computeFaceNormals(); + radius = radius || 100; + tube = tube || 40; + radialSegments = radialSegments || 64; + tubularSegments = tubularSegments || 8; + p = p || 2; + q = q || 3; + heightScale = heightScale || 1; + + var grid = new Array( radialSegments ); + var tang = new THREE.Vector3(); + var n = new THREE.Vector3(); + var bitan = new THREE.Vector3(); - var vertices = geometry2.vertices; - var faces = geometry2.faces; - var numEdges = 0; + for ( var i = 0; i < radialSegments; ++ i ) { - for ( var i = 0, l = faces.length; i < l; i ++ ) { + grid[ i ] = new Array( tubularSegments ); + var u = i / radialSegments * 2 * p * Math.PI; + var p1 = getPos( u, q, p, radius, heightScale ); + var p2 = getPos( u + 0.01, q, p, radius, heightScale ); + tang.subVectors( p2, p1 ); + n.addVectors( p2, p1 ); - var face = faces[ i ]; + bitan.crossVectors( tang, n ); + n.crossVectors( bitan, tang ); + bitan.normalize(); + n.normalize(); - for ( var j = 0; j < 3; j ++ ) { + for ( var j = 0; j < tubularSegments; ++ j ) { - edge[ 0 ] = face[ keys[ j ] ]; - edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; - edge.sort( sortFunction ); + var v = j / tubularSegments * 2 * Math.PI; + var cx = - tube * Math.cos( v ); // TODO: Hack: Negating it so it faces outside. + var cy = tube * Math.sin( v ); - var key = edge.toString(); + var pos = new THREE.Vector3(); + pos.x = p1.x + cx * n.x + cy * bitan.x; + pos.y = p1.y + cx * n.y + cy * bitan.y; + pos.z = p1.z + cx * n.z + cy * bitan.z; - if ( hash[ key ] === undefined ) { + grid[ i ][ j ] = this.vertices.push( pos ) - 1; - hash[ key ] = { vert1: edge[ 0 ], vert2: edge[ 1 ], face1: i, face2: undefined }; - numEdges ++; + } - } else { + } - hash[ key ].face2 = i; + for ( var i = 0; i < radialSegments; ++ i ) { - } + for ( var j = 0; j < tubularSegments; ++ j ) { - } + var ip = ( i + 1 ) % radialSegments; + var jp = ( j + 1 ) % tubularSegments; - } + var a = grid[ i ][ j ]; + var b = grid[ ip ][ j ]; + var c = grid[ ip ][ jp ]; + var d = grid[ i ][ jp ]; - geometry.addAttribute( 'position', new THREE.Float32Attribute( numEdges * 2 * 3, 3 ) ); + var uva = new THREE.Vector2( i / radialSegments, j / tubularSegments ); + var uvb = new THREE.Vector2( ( i + 1 ) / radialSegments, j / tubularSegments ); + var uvc = new THREE.Vector2( ( i + 1 ) / radialSegments, ( j + 1 ) / tubularSegments ); + var uvd = new THREE.Vector2( i / radialSegments, ( j + 1 ) / tubularSegments ); - var coords = geometry.attributes.position.array; + this.faces.push( new THREE.Face3( a, b, d ) ); + this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); - var index = 0; + this.faces.push( new THREE.Face3( b, c, d ) ); + this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); - for ( var key in hash ) { + } + } - var h = hash[ key ]; + this.computeFaceNormals(); + this.computeVertexNormals(); - if ( h.face2 === undefined || faces[ h.face1 ].normal.dot( faces[ h.face2 ].normal ) < 0.9999 ) { // hardwired const OK + function getPos( u, in_q, in_p, radius, heightScale ) { - var vertex = vertices[ h.vert1 ]; - coords[ index ++ ] = vertex.x; - coords[ index ++ ] = vertex.y; - coords[ index ++ ] = vertex.z; + var cu = Math.cos( u ); + var su = Math.sin( u ); + var quOverP = in_q / in_p * u; + var cs = Math.cos( quOverP ); - vertex = vertices[ h.vert2 ]; - coords[ index ++ ] = vertex.x; - coords[ index ++ ] = vertex.y; - coords[ index ++ ] = vertex.z; + var tx = radius * ( 2 + cs ) * 0.5 * cu; + var ty = radius * ( 2 + cs ) * su * 0.5; + var tz = heightScale * radius * Math.sin( quOverP ) * 0.5; - } + return new THREE.Vector3( tx, ty, tz ); } - THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color } ), THREE.LinePieces ); - - this.matrixAutoUpdate = false; - this.matrixWorld = object.matrixWorld; - }; -THREE.EdgesHelper.prototype = Object.create( THREE.Line.prototype ); +THREE.TorusKnotGeometry.prototype = Object.create( THREE.Geometry.prototype ); -// File:src/extras/helpers/FaceNormalsHelper.js +// File:src/extras/geometries/TubeGeometry.js /** - * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley -*/ - -THREE.FaceNormalsHelper = function ( object, size, hex, linewidth ) { - - this.object = object; + * @author WestLangley / https://github.com/WestLangley + * @author zz85 / https://github.com/zz85 + * @author miningold / https://github.com/miningold + * + * Modified from the TorusKnotGeometry by @oosmoxiecode + * + * Creates a tube which extrudes along a 3d spline + * + * Uses parallel transport frames as described in + * http://www.cs.indiana.edu/pub/techreports/TR425.pdf + */ - this.size = ( size !== undefined ) ? size : 1; +THREE.TubeGeometry = function ( path, segments, radius, radialSegments, closed ) { - var color = ( hex !== undefined ) ? hex : 0xffff00; + THREE.Geometry.call( this ); - var width = ( linewidth !== undefined ) ? linewidth : 1; + this.type = 'TubeGeometry'; - var geometry = new THREE.Geometry(); + this.parameters = { + path: path, + segments: segments, + radius: radius, + radialSegments: radialSegments, + closed: closed + }; - var faces = this.object.geometry.faces; + segments = segments || 64; + radius = radius || 1; + radialSegments = radialSegments || 8; + closed = closed || false; - for ( var i = 0, l = faces.length; i < l; i ++ ) { + var grid = []; - geometry.vertices.push( new THREE.Vector3(), new THREE.Vector3() ); + var scope = this, - } + tangent, + normal, + binormal, - THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces ); + numpoints = segments + 1, - this.matrixAutoUpdate = false; + x, y, z, + tx, ty, tz, + u, v, - this.normalMatrix = new THREE.Matrix3(); + cx, cy, + pos, pos2 = new THREE.Vector3(), + i, j, + ip, jp, + a, b, c, d, + uva, uvb, uvc, uvd; - this.update(); + var frames = new THREE.TubeGeometry.FrenetFrames( path, segments, closed ), + tangents = frames.tangents, + normals = frames.normals, + binormals = frames.binormals; -}; + // proxy internals + this.tangents = tangents; + this.normals = normals; + this.binormals = binormals; -THREE.FaceNormalsHelper.prototype = Object.create( THREE.Line.prototype ); + function vert( x, y, z ) { -THREE.FaceNormalsHelper.prototype.update = function () { + return scope.vertices.push( new THREE.Vector3( x, y, z ) ) - 1; - var vertices = this.geometry.vertices; + } - var object = this.object; - var objectVertices = object.geometry.vertices; - var objectFaces = object.geometry.faces; - var objectWorldMatrix = object.matrixWorld; + // consruct the grid - object.updateMatrixWorld( true ); + for ( i = 0; i < numpoints; i ++ ) { - this.normalMatrix.getNormalMatrix( objectWorldMatrix ); + grid[ i ] = []; - for ( var i = 0, i2 = 0, l = objectFaces.length; i < l; i ++, i2 += 2 ) { + u = i / ( numpoints - 1 ); - var face = objectFaces[ i ]; + pos = path.getPointAt( u ); - vertices[ i2 ].copy( objectVertices[ face.a ] ) - .add( objectVertices[ face.b ] ) - .add( objectVertices[ face.c ] ) - .divideScalar( 3 ) - .applyMatrix4( objectWorldMatrix ); + tangent = tangents[ i ]; + normal = normals[ i ]; + binormal = binormals[ i ]; - vertices[ i2 + 1 ].copy( face.normal ) - .applyMatrix3( this.normalMatrix ) - .normalize() - .multiplyScalar( this.size ) - .add( vertices[ i2 ] ); + for ( j = 0; j < radialSegments; j ++ ) { - } + v = j / radialSegments * 2 * Math.PI; - this.geometry.verticesNeedUpdate = true; + cx = - radius * Math.cos( v ); // TODO: Hack: Negating it so it faces outside. + cy = radius * Math.sin( v ); - return this; + pos2.copy( pos ); + pos2.x += cx * normal.x + cy * binormal.x; + pos2.y += cx * normal.y + cy * binormal.y; + pos2.z += cx * normal.z + cy * binormal.z; -}; + grid[ i ][ j ] = vert( pos2.x, pos2.y, pos2.z ); + } + } -// File:src/extras/helpers/GridHelper.js -/** - * @author mrdoob / http://mrdoob.com/ - */ + // construct the mesh -THREE.GridHelper = function ( size, step ) { + for ( i = 0; i < segments; i ++ ) { - var geometry = new THREE.Geometry(); - var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } ); + for ( j = 0; j < radialSegments; j ++ ) { - this.color1 = new THREE.Color( 0x444444 ); - this.color2 = new THREE.Color( 0x888888 ); + ip = ( closed ) ? (i + 1) % segments : i + 1; + jp = (j + 1) % radialSegments; - for ( var i = - size; i <= size; i += step ) { + a = grid[ i ][ j ]; // *** NOT NECESSARILY PLANAR ! *** + b = grid[ ip ][ j ]; + c = grid[ ip ][ jp ]; + d = grid[ i ][ jp ]; - geometry.vertices.push( - new THREE.Vector3( - size, 0, i ), new THREE.Vector3( size, 0, i ), - new THREE.Vector3( i, 0, - size ), new THREE.Vector3( i, 0, size ) - ); + uva = new THREE.Vector2( i / segments, j / radialSegments ); + uvb = new THREE.Vector2( ( i + 1 ) / segments, j / radialSegments ); + uvc = new THREE.Vector2( ( i + 1 ) / segments, ( j + 1 ) / radialSegments ); + uvd = new THREE.Vector2( i / segments, ( j + 1 ) / radialSegments ); - var color = i === 0 ? this.color1 : this.color2; + this.faces.push( new THREE.Face3( a, b, d ) ); + this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); - geometry.colors.push( color, color, color, color ); + this.faces.push( new THREE.Face3( b, c, d ) ); + this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); + } } - THREE.Line.call( this, geometry, material, THREE.LinePieces ); + this.computeFaceNormals(); + this.computeVertexNormals(); }; -THREE.GridHelper.prototype = Object.create( THREE.Line.prototype ); +THREE.TubeGeometry.prototype = Object.create( THREE.Geometry.prototype ); -THREE.GridHelper.prototype.setColors = function( colorCenterLine, colorGrid ) { - this.color1.set( colorCenterLine ); - this.color2.set( colorGrid ); +// For computing of Frenet frames, exposing the tangents, normals and binormals the spline +THREE.TubeGeometry.FrenetFrames = function ( path, segments, closed ) { - this.geometry.colorsNeedUpdate = true; + var tangent = new THREE.Vector3(), + normal = new THREE.Vector3(), + binormal = new THREE.Vector3(), -} + tangents = [], + normals = [], + binormals = [], -// File:src/extras/helpers/HemisphereLightHelper.js + vec = new THREE.Vector3(), + mat = new THREE.Matrix4(), -/** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - */ + numpoints = segments + 1, + theta, + epsilon = 0.0001, + smallest, -THREE.HemisphereLightHelper = function ( light, sphereSize, arrowLength, domeSize ) { + tx, ty, tz, + i, u, v; - THREE.Object3D.call( this ); - this.light = light; - this.light.updateMatrixWorld(); + // expose internals + this.tangents = tangents; + this.normals = normals; + this.binormals = binormals; - this.matrixWorld = light.matrixWorld; - this.matrixAutoUpdate = false; + // compute the tangent vectors for each segment on the path - this.colors = [ new THREE.Color(), new THREE.Color() ]; + for ( i = 0; i < numpoints; i ++ ) { - var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 ); - geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) ); + u = i / ( numpoints - 1 ); - for ( var i = 0, il = 8; i < il; i ++ ) { + tangents[ i ] = path.getTangentAt( u ); + tangents[ i ].normalize(); - geometry.faces[ i ].color = this.colors[ i < 4 ? 0 : 1 ]; + } + + initialNormal3(); + /* + function initialNormal1(lastBinormal) { + // fixed start binormal. Has dangers of 0 vectors + normals[ 0 ] = new THREE.Vector3(); + binormals[ 0 ] = new THREE.Vector3(); + if (lastBinormal===undefined) lastBinormal = new THREE.Vector3( 0, 0, 1 ); + normals[ 0 ].crossVectors( lastBinormal, tangents[ 0 ] ).normalize(); + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize(); } - var material = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors, wireframe: true } ); + function initialNormal2() { - this.lightSphere = new THREE.Mesh( geometry, material ); - this.add( this.lightSphere ); + // This uses the Frenet-Serret formula for deriving binormal + var t2 = path.getTangentAt( epsilon ); - this.update(); + normals[ 0 ] = new THREE.Vector3().subVectors( t2, tangents[ 0 ] ).normalize(); + binormals[ 0 ] = new THREE.Vector3().crossVectors( tangents[ 0 ], normals[ 0 ] ); -}; + normals[ 0 ].crossVectors( binormals[ 0 ], tangents[ 0 ] ).normalize(); // last binormal x tangent + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize(); -THREE.HemisphereLightHelper.prototype = Object.create( THREE.Object3D.prototype ); + } + */ -THREE.HemisphereLightHelper.prototype.dispose = function () { - this.lightSphere.geometry.dispose(); - this.lightSphere.material.dispose(); -}; + function initialNormal3() { + // select an initial normal vector perpenicular to the first tangent vector, + // and in the direction of the smallest tangent xyz component -THREE.HemisphereLightHelper.prototype.update = function () { + normals[ 0 ] = new THREE.Vector3(); + binormals[ 0 ] = new THREE.Vector3(); + smallest = Number.MAX_VALUE; + tx = Math.abs( tangents[ 0 ].x ); + ty = Math.abs( tangents[ 0 ].y ); + tz = Math.abs( tangents[ 0 ].z ); - var vector = new THREE.Vector3(); + if ( tx <= smallest ) { + smallest = tx; + normal.set( 1, 0, 0 ); + } - return function () { + if ( ty <= smallest ) { + smallest = ty; + normal.set( 0, 1, 0 ); + } - this.colors[ 0 ].copy( this.light.color ).multiplyScalar( this.light.intensity ); - this.colors[ 1 ].copy( this.light.groundColor ).multiplyScalar( this.light.intensity ); + if ( tz <= smallest ) { + normal.set( 0, 0, 1 ); + } - this.lightSphere.lookAt( vector.setFromMatrixPosition( this.light.matrixWorld ).negate() ); - this.lightSphere.geometry.colorsNeedUpdate = true; + vec.crossVectors( tangents[ 0 ], normal ).normalize(); + normals[ 0 ].crossVectors( tangents[ 0 ], vec ); + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); } -}(); + // compute the slowly-varying normal and binormal vectors for each segment on the path -// File:src/extras/helpers/PointLightHelper.js + for ( i = 1; i < numpoints; i ++ ) { -/** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - */ + normals[ i ] = normals[ i-1 ].clone(); -THREE.PointLightHelper = function ( light, sphereSize ) { + binormals[ i ] = binormals[ i-1 ].clone(); - this.light = light; - this.light.updateMatrixWorld(); + vec.crossVectors( tangents[ i-1 ], tangents[ i ] ); - var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 ); - var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } ); - material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + if ( vec.length() > epsilon ) { - THREE.Mesh.call( this, geometry, material ); + vec.normalize(); - this.matrixWorld = this.light.matrixWorld; - this.matrixAutoUpdate = false; + theta = Math.acos( THREE.Math.clamp( tangents[ i-1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors - /* - var distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 ); - var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); + normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); - this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); - this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); + } - var d = light.distance; + binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); - if ( d === 0.0 ) { + } - this.lightDistance.visible = false; - } else { + // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same - this.lightDistance.scale.set( d, d, d ); + if ( closed ) { - } + theta = Math.acos( THREE.Math.clamp( normals[ 0 ].dot( normals[ numpoints-1 ] ), - 1, 1 ) ); + theta /= ( numpoints - 1 ); - this.add( this.lightDistance ); - */ + if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ numpoints-1 ] ) ) > 0 ) { -}; + theta = - theta; -THREE.PointLightHelper.prototype = Object.create( THREE.Mesh.prototype ); + } -THREE.PointLightHelper.prototype.dispose = function () { - - this.geometry.dispose(); - this.material.dispose(); + for ( i = 1; i < numpoints; i ++ ) { + + // twist a little... + normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); + binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); + + } + + } }; -THREE.PointLightHelper.prototype.update = function () { +// File:src/extras/geometries/PolyhedronGeometry.js + +/** + * @author clockworkgeek / https://github.com/clockworkgeek + * @author timothypratley / https://github.com/timothypratley + * @author WestLangley / http://github.com/WestLangley +*/ + +THREE.PolyhedronGeometry = function ( vertices, indices, radius, detail ) { + + THREE.Geometry.call( this ); - this.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + this.type = 'PolyhedronGeometry'; - /* - var d = this.light.distance; + this.parameters = { + vertices: vertices, + indices: indices, + radius: radius, + detail: detail + }; - if ( d === 0.0 ) { + radius = radius || 1; + detail = detail || 0; - this.lightDistance.visible = false; + var that = this; - } else { + for ( var i = 0, l = vertices.length; i < l; i += 3 ) { - this.lightDistance.visible = true; - this.lightDistance.scale.set( d, d, d ); + prepare( new THREE.Vector3( vertices[ i ], vertices[ i + 1 ], vertices[ i + 2 ] ) ); } - */ - -}; + var midpoints = [], p = this.vertices; -// File:src/extras/helpers/SkeletonHelper.js + var faces = []; -/** - * @author Sean Griffin / http://twitter.com/sgrif - * @author Michael Guerrero / http://realitymeltdown.com - * @author mrdoob / http://mrdoob.com/ - * @author ikerr / http://verold.com - */ + for ( var i = 0, j = 0, l = indices.length; i < l; i += 3, j ++ ) { -THREE.SkeletonHelper = function ( object ) { + var v1 = p[ indices[ i ] ]; + var v2 = p[ indices[ i + 1 ] ]; + var v3 = p[ indices[ i + 2 ] ]; - this.bones = this.getBoneList( object ); + faces[ j ] = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] ); - var geometry = new THREE.Geometry(); + } - for ( var i = 0; i < this.bones.length; i ++ ) { + var centroid = new THREE.Vector3(); - var bone = this.bones[ i ]; + for ( var i = 0, l = faces.length; i < l; i ++ ) { - if ( bone.parent instanceof THREE.Bone ) { + subdivide( faces[ i ], detail ); - geometry.vertices.push( new THREE.Vector3() ); - geometry.vertices.push( new THREE.Vector3() ); - geometry.colors.push( new THREE.Color( 0, 0, 1 ) ); - geometry.colors.push( new THREE.Color( 0, 1, 0 ) ); + } - } - } + // Handle case when face straddles the seam - var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors, depthTest: false, depthWrite: false, transparent: true } ); + for ( var i = 0, l = this.faceVertexUvs[ 0 ].length; i < l; i ++ ) { - THREE.Line.call( this, geometry, material, THREE.LinePieces ); + var uvs = this.faceVertexUvs[ 0 ][ i ]; - this.root = object; + var x0 = uvs[ 0 ].x; + var x1 = uvs[ 1 ].x; + var x2 = uvs[ 2 ].x; - this.matrixWorld = object.matrixWorld; - this.matrixAutoUpdate = false; + var max = Math.max( x0, Math.max( x1, x2 ) ); + var min = Math.min( x0, Math.min( x1, x2 ) ); - this.update(); + if ( max > 0.9 && min < 0.1 ) { // 0.9 is somewhat arbitrary -}; + if ( x0 < 0.2 ) uvs[ 0 ].x += 1; + if ( x1 < 0.2 ) uvs[ 1 ].x += 1; + if ( x2 < 0.2 ) uvs[ 2 ].x += 1; + } -THREE.SkeletonHelper.prototype = Object.create( THREE.Line.prototype ); + } -THREE.SkeletonHelper.prototype.getBoneList = function( object ) { - var boneList = []; + // Apply radius - if ( object instanceof THREE.Bone ) { + for ( var i = 0, l = this.vertices.length; i < l; i ++ ) { - boneList.push( object ); + this.vertices[ i ].multiplyScalar( radius ); } - for ( var i = 0; i < object.children.length; i ++ ) { - - boneList.push.apply( boneList, this.getBoneList( object.children[ i ] ) ); - } + // Merge vertices - return boneList; + this.mergeVertices(); -}; + this.computeFaceNormals(); -THREE.SkeletonHelper.prototype.update = function () { + this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); - var geometry = this.geometry; - var matrixWorldInv = new THREE.Matrix4().getInverse( this.root.matrixWorld ); + // Project vector onto sphere's surface - var boneMatrix = new THREE.Matrix4(); + function prepare( vector ) { - var j = 0; + var vertex = vector.normalize().clone(); + vertex.index = that.vertices.push( vertex ) - 1; - for ( var i = 0; i < this.bones.length; i ++ ) { + // Texture coords are equivalent to map coords, calculate angle and convert to fraction of a circle. - var bone = this.bones[ i ]; + var u = azimuth( vector ) / 2 / Math.PI + 0.5; + var v = inclination( vector ) / Math.PI + 0.5; + vertex.uv = new THREE.Vector2( u, 1 - v ); - if ( bone.parent instanceof THREE.Bone ) { + return vertex; - boneMatrix.multiplyMatrices( matrixWorldInv, bone.matrixWorld ); - geometry.vertices[ j ].setFromMatrixPosition( boneMatrix ); + } - boneMatrix.multiplyMatrices( matrixWorldInv, bone.parent.matrixWorld ); - geometry.vertices[ j + 1 ].setFromMatrixPosition( boneMatrix ); - j += 2; + // Approximate a curved face with recursively sub-divided triangles. - } + function make( v1, v2, v3 ) { - } + var face = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] ); + that.faces.push( face ); - geometry.verticesNeedUpdate = true; + centroid.copy( v1 ).add( v2 ).add( v3 ).divideScalar( 3 ); - geometry.computeBoundingSphere(); + var azi = azimuth( centroid ); -}; + that.faceVertexUvs[ 0 ].push( [ + correctUV( v1.uv, v1, azi ), + correctUV( v2.uv, v2, azi ), + correctUV( v3.uv, v3, azi ) + ] ); -// File:src/extras/helpers/SpotLightHelper.js + } -/** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley -*/ -THREE.SpotLightHelper = function ( light ) { + // Analytically subdivide a face to the required detail level. - THREE.Object3D.call( this ); + function subdivide( face, detail ) { - this.light = light; - this.light.updateMatrixWorld(); + var cols = Math.pow(2, detail); + var cells = Math.pow(4, detail); + var a = prepare( that.vertices[ face.a ] ); + var b = prepare( that.vertices[ face.b ] ); + var c = prepare( that.vertices[ face.c ] ); + var v = []; - this.matrixWorld = light.matrixWorld; - this.matrixAutoUpdate = false; + // Construct all of the vertices for this subdivision. - var geometry = new THREE.CylinderGeometry( 0, 1, 1, 8, 1, true ); + for ( var i = 0 ; i <= cols; i ++ ) { - geometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, - 0.5, 0 ) ); - geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) ); + v[ i ] = []; - var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } ); - - this.cone = new THREE.Mesh( geometry, material ); - this.add( this.cone ); + var aj = prepare( a.clone().lerp( c, i / cols ) ); + var bj = prepare( b.clone().lerp( c, i / cols ) ); + var rows = cols - i; - this.update(); + for ( var j = 0; j <= rows; j ++) { -}; + if ( j == 0 && i == cols ) { -THREE.SpotLightHelper.prototype = Object.create( THREE.Object3D.prototype ); + v[ i ][ j ] = aj; -THREE.SpotLightHelper.prototype.dispose = function () { - this.cone.geometry.dispose(); - this.cone.material.dispose(); -}; + } else { -THREE.SpotLightHelper.prototype.update = function () { + v[ i ][ j ] = prepare( aj.clone().lerp( bj, j / rows ) ); - var vector = new THREE.Vector3(); - var vector2 = new THREE.Vector3(); + } - return function () { + } - var coneLength = this.light.distance ? this.light.distance : 10000; - var coneWidth = coneLength * Math.tan( this.light.angle ); + } - this.cone.scale.set( coneWidth, coneWidth, coneLength ); + // Construct all of the faces. - vector.setFromMatrixPosition( this.light.matrixWorld ); - vector2.setFromMatrixPosition( this.light.target.matrixWorld ); + for ( var i = 0; i < cols ; i ++ ) { - this.cone.lookAt( vector2.sub( vector ) ); + for ( var j = 0; j < 2 * (cols - i) - 1; j ++ ) { - this.cone.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + var k = Math.floor( j / 2 ); - }; + if ( j % 2 == 0 ) { -}(); + make( + v[ i ][ k + 1], + v[ i + 1 ][ k ], + v[ i ][ k ] + ); -// File:src/extras/helpers/VertexNormalsHelper.js + } else { -/** - * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley -*/ + make( + v[ i ][ k + 1 ], + v[ i + 1][ k + 1], + v[ i + 1 ][ k ] + ); -THREE.VertexNormalsHelper = function ( object, size, hex, linewidth ) { + } - this.object = object; + } - this.size = ( size !== undefined ) ? size : 1; + } - var color = ( hex !== undefined ) ? hex : 0xff0000; + } - var width = ( linewidth !== undefined ) ? linewidth : 1; - var geometry = new THREE.Geometry(); + // Angle around the Y axis, counter-clockwise when looking from above. - var vertices = object.geometry.vertices; + function azimuth( vector ) { - var faces = object.geometry.faces; + return Math.atan2( vector.z, - vector.x ); - for ( var i = 0, l = faces.length; i < l; i ++ ) { + } - var face = faces[ i ]; - for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { + // Angle above the XZ plane. - geometry.vertices.push( new THREE.Vector3(), new THREE.Vector3() ); + function inclination( vector ) { - } + return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) ); } - THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces ); - this.matrixAutoUpdate = false; + // Texture fixing helper. Spheres have some odd behaviours. - this.normalMatrix = new THREE.Matrix3(); + function correctUV( uv, vector, azimuth ) { - this.update(); + if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) uv = new THREE.Vector2( uv.x - 1, uv.y ); + if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) uv = new THREE.Vector2( azimuth / 2 / Math.PI + 0.5, uv.y ); + return uv.clone(); -}; + } -THREE.VertexNormalsHelper.prototype = Object.create( THREE.Line.prototype ); -THREE.VertexNormalsHelper.prototype.update = ( function ( object ) { +}; - var v1 = new THREE.Vector3(); +THREE.PolyhedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); - return function( object ) { +// File:src/extras/geometries/DodecahedronGeometry.js - var keys = [ 'a', 'b', 'c', 'd' ]; +/** + * @author Abe Pazos / https://hamoid.com + */ - this.object.updateMatrixWorld( true ); +THREE.DodecahedronGeometry = function ( radius, detail ) { - this.normalMatrix.getNormalMatrix( this.object.matrixWorld ); + this.parameters = { + radius: radius, + detail: detail + }; - var vertices = this.geometry.vertices; + var t = ( 1 + Math.sqrt( 5 ) ) / 2; + var r = 1 / t; - var verts = this.object.geometry.vertices; + var vertices = [ - var faces = this.object.geometry.faces; + // (±1, ±1, ±1) + -1, -1, -1, -1, -1, 1, + -1, 1, -1, -1, 1, 1, + 1, -1, -1, 1, -1, 1, + 1, 1, -1, 1, 1, 1, - var worldMatrix = this.object.matrixWorld; + // (0, ±1/φ, ±φ) + 0, -r, -t, 0, -r, t, + 0, r, -t, 0, r, t, - var idx = 0; + // (±1/φ, ±φ, 0) + -r, -t, 0, -r, t, 0, + r, -t, 0, r, t, 0, - for ( var i = 0, l = faces.length; i < l; i ++ ) { + // (±φ, 0, ±1/φ) + -t, 0, -r, t, 0, -r, + -t, 0, r, t, 0, r + ]; - var face = faces[ i ]; + var indices = [ + 3, 11, 7, 3, 7, 15, 3, 15, 13, + 7, 19, 17, 7, 17, 6, 7, 6, 15, + 17, 4, 8, 17, 8, 10, 17, 10, 6, + 8, 0, 16, 8, 16, 2, 8, 2, 10, + 0, 12, 1, 0, 1, 18, 0, 18, 16, + 6, 10, 2, 6, 2, 13, 6, 13, 15, + 2, 16, 18, 2, 18, 3, 2, 3, 13, + 18, 1, 9, 18, 9, 11, 18, 11, 3, + 4, 14, 12, 4, 12, 0, 4, 0, 8, + 11, 9, 5, 11, 5, 19, 11, 19, 7, + 19, 5, 14, 19, 14, 4, 19, 4, 17, + 1, 12, 14, 1, 14, 5, 1, 5, 9 + ]; - for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { + THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); - var vertexId = face[ keys[ j ] ]; - var vertex = verts[ vertexId ]; +}; - var normal = face.vertexNormals[ j ]; +THREE.DodecahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); - vertices[ idx ].copy( vertex ).applyMatrix4( worldMatrix ); +// File:src/extras/geometries/IcosahedronGeometry.js - v1.copy( normal ).applyMatrix3( this.normalMatrix ).normalize().multiplyScalar( this.size ); +/** + * @author timothypratley / https://github.com/timothypratley + */ - v1.add( vertices[ idx ] ); - idx = idx + 1; +THREE.IcosahedronGeometry = function ( radius, detail ) { - vertices[ idx ].copy( v1 ); - idx = idx + 1; + var t = ( 1 + Math.sqrt( 5 ) ) / 2; - } + var vertices = [ + - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, 0, + 0, - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, + t, 0, - 1, t, 0, 1, - t, 0, - 1, - t, 0, 1 + ]; - } + var indices = [ + 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, + 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, + 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, + 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1 + ]; - this.geometry.verticesNeedUpdate = true; + THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); - return this; + this.type = 'IcosahedronGeometry'; - } + this.parameters = { + radius: radius, + detail: detail + }; +}; -}()); +THREE.IcosahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); -// File:src/extras/helpers/VertexTangentsHelper.js +// File:src/extras/geometries/OctahedronGeometry.js /** - * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley -*/ - -THREE.VertexTangentsHelper = function ( object, size, hex, linewidth ) { + * @author timothypratley / https://github.com/timothypratley + */ - this.object = object; +THREE.OctahedronGeometry = function ( radius, detail ) { - this.size = ( size !== undefined ) ? size : 1; + this.parameters = { + radius: radius, + detail: detail + }; - var color = ( hex !== undefined ) ? hex : 0x0000ff; + var vertices = [ + 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0,- 1, 0, 0, 0, 1, 0, 0,- 1 + ]; - var width = ( linewidth !== undefined ) ? linewidth : 1; + var indices = [ + 0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2 + ]; - var geometry = new THREE.Geometry(); + THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); - var vertices = object.geometry.vertices; + this.type = 'OctahedronGeometry'; - var faces = object.geometry.faces; + this.parameters = { + radius: radius, + detail: detail + }; +}; - for ( var i = 0, l = faces.length; i < l; i ++ ) { +THREE.OctahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); - var face = faces[ i ]; +// File:src/extras/geometries/TetrahedronGeometry.js - for ( var j = 0, jl = face.vertexTangents.length; j < jl; j ++ ) { +/** + * @author timothypratley / https://github.com/timothypratley + */ - geometry.vertices.push( new THREE.Vector3() ); - geometry.vertices.push( new THREE.Vector3() ); +THREE.TetrahedronGeometry = function ( radius, detail ) { - } + var vertices = [ + 1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1 + ]; - } + var indices = [ + 2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1 + ]; - THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces ); + THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); - this.matrixAutoUpdate = false; + this.type = 'TetrahedronGeometry'; - this.update(); + this.parameters = { + radius: radius, + detail: detail + }; }; -THREE.VertexTangentsHelper.prototype = Object.create( THREE.Line.prototype ); +THREE.TetrahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); -THREE.VertexTangentsHelper.prototype.update = ( function ( object ) { +// File:src/extras/geometries/ParametricGeometry.js - var v1 = new THREE.Vector3(); +/** + * @author zz85 / https://github.com/zz85 + * Parametric Surfaces Geometry + * based on the brilliant article by @prideout http://prideout.net/blog/?p=44 + * + * new THREE.ParametricGeometry( parametricFunction, uSegments, ySegements ); + * + */ - return function( object ) { +THREE.ParametricGeometry = function ( func, slices, stacks ) { - var keys = [ 'a', 'b', 'c', 'd' ]; + THREE.Geometry.call( this ); - this.object.updateMatrixWorld( true ); + this.type = 'ParametricGeometry'; - var vertices = this.geometry.vertices; + this.parameters = { + func: func, + slices: slices, + stacks: stacks + }; - var verts = this.object.geometry.vertices; + var verts = this.vertices; + var faces = this.faces; + var uvs = this.faceVertexUvs[ 0 ]; - var faces = this.object.geometry.faces; + var i, il, j, p; + var u, v; - var worldMatrix = this.object.matrixWorld; + var stackCount = stacks + 1; + var sliceCount = slices + 1; - var idx = 0; + for ( i = 0; i <= stacks; i ++ ) { - for ( var i = 0, l = faces.length; i < l; i ++ ) { + v = i / stacks; - var face = faces[ i ]; + for ( j = 0; j <= slices; j ++ ) { - for ( var j = 0, jl = face.vertexTangents.length; j < jl; j ++ ) { + u = j / slices; - var vertexId = face[ keys[ j ] ]; - var vertex = verts[ vertexId ]; + p = func( u, v ); + verts.push( p ); - var tangent = face.vertexTangents[ j ]; + } + } - vertices[ idx ].copy( vertex ).applyMatrix4( worldMatrix ); + var a, b, c, d; + var uva, uvb, uvc, uvd; - v1.copy( tangent ).transformDirection( worldMatrix ).multiplyScalar( this.size ); + for ( i = 0; i < stacks; i ++ ) { - v1.add( vertices[ idx ] ); - idx = idx + 1; + for ( j = 0; j < slices; j ++ ) { - vertices[ idx ].copy( v1 ); - idx = idx + 1; + a = i * sliceCount + j; + b = i * sliceCount + j + 1; + c = (i + 1) * sliceCount + j + 1; + d = (i + 1) * sliceCount + j; - } + uva = new THREE.Vector2( j / slices, i / stacks ); + uvb = new THREE.Vector2( ( j + 1 ) / slices, i / stacks ); + uvc = new THREE.Vector2( ( j + 1 ) / slices, ( i + 1 ) / stacks ); + uvd = new THREE.Vector2( j / slices, ( i + 1 ) / stacks ); - } + faces.push( new THREE.Face3( a, b, d ) ); + uvs.push( [ uva, uvb, uvd ] ); - this.geometry.verticesNeedUpdate = true; + faces.push( new THREE.Face3( b, c, d ) ); + uvs.push( [ uvb.clone(), uvc, uvd.clone() ] ); - return this; + } } -}()); + // console.log(this); -// File:src/extras/helpers/WireframeHelper.js + // magic bullet + // var diff = this.mergeVertices(); + // console.log('removed ', diff, ' vertices by merging'); + + this.computeFaceNormals(); + this.computeVertexNormals(); + +}; + +THREE.ParametricGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +// File:src/extras/helpers/AxisHelper.js /** + * @author sroucheray / http://sroucheray.org/ * @author mrdoob / http://mrdoob.com/ */ -THREE.WireframeHelper = function ( object, hex ) { +THREE.AxisHelper = function ( size ) { - var color = ( hex !== undefined ) ? hex : 0xffffff; + size = size || 1; - var edge = [ 0, 0 ], hash = {}; - var sortFunction = function ( a, b ) { return a - b }; + var vertices = new Float32Array( [ + 0, 0, 0, size, 0, 0, + 0, 0, 0, 0, size, 0, + 0, 0, 0, 0, 0, size + ] ); + + var colors = new Float32Array( [ + 1, 0, 0, 1, 0.6, 0, + 0, 1, 0, 0.6, 1, 0, + 0, 0, 1, 0, 0.6, 1 + ] ); - var keys = [ 'a', 'b', 'c' ]; var geometry = new THREE.BufferGeometry(); + geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); + geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) ); - if ( object.geometry instanceof THREE.Geometry ) { + var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } ); - var vertices = object.geometry.vertices; - var faces = object.geometry.faces; - var numEdges = 0; + THREE.Line.call( this, geometry, material, THREE.LinePieces ); - // allocate maximal size - var edges = new Uint32Array( 6 * faces.length ); +}; - for ( var i = 0, l = faces.length; i < l; i ++ ) { +THREE.AxisHelper.prototype = Object.create( THREE.Line.prototype ); - var face = faces[ i ]; +// File:src/extras/helpers/ArrowHelper.js + +/** + * @author WestLangley / http://github.com/WestLangley + * @author zz85 / http://github.com/zz85 + * @author bhouston / http://exocortex.com + * + * Creates an arrow for visualizing directions + * + * Parameters: + * dir - Vector3 + * origin - Vector3 + * length - Number + * color - color in hex value + * headLength - Number + * headWidth - Number + */ - for ( var j = 0; j < 3; j ++ ) { +THREE.ArrowHelper = ( function () { - edge[ 0 ] = face[ keys[ j ] ]; - edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; - edge.sort( sortFunction ); + var lineGeometry = new THREE.Geometry(); + lineGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 1, 0 ) ); - var key = edge.toString(); + var coneGeometry = new THREE.CylinderGeometry( 0, 0.5, 1, 5, 1 ); + coneGeometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, - 0.5, 0 ) ); - if ( hash[ key ] === undefined ) { + return function ( dir, origin, length, color, headLength, headWidth ) { - edges[ 2 * numEdges ] = edge[ 0 ]; - edges[ 2 * numEdges + 1 ] = edge[ 1 ]; - hash[ key ] = true; - numEdges ++; + // dir is assumed to be normalized - } + THREE.Object3D.call( this ); - } + if ( color === undefined ) color = 0xffff00; + if ( length === undefined ) length = 1; + if ( headLength === undefined ) headLength = 0.2 * length; + if ( headWidth === undefined ) headWidth = 0.2 * headLength; - } + this.position.copy( origin ); - var coords = new Float32Array( numEdges * 2 * 3 ); + this.line = new THREE.Line( lineGeometry, new THREE.LineBasicMaterial( { color: color } ) ); + this.line.matrixAutoUpdate = false; + this.add( this.line ); - for ( var i = 0, l = numEdges; i < l; i ++ ) { + this.cone = new THREE.Mesh( coneGeometry, new THREE.MeshBasicMaterial( { color: color } ) ); + this.cone.matrixAutoUpdate = false; + this.add( this.cone ); - for ( var j = 0; j < 2; j ++ ) { + this.setDirection( dir ); + this.setLength( length, headLength, headWidth ); - var vertex = vertices[ edges [ 2 * i + j] ]; + } - var index = 6 * i + 3 * j; - coords[ index + 0 ] = vertex.x; - coords[ index + 1 ] = vertex.y; - coords[ index + 2 ] = vertex.z; +}() ); - } +THREE.ArrowHelper.prototype = Object.create( THREE.Object3D.prototype ); - } +THREE.ArrowHelper.prototype.setDirection = ( function () { - geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); + var axis = new THREE.Vector3(); + var radians; - } else if ( object.geometry instanceof THREE.BufferGeometry ) { + return function ( dir ) { - if ( object.geometry.attributes.index !== undefined ) { // Indexed BufferGeometry + // dir is assumed to be normalized - var vertices = object.geometry.attributes.position.array; - var indices = object.geometry.attributes.index.array; - var offsets = object.geometry.offsets; - var numEdges = 0; + if ( dir.y > 0.99999 ) { - // allocate maximal size - var edges = new Uint32Array( 2 * indices.length ); + this.quaternion.set( 0, 0, 0, 1 ); - for ( var o = 0, ol = offsets.length; o < ol; ++ o ) { + } else if ( dir.y < - 0.99999 ) { - var start = offsets[ o ].start; - var count = offsets[ o ].count; - var index = offsets[ o ].index; + this.quaternion.set( 1, 0, 0, 0 ); - for ( var i = start, il = start + count; i < il; i += 3 ) { + } else { - for ( var j = 0; j < 3; j ++ ) { + axis.set( dir.z, 0, - dir.x ).normalize(); - edge[ 0 ] = index + indices[ i + j ]; - edge[ 1 ] = index + indices[ i + ( j + 1 ) % 3 ]; - edge.sort( sortFunction ); + radians = Math.acos( dir.y ); - var key = edge.toString(); + this.quaternion.setFromAxisAngle( axis, radians ); - if ( hash[ key ] === undefined ) { + } - edges[ 2 * numEdges ] = edge[ 0 ]; - edges[ 2 * numEdges + 1 ] = edge[ 1 ]; - hash[ key ] = true; - numEdges ++; + }; - } +}() ); - } +THREE.ArrowHelper.prototype.setLength = function ( length, headLength, headWidth ) { - } + if ( headLength === undefined ) headLength = 0.2 * length; + if ( headWidth === undefined ) headWidth = 0.2 * headLength; - } + this.line.scale.set( 1, length, 1 ); + this.line.updateMatrix(); - var coords = new Float32Array( numEdges * 2 * 3 ); + this.cone.scale.set( headWidth, headLength, headWidth ); + this.cone.position.y = length; + this.cone.updateMatrix(); - for ( var i = 0, l = numEdges; i < l; i ++ ) { +}; - for ( var j = 0; j < 2; j ++ ) { +THREE.ArrowHelper.prototype.setColor = function ( color ) { - var index = 6 * i + 3 * j; - var index2 = 3 * edges[ 2 * i + j]; - coords[ index + 0 ] = vertices[ index2 ]; - coords[ index + 1 ] = vertices[ index2 + 1 ]; - coords[ index + 2 ] = vertices[ index2 + 2 ]; + this.line.material.color.set( color ); + this.cone.material.color.set( color ); - } +}; - } +// File:src/extras/helpers/BoxHelper.js - geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); +/** + * @author mrdoob / http://mrdoob.com/ + */ - } else { // non-indexed BufferGeometry +THREE.BoxHelper = function ( object ) { - var vertices = object.geometry.attributes.position.array; - var numEdges = vertices.length / 3; - var numTris = numEdges / 3; + var geometry = new THREE.BufferGeometry(); + geometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( 72 ), 3 ) ); - var coords = new Float32Array( numEdges * 2 * 3 ); + THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: 0xffff00 } ), THREE.LinePieces ); - for ( var i = 0, l = numTris; i < l; i ++ ) { + if ( object !== undefined ) { - for ( var j = 0; j < 3; j ++ ) { + this.update( object ); - var index = 18 * i + 6 * j; + } - var index1 = 9 * i + 3 * j; - coords[ index + 0 ] = vertices[ index1 ]; - coords[ index + 1 ] = vertices[ index1 + 1 ]; - coords[ index + 2 ] = vertices[ index1 + 2 ]; +}; - var index2 = 9 * i + 3 * ( ( j + 1 ) % 3 ); - coords[ index + 3 ] = vertices[ index2 ]; - coords[ index + 4 ] = vertices[ index2 + 1 ]; - coords[ index + 5 ] = vertices[ index2 + 2 ]; +THREE.BoxHelper.prototype = Object.create( THREE.Line.prototype ); - } +THREE.BoxHelper.prototype.update = function ( object ) { - } + var geometry = object.geometry; - geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); + if ( geometry.boundingBox === null ) { - } + geometry.computeBoundingBox(); } - THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color } ), THREE.LinePieces ); + var min = geometry.boundingBox.min; + var max = geometry.boundingBox.max; - this.matrixAutoUpdate = false; - this.matrixWorld = object.matrixWorld; + /* + 5____4 + 1/___0/| + | 6__|_7 + 2/___3/ -}; + 0: max.x, max.y, max.z + 1: min.x, max.y, max.z + 2: min.x, min.y, max.z + 3: max.x, min.y, max.z + 4: max.x, max.y, min.z + 5: min.x, max.y, min.z + 6: min.x, min.y, min.z + 7: max.x, min.y, min.z + */ -THREE.WireframeHelper.prototype = Object.create( THREE.Line.prototype ); + var vertices = this.geometry.attributes.position.array; -// File:src/extras/objects/ImmediateRenderObject.js + vertices[ 0 ] = max.x; vertices[ 1 ] = max.y; vertices[ 2 ] = max.z; + vertices[ 3 ] = min.x; vertices[ 4 ] = max.y; vertices[ 5 ] = max.z; -/** - * @author alteredq / http://alteredqualia.com/ - */ + vertices[ 6 ] = min.x; vertices[ 7 ] = max.y; vertices[ 8 ] = max.z; + vertices[ 9 ] = min.x; vertices[ 10 ] = min.y; vertices[ 11 ] = max.z; -THREE.ImmediateRenderObject = function () { + vertices[ 12 ] = min.x; vertices[ 13 ] = min.y; vertices[ 14 ] = max.z; + vertices[ 15 ] = max.x; vertices[ 16 ] = min.y; vertices[ 17 ] = max.z; - THREE.Object3D.call( this ); + vertices[ 18 ] = max.x; vertices[ 19 ] = min.y; vertices[ 20 ] = max.z; + vertices[ 21 ] = max.x; vertices[ 22 ] = max.y; vertices[ 23 ] = max.z; - this.render = function ( renderCallback ) {}; + // -}; + vertices[ 24 ] = max.x; vertices[ 25 ] = max.y; vertices[ 26 ] = min.z; + vertices[ 27 ] = min.x; vertices[ 28 ] = max.y; vertices[ 29 ] = min.z; -THREE.ImmediateRenderObject.prototype = Object.create( THREE.Object3D.prototype ); + vertices[ 30 ] = min.x; vertices[ 31 ] = max.y; vertices[ 32 ] = min.z; + vertices[ 33 ] = min.x; vertices[ 34 ] = min.y; vertices[ 35 ] = min.z; -// File:src/extras/objects/LensFlare.js + vertices[ 36 ] = min.x; vertices[ 37 ] = min.y; vertices[ 38 ] = min.z; + vertices[ 39 ] = max.x; vertices[ 40 ] = min.y; vertices[ 41 ] = min.z; -/** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - */ + vertices[ 42 ] = max.x; vertices[ 43 ] = min.y; vertices[ 44 ] = min.z; + vertices[ 45 ] = max.x; vertices[ 46 ] = max.y; vertices[ 47 ] = min.z; -THREE.LensFlare = function ( texture, size, distance, blending, color ) { + // - THREE.Object3D.call( this ); + vertices[ 48 ] = max.x; vertices[ 49 ] = max.y; vertices[ 50 ] = max.z; + vertices[ 51 ] = max.x; vertices[ 52 ] = max.y; vertices[ 53 ] = min.z; - this.lensFlares = []; + vertices[ 54 ] = min.x; vertices[ 55 ] = max.y; vertices[ 56 ] = max.z; + vertices[ 57 ] = min.x; vertices[ 58 ] = max.y; vertices[ 59 ] = min.z; - this.positionScreen = new THREE.Vector3(); - this.customUpdateCallback = undefined; + vertices[ 60 ] = min.x; vertices[ 61 ] = min.y; vertices[ 62 ] = max.z; + vertices[ 63 ] = min.x; vertices[ 64 ] = min.y; vertices[ 65 ] = min.z; - if( texture !== undefined ) { + vertices[ 66 ] = max.x; vertices[ 67 ] = min.y; vertices[ 68 ] = max.z; + vertices[ 69 ] = max.x; vertices[ 70 ] = min.y; vertices[ 71 ] = min.z; - this.add( texture, size, distance, blending, color ); + this.geometry.attributes.position.needsUpdate = true; - } + this.geometry.computeBoundingSphere(); -}; + this.matrix = object.matrixWorld; + this.matrixAutoUpdate = false; -THREE.LensFlare.prototype = Object.create( THREE.Object3D.prototype ); +}; +// File:src/extras/helpers/BoundingBoxHelper.js -/* - * Add: adds another flare +/** + * @author WestLangley / http://github.com/WestLangley */ -THREE.LensFlare.prototype.add = function ( texture, size, distance, blending, color, opacity ) { - - if( size === undefined ) size = - 1; - if( distance === undefined ) distance = 0; - if( opacity === undefined ) opacity = 1; - if( color === undefined ) color = new THREE.Color( 0xffffff ); - if( blending === undefined ) blending = THREE.NormalBlending; - - distance = Math.min( distance, Math.max( 0, distance ) ); +// a helper to show the world-axis-aligned bounding box for an object - this.lensFlares.push( { texture: texture, // THREE.Texture - size: size, // size in pixels (-1 = use texture.width) - distance: distance, // distance (0-1) from light source (0=at light source) - x: 0, y: 0, z: 0, // screen position (-1 => 1) z = 0 is ontop z = 1 is back - scale: 1, // scale - rotation: 1, // rotation - opacity: opacity, // opacity - color: color, // color - blending: blending } ); // blending +THREE.BoundingBoxHelper = function ( object, hex ) { -}; + var color = ( hex !== undefined ) ? hex : 0x888888; + this.object = object; -/* - * Update lens flares update positions on all flares based on the screen position - * Set myLensFlare.customUpdateCallback to alter the flares in your project specific way. - */ + this.box = new THREE.Box3(); -THREE.LensFlare.prototype.updateLensFlares = function () { + THREE.Mesh.call( this, new THREE.BoxGeometry( 1, 1, 1 ), new THREE.MeshBasicMaterial( { color: color, wireframe: true } ) ); - var f, fl = this.lensFlares.length; - var flare; - var vecX = - this.positionScreen.x * 2; - var vecY = - this.positionScreen.y * 2; +}; - for( f = 0; f < fl; f ++ ) { +THREE.BoundingBoxHelper.prototype = Object.create( THREE.Mesh.prototype ); - flare = this.lensFlares[ f ]; +THREE.BoundingBoxHelper.prototype.update = function () { - flare.x = this.positionScreen.x + vecX * flare.distance; - flare.y = this.positionScreen.y + vecY * flare.distance; + this.box.setFromObject( this.object ); - flare.wantedRotation = flare.x * Math.PI * 0.25; - flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25; + this.box.size( this.scale ); - } + this.box.center( this.position ); }; +// File:src/extras/helpers/CameraHelper.js +/** + * @author alteredq / http://alteredqualia.com/ + * + * - shows frustum, line of sight and up of the camera + * - suitable for fast updates + * - based on frustum visualization in lightgl.js shadowmap example + * http://evanw.github.com/lightgl.js/tests/shadowmap.html + */ +THREE.CameraHelper = function ( camera ) { + var geometry = new THREE.Geometry(); + var material = new THREE.LineBasicMaterial( { color: 0xffffff, vertexColors: THREE.FaceColors } ); + var pointMap = {}; + // colors + var hexFrustum = 0xffaa00; + var hexCone = 0xff0000; + var hexUp = 0x00aaff; + var hexTarget = 0xffffff; + var hexCross = 0x333333; + // near + addLine( "n1", "n2", hexFrustum ); + addLine( "n2", "n4", hexFrustum ); + addLine( "n4", "n3", hexFrustum ); + addLine( "n3", "n1", hexFrustum ); + // far + addLine( "f1", "f2", hexFrustum ); + addLine( "f2", "f4", hexFrustum ); + addLine( "f4", "f3", hexFrustum ); + addLine( "f3", "f1", hexFrustum ); + // sides -// File:src/extras/objects/MorphBlendMesh.js - -/** - * @author alteredq / http://alteredqualia.com/ - */ - -THREE.MorphBlendMesh = function( geometry, material ) { - - THREE.Mesh.call( this, geometry, material ); - - this.animationsMap = {}; - this.animationsList = []; - - // prepare default animation - // (all frames played together in 1 second) - - var numFrames = this.geometry.morphTargets.length; - - var name = "__default"; - - var startFrame = 0; - var endFrame = numFrames - 1; - - var fps = numFrames / 1; - - this.createAnimation( name, startFrame, endFrame, fps ); - this.setAnimationWeight( name, 1 ); - -}; - -THREE.MorphBlendMesh.prototype = Object.create( THREE.Mesh.prototype ); - -THREE.MorphBlendMesh.prototype.createAnimation = function ( name, start, end, fps ) { - - var animation = { - - startFrame: start, - endFrame: end, - - length: end - start + 1, - - fps: fps, - duration: ( end - start ) / fps, - - lastFrame: 0, - currentFrame: 0, - - active: false, - - time: 0, - direction: 1, - weight: 1, - - directionBackwards: false, - mirroredLoop: false - - }; - - this.animationsMap[ name ] = animation; - this.animationsList.push( animation ); - -}; - -THREE.MorphBlendMesh.prototype.autoCreateAnimations = function ( fps ) { - - var pattern = /([a-z]+)_?(\d+)/; - - var firstAnimation, frameRanges = {}; - - var geometry = this.geometry; - - for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) { - - var morph = geometry.morphTargets[ i ]; - var chunks = morph.name.match( pattern ); - - if ( chunks && chunks.length > 1 ) { - - var name = chunks[ 1 ]; - var num = chunks[ 2 ]; - - if ( ! frameRanges[ name ] ) frameRanges[ name ] = { start: Infinity, end: - Infinity }; - - var range = frameRanges[ name ]; - - if ( i < range.start ) range.start = i; - if ( i > range.end ) range.end = i; - - if ( ! firstAnimation ) firstAnimation = name; - - } - - } - - for ( var name in frameRanges ) { - - var range = frameRanges[ name ]; - this.createAnimation( name, range.start, range.end, fps ); - - } - - this.firstAnimation = firstAnimation; - -}; - -THREE.MorphBlendMesh.prototype.setAnimationDirectionForward = function ( name ) { - - var animation = this.animationsMap[ name ]; - - if ( animation ) { - - animation.direction = 1; - animation.directionBackwards = false; - - } - -}; - -THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward = function ( name ) { - - var animation = this.animationsMap[ name ]; - - if ( animation ) { - - animation.direction = - 1; - animation.directionBackwards = true; - - } - -}; - -THREE.MorphBlendMesh.prototype.setAnimationFPS = function ( name, fps ) { - - var animation = this.animationsMap[ name ]; - - if ( animation ) { - - animation.fps = fps; - animation.duration = ( animation.end - animation.start ) / animation.fps; - - } - -}; - -THREE.MorphBlendMesh.prototype.setAnimationDuration = function ( name, duration ) { - - var animation = this.animationsMap[ name ]; - - if ( animation ) { - - animation.duration = duration; - animation.fps = ( animation.end - animation.start ) / animation.duration; - - } - -}; - -THREE.MorphBlendMesh.prototype.setAnimationWeight = function ( name, weight ) { - - var animation = this.animationsMap[ name ]; - - if ( animation ) { - - animation.weight = weight; - - } - -}; - -THREE.MorphBlendMesh.prototype.setAnimationTime = function ( name, time ) { - - var animation = this.animationsMap[ name ]; - - if ( animation ) { - - animation.time = time; - - } - -}; - -THREE.MorphBlendMesh.prototype.getAnimationTime = function ( name ) { - - var time = 0; - - var animation = this.animationsMap[ name ]; - - if ( animation ) { - - time = animation.time; - - } - - return time; - -}; - -THREE.MorphBlendMesh.prototype.getAnimationDuration = function ( name ) { - - var duration = - 1; - - var animation = this.animationsMap[ name ]; - - if ( animation ) { - - duration = animation.duration; - - } - - return duration; - -}; - -THREE.MorphBlendMesh.prototype.playAnimation = function ( name ) { - - var animation = this.animationsMap[ name ]; - - if ( animation ) { - - animation.time = 0; - animation.active = true; - - } else { - - console.warn( "animation[" + name + "] undefined" ); - - } - -}; - -THREE.MorphBlendMesh.prototype.stopAnimation = function ( name ) { - - var animation = this.animationsMap[ name ]; - - if ( animation ) { - - animation.active = false; - - } - -}; - -THREE.MorphBlendMesh.prototype.update = function ( delta ) { - - for ( var i = 0, il = this.animationsList.length; i < il; i ++ ) { - - var animation = this.animationsList[ i ]; - - if ( ! animation.active ) continue; - - var frameTime = animation.duration / animation.length; - - animation.time += animation.direction * delta; - - if ( animation.mirroredLoop ) { - - if ( animation.time > animation.duration || animation.time < 0 ) { - - animation.direction *= - 1; - - if ( animation.time > animation.duration ) { - - animation.time = animation.duration; - animation.directionBackwards = true; - - } - - if ( animation.time < 0 ) { - - animation.time = 0; - animation.directionBackwards = false; - - } - - } - - } else { - - animation.time = animation.time % animation.duration; - - if ( animation.time < 0 ) animation.time += animation.duration; - - } - - var keyframe = animation.startFrame + THREE.Math.clamp( Math.floor( animation.time / frameTime ), 0, animation.length - 1 ); - var weight = animation.weight; - - if ( keyframe !== animation.currentFrame ) { - - this.morphTargetInfluences[ animation.lastFrame ] = 0; - this.morphTargetInfluences[ animation.currentFrame ] = 1 * weight; - - this.morphTargetInfluences[ keyframe ] = 0; - - animation.lastFrame = animation.currentFrame; - animation.currentFrame = keyframe; - - } - - var mix = ( animation.time % frameTime ) / frameTime; - - if ( animation.directionBackwards ) mix = 1 - mix; - - this.morphTargetInfluences[ animation.currentFrame ] = mix * weight; - this.morphTargetInfluences[ animation.lastFrame ] = ( 1 - mix ) * weight; - - } - -}; - -// File:src/extras/renderers/plugins/LensFlarePlugin.js + addLine( "n1", "f1", hexFrustum ); + addLine( "n2", "f2", hexFrustum ); + addLine( "n3", "f3", hexFrustum ); + addLine( "n4", "f4", hexFrustum ); -/** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - */ + // cone -THREE.LensFlarePlugin = function () { + addLine( "p", "n1", hexCone ); + addLine( "p", "n2", hexCone ); + addLine( "p", "n3", hexCone ); + addLine( "p", "n4", hexCone ); - var flares = []; + // up - var _gl, _renderer, _precision, _lensFlare = {}; + addLine( "u1", "u2", hexUp ); + addLine( "u2", "u3", hexUp ); + addLine( "u3", "u1", hexUp ); - this.init = function ( renderer ) { + // target - _gl = renderer.context; - _renderer = renderer; + addLine( "c", "t", hexTarget ); + addLine( "p", "c", hexCross ); - _precision = renderer.getPrecision(); + // cross - _lensFlare.vertices = new Float32Array( 8 + 8 ); - _lensFlare.faces = new Uint16Array( 6 ); + addLine( "cn1", "cn2", hexCross ); + addLine( "cn3", "cn4", hexCross ); - var i = 0; - _lensFlare.vertices[ i ++ ] = - 1; _lensFlare.vertices[ i ++ ] = - 1; // vertex - _lensFlare.vertices[ i ++ ] = 0; _lensFlare.vertices[ i ++ ] = 0; // uv... etc. + addLine( "cf1", "cf2", hexCross ); + addLine( "cf3", "cf4", hexCross ); - _lensFlare.vertices[ i ++ ] = 1; _lensFlare.vertices[ i ++ ] = - 1; - _lensFlare.vertices[ i ++ ] = 1; _lensFlare.vertices[ i ++ ] = 0; + function addLine( a, b, hex ) { - _lensFlare.vertices[ i ++ ] = 1; _lensFlare.vertices[ i ++ ] = 1; - _lensFlare.vertices[ i ++ ] = 1; _lensFlare.vertices[ i ++ ] = 1; + addPoint( a, hex ); + addPoint( b, hex ); - _lensFlare.vertices[ i ++ ] = - 1; _lensFlare.vertices[ i ++ ] = 1; - _lensFlare.vertices[ i ++ ] = 0; _lensFlare.vertices[ i ++ ] = 1; + } - i = 0; - _lensFlare.faces[ i ++ ] = 0; _lensFlare.faces[ i ++ ] = 1; _lensFlare.faces[ i ++ ] = 2; - _lensFlare.faces[ i ++ ] = 0; _lensFlare.faces[ i ++ ] = 2; _lensFlare.faces[ i ++ ] = 3; + function addPoint( id, hex ) { - // buffers + geometry.vertices.push( new THREE.Vector3() ); + geometry.colors.push( new THREE.Color( hex ) ); - _lensFlare.vertexBuffer = _gl.createBuffer(); - _lensFlare.elementBuffer = _gl.createBuffer(); + if ( pointMap[ id ] === undefined ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, _lensFlare.vertices, _gl.STATIC_DRAW ); + pointMap[ id ] = []; - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.elementBuffer ); - _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.faces, _gl.STATIC_DRAW ); + } - // textures + pointMap[ id ].push( geometry.vertices.length - 1 ); - _lensFlare.tempTexture = _gl.createTexture(); - _lensFlare.occlusionTexture = _gl.createTexture(); + } - _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture ); - _gl.texImage2D( _gl.TEXTURE_2D, 0, _gl.RGB, 16, 16, 0, _gl.RGB, _gl.UNSIGNED_BYTE, null ); - _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); - _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); - _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, _gl.NEAREST ); - _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, _gl.NEAREST ); + THREE.Line.call( this, geometry, material, THREE.LinePieces ); - _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.occlusionTexture ); - _gl.texImage2D( _gl.TEXTURE_2D, 0, _gl.RGBA, 16, 16, 0, _gl.RGBA, _gl.UNSIGNED_BYTE, null ); - _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); - _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); - _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, _gl.NEAREST ); - _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, _gl.NEAREST ); + this.camera = camera; + this.matrix = camera.matrixWorld; + this.matrixAutoUpdate = false; - if ( _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) <= 0 ) { + this.pointMap = pointMap; - _lensFlare.hasVertexTexture = false; - _lensFlare.program = createProgram( THREE.ShaderFlares[ "lensFlare" ], _precision ); + this.update(); - } else { +}; - _lensFlare.hasVertexTexture = true; - _lensFlare.program = createProgram( THREE.ShaderFlares[ "lensFlareVertexTexture" ], _precision ); +THREE.CameraHelper.prototype = Object.create( THREE.Line.prototype ); - } +THREE.CameraHelper.prototype.update = function () { - _lensFlare.attributes = {}; - _lensFlare.uniforms = {}; + var geometry, pointMap; + + var vector = new THREE.Vector3(); + var camera = new THREE.Camera(); - _lensFlare.attributes.vertex = _gl.getAttribLocation ( _lensFlare.program, "position" ); - _lensFlare.attributes.uv = _gl.getAttribLocation ( _lensFlare.program, "uv" ); + var setPoint = function ( point, x, y, z ) { - _lensFlare.uniforms.renderType = _gl.getUniformLocation( _lensFlare.program, "renderType" ); - _lensFlare.uniforms.map = _gl.getUniformLocation( _lensFlare.program, "map" ); - _lensFlare.uniforms.occlusionMap = _gl.getUniformLocation( _lensFlare.program, "occlusionMap" ); - _lensFlare.uniforms.opacity = _gl.getUniformLocation( _lensFlare.program, "opacity" ); - _lensFlare.uniforms.color = _gl.getUniformLocation( _lensFlare.program, "color" ); - _lensFlare.uniforms.scale = _gl.getUniformLocation( _lensFlare.program, "scale" ); - _lensFlare.uniforms.rotation = _gl.getUniformLocation( _lensFlare.program, "rotation" ); - _lensFlare.uniforms.screenPosition = _gl.getUniformLocation( _lensFlare.program, "screenPosition" ); + vector.set( x, y, z ).unproject( camera ); - }; + var points = pointMap[ point ]; + if ( points !== undefined ) { - /* - * Render lens flares - * Method: renders 16x16 0xff00ff-colored points scattered over the light source area, - * reads these back and calculates occlusion. - * Then _lensFlare.update_lensFlares() is called to re-position and - * update transparency of flares. Then they are rendered. - * - */ + for ( var i = 0, il = points.length; i < il; i ++ ) { - this.render = function ( scene, camera, viewportWidth, viewportHeight ) { + geometry.vertices[ points[ i ] ].copy( vector ); - flares.length = 0; + } - scene.traverseVisible( function ( child ) { + } - if ( child instanceof THREE.LensFlare ) { + }; - flares.push( child ); + return function () { - } + geometry = this.geometry; + pointMap = this.pointMap; - } ); + var w = 1, h = 1; - if ( flares.length === 0 ) return; + // we need just camera projection matrix + // world matrix must be identity - var tempPosition = new THREE.Vector3(); + camera.projectionMatrix.copy( this.camera.projectionMatrix ); - var invAspect = viewportHeight / viewportWidth, - halfViewportWidth = viewportWidth * 0.5, - halfViewportHeight = viewportHeight * 0.5; + // center / target - var size = 16 / viewportHeight, - scale = new THREE.Vector2( size * invAspect, size ); + setPoint( "c", 0, 0, - 1 ); + setPoint( "t", 0, 0, 1 ); - var screenPosition = new THREE.Vector3( 1, 1, 0 ), - screenPositionPixels = new THREE.Vector2( 1, 1 ); + // near - var uniforms = _lensFlare.uniforms, - attributes = _lensFlare.attributes; + setPoint( "n1", - w, - h, - 1 ); + setPoint( "n2", w, - h, - 1 ); + setPoint( "n3", - w, h, - 1 ); + setPoint( "n4", w, h, - 1 ); - // set _lensFlare program and reset blending + // far - _gl.useProgram( _lensFlare.program ); + setPoint( "f1", - w, - h, 1 ); + setPoint( "f2", w, - h, 1 ); + setPoint( "f3", - w, h, 1 ); + setPoint( "f4", w, h, 1 ); - _gl.enableVertexAttribArray( _lensFlare.attributes.vertex ); - _gl.enableVertexAttribArray( _lensFlare.attributes.uv ); + // up - // loop through all lens flares to update their occlusion and positions - // setup gl and common used attribs/unforms + setPoint( "u1", w * 0.7, h * 1.1, - 1 ); + setPoint( "u2", - w * 0.7, h * 1.1, - 1 ); + setPoint( "u3", 0, h * 2, - 1 ); - _gl.uniform1i( uniforms.occlusionMap, 0 ); - _gl.uniform1i( uniforms.map, 1 ); + // cross - _gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer ); - _gl.vertexAttribPointer( attributes.vertex, 2, _gl.FLOAT, false, 2 * 8, 0 ); - _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 2 * 8, 8 ); + setPoint( "cf1", - w, 0, 1 ); + setPoint( "cf2", w, 0, 1 ); + setPoint( "cf3", 0, - h, 1 ); + setPoint( "cf4", 0, h, 1 ); - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.elementBuffer ); + setPoint( "cn1", - w, 0, - 1 ); + setPoint( "cn2", w, 0, - 1 ); + setPoint( "cn3", 0, - h, - 1 ); + setPoint( "cn4", 0, h, - 1 ); - _gl.disable( _gl.CULL_FACE ); - _gl.depthMask( false ); + geometry.verticesNeedUpdate = true; - for ( var i = 0, l = flares.length; i < l; i ++ ) { + }; - size = 16 / viewportHeight; - scale.set( size * invAspect, size ); +}(); - // calc object screen position +// File:src/extras/helpers/DirectionalLightHelper.js - var flare = flares[ i ]; - - tempPosition.set( flare.matrixWorld.elements[12], flare.matrixWorld.elements[13], flare.matrixWorld.elements[14] ); +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley + */ - tempPosition.applyMatrix4( camera.matrixWorldInverse ); - tempPosition.applyProjection( camera.projectionMatrix ); +THREE.DirectionalLightHelper = function ( light, size ) { - // setup arrays for gl programs + THREE.Object3D.call( this ); - screenPosition.copy( tempPosition ) + this.light = light; + this.light.updateMatrixWorld(); - screenPositionPixels.x = screenPosition.x * halfViewportWidth + halfViewportWidth; - screenPositionPixels.y = screenPosition.y * halfViewportHeight + halfViewportHeight; + this.matrix = light.matrixWorld; + this.matrixAutoUpdate = false; - // screen cull + size = size || 1; - if ( _lensFlare.hasVertexTexture || ( - screenPositionPixels.x > 0 && - screenPositionPixels.x < viewportWidth && - screenPositionPixels.y > 0 && - screenPositionPixels.y < viewportHeight ) ) { + var geometry = new THREE.Geometry(); + geometry.vertices.push( + new THREE.Vector3( - size, size, 0 ), + new THREE.Vector3( size, size, 0 ), + new THREE.Vector3( size, - size, 0 ), + new THREE.Vector3( - size, - size, 0 ), + new THREE.Vector3( - size, size, 0 ) + ); - // save current RGB to temp texture + var material = new THREE.LineBasicMaterial( { fog: false } ); + material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); - _gl.activeTexture( _gl.TEXTURE1 ); - _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture ); - _gl.copyTexImage2D( _gl.TEXTURE_2D, 0, _gl.RGB, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 ); + this.lightPlane = new THREE.Line( geometry, material ); + this.add( this.lightPlane ); + geometry = new THREE.Geometry(); + geometry.vertices.push( + new THREE.Vector3(), + new THREE.Vector3() + ); - // render pink quad + material = new THREE.LineBasicMaterial( { fog: false } ); + material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); - _gl.uniform1i( uniforms.renderType, 0 ); - _gl.uniform2f( uniforms.scale, scale.x, scale.y ); - _gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); + this.targetLine = new THREE.Line( geometry, material ); + this.add( this.targetLine ); - _gl.disable( _gl.BLEND ); - _gl.enable( _gl.DEPTH_TEST ); + this.update(); - _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); +}; +THREE.DirectionalLightHelper.prototype = Object.create( THREE.Object3D.prototype ); - // copy result to occlusionMap +THREE.DirectionalLightHelper.prototype.dispose = function () { - _gl.activeTexture( _gl.TEXTURE0 ); - _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.occlusionTexture ); - _gl.copyTexImage2D( _gl.TEXTURE_2D, 0, _gl.RGBA, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 ); + this.lightPlane.geometry.dispose(); + this.lightPlane.material.dispose(); + this.targetLine.geometry.dispose(); + this.targetLine.material.dispose(); +}; +THREE.DirectionalLightHelper.prototype.update = function () { - // restore graphics + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + var v3 = new THREE.Vector3(); - _gl.uniform1i( uniforms.renderType, 1 ); - _gl.disable( _gl.DEPTH_TEST ); + return function () { - _gl.activeTexture( _gl.TEXTURE1 ); - _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture ); - _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); + v1.setFromMatrixPosition( this.light.matrixWorld ); + v2.setFromMatrixPosition( this.light.target.matrixWorld ); + v3.subVectors( v2, v1 ); + this.lightPlane.lookAt( v3 ); + this.lightPlane.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); - // update object positions + this.targetLine.geometry.vertices[ 1 ].copy( v3 ); + this.targetLine.geometry.verticesNeedUpdate = true; + this.targetLine.material.color.copy( this.lightPlane.material.color ); - flare.positionScreen.copy( screenPosition ) + }; - if ( flare.customUpdateCallback ) { +}(); - flare.customUpdateCallback( flare ); +// File:src/extras/helpers/EdgesHelper.js - } else { +/** + * @author WestLangley / http://github.com/WestLangley + */ - flare.updateLensFlares(); +THREE.EdgesHelper = function ( object, hex ) { - } + var color = ( hex !== undefined ) ? hex : 0xffffff; - // render flares + var edge = [ 0, 0 ], hash = {}; + var sortFunction = function ( a, b ) { return a - b }; - _gl.uniform1i( uniforms.renderType, 2 ); - _gl.enable( _gl.BLEND ); + var keys = [ 'a', 'b', 'c' ]; + var geometry = new THREE.BufferGeometry(); - for ( var j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) { + var geometry2 = object.geometry.clone(); - var sprite = flare.lensFlares[ j ]; + geometry2.mergeVertices(); + geometry2.computeFaceNormals(); - if ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) { + var vertices = geometry2.vertices; + var faces = geometry2.faces; + var numEdges = 0; - screenPosition.x = sprite.x; - screenPosition.y = sprite.y; - screenPosition.z = sprite.z; + for ( var i = 0, l = faces.length; i < l; i ++ ) { - size = sprite.size * sprite.scale / viewportHeight; + var face = faces[ i ]; - scale.x = size * invAspect; - scale.y = size; + for ( var j = 0; j < 3; j ++ ) { - _gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); - _gl.uniform2f( uniforms.scale, scale.x, scale.y ); - _gl.uniform1f( uniforms.rotation, sprite.rotation ); + edge[ 0 ] = face[ keys[ j ] ]; + edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; + edge.sort( sortFunction ); - _gl.uniform1f( uniforms.opacity, sprite.opacity ); - _gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b ); + var key = edge.toString(); - _renderer.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst ); - _renderer.setTexture( sprite.texture, 1 ); + if ( hash[ key ] === undefined ) { - _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); + hash[ key ] = { vert1: edge[ 0 ], vert2: edge[ 1 ], face1: i, face2: undefined }; + numEdges ++; - } + } else { - } + hash[ key ].face2 = i; } } - // restore gl + } - _gl.enable( _gl.CULL_FACE ); - _gl.enable( _gl.DEPTH_TEST ); - _gl.depthMask( true ); + var coords = new Float32Array( numEdges * 2 * 3 ); - }; + var index = 0; - function createProgram ( shader, precision ) { + for ( var key in hash ) { - var program = _gl.createProgram(); + var h = hash[ key ]; - var fragmentShader = _gl.createShader( _gl.FRAGMENT_SHADER ); - var vertexShader = _gl.createShader( _gl.VERTEX_SHADER ); + if ( h.face2 === undefined || faces[ h.face1 ].normal.dot( faces[ h.face2 ].normal ) < 0.9999 ) { // hardwired const OK - var prefix = "precision " + precision + " float;\n"; + var vertex = vertices[ h.vert1 ]; + coords[ index ++ ] = vertex.x; + coords[ index ++ ] = vertex.y; + coords[ index ++ ] = vertex.z; - _gl.shaderSource( fragmentShader, prefix + shader.fragmentShader ); - _gl.shaderSource( vertexShader, prefix + shader.vertexShader ); + vertex = vertices[ h.vert2 ]; + coords[ index ++ ] = vertex.x; + coords[ index ++ ] = vertex.y; + coords[ index ++ ] = vertex.z; - _gl.compileShader( fragmentShader ); - _gl.compileShader( vertexShader ); + } - _gl.attachShader( program, fragmentShader ); - _gl.attachShader( program, vertexShader ); + } - _gl.linkProgram( program ); + geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); - return program; + THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color } ), THREE.LinePieces ); - }; + this.matrix = object.matrixWorld; + this.matrixAutoUpdate = false; }; -// File:src/extras/renderers/plugins/ShadowMapPlugin.js +THREE.EdgesHelper.prototype = Object.create( THREE.Line.prototype ); + +// File:src/extras/helpers/FaceNormalsHelper.js /** - * @author alteredq / http://alteredqualia.com/ - */ + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley +*/ -THREE.ShadowMapPlugin = function () { +THREE.FaceNormalsHelper = function ( object, size, hex, linewidth ) { - var _gl, - _renderer, - _depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin, + this.object = object; - _frustum = new THREE.Frustum(), - _projScreenMatrix = new THREE.Matrix4(), + this.size = ( size !== undefined ) ? size : 1; - _min = new THREE.Vector3(), - _max = new THREE.Vector3(), + var color = ( hex !== undefined ) ? hex : 0xffff00; - _matrixPosition = new THREE.Vector3(), - - _renderList = []; + var width = ( linewidth !== undefined ) ? linewidth : 1; - this.init = function ( renderer ) { + var geometry = new THREE.Geometry(); - _gl = renderer.context; - _renderer = renderer; + var faces = this.object.geometry.faces; - var depthShader = THREE.ShaderLib[ "depthRGBA" ]; - var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms ); + for ( var i = 0, l = faces.length; i < l; i ++ ) { - _depthMaterial = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms } ); - _depthMaterialMorph = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true } ); - _depthMaterialSkin = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, skinning: true } ); - _depthMaterialMorphSkin = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true, skinning: true } ); + geometry.vertices.push( new THREE.Vector3(), new THREE.Vector3() ); - _depthMaterial._shadowPass = true; - _depthMaterialMorph._shadowPass = true; - _depthMaterialSkin._shadowPass = true; - _depthMaterialMorphSkin._shadowPass = true; + } - }; + THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces ); - this.render = function ( scene, camera ) { + this.matrixAutoUpdate = false; - if ( ! ( _renderer.shadowMapEnabled && _renderer.shadowMapAutoUpdate ) ) return; + this.normalMatrix = new THREE.Matrix3(); - this.update( scene, camera ); + this.update(); - }; +}; - this.update = function ( scene, camera ) { +THREE.FaceNormalsHelper.prototype = Object.create( THREE.Line.prototype ); - var i, il, j, jl, n, +THREE.FaceNormalsHelper.prototype.update = function () { - shadowMap, shadowMatrix, shadowCamera, - program, buffer, material, - webglObject, object, light, + var vertices = this.geometry.vertices; - lights = [], - k = 0, + var object = this.object; + var objectVertices = object.geometry.vertices; + var objectFaces = object.geometry.faces; + var objectWorldMatrix = object.matrixWorld; - fog = null; + object.updateMatrixWorld( true ); - // set GL state for depth map + this.normalMatrix.getNormalMatrix( objectWorldMatrix ); - _gl.clearColor( 1, 1, 1, 1 ); - _gl.disable( _gl.BLEND ); + for ( var i = 0, i2 = 0, l = objectFaces.length; i < l; i ++, i2 += 2 ) { - _gl.enable( _gl.CULL_FACE ); - _gl.frontFace( _gl.CCW ); + var face = objectFaces[ i ]; - if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) { + vertices[ i2 ].copy( objectVertices[ face.a ] ) + .add( objectVertices[ face.b ] ) + .add( objectVertices[ face.c ] ) + .divideScalar( 3 ) + .applyMatrix4( objectWorldMatrix ); - _gl.cullFace( _gl.FRONT ); + vertices[ i2 + 1 ].copy( face.normal ) + .applyMatrix3( this.normalMatrix ) + .normalize() + .multiplyScalar( this.size ) + .add( vertices[ i2 ] ); - } else { + } - _gl.cullFace( _gl.BACK ); + this.geometry.verticesNeedUpdate = true; - } + return this; - _renderer.setDepthTest( true ); +}; - // preprocess lights - // - skip lights that are not casting shadows - // - create virtual lights for cascaded shadow maps - for ( i = 0, il = scene.__lights.length; i < il; i ++ ) { +// File:src/extras/helpers/GridHelper.js - light = scene.__lights[ i ]; +/** + * @author mrdoob / http://mrdoob.com/ + */ - if ( ! light.castShadow ) continue; +THREE.GridHelper = function ( size, step ) { - if ( ( light instanceof THREE.DirectionalLight ) && light.shadowCascade ) { + var geometry = new THREE.Geometry(); + var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } ); - for ( n = 0; n < light.shadowCascadeCount; n ++ ) { + this.color1 = new THREE.Color( 0x444444 ); + this.color2 = new THREE.Color( 0x888888 ); - var virtualLight; + for ( var i = - size; i <= size; i += step ) { - if ( ! light.shadowCascadeArray[ n ] ) { + geometry.vertices.push( + new THREE.Vector3( - size, 0, i ), new THREE.Vector3( size, 0, i ), + new THREE.Vector3( i, 0, - size ), new THREE.Vector3( i, 0, size ) + ); - virtualLight = createVirtualLight( light, n ); - virtualLight.originalCamera = camera; + var color = i === 0 ? this.color1 : this.color2; - var gyro = new THREE.Gyroscope(); - gyro.position.copy( light.shadowCascadeOffset ); + geometry.colors.push( color, color, color, color ); - gyro.add( virtualLight ); - gyro.add( virtualLight.target ); + } - camera.add( gyro ); + THREE.Line.call( this, geometry, material, THREE.LinePieces ); - light.shadowCascadeArray[ n ] = virtualLight; +}; - console.log( "Created virtualLight", virtualLight ); +THREE.GridHelper.prototype = Object.create( THREE.Line.prototype ); - } else { +THREE.GridHelper.prototype.setColors = function( colorCenterLine, colorGrid ) { - virtualLight = light.shadowCascadeArray[ n ]; + this.color1.set( colorCenterLine ); + this.color2.set( colorGrid ); - } + this.geometry.colorsNeedUpdate = true; - updateVirtualLight( light, n ); +} - lights[ k ] = virtualLight; - k ++; +// File:src/extras/helpers/HemisphereLightHelper.js - } +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ - } else { +THREE.HemisphereLightHelper = function ( light, sphereSize, arrowLength, domeSize ) { - lights[ k ] = light; - k ++; + THREE.Object3D.call( this ); - } + this.light = light; + this.light.updateMatrixWorld(); - } + this.matrix = light.matrixWorld; + this.matrixAutoUpdate = false; - // render depth map + this.colors = [ new THREE.Color(), new THREE.Color() ]; - for ( i = 0, il = lights.length; i < il; i ++ ) { + var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 ); + geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) ); - light = lights[ i ]; + for ( var i = 0, il = 8; i < il; i ++ ) { - if ( ! light.shadowMap ) { + geometry.faces[ i ].color = this.colors[ i < 4 ? 0 : 1 ]; - var shadowFilter = THREE.LinearFilter; + } - if ( _renderer.shadowMapType === THREE.PCFSoftShadowMap ) { + var material = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors, wireframe: true } ); - shadowFilter = THREE.NearestFilter; + this.lightSphere = new THREE.Mesh( geometry, material ); + this.add( this.lightSphere ); - } + this.update(); - var pars = { minFilter: shadowFilter, magFilter: shadowFilter, format: THREE.RGBAFormat }; +}; - light.shadowMap = new THREE.WebGLRenderTarget( light.shadowMapWidth, light.shadowMapHeight, pars ); - light.shadowMapSize = new THREE.Vector2( light.shadowMapWidth, light.shadowMapHeight ); +THREE.HemisphereLightHelper.prototype = Object.create( THREE.Object3D.prototype ); - light.shadowMatrix = new THREE.Matrix4(); +THREE.HemisphereLightHelper.prototype.dispose = function () { + this.lightSphere.geometry.dispose(); + this.lightSphere.material.dispose(); +}; - } +THREE.HemisphereLightHelper.prototype.update = function () { - if ( ! light.shadowCamera ) { + var vector = new THREE.Vector3(); - if ( light instanceof THREE.SpotLight ) { + return function () { - light.shadowCamera = new THREE.PerspectiveCamera( light.shadowCameraFov, light.shadowMapWidth / light.shadowMapHeight, light.shadowCameraNear, light.shadowCameraFar ); + this.colors[ 0 ].copy( this.light.color ).multiplyScalar( this.light.intensity ); + this.colors[ 1 ].copy( this.light.groundColor ).multiplyScalar( this.light.intensity ); - } else if ( light instanceof THREE.DirectionalLight ) { + this.lightSphere.lookAt( vector.setFromMatrixPosition( this.light.matrixWorld ).negate() ); + this.lightSphere.geometry.colorsNeedUpdate = true; - light.shadowCamera = new THREE.OrthographicCamera( light.shadowCameraLeft, light.shadowCameraRight, light.shadowCameraTop, light.shadowCameraBottom, light.shadowCameraNear, light.shadowCameraFar ); + } - } else { +}(); - console.error( "Unsupported light type for shadow" ); - continue; +// File:src/extras/helpers/PointLightHelper.js - } +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ - scene.add( light.shadowCamera ); +THREE.PointLightHelper = function ( light, sphereSize ) { - if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); + this.light = light; + this.light.updateMatrixWorld(); - } + var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 ); + var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } ); + material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); - if ( light.shadowCameraVisible && ! light.cameraHelper ) { + THREE.Mesh.call( this, geometry, material ); - light.cameraHelper = new THREE.CameraHelper( light.shadowCamera ); - light.shadowCamera.add( light.cameraHelper ); + this.matrix = this.light.matrixWorld; + this.matrixAutoUpdate = false; - } + /* + var distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 ); + var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); - if ( light.isVirtual && virtualLight.originalCamera == camera ) { + this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); + this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); - updateShadowCamera( camera, light ); + var d = light.distance; - } + if ( d === 0.0 ) { - shadowMap = light.shadowMap; - shadowMatrix = light.shadowMatrix; - shadowCamera = light.shadowCamera; + this.lightDistance.visible = false; - shadowCamera.position.setFromMatrixPosition( light.matrixWorld ); - _matrixPosition.setFromMatrixPosition( light.target.matrixWorld ); - shadowCamera.lookAt( _matrixPosition ); - shadowCamera.updateMatrixWorld(); + } else { - shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld ); + this.lightDistance.scale.set( d, d, d ); - if ( light.cameraHelper ) light.cameraHelper.visible = light.shadowCameraVisible; - if ( light.shadowCameraVisible ) light.cameraHelper.update(); + } - // compute shadow matrix + this.add( this.lightDistance ); + */ - shadowMatrix.set( 0.5, 0.0, 0.0, 0.5, - 0.0, 0.5, 0.0, 0.5, - 0.0, 0.0, 0.5, 0.5, - 0.0, 0.0, 0.0, 1.0 ); +}; - shadowMatrix.multiply( shadowCamera.projectionMatrix ); - shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); +THREE.PointLightHelper.prototype = Object.create( THREE.Mesh.prototype ); - // update camera matrices and frustum +THREE.PointLightHelper.prototype.dispose = function () { - _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); - _frustum.setFromMatrix( _projScreenMatrix ); + this.geometry.dispose(); + this.material.dispose(); +}; - // render shadow map +THREE.PointLightHelper.prototype.update = function () { - _renderer.setRenderTarget( shadowMap ); - _renderer.clear(); + this.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); - // set object matrices & frustum culling + /* + var d = this.light.distance; - _renderList.length = 0; - projectObject(scene,scene,shadowCamera); + if ( d === 0.0 ) { + this.lightDistance.visible = false; - // render regular objects + } else { - var objectMaterial, useMorphing, useSkinning; + this.lightDistance.visible = true; + this.lightDistance.scale.set( d, d, d ); - for ( j = 0, jl = _renderList.length; j < jl; j ++ ) { + } + */ - webglObject = _renderList[ j ]; +}; - object = webglObject.object; - buffer = webglObject.buffer; +// File:src/extras/helpers/SkeletonHelper.js - // culling is overriden globally for all objects - // while rendering depth map +/** + * @author Sean Griffin / http://twitter.com/sgrif + * @author Michael Guerrero / http://realitymeltdown.com + * @author mrdoob / http://mrdoob.com/ + * @author ikerr / http://verold.com + */ - // need to deal with MeshFaceMaterial somehow - // in that case just use the first of material.materials for now - // (proper solution would require to break objects by materials - // similarly to regular rendering and then set corresponding - // depth materials per each chunk instead of just once per object) +THREE.SkeletonHelper = function ( object ) { - objectMaterial = getObjectMaterial( object ); + this.bones = this.getBoneList( object ); - useMorphing = object.geometry.morphTargets !== undefined && object.geometry.morphTargets.length > 0 && objectMaterial.morphTargets; - useSkinning = object instanceof THREE.SkinnedMesh && objectMaterial.skinning; + var geometry = new THREE.Geometry(); - if ( object.customDepthMaterial ) { + for ( var i = 0; i < this.bones.length; i ++ ) { - material = object.customDepthMaterial; + var bone = this.bones[ i ]; - } else if ( useSkinning ) { + if ( bone.parent instanceof THREE.Bone ) { - material = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin; + geometry.vertices.push( new THREE.Vector3() ); + geometry.vertices.push( new THREE.Vector3() ); + geometry.colors.push( new THREE.Color( 0, 0, 1 ) ); + geometry.colors.push( new THREE.Color( 0, 1, 0 ) ); - } else if ( useMorphing ) { + } - material = _depthMaterialMorph; + } - } else { + var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors, depthTest: false, depthWrite: false, transparent: true } ); - material = _depthMaterial; + THREE.Line.call( this, geometry, material, THREE.LinePieces ); - } + this.root = object; - _renderer.setMaterialFaces( objectMaterial ); + this.matrix = object.matrixWorld; + this.matrixAutoUpdate = false; - if ( buffer instanceof THREE.BufferGeometry ) { + this.update(); - _renderer.renderBufferDirect( shadowCamera, scene.__lights, fog, material, buffer, object ); +}; - } else { - _renderer.renderBuffer( shadowCamera, scene.__lights, fog, material, buffer, object ); +THREE.SkeletonHelper.prototype = Object.create( THREE.Line.prototype ); - } +THREE.SkeletonHelper.prototype.getBoneList = function( object ) { - } + var boneList = []; - // set matrices and render immediate objects + if ( object instanceof THREE.Bone ) { - var renderList = scene.__webglObjectsImmediate; + boneList.push( object ); - for ( j = 0, jl = renderList.length; j < jl; j ++ ) { + } - webglObject = renderList[ j ]; - object = webglObject.object; + for ( var i = 0; i < object.children.length; i ++ ) { - if ( object.visible && object.castShadow ) { + boneList.push.apply( boneList, this.getBoneList( object.children[ i ] ) ); - object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); + } - _renderer.renderImmediateObject( shadowCamera, scene.__lights, fog, _depthMaterial, object ); + return boneList; - } +}; - } +THREE.SkeletonHelper.prototype.update = function () { - } + var geometry = this.geometry; - // restore GL state + var matrixWorldInv = new THREE.Matrix4().getInverse( this.root.matrixWorld ); - var clearColor = _renderer.getClearColor(), - clearAlpha = _renderer.getClearAlpha(); + var boneMatrix = new THREE.Matrix4(); - _gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha ); - _gl.enable( _gl.BLEND ); + var j = 0; - if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) { + for ( var i = 0; i < this.bones.length; i ++ ) { - _gl.cullFace( _gl.BACK ); + var bone = this.bones[ i ]; - } + if ( bone.parent instanceof THREE.Bone ) { - }; - - function projectObject(scene, object,shadowCamera){ - - if ( object.visible ) { - - var webglObjects = scene.__webglObjects[object.id]; - - if (webglObjects && object.castShadow && (object.frustumCulled === false || _frustum.intersectsObject( object ) === true) ) { - - - for (var i = 0, l = webglObjects.length; i < l; i++){ - - var webglObject = webglObjects[i]; - - object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); - _renderList.push(webglObject); - - } - } - - for(var i = 0, l = object.children.length; i < l; i++) { - - projectObject(scene, object.children[i],shadowCamera); - } - - } - } + boneMatrix.multiplyMatrices( matrixWorldInv, bone.matrixWorld ); + geometry.vertices[ j ].setFromMatrixPosition( boneMatrix ); - function createVirtualLight( light, cascade ) { + boneMatrix.multiplyMatrices( matrixWorldInv, bone.parent.matrixWorld ); + geometry.vertices[ j + 1 ].setFromMatrixPosition( boneMatrix ); - var virtualLight = new THREE.DirectionalLight(); + j += 2; - virtualLight.isVirtual = true; + } - virtualLight.onlyShadow = true; - virtualLight.castShadow = true; + } - virtualLight.shadowCameraNear = light.shadowCameraNear; - virtualLight.shadowCameraFar = light.shadowCameraFar; + geometry.verticesNeedUpdate = true; - virtualLight.shadowCameraLeft = light.shadowCameraLeft; - virtualLight.shadowCameraRight = light.shadowCameraRight; - virtualLight.shadowCameraBottom = light.shadowCameraBottom; - virtualLight.shadowCameraTop = light.shadowCameraTop; + geometry.computeBoundingSphere(); - virtualLight.shadowCameraVisible = light.shadowCameraVisible; +}; - virtualLight.shadowDarkness = light.shadowDarkness; +// File:src/extras/helpers/SpotLightHelper.js - virtualLight.shadowBias = light.shadowCascadeBias[ cascade ]; - virtualLight.shadowMapWidth = light.shadowCascadeWidth[ cascade ]; - virtualLight.shadowMapHeight = light.shadowCascadeHeight[ cascade ]; +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley +*/ - virtualLight.pointsWorld = []; - virtualLight.pointsFrustum = []; +THREE.SpotLightHelper = function ( light ) { - var pointsWorld = virtualLight.pointsWorld, - pointsFrustum = virtualLight.pointsFrustum; + THREE.Object3D.call( this ); - for ( var i = 0; i < 8; i ++ ) { + this.light = light; + this.light.updateMatrixWorld(); - pointsWorld[ i ] = new THREE.Vector3(); - pointsFrustum[ i ] = new THREE.Vector3(); + this.matrix = light.matrixWorld; + this.matrixAutoUpdate = false; - } + var geometry = new THREE.CylinderGeometry( 0, 1, 1, 8, 1, true ); - var nearZ = light.shadowCascadeNearZ[ cascade ]; - var farZ = light.shadowCascadeFarZ[ cascade ]; + geometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, - 0.5, 0 ) ); + geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) ); - pointsFrustum[ 0 ].set( - 1, - 1, nearZ ); - pointsFrustum[ 1 ].set( 1, - 1, nearZ ); - pointsFrustum[ 2 ].set( - 1, 1, nearZ ); - pointsFrustum[ 3 ].set( 1, 1, nearZ ); + var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } ); - pointsFrustum[ 4 ].set( - 1, - 1, farZ ); - pointsFrustum[ 5 ].set( 1, - 1, farZ ); - pointsFrustum[ 6 ].set( - 1, 1, farZ ); - pointsFrustum[ 7 ].set( 1, 1, farZ ); + this.cone = new THREE.Mesh( geometry, material ); + this.add( this.cone ); - return virtualLight; + this.update(); - } +}; - // Synchronize virtual light with the original light +THREE.SpotLightHelper.prototype = Object.create( THREE.Object3D.prototype ); - function updateVirtualLight( light, cascade ) { +THREE.SpotLightHelper.prototype.dispose = function () { + this.cone.geometry.dispose(); + this.cone.material.dispose(); +}; - var virtualLight = light.shadowCascadeArray[ cascade ]; +THREE.SpotLightHelper.prototype.update = function () { - virtualLight.position.copy( light.position ); - virtualLight.target.position.copy( light.target.position ); - virtualLight.lookAt( virtualLight.target ); + var vector = new THREE.Vector3(); + var vector2 = new THREE.Vector3(); - virtualLight.shadowCameraVisible = light.shadowCameraVisible; - virtualLight.shadowDarkness = light.shadowDarkness; + return function () { - virtualLight.shadowBias = light.shadowCascadeBias[ cascade ]; + var coneLength = this.light.distance ? this.light.distance : 10000; + var coneWidth = coneLength * Math.tan( this.light.angle ); - var nearZ = light.shadowCascadeNearZ[ cascade ]; - var farZ = light.shadowCascadeFarZ[ cascade ]; + this.cone.scale.set( coneWidth, coneWidth, coneLength ); - var pointsFrustum = virtualLight.pointsFrustum; + vector.setFromMatrixPosition( this.light.matrixWorld ); + vector2.setFromMatrixPosition( this.light.target.matrixWorld ); - pointsFrustum[ 0 ].z = nearZ; - pointsFrustum[ 1 ].z = nearZ; - pointsFrustum[ 2 ].z = nearZ; - pointsFrustum[ 3 ].z = nearZ; + this.cone.lookAt( vector2.sub( vector ) ); - pointsFrustum[ 4 ].z = farZ; - pointsFrustum[ 5 ].z = farZ; - pointsFrustum[ 6 ].z = farZ; - pointsFrustum[ 7 ].z = farZ; + this.cone.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); - } + }; - // Fit shadow camera's ortho frustum to camera frustum +}(); - function updateShadowCamera( camera, light ) { +// File:src/extras/helpers/VertexNormalsHelper.js + +/** + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley +*/ - var shadowCamera = light.shadowCamera, - pointsFrustum = light.pointsFrustum, - pointsWorld = light.pointsWorld; +THREE.VertexNormalsHelper = function ( object, size, hex, linewidth ) { - _min.set( Infinity, Infinity, Infinity ); - _max.set( - Infinity, - Infinity, - Infinity ); + this.object = object; - for ( var i = 0; i < 8; i ++ ) { + this.size = ( size !== undefined ) ? size : 1; - var p = pointsWorld[ i ]; + var color = ( hex !== undefined ) ? hex : 0xff0000; - p.copy( pointsFrustum[ i ] ); - THREE.ShadowMapPlugin.__projector.unprojectVector( p, camera ); + var width = ( linewidth !== undefined ) ? linewidth : 1; - p.applyMatrix4( shadowCamera.matrixWorldInverse ); + var geometry = new THREE.Geometry(); - if ( p.x < _min.x ) _min.x = p.x; - if ( p.x > _max.x ) _max.x = p.x; + var vertices = object.geometry.vertices; - if ( p.y < _min.y ) _min.y = p.y; - if ( p.y > _max.y ) _max.y = p.y; + var faces = object.geometry.faces; - if ( p.z < _min.z ) _min.z = p.z; - if ( p.z > _max.z ) _max.z = p.z; + for ( var i = 0, l = faces.length; i < l; i ++ ) { - } + var face = faces[ i ]; - shadowCamera.left = _min.x; - shadowCamera.right = _max.x; - shadowCamera.top = _max.y; - shadowCamera.bottom = _min.y; + for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { - // can't really fit near/far - //shadowCamera.near = _min.z; - //shadowCamera.far = _max.z; + geometry.vertices.push( new THREE.Vector3(), new THREE.Vector3() ); - shadowCamera.updateProjectionMatrix(); + } } - // For the moment just ignore objects that have multiple materials with different animation methods - // Only the first material will be taken into account for deciding which depth material to use for shadow maps + THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces ); - function getObjectMaterial( object ) { + this.matrixAutoUpdate = false; - return object.material instanceof THREE.MeshFaceMaterial - ? object.material.materials[ 0 ] - : object.material; + this.normalMatrix = new THREE.Matrix3(); - }; + this.update(); }; -THREE.ShadowMapPlugin.__projector = new THREE.Projector(); - -// File:src/extras/renderers/plugins/SpritePlugin.js - -/** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - */ +THREE.VertexNormalsHelper.prototype = Object.create( THREE.Line.prototype ); -THREE.SpritePlugin = function () { +THREE.VertexNormalsHelper.prototype.update = ( function ( object ) { - var _gl, _renderer, _texture; + var v1 = new THREE.Vector3(); - var sprites = []; + return function( object ) { - var vertices, faces, vertexBuffer, elementBuffer; - var program, attributes, uniforms; + var keys = [ 'a', 'b', 'c', 'd' ]; - this.init = function ( renderer ) { + this.object.updateMatrixWorld( true ); - _gl = renderer.context; - _renderer = renderer; + this.normalMatrix.getNormalMatrix( this.object.matrixWorld ); - vertices = new Float32Array( [ - - 0.5, - 0.5, 0, 0, - 0.5, - 0.5, 1, 0, - 0.5, 0.5, 1, 1, - - 0.5, 0.5, 0, 1 - ] ); + var vertices = this.geometry.vertices; - faces = new Uint16Array( [ - 0, 1, 2, - 0, 2, 3 - ] ); + var verts = this.object.geometry.vertices; - vertexBuffer = _gl.createBuffer(); - elementBuffer = _gl.createBuffer(); + var faces = this.object.geometry.faces; - _gl.bindBuffer( _gl.ARRAY_BUFFER, vertexBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, vertices, _gl.STATIC_DRAW ); + var worldMatrix = this.object.matrixWorld; - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); - _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, faces, _gl.STATIC_DRAW ); + var idx = 0; - program = createProgram(); + for ( var i = 0, l = faces.length; i < l; i ++ ) { - attributes = { - position: _gl.getAttribLocation ( program, 'position' ), - uv: _gl.getAttribLocation ( program, 'uv' ) - }; + var face = faces[ i ]; - uniforms = { - uvOffset: _gl.getUniformLocation( program, 'uvOffset' ), - uvScale: _gl.getUniformLocation( program, 'uvScale' ), + for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { - rotation: _gl.getUniformLocation( program, 'rotation' ), - scale: _gl.getUniformLocation( program, 'scale' ), + var vertexId = face[ keys[ j ] ]; + var vertex = verts[ vertexId ]; - color: _gl.getUniformLocation( program, 'color' ), - map: _gl.getUniformLocation( program, 'map' ), - opacity: _gl.getUniformLocation( program, 'opacity' ), + var normal = face.vertexNormals[ j ]; - modelViewMatrix: _gl.getUniformLocation( program, 'modelViewMatrix' ), - projectionMatrix: _gl.getUniformLocation( program, 'projectionMatrix' ), + vertices[ idx ].copy( vertex ).applyMatrix4( worldMatrix ); - fogType: _gl.getUniformLocation( program, 'fogType' ), - fogDensity: _gl.getUniformLocation( program, 'fogDensity' ), - fogNear: _gl.getUniformLocation( program, 'fogNear' ), - fogFar: _gl.getUniformLocation( program, 'fogFar' ), - fogColor: _gl.getUniformLocation( program, 'fogColor' ), + v1.copy( normal ).applyMatrix3( this.normalMatrix ).normalize().multiplyScalar( this.size ); - alphaTest: _gl.getUniformLocation( program, 'alphaTest' ) - }; - - var canvas = document.createElement( 'canvas' ); - canvas.width = 8; - canvas.height = 8; - - var context = canvas.getContext( '2d' ); - context.fillStyle = 'white'; - context.fillRect( 0, 0, 8, 8 ); + v1.add( vertices[ idx ] ); + idx = idx + 1; - _texture = new THREE.Texture( canvas ); - _texture.needsUpdate = true; + vertices[ idx ].copy( v1 ); + idx = idx + 1; - }; + } - this.render = function ( scene, camera, viewportWidth, viewportHeight ) { + } - sprites.length = 0; + this.geometry.verticesNeedUpdate = true; - scene.traverseVisible( function ( child ) { + return this; - if ( child instanceof THREE.Sprite ) { + } - sprites.push( child ); +}()); - } +// File:src/extras/helpers/VertexTangentsHelper.js - } ); +/** + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley +*/ - if ( sprites.length === 0 ) return; +THREE.VertexTangentsHelper = function ( object, size, hex, linewidth ) { - // setup gl + this.object = object; - _gl.useProgram( program ); + this.size = ( size !== undefined ) ? size : 1; - _gl.enableVertexAttribArray( attributes.position ); - _gl.enableVertexAttribArray( attributes.uv ); + var color = ( hex !== undefined ) ? hex : 0x0000ff; - _gl.disable( _gl.CULL_FACE ); - _gl.enable( _gl.BLEND ); + var width = ( linewidth !== undefined ) ? linewidth : 1; - _gl.bindBuffer( _gl.ARRAY_BUFFER, vertexBuffer ); - _gl.vertexAttribPointer( attributes.position, 2, _gl.FLOAT, false, 2 * 8, 0 ); - _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 2 * 8, 8 ); + var geometry = new THREE.Geometry(); - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); + var vertices = object.geometry.vertices; - _gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); + var faces = object.geometry.faces; - _gl.activeTexture( _gl.TEXTURE0 ); - _gl.uniform1i( uniforms.map, 0 ); + for ( var i = 0, l = faces.length; i < l; i ++ ) { - var oldFogType = 0; - var sceneFogType = 0; - var fog = scene.fog; + var face = faces[ i ]; - if ( fog ) { + for ( var j = 0, jl = face.vertexTangents.length; j < jl; j ++ ) { - _gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b ); + geometry.vertices.push( new THREE.Vector3() ); + geometry.vertices.push( new THREE.Vector3() ); - if ( fog instanceof THREE.Fog ) { + } - _gl.uniform1f( uniforms.fogNear, fog.near ); - _gl.uniform1f( uniforms.fogFar, fog.far ); + } - _gl.uniform1i( uniforms.fogType, 1 ); - oldFogType = 1; - sceneFogType = 1; + THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces ); - } else if ( fog instanceof THREE.FogExp2 ) { + this.matrixAutoUpdate = false; - _gl.uniform1f( uniforms.fogDensity, fog.density ); + this.update(); - _gl.uniform1i( uniforms.fogType, 2 ); - oldFogType = 2; - sceneFogType = 2; +}; - } +THREE.VertexTangentsHelper.prototype = Object.create( THREE.Line.prototype ); - } else { +THREE.VertexTangentsHelper.prototype.update = ( function ( object ) { - _gl.uniform1i( uniforms.fogType, 0 ); - oldFogType = 0; - sceneFogType = 0; + var v1 = new THREE.Vector3(); - } + return function( object ) { + var keys = [ 'a', 'b', 'c', 'd' ]; - // update positions and sort + this.object.updateMatrixWorld( true ); - for ( var i = 0, l = sprites.length; i < l; i ++ ) { + var vertices = this.geometry.vertices; - var sprite = sprites[ i ]; - var material = sprite.material; + var verts = this.object.geometry.vertices; - sprite._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld ); - sprite.z = - sprite._modelViewMatrix.elements[ 14 ]; + var faces = this.object.geometry.faces; - } + var worldMatrix = this.object.matrixWorld; - sprites.sort( painterSortStable ); + var idx = 0; - // render all sprites + for ( var i = 0, l = faces.length; i < l; i ++ ) { - var scale = []; + var face = faces[ i ]; - for ( var i = 0, l = sprites.length; i < l; i ++ ) { + for ( var j = 0, jl = face.vertexTangents.length; j < jl; j ++ ) { - var sprite = sprites[ i ]; - var material = sprite.material; + var vertexId = face[ keys[ j ] ]; + var vertex = verts[ vertexId ]; - _gl.uniform1f( uniforms.alphaTest, material.alphaTest ); - _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite._modelViewMatrix.elements ); + var tangent = face.vertexTangents[ j ]; - scale[ 0 ] = sprite.scale.x; - scale[ 1 ] = sprite.scale.y; + vertices[ idx ].copy( vertex ).applyMatrix4( worldMatrix ); - var fogType = 0; + v1.copy( tangent ).transformDirection( worldMatrix ).multiplyScalar( this.size ); - if ( scene.fog && material.fog ) { + v1.add( vertices[ idx ] ); + idx = idx + 1; - fogType = sceneFogType; + vertices[ idx ].copy( v1 ); + idx = idx + 1; } - if ( oldFogType !== fogType ) { + } - _gl.uniform1i( uniforms.fogType, fogType ); - oldFogType = fogType; + this.geometry.verticesNeedUpdate = true; - } + return this; - if ( material.map !== null ) { + } + +}()); - _gl.uniform2f( uniforms.uvOffset, material.map.offset.x, material.map.offset.y ); - _gl.uniform2f( uniforms.uvScale, material.map.repeat.x, material.map.repeat.y ); +// File:src/extras/helpers/WireframeHelper.js - } else { +/** + * @author mrdoob / http://mrdoob.com/ + */ - _gl.uniform2f( uniforms.uvOffset, 0, 0 ); - _gl.uniform2f( uniforms.uvScale, 1, 1 ); +THREE.WireframeHelper = function ( object, hex ) { - } + var color = ( hex !== undefined ) ? hex : 0xffffff; - _gl.uniform1f( uniforms.opacity, material.opacity ); - _gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b ); + var edge = [ 0, 0 ], hash = {}; + var sortFunction = function ( a, b ) { return a - b }; - _gl.uniform1f( uniforms.rotation, material.rotation ); - _gl.uniform2fv( uniforms.scale, scale ); + var keys = [ 'a', 'b', 'c' ]; + var geometry = new THREE.BufferGeometry(); - _renderer.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); - _renderer.setDepthTest( material.depthTest ); - _renderer.setDepthWrite( material.depthWrite ); + if ( object.geometry instanceof THREE.Geometry ) { - if ( material.map && material.map.image && material.map.image.width ) { + var vertices = object.geometry.vertices; + var faces = object.geometry.faces; + var numEdges = 0; - _renderer.setTexture( material.map, 0 ); + // allocate maximal size + var edges = new Uint32Array( 6 * faces.length ); - } else { + for ( var i = 0, l = faces.length; i < l; i ++ ) { - _renderer.setTexture( _texture, 0 ); + var face = faces[ i ]; - } + for ( var j = 0; j < 3; j ++ ) { - _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); + edge[ 0 ] = face[ keys[ j ] ]; + edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; + edge.sort( sortFunction ); - } + var key = edge.toString(); - // restore gl + if ( hash[ key ] === undefined ) { - _gl.enable( _gl.CULL_FACE ); + edges[ 2 * numEdges ] = edge[ 0 ]; + edges[ 2 * numEdges + 1 ] = edge[ 1 ]; + hash[ key ] = true; + numEdges ++; - }; + } - function createProgram () { + } - var program = _gl.createProgram(); + } - var vertexShader = _gl.createShader( _gl.VERTEX_SHADER ); - var fragmentShader = _gl.createShader( _gl.FRAGMENT_SHADER ); + var coords = new Float32Array( numEdges * 2 * 3 ); - _gl.shaderSource( vertexShader, [ + for ( var i = 0, l = numEdges; i < l; i ++ ) { - 'precision ' + _renderer.getPrecision() + ' float;', + for ( var j = 0; j < 2; j ++ ) { - 'uniform mat4 modelViewMatrix;', - 'uniform mat4 projectionMatrix;', - 'uniform float rotation;', - 'uniform vec2 scale;', - 'uniform vec2 uvOffset;', - 'uniform vec2 uvScale;', + var vertex = vertices[ edges [ 2 * i + j] ]; - 'attribute vec2 position;', - 'attribute vec2 uv;', + var index = 6 * i + 3 * j; + coords[ index + 0 ] = vertex.x; + coords[ index + 1 ] = vertex.y; + coords[ index + 2 ] = vertex.z; - 'varying vec2 vUV;', + } - 'void main() {', + } - 'vUV = uvOffset + uv * uvScale;', + geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); - 'vec2 alignedPosition = position * scale;', + } else if ( object.geometry instanceof THREE.BufferGeometry ) { - 'vec2 rotatedPosition;', - 'rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;', - 'rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;', + if ( object.geometry.attributes.index !== undefined ) { // Indexed BufferGeometry - 'vec4 finalPosition;', + var vertices = object.geometry.attributes.position.array; + var indices = object.geometry.attributes.index.array; + var drawcalls = object.geometry.drawcalls; + var numEdges = 0; - 'finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );', - 'finalPosition.xy += rotatedPosition;', - 'finalPosition = projectionMatrix * finalPosition;', + if ( drawcalls.length === 0 ) { - 'gl_Position = finalPosition;', + drawcalls = [ { count : indices.length, index : 0, start : 0 } ]; - '}' + } - ].join( '\n' ) ); + // allocate maximal size + var edges = new Uint32Array( 2 * indices.length ); - _gl.shaderSource( fragmentShader, [ + for ( var o = 0, ol = drawcalls.length; o < ol; ++ o ) { - 'precision ' + _renderer.getPrecision() + ' float;', + var start = drawcalls[ o ].start; + var count = drawcalls[ o ].count; + var index = drawcalls[ o ].index; - 'uniform vec3 color;', - 'uniform sampler2D map;', - 'uniform float opacity;', + for ( var i = start, il = start + count; i < il; i += 3 ) { - 'uniform int fogType;', - 'uniform vec3 fogColor;', - 'uniform float fogDensity;', - 'uniform float fogNear;', - 'uniform float fogFar;', - 'uniform float alphaTest;', + for ( var j = 0; j < 3; j ++ ) { - 'varying vec2 vUV;', + edge[ 0 ] = index + indices[ i + j ]; + edge[ 1 ] = index + indices[ i + ( j + 1 ) % 3 ]; + edge.sort( sortFunction ); - 'void main() {', + var key = edge.toString(); - 'vec4 texture = texture2D( map, vUV );', + if ( hash[ key ] === undefined ) { - 'if ( texture.a < alphaTest ) discard;', + edges[ 2 * numEdges ] = edge[ 0 ]; + edges[ 2 * numEdges + 1 ] = edge[ 1 ]; + hash[ key ] = true; + numEdges ++; - 'gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );', + } - 'if ( fogType > 0 ) {', + } - 'float depth = gl_FragCoord.z / gl_FragCoord.w;', - 'float fogFactor = 0.0;', + } - 'if ( fogType == 1 ) {', + } - 'fogFactor = smoothstep( fogNear, fogFar, depth );', + var coords = new Float32Array( numEdges * 2 * 3 ); - '} else {', + for ( var i = 0, l = numEdges; i < l; i ++ ) { - 'const float LOG2 = 1.442695;', - 'float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );', - 'fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );', + for ( var j = 0; j < 2; j ++ ) { - '}', + var index = 6 * i + 3 * j; + var index2 = 3 * edges[ 2 * i + j]; + coords[ index + 0 ] = vertices[ index2 ]; + coords[ index + 1 ] = vertices[ index2 + 1 ]; + coords[ index + 2 ] = vertices[ index2 + 2 ]; - 'gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );', + } - '}', + } - '}' + geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); - ].join( '\n' ) ); + } else { // non-indexed BufferGeometry - _gl.compileShader( vertexShader ); - _gl.compileShader( fragmentShader ); + var vertices = object.geometry.attributes.position.array; + var numEdges = vertices.length / 3; + var numTris = numEdges / 3; - _gl.attachShader( program, vertexShader ); - _gl.attachShader( program, fragmentShader ); + var coords = new Float32Array( numEdges * 2 * 3 ); - _gl.linkProgram( program ); + for ( var i = 0, l = numTris; i < l; i ++ ) { - return program; + for ( var j = 0; j < 3; j ++ ) { - }; + var index = 18 * i + 6 * j; - function painterSortStable ( a, b ) { + var index1 = 9 * i + 3 * j; + coords[ index + 0 ] = vertices[ index1 ]; + coords[ index + 1 ] = vertices[ index1 + 1 ]; + coords[ index + 2 ] = vertices[ index1 + 2 ]; - if ( a.z !== b.z ) { + var index2 = 9 * i + 3 * ( ( j + 1 ) % 3 ); + coords[ index + 3 ] = vertices[ index2 ]; + coords[ index + 4 ] = vertices[ index2 + 1 ]; + coords[ index + 5 ] = vertices[ index2 + 2 ]; - return b.z - a.z; + } - } else { + } - return b.id - a.id; + geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); } - }; + } + + THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color } ), THREE.LinePieces ); + + this.matrix = object.matrixWorld; + this.matrixAutoUpdate = false; }; -// File:src/extras/renderers/plugins/DepthPassPlugin.js +THREE.WireframeHelper.prototype = Object.create( THREE.Line.prototype ); + +// File:src/extras/objects/ImmediateRenderObject.js /** * @author alteredq / http://alteredqualia.com/ */ -THREE.DepthPassPlugin = function () { - - this.enabled = false; - this.renderTarget = null; - - var _gl, - _renderer, - _depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin, +THREE.ImmediateRenderObject = function () { - _frustum = new THREE.Frustum(), - _projScreenMatrix = new THREE.Matrix4(), - _renderList = []; + THREE.Object3D.call( this ); - this.init = function ( renderer ) { + this.render = function ( renderCallback ) {}; - _gl = renderer.context; - _renderer = renderer; +}; - var depthShader = THREE.ShaderLib[ "depthRGBA" ]; - var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms ); +THREE.ImmediateRenderObject.prototype = Object.create( THREE.Object3D.prototype ); - _depthMaterial = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms } ); - _depthMaterialMorph = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true } ); - _depthMaterialSkin = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, skinning: true } ); - _depthMaterialMorphSkin = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true, skinning: true } ); +// File:src/extras/objects/MorphBlendMesh.js - _depthMaterial._shadowPass = true; - _depthMaterialMorph._shadowPass = true; - _depthMaterialSkin._shadowPass = true; - _depthMaterialMorphSkin._shadowPass = true; +/** + * @author alteredq / http://alteredqualia.com/ + */ - }; +THREE.MorphBlendMesh = function( geometry, material ) { - this.render = function ( scene, camera ) { + THREE.Mesh.call( this, geometry, material ); - if ( ! this.enabled ) return; + this.animationsMap = {}; + this.animationsList = []; - this.update( scene, camera ); + // prepare default animation + // (all frames played together in 1 second) - }; + var numFrames = this.geometry.morphTargets.length; - this.update = function ( scene, camera ) { + var name = "__default"; - var i, il, j, jl, n, + var startFrame = 0; + var endFrame = numFrames - 1; - program, buffer, material, - webglObject, object, light, - renderList, + var fps = numFrames / 1; - fog = null; + this.createAnimation( name, startFrame, endFrame, fps ); + this.setAnimationWeight( name, 1 ); - // set GL state for depth map +}; - _gl.clearColor( 1, 1, 1, 1 ); - _gl.disable( _gl.BLEND ); +THREE.MorphBlendMesh.prototype = Object.create( THREE.Mesh.prototype ); - _renderer.setDepthTest( true ); +THREE.MorphBlendMesh.prototype.createAnimation = function ( name, start, end, fps ) { - // update scene + var animation = { - if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); + startFrame: start, + endFrame: end, - // update camera matrices and frustum + length: end - start + 1, - camera.matrixWorldInverse.getInverse( camera.matrixWorld ); + fps: fps, + duration: ( end - start ) / fps, - _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); - _frustum.setFromMatrix( _projScreenMatrix ); + lastFrame: 0, + currentFrame: 0, - // render depth map + active: false, - _renderer.setRenderTarget( this.renderTarget ); - _renderer.clear(); + time: 0, + direction: 1, + weight: 1, - // set object matrices & frustum culling - - _renderList.length = 0; - projectObject(scene,scene,camera); + directionBackwards: false, + mirroredLoop: false - // render regular objects + }; - var objectMaterial, useMorphing, useSkinning; + this.animationsMap[ name ] = animation; + this.animationsList.push( animation ); - for ( j = 0, jl = _renderList.length; j < jl; j ++ ) { +}; - webglObject = _renderList[ j ]; +THREE.MorphBlendMesh.prototype.autoCreateAnimations = function ( fps ) { - object = webglObject.object; - buffer = webglObject.buffer; + var pattern = /([a-z]+)_?(\d+)/; - // todo: create proper depth material for particles + var firstAnimation, frameRanges = {}; - if ( object instanceof THREE.PointCloud && ! object.customDepthMaterial ) continue; + var geometry = this.geometry; - objectMaterial = getObjectMaterial( object ); + for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) { - if ( objectMaterial ) _renderer.setMaterialFaces( object.material ); + var morph = geometry.morphTargets[ i ]; + var chunks = morph.name.match( pattern ); - useMorphing = object.geometry.morphTargets !== undefined && object.geometry.morphTargets.length > 0 && objectMaterial.morphTargets; - useSkinning = object instanceof THREE.SkinnedMesh && objectMaterial.skinning; + if ( chunks && chunks.length > 1 ) { - if ( object.customDepthMaterial ) { + var name = chunks[ 1 ]; + var num = chunks[ 2 ]; - material = object.customDepthMaterial; + if ( ! frameRanges[ name ] ) frameRanges[ name ] = { start: Infinity, end: - Infinity }; - } else if ( useSkinning ) { + var range = frameRanges[ name ]; - material = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin; + if ( i < range.start ) range.start = i; + if ( i > range.end ) range.end = i; - } else if ( useMorphing ) { + if ( ! firstAnimation ) firstAnimation = name; - material = _depthMaterialMorph; + } - } else { + } - material = _depthMaterial; + for ( var name in frameRanges ) { - } + var range = frameRanges[ name ]; + this.createAnimation( name, range.start, range.end, fps ); - if ( buffer instanceof THREE.BufferGeometry ) { + } - _renderer.renderBufferDirect( camera, scene.__lights, fog, material, buffer, object ); + this.firstAnimation = firstAnimation; - } else { +}; - _renderer.renderBuffer( camera, scene.__lights, fog, material, buffer, object ); +THREE.MorphBlendMesh.prototype.setAnimationDirectionForward = function ( name ) { - } + var animation = this.animationsMap[ name ]; + if ( animation ) { - } + animation.direction = 1; + animation.directionBackwards = false; - // set matrices and render immediate objects + } - renderList = scene.__webglObjectsImmediate; +}; - for ( j = 0, jl = renderList.length; j < jl; j ++ ) { +THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward = function ( name ) { - webglObject = renderList[ j ]; - object = webglObject.object; + var animation = this.animationsMap[ name ]; - if ( object.visible ) { + if ( animation ) { - object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + animation.direction = - 1; + animation.directionBackwards = true; - _renderer.renderImmediateObject( camera, scene.__lights, fog, _depthMaterial, object ); + } - } +}; - } +THREE.MorphBlendMesh.prototype.setAnimationFPS = function ( name, fps ) { - // restore GL state + var animation = this.animationsMap[ name ]; - var clearColor = _renderer.getClearColor(), - clearAlpha = _renderer.getClearAlpha(); + if ( animation ) { - _gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha ); - _gl.enable( _gl.BLEND ); + animation.fps = fps; + animation.duration = ( animation.end - animation.start ) / animation.fps; - }; - - function projectObject(scene, object,camera){ - - if ( object.visible ) { - - var webglObjects = scene.__webglObjects[object.id]; - - if (webglObjects && (object.frustumCulled === false || _frustum.intersectsObject( object ) === true) ) { - - - for (var i = 0, l = webglObjects.length; i < l; i++){ - - var webglObject = webglObjects[i]; - - object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); - _renderList.push(webglObject); - - } - } - - for(var i = 0, l = object.children.length; i < l; i++) { - - projectObject(scene, object.children[i], camera); - } - - } } - // For the moment just ignore objects that have multiple materials with different animation methods - // Only the first material will be taken into account for deciding which depth material to use - - function getObjectMaterial( object ) { +}; - return object.material instanceof THREE.MeshFaceMaterial - ? object.material.materials[ 0 ] - : object.material; +THREE.MorphBlendMesh.prototype.setAnimationDuration = function ( name, duration ) { - }; + var animation = this.animationsMap[ name ]; -}; + if ( animation ) { + animation.duration = duration; + animation.fps = ( animation.end - animation.start ) / animation.duration; -// File:src/extras/shaders/ShaderFlares.js + } -/** - * @author mikael emtinger / http://gomo.se/ - */ +}; -THREE.ShaderFlares = { +THREE.MorphBlendMesh.prototype.setAnimationWeight = function ( name, weight ) { - 'lensFlareVertexTexture': { + var animation = this.animationsMap[ name ]; - vertexShader: [ + if ( animation ) { - "uniform lowp int renderType;", + animation.weight = weight; - "uniform vec3 screenPosition;", - "uniform vec2 scale;", - "uniform float rotation;", + } - "uniform sampler2D occlusionMap;", +}; - "attribute vec2 position;", - "attribute vec2 uv;", +THREE.MorphBlendMesh.prototype.setAnimationTime = function ( name, time ) { - "varying vec2 vUV;", - "varying float vVisibility;", + var animation = this.animationsMap[ name ]; - "void main() {", + if ( animation ) { - "vUV = uv;", + animation.time = time; - "vec2 pos = position;", + } - "if( renderType == 2 ) {", +}; - "vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );", - "visibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );", - "visibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );", - "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );", - "visibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );", - "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );", - "visibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );", - "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );", - "visibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );", +THREE.MorphBlendMesh.prototype.getAnimationTime = function ( name ) { - "vVisibility = visibility.r / 9.0;", - "vVisibility *= 1.0 - visibility.g / 9.0;", - "vVisibility *= visibility.b / 9.0;", - "vVisibility *= 1.0 - visibility.a / 9.0;", + var time = 0; - "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", - "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", + var animation = this.animationsMap[ name ]; - "}", + if ( animation ) { - "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", + time = animation.time; - "}" + } - ].join( "\n" ), + return time; - fragmentShader: [ +}; - "uniform lowp int renderType;", +THREE.MorphBlendMesh.prototype.getAnimationDuration = function ( name ) { - "uniform sampler2D map;", - "uniform float opacity;", - "uniform vec3 color;", + var duration = - 1; - "varying vec2 vUV;", - "varying float vVisibility;", + var animation = this.animationsMap[ name ]; - "void main() {", + if ( animation ) { - // pink square + duration = animation.duration; - "if( renderType == 0 ) {", + } - "gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );", + return duration; - // restore +}; - "} else if( renderType == 1 ) {", +THREE.MorphBlendMesh.prototype.playAnimation = function ( name ) { - "gl_FragColor = texture2D( map, vUV );", + var animation = this.animationsMap[ name ]; - // flare + if ( animation ) { - "} else {", + animation.time = 0; + animation.active = true; - "vec4 texture = texture2D( map, vUV );", - "texture.a *= opacity * vVisibility;", - "gl_FragColor = texture;", - "gl_FragColor.rgb *= color;", + } else { - "}", + console.warn( "animation[" + name + "] undefined" ); - "}" - ].join( "\n" ) + } - }, +}; +THREE.MorphBlendMesh.prototype.stopAnimation = function ( name ) { - 'lensFlare': { + var animation = this.animationsMap[ name ]; - vertexShader: [ + if ( animation ) { - "uniform lowp int renderType;", + animation.active = false; - "uniform vec3 screenPosition;", - "uniform vec2 scale;", - "uniform float rotation;", + } - "attribute vec2 position;", - "attribute vec2 uv;", +}; - "varying vec2 vUV;", +THREE.MorphBlendMesh.prototype.update = function ( delta ) { - "void main() {", + for ( var i = 0, il = this.animationsList.length; i < il; i ++ ) { - "vUV = uv;", + var animation = this.animationsList[ i ]; - "vec2 pos = position;", + if ( ! animation.active ) continue; - "if( renderType == 2 ) {", + var frameTime = animation.duration / animation.length; - "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", - "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", + animation.time += animation.direction * delta; - "}", + if ( animation.mirroredLoop ) { - "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", + if ( animation.time > animation.duration || animation.time < 0 ) { - "}" + animation.direction *= - 1; - ].join( "\n" ), + if ( animation.time > animation.duration ) { - fragmentShader: [ + animation.time = animation.duration; + animation.directionBackwards = true; - "precision mediump float;", + } - "uniform lowp int renderType;", + if ( animation.time < 0 ) { - "uniform sampler2D map;", - "uniform sampler2D occlusionMap;", - "uniform float opacity;", - "uniform vec3 color;", + animation.time = 0; + animation.directionBackwards = false; - "varying vec2 vUV;", + } - "void main() {", + } - // pink square + } else { - "if( renderType == 0 ) {", + animation.time = animation.time % animation.duration; - "gl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );", + if ( animation.time < 0 ) animation.time += animation.duration; - // restore + } - "} else if( renderType == 1 ) {", + var keyframe = animation.startFrame + THREE.Math.clamp( Math.floor( animation.time / frameTime ), 0, animation.length - 1 ); + var weight = animation.weight; - "gl_FragColor = texture2D( map, vUV );", + if ( keyframe !== animation.currentFrame ) { - // flare + this.morphTargetInfluences[ animation.lastFrame ] = 0; + this.morphTargetInfluences[ animation.currentFrame ] = 1 * weight; - "} else {", + this.morphTargetInfluences[ keyframe ] = 0; - "float visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 ) ).a;", - "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a;", - "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a;", - "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) ).a;", - "visibility = ( 1.0 - visibility / 4.0 );", + animation.lastFrame = animation.currentFrame; + animation.currentFrame = keyframe; - "vec4 texture = texture2D( map, vUV );", - "texture.a *= opacity * visibility;", - "gl_FragColor = texture;", - "gl_FragColor.rgb *= color;", + } - "}", + var mix = ( animation.time % frameTime ) / frameTime; - "}" + if ( animation.directionBackwards ) mix = 1 - mix; - ].join( "\n" ) + this.morphTargetInfluences[ animation.currentFrame ] = mix * weight; + this.morphTargetInfluences[ animation.lastFrame ] = ( 1 - mix ) * weight; }