From bf8c2562369bcbd329ba9eaa6023cdd3de6a3a86 Mon Sep 17 00:00:00 2001 From: Brian Chirls Date: Mon, 13 Oct 2014 10:26:59 -0400 Subject: [PATCH 1/2] Update README.md to reflect newer features and wider browser support for WebGL --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e26a04f..a810dba 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,10 @@ Full documentation is in progress at the [wiki](https://github.com/brianchirls/S ## Features - Optimized rendering path and GPU accelerated up to 60 frames per second -- Accept image input from varied sources: videos, images, canvases and arrays +- Accept image input from varied sources: video, image, canvas, array, webcam, Three.js - Effect parameters accept multiple formats and can monitor HTML form inputs - Basic 2D transforms (translate, rotate, scale, skew) on effect nodes -- Plugin architecture for adding new effects +- Plugin architecture for adding new effects, sources and targets - Read pixel array from any node - Load with [AMD](http://requirejs.org/docs/whyamd.html#amd)/[RequireJS](http://www.requirejs.org/) @@ -88,8 +88,8 @@ on the system's graphics card. Seriously.js is heavily optimized, so most modern desktops and notebooks should be sufficient. Older systems may run slower, especially when using high-resolution videos. -Mobile browser support for WebGL is limited. Mobile Firefox and Chrome have decent -support, but the Android Browser and Mobile Safari do not. +Mobile browser support for WebGL has improved. Mobile Firefox, Chrome and Safari have decent +support, but they can be slower than desktop versions due to limited system resources. Seriously.js provides a method to detect browser support and offer descriptive error messages wherever possible. From 65b0990638a60f26e80e7582f4468bf21b86e18f Mon Sep 17 00:00:00 2001 From: Brian Chirls Date: Sun, 26 Oct 2014 15:37:00 -0400 Subject: [PATCH 2/2] Update to latest version of three.js; fix example to accommodate three.js internal changes --- examples/demo/threejs-target.html | 4 + lib/three.js | 20146 +++++++++++++--------------- 2 files changed, 9521 insertions(+), 10629 deletions(-) diff --git a/examples/demo/threejs-target.html b/examples/demo/threejs-target.html index 2569493..be68f70 100644 --- a/examples/demo/threejs-target.html +++ b/examples/demo/threejs-target.html @@ -232,8 +232,12 @@ was its own render buffer. But Seriously.js changed it, so we need to make this call to force Three.js to update itself. Setting render target to anything other than null should do it. + + We also need to call resetGLState to force Three.js to use + its own shader */ renderer.setRenderTarget(noiseTexture); + renderer.resetGLState(); // render updated 3D scene to render target texture renderer.render(scene, camera); diff --git a/lib/three.js b/lib/three.js index d98a124..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 ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; - return [ this._x, this._y, this._z, this._w ]; + 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 ) { - return [ this.x, this.y ]; + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; + + array[ offset ] = this.x; + array[ offset + 1 ] = 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 ) { + + if ( offset === undefined ) offset = 0; - this.x = array[ 0 ]; - this.y = array[ 1 ]; - this.z = array[ 2 ]; + this.x = array[ offset ]; + this.y = array[ offset + 1 ]; + this.z = array[ offset + 2 ]; return this; }, - toArray: function () { + toArray: function ( array, offset ) { + + if ( array === undefined ) array = []; + if ( offset === undefined ) offset = 0; - return [ this.x, this.y, this.z ]; + 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; - return [ this.x, this.y, this.z, this.w ]; + array[ offset ] = this.x; + array[ offset + 1 ] = this.y; + array[ offset + 2 ] = this.z; + array[ offset + 3 ] = 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; - var vertices = node.geometry.vertices; + if ( geometry !== undefined ) { - for ( var i = 0, il = vertices.length; i < il; i ++ ) { + if ( geometry instanceof THREE.Geometry ) { - v1.copy( vertices[ i ] ); + var vertices = geometry.vertices; - v1.applyMatrix4( node.matrixWorld ); + for ( var i = 0, il = vertices.length; i < il; i ++ ) { + + v1.copy( vertices[ i ] ); + + v1.applyMatrix4( node.matrixWorld ); + + scope.expandByPoint( v1 ); + + } - scope.expandByPoint( v1 ); + } 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 ) { + + v1.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); + + v1.applyMatrix4( node.matrixWorld ); + + 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( 9 ); + this.elements = new Float32Array( [ - var te = this.elements; + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 + + ] ); - 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; + if ( arguments.length > 0 ) { + + console.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' ); + + } }; @@ -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 () { -THREE.Matrix4 = function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { + this.elements = new Float32Array( [ - this.elements = new Float32Array( 16 ); + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + + ] ); - // 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 ); - if ( child.id === id ) { + this.matrixWorld.decompose( position, result, scale ); - return child; + return result; - } + } - if ( recursive === true ) { + }(), - child = child.getObjectById( id, recursive ); + getWorldRotation: function () { - if ( child !== undefined ) { + var quaternion = new THREE.Quaternion(); - return child; + return function ( optionalTarget ) { - } + 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,6 +7790,169 @@ THREE.Object3D.prototype = { }, + toJSON: function () { + + var output = { + metadata: { + version: 4.3, + type: 'Object', + generator: 'ObjectExporter' + } + }; + + // + + var geometries = {}; + + var parseGeometry = function ( geometry ) { + + if ( output.geometries === undefined ) { + + output.geometries = []; + + } + + if ( geometries[ geometry.uuid ] === undefined ) { + + var json = geometry.toJSON(); + + delete json.metadata; + + geometries[ geometry.uuid ] = json; + + output.geometries.push( json ); + + } + + return geometry.uuid; + + }; + + // + + var materials = {}; + + var parseMaterial = function ( material ) { + + if ( output.materials === undefined ) { + + output.materials = []; + + } + + if ( materials[ material.uuid ] === undefined ) { + + var json = material.toJSON(); + + delete json.metadata; + + materials[ material.uuid ] = json; + + output.materials.push( json ); + + } + + return material.uuid; + + }; + + // + + var parseObject = function ( object ) { + + var data = {}; + + data.uuid = object.uuid; + data.type = object.type; + + if ( object.name !== '' ) data.name = object.name; + if ( JSON.stringify( object.userData ) !== '{}' ) data.userData = object.userData; + if ( object.visible !== true ) data.visible = object.visible; + + if ( object instanceof THREE.PerspectiveCamera ) { + + data.fov = object.fov; + data.aspect = object.aspect; + data.near = object.near; + data.far = object.far; + + } else if ( object instanceof THREE.OrthographicCamera ) { + + data.left = object.left; + data.right = object.right; + data.top = object.top; + data.bottom = object.bottom; + data.near = object.near; + data.far = object.far; + + } else if ( object instanceof THREE.AmbientLight ) { + + data.color = object.color.getHex(); + + } else if ( object instanceof THREE.DirectionalLight ) { + + data.color = object.color.getHex(); + data.intensity = object.intensity; + + } else if ( object instanceof THREE.PointLight ) { + + data.color = object.color.getHex(); + data.intensity = object.intensity; + data.distance = object.distance; + + } 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 ) { + + data.color = object.color.getHex(); + data.groundColor = object.groundColor.getHex(); + + } else if ( object instanceof THREE.Mesh ) { + + data.geometry = parseGeometry( object.geometry ); + data.material = parseMaterial( object.material ); + + } else if ( object instanceof THREE.Line ) { + + data.geometry = parseGeometry( object.geometry ); + data.material = parseMaterial( object.material ); + + } else if ( object instanceof THREE.Sprite ) { + + data.material = parseMaterial( object.material ); + + } + + data.matrix = object.matrix.toArray(); + + if ( object.children.length > 0 ) { + + data.children = []; + + for ( var i = 0; i < object.children.length; i ++ ) { + + data.children.push( parseObject( object.children[ i ] ) ); + + } + + } + + return data; + + } + + output.object = parseObject( this ); + + return output; + + }, + clone: function ( object, recursive ) { if ( object === undefined ) object = new THREE.Object3D(); @@ -7709,860 +8010,32 @@ THREE.Object3DIdCount = 0; /** * @author mrdoob / http://mrdoob.com/ - * @author supereggbert / http://www.paulbrunt.co.uk/ - * @author julianwa / https://github.com/julianwa */ THREE.Projector = function () { - 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, - - _renderData = { objects: [], lights: [], elements: [] }, - - _vA = new THREE.Vector3(), - _vB = new THREE.Vector3(), - _vC = new THREE.Vector3(), - - _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(), - - _modelMatrix, - _modelViewProjectionMatrix = new THREE.Matrix4(), - - _normalMatrix = new THREE.Matrix3(), - - _frustum = new THREE.Frustum(), - - _clippedVertex1PositionScreen = new THREE.Vector4(), - _clippedVertex2PositionScreen = new THREE.Vector4(); + console.warn( 'THREE.Projector has been moved to /examples/renderers/Projector.js.' ); this.projectVector = function ( vector, camera ) { - camera.matrixWorldInverse.getInverse( camera.matrixWorld ); - - _viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); - - return vector.applyProjection( _viewProjectionMatrix ); - - }; - - this.unprojectVector = function () { - - var projectionMatrixInverse = new THREE.Matrix4(); - - return function ( vector, camera ) { - - projectionMatrixInverse.getInverse( camera.projectionMatrix ); - _viewProjectionMatrix.multiplyMatrices( camera.matrixWorld, projectionMatrixInverse ); - - return vector.applyProjection( _viewProjectionMatrix ); - - }; - - }(); - - this.pickingRay = function ( vector, camera ) { - - // set two vectors with opposing z values - vector.z = - 1.0; - var end = new THREE.Vector3( vector.x, vector.y, 1.0 ); - - this.unprojectVector( vector, camera ); - this.unprojectVector( end, camera ); - - // find direction from vector to end - end.sub( vector ).normalize(); - - return new THREE.Raycaster( vector, end ); + console.warn( 'THREE.Projector: .projectVector() is now vector.project().' ); + vector.project( camera ); }; - var RenderList = function () { - - var normals = []; - var uvs = []; - - var object = null; - var material = null; - - var normalMatrix = new THREE.Matrix3(); - - var setObject = function ( value ) { - - object = value; - material = object.material; - - normalMatrix.getNormalMatrix( object.matrixWorld ); - - normals.length = 0; - uvs.length = 0; - - }; - - var projectVertex = function ( vertex ) { - - var position = vertex.position; - var positionWorld = vertex.positionWorld; - var positionScreen = vertex.positionScreen; - - positionWorld.copy( position ).applyMatrix4( _modelMatrix ); - positionScreen.copy( positionWorld ).applyMatrix4( _viewProjectionMatrix ); - - var invW = 1 / positionScreen.w; - - 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; - - }; - - var pushVertex = function ( x, y, z ) { - - _vertex = getNextVertexInPool(); - _vertex.position.set( x, y, z ); - - projectVertex( _vertex ); - - }; - - var pushNormal = function ( x, y, z ) { - - normals.push( x, y, z ); - - }; - - var pushUv = function ( x, y ) { - - uvs.push( x, y ); - - }; - - var checkTriangleVisibility = function ( v1, v2, v3 ) { - - if ( v1.visible === true || v2.visible === true || v3.visible === true ) return true; - - _points3[ 0 ] = v1.positionScreen; - _points3[ 1 ] = v2.positionScreen; - _points3[ 2 ] = v3.positionScreen; - - return _clipBox.isIntersectionBox( _boundingBox.setFromPoints( _points3 ) ); - - }; - - 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; - - }; - - var pushLine = function ( a, b ) { - - var v1 = _vertexPool[ a ]; - var v2 = _vertexPool[ b ]; - - _line = getNextLineInPool(); - - _line.id = object.id; - _line.v1.copy( v1 ); - _line.v2.copy( v2 ); - _line.z = ( v1.positionScreen.z + v2.positionScreen.z ) / 2; - - _line.material = object.material; - - _renderData.elements.push( _line ); - - }; - - var pushTriangle = function ( a, b, c ) { - - var v1 = _vertexPool[ a ]; - var v2 = _vertexPool[ b ]; - var v3 = _vertexPool[ c ]; - - if ( checkTriangleVisibility( v1, v2, v3 ) === false ) return; - - if ( material.side === THREE.DoubleSide || checkBackfaceCulling( v1, v2, v3 ) === true ) { - - _face = getNextFaceInPool(); - - _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 ++ ) { - - var offset = arguments[ i ] * 3; - var normal = _face.vertexNormalsModel[ i ]; - - 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 ] ); - - } - - _face.vertexNormalsLength = 3; - - _face.material = object.material; - - _renderData.elements.push( _face ); - - } - - }; + this.unprojectVector = function ( vector, camera ) { - return { - setObject: setObject, - projectVertex: projectVertex, - checkTriangleVisibility: checkTriangleVisibility, - checkBackfaceCulling: checkBackfaceCulling, - pushVertex: pushVertex, - pushNormal: pushNormal, - pushUv: pushUv, - pushLine: pushLine, - pushTriangle: pushTriangle - } + console.warn( 'THREE.Projector: .unprojectVector() is now vector.unproject().' ); + vector.unproject( camera ); }; - var renderList = new RenderList(); - - this.projectScene = function ( scene, camera, sortObjects, sortElements ) { - - _faceCount = 0; - _lineCount = 0; - _spriteCount = 0; - - _renderData.elements.length = 0; - - if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); - if ( camera.parent === undefined ) camera.updateMatrixWorld(); - - _viewMatrix.copy( camera.matrixWorldInverse.getInverse( camera.matrixWorld ) ); - _viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix ); - - _frustum.setFromMatrix( _viewProjectionMatrix ); - - // - - _objectCount = 0; - - _renderData.objects.length = 0; - _renderData.lights.length = 0; - - scene.traverseVisible( function ( object ) { - - if ( object instanceof THREE.Light ) { - - _renderData.lights.push( object ); - - } else if ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Sprite ) { - - if ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) { - - _object = getNextObjectInPool(); - _object.id = object.id; - _object.object = object; - - if ( object.renderDepth !== null ) { - - _object.z = object.renderDepth; - - } else { - - _vector3.setFromMatrixPosition( object.matrixWorld ); - _vector3.applyProjection( _viewProjectionMatrix ); - _object.z = _vector3.z; - - } - - _renderData.objects.push( _object ); - - } - - } - - } ); - - if ( sortObjects === true ) { - - _renderData.objects.sort( painterSort ); - - } - - // - - for ( var o = 0, ol = _renderData.objects.length; o < ol; o ++ ) { - - var object = _renderData.objects[ o ].object; - var geometry = object.geometry; - - renderList.setObject( object ); - - _modelMatrix = object.matrixWorld; - - _vertexCount = 0; - - if ( object instanceof THREE.Mesh ) { - - if ( geometry instanceof THREE.BufferGeometry ) { - - var attributes = geometry.attributes; - var offsets = geometry.offsets; - - if ( attributes.position === undefined ) continue; - - 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 ] ); - - } - - if ( attributes.normal !== undefined ) { - - var normals = attributes.normal.array; - - for ( var i = 0, l = normals.length; i < l; i += 3 ) { - - renderList.pushNormal( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ); - - } - - } - - if ( attributes.uv !== undefined ) { - - var uvs = attributes.uv.array; - - for ( var i = 0, l = uvs.length; i < l; i += 2 ) { - - renderList.pushUv( uvs[ i ], uvs[ i + 1 ] ); - - } - - } - - if ( attributes.index !== undefined ) { - - var indices = attributes.index.array; - - if ( offsets.length > 0 ) { - - for ( var o = 0; o < offsets.length; o ++ ) { - - var offset = offsets[ o ]; - var index = offset.index; - - for ( var i = offset.start, l = offset.start + offset.count; i < l; i += 3 ) { - - renderList.pushTriangle( indices[ i ] + index, indices[ i + 1 ] + index, indices[ i + 2 ] + index ); - - } - - } - - } else { - - for ( var i = 0, l = indices.length; i < l; i += 3 ) { - - renderList.pushTriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] ); - - } - - } - - } else { - - for ( var i = 0, l = positions.length / 3; i < l; i += 3 ) { - - renderList.pushTriangle( i, i + 1, i + 2 ); - - } - - } - - } 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; - - for ( var v = 0, vl = vertices.length; v < vl; v ++ ) { - - var vertex = vertices[ v ]; - renderList.pushVertex( vertex.x, vertex.y, vertex.z ); - - } - - for ( var f = 0, fl = faces.length; f < fl; f ++ ) { - - var face = faces[ f ]; - - var material = isFaceMaterial === true - ? objectMaterials.materials[ face.materialIndex ] - : object.material; - - if ( material === undefined ) continue; - - var side = material.side; - - var v1 = _vertexPool[ face.a ]; - var v2 = _vertexPool[ face.b ]; - var v3 = _vertexPool[ face.c ]; - - if ( material.morphTargets === true ) { - - var morphTargets = geometry.morphTargets; - var morphInfluences = object.morphTargetInfluences; - - 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 ); - - for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) { - - var influence = morphInfluences[ t ]; - - if ( influence === 0 ) continue; - - var targets = morphTargets[ t ].vertices; - - _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; - - _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; - - } - - v1.position.add( _vA ); - v2.position.add( _vB ); - v3.position.add( _vC ); - - renderList.projectVertex( v1 ); - renderList.projectVertex( v2 ); - renderList.projectVertex( v3 ); - - } - - if ( renderList.checkTriangleVisibility( v1, v2, v3 ) === false ) continue; - - var visible = renderList.checkBackfaceCulling( v1, v2, v3 ); - - if ( side !== THREE.DoubleSide ) { - if ( side === THREE.FrontSide && visible === false ) continue; - if ( side === THREE.BackSide && visible === true ) continue; - } - - _face = getNextFaceInPool(); - - _face.id = object.id; - _face.v1.copy( v1 ); - _face.v2.copy( v2 ); - _face.v3.copy( v3 ); - - _face.normalModel.copy( face.normal ); - - if ( visible === false && ( side === THREE.BackSide || side === THREE.DoubleSide ) ) { - - _face.normalModel.negate(); - - } - - _face.normalModel.applyMatrix3( _normalMatrix ).normalize(); - - var faceVertexNormals = face.vertexNormals; - - for ( var n = 0, nl = Math.min( faceVertexNormals.length, 3 ); n < nl; n ++ ) { - - var normalModel = _face.vertexNormalsModel[ n ]; - normalModel.copy( faceVertexNormals[ n ] ); - - if ( visible === false && ( side === THREE.BackSide || side === THREE.DoubleSide ) ) { - - normalModel.negate(); - - } - - normalModel.applyMatrix3( _normalMatrix ).normalize(); - - } - - _face.vertexNormalsLength = faceVertexNormals.length; - - var vertexUvs = faceVertexUvs[ f ]; - - if ( vertexUvs !== undefined ) { - - for ( var u = 0; u < 3; u ++ ) { - - _face.uvs[ u ].copy( vertexUvs[ u ] ); - - } - - } - - _face.color = face.color; - _face.material = material; - - _face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3; - - _renderData.elements.push( _face ); - - } - - } - - } else if ( object instanceof THREE.Line ) { - - if ( geometry instanceof THREE.BufferGeometry ) { - - var attributes = geometry.attributes; - - if ( attributes.position !== undefined ) { - - 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 ] ); - - } - - if ( attributes.index !== undefined ) { - - var indices = attributes.index.array; - - for ( var i = 0, l = indices.length; i < l; i += 2 ) { - - renderList.pushLine( indices[ i ], indices[ i + 1 ] ); - - } - - } else { - - var step = object.type === THREE.LinePieces ? 2 : 1; - - for ( var i = 0, l = ( positions.length / 3 ) - 1; i < l; i += step ) { - - renderList.pushLine( i, i + 1 ); - - } - - } - - } - - } else if ( geometry instanceof THREE.Geometry ) { - - _modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix ); - - var vertices = object.geometry.vertices; - - if ( vertices.length === 0 ) continue; - - v1 = getNextVertexInPool(); - v1.positionScreen.copy( vertices[ 0 ] ).applyMatrix4( _modelViewProjectionMatrix ); - - // Handle LineStrip and LinePieces - var step = object.type === THREE.LinePieces ? 2 : 1; - - for ( var v = 1, vl = vertices.length; v < vl; v ++ ) { - - v1 = getNextVertexInPool(); - v1.positionScreen.copy( vertices[ v ] ).applyMatrix4( _modelViewProjectionMatrix ); - - if ( ( v + 1 ) % step > 0 ) continue; - - v2 = _vertexPool[ _vertexCount - 2 ]; - - _clippedVertex1PositionScreen.copy( v1.positionScreen ); - _clippedVertex2PositionScreen.copy( v2.positionScreen ); - - if ( clipLine( _clippedVertex1PositionScreen, _clippedVertex2PositionScreen ) === true ) { - - // Perform the perspective divide - _clippedVertex1PositionScreen.multiplyScalar( 1 / _clippedVertex1PositionScreen.w ); - _clippedVertex2PositionScreen.multiplyScalar( 1 / _clippedVertex2PositionScreen.w ); - - _line = getNextLineInPool(); - - _line.id = object.id; - _line.v1.positionScreen.copy( _clippedVertex1PositionScreen ); - _line.v2.positionScreen.copy( _clippedVertex2PositionScreen ); - - _line.z = Math.max( _clippedVertex1PositionScreen.z, _clippedVertex2PositionScreen.z ); - - _line.material = object.material; - - if ( object.material.vertexColors === THREE.VertexColors ) { - - _line.vertexColors[ 0 ].copy( object.geometry.colors[ v ] ); - _line.vertexColors[ 1 ].copy( object.geometry.colors[ v - 1 ] ); - - } - - _renderData.elements.push( _line ); - - } - - } - - } - - } else if ( object instanceof THREE.Sprite ) { - - _vector4.set( _modelMatrix.elements[ 12 ], _modelMatrix.elements[ 13 ], _modelMatrix.elements[ 14 ], 1 ); - _vector4.applyMatrix4( _viewProjectionMatrix ); - - var invW = 1 / _vector4.w; - - _vector4.z *= invW; - - if ( _vector4.z >= - 1 && _vector4.z <= 1 ) { - - _sprite = getNextSpriteInPool(); - _sprite.id = object.id; - _sprite.x = _vector4.x * invW; - _sprite.y = _vector4.y * invW; - _sprite.z = _vector4.z; - _sprite.object = object; - - _sprite.rotation = object.rotation; - - _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 ] ) ); - - _sprite.material = object.material; - - _renderData.elements.push( _sprite ); - - } - - } - - } - - if ( sortElements === true ) _renderData.elements.sort( painterSort ); + this.pickingRay = function ( vector, camera ) { - return _renderData; + console.error( 'THREE.Projector: .pickingRay() has been removed.' ); }; - // Pools - - function getNextObjectInPool() { - - if ( _objectCount === _objectPoolLength ) { - - var object = new THREE.RenderableObject(); - _objectPool.push( object ); - _objectPoolLength ++; - _objectCount ++; - return object; - - } - - return _objectPool[ _objectCount ++ ]; - - } - - function getNextVertexInPool() { - - if ( _vertexCount === _vertexPoolLength ) { - - var vertex = new THREE.RenderableVertex(); - _vertexPool.push( vertex ); - _vertexPoolLength ++; - _vertexCount ++; - return vertex; - - } - - return _vertexPool[ _vertexCount ++ ]; - - } - - function getNextFaceInPool() { - - if ( _faceCount === _facePoolLength ) { - - var face = new THREE.RenderableFace(); - _facePool.push( face ); - _facePoolLength ++; - _faceCount ++; - return face; - - } - - return _facePool[ _faceCount ++ ]; - - - } - - function getNextLineInPool() { - - if ( _lineCount === _linePoolLength ) { - - var line = new THREE.RenderableLine(); - _linePool.push( line ); - _linePoolLength ++; - _lineCount ++ - return line; - - } - - return _linePool[ _lineCount ++ ]; - - } - - function getNextSpriteInPool() { - - if ( _spriteCount === _spritePoolLength ) { - - var sprite = new THREE.RenderableSprite(); - _spritePool.push( sprite ); - _spritePoolLength ++; - _spriteCount ++ - return sprite; - - } - - return _spritePool[ _spriteCount ++ ]; - - } - - // - - function painterSort( a, b ) { - - if ( a.z !== b.z ) { - - return b.z - a.z; - - } else if ( a.id !== b.id ) { - - return a.id - b.id; - - } else { - - return 0; - - } - - } - - function clipLine( s1, s2 ) { - - 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; - - if ( bc1near >= 0 && bc2near >= 0 && bc1far >= 0 && bc2far >= 0 ) { - - // Both vertices lie entirely within all clip planes. - return true; - - } 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 ) { - - // v1 lies outside the near plane, v2 inside - alpha1 = Math.max( alpha1, bc1near / ( bc1near - bc2near ) ); - - } else if ( bc2near < 0 ) { - - // v2 lies outside the near plane, v1 inside - alpha2 = Math.min( alpha2, bc1near / ( bc1near - bc2near ) ); - - } - - if ( bc1far < 0 ) { - - // v1 lies outside the far plane, v2 inside - alpha1 = Math.max( alpha1, bc1far / ( bc1far - bc2far ) ); - - } else if ( bc2far < 0 ) { - - // v2 lies outside the far plane, v2 inside - alpha2 = Math.min( alpha2, bc1far / ( bc1far - bc2far ) ); - - } - - if ( alpha2 < alpha1 ) { - - // 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; - - } else { - - // Update the s1 and s2 vertices to match the clipped line segment. - s1.lerp( s2, alpha1 ); - s2.lerp( s1, 1 - alpha2 ); - - return true; - - } - - } - - } - }; // File:src/core/Face3.js @@ -8651,6 +8124,8 @@ THREE.BufferAttribute = function ( array, itemSize ) { this.array = array; this.itemSize = itemSize; + this.needsUpdate = false; + }; THREE.BufferAttribute.prototype = { @@ -8663,6 +8138,19 @@ THREE.BufferAttribute.prototype = { }, + copyAt: function ( index1, attribute, index2 ) { + + index1 *= this.itemSize; + index2 *= attribute.itemSize; + + for ( var i = 0, l = this.itemSize; i < l; i ++ ) { + + this.array[ index1 + i ] = attribute.array[ index2 + i ]; + + } + + }, + set: function ( value ) { this.array.set( value ); @@ -8729,6 +8217,12 @@ THREE.BufferAttribute.prototype = { return this; + }, + + clone: function () { + + return new THREE.BufferAttribute( new this.array.constructor( this.array ), this.itemSize ); + } }; @@ -8803,16 +8297,21 @@ THREE.Float64Attribute = function ( data, itemSize ) { /** * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ */ THREE.BufferGeometry = function () { - this.id = THREE.GeometryIdCount ++; + Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } ); + this.uuid = THREE.Math.generateUUID(); this.name = ''; + this.type = 'BufferGeometry'; this.attributes = {}; + this.attributesKeys = []; + this.drawcalls = []; this.offsets = this.drawcalls; // backwards compatibility @@ -8838,6 +8337,7 @@ THREE.BufferGeometry.prototype = { } this.attributes[ name ] = attribute; + this.attributesKeys = Object.keys( this.attributes ); }, @@ -8883,6 +8383,12 @@ THREE.BufferGeometry.prototype = { }, + center: function () { + + // TODO + + }, + fromGeometry: function ( geometry, settings ) { settings = settings || { 'vertexColors': THREE.NoColors }; @@ -8910,7 +8416,7 @@ THREE.BufferGeometry.prototype = { if ( hasFaceVertexUv === true ) { var uvs = new Float32Array( faces.length * 3 * 2 ); - this.addAttribute( 'uvs', new THREE.BufferAttribute( uvs, 2 ) ); + this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); } @@ -9033,80 +8539,48 @@ THREE.BufferGeometry.prototype = { computeBoundingBox: function () { - if ( this.boundingBox === null ) { - - this.boundingBox = new THREE.Box3(); - - } + var vector = new THREE.Vector3(); - var positions = this.attributes[ 'position' ].array; + return function () { - if ( positions ) { + if ( this.boundingBox === null ) { - var bb = this.boundingBox; + this.boundingBox = new THREE.Box3(); - 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 ( x < bb.min.x ) { - - bb.min.x = x; + var positions = this.attributes.position.array; - } else if ( x > bb.max.x ) { - - bb.max.x = x; - - } - - if ( y < bb.min.y ) { - - bb.min.y = y; - - } else if ( y > bb.max.y ) { - - bb.max.y = y; - - } - - if ( z < bb.min.z ) { + if ( positions ) { - bb.min.z = z; + var bb = this.boundingBox; + bb.makeEmpty(); - } else if ( z > bb.max.z ) { + for ( var i = 0, il = positions.length; i < il; i += 3 ) { - bb.max.z = z; + vector.set( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); + bb.expandByPoint( vector ); } } - } + if ( positions === undefined || positions.length === 0 ) { - if ( positions === undefined || positions.length === 0 ) { + this.boundingBox.min.set( 0, 0, 0 ); + this.boundingBox.max.set( 0, 0, 0 ); - this.boundingBox.min.set( 0, 0, 0 ); - this.boundingBox.max.set( 0, 0, 0 ); + } - } + if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { - if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { + console.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.' ); - console.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.' ); + } } - }, + }(), computeBoundingSphere: function () { @@ -9121,7 +8595,7 @@ THREE.BufferGeometry.prototype = { } - var positions = this.attributes[ 'position' ].array; + var positions = this.attributes.position.array; if ( positions ) { @@ -9172,38 +8646,33 @@ THREE.BufferGeometry.prototype = { computeVertexNormals: function () { - if ( this.attributes[ 'position' ] ) { - - var i, il; - var j, jl; + var attributes = this.attributes; - var nVertexElements = this.attributes[ 'position' ].array.length; + if ( attributes.position ) { - if ( this.attributes[ 'normal' ] === undefined ) { - - this.attributes[ 'normal' ] = { + var positions = attributes.position.array; - itemSize: 3, - array: new Float32Array( nVertexElements ) + if ( attributes.normal === undefined ) { - }; + this.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( positions.length ), 3 ) ); } else { // reset existing normals to zero - for ( i = 0, il = this.attributes[ 'normal' ].array.length; i < il; i ++ ) { + var normals = attributes.normal.array; + + for ( var i = 0, il = normals.length; i < il; i ++ ) { - this.attributes[ 'normal' ].array[ i ] = 0; + normals[ i ] = 0; } } - var positions = this.attributes[ 'position' ].array; - var normals = this.attributes[ 'normal' ].array; + var normals = attributes.normal.array; - var vA, vB, vC, x, y, z, + var vA, vB, vC, pA = new THREE.Vector3(), pB = new THREE.Vector3(), @@ -9214,79 +8683,57 @@ THREE.BufferGeometry.prototype = { // indexed elements - if ( this.attributes[ 'index' ] ) { + if ( attributes.index ) { - var indices = this.attributes[ 'index' ].array; + var indices = attributes.index.array; var offsets = ( this.offsets.length > 0 ? this.offsets : [ { start: 0, count: indices.length, index: 0 } ] ); - for ( j = 0, jl = offsets.length; j < jl; ++ j ) { + for ( var j = 0, jl = offsets.length; j < jl; ++ j ) { 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 ) { - - vA = index + indices[ i ]; - vB = index + indices[ i + 1 ]; - vC = index + indices[ i + 2 ]; - - x = positions[ vA * 3 ]; - y = positions[ vA * 3 + 1 ]; - z = positions[ vA * 3 + 2 ]; - pA.set( x, y, z ); + for ( var i = start, il = start + count; i < il; i += 3 ) { - x = positions[ vB * 3 ]; - y = positions[ vB * 3 + 1 ]; - z = positions[ vB * 3 + 2 ]; - pB.set( x, y, z ); + vA = ( index + indices[ i ] ) * 3; + vB = ( index + indices[ i + 1 ] ) * 3; + vC = ( index + indices[ i + 2 ] ) * 3; - x = positions[ vC * 3 ]; - y = positions[ vC * 3 + 1 ]; - z = positions[ vC * 3 + 2 ]; - pC.set( x, y, z ); + pA.fromArray( positions, vA ); + pB.fromArray( positions, vB ); + pC.fromArray( positions, vC ); cb.subVectors( pC, pB ); ab.subVectors( pA, pB ); cb.cross( ab ); - normals[ vA * 3 ] += cb.x; - normals[ vA * 3 + 1 ] += cb.y; - normals[ vA * 3 + 2 ] += cb.z; + normals[ vA ] += cb.x; + normals[ vA + 1 ] += cb.y; + normals[ vA + 2 ] += cb.z; - normals[ vB * 3 ] += cb.x; - normals[ vB * 3 + 1 ] += cb.y; - normals[ vB * 3 + 2 ] += cb.z; + normals[ vB ] += cb.x; + normals[ vB + 1 ] += cb.y; + normals[ vB + 2 ] += cb.z; - normals[ vC * 3 ] += cb.x; - normals[ vC * 3 + 1 ] += cb.y; - normals[ vC * 3 + 2 ] += cb.z; + normals[ vC ] += cb.x; + normals[ vC + 1 ] += cb.y; + normals[ vC + 2 ] += cb.z; } } - // non-indexed elements (unconnected triangle soup) - } else { - for ( i = 0, il = positions.length; i < il; i += 9 ) { - - x = positions[ i ]; - y = positions[ i + 1 ]; - z = positions[ i + 2 ]; - pA.set( x, y, z ); + // non-indexed elements (unconnected triangle soup) - x = positions[ i + 3 ]; - y = positions[ i + 4 ]; - z = positions[ i + 5 ]; - pB.set( x, y, z ); + for ( var i = 0, il = positions.length; i < il; i += 9 ) { - x = positions[ i + 6 ]; - y = positions[ i + 7 ]; - z = positions[ i + 8 ]; - pC.set( x, y, z ); + pA.fromArray( positions, i ); + pB.fromArray( positions, i + 3 ); + pC.fromArray( positions, i + 6 ); cb.subVectors( pC, pB ); ab.subVectors( pA, pB ); @@ -9310,7 +8757,7 @@ THREE.BufferGeometry.prototype = { this.normalizeNormals(); - this.normalsNeedUpdate = true; + attributes.normal.needsUpdate = true; } @@ -9321,37 +8768,30 @@ THREE.BufferGeometry.prototype = { // based on http://www.terathon.com/code/tangent.html // (per vertex tangents) - if ( this.attributes[ 'index' ] === undefined || - this.attributes[ 'position' ] === undefined || - this.attributes[ 'normal' ] === undefined || - this.attributes[ 'uv' ] === undefined ) { + if ( this.attributes.index === undefined || + this.attributes.position === undefined || + this.attributes.normal === undefined || + this.attributes.uv === undefined ) { console.warn( 'Missing required attributes (index, position, normal or uv) in BufferGeometry.computeTangents()' ); return; } - var indices = this.attributes[ 'index' ].array; - var positions = this.attributes[ 'position' ].array; - var normals = this.attributes[ 'normal' ].array; - var uvs = this.attributes[ 'uv' ].array; + var indices = this.attributes.index.array; + var positions = this.attributes.position.array; + var normals = this.attributes.normal.array; + var uvs = this.attributes.uv.array; var nVertices = positions.length / 3; - if ( this.attributes[ 'tangent' ] === undefined ) { + if ( this.attributes.tangent === undefined ) { - var nTangentElements = 4 * nVertices; + this.addAttribute( 'tangent', new THREE.BufferAttribute( new Float32Array( 4 * nVertices ), 4 ) ); - this.attributes[ 'tangent' ] = { + } - itemSize: 4, - array: new Float32Array( nTangentElements ) - - }; - - } - - var tangents = this.attributes[ 'tangent' ].array; + var tangents = this.attributes.tangent.array; var tan1 = [], tan2 = []; @@ -9362,13 +8802,13 @@ THREE.BufferGeometry.prototype = { } - var xA, yA, zA, - xB, yB, zB, - xC, yC, zC, + var vA = new THREE.Vector3(), + vB = new THREE.Vector3(), + vC = new THREE.Vector3(), - uA, vA, - uB, vB, - uC, vC, + uvA = new THREE.Vector2(), + uvB = new THREE.Vector2(), + uvC = new THREE.Vector2(), x1, x2, y1, y2, z1, z2, s1, s2, t1, t2, r; @@ -9377,41 +8817,28 @@ THREE.BufferGeometry.prototype = { function handleTriangle( a, b, c ) { - xA = positions[ a * 3 ]; - yA = positions[ a * 3 + 1 ]; - zA = positions[ a * 3 + 2 ]; - - xB = positions[ b * 3 ]; - yB = positions[ b * 3 + 1 ]; - zB = positions[ b * 3 + 2 ]; + vA.fromArray( positions, a * 3 ); + vB.fromArray( positions, b * 3 ); + vC.fromArray( positions, c * 3 ); - xC = positions[ c * 3 ]; - yC = positions[ c * 3 + 1 ]; - zC = positions[ c * 3 + 2 ]; + uvA.fromArray( uvs, a * 2 ); + uvB.fromArray( uvs, b * 2 ); + uvC.fromArray( uvs, c * 2 ); - 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 ]; - - x1 = xB - xA; - x2 = xC - xA; + x1 = vB.x - vA.x; + x2 = vC.x - vA.x; - y1 = yB - yA; - y2 = yC - yA; + y1 = vB.y - vA.y; + y2 = vC.y - vA.y; - z1 = zB - zA; - z2 = zC - zA; + z1 = vB.z - vA.z; + z2 = vC.z - vA.z; - s1 = uB - uA; - s2 = uC - uA; + s1 = uvB.x - uvA.x; + s2 = uvC.x - uvA.x; - t1 = vB - vA; - t2 = vC - vA; + t1 = uvB.y - uvA.y; + t2 = uvC.y - uvA.y; r = 1.0 / ( s1 * t2 - s2 * t1 ); @@ -9441,13 +8868,19 @@ THREE.BufferGeometry.prototype = { var j, jl; var iA, iB, iC; - var offsets = this.offsets; + if ( this.drawcalls.length === 0 ) { + + this.addDrawCall( 0, indices.length, 0 ); + + } - for ( j = 0, jl = offsets.length; j < jl; ++ j ) { + var drawcalls = this.drawcalls; - var start = offsets[ j ].start; - var count = offsets[ j ].count; - var index = offsets[ j ].index; + for ( j = 0, jl = drawcalls.length; j < jl; ++ j ) { + + var start = drawcalls[ j ].start; + var count = drawcalls[ j ].count; + var index = drawcalls[ j ].index; for ( i = start, il = start + count; i < il; i += 3 ) { @@ -9467,10 +8900,7 @@ THREE.BufferGeometry.prototype = { function handleVertex( v ) { - n.x = normals[ v * 3 ]; - n.y = normals[ v * 3 + 1 ]; - n.z = normals[ v * 3 + 2 ]; - + n.fromArray( normals, v * 3 ); n2.copy( n ); t = tan1[ v ]; @@ -9493,11 +8923,11 @@ THREE.BufferGeometry.prototype = { } - for ( j = 0, jl = offsets.length; j < jl; ++ j ) { + for ( j = 0, jl = drawcalls.length; j < jl; ++ j ) { - var start = offsets[ j ].start; - var count = offsets[ j ].count; - var index = offsets[ j ].index; + var start = drawcalls[ j ].start; + var count = drawcalls[ j ].count; + var index = drawcalls[ j ].index; for ( i = start, il = start + count; i < il; i += 3 ) { @@ -9530,8 +8960,8 @@ THREE.BufferGeometry.prototype = { var s = Date.now(); - var indices = this.attributes[ 'index' ].array; - var vertices = this.attributes[ 'position' ].array; + var indices = this.attributes.index.array; + var vertices = this.attributes.position.array; var verticesCount = ( vertices.length / 3 ); var facesCount = ( indices.length / 3 ); @@ -9634,7 +9064,7 @@ THREE.BufferGeometry.prototype = { normalizeNormals: function () { - var normals = this.attributes[ 'normal' ].array; + var normals = this.attributes.normal.array; var x, y, z, n; @@ -9665,18 +9095,11 @@ THREE.BufferGeometry.prototype = { /* 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; - } - } + sortedAttributes[ attr ] = new sourceArray.constructor( this.attributes[ attr ].itemSize * vertexCount ); } /* Move attribute positions based on the new index map */ @@ -9703,38 +9126,72 @@ THREE.BufferGeometry.prototype = { } }, - clone: function () { + toJSON: function () { - var geometry = new THREE.BufferGeometry(); + var output = { + metadata: { + version: 4.0, + type: 'BufferGeometry', + generator: 'BufferGeometryExporter' + }, + uuid: this.uuid, + type: this.type, + data: { + attributes: {} + } + }; - var types = [ Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array ]; + var attributes = this.attributes; + var offsets = this.offsets; + var boundingSphere = this.boundingSphere; - for ( var attr in this.attributes ) { + for ( var key in attributes ) { - var sourceAttr = this.attributes[ attr ]; - var sourceArray = sourceAttr.array; + var attribute = attributes[ key ]; - var attribute = { + var array = [], typeArray = attribute.array; - itemSize: sourceAttr.itemSize, - array: null + for ( var i = 0, l = typeArray.length; i < l; i ++ ) { - }; + array[ i ] = typeArray[ i ]; - for ( var i = 0, il = types.length; i < il; i ++ ) { + } - var type = types[ i ]; + output.data.attributes[ key ] = { + itemSize: attribute.itemSize, + type: attribute.array.constructor.name, + array: array + } - if ( sourceArray instanceof type ) { + } - attribute.array = new type( sourceArray ); - break; + if ( offsets.length > 0 ) { - } + output.data.offsets = JSON.parse( JSON.stringify( offsets ) ); + } + + if ( boundingSphere !== null ) { + + output.data.boundingSphere = { + center: boundingSphere.center.toArray(), + radius: boundingSphere.radius } - geometry.attributes[ attr ] = attribute; + } + + return output; + + }, + + clone: function () { + + var geometry = new THREE.BufferGeometry(); + + for ( var attr in this.attributes ) { + + var sourceAttr = this.attributes[ attr ]; + geometry.addAttribute( attr, sourceAttr.clone() ); } @@ -9779,10 +9236,12 @@ THREE.EventDispatcher.prototype.apply( THREE.BufferGeometry.prototype ); THREE.Geometry = function () { - this.id = THREE.GeometryIdCount ++; + Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } ); + this.uuid = THREE.Math.generateUUID(); this.name = ''; + this.type = 'Geometry'; this.vertices = []; this.colors = []; // one-to-one vertex colors, used in Points and Line @@ -9817,7 +9276,6 @@ THREE.Geometry = function () { this.colorsNeedUpdate = false; this.lineDistancesNeedUpdate = false; - this.buffersNeedUpdate = false; this.groupsNeedUpdate = false; }; @@ -9864,6 +9322,91 @@ THREE.Geometry.prototype = { }, + fromBufferGeometry: function ( geometry ) { + + var scope = this; + + 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; + + var tempNormals = []; + var tempUVs = []; + + for ( var i = 0, j = 0; i < vertices.length; i += 3, j += 2 ) { + + scope.vertices.push( new THREE.Vector3( vertices[ i ], vertices[ i + 1 ], vertices[ i + 2 ] ) ); + + if ( normals !== undefined ) { + + tempNormals.push( new THREE.Vector3( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ) ); + + } + + if ( colors !== undefined ) { + + scope.colors.push( new THREE.Color( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ) ); + + } + + if ( uvs !== undefined ) { + + tempUVs.push( new THREE.Vector2( uvs[ j ], uvs[ j + 1 ] ) ); + + } + + } + + var addFace = function ( a, b, c ) { + + 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() ] : []; + + scope.faces.push( new THREE.Face3( a, b, c, vertexNormals, vertexColors ) ); + scope.faceVertexUvs[ 0 ].push( [ tempUVs[ a ], tempUVs[ b ], tempUVs[ c ] ] ); + + }; + + if ( indices !== undefined ) { + + for ( var i = 0; i < indices.length; i += 3 ) { + + addFace( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] ); + + } + + } else { + + for ( var i = 0; i < vertices.length / 3; i += 3 ) { + + addFace( i, i + 1, i + 2 ); + + } + + } + + this.computeFaceNormals(); + + if ( geometry.boundingBox !== null ) { + + this.boundingBox = geometry.boundingBox.clone(); + + } + + if ( geometry.boundingSphere !== null ) { + + this.boundingSphere = geometry.boundingSphere.clone(); + + } + + return this; + + }, + center: function () { this.computeBoundingBox(); @@ -10251,7 +9794,6 @@ THREE.Geometry.prototype = { var normalMatrix, vertexOffset = this.vertices.length, - uvPosition = this.faceVertexUvs[ 0 ].length, vertices1 = this.vertices, vertices2 = geometry.vertices, faces1 = this.faces, @@ -10440,66 +9982,206 @@ THREE.Geometry.prototype = { }, - // Geometry splitting + toJSON: function () { - makeGroups: ( function () { + var output = { + metadata: { + version: 4.0, + type: 'BufferGeometry', + generator: 'BufferGeometryExporter' + }, + uuid: this.uuid, + type: this.type + }; - var geometryGroupCounter = 0; + if ( this.name !== "" ) output.name = this.name; - return function ( usesFaceMaterial, maxVerticesInGroup ) { + if ( this.parameters !== undefined ) { - var f, fl, face, materialIndex, - groupHash, hash_map = {},geometryGroup; + var parameters = this.parameters; - var numMorphTargets = this.morphTargets.length; - var numMorphNormals = this.morphNormals.length; + for ( var key in parameters ) { - this.geometryGroups = {}; - this.geometryGroupsList = []; + if ( parameters[ key ] !== undefined ) output[ key ] = parameters[ key ]; - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + } - face = this.faces[ f ]; - materialIndex = usesFaceMaterial ? face.materialIndex : 0; + return output; - if ( ! ( materialIndex in hash_map ) ) { + } - hash_map[ materialIndex ] = { 'hash': materialIndex, 'counter': 0 }; + var vertices = []; - } + for ( var i = 0; i < this.vertices.length; i ++ ) { - groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter; + var vertex = this.vertices[ i ]; + vertices.push( vertex.x, vertex.y, vertex.z ); - if ( ! ( groupHash in this.geometryGroups ) ) { + } - geometryGroup = { 'id': geometryGroupCounter++, 'faces3': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets, 'numMorphNormals': numMorphNormals }; - this.geometryGroups[ groupHash ] = geometryGroup; - this.geometryGroupsList.push(geometryGroup); - } + var faces = []; + var normals = []; + var normalsHash = {}; + var colors = []; + var colorsHash = {}; + var uvs = []; + var uvsHash = {}; - if ( this.geometryGroups[ groupHash ].vertices + 3 > maxVerticesInGroup ) { + for ( var i = 0; i < this.faces.length; i ++ ) { - hash_map[ materialIndex ].counter += 1; - groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter; + var face = this.faces[ i ]; - if ( ! ( groupHash in this.geometryGroups ) ) { + 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; - geometryGroup = { 'id': geometryGroupCounter++, 'faces3': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets, 'numMorphNormals': numMorphNormals }; - this.geometryGroups[ groupHash ] = geometryGroup; - this.geometryGroupsList.push(geometryGroup); - - } + 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 ); + + + /* + if ( hasMaterial ) { - this.geometryGroups[ groupHash ].faces3.push( f ); - this.geometryGroups[ groupHash ].vertices += 3; + faces.push( face.materialIndex ); } + */ - }; + if ( hasFaceVertexUv ) { - } )(), + var faceVertexUvs = this.faceVertexUvs[ 0 ][ i ]; + + faces.push( + getUvIndex( faceVertexUvs[ 0 ] ), + getUvIndex( faceVertexUvs[ 1 ] ), + getUvIndex( faceVertexUvs[ 2 ] ) + ); + + } + + if ( hasFaceNormal ) { + + faces.push( getNormalIndex( face.normal ) ); + + } + + if ( hasFaceVertexNormal ) { + + var vertexNormals = face.vertexNormals; + + faces.push( + getNormalIndex( vertexNormals[ 0 ] ), + getNormalIndex( vertexNormals[ 1 ] ), + getNormalIndex( vertexNormals[ 2 ] ) + ); + + } + + if ( hasFaceColor ) { + + faces.push( getColorIndex( face.color ) ); + + } + + if ( hasFaceVertexColor ) { + + var vertexColors = face.vertexColors; + + faces.push( + getColorIndex( vertexColors[ 0 ] ), + getColorIndex( vertexColors[ 1 ] ), + getColorIndex( vertexColors[ 2 ] ) + ); + + } + + } + + function setBit( value, position, enabled ) { + + return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position) ); + + } + + function getNormalIndex( normal ) { + + var hash = normal.x.toString() + normal.y.toString() + normal.z.toString(); + + if ( normalsHash[ hash ] !== undefined ) { + + return normalsHash[ hash ]; + + } + + normalsHash[ hash ] = normals.length / 3; + normals.push( normal.x, normal.y, normal.z ); + + return normalsHash[ hash ]; + + } + + function getColorIndex( color ) { + + var hash = color.r.toString() + color.g.toString() + color.b.toString(); + + if ( colorsHash[ hash ] !== undefined ) { + + return colorsHash[ hash ]; + + } + + colorsHash[ hash ] = colors.length; + colors.push( color.getHex() ); + + return colorsHash[ hash ]; + + } + + function getUvIndex( uv ) { + + var hash = uv.x.toString() + uv.y.toString(); + + if ( uvsHash[ hash ] !== undefined ) { + + return uvsHash[ hash ]; + + } + + uvsHash[ hash ] = uvs.length / 2; + uvs.push( uv.x, uv.y ); + + return uvsHash[ hash ]; + + } + + output.data = {}; + + 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; + + // + + return output; + + }, clone: function () { @@ -10565,6 +10247,8 @@ THREE.Camera = function () { THREE.Object3D.call( this ); + this.type = 'Camera'; + this.matrixWorldInverse = new THREE.Matrix4(); this.projectionMatrix = new THREE.Matrix4(); @@ -10572,6 +10256,22 @@ THREE.Camera = function () { THREE.Camera.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Camera.prototype.getWorldDirection = function () { + + var quaternion = new THREE.Quaternion(); + + return function ( optionalTarget ) { + + var result = optionalTarget || new THREE.Vector3(); + + this.getWorldQuaternion( quaternion ); + + return result.set( 0, 0, - 1 ).applyQuaternion( quaternion ); + + } + +}(); + THREE.Camera.prototype.lookAt = function () { // This routine does not support cameras with rotated and/or translated parent(s) @@ -10613,6 +10313,8 @@ THREE.CubeCamera = function ( near, far, cubeResolution ) { THREE.Object3D.call( this ); + this.type = 'CubeCamera'; + var fov = 90, aspect = 1; var cameraPX = new THREE.PerspectiveCamera( fov, aspect, near, far ); @@ -10690,6 +10392,10 @@ THREE.OrthographicCamera = function ( left, right, top, bottom, near, far ) { THREE.Camera.call( this ); + this.type = 'OrthographicCamera'; + + this.zoom = 1; + this.left = left; this.right = right; this.top = top; @@ -10706,7 +10412,12 @@ THREE.OrthographicCamera.prototype = Object.create( THREE.Camera.prototype ); THREE.OrthographicCamera.prototype.updateProjectionMatrix = function () { - this.projectionMatrix.makeOrthographic( this.left, this.right, this.top, this.bottom, this.near, this.far ); + 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; + + this.projectionMatrix.makeOrthographic( cx - dx, cx + dx, cy + dy, cy - dy, this.near, this.far ); }; @@ -10716,6 +10427,8 @@ THREE.OrthographicCamera.prototype.clone = function () { THREE.Camera.prototype.clone.call( this, camera ); + camera.zoom = this.zoom; + camera.left = this.left; camera.right = this.right; camera.top = this.top; @@ -10724,6 +10437,8 @@ THREE.OrthographicCamera.prototype.clone = function () { camera.near = this.near; camera.far = this.far; + camera.projectionMatrix.copy( this.projectionMatrix ); + return camera; }; @@ -10739,6 +10454,10 @@ THREE.PerspectiveCamera = function ( fov, aspect, near, far ) { THREE.Camera.call( this ); + this.type = 'PerspectiveCamera'; + + this.zoom = 1; + this.fov = fov !== undefined ? fov : 50; this.aspect = aspect !== undefined ? aspect : 1; this.near = near !== undefined ? near : 0.1; @@ -10819,10 +10538,12 @@ THREE.PerspectiveCamera.prototype.setViewOffset = function ( fullWidth, fullHeig THREE.PerspectiveCamera.prototype.updateProjectionMatrix = function () { + var fov = THREE.Math.radToDeg( 2 * Math.atan( Math.tan( THREE.Math.degToRad( this.fov ) * 0.5 ) / this.zoom ) ); + if ( this.fullWidth ) { var aspect = this.fullWidth / this.fullHeight; - var top = Math.tan( THREE.Math.degToRad( this.fov * 0.5 ) ) * this.near; + var top = Math.tan( THREE.Math.degToRad( fov * 0.5 ) ) * this.near; var bottom = - top; var left = aspect * bottom; var right = aspect * top; @@ -10840,7 +10561,7 @@ THREE.PerspectiveCamera.prototype.updateProjectionMatrix = function () { } else { - this.projectionMatrix.makePerspective( this.fov, this.aspect, this.near, this.far ); + this.projectionMatrix.makePerspective( fov, this.aspect, this.near, this.far ); } @@ -10852,12 +10573,17 @@ THREE.PerspectiveCamera.prototype.clone = function () { 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; + camera.projectionMatrix.copy( this.projectionMatrix ); + return camera; + }; // File:src/lights/Light.js @@ -10871,6 +10597,8 @@ THREE.Light = function ( color ) { THREE.Object3D.call( this ); + this.type = 'Light'; + this.color = new THREE.Color( color ); }; @@ -10899,6 +10627,8 @@ THREE.AmbientLight = function ( color ) { THREE.Light.call( this, color ); + this.type = 'AmbientLight'; + }; THREE.AmbientLight.prototype = Object.create( THREE.Light.prototype ); @@ -10924,6 +10654,8 @@ THREE.AreaLight = function ( color, intensity ) { THREE.Light.call( this, color ); + this.type = 'AreaLight'; + this.normal = new THREE.Vector3( 0, - 1, 0 ); this.right = new THREE.Vector3( 1, 0, 0 ); @@ -10952,6 +10684,8 @@ THREE.DirectionalLight = function ( color, intensity ) { THREE.Light.call( this, color ); + this.type = 'DirectionalLight'; + this.position.set( 0, 1, 0 ); this.target = new THREE.Object3D(); @@ -11064,6 +10798,8 @@ THREE.HemisphereLight = function ( skyColor, groundColor, intensity ) { THREE.Light.call( this, skyColor ); + this.type = 'HemisphereLight'; + this.position.set( 0, 100, 0 ); this.groundColor = new THREE.Color( groundColor ); @@ -11096,6 +10832,8 @@ THREE.PointLight = function ( color, intensity, distance ) { THREE.Light.call( this, color ); + this.type = 'PointLight'; + this.intensity = ( intensity !== undefined ) ? intensity : 1; this.distance = ( distance !== undefined ) ? distance : 0; @@ -11126,6 +10864,8 @@ THREE.SpotLight = function ( color, intensity, distance, angle, exponent ) { THREE.Light.call( this, color ); + this.type = 'SpotLight'; + this.position.set( 0, 1, 0 ); this.target = new THREE.Object3D(); @@ -12527,11 +12267,9 @@ THREE.BufferGeometryLoader.prototype = { for ( var key in attributes ) { var attribute = attributes[ key ]; + var typedArray = new self[ attribute.type ]( attribute.array ); - geometry.attributes[ key ] = { - itemSize: attribute.itemSize, - array: new self[ attribute.type ]( attribute.array ) - } + geometry.addAttribute( key, new THREE.BufferAttribute( typedArray, attribute.itemSize ) ); } @@ -12547,10 +12285,15 @@ THREE.BufferGeometryLoader.prototype = { if ( boundingSphere !== undefined ) { - geometry.boundingSphere = new THREE.Sphere( - new THREE.Vector3().fromArray( boundingSphere.center !== undefined ? boundingSphere.center : [ 0, 0, 0 ] ), - boundingSphere.radius - ); + var center = new THREE.Vector3(); + + if ( boundingSphere.center !== undefined ) { + + center.fromArray( boundingSphere.center ); + + } + + geometry.boundingSphere = new THREE.Sphere( center, boundingSphere.radius ); } @@ -12609,6 +12352,7 @@ THREE.MaterialLoader.prototype = { 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; @@ -12909,13 +12653,13 @@ THREE.ObjectLoader.prototype = { if ( geometry === undefined ) { - console.error( 'THREE.ObjectLoader: Undefined geometry ' + data.geometry ); + console.warn( 'THREE.ObjectLoader: Undefined geometry', data.geometry ); } if ( material === undefined ) { - console.error( 'THREE.ObjectLoader: Undefined material ' + data.material ); + console.warn( 'THREE.ObjectLoader: Undefined material', data.material ); } @@ -12923,13 +12667,34 @@ THREE.ObjectLoader.prototype = { break; + case 'Line': + + var geometry = geometries[ data.geometry ]; + var material = materials[ data.material ]; + + if ( geometry === undefined ) { + + console.warn( 'THREE.ObjectLoader: Undefined geometry', data.geometry ); + + } + + if ( material === undefined ) { + + console.warn( 'THREE.ObjectLoader: Undefined material', data.material ); + + } + + object = new THREE.Line( geometry, material ); + + break; + case 'Sprite': var material = materials[ data.material ]; if ( material === undefined ) { - console.error( 'THREE.ObjectLoader: Undefined material ' + data.material ); + console.warn( 'THREE.ObjectLoader: Undefined material', data.material ); } @@ -12937,6 +12702,12 @@ THREE.ObjectLoader.prototype = { break; + case 'Group': + + object = new THREE.Group(); + + break; + default: object = new THREE.Object3D(); @@ -13025,6 +12796,135 @@ THREE.TextureLoader.prototype = { }; +// File:src/loaders/CompressedTextureLoader.js + +/** + * @author mrdoob / http://mrdoob.com/ + * + * Abstract Base class to block based textures loader (dds, pvr, ...) + */ + +THREE.CompressedTextureLoader = function () { + + // override in sub classes + this._parser = null; + +}; + + +THREE.CompressedTextureLoader.prototype = { + + constructor: THREE.CompressedTextureLoader, + + load: function ( url, onLoad, onError ) { + + var scope = this; + + var images = []; + + var texture = new THREE.CompressedTexture(); + texture.image = images; + + var loader = new THREE.XHRLoader(); + loader.setResponseType( 'arraybuffer' ); + + if ( url instanceof Array ) { + + var loaded = 0; + + var loadTexture = function ( i ) { + + loader.load( url[ i ], function ( buffer ) { + + var texDatas = scope._parser( buffer, true ); + + images[ i ] = { + width: texDatas.width, + height: texDatas.height, + format: texDatas.format, + mipmaps: texDatas.mipmaps + }; + + loaded += 1; + + if ( loaded === 6 ) { + + if (texDatas.mipmapCount == 1) + texture.minFilter = THREE.LinearFilter; + + texture.format = texDatas.format; + texture.needsUpdate = true; + + if ( onLoad ) onLoad( texture ); + + } + + } ); + + }; + + for ( var i = 0, il = url.length; i < il; ++ i ) { + + loadTexture( i ); + + } + + } else { + + // compressed cubemap texture stored in a single DDS file + + loader.load( url, function ( buffer ) { + + var texDatas = scope._parser( buffer, true ); + + if ( texDatas.isCubemap ) { + + var faces = texDatas.mipmaps.length / texDatas.mipmapCount; + + for ( var f = 0; f < faces; f ++ ) { + + images[ f ] = { mipmaps : [] }; + + for ( var i = 0; i < texDatas.mipmapCount; i ++ ) { + + 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; + + } + + } + + } else { + + texture.image.width = texDatas.width; + texture.image.height = texDatas.height; + texture.mipmaps = texDatas.mipmaps; + + } + + if ( texDatas.mipmapCount === 1 ) { + + texture.minFilter = THREE.LinearFilter; + + } + + texture.format = texDatas.format; + texture.needsUpdate = true; + + if ( onLoad ) onLoad( texture ); + + } ); + + } + + return texture; + + } + +}; + // File:src/materials/Material.js /** @@ -13034,10 +12934,12 @@ THREE.TextureLoader.prototype = { THREE.Material = function () { - this.id = THREE.MaterialIdCount ++; + Object.defineProperty( this, 'id', { value: THREE.MaterialIdCount ++ } ); + this.uuid = THREE.Math.generateUUID(); this.name = ''; + this.type = 'Material'; this.side = THREE.FrontSide; @@ -13115,6 +13017,78 @@ THREE.Material.prototype = { }, + toJSON: function () { + + var output = { + metadata: { + version: 4.2, + type: 'material', + generator: 'MaterialExporter' + }, + uuid: this.uuid, + type: this.type + }; + + if ( this.name !== "" ) output.name = this.name; + + if ( this instanceof THREE.MeshBasicMaterial ) { + + 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; + + } else if ( this instanceof THREE.MeshPhongMaterial ) { + + 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; + + } else if ( this instanceof THREE.MeshNormalMaterial ) { + + 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; + + } else if ( this instanceof THREE.MeshDepthMaterial ) { + + if ( this.blending !== THREE.NormalBlending ) output.blending = this.blending; + if ( this.side !== THREE.FrontSide ) output.side = this.side; + + } else if ( this instanceof THREE.ShaderMaterial ) { + + output.uniforms = this.uniforms; + output.vertexShader = this.vertexShader; + output.fragmentShader = this.fragmentShader; + + } else if ( this instanceof THREE.SpriteMaterial ) { + + output.color = this.color.getHex(); + + } + + if ( this.opacity < 1 ) output.opacity = this.opacity; + if ( this.transparent !== false ) output.transparent = this.transparent; + if ( this.wireframe !== false ) output.wireframe = this.wireframe; + + return output; + + }, + clone: function ( material ) { if ( material === undefined ) material = new THREE.Material(); @@ -13189,6 +13163,8 @@ THREE.LineBasicMaterial = function ( parameters ) { THREE.Material.call( this ); + this.type = 'LineBasicMaterial'; + this.color = new THREE.Color( 0xffffff ); this.linewidth = 1; @@ -13254,6 +13230,8 @@ THREE.LineDashedMaterial = function ( parameters ) { THREE.Material.call( this ); + this.type = 'LineDashedMaterial'; + this.color = new THREE.Color( 0xffffff ); this.linewidth = 1; @@ -13337,6 +13315,8 @@ THREE.MeshBasicMaterial = function ( parameters ) { THREE.Material.call( this ); + this.type = 'MeshBasicMaterial'; + this.color = new THREE.Color( 0xffffff ); // emissive this.map = null; @@ -13458,154 +13438,11 @@ THREE.MeshLambertMaterial = function ( parameters ) { THREE.Material.call( this ); - this.color = new THREE.Color( 0xffffff ); // diffuse - this.ambient = new THREE.Color( 0xffffff ); - this.emissive = new THREE.Color( 0x000000 ); - - this.wrapAround = false; - this.wrapRGB = new THREE.Vector3( 1, 1, 1 ); - - this.map = null; - - this.lightMap = null; - - this.specularMap = null; - - this.alphaMap = null; - - this.envMap = null; - this.combine = THREE.MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; - - this.fog = true; - - this.shading = THREE.SmoothShading; - - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; - - this.vertexColors = THREE.NoColors; - - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; - - this.setValues( parameters ); - -}; - -THREE.MeshLambertMaterial.prototype = Object.create( THREE.Material.prototype ); - -THREE.MeshLambertMaterial.prototype.clone = function () { - - var material = new THREE.MeshLambertMaterial(); - - THREE.Material.prototype.clone.call( this, material ); - - material.color.copy( this.color ); - material.ambient.copy( this.ambient ); - material.emissive.copy( this.emissive ); - - material.wrapAround = this.wrapAround; - material.wrapRGB.copy( this.wrapRGB ); - - material.map = this.map; - - 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; - - 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; - - return material; - -}; - -// File:src/materials/MeshPhongMaterial.js - -/** - * @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: - * } - */ - -THREE.MeshPhongMaterial = function ( parameters ) { - - THREE.Material.call( this ); + this.type = 'MeshLambertMaterial'; 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; - - this.metal = false; this.wrapAround = false; this.wrapRGB = new THREE.Vector3( 1, 1, 1 ); @@ -13614,12 +13451,159 @@ THREE.MeshPhongMaterial = function ( parameters ) { this.lightMap = null; - this.bumpMap = null; - this.bumpScale = 1; - - 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; + + this.fog = true; + + this.shading = THREE.SmoothShading; + + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; + + this.vertexColors = THREE.NoColors; + + this.skinning = false; + this.morphTargets = false; + this.morphNormals = false; + + this.setValues( parameters ); + +}; + +THREE.MeshLambertMaterial.prototype = Object.create( THREE.Material.prototype ); + +THREE.MeshLambertMaterial.prototype.clone = function () { + + var material = new THREE.MeshLambertMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.color.copy( this.color ); + material.ambient.copy( this.ambient ); + material.emissive.copy( this.emissive ); + + material.wrapAround = this.wrapAround; + material.wrapRGB.copy( this.wrapRGB ); + + material.map = this.map; + + 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; + + 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; + + return material; + +}; + +// File:src/materials/MeshPhongMaterial.js + +/** + * @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: + * } + */ + +THREE.MeshPhongMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + 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; + + this.metal = false; + + this.wrapAround = false; + this.wrapRGB = new THREE.Vector3( 1, 1, 1 ); + + this.map = null; + + this.lightMap = null; + + this.bumpMap = null; + this.bumpScale = 1; + + this.normalMap = null; + this.normalScale = new THREE.Vector2( 1, 1 ); + this.specularMap = null; this.alphaMap = null; @@ -13727,6 +13711,8 @@ THREE.MeshDepthMaterial = function ( parameters ) { THREE.Material.call( this ); + this.type = 'MeshDepthMaterial'; + this.morphTargets = false; this.wireframe = false; this.wireframeLinewidth = 1; @@ -13772,6 +13758,8 @@ THREE.MeshNormalMaterial = function ( parameters ) { THREE.Material.call( this, parameters ); + this.type = 'MeshNormalMaterial'; + this.shading = THREE.FlatShading; this.wireframe = false; @@ -13808,21 +13796,54 @@ THREE.MeshNormalMaterial.prototype.clone = function () { THREE.MeshFaceMaterial = function ( materials ) { + this.uuid = THREE.Math.generateUUID(); + + this.type = 'MeshFaceMaterial'; + this.materials = materials instanceof Array ? materials : []; }; -THREE.MeshFaceMaterial.prototype.clone = function () { +THREE.MeshFaceMaterial.prototype = { - var material = new THREE.MeshFaceMaterial(); + constructor: THREE.MeshFaceMaterial, - for ( var i = 0; i < this.materials.length; i ++ ) { + toJSON: function () { - material.materials.push( this.materials[ i ].clone() ); + var output = { + metadata: { + version: 4.2, + type: 'material', + generator: 'MaterialExporter' + }, + uuid: this.uuid, + type: this.type, + materials: [] + }; - } + for ( var i = 0, l = this.materials.length; i < l; i ++ ) { - return material; + output.materials.push( this.materials[ i ].toJSON() ); + + } + + return output; + + }, + + clone: function () { + + var material = new THREE.MeshFaceMaterial(); + + for ( var i = 0; i < this.materials.length; i ++ ) { + + material.materials.push( this.materials[ i ].clone() ); + + } + + return material; + + } }; @@ -13853,6 +13874,8 @@ THREE.PointCloudMaterial = function ( parameters ) { THREE.Material.call( this ); + this.type = 'PointCloudMaterial'; + this.color = new THREE.Color( 0xffffff ); this.map = null; @@ -13943,6 +13966,8 @@ THREE.ShaderMaterial = function ( parameters ) { THREE.Material.call( this ); + this.type = 'ShaderMaterial'; + this.defines = {}; this.uniforms = {}; this.attributes = null; @@ -14028,6 +14053,8 @@ THREE.RawShaderMaterial = function ( parameters ) { THREE.ShaderMaterial.call( this, parameters ); + this.type = 'RawShaderMaterial'; + }; THREE.RawShaderMaterial.prototype = Object.create( THREE.ShaderMaterial.prototype ); @@ -14067,7 +14094,7 @@ THREE.SpriteMaterial = function ( parameters ) { THREE.Material.call( this ); - // defaults + this.type = 'SpriteMaterial'; this.color = new THREE.Color( 0xffffff ); this.map = null; @@ -14101,49 +14128,6 @@ THREE.SpriteMaterial.prototype.clone = function () { }; -// File:src/materials/SpriteCanvasMaterial.js - -/** - * @author mrdoob / http://mrdoob.com/ - * - * parameters = { - * color: , - * program: , - * opacity: , - * blending: THREE.NormalBlending - * } - */ - -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 ); - -THREE.SpriteCanvasMaterial.prototype.clone = function () { - - var material = new THREE.SpriteCanvasMaterial(); - - THREE.Material.prototype.clone.call( this, material ); - - material.color.copy( this.color ); - material.program = this.program; - - return material; - -}; - -// backwards compatibility - -THREE.ParticleCanvasMaterial = THREE.SpriteCanvasMaterial; - // File:src/textures/Texture.js /** @@ -14154,7 +14138,8 @@ THREE.ParticleCanvasMaterial = THREE.SpriteCanvasMaterial; THREE.Texture = function ( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { - this.id = THREE.TextureIdCount ++; + Object.defineProperty( this, 'id', { value: THREE.TextureIdCount ++ } ); + this.uuid = THREE.Math.generateUUID(); this.name = ''; @@ -14300,7 +14285,15 @@ THREE.CompressedTexture = function ( mipmaps, width, height, format, type, mappi this.image = { width: width, height: height }; this.mipmaps = mipmaps; - this.generateMipmaps = false; // WebGL currently can't generate mipmaps for compressed textures, they must be embedded in DDS file + // no flipping for cube textures + // (also flipping doesn't work for compressed textures ) + + this.flipY = false; + + // can't generate mipmaps for compressed textures + // mips must be embedded in DDS files + + this.generateMipmaps = false; }; @@ -14342,6 +14335,54 @@ THREE.DataTexture.prototype.clone = function () { }; +// File:src/textures/VideoTexture.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.VideoTexture = function ( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { + + THREE.Texture.call( this, video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + + this.generateMipmaps = false; + + var scope = this; + + var update = function () { + + requestAnimationFrame( update ); + + if ( video.readyState === video.HAVE_ENOUGH_DATA ) { + + scope.needsUpdate = true; + + } + + }; + + update(); + +}; + +THREE.VideoTexture.prototype = Object.create( THREE.Texture.prototype ); + +// File:src/objects/Group.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Group = function () { + + THREE.Object3D.call( this ); + + this.type = 'Group'; + +}; + +THREE.Group.prototype = Object.create( THREE.Object3D.prototype ); + // File:src/objects/PointCloud.js /** @@ -14352,6 +14393,8 @@ THREE.PointCloud = function ( geometry, material ) { THREE.Object3D.call( this ); + this.type = 'PointCloud'; + this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); this.material = material !== undefined ? material : new THREE.PointCloudMaterial( { color: Math.random() * 0xffffff } ); @@ -14446,11 +14489,7 @@ THREE.PointCloud.prototype.raycast = ( function () { var a = index + indices[ i ]; - position.set( - positions[ a * 3 ], - positions[ a * 3 + 1 ], - positions[ a * 3 + 2 ] - ); + position.fromArray( positions, a * 3 ); testPoint( position, a ); @@ -14519,14 +14558,16 @@ THREE.ParticleSystem = function ( geometry, material ) { * @author mrdoob / http://mrdoob.com/ */ -THREE.Line = function ( geometry, material, type ) { +THREE.Line = function ( geometry, material, mode ) { THREE.Object3D.call( this ); + this.type = 'Line'; + this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); this.material = material !== undefined ? material : new THREE.LineBasicMaterial( { color: Math.random() * 0xffffff } ); - this.type = ( type !== undefined ) ? type : THREE.LineStrip; + this.mode = ( mode !== undefined ) ? mode : THREE.LineStrip; }; @@ -14572,7 +14613,7 @@ THREE.Line.prototype.raycast = ( function () { var nbVertices = vertices.length; var interSegment = new THREE.Vector3(); var interRay = new THREE.Vector3(); - var step = this.type === THREE.LineStrip ? 1 : 2; + var step = this.mode === THREE.LineStrip ? 1 : 2; for ( var i = 0; i < nbVertices - 1; i = i + step ) { @@ -14606,7 +14647,7 @@ THREE.Line.prototype.raycast = ( function () { THREE.Line.prototype.clone = function ( object ) { - if ( object === undefined ) object = new THREE.Line( this.geometry, this.material, this.type ); + if ( object === undefined ) object = new THREE.Line( this.geometry, this.material, this.mode ); THREE.Object3D.prototype.clone.call( this, object ); @@ -14627,6 +14668,8 @@ 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 } ); @@ -14748,22 +14791,9 @@ THREE.Mesh.prototype.raycast = ( function () { b = index + indices[ i + 1 ]; c = index + indices[ i + 2 ]; - 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 ] - ); - + vA.fromArray( positions, a * 3 ); + vB.fromArray( positions, b * 3 ); + vC.fromArray( positions, c * 3 ); if ( material.side === THREE.BackSide ) { @@ -14787,8 +14817,7 @@ THREE.Mesh.prototype.raycast = ( function () { distance: distance, point: intersectionPoint, - indices: [ a, b, c ], - face: null, + face: new THREE.Face3( a, b, c, THREE.Triangle.normal( vA, vB, vC ) ), faceIndex: null, object: this @@ -14808,22 +14837,9 @@ THREE.Mesh.prototype.raycast = ( function () { b = i + 1; c = i + 2; - 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 ] - ); - + vA.fromArray( positions, j ); + vB.fromArray( positions, j + 3 ); + vC.fromArray( positions, j + 6 ); if ( material.side === THREE.BackSide ) { @@ -14847,8 +14863,7 @@ THREE.Mesh.prototype.raycast = ( function () { distance: distance, point: intersectionPoint, - indices: [ a, b, c ], - face: null, + face: new THREE.Face3( a, b, c, THREE.Triangle.normal( vA, vB, vC ) ), faceIndex: null, object: this @@ -14981,26 +14996,10 @@ THREE.Bone = function ( belongsToSkin ) { this.skin = belongsToSkin; - this.accumulatedRotWeight = 0; - this.accumulatedPosWeight = 0; - this.accumulatedSclWeight = 0; - }; THREE.Bone.prototype = Object.create( THREE.Object3D.prototype ); -THREE.Bone.prototype.updateMatrixWorld = function ( force ) { - - THREE.Object3D.prototype.updateMatrixWorld.call( this, force ); - - // Reset weights to be re-accumulated in the next frame - - this.accumulatedRotWeight = 0; - this.accumulatedPosWeight = 0; - this.accumulatedSclWeight = 0; - -}; - // File:src/objects/Skeleton.js @@ -15142,8 +15141,7 @@ THREE.Skeleton.prototype.pose = function () { bone.matrix.getInverse( bone.parent.matrixWorld ); bone.matrix.multiply( bone.matrixWorld ); - } - else { + } else { bone.matrix.copy( bone.matrixWorld ); @@ -15157,30 +15155,34 @@ THREE.Skeleton.prototype.pose = function () { }; -THREE.Skeleton.prototype.update = function () { +THREE.Skeleton.prototype.update = ( function () { var offsetMatrix = new THREE.Matrix4(); + + return function () { - // flatten bone matrices to array + // flatten bone matrices to array - for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { - // compute the offset between the current and the original transform + // compute the offset between the current and the original transform - var matrix = this.bones[ b ] ? this.bones[ b ].matrixWorld : this.identityMatrix; + var matrix = this.bones[ b ] ? this.bones[ b ].matrixWorld : this.identityMatrix; - offsetMatrix.multiplyMatrices( matrix, this.boneInverses[ b ] ); - offsetMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 ); + offsetMatrix.multiplyMatrices( matrix, this.boneInverses[ b ] ); + offsetMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 ); - } + } - if ( this.useVertexTexture ) { + if ( this.useVertexTexture ) { - this.boneTexture.needsUpdate = true; + this.boneTexture.needsUpdate = true; - } + } + + }; -}; +} )(); // File:src/objects/SkinnedMesh.js @@ -15195,6 +15197,8 @@ THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) { THREE.Mesh.call( this, geometry, material ); + this.type = 'SkinnedMesh'; + this.bindMode = "attached"; this.bindMatrix = new THREE.Matrix4(); this.bindMatrixInverse = new THREE.Matrix4(); @@ -15363,6 +15367,8 @@ THREE.MorphAnimMesh = function ( geometry, material ) { THREE.Mesh.call( this, geometry, material ); + this.type = 'MorphAnimMesh'; + // API this.duration = 1000; // milliseconds @@ -15707,15 +15713,21 @@ THREE.LOD.prototype.clone = function ( object ) { THREE.Sprite = ( function () { - var vertices = new Float32Array( [ - 0.5, - 0.5, 0, 0.5, - 0.5, 0, 0.5, 0.5, 0 ] ); + 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 ] ); 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 ) ); return function ( material ) { THREE.Object3D.call( this ); + this.type = 'Sprite'; + this.geometry = geometry; this.material = ( material !== undefined ) ? material : new THREE.SpriteMaterial(); @@ -15754,14 +15766,6 @@ THREE.Sprite.prototype.raycast = ( function () { }() ); -THREE.Sprite.prototype.updateMatrix = function () { - - this.matrix.compose( this.position, this.quaternion, this.scale ); - - this.matrixWorldNeedsUpdate = true; - -}; - THREE.Sprite.prototype.clone = function ( object ) { if ( object === undefined ) object = new THREE.Sprite( this.material ); @@ -15776,123 +15780,109 @@ THREE.Sprite.prototype.clone = function ( object ) { THREE.Particle = THREE.Sprite; -// File:src/scenes/Scene.js +// File:src/objects/LensFlare.js /** - * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ */ -THREE.Scene = function () { +THREE.LensFlare = function ( texture, size, distance, blending, color ) { THREE.Object3D.call( this ); - this.fog = null; - this.overrideMaterial = null; - - this.autoUpdate = true; // checked by the renderer - this.matrixAutoUpdate = false; - - this.__lights = []; - - this.__objectsAdded = []; - this.__objectsRemoved = []; - -}; + this.lensFlares = []; -THREE.Scene.prototype = Object.create( THREE.Object3D.prototype ); + this.positionScreen = new THREE.Vector3(); + this.customUpdateCallback = undefined; -THREE.Scene.prototype.__addObject = function ( object ) { + if( texture !== undefined ) { - if ( object instanceof THREE.Light ) { + this.add( texture, size, distance, blending, color ); - if ( this.__lights.indexOf( object ) === - 1 ) { + } - this.__lights.push( object ); +}; - } +THREE.LensFlare.prototype = Object.create( THREE.Object3D.prototype ); - if ( object.target && object.target.parent === undefined ) { - this.add( object.target ); +/* + * Add: adds another flare + */ - } +THREE.LensFlare.prototype.add = function ( texture, size, distance, blending, color, opacity ) { - } else if ( ! ( object instanceof THREE.Camera || object instanceof THREE.Bone ) ) { + 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; - this.__objectsAdded.push( object ); + distance = Math.min( distance, Math.max( 0, distance ) ); - // check if previously removed + 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 + } ); - var i = this.__objectsRemoved.indexOf( object ); +}; - if ( i !== - 1 ) { +/* + * 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.__objectsRemoved.splice( i, 1 ); +THREE.LensFlare.prototype.updateLensFlares = function () { - } + 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 ++ ) { - this.dispatchEvent( { type: 'objectAdded', object: object } ); - object.dispatchEvent( { type: 'addedToScene', scene: this } ); + flare = this.lensFlares[ f ]; - for ( var c = 0; c < object.children.length; c ++ ) { + flare.x = this.positionScreen.x + vecX * flare.distance; + flare.y = this.positionScreen.y + vecY * flare.distance; - this.__addObject( object.children[ c ] ); + flare.wantedRotation = flare.x * Math.PI * 0.25; + flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25; } }; -THREE.Scene.prototype.__removeObject = function ( object ) { - - if ( object instanceof THREE.Light ) { - - var i = this.__lights.indexOf( object ); - - if ( i !== - 1 ) { - - this.__lights.splice( i, 1 ); - - } - - if ( object.shadowCascadeArray ) { - - for ( var x = 0; x < object.shadowCascadeArray.length; x ++ ) { - - this.__removeObject( object.shadowCascadeArray[ x ] ); - - } - - } - - } else if ( ! ( object instanceof THREE.Camera ) ) { - - this.__objectsRemoved.push( object ); - - // check if previously added - var i = this.__objectsAdded.indexOf( object ); - - if ( i !== - 1 ) { - - this.__objectsAdded.splice( i, 1 ); +// File:src/scenes/Scene.js - } +/** + * @author mrdoob / http://mrdoob.com/ + */ - } +THREE.Scene = function () { - this.dispatchEvent( { type: 'objectRemoved', object: object } ); - object.dispatchEvent( { type: 'removedFromScene', scene: this } ); + THREE.Object3D.call( this ); - for ( var c = 0; c < object.children.length; c ++ ) { + this.type = 'Scene'; - this.__removeObject( object.children[ c ] ); + this.fog = null; + this.overrideMaterial = null; - } + this.autoUpdate = true; // checked by the renderer }; +THREE.Scene.prototype = Object.create( THREE.Object3D.prototype ); + THREE.Scene.prototype.clone = function ( object ) { if ( object === undefined ) object = new THREE.Scene(); @@ -15955,1689 +15945,1644 @@ THREE.FogExp2.prototype.clone = function () { }; -// File:src/renderers/CanvasRenderer.js - -/** - * @author mrdoob / http://mrdoob.com/ - */ - -THREE.CanvasRenderer = function ( parameters ) { - - console.log( 'THREE.CanvasRenderer', THREE.REVISION ); - - var smoothstep = THREE.Math.smoothstep; - - parameters = parameters || {}; - - var _this = this, - _renderData, _elements, _lights, - _projector = new THREE.Projector(), - - _canvas = parameters.canvas !== undefined - ? parameters.canvas - : document.createElement( 'canvas' ), - - _canvasWidth = _canvas.width, - _canvasHeight = _canvas.height, - _canvasWidthHalf = Math.floor( _canvasWidth / 2 ), - _canvasHeightHalf = Math.floor( _canvasHeight / 2 ), - - _viewportX = 0, - _viewportY = 0, - _viewportWidth = _canvasWidth, - _viewportHeight = _canvasHeight, +// File:src/renderers/shaders/ShaderChunk.js - _context = _canvas.getContext( '2d', { - alpha: parameters.alpha === true - } ), +THREE.ShaderChunk = {}; - _clearColor = new THREE.Color( 0x000000 ), - _clearAlpha = 0, +// File:src/renderers/shaders/ShaderChunk/alphatest_fragment.glsl - _contextGlobalAlpha = 1, - _contextGlobalCompositeOperation = 0, - _contextStrokeStyle = null, - _contextFillStyle = null, - _contextLineWidth = null, - _contextLineCap = null, - _contextLineJoin = null, - _contextLineDash = [], +THREE.ShaderChunk[ 'alphatest_fragment'] = "#ifdef ALPHATEST\n\n if ( gl_FragColor.a < ALPHATEST ) discard;\n\n#endif\n"; - _camera, +// File:src/renderers/shaders/ShaderChunk/lights_lambert_vertex.glsl - _v1, _v2, _v3, _v4, - _v5 = new THREE.RenderableVertex(), - _v6 = new THREE.RenderableVertex(), +THREE.ShaderChunk[ 'lights_lambert_vertex'] = "vLightFront = vec3( 0.0 );\n\n#ifdef DOUBLE_SIDED\n\n vLightBack = vec3( 0.0 );\n\n#endif\n\ntransformedNormal = normalize( transformedNormal );\n\n#if MAX_DIR_LIGHTS > 0\n\nfor( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\n vec3 dirVector = normalize( lDirection.xyz );\n\n float dotProduct = dot( transformedNormal, dirVector );\n vec3 directionalLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n #ifdef DOUBLE_SIDED\n\n vec3 directionalLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n #ifdef WRAP_AROUND\n\n vec3 directionalLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n #endif\n\n #endif\n\n #ifdef WRAP_AROUND\n\n vec3 directionalLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n directionalLightWeighting = mix( directionalLightWeighting, directionalLightWeightingHalf, wrapRGB );\n\n #ifdef DOUBLE_SIDED\n\n directionalLightWeightingBack = mix( directionalLightWeightingBack, directionalLightWeightingHalfBack, wrapRGB );\n\n #endif\n\n #endif\n\n vLightFront += directionalLightColor[ i ] * directionalLightWeighting;\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += directionalLightColor[ i ] * directionalLightWeightingBack;\n\n #endif\n\n}\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n for( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\n vec3 lVector = lPosition.xyz - mvPosition.xyz;\n\n float lDistance = 1.0;\n if ( pointLightDistance[ i ] > 0.0 )\n lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );\n\n lVector = normalize( lVector );\n float dotProduct = dot( transformedNormal, lVector );\n\n vec3 pointLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n #ifdef DOUBLE_SIDED\n\n vec3 pointLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n #ifdef WRAP_AROUND\n\n vec3 pointLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n #endif\n\n #endif\n\n #ifdef WRAP_AROUND\n\n vec3 pointLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n pointLightWeighting = mix( pointLightWeighting, pointLightWeightingHalf, wrapRGB );\n\n #ifdef DOUBLE_SIDED\n\n pointLightWeightingBack = mix( pointLightWeightingBack, pointLightWeightingHalfBack, wrapRGB );\n\n #endif\n\n #endif\n\n vLightFront += pointLightColor[ i ] * pointLightWeighting * lDistance;\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += pointLightColor[ i ] * pointLightWeightingBack * lDistance;\n\n #endif\n\n }\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n for( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\n vec3 lVector = lPosition.xyz - mvPosition.xyz;\n\n float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - worldPosition.xyz ) );\n\n if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n spotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );\n\n float lDistance = 1.0;\n if ( spotLightDistance[ i ] > 0.0 )\n lDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );\n\n lVector = normalize( lVector );\n\n float dotProduct = dot( transformedNormal, lVector );\n vec3 spotLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n #ifdef DOUBLE_SIDED\n\n vec3 spotLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n #ifdef WRAP_AROUND\n\n vec3 spotLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n #endif\n\n #endif\n\n #ifdef WRAP_AROUND\n\n vec3 spotLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n spotLightWeighting = mix( spotLightWeighting, spotLightWeightingHalf, wrapRGB );\n\n #ifdef DOUBLE_SIDED\n\n spotLightWeightingBack = mix( spotLightWeightingBack, spotLightWeightingHalfBack, wrapRGB );\n\n #endif\n\n #endif\n\n vLightFront += spotLightColor[ i ] * spotLightWeighting * lDistance * spotEffect;\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += spotLightColor[ i ] * spotLightWeightingBack * lDistance * spotEffect;\n\n #endif\n\n }\n\n }\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n vec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );\n vec3 lVector = normalize( lDirection.xyz );\n\n float dotProduct = dot( transformedNormal, lVector );\n\n float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n float hemiDiffuseWeightBack = -0.5 * dotProduct + 0.5;\n\n vLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );\n\n #endif\n\n }\n\n#endif\n\nvLightFront = vLightFront * diffuse + ambient * ambientLightColor + emissive;\n\n#ifdef DOUBLE_SIDED\n\n vLightBack = vLightBack * diffuse + ambient * ambientLightColor + emissive;\n\n#endif"; - _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, - _v4x, _v4y, _v5x, _v5y, _v6x, _v6y, +// File:src/renderers/shaders/ShaderChunk/map_particle_pars_fragment.glsl - _color = new THREE.Color(), - _color1 = new THREE.Color(), - _color2 = new THREE.Color(), - _color3 = new THREE.Color(), - _color4 = new THREE.Color(), +THREE.ShaderChunk[ 'map_particle_pars_fragment'] = "#ifdef USE_MAP\n\n uniform sampler2D map;\n\n#endif"; - _diffuseColor = new THREE.Color(), - _emissiveColor = new THREE.Color(), +// File:src/renderers/shaders/ShaderChunk/default_vertex.glsl - _lightColor = new THREE.Color(), +THREE.ShaderChunk[ 'default_vertex'] = "vec4 mvPosition;\n\n#ifdef USE_SKINNING\n\n mvPosition = modelViewMatrix * skinned;\n\n#endif\n\n#if !defined( USE_SKINNING ) && defined( USE_MORPHTARGETS )\n\n mvPosition = modelViewMatrix * vec4( morphed, 1.0 );\n\n#endif\n\n#if !defined( USE_SKINNING ) && ! defined( USE_MORPHTARGETS )\n\n mvPosition = modelViewMatrix * vec4( position, 1.0 );\n\n#endif\n\ngl_Position = projectionMatrix * mvPosition;"; - _patterns = {}, +// File:src/renderers/shaders/ShaderChunk/map_pars_fragment.glsl - _image, _uvs, - _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, +THREE.ShaderChunk[ 'map_pars_fragment'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n varying vec2 vUv;\n\n#endif\n\n#ifdef USE_MAP\n\n uniform sampler2D map;\n\n#endif"; - _clipBox = new THREE.Box2(), - _clearBox = new THREE.Box2(), - _elemBox = new THREE.Box2(), +// File:src/renderers/shaders/ShaderChunk/skinnormal_vertex.glsl - _ambientLight = new THREE.Color(), - _directionalLights = new THREE.Color(), - _pointLights = new THREE.Color(), +THREE.ShaderChunk[ 'skinnormal_vertex'] = "#ifdef USE_SKINNING\n\n mat4 skinMatrix = mat4( 0.0 );\n skinMatrix += skinWeight.x * boneMatX;\n skinMatrix += skinWeight.y * boneMatY;\n skinMatrix += skinWeight.z * boneMatZ;\n skinMatrix += skinWeight.w * boneMatW;\n skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\n #ifdef USE_MORPHNORMALS\n\n vec4 skinnedNormal = skinMatrix * vec4( morphedNormal, 0.0 );\n\n #else\n\n vec4 skinnedNormal = skinMatrix * vec4( normal, 0.0 );\n\n #endif\n\n#endif\n"; - _vector3 = new THREE.Vector3(), // Needed for PointLight - _centroid = new THREE.Vector3(), - _normal = new THREE.Vector3(), - _normalViewMatrix = new THREE.Matrix3(); +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_vertex.glsl - // dash+gap fallbacks for Firefox and everything else +THREE.ShaderChunk[ 'logdepthbuf_pars_vertex'] = "#ifdef USE_LOGDEPTHBUF\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n varying float vFragDepth;\n\n #endif\n\n uniform float logDepthBufFC;\n\n#endif"; - if ( _context.setLineDash === undefined ) { +// File:src/renderers/shaders/ShaderChunk/lightmap_pars_vertex.glsl - _context.setLineDash = function () {} +THREE.ShaderChunk[ 'lightmap_pars_vertex'] = "#ifdef USE_LIGHTMAP\n\n varying vec2 vUv2;\n\n#endif"; - } +// File:src/renderers/shaders/ShaderChunk/lights_phong_fragment.glsl - this.domElement = _canvas; +THREE.ShaderChunk[ 'lights_phong_fragment'] = "vec3 normal = normalize( vNormal );\nvec3 viewPosition = normalize( vViewPosition );\n\n#ifdef DOUBLE_SIDED\n\n normal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n\n#endif\n\n#ifdef USE_NORMALMAP\n\n normal = perturbNormal2Arb( -vViewPosition, normal );\n\n#elif defined( USE_BUMPMAP )\n\n normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n vec3 pointDiffuse = vec3( 0.0 );\n vec3 pointSpecular = vec3( 0.0 );\n\n for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\n vec3 lVector = lPosition.xyz + vViewPosition.xyz;\n\n float lDistance = 1.0;\n if ( pointLightDistance[ i ] > 0.0 )\n lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );\n\n lVector = normalize( lVector );\n\n // diffuse\n\n float dotProduct = dot( normal, lVector );\n\n #ifdef WRAP_AROUND\n\n float pointDiffuseWeightFull = max( dotProduct, 0.0 );\n float pointDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n vec3 pointDiffuseWeight = mix( vec3( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );\n\n #else\n\n float pointDiffuseWeight = max( dotProduct, 0.0 );\n\n #endif\n\n pointDiffuse += diffuse * pointLightColor[ i ] * pointDiffuseWeight * lDistance;\n\n // specular\n\n vec3 pointHalfVector = normalize( lVector + viewPosition );\n float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );\n float pointSpecularWeight = specularStrength * max( pow( pointDotNormalHalf, shininess ), 0.0 );\n\n float specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, pointHalfVector ), 0.0 ), 5.0 );\n pointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance * specularNormalization;\n\n }\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n vec3 spotDiffuse = vec3( 0.0 );\n vec3 spotSpecular = vec3( 0.0 );\n\n for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\n vec3 lVector = lPosition.xyz + vViewPosition.xyz;\n\n float lDistance = 1.0;\n if ( spotLightDistance[ i ] > 0.0 )\n lDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );\n\n lVector = normalize( lVector );\n\n float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );\n\n if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n spotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );\n\n // diffuse\n\n float dotProduct = dot( normal, lVector );\n\n #ifdef WRAP_AROUND\n\n float spotDiffuseWeightFull = max( dotProduct, 0.0 );\n float spotDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n vec3 spotDiffuseWeight = mix( vec3( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );\n\n #else\n\n float spotDiffuseWeight = max( dotProduct, 0.0 );\n\n #endif\n\n spotDiffuse += diffuse * spotLightColor[ i ] * spotDiffuseWeight * lDistance * spotEffect;\n\n // specular\n\n vec3 spotHalfVector = normalize( lVector + viewPosition );\n float spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );\n float spotSpecularWeight = specularStrength * max( pow( spotDotNormalHalf, shininess ), 0.0 );\n\n float specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, spotHalfVector ), 0.0 ), 5.0 );\n spotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * lDistance * specularNormalization * spotEffect;\n\n }\n\n }\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n vec3 dirDiffuse = vec3( 0.0 );\n vec3 dirSpecular = vec3( 0.0 );\n\n for( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\n vec3 dirVector = normalize( lDirection.xyz );\n\n // diffuse\n\n float dotProduct = dot( normal, dirVector );\n\n #ifdef WRAP_AROUND\n\n float dirDiffuseWeightFull = max( dotProduct, 0.0 );\n float dirDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n vec3 dirDiffuseWeight = mix( vec3( dirDiffuseWeightFull ), vec3( dirDiffuseWeightHalf ), wrapRGB );\n\n #else\n\n float dirDiffuseWeight = max( dotProduct, 0.0 );\n\n #endif\n\n dirDiffuse += diffuse * directionalLightColor[ i ] * dirDiffuseWeight;\n\n // specular\n\n vec3 dirHalfVector = normalize( dirVector + viewPosition );\n float dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );\n float dirSpecularWeight = specularStrength * max( pow( dirDotNormalHalf, shininess ), 0.0 );\n\n /*\n // fresnel term from skin shader\n const float F0 = 0.128;\n\n float base = 1.0 - dot( viewPosition, dirHalfVector );\n float exponential = pow( base, 5.0 );\n\n float fresnel = exponential + F0 * ( 1.0 - exponential );\n */\n\n /*\n // fresnel term from fresnel shader\n const float mFresnelBias = 0.08;\n const float mFresnelScale = 0.3;\n const float mFresnelPower = 5.0;\n\n float fresnel = mFresnelBias + mFresnelScale * pow( 1.0 + dot( normalize( -viewPosition ), normal ), mFresnelPower );\n */\n\n float specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n // dirSpecular += specular * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization * fresnel;\n\n vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( dirVector, dirHalfVector ), 0.0 ), 5.0 );\n dirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;\n\n\n }\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n vec3 hemiDiffuse = vec3( 0.0 );\n vec3 hemiSpecular = vec3( 0.0 );\n\n for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n vec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );\n vec3 lVector = normalize( lDirection.xyz );\n\n // diffuse\n\n float dotProduct = dot( normal, lVector );\n float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n vec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n hemiDiffuse += diffuse * hemiColor;\n\n // specular (sky light)\n\n vec3 hemiHalfVectorSky = normalize( lVector + viewPosition );\n float hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;\n float hemiSpecularWeightSky = specularStrength * max( pow( max( hemiDotNormalHalfSky, 0.0 ), shininess ), 0.0 );\n\n // specular (ground light)\n\n vec3 lVectorGround = -lVector;\n\n vec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );\n float hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;\n float hemiSpecularWeightGround = specularStrength * max( pow( max( hemiDotNormalHalfGround, 0.0 ), shininess ), 0.0 );\n\n float dotProductGround = dot( normal, lVectorGround );\n\n float specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n vec3 schlickSky = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, hemiHalfVectorSky ), 0.0 ), 5.0 );\n vec3 schlickGround = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 0.0 ), 5.0 );\n hemiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );\n\n }\n\n#endif\n\nvec3 totalDiffuse = vec3( 0.0 );\nvec3 totalSpecular = vec3( 0.0 );\n\n#if MAX_DIR_LIGHTS > 0\n\n totalDiffuse += dirDiffuse;\n totalSpecular += dirSpecular;\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n totalDiffuse += hemiDiffuse;\n totalSpecular += hemiSpecular;\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n totalDiffuse += pointDiffuse;\n totalSpecular += pointSpecular;\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n totalDiffuse += spotDiffuse;\n totalSpecular += spotSpecular;\n\n#endif\n\n#ifdef METAL\n\n gl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient + totalSpecular );\n\n#else\n\n gl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient ) + totalSpecular;\n\n#endif"; - this.devicePixelRatio = parameters.devicePixelRatio !== undefined - ? parameters.devicePixelRatio - : self.devicePixelRatio !== undefined - ? self.devicePixelRatio - : 1; +// File:src/renderers/shaders/ShaderChunk/fog_pars_fragment.glsl - this.autoClear = true; - this.sortObjects = true; - this.sortElements = true; +THREE.ShaderChunk[ 'fog_pars_fragment'] = "#ifdef USE_FOG\n\n uniform vec3 fogColor;\n\n #ifdef FOG_EXP2\n\n uniform float fogDensity;\n\n #else\n\n uniform float fogNear;\n uniform float fogFar;\n #endif\n\n#endif"; - this.info = { +// File:src/renderers/shaders/ShaderChunk/morphnormal_vertex.glsl - render: { +THREE.ShaderChunk[ 'morphnormal_vertex'] = "#ifdef USE_MORPHNORMALS\n\n vec3 morphedNormal = vec3( 0.0 );\n\n morphedNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n morphedNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n morphedNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n morphedNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n\n morphedNormal += normal;\n\n#endif"; - vertices: 0, - faces: 0 +// 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 ) || 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 - // WebGLRenderer compatibility +THREE.ShaderChunk[ 'logdepthbuf_fragment'] = "#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)\n\n gl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;\n\n#endif"; - this.supportsVertexTextures = function () {}; - this.setFaceCulling = function () {}; +// File:src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl - this.setSize = function ( width, height, updateStyle ) { +THREE.ShaderChunk[ 'normalmap_pars_fragment'] = "#ifdef USE_NORMALMAP\n\n uniform sampler2D normalMap;\n uniform vec2 normalScale;\n\n // Per-Pixel Tangent Space Normal Mapping\n // http://hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping.html\n\n vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n\n vec3 q0 = dFdx( eye_pos.xyz );\n vec3 q1 = dFdy( eye_pos.xyz );\n vec2 st0 = dFdx( vUv.st );\n vec2 st1 = dFdy( vUv.st );\n\n vec3 S = normalize( q0 * st1.t - q1 * st0.t );\n vec3 T = normalize( -q0 * st1.s + q1 * st0.s );\n vec3 N = normalize( surf_norm );\n\n vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n mapN.xy = normalScale * mapN.xy;\n mat3 tsn = mat3( S, T, N );\n return normalize( tsn * mapN );\n\n }\n\n#endif\n"; - _canvasWidth = width * this.devicePixelRatio; - _canvasHeight = height * this.devicePixelRatio; +// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_vertex.glsl - _canvas.width = _canvasWidth; - _canvas.height = _canvasHeight; +THREE.ShaderChunk[ 'lights_phong_pars_vertex'] = "#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n varying vec3 vWorldPosition;\n\n#endif\n"; - _canvasWidthHalf = Math.floor( _canvasWidth / 2 ); - _canvasHeightHalf = Math.floor( _canvasHeight / 2 ); +// File:src/renderers/shaders/ShaderChunk/lightmap_pars_fragment.glsl - if ( updateStyle !== false ) { +THREE.ShaderChunk[ 'lightmap_pars_fragment'] = "#ifdef USE_LIGHTMAP\n\n varying vec2 vUv2;\n uniform sampler2D lightMap;\n\n#endif"; - _canvas.style.width = width + 'px'; - _canvas.style.height = height + 'px'; +// File:src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl - } +THREE.ShaderChunk[ 'shadowmap_vertex'] = "#ifdef USE_SHADOWMAP\n\n for( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;\n\n }\n\n#endif"; - _clipBox.min.set( -_canvasWidthHalf, -_canvasHeightHalf ), - _clipBox.max.set( _canvasWidthHalf, _canvasHeightHalf ); +// File:src/renderers/shaders/ShaderChunk/lights_phong_vertex.glsl - _clearBox.min.set( - _canvasWidthHalf, - _canvasHeightHalf ); - _clearBox.max.set( _canvasWidthHalf, _canvasHeightHalf ); +THREE.ShaderChunk[ 'lights_phong_vertex'] = "#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n vWorldPosition = worldPosition.xyz;\n\n#endif"; - _contextGlobalAlpha = 1; - _contextGlobalCompositeOperation = 0; - _contextStrokeStyle = null; - _contextFillStyle = null; - _contextLineWidth = null; - _contextLineCap = null; - _contextLineJoin = null; +// File:src/renderers/shaders/ShaderChunk/map_fragment.glsl - this.setViewport( 0, 0, width, height ); +THREE.ShaderChunk[ 'map_fragment'] = "#ifdef USE_MAP\n\n vec4 texelColor = texture2D( map, vUv );\n\n #ifdef GAMMA_INPUT\n\n texelColor.xyz *= texelColor.xyz;\n\n #endif\n\n gl_FragColor = gl_FragColor * texelColor;\n\n#endif"; - }; +// File:src/renderers/shaders/ShaderChunk/lightmap_vertex.glsl - this.setViewport = function ( x, y, width, height ) { +THREE.ShaderChunk[ 'lightmap_vertex'] = "#ifdef USE_LIGHTMAP\n\n vUv2 = uv2;\n\n#endif"; - _viewportX = x * this.devicePixelRatio; - _viewportY = y * this.devicePixelRatio; +// File:src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl - _viewportWidth = width * this.devicePixelRatio; - _viewportHeight = height * this.devicePixelRatio; +THREE.ShaderChunk[ 'map_particle_fragment'] = "#ifdef USE_MAP\n\n gl_FragColor = gl_FragColor * texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) );\n\n#endif"; - }; +// File:src/renderers/shaders/ShaderChunk/color_pars_fragment.glsl - this.setScissor = function () {}; - this.enableScissorTest = function () {}; +THREE.ShaderChunk[ 'color_pars_fragment'] = "#ifdef USE_COLOR\n\n varying vec3 vColor;\n\n#endif\n"; - this.setClearColor = function ( color, alpha ) { +// File:src/renderers/shaders/ShaderChunk/color_vertex.glsl - _clearColor.set( color ); - _clearAlpha = alpha !== undefined ? alpha : 1; +THREE.ShaderChunk[ 'color_vertex'] = "#ifdef USE_COLOR\n\n #ifdef GAMMA_INPUT\n\n vColor = color * color;\n\n #else\n\n vColor = color;\n\n #endif\n\n#endif"; - _clearBox.min.set( - _canvasWidthHalf, - _canvasHeightHalf ); - _clearBox.max.set( _canvasWidthHalf, _canvasHeightHalf ); +// File:src/renderers/shaders/ShaderChunk/skinning_vertex.glsl - }; +THREE.ShaderChunk[ 'skinning_vertex'] = "#ifdef USE_SKINNING\n\n #ifdef USE_MORPHTARGETS\n\n vec4 skinVertex = bindMatrix * vec4( morphed, 1.0 );\n\n #else\n\n vec4 skinVertex = bindMatrix * vec4( position, 1.0 );\n\n #endif\n\n vec4 skinned = vec4( 0.0 );\n skinned += boneMatX * skinVertex * skinWeight.x;\n skinned += boneMatY * skinVertex * skinWeight.y;\n skinned += boneMatZ * skinVertex * skinWeight.z;\n skinned += boneMatW * skinVertex * skinWeight.w;\n skinned = bindMatrixInverse * skinned;\n\n#endif\n"; - this.setClearColorHex = function ( hex, alpha ) { +// File:src/renderers/shaders/ShaderChunk/envmap_pars_vertex.glsl - console.warn( 'THREE.CanvasRenderer: .setClearColorHex() is being removed. Use .setClearColor() instead.' ); - this.setClearColor( hex, alpha ); +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"; - }; - - this.getClearColor = function () { +// File:src/renderers/shaders/ShaderChunk/linear_to_gamma_fragment.glsl - return _clearColor; +THREE.ShaderChunk[ 'linear_to_gamma_fragment'] = "#ifdef GAMMA_OUTPUT\n\n gl_FragColor.xyz = sqrt( gl_FragColor.xyz );\n\n#endif"; - }; +// File:src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl - this.getClearAlpha = function () { +THREE.ShaderChunk[ 'color_pars_vertex'] = "#ifdef USE_COLOR\n\n varying vec3 vColor;\n\n#endif"; - return _clearAlpha; +// File:src/renderers/shaders/ShaderChunk/lights_lambert_pars_vertex.glsl - }; +THREE.ShaderChunk[ 'lights_lambert_pars_vertex'] = "uniform vec3 ambient;\nuniform vec3 diffuse;\nuniform vec3 emissive;\n\nuniform vec3 ambientLightColor;\n\n#if MAX_DIR_LIGHTS > 0\n\n uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#ifdef WRAP_AROUND\n\n uniform vec3 wrapRGB;\n\n#endif\n"; - this.getMaxAnisotropy = function () { +// File:src/renderers/shaders/ShaderChunk/map_pars_vertex.glsl - return 0; +THREE.ShaderChunk[ 'map_pars_vertex'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n varying vec2 vUv;\n uniform vec4 offsetRepeat;\n\n#endif\n"; - }; +// File:src/renderers/shaders/ShaderChunk/envmap_fragment.glsl - this.clear = function () { +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"; - if ( _clearBox.empty() === false ) { +// File:src/renderers/shaders/ShaderChunk/specularmap_pars_fragment.glsl - _clearBox.intersect( _clipBox ); - _clearBox.expandByScalar( 2 ); +THREE.ShaderChunk[ 'specularmap_pars_fragment'] = "#ifdef USE_SPECULARMAP\n\n uniform sampler2D specularMap;\n\n#endif"; - _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/renderers/shaders/ShaderChunk/logdepthbuf_vertex.glsl - if ( _clearAlpha < 1 ) { +THREE.ShaderChunk[ 'logdepthbuf_vertex'] = "#ifdef USE_LOGDEPTHBUF\n\n gl_Position.z = log2(max(1e-6, gl_Position.w + 1.0)) * logDepthBufFC;\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n vFragDepth = 1.0 + gl_Position.w;\n\n#else\n\n gl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;\n\n #endif\n\n#endif"; - _context.clearRect( - _clearBox.min.x | 0, - _clearBox.min.y | 0, - ( _clearBox.max.x - _clearBox.min.x ) | 0, - ( _clearBox.max.y - _clearBox.min.y ) | 0 - ); +// File:src/renderers/shaders/ShaderChunk/morphtarget_pars_vertex.glsl - } +THREE.ShaderChunk[ 'morphtarget_pars_vertex'] = "#ifdef USE_MORPHTARGETS\n\n #ifndef USE_MORPHNORMALS\n\n uniform float morphTargetInfluences[ 8 ];\n\n #else\n\n uniform float morphTargetInfluences[ 4 ];\n\n #endif\n\n#endif"; - if ( _clearAlpha > 0 ) { +// File:src/renderers/shaders/ShaderChunk/specularmap_fragment.glsl - setBlending( THREE.NormalBlending ); - setOpacity( 1 ); +THREE.ShaderChunk[ 'specularmap_fragment'] = "float specularStrength;\n\n#ifdef USE_SPECULARMAP\n\n vec4 texelSpecular = texture2D( specularMap, vUv );\n specularStrength = texelSpecular.r;\n\n#else\n\n specularStrength = 1.0;\n\n#endif"; - setFillStyle( 'rgba(' + Math.floor( _clearColor.r * 255 ) + ',' + Math.floor( _clearColor.g * 255 ) + ',' + Math.floor( _clearColor.b * 255 ) + ',' + _clearAlpha + ')' ); +// File:src/renderers/shaders/ShaderChunk/fog_fragment.glsl - _context.fillRect( - _clearBox.min.x | 0, - _clearBox.min.y | 0, - ( _clearBox.max.x - _clearBox.min.x ) | 0, - ( _clearBox.max.y - _clearBox.min.y ) | 0 - ); +THREE.ShaderChunk[ 'fog_fragment'] = "#ifdef USE_FOG\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n float depth = gl_FragDepthEXT / gl_FragCoord.w;\n\n #else\n\n float depth = gl_FragCoord.z / gl_FragCoord.w;\n\n #endif\n\n #ifdef FOG_EXP2\n\n const float LOG2 = 1.442695;\n float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );\n fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );\n\n #else\n\n float fogFactor = smoothstep( fogNear, fogFar, depth );\n\n #endif\n \n gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );\n\n#endif"; - } +// File:src/renderers/shaders/ShaderChunk/bumpmap_pars_fragment.glsl - _clearBox.makeEmpty(); +THREE.ShaderChunk[ 'bumpmap_pars_fragment'] = "#ifdef USE_BUMPMAP\n\n uniform sampler2D bumpMap;\n uniform float bumpScale;\n\n // Derivative maps - bump mapping unparametrized surfaces by Morten Mikkelsen\n // http://mmikkelsen3d.blogspot.sk/2011/07/derivative-maps.html\n\n // Evaluate the derivative of the height w.r.t. screen-space using forward differencing (listing 2)\n\n vec2 dHdxy_fwd() {\n\n vec2 dSTdx = dFdx( vUv );\n vec2 dSTdy = dFdy( vUv );\n\n float Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\n return vec2( dBx, dBy );\n\n }\n\n vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\n vec3 vSigmaX = dFdx( surf_pos );\n vec3 vSigmaY = dFdy( surf_pos );\n vec3 vN = surf_norm; // normalized\n\n vec3 R1 = cross( vSigmaY, vN );\n vec3 R2 = cross( vN, vSigmaX );\n\n float fDet = dot( vSigmaX, R1 );\n\n vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n return normalize( abs( fDet ) * surf_norm - vGrad );\n\n }\n\n#endif"; - } +// File:src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl - }; +THREE.ShaderChunk[ 'defaultnormal_vertex'] = "vec3 objectNormal;\n\n#ifdef USE_SKINNING\n\n objectNormal = skinnedNormal.xyz;\n\n#endif\n\n#if !defined( USE_SKINNING ) && defined( USE_MORPHNORMALS )\n\n objectNormal = morphedNormal;\n\n#endif\n\n#if !defined( USE_SKINNING ) && ! defined( USE_MORPHNORMALS )\n\n objectNormal = normal;\n\n#endif\n\n#ifdef FLIP_SIDED\n\n objectNormal = -objectNormal;\n\n#endif\n\nvec3 transformedNormal = normalMatrix * objectNormal;"; - // compatibility +// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_fragment.glsl - this.clearColor = function () {}; - this.clearDepth = function () {}; - this.clearStencil = function () {}; +THREE.ShaderChunk[ 'lights_phong_pars_fragment'] = "uniform vec3 ambientLightColor;\n\n#if MAX_DIR_LIGHTS > 0\n\n uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n\n uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n varying vec3 vWorldPosition;\n\n#endif\n\n#ifdef WRAP_AROUND\n\n uniform vec3 wrapRGB;\n\n#endif\n\nvarying vec3 vViewPosition;\nvarying vec3 vNormal;"; - this.render = function ( scene, camera ) { +// File:src/renderers/shaders/ShaderChunk/skinbase_vertex.glsl - if ( camera instanceof THREE.Camera === false ) { +THREE.ShaderChunk[ 'skinbase_vertex'] = "#ifdef USE_SKINNING\n\n mat4 boneMatX = getBoneMatrix( skinIndex.x );\n mat4 boneMatY = getBoneMatrix( skinIndex.y );\n mat4 boneMatZ = getBoneMatrix( skinIndex.z );\n mat4 boneMatW = getBoneMatrix( skinIndex.w );\n\n#endif"; - console.error( 'THREE.CanvasRenderer.render: camera is not an instance of THREE.Camera.' ); - return; +// File:src/renderers/shaders/ShaderChunk/map_vertex.glsl - } +THREE.ShaderChunk[ 'map_vertex'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n vUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n\n#endif"; - if ( this.autoClear === true ) this.clear(); +// File:src/renderers/shaders/ShaderChunk/lightmap_fragment.glsl - _this.info.render.vertices = 0; - _this.info.render.faces = 0; +THREE.ShaderChunk[ 'lightmap_fragment'] = "#ifdef USE_LIGHTMAP\n\n gl_FragColor = gl_FragColor * texture2D( lightMap, vUv2 );\n\n#endif"; - _context.setTransform( _viewportWidth / _canvasWidth, 0, 0, - _viewportHeight / _canvasHeight, _viewportX, _canvasHeight - _viewportY ); - _context.translate( _canvasWidthHalf, _canvasHeightHalf ); +// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl - _renderData = _projector.projectScene( scene, camera, this.sortObjects, this.sortElements ); - _elements = _renderData.elements; - _lights = _renderData.lights; - _camera = camera; +THREE.ShaderChunk[ 'shadowmap_pars_vertex'] = "#ifdef USE_SHADOWMAP\n\n varying vec4 vShadowCoord[ MAX_SHADOWS ];\n uniform mat4 shadowMatrix[ MAX_SHADOWS ];\n\n#endif"; - _normalViewMatrix.getNormalMatrix( camera.matrixWorldInverse ); +// File:src/renderers/shaders/ShaderChunk/color_fragment.glsl - /* 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 ); - */ +THREE.ShaderChunk[ 'color_fragment'] = "#ifdef USE_COLOR\n\n gl_FragColor = gl_FragColor * vec4( vColor, 1.0 );\n\n#endif"; - calculateLights(); +// File:src/renderers/shaders/ShaderChunk/morphtarget_vertex.glsl - for ( var e = 0, el = _elements.length; e < el; e ++ ) { +THREE.ShaderChunk[ 'morphtarget_vertex'] = "#ifdef USE_MORPHTARGETS\n\n vec3 morphed = vec3( 0.0 );\n morphed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n morphed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n morphed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n morphed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\n #ifndef USE_MORPHNORMALS\n\n morphed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n morphed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n morphed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n morphed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\n #endif\n\n morphed += position;\n\n#endif"; - var element = _elements[ e ]; +// File:src/renderers/shaders/ShaderChunk/envmap_vertex.glsl - var material = element.material; +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"; - if ( material === undefined || material.opacity === 0 ) continue; +// File:src/renderers/shaders/ShaderChunk/shadowmap_fragment.glsl - _elemBox.makeEmpty(); +THREE.ShaderChunk[ 'shadowmap_fragment'] = "#ifdef USE_SHADOWMAP\n\n #ifdef SHADOWMAP_DEBUG\n\n vec3 frustumColors[3];\n frustumColors[0] = vec3( 1.0, 0.5, 0.0 );\n frustumColors[1] = vec3( 0.0, 1.0, 0.8 );\n frustumColors[2] = vec3( 0.0, 0.5, 1.0 );\n\n #endif\n\n #ifdef SHADOWMAP_CASCADE\n\n int inFrustumCount = 0;\n\n #endif\n\n float fDepth;\n vec3 shadowColor = vec3( 1.0 );\n\n for( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n vec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;\n\n // if ( something && something ) breaks ATI OpenGL shader compiler\n // if ( all( something, something ) ) using this instead\n\n bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n bool inFrustum = all( inFrustumVec );\n\n // don't shadow pixels outside of light frustum\n // use just first frustum (for cascades)\n // don't shadow pixels behind far plane of light frustum\n\n #ifdef SHADOWMAP_CASCADE\n\n inFrustumCount += int( inFrustum );\n bvec3 frustumTestVec = bvec3( inFrustum, inFrustumCount == 1, shadowCoord.z <= 1.0 );\n\n #else\n\n bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\n #endif\n\n bool frustumTest = all( frustumTestVec );\n\n if ( frustumTest ) {\n\n shadowCoord.z += shadowBias[ i ];\n\n #if defined( SHADOWMAP_TYPE_PCF )\n\n // Percentage-close filtering\n // (9 pixel kernel)\n // http://fabiensanglard.net/shadowmappingPCF/\n\n float shadow = 0.0;\n\n /*\n // nested loops breaks shader compiler / validator on some ATI cards when using OpenGL\n // must enroll loop manually\n\n for ( float y = -1.25; y <= 1.25; y += 1.25 )\n for ( float x = -1.25; x <= 1.25; x += 1.25 ) {\n\n vec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy );\n\n // doesn't seem to produce any noticeable visual difference compared to simple texture2D lookup\n //vec4 rgbaDepth = texture2DProj( shadowMap[ i ], vec4( vShadowCoord[ i ].w * ( vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy ), 0.05, vShadowCoord[ i ].w ) );\n\n float fDepth = unpackDepth( rgbaDepth );\n\n if ( fDepth < shadowCoord.z )\n shadow += 1.0;\n\n }\n\n shadow /= 9.0;\n\n */\n\n const float shadowDelta = 1.0 / 9.0;\n\n float xPixelOffset = 1.0 / shadowMapSize[ i ].x;\n float yPixelOffset = 1.0 / shadowMapSize[ i ].y;\n\n float dx0 = -1.25 * xPixelOffset;\n float dy0 = -1.25 * yPixelOffset;\n float dx1 = 1.25 * xPixelOffset;\n float dy1 = 1.25 * yPixelOffset;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );\n\n #elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\n // Percentage-close filtering\n // (9 pixel kernel)\n // http://fabiensanglard.net/shadowmappingPCF/\n\n float shadow = 0.0;\n\n float xPixelOffset = 1.0 / shadowMapSize[ i ].x;\n float yPixelOffset = 1.0 / shadowMapSize[ i ].y;\n\n float dx0 = -1.0 * xPixelOffset;\n float dy0 = -1.0 * yPixelOffset;\n float dx1 = 1.0 * xPixelOffset;\n float dy1 = 1.0 * yPixelOffset;\n\n mat3 shadowKernel;\n mat3 depthKernel;\n\n depthKernel[0][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n depthKernel[0][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n depthKernel[0][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n depthKernel[1][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n depthKernel[1][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n depthKernel[1][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n depthKernel[2][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n depthKernel[2][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n depthKernel[2][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n\n vec3 shadowZ = vec3( shadowCoord.z );\n shadowKernel[0] = vec3(lessThan(depthKernel[0], shadowZ ));\n shadowKernel[0] *= vec3(0.25);\n\n shadowKernel[1] = vec3(lessThan(depthKernel[1], shadowZ ));\n shadowKernel[1] *= vec3(0.25);\n\n shadowKernel[2] = vec3(lessThan(depthKernel[2], shadowZ ));\n shadowKernel[2] *= vec3(0.25);\n\n vec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[i].xy );\n\n shadowKernel[0] = mix( shadowKernel[1], shadowKernel[0], fractionalCoord.x );\n shadowKernel[1] = mix( shadowKernel[2], shadowKernel[1], fractionalCoord.x );\n\n vec4 shadowValues;\n shadowValues.x = mix( shadowKernel[0][1], shadowKernel[0][0], fractionalCoord.y );\n shadowValues.y = mix( shadowKernel[0][2], shadowKernel[0][1], fractionalCoord.y );\n shadowValues.z = mix( shadowKernel[1][1], shadowKernel[1][0], fractionalCoord.y );\n shadowValues.w = mix( shadowKernel[1][2], shadowKernel[1][1], fractionalCoord.y );\n\n shadow = dot( shadowValues, vec4( 1.0 ) );\n\n shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );\n\n #else\n\n vec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );\n float fDepth = unpackDepth( rgbaDepth );\n\n if ( fDepth < shadowCoord.z )\n\n // spot with multiple shadows is darker\n\n shadowColor = shadowColor * vec3( 1.0 - shadowDarkness[ i ] );\n\n // spot with multiple shadows has the same color as single shadow spot\n\n // shadowColor = min( shadowColor, vec3( shadowDarkness[ i ] ) );\n\n #endif\n\n }\n\n\n #ifdef SHADOWMAP_DEBUG\n\n #ifdef SHADOWMAP_CASCADE\n\n if ( inFrustum && inFrustumCount == 1 ) gl_FragColor.xyz *= frustumColors[ i ];\n\n #else\n\n if ( inFrustum ) gl_FragColor.xyz *= frustumColors[ i ];\n\n #endif\n\n #endif\n\n }\n\n #ifdef GAMMA_OUTPUT\n\n shadowColor *= shadowColor;\n\n #endif\n\n gl_FragColor.xyz = gl_FragColor.xyz * shadowColor;\n\n#endif\n"; - if ( element instanceof THREE.RenderableSprite ) { +// File:src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl - _v1 = element; - _v1.x *= _canvasWidthHalf; _v1.y *= _canvasHeightHalf; +THREE.ShaderChunk[ 'worldpos_vertex'] = "#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )\n\n #ifdef USE_SKINNING\n\n vec4 worldPosition = modelMatrix * skinned;\n\n #endif\n\n #if defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )\n\n vec4 worldPosition = modelMatrix * vec4( morphed, 1.0 );\n\n #endif\n\n #if ! defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )\n\n vec4 worldPosition = modelMatrix * vec4( position, 1.0 );\n\n #endif\n\n#endif"; - renderSprite( _v1, element, material ); +// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl - } else if ( element instanceof THREE.RenderableLine ) { +THREE.ShaderChunk[ 'shadowmap_pars_fragment'] = "#ifdef USE_SHADOWMAP\n\n uniform sampler2D shadowMap[ MAX_SHADOWS ];\n uniform vec2 shadowMapSize[ MAX_SHADOWS ];\n\n uniform float shadowDarkness[ MAX_SHADOWS ];\n uniform float shadowBias[ MAX_SHADOWS ];\n\n varying vec4 vShadowCoord[ MAX_SHADOWS ];\n\n float unpackDepth( const in vec4 rgba_depth ) {\n\n const vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n float depth = dot( rgba_depth, bit_shift );\n return depth;\n\n }\n\n#endif"; - _v1 = element.v1; _v2 = element.v2; +// File:src/renderers/shaders/ShaderChunk/skinning_pars_vertex.glsl - _v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf; - _v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf; +THREE.ShaderChunk[ 'skinning_pars_vertex'] = "#ifdef USE_SKINNING\n\n uniform mat4 bindMatrix;\n uniform mat4 bindMatrixInverse;\n\n #ifdef BONE_TEXTURE\n\n uniform sampler2D boneTexture;\n uniform int boneTextureWidth;\n uniform int boneTextureHeight;\n\n mat4 getBoneMatrix( const in float i ) {\n\n float j = i * 4.0;\n float x = mod( j, float( boneTextureWidth ) );\n float y = floor( j / float( boneTextureWidth ) );\n\n float dx = 1.0 / float( boneTextureWidth );\n float dy = 1.0 / float( boneTextureHeight );\n\n y = dy * ( y + 0.5 );\n\n vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\n mat4 bone = mat4( v1, v2, v3, v4 );\n\n return bone;\n\n }\n\n #else\n\n uniform mat4 boneGlobalMatrices[ MAX_BONES ];\n\n mat4 getBoneMatrix( const in float i ) {\n\n mat4 bone = boneGlobalMatrices[ int(i) ];\n return bone;\n\n }\n\n #endif\n\n#endif\n"; - _elemBox.setFromPoints( [ - _v1.positionScreen, - _v2.positionScreen - ] ); +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_fragment.glsl - if ( _clipBox.isIntersectionBox( _elemBox ) === true ) { +THREE.ShaderChunk[ 'logdepthbuf_pars_fragment'] = "#ifdef USE_LOGDEPTHBUF\n\n uniform float logDepthBufFC;\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n #extension GL_EXT_frag_depth : enable\n varying float vFragDepth;\n\n #endif\n\n#endif"; - renderLine( _v1, _v2, element, material ); +// File:src/renderers/shaders/ShaderChunk/alphamap_fragment.glsl - } +THREE.ShaderChunk[ 'alphamap_fragment'] = "#ifdef USE_ALPHAMAP\n\n gl_FragColor.a *= texture2D( alphaMap, vUv ).g;\n\n#endif\n"; - } else if ( element instanceof THREE.RenderableFace ) { +// File:src/renderers/shaders/ShaderChunk/alphamap_pars_fragment.glsl - _v1 = element.v1; _v2 = element.v2; _v3 = element.v3; +THREE.ShaderChunk[ 'alphamap_pars_fragment'] = "#ifdef USE_ALPHAMAP\n\n uniform sampler2D alphaMap;\n\n#endif\n"; - 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; +// File:src/renderers/shaders/UniformsUtils.js - _v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf; - _v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf; - _v3.positionScreen.x *= _canvasWidthHalf; _v3.positionScreen.y *= _canvasHeightHalf; +/** + * Uniform Utilities + */ - if ( material.overdraw > 0 ) { +THREE.UniformsUtils = { - expand( _v1.positionScreen, _v2.positionScreen, material.overdraw ); - expand( _v2.positionScreen, _v3.positionScreen, material.overdraw ); - expand( _v3.positionScreen, _v1.positionScreen, material.overdraw ); + merge: function ( uniforms ) { - } + var merged = {}; - _elemBox.setFromPoints( [ - _v1.positionScreen, - _v2.positionScreen, - _v3.positionScreen - ] ); + for ( var u = 0; u < uniforms.length; u ++ ) { - if ( _clipBox.isIntersectionBox( _elemBox ) === true ) { + var tmp = this.clone( uniforms[ u ] ); - renderFace3( _v1, _v2, _v3, 0, 1, 2, element, material ); + for ( var p in tmp ) { - } + merged[ p ] = tmp[ p ]; } - /* 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 ); - */ - - _clearBox.union( _elemBox ); - } - /* 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 ); - */ - - _context.setTransform( 1, 0, 0, 1, 0, 0 ); + return merged; - }; + }, - // + clone: function ( uniforms_src ) { - function calculateLights() { + var uniforms_dst = {}; - _ambientLight.setRGB( 0, 0, 0 ); - _directionalLights.setRGB( 0, 0, 0 ); - _pointLights.setRGB( 0, 0, 0 ); + for ( var u in uniforms_src ) { - for ( var l = 0, ll = _lights.length; l < ll; l ++ ) { + uniforms_dst[ u ] = {}; - var light = _lights[ l ]; - var lightColor = light.color; + for ( var p in uniforms_src[ u ] ) { - if ( light instanceof THREE.AmbientLight ) { + var parameter_src = uniforms_src[ u ][ p ]; - _ambientLight.add( lightColor ); + if ( parameter_src instanceof THREE.Color || + parameter_src instanceof THREE.Vector2 || + parameter_src instanceof THREE.Vector3 || + parameter_src instanceof THREE.Vector4 || + parameter_src instanceof THREE.Matrix4 || + parameter_src instanceof THREE.Texture ) { - } else if ( light instanceof THREE.DirectionalLight ) { + uniforms_dst[ u ][ p ] = parameter_src.clone(); - // for sprites + } else if ( parameter_src instanceof Array ) { - _directionalLights.add( lightColor ); + uniforms_dst[ u ][ p ] = parameter_src.slice(); - } else if ( light instanceof THREE.PointLight ) { + } else { - // for sprites + uniforms_dst[ u ][ p ] = parameter_src; - _pointLights.add( lightColor ); + } } } - } + return uniforms_dst; - function calculateLight( position, normal, color ) { + } - for ( var l = 0, ll = _lights.length; l < ll; l ++ ) { +}; - var light = _lights[ l ]; +// File:src/renderers/shaders/UniformsLib.js - _lightColor.copy( light.color ); +/** + * Uniforms library for shared webgl shaders + */ - if ( light instanceof THREE.DirectionalLight ) { +THREE.UniformsLib = { - var lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld ).normalize(); + common: { - var amount = normal.dot( lightPosition ); + "diffuse" : { type: "c", value: new THREE.Color( 0xeeeeee ) }, + "opacity" : { type: "f", value: 1.0 }, - if ( amount <= 0 ) continue; + "map" : { type: "t", value: null }, + "offsetRepeat" : { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) }, - amount *= light.intensity; + "lightMap" : { type: "t", value: null }, + "specularMap" : { type: "t", value: null }, + "alphaMap" : { type: "t", value: null }, - color.add( _lightColor.multiplyScalar( amount ) ); + "envMap" : { type: "t", value: null }, + "flipEnvMap" : { type: "f", value: - 1 }, + "useRefract" : { type: "i", value: 0 }, + "reflectivity" : { type: "f", value: 1.0 }, + "refractionRatio" : { type: "f", value: 0.98 }, + "combine" : { type: "i", value: 0 }, - } else if ( light instanceof THREE.PointLight ) { + "morphTargetInfluences" : { type: "f", value: 0 } - var lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld ); + }, - var amount = normal.dot( _vector3.subVectors( lightPosition, position ).normalize() ); + bump: { - if ( amount <= 0 ) continue; + "bumpMap" : { type: "t", value: null }, + "bumpScale" : { type: "f", value: 1 } - amount *= light.distance == 0 ? 1 : 1 - Math.min( position.distanceTo( lightPosition ) / light.distance, 1 ); + }, - if ( amount == 0 ) continue; + normalmap: { - amount *= light.intensity; + "normalMap" : { type: "t", value: null }, + "normalScale" : { type: "v2", value: new THREE.Vector2( 1, 1 ) } + }, - color.add( _lightColor.multiplyScalar( amount ) ); + fog : { - } + "fogDensity" : { type: "f", value: 0.00025 }, + "fogNear" : { type: "f", value: 1 }, + "fogFar" : { type: "f", value: 2000 }, + "fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) } - } + }, - } + lights: { - function renderSprite( v1, element, material ) { + "ambientLightColor" : { type: "fv", value: [] }, - setOpacity( material.opacity ); - setBlending( material.blending ); + "directionalLightDirection" : { type: "fv", value: [] }, + "directionalLightColor" : { type: "fv", value: [] }, - var scaleX = element.scale.x * _canvasWidthHalf; - var scaleY = element.scale.y * _canvasHeightHalf; + "hemisphereLightDirection" : { type: "fv", value: [] }, + "hemisphereLightSkyColor" : { type: "fv", value: [] }, + "hemisphereLightGroundColor" : { type: "fv", value: [] }, - 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 ); + "pointLightColor" : { type: "fv", value: [] }, + "pointLightPosition" : { type: "fv", value: [] }, + "pointLightDistance" : { type: "fv1", value: [] }, - if ( material instanceof THREE.SpriteMaterial ) { + "spotLightColor" : { type: "fv", value: [] }, + "spotLightPosition" : { type: "fv", value: [] }, + "spotLightDirection" : { type: "fv", value: [] }, + "spotLightDistance" : { type: "fv1", value: [] }, + "spotLightAngleCos" : { type: "fv1", value: [] }, + "spotLightExponent" : { type: "fv1", value: [] } - var texture = material.map; + }, - if ( texture !== null && texture.image !== undefined ) { + particle: { - if ( texture.hasEventListener( 'update', onTextureUpdate ) === false ) { + "psColor" : { type: "c", value: new THREE.Color( 0xeeeeee ) }, + "opacity" : { type: "f", value: 1.0 }, + "size" : { type: "f", value: 1.0 }, + "scale" : { type: "f", value: 1.0 }, + "map" : { type: "t", value: null }, - if ( texture.image.width > 0 ) { + "fogDensity" : { type: "f", value: 0.00025 }, + "fogNear" : { type: "f", value: 1 }, + "fogFar" : { type: "f", value: 2000 }, + "fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) } - textureToPattern( texture ); + }, - } + shadowmap: { - texture.addEventListener( 'update', onTextureUpdate ); + "shadowMap": { type: "tv", value: [] }, + "shadowMapSize": { type: "v2v", value: [] }, - } + "shadowBias" : { type: "fv1", value: [] }, + "shadowDarkness": { type: "fv1", value: [] }, - var pattern = _patterns[ texture.id ]; + "shadowMatrix" : { type: "m4v", value: [] } - if ( pattern !== undefined ) { + } - setFillStyle( pattern ); +}; - } else { +// File:src/renderers/shaders/ShaderLib.js - setFillStyle( 'rgba( 0, 0, 0, 1 )' ); +/** + * Webgl Shader Library for three.js + * + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + */ - } - // +THREE.ShaderLib = { - var bitmap = texture.image; + 'basic': { - var ox = bitmap.width * texture.offset.x; - var oy = bitmap.height * texture.offset.y; + uniforms: THREE.UniformsUtils.merge( [ - var sx = bitmap.width * texture.repeat.x; - var sy = bitmap.height * texture.repeat.y; + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "shadowmap" ] - var cx = scaleX / sx; - var cy = scaleY / sy; + ] ), - _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(); + vertexShader: [ - } else { + THREE.ShaderChunk[ "map_pars_vertex" ], + THREE.ShaderChunk[ "lightmap_pars_vertex" ], + THREE.ShaderChunk[ "envmap_pars_vertex" ], + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], - // no texture + "void main() {", - setFillStyle( material.color.getStyle() ); + THREE.ShaderChunk[ "map_vertex" ], + THREE.ShaderChunk[ "lightmap_vertex" ], + THREE.ShaderChunk[ "color_vertex" ], + THREE.ShaderChunk[ "skinbase_vertex" ], - _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(); + " #ifdef USE_ENVMAP", - } + THREE.ShaderChunk[ "morphnormal_vertex" ], + THREE.ShaderChunk[ "skinnormal_vertex" ], + THREE.ShaderChunk[ "defaultnormal_vertex" ], - } else if ( material instanceof THREE.SpriteCanvasMaterial ) { + " #endif", - setStrokeStyle( material.color.getStyle() ); - setFillStyle( material.color.getStyle() ); + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "default_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], - _context.save(); - _context.translate( v1.x, v1.y ); - if ( material.rotation !== 0 ) _context.rotate( material.rotation ); - _context.scale( scaleX, scaleY ); + THREE.ShaderChunk[ "worldpos_vertex" ], + THREE.ShaderChunk[ "envmap_vertex" ], + THREE.ShaderChunk[ "shadowmap_vertex" ], - material.program( _context ); + "}" - _context.restore(); + ].join("\n"), - } + fragmentShader: [ - /* 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(); - */ + "uniform vec3 diffuse;", + "uniform float opacity;", - } + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "map_pars_fragment" ], + THREE.ShaderChunk[ "alphamap_pars_fragment" ], + THREE.ShaderChunk[ "lightmap_pars_fragment" ], + THREE.ShaderChunk[ "envmap_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "specularmap_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], - function renderLine( v1, v2, element, material ) { + "void main() {", - setOpacity( material.opacity ); - setBlending( material.blending ); + " gl_FragColor = vec4( diffuse, opacity );", - _context.beginPath(); - _context.moveTo( v1.positionScreen.x, v1.positionScreen.y ); - _context.lineTo( v2.positionScreen.x, v2.positionScreen.y ); + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "map_fragment" ], + THREE.ShaderChunk[ "alphamap_fragment" ], + THREE.ShaderChunk[ "alphatest_fragment" ], + THREE.ShaderChunk[ "specularmap_fragment" ], + THREE.ShaderChunk[ "lightmap_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "envmap_fragment" ], + THREE.ShaderChunk[ "shadowmap_fragment" ], - if ( material instanceof THREE.LineBasicMaterial ) { + THREE.ShaderChunk[ "linear_to_gamma_fragment" ], - setLineWidth( material.linewidth ); - setLineCap( material.linecap ); - setLineJoin( material.linejoin ); + THREE.ShaderChunk[ "fog_fragment" ], - if ( material.vertexColors !== THREE.VertexColors ) { + "}" - setStrokeStyle( material.color.getStyle() ); + ].join("\n") - } else { + }, - var colorStyle1 = element.vertexColors[ 0 ].getStyle(); - var colorStyle2 = element.vertexColors[ 1 ].getStyle(); + 'lambert': { - if ( colorStyle1 === colorStyle2 ) { + uniforms: THREE.UniformsUtils.merge( [ - setStrokeStyle( colorStyle1 ); + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "lights" ], + THREE.UniformsLib[ "shadowmap" ], - } else { + { + "ambient" : { type: "c", value: new THREE.Color( 0xffffff ) }, + "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) }, + "wrapRGB" : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) } + } - 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 ); + vertexShader: [ - } catch ( exception ) { + "#define LAMBERT", - grad = colorStyle1; + "varying vec3 vLightFront;", - } + "#ifdef DOUBLE_SIDED", - setStrokeStyle( grad ); + " varying vec3 vLightBack;", - } + "#endif", - } + THREE.ShaderChunk[ "map_pars_vertex" ], + THREE.ShaderChunk[ "lightmap_pars_vertex" ], + THREE.ShaderChunk[ "envmap_pars_vertex" ], + THREE.ShaderChunk[ "lights_lambert_pars_vertex" ], + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], - _context.stroke(); - _elemBox.expandByScalar( material.linewidth * 2 ); + "void main() {", - } else if ( material instanceof THREE.LineDashedMaterial ) { + THREE.ShaderChunk[ "map_vertex" ], + THREE.ShaderChunk[ "lightmap_vertex" ], + THREE.ShaderChunk[ "color_vertex" ], - setLineWidth( material.linewidth ); - setLineCap( material.linecap ); - setLineJoin( material.linejoin ); - setStrokeStyle( material.color.getStyle() ); - setLineDash( [ material.dashSize, material.gapSize ] ); + THREE.ShaderChunk[ "morphnormal_vertex" ], + THREE.ShaderChunk[ "skinbase_vertex" ], + THREE.ShaderChunk[ "skinnormal_vertex" ], + THREE.ShaderChunk[ "defaultnormal_vertex" ], - _context.stroke(); + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "default_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], - _elemBox.expandByScalar( material.linewidth * 2 ); + THREE.ShaderChunk[ "worldpos_vertex" ], + THREE.ShaderChunk[ "envmap_vertex" ], + THREE.ShaderChunk[ "lights_lambert_vertex" ], + THREE.ShaderChunk[ "shadowmap_vertex" ], - setLineDash( [] ); + "}" - } + ].join("\n"), - } + fragmentShader: [ - function renderFace3( v1, v2, v3, uv1, uv2, uv3, element, material ) { + "uniform float opacity;", - _this.info.render.vertices += 3; - _this.info.render.faces ++; + "varying vec3 vLightFront;", - setOpacity( material.opacity ); - setBlending( material.blending ); + "#ifdef DOUBLE_SIDED", - _v1x = v1.positionScreen.x; _v1y = v1.positionScreen.y; - _v2x = v2.positionScreen.x; _v2y = v2.positionScreen.y; - _v3x = v3.positionScreen.x; _v3y = v3.positionScreen.y; + " varying vec3 vLightBack;", - drawTriangle( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y ); + "#endif", - if ( ( material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) && material.map === null ) { + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "map_pars_fragment" ], + THREE.ShaderChunk[ "alphamap_pars_fragment" ], + THREE.ShaderChunk[ "lightmap_pars_fragment" ], + THREE.ShaderChunk[ "envmap_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "specularmap_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], - _diffuseColor.copy( material.color ); - _emissiveColor.copy( material.emissive ); + "void main() {", - if ( material.vertexColors === THREE.FaceColors ) { + " gl_FragColor = vec4( vec3( 1.0 ), opacity );", - _diffuseColor.multiply( element.color ); + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "map_fragment" ], + THREE.ShaderChunk[ "alphamap_fragment" ], + THREE.ShaderChunk[ "alphatest_fragment" ], + THREE.ShaderChunk[ "specularmap_fragment" ], - } + " #ifdef DOUBLE_SIDED", - _color.copy( _ambientLight ); + //"float isFront = float( gl_FrontFacing );", + //"gl_FragColor.xyz *= isFront * vLightFront + ( 1.0 - isFront ) * vLightBack;", - _centroid.copy( v1.positionWorld ).add( v2.positionWorld ).add( v3.positionWorld ).divideScalar( 3 ); + " if ( gl_FrontFacing )", + " gl_FragColor.xyz *= vLightFront;", + " else", + " gl_FragColor.xyz *= vLightBack;", - calculateLight( _centroid, element.normalModel, _color ); + " #else", - _color.multiply( _diffuseColor ).add( _emissiveColor ); + " gl_FragColor.xyz *= vLightFront;", - material.wireframe === true - ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) - : fillPath( _color ); + " #endif", - } else if ( material instanceof THREE.MeshBasicMaterial || - material instanceof THREE.MeshLambertMaterial || - material instanceof THREE.MeshPhongMaterial ) { + THREE.ShaderChunk[ "lightmap_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "envmap_fragment" ], + THREE.ShaderChunk[ "shadowmap_fragment" ], - if ( material.map !== null ) { + THREE.ShaderChunk[ "linear_to_gamma_fragment" ], - if ( material.map.mapping instanceof THREE.UVMapping ) { + THREE.ShaderChunk[ "fog_fragment" ], - _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 ); + "}" - } + ].join("\n") - } else if ( material.envMap !== null ) { + }, - if ( material.envMap.mapping instanceof THREE.SphericalReflectionMapping ) { + 'phong': { - _normal.copy( element.vertexNormalsModel[ uv1 ] ).applyMatrix3( _normalViewMatrix ); - _uv1x = 0.5 * _normal.x + 0.5; - _uv1y = 0.5 * _normal.y + 0.5; + uniforms: THREE.UniformsUtils.merge( [ - _normal.copy( element.vertexNormalsModel[ uv2 ] ).applyMatrix3( _normalViewMatrix ); - _uv2x = 0.5 * _normal.x + 0.5; - _uv2y = 0.5 * _normal.y + 0.5; + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "bump" ], + THREE.UniformsLib[ "normalmap" ], + THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "lights" ], + THREE.UniformsLib[ "shadowmap" ], - _normal.copy( element.vertexNormalsModel[ uv3 ] ).applyMatrix3( _normalViewMatrix ); - _uv3x = 0.5 * _normal.x + 0.5; - _uv3y = 0.5 * _normal.y + 0.5; + { + "ambient" : { type: "c", value: new THREE.Color( 0xffffff ) }, + "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) }, + "specular" : { type: "c", value: new THREE.Color( 0x111111 ) }, + "shininess": { type: "f", value: 30 }, + "wrapRGB" : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) } + } - patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, material.envMap ); + ] ), - } else if ( material.envMap.mapping instanceof THREE.SphericalRefractionMapping ) { + vertexShader: [ - _normal.copy( element.vertexNormalsModel[ uv1 ] ).applyMatrix3( _normalViewMatrix ); - _uv1x = - 0.5 * _normal.x + 0.5; - _uv1y = - 0.5 * _normal.y + 0.5; + "#define PHONG", - _normal.copy( element.vertexNormalsModel[ uv2 ] ).applyMatrix3( _normalViewMatrix ); - _uv2x = - 0.5 * _normal.x + 0.5; - _uv2y = - 0.5 * _normal.y + 0.5; + "varying vec3 vViewPosition;", + "varying vec3 vNormal;", - _normal.copy( element.vertexNormalsModel[ uv3 ] ).applyMatrix3( _normalViewMatrix ); - _uv3x = - 0.5 * _normal.x + 0.5; - _uv3y = - 0.5 * _normal.y + 0.5; + THREE.ShaderChunk[ "map_pars_vertex" ], + THREE.ShaderChunk[ "lightmap_pars_vertex" ], + THREE.ShaderChunk[ "envmap_pars_vertex" ], + THREE.ShaderChunk[ "lights_phong_pars_vertex" ], + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], - patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, material.envMap ); + "void main() {", - } + THREE.ShaderChunk[ "map_vertex" ], + THREE.ShaderChunk[ "lightmap_vertex" ], + THREE.ShaderChunk[ "color_vertex" ], + THREE.ShaderChunk[ "morphnormal_vertex" ], + THREE.ShaderChunk[ "skinbase_vertex" ], + THREE.ShaderChunk[ "skinnormal_vertex" ], + THREE.ShaderChunk[ "defaultnormal_vertex" ], - } else { + " vNormal = normalize( transformedNormal );", - _color.copy( material.color ); + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "default_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], - if ( material.vertexColors === THREE.FaceColors ) { + " vViewPosition = -mvPosition.xyz;", - _color.multiply( element.color ); + THREE.ShaderChunk[ "worldpos_vertex" ], + THREE.ShaderChunk[ "envmap_vertex" ], + THREE.ShaderChunk[ "lights_phong_vertex" ], + THREE.ShaderChunk[ "shadowmap_vertex" ], - } + "}" - material.wireframe === true - ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) - : fillPath( _color ); + ].join("\n"), - } + fragmentShader: [ - } else if ( material instanceof THREE.MeshDepthMaterial ) { + "#define PHONG", - _color.r = _color.g = _color.b = 1 - smoothstep( v1.positionScreen.z * v1.positionScreen.w, _camera.near, _camera.far ); + "uniform vec3 diffuse;", + "uniform float opacity;", - material.wireframe === true - ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) - : fillPath( _color ); + "uniform vec3 ambient;", + "uniform vec3 emissive;", + "uniform vec3 specular;", + "uniform float shininess;", - } else if ( material instanceof THREE.MeshNormalMaterial ) { + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "map_pars_fragment" ], + THREE.ShaderChunk[ "alphamap_pars_fragment" ], + THREE.ShaderChunk[ "lightmap_pars_fragment" ], + THREE.ShaderChunk[ "envmap_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "lights_phong_pars_fragment" ], + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "bumpmap_pars_fragment" ], + THREE.ShaderChunk[ "normalmap_pars_fragment" ], + THREE.ShaderChunk[ "specularmap_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + + "void main() {", - _normal.copy( element.normalModel ).applyMatrix3( _normalViewMatrix ); + " gl_FragColor = vec4( vec3( 1.0 ), opacity );", - _color.setRGB( _normal.x, _normal.y, _normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 ); + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "map_fragment" ], + THREE.ShaderChunk[ "alphamap_fragment" ], + THREE.ShaderChunk[ "alphatest_fragment" ], + THREE.ShaderChunk[ "specularmap_fragment" ], - material.wireframe === true - ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) - : fillPath( _color ); + THREE.ShaderChunk[ "lights_phong_fragment" ], - } else { + THREE.ShaderChunk[ "lightmap_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "envmap_fragment" ], + THREE.ShaderChunk[ "shadowmap_fragment" ], - _color.setRGB( 1, 1, 1 ); + THREE.ShaderChunk[ "linear_to_gamma_fragment" ], - material.wireframe === true - ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) - : fillPath( _color ); + THREE.ShaderChunk[ "fog_fragment" ], - } + "}" - } + ].join("\n") - // + }, - function drawTriangle( x0, y0, x1, y1, x2, y2 ) { + 'particle_basic': { - _context.beginPath(); - _context.moveTo( x0, y0 ); - _context.lineTo( x1, y1 ); - _context.lineTo( x2, y2 ); - _context.closePath(); + uniforms: THREE.UniformsUtils.merge( [ - } + THREE.UniformsLib[ "particle" ], + THREE.UniformsLib[ "shadowmap" ] - function strokePath( color, linewidth, linecap, linejoin ) { + ] ), - setLineWidth( linewidth ); - setLineCap( linecap ); - setLineJoin( linejoin ); - setStrokeStyle( color.getStyle() ); + vertexShader: [ - _context.stroke(); + "uniform float size;", + "uniform float scale;", - _elemBox.expandByScalar( linewidth * 2 ); + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], - } + "void main() {", - function fillPath( color ) { + THREE.ShaderChunk[ "color_vertex" ], - setFillStyle( color.getStyle() ); - _context.fill(); + " vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", - } + " #ifdef USE_SIZEATTENUATION", + " gl_PointSize = size * ( scale / length( mvPosition.xyz ) );", + " #else", + " gl_PointSize = size;", + " #endif", - function onTextureUpdate ( event ) { + " gl_Position = projectionMatrix * mvPosition;", - textureToPattern( event.target ); + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + THREE.ShaderChunk[ "worldpos_vertex" ], + THREE.ShaderChunk[ "shadowmap_vertex" ], - } + "}" - function textureToPattern( texture ) { + ].join("\n"), - if ( texture instanceof THREE.CompressedTexture ) return; + fragmentShader: [ - var repeatX = texture.wrapS === THREE.RepeatWrapping; - var repeatY = texture.wrapT === THREE.RepeatWrapping; + "uniform vec3 psColor;", + "uniform float opacity;", - var image = texture.image; + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "map_particle_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], - var canvas = document.createElement( 'canvas' ); - canvas.width = image.width; - canvas.height = image.height; + "void main() {", - var context = canvas.getContext( '2d' ); - context.setTransform( 1, 0, 0, - 1, 0, image.height ); - context.drawImage( image, 0, 0 ); + " gl_FragColor = vec4( psColor, opacity );", - _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.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "map_particle_fragment" ], + THREE.ShaderChunk[ "alphatest_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "shadowmap_fragment" ], + THREE.ShaderChunk[ "fog_fragment" ], - } + "}" - function patternPath( x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2, texture ) { + ].join("\n") - if ( texture instanceof THREE.DataTexture ) return; + }, - if ( texture.hasEventListener( 'update', onTextureUpdate ) === false ) { + 'dashed': { - if ( texture.image !== undefined && texture.image.width > 0 ) { + uniforms: THREE.UniformsUtils.merge( [ - textureToPattern( texture ); + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "fog" ], + { + "scale" : { type: "f", value: 1 }, + "dashSize" : { type: "f", value: 1 }, + "totalSize": { type: "f", value: 2 } } - texture.addEventListener( 'update', onTextureUpdate ); + ] ), - } + vertexShader: [ - var pattern = _patterns[ texture.id ]; + "uniform float scale;", + "attribute float lineDistance;", - if ( pattern !== undefined ) { + "varying float vLineDistance;", - setFillStyle( pattern ); + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], - } else { + "void main() {", - setFillStyle( 'rgba(0,0,0,1)' ); - _context.fill(); + THREE.ShaderChunk[ "color_vertex" ], - return; + " vLineDistance = scale * lineDistance;", - } + " vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + " gl_Position = projectionMatrix * mvPosition;", - // http://extremelysatisfactorytotalitarianism.com/blog/?p=2120 + THREE.ShaderChunk[ "logdepthbuf_vertex" ], - 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; + "}" - u0 = ( u0 + offsetX ) * width; - v0 = ( v0 + offsetY ) * height; + ].join("\n"), - u1 = ( u1 + offsetX ) * width; - v1 = ( v1 + offsetY ) * height; + fragmentShader: [ - u2 = ( u2 + offsetX ) * width; - v2 = ( v2 + offsetY ) * height; + "uniform vec3 diffuse;", + "uniform float opacity;", - x1 -= x0; y1 -= y0; - x2 -= x0; y2 -= y0; + "uniform float dashSize;", + "uniform float totalSize;", - u1 -= u0; v1 -= v0; - u2 -= u0; v2 -= v0; + "varying float vLineDistance;", - det = u1 * v2 - u2 * v1; + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], - if ( det === 0 ) return; + "void main() {", - idet = 1 / det; + " if ( mod( vLineDistance, totalSize ) > dashSize ) {", - a = ( v2 * x1 - v1 * x2 ) * idet; - b = ( v2 * y1 - v1 * y2 ) * idet; - c = ( u1 * x2 - u2 * x1 ) * idet; - d = ( u1 * y2 - u2 * y1 ) * idet; + " discard;", - e = x0 - a * u0 - c * v0; - f = y0 - b * u0 - d * v0; + " }", - _context.save(); - _context.transform( a, b, c, d, e, f ); - _context.fill(); - _context.restore(); + " gl_FragColor = vec4( diffuse, opacity );", - } + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "fog_fragment" ], - function clipImage( x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2, image ) { + "}" - // http://extremelysatisfactorytotalitarianism.com/blog/?p=2120 + ].join("\n") - var a, b, c, d, e, f, det, idet, - width = image.width - 1, - height = image.height - 1; + }, - u0 *= width; v0 *= height; - u1 *= width; v1 *= height; - u2 *= width; v2 *= height; + 'depth': { - x1 -= x0; y1 -= y0; - x2 -= x0; y2 -= y0; + uniforms: { - u1 -= u0; v1 -= v0; - u2 -= u0; v2 -= v0; + "mNear": { type: "f", value: 1.0 }, + "mFar" : { type: "f", value: 2000.0 }, + "opacity" : { type: "f", value: 1.0 } - det = u1 * v2 - u2 * v1; + }, - idet = 1 / det; + vertexShader: [ - a = ( v2 * x1 - v1 * x2 ) * idet; - b = ( v2 * y1 - v1 * y2 ) * idet; - c = ( u1 * x2 - u2 * x1 ) * idet; - d = ( u1 * y2 - u2 * y1 ) * idet; + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], - e = x0 - a * u0 - c * v0; - f = y0 - b * u0 - d * v0; + "void main() {", - _context.save(); - _context.transform( a, b, c, d, e, f ); - _context.clip(); - _context.drawImage( image, 0, 0 ); - _context.restore(); + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "default_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], - } + "}" - // Hide anti-alias gaps + ].join("\n"), - function expand( v1, v2, pixels ) { + fragmentShader: [ - var x = v2.x - v1.x, y = v2.y - v1.y, - det = x * x + y * y, idet; + "uniform float mNear;", + "uniform float mFar;", + "uniform float opacity;", - if ( det === 0 ) return; + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], - idet = pixels / Math.sqrt( det ); + "void main() {", - x *= idet; y *= idet; + THREE.ShaderChunk[ "logdepthbuf_fragment" ], - v2.x += x; v2.y += y; - v1.x -= x; v1.y -= y; + " #ifdef USE_LOGDEPTHBUF_EXT", - } + " float depth = gl_FragDepthEXT / gl_FragCoord.w;", - // Context cached methods. + " #else", - function setOpacity( value ) { + " float depth = gl_FragCoord.z / gl_FragCoord.w;", - if ( _contextGlobalAlpha !== value ) { + " #endif", - _context.globalAlpha = value; - _contextGlobalAlpha = value; + " float color = 1.0 - smoothstep( mNear, mFar, depth );", + " gl_FragColor = vec4( vec3( color ), opacity );", - } + "}" - } + ].join("\n") - function setBlending( value ) { + }, - if ( _contextGlobalCompositeOperation !== value ) { + 'normal': { - if ( value === THREE.NormalBlending ) { + uniforms: { - _context.globalCompositeOperation = 'source-over'; + "opacity" : { type: "f", value: 1.0 } - } else if ( value === THREE.AdditiveBlending ) { + }, - _context.globalCompositeOperation = 'lighter'; + vertexShader: [ - } else if ( value === THREE.SubtractiveBlending ) { + "varying vec3 vNormal;", - _context.globalCompositeOperation = 'darker'; + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], - } + "void main() {", - _contextGlobalCompositeOperation = value; + " vNormal = normalize( normalMatrix * normal );", - } + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "default_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], - } + "}" - function setLineWidth( value ) { + ].join("\n"), - if ( _contextLineWidth !== value ) { + fragmentShader: [ - _context.lineWidth = value; - _contextLineWidth = value; + "uniform float opacity;", + "varying vec3 vNormal;", - } + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], - } + "void main() {", - function setLineCap( value ) { + " gl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );", - // "butt", "round", "square" + THREE.ShaderChunk[ "logdepthbuf_fragment" ], - if ( _contextLineCap !== value ) { + "}" - _context.lineCap = value; - _contextLineCap = value; + ].join("\n") - } + }, - } + /* ------------------------------------------------------------------------- + // Normal map shader + // - Blinn-Phong + // - normal + diffuse + specular + AO + displacement + reflection + shadow maps + // - point and directional lights (use with "lights: true" material option) + ------------------------------------------------------------------------- */ - function setLineJoin( value ) { + 'normalmap' : { - // "round", "bevel", "miter" + uniforms: THREE.UniformsUtils.merge( [ - if ( _contextLineJoin !== value ) { + THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "lights" ], + THREE.UniformsLib[ "shadowmap" ], - _context.lineJoin = value; - _contextLineJoin = value; + { - } + "enableAO" : { type: "i", value: 0 }, + "enableDiffuse" : { type: "i", value: 0 }, + "enableSpecular" : { type: "i", value: 0 }, + "enableReflection" : { type: "i", value: 0 }, + "enableDisplacement": { type: "i", value: 0 }, - } + "tDisplacement": { type: "t", value: null }, // must go first as this is vertex texture + "tDiffuse" : { type: "t", value: null }, + "tCube" : { type: "t", value: null }, + "tNormal" : { type: "t", value: null }, + "tSpecular" : { type: "t", value: null }, + "tAO" : { type: "t", value: null }, - function setStrokeStyle( value ) { + "uNormalScale": { type: "v2", value: new THREE.Vector2( 1, 1 ) }, - if ( _contextStrokeStyle !== value ) { + "uDisplacementBias": { type: "f", value: 0.0 }, + "uDisplacementScale": { type: "f", value: 1.0 }, - _context.strokeStyle = value; - _contextStrokeStyle = value; + "diffuse": { type: "c", value: new THREE.Color( 0xffffff ) }, + "specular": { type: "c", value: new THREE.Color( 0x111111 ) }, + "ambient": { type: "c", value: new THREE.Color( 0xffffff ) }, + "shininess": { type: "f", value: 30 }, + "opacity": { type: "f", value: 1 }, - } + "useRefract": { type: "i", value: 0 }, + "refractionRatio": { type: "f", value: 0.98 }, + "reflectivity": { type: "f", value: 0.5 }, - } + "uOffset" : { type: "v2", value: new THREE.Vector2( 0, 0 ) }, + "uRepeat" : { type: "v2", value: new THREE.Vector2( 1, 1 ) }, - function setFillStyle( value ) { + "wrapRGB" : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) } - if ( _contextFillStyle !== value ) { + } - _context.fillStyle = value; - _contextFillStyle = value; + ] ), - } + fragmentShader: [ - } + "uniform vec3 ambient;", + "uniform vec3 diffuse;", + "uniform vec3 specular;", + "uniform float shininess;", + "uniform float opacity;", - function setLineDash( value ) { + "uniform bool enableDiffuse;", + "uniform bool enableSpecular;", + "uniform bool enableAO;", + "uniform bool enableReflection;", - if ( _contextLineDash.length !== value.length ) { + "uniform sampler2D tDiffuse;", + "uniform sampler2D tNormal;", + "uniform sampler2D tSpecular;", + "uniform sampler2D tAO;", - _context.setLineDash( value ); - _contextLineDash = value; + "uniform samplerCube tCube;", - } + "uniform vec2 uNormalScale;", - } + "uniform bool useRefract;", + "uniform float refractionRatio;", + "uniform float reflectivity;", -}; + "varying vec3 vTangent;", + "varying vec3 vBinormal;", + "varying vec3 vNormal;", + "varying vec2 vUv;", -// File:src/renderers/shaders/ShaderChunk.js + "uniform vec3 ambientLightColor;", -THREE.ShaderChunk = {}; + "#if MAX_DIR_LIGHTS > 0", -// File:src/renderers/shaders/ShaderChunk/alphatest_fragment.glsl + " uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];", + " uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];", -THREE.ShaderChunk[ 'alphatest_fragment'] = "#ifdef ALPHATEST\n\n if ( gl_FragColor.a < ALPHATEST ) discard;\n\n#endif\n"; + "#endif", -// File:src/renderers/shaders/ShaderChunk/lights_lambert_vertex.glsl + "#if MAX_HEMI_LIGHTS > 0", -THREE.ShaderChunk[ 'lights_lambert_vertex'] = "vLightFront = vec3( 0.0 );\n\n#ifdef DOUBLE_SIDED\n\n vLightBack = vec3( 0.0 );\n\n#endif\n\ntransformedNormal = normalize( transformedNormal );\n\n#if MAX_DIR_LIGHTS > 0\n\nfor( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\n vec3 dirVector = normalize( lDirection.xyz );\n\n float dotProduct = dot( transformedNormal, dirVector );\n vec3 directionalLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n #ifdef DOUBLE_SIDED\n\n vec3 directionalLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n #ifdef WRAP_AROUND\n\n vec3 directionalLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n #endif\n\n #endif\n\n #ifdef WRAP_AROUND\n\n vec3 directionalLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n directionalLightWeighting = mix( directionalLightWeighting, directionalLightWeightingHalf, wrapRGB );\n\n #ifdef DOUBLE_SIDED\n\n directionalLightWeightingBack = mix( directionalLightWeightingBack, directionalLightWeightingHalfBack, wrapRGB );\n\n #endif\n\n #endif\n\n vLightFront += directionalLightColor[ i ] * directionalLightWeighting;\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += directionalLightColor[ i ] * directionalLightWeightingBack;\n\n #endif\n\n}\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n for( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\n vec3 lVector = lPosition.xyz - mvPosition.xyz;\n\n float lDistance = 1.0;\n if ( pointLightDistance[ i ] > 0.0 )\n lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );\n\n lVector = normalize( lVector );\n float dotProduct = dot( transformedNormal, lVector );\n\n vec3 pointLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n #ifdef DOUBLE_SIDED\n\n vec3 pointLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n #ifdef WRAP_AROUND\n\n vec3 pointLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n #endif\n\n #endif\n\n #ifdef WRAP_AROUND\n\n vec3 pointLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n pointLightWeighting = mix( pointLightWeighting, pointLightWeightingHalf, wrapRGB );\n\n #ifdef DOUBLE_SIDED\n\n pointLightWeightingBack = mix( pointLightWeightingBack, pointLightWeightingHalfBack, wrapRGB );\n\n #endif\n\n #endif\n\n vLightFront += pointLightColor[ i ] * pointLightWeighting * lDistance;\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += pointLightColor[ i ] * pointLightWeightingBack * lDistance;\n\n #endif\n\n }\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n for( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\n vec3 lVector = lPosition.xyz - mvPosition.xyz;\n\n float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - worldPosition.xyz ) );\n\n if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n spotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );\n\n float lDistance = 1.0;\n if ( spotLightDistance[ i ] > 0.0 )\n lDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );\n\n lVector = normalize( lVector );\n\n float dotProduct = dot( transformedNormal, lVector );\n vec3 spotLightWeighting = vec3( max( dotProduct, 0.0 ) );\n\n #ifdef DOUBLE_SIDED\n\n vec3 spotLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );\n\n #ifdef WRAP_AROUND\n\n vec3 spotLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );\n\n #endif\n\n #endif\n\n #ifdef WRAP_AROUND\n\n vec3 spotLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );\n spotLightWeighting = mix( spotLightWeighting, spotLightWeightingHalf, wrapRGB );\n\n #ifdef DOUBLE_SIDED\n\n spotLightWeightingBack = mix( spotLightWeightingBack, spotLightWeightingHalfBack, wrapRGB );\n\n #endif\n\n #endif\n\n vLightFront += spotLightColor[ i ] * spotLightWeighting * lDistance * spotEffect;\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += spotLightColor[ i ] * spotLightWeightingBack * lDistance * spotEffect;\n\n #endif\n\n }\n\n }\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n vec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );\n vec3 lVector = normalize( lDirection.xyz );\n\n float dotProduct = dot( transformedNormal, lVector );\n\n float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n float hemiDiffuseWeightBack = -0.5 * dotProduct + 0.5;\n\n vLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );\n\n #endif\n\n }\n\n#endif\n\nvLightFront = vLightFront * diffuse + ambient * ambientLightColor + emissive;\n\n#ifdef DOUBLE_SIDED\n\n vLightBack = vLightBack * diffuse + ambient * ambientLightColor + emissive;\n\n#endif"; + " uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];", + " uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];", + " uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];", -// File:src/renderers/shaders/ShaderChunk/map_particle_pars_fragment.glsl + "#endif", -THREE.ShaderChunk[ 'map_particle_pars_fragment'] = "#ifdef USE_MAP\n\n uniform sampler2D map;\n\n#endif"; + "#if MAX_POINT_LIGHTS > 0", -// File:src/renderers/shaders/ShaderChunk/default_vertex.glsl + " uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];", + " uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];", + " uniform float pointLightDistance[ MAX_POINT_LIGHTS ];", -THREE.ShaderChunk[ 'default_vertex'] = "vec4 mvPosition;\n\n#ifdef USE_SKINNING\n\n mvPosition = modelViewMatrix * skinned;\n\n#endif\n\n#if !defined( USE_SKINNING ) && defined( USE_MORPHTARGETS )\n\n mvPosition = modelViewMatrix * vec4( morphed, 1.0 );\n\n#endif\n\n#if !defined( USE_SKINNING ) && ! defined( USE_MORPHTARGETS )\n\n mvPosition = modelViewMatrix * vec4( position, 1.0 );\n\n#endif\n\ngl_Position = projectionMatrix * mvPosition;"; + "#endif", -// File:src/renderers/shaders/ShaderChunk/map_pars_fragment.glsl + "#if MAX_SPOT_LIGHTS > 0", -THREE.ShaderChunk[ 'map_pars_fragment'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n varying vec2 vUv;\n\n#endif\n\n#ifdef USE_MAP\n\n uniform sampler2D map;\n\n#endif"; + " uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];", + " uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];", + " uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];", + " uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];", + " uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];", + " uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];", -// File:src/renderers/shaders/ShaderChunk/skinnormal_vertex.glsl + "#endif", -THREE.ShaderChunk[ 'skinnormal_vertex'] = "#ifdef USE_SKINNING\n\n mat4 skinMatrix = mat4( 0.0 );\n skinMatrix += skinWeight.x * boneMatX;\n skinMatrix += skinWeight.y * boneMatY;\n skinMatrix += skinWeight.z * boneMatZ;\n skinMatrix += skinWeight.w * boneMatW;\n skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n\n #ifdef USE_MORPHNORMALS\n\n vec4 skinnedNormal = skinMatrix * vec4( morphedNormal, 0.0 );\n\n #else\n\n vec4 skinnedNormal = skinMatrix * vec4( normal, 0.0 );\n\n #endif\n\n#endif\n"; + "#ifdef WRAP_AROUND", -// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_vertex.glsl + " uniform vec3 wrapRGB;", -THREE.ShaderChunk[ 'logdepthbuf_pars_vertex'] = "#ifdef USE_LOGDEPTHBUF\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n varying float vFragDepth;\n\n #endif\n\n uniform float logDepthBufFC;\n\n#endif"; + "#endif", -// File:src/renderers/shaders/ShaderChunk/lightmap_pars_vertex.glsl + "varying vec3 vWorldPosition;", + "varying vec3 vViewPosition;", -THREE.ShaderChunk[ 'lightmap_pars_vertex'] = "#ifdef USE_LIGHTMAP\n\n varying vec2 vUv2;\n\n#endif"; + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], -// File:src/renderers/shaders/ShaderChunk/lights_phong_fragment.glsl + "void main() {", + THREE.ShaderChunk[ "logdepthbuf_fragment" ], -THREE.ShaderChunk[ 'lights_phong_fragment'] = "vec3 normal = normalize( vNormal );\nvec3 viewPosition = normalize( vViewPosition );\n\n#ifdef DOUBLE_SIDED\n\n normal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n\n#endif\n\n#ifdef USE_NORMALMAP\n\n normal = perturbNormal2Arb( -vViewPosition, normal );\n\n#elif defined( USE_BUMPMAP )\n\n normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n vec3 pointDiffuse = vec3( 0.0 );\n vec3 pointSpecular = vec3( 0.0 );\n\n for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\n vec3 lVector = lPosition.xyz + vViewPosition.xyz;\n\n float lDistance = 1.0;\n if ( pointLightDistance[ i ] > 0.0 )\n lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );\n\n lVector = normalize( lVector );\n\n // diffuse\n\n float dotProduct = dot( normal, lVector );\n\n #ifdef WRAP_AROUND\n\n float pointDiffuseWeightFull = max( dotProduct, 0.0 );\n float pointDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n vec3 pointDiffuseWeight = mix( vec3( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );\n\n #else\n\n float pointDiffuseWeight = max( dotProduct, 0.0 );\n\n #endif\n\n pointDiffuse += diffuse * pointLightColor[ i ] * pointDiffuseWeight * lDistance;\n\n // specular\n\n vec3 pointHalfVector = normalize( lVector + viewPosition );\n float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );\n float pointSpecularWeight = specularStrength * max( pow( pointDotNormalHalf, shininess ), 0.0 );\n\n float specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, pointHalfVector ), 0.0 ), 5.0 );\n pointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance * specularNormalization;\n\n }\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n vec3 spotDiffuse = vec3( 0.0 );\n vec3 spotSpecular = vec3( 0.0 );\n\n for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {\n\n vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );\n vec3 lVector = lPosition.xyz + vViewPosition.xyz;\n\n float lDistance = 1.0;\n if ( spotLightDistance[ i ] > 0.0 )\n lDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );\n\n lVector = normalize( lVector );\n\n float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );\n\n if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n spotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );\n\n // diffuse\n\n float dotProduct = dot( normal, lVector );\n\n #ifdef WRAP_AROUND\n\n float spotDiffuseWeightFull = max( dotProduct, 0.0 );\n float spotDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n vec3 spotDiffuseWeight = mix( vec3( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );\n\n #else\n\n float spotDiffuseWeight = max( dotProduct, 0.0 );\n\n #endif\n\n spotDiffuse += diffuse * spotLightColor[ i ] * spotDiffuseWeight * lDistance * spotEffect;\n\n // specular\n\n vec3 spotHalfVector = normalize( lVector + viewPosition );\n float spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );\n float spotSpecularWeight = specularStrength * max( pow( spotDotNormalHalf, shininess ), 0.0 );\n\n float specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, spotHalfVector ), 0.0 ), 5.0 );\n spotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * lDistance * specularNormalization * spotEffect;\n\n }\n\n }\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n vec3 dirDiffuse = vec3( 0.0 );\n vec3 dirSpecular = vec3( 0.0 );\n\n for( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\n vec3 dirVector = normalize( lDirection.xyz );\n\n // diffuse\n\n float dotProduct = dot( normal, dirVector );\n\n #ifdef WRAP_AROUND\n\n float dirDiffuseWeightFull = max( dotProduct, 0.0 );\n float dirDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );\n\n vec3 dirDiffuseWeight = mix( vec3( dirDiffuseWeightFull ), vec3( dirDiffuseWeightHalf ), wrapRGB );\n\n #else\n\n float dirDiffuseWeight = max( dotProduct, 0.0 );\n\n #endif\n\n dirDiffuse += diffuse * directionalLightColor[ i ] * dirDiffuseWeight;\n\n // specular\n\n vec3 dirHalfVector = normalize( dirVector + viewPosition );\n float dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );\n float dirSpecularWeight = specularStrength * max( pow( dirDotNormalHalf, shininess ), 0.0 );\n\n /*\n // fresnel term from skin shader\n const float F0 = 0.128;\n\n float base = 1.0 - dot( viewPosition, dirHalfVector );\n float exponential = pow( base, 5.0 );\n\n float fresnel = exponential + F0 * ( 1.0 - exponential );\n */\n\n /*\n // fresnel term from fresnel shader\n const float mFresnelBias = 0.08;\n const float mFresnelScale = 0.3;\n const float mFresnelPower = 5.0;\n\n float fresnel = mFresnelBias + mFresnelScale * pow( 1.0 + dot( normalize( -viewPosition ), normal ), mFresnelPower );\n */\n\n float specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n // dirSpecular += specular * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization * fresnel;\n\n vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( dirVector, dirHalfVector ), 0.0 ), 5.0 );\n dirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;\n\n\n }\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n vec3 hemiDiffuse = vec3( 0.0 );\n vec3 hemiSpecular = vec3( 0.0 );\n\n for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n vec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );\n vec3 lVector = normalize( lDirection.xyz );\n\n // diffuse\n\n float dotProduct = dot( normal, lVector );\n float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n vec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n hemiDiffuse += diffuse * hemiColor;\n\n // specular (sky light)\n\n vec3 hemiHalfVectorSky = normalize( lVector + viewPosition );\n float hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;\n float hemiSpecularWeightSky = specularStrength * max( pow( max( hemiDotNormalHalfSky, 0.0 ), shininess ), 0.0 );\n\n // specular (ground light)\n\n vec3 lVectorGround = -lVector;\n\n vec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );\n float hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;\n float hemiSpecularWeightGround = specularStrength * max( pow( max( hemiDotNormalHalfGround, 0.0 ), shininess ), 0.0 );\n\n float dotProductGround = dot( normal, lVectorGround );\n\n float specularNormalization = ( shininess + 2.0 ) / 8.0;\n\n vec3 schlickSky = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, hemiHalfVectorSky ), 0.0 ), 5.0 );\n vec3 schlickGround = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 0.0 ), 5.0 );\n hemiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );\n\n }\n\n#endif\n\nvec3 totalDiffuse = vec3( 0.0 );\nvec3 totalSpecular = vec3( 0.0 );\n\n#if MAX_DIR_LIGHTS > 0\n\n totalDiffuse += dirDiffuse;\n totalSpecular += dirSpecular;\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n totalDiffuse += hemiDiffuse;\n totalSpecular += hemiSpecular;\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n totalDiffuse += pointDiffuse;\n totalSpecular += pointSpecular;\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n totalDiffuse += spotDiffuse;\n totalSpecular += spotSpecular;\n\n#endif\n\n#ifdef METAL\n\n gl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient + totalSpecular );\n\n#else\n\n gl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient ) + totalSpecular;\n\n#endif"; + " gl_FragColor = vec4( vec3( 1.0 ), opacity );", -// File:src/renderers/shaders/ShaderChunk/fog_pars_fragment.glsl + " vec3 specularTex = vec3( 1.0 );", -THREE.ShaderChunk[ 'fog_pars_fragment'] = "#ifdef USE_FOG\n\n uniform vec3 fogColor;\n\n #ifdef FOG_EXP2\n\n uniform float fogDensity;\n\n #else\n\n uniform float fogNear;\n uniform float fogFar;\n #endif\n\n#endif"; + " vec3 normalTex = texture2D( tNormal, vUv ).xyz * 2.0 - 1.0;", + " normalTex.xy *= uNormalScale;", + " normalTex = normalize( normalTex );", -// File:src/renderers/shaders/ShaderChunk/morphnormal_vertex.glsl + " if( enableDiffuse ) {", -THREE.ShaderChunk[ 'morphnormal_vertex'] = "#ifdef USE_MORPHNORMALS\n\n vec3 morphedNormal = vec3( 0.0 );\n\n morphedNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n morphedNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n morphedNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n morphedNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n\n morphedNormal += normal;\n\n#endif"; + " #ifdef GAMMA_INPUT", -// File:src/renderers/shaders/ShaderChunk/envmap_pars_fragment.glsl + " vec4 texelColor = texture2D( tDiffuse, vUv );", + " texelColor.xyz *= texelColor.xyz;", -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"; + " gl_FragColor = gl_FragColor * texelColor;", -// File:src/renderers/shaders/ShaderChunk/logdepthbuf_fragment.glsl + " #else", -THREE.ShaderChunk[ 'logdepthbuf_fragment'] = "#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)\n\n gl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;\n\n#endif"; + " gl_FragColor = gl_FragColor * texture2D( tDiffuse, vUv );", -// File:src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl + " #endif", -THREE.ShaderChunk[ 'normalmap_pars_fragment'] = "#ifdef USE_NORMALMAP\n\n uniform sampler2D normalMap;\n uniform vec2 normalScale;\n\n // Per-Pixel Tangent Space Normal Mapping\n // http://hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping.html\n\n vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n\n vec3 q0 = dFdx( eye_pos.xyz );\n vec3 q1 = dFdy( eye_pos.xyz );\n vec2 st0 = dFdx( vUv.st );\n vec2 st1 = dFdy( vUv.st );\n\n vec3 S = normalize( q0 * st1.t - q1 * st0.t );\n vec3 T = normalize( -q0 * st1.s + q1 * st0.s );\n vec3 N = normalize( surf_norm );\n\n vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n mapN.xy = normalScale * mapN.xy;\n mat3 tsn = mat3( S, T, N );\n return normalize( tsn * mapN );\n\n }\n\n#endif\n"; + " }", -// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_vertex.glsl + " if( enableAO ) {", -THREE.ShaderChunk[ 'lights_phong_pars_vertex'] = "#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n varying vec3 vWorldPosition;\n\n#endif\n"; + " #ifdef GAMMA_INPUT", -// File:src/renderers/shaders/ShaderChunk/lightmap_pars_fragment.glsl + " vec4 aoColor = texture2D( tAO, vUv );", + " aoColor.xyz *= aoColor.xyz;", -THREE.ShaderChunk[ 'lightmap_pars_fragment'] = "#ifdef USE_LIGHTMAP\n\n varying vec2 vUv2;\n uniform sampler2D lightMap;\n\n#endif"; + " gl_FragColor.xyz = gl_FragColor.xyz * aoColor.xyz;", -// File:src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl + " #else", -THREE.ShaderChunk[ 'shadowmap_vertex'] = "#ifdef USE_SHADOWMAP\n\n for( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;\n\n }\n\n#endif"; + " gl_FragColor.xyz = gl_FragColor.xyz * texture2D( tAO, vUv ).xyz;", -// File:src/renderers/shaders/ShaderChunk/lights_phong_vertex.glsl + " #endif", -THREE.ShaderChunk[ 'lights_phong_vertex'] = "#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n vWorldPosition = worldPosition.xyz;\n\n#endif"; + " }", + + THREE.ShaderChunk[ "alphatest_fragment" ], -// File:src/renderers/shaders/ShaderChunk/map_fragment.glsl + " if( enableSpecular )", + " specularTex = texture2D( tSpecular, vUv ).xyz;", -THREE.ShaderChunk[ 'map_fragment'] = "#ifdef USE_MAP\n\n vec4 texelColor = texture2D( map, vUv );\n\n #ifdef GAMMA_INPUT\n\n texelColor.xyz *= texelColor.xyz;\n\n #endif\n\n gl_FragColor = gl_FragColor * texelColor;\n\n#endif"; + " mat3 tsb = mat3( normalize( vTangent ), normalize( vBinormal ), normalize( vNormal ) );", + " vec3 finalNormal = tsb * normalTex;", -// File:src/renderers/shaders/ShaderChunk/lightmap_vertex.glsl + " #ifdef FLIP_SIDED", -THREE.ShaderChunk[ 'lightmap_vertex'] = "#ifdef USE_LIGHTMAP\n\n vUv2 = uv2;\n\n#endif"; + " finalNormal = -finalNormal;", -// File:src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl + " #endif", -THREE.ShaderChunk[ 'map_particle_fragment'] = "#ifdef USE_MAP\n\n gl_FragColor = gl_FragColor * texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) );\n\n#endif"; + " vec3 normal = normalize( finalNormal );", + " vec3 viewPosition = normalize( vViewPosition );", -// File:src/renderers/shaders/ShaderChunk/color_pars_fragment.glsl + // point lights -THREE.ShaderChunk[ 'color_pars_fragment'] = "#ifdef USE_COLOR\n\n varying vec3 vColor;\n\n#endif\n"; + " #if MAX_POINT_LIGHTS > 0", -// File:src/renderers/shaders/ShaderChunk/color_vertex.glsl + " vec3 pointDiffuse = vec3( 0.0 );", + " vec3 pointSpecular = vec3( 0.0 );", -THREE.ShaderChunk[ 'color_vertex'] = "#ifdef USE_COLOR\n\n #ifdef GAMMA_INPUT\n\n vColor = color * color;\n\n #else\n\n vColor = color;\n\n #endif\n\n#endif"; + " for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {", -// File:src/renderers/shaders/ShaderChunk/skinning_vertex.glsl + " vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );", + " vec3 pointVector = lPosition.xyz + vViewPosition.xyz;", -THREE.ShaderChunk[ 'skinning_vertex'] = "#ifdef USE_SKINNING\n\n #ifdef USE_MORPHTARGETS\n\n vec4 skinVertex = bindMatrix * vec4( morphed, 1.0 );\n\n #else\n\n vec4 skinVertex = bindMatrix * vec4( position, 1.0 );\n\n #endif\n\n vec4 skinned = vec4( 0.0 );\n skinned += boneMatX * skinVertex * skinWeight.x;\n skinned += boneMatY * skinVertex * skinWeight.y;\n skinned += boneMatZ * skinVertex * skinWeight.z;\n skinned += boneMatW * skinVertex * skinWeight.w;\n skinned = bindMatrixInverse * skinned;\n\n#endif\n"; + " float pointDistance = 1.0;", + " if ( pointLightDistance[ i ] > 0.0 )", + " pointDistance = 1.0 - min( ( length( pointVector ) / pointLightDistance[ i ] ), 1.0 );", -// File:src/renderers/shaders/ShaderChunk/envmap_pars_vertex.glsl + " pointVector = normalize( pointVector );", -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"; + // diffuse -// File:src/renderers/shaders/ShaderChunk/linear_to_gamma_fragment.glsl + " #ifdef WRAP_AROUND", -THREE.ShaderChunk[ 'linear_to_gamma_fragment'] = "#ifdef GAMMA_OUTPUT\n\n gl_FragColor.xyz = sqrt( gl_FragColor.xyz );\n\n#endif"; + " float pointDiffuseWeightFull = max( dot( normal, pointVector ), 0.0 );", + " float pointDiffuseWeightHalf = max( 0.5 * dot( normal, pointVector ) + 0.5, 0.0 );", -// File:src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl + " vec3 pointDiffuseWeight = mix( vec3( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );", -THREE.ShaderChunk[ 'color_pars_vertex'] = "#ifdef USE_COLOR\n\n varying vec3 vColor;\n\n#endif"; + " #else", -// File:src/renderers/shaders/ShaderChunk/lights_lambert_pars_vertex.glsl + " float pointDiffuseWeight = max( dot( normal, pointVector ), 0.0 );", -THREE.ShaderChunk[ 'lights_lambert_pars_vertex'] = "uniform vec3 ambient;\nuniform vec3 diffuse;\nuniform vec3 emissive;\n\nuniform vec3 ambientLightColor;\n\n#if MAX_DIR_LIGHTS > 0\n\n uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#ifdef WRAP_AROUND\n\n uniform vec3 wrapRGB;\n\n#endif\n"; + " #endif", -// File:src/renderers/shaders/ShaderChunk/map_pars_vertex.glsl + " pointDiffuse += pointDistance * pointLightColor[ i ] * diffuse * pointDiffuseWeight;", -THREE.ShaderChunk[ 'map_pars_vertex'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n varying vec2 vUv;\n uniform vec4 offsetRepeat;\n\n#endif\n"; + // specular -// File:src/renderers/shaders/ShaderChunk/envmap_fragment.glsl + " vec3 pointHalfVector = normalize( pointVector + viewPosition );", + " float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );", + " float pointSpecularWeight = specularTex.r * max( pow( pointDotNormalHalf, shininess ), 0.0 );", -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"; + " float specularNormalization = ( shininess + 2.0 ) / 8.0;", -// File:src/renderers/shaders/ShaderChunk/specularmap_pars_fragment.glsl + " vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( pointVector, pointHalfVector ), 0.0 ), 5.0 );", + " pointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * pointDistance * specularNormalization;", -THREE.ShaderChunk[ 'specularmap_pars_fragment'] = "#ifdef USE_SPECULARMAP\n\n uniform sampler2D specularMap;\n\n#endif"; + " }", -// File:src/renderers/shaders/ShaderChunk/logdepthbuf_vertex.glsl + " #endif", -THREE.ShaderChunk[ 'logdepthbuf_vertex'] = "#ifdef USE_LOGDEPTHBUF\n\n gl_Position.z = log2(max(1e-6, gl_Position.w + 1.0)) * logDepthBufFC;\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n vFragDepth = 1.0 + gl_Position.w;\n\n#else\n\n gl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;\n\n #endif\n\n#endif"; + // spot lights -// File:src/renderers/shaders/ShaderChunk/morphtarget_pars_vertex.glsl + " #if MAX_SPOT_LIGHTS > 0", -THREE.ShaderChunk[ 'morphtarget_pars_vertex'] = "#ifdef USE_MORPHTARGETS\n\n #ifndef USE_MORPHNORMALS\n\n uniform float morphTargetInfluences[ 8 ];\n\n #else\n\n uniform float morphTargetInfluences[ 4 ];\n\n #endif\n\n#endif"; + " vec3 spotDiffuse = vec3( 0.0 );", + " vec3 spotSpecular = vec3( 0.0 );", -// File:src/renderers/shaders/ShaderChunk/specularmap_fragment.glsl + " for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {", -THREE.ShaderChunk[ 'specularmap_fragment'] = "float specularStrength;\n\n#ifdef USE_SPECULARMAP\n\n vec4 texelSpecular = texture2D( specularMap, vUv );\n specularStrength = texelSpecular.r;\n\n#else\n\n specularStrength = 1.0;\n\n#endif"; + " vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );", + " vec3 spotVector = lPosition.xyz + vViewPosition.xyz;", -// File:src/renderers/shaders/ShaderChunk/fog_fragment.glsl + " float spotDistance = 1.0;", + " if ( spotLightDistance[ i ] > 0.0 )", + " spotDistance = 1.0 - min( ( length( spotVector ) / spotLightDistance[ i ] ), 1.0 );", -THREE.ShaderChunk[ 'fog_fragment'] = "#ifdef USE_FOG\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n float depth = gl_FragDepthEXT / gl_FragCoord.w;\n\n #else\n\n float depth = gl_FragCoord.z / gl_FragCoord.w;\n\n #endif\n\n #ifdef FOG_EXP2\n\n const float LOG2 = 1.442695;\n float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );\n fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );\n\n #else\n\n float fogFactor = smoothstep( fogNear, fogFar, depth );\n\n #endif\n \n gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );\n\n#endif"; + " spotVector = normalize( spotVector );", -// File:src/renderers/shaders/ShaderChunk/bumpmap_pars_fragment.glsl + " float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );", -THREE.ShaderChunk[ 'bumpmap_pars_fragment'] = "#ifdef USE_BUMPMAP\n\n uniform sampler2D bumpMap;\n uniform float bumpScale;\n\n // Derivative maps - bump mapping unparametrized surfaces by Morten Mikkelsen\n // http://mmikkelsen3d.blogspot.sk/2011/07/derivative-maps.html\n\n // Evaluate the derivative of the height w.r.t. screen-space using forward differencing (listing 2)\n\n vec2 dHdxy_fwd() {\n\n vec2 dSTdx = dFdx( vUv );\n vec2 dSTdy = dFdy( vUv );\n\n float Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n\n return vec2( dBx, dBy );\n\n }\n\n vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n\n vec3 vSigmaX = dFdx( surf_pos );\n vec3 vSigmaY = dFdy( surf_pos );\n vec3 vN = surf_norm; // normalized\n\n vec3 R1 = cross( vSigmaY, vN );\n vec3 R2 = cross( vN, vSigmaX );\n\n float fDet = dot( vSigmaX, R1 );\n\n vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n return normalize( abs( fDet ) * surf_norm - vGrad );\n\n }\n\n#endif"; + " if ( spotEffect > spotLightAngleCos[ i ] ) {", -// File:src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl + " spotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );", -THREE.ShaderChunk[ 'defaultnormal_vertex'] = "vec3 objectNormal;\n\n#ifdef USE_SKINNING\n\n objectNormal = skinnedNormal.xyz;\n\n#endif\n\n#if !defined( USE_SKINNING ) && defined( USE_MORPHNORMALS )\n\n objectNormal = morphedNormal;\n\n#endif\n\n#if !defined( USE_SKINNING ) && ! defined( USE_MORPHNORMALS )\n\n objectNormal = normal;\n\n#endif\n\n#ifdef FLIP_SIDED\n\n objectNormal = -objectNormal;\n\n#endif\n\nvec3 transformedNormal = normalMatrix * objectNormal;"; + // diffuse -// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_fragment.glsl + " #ifdef WRAP_AROUND", -THREE.ShaderChunk[ 'lights_phong_pars_fragment'] = "uniform vec3 ambientLightColor;\n\n#if MAX_DIR_LIGHTS > 0\n\n uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\n uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n\n#endif\n\n#if MAX_HEMI_LIGHTS > 0\n\n uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];\n uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];\n\n#endif\n\n#if MAX_POINT_LIGHTS > 0\n\n uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\n\n uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0\n\n uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];\n uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];\n uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];\n uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];\n\n uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP ) || defined( USE_ENVMAP )\n\n varying vec3 vWorldPosition;\n\n#endif\n\n#ifdef WRAP_AROUND\n\n uniform vec3 wrapRGB;\n\n#endif\n\nvarying vec3 vViewPosition;\nvarying vec3 vNormal;"; + " float spotDiffuseWeightFull = max( dot( normal, spotVector ), 0.0 );", + " float spotDiffuseWeightHalf = max( 0.5 * dot( normal, spotVector ) + 0.5, 0.0 );", -// File:src/renderers/shaders/ShaderChunk/skinbase_vertex.glsl + " vec3 spotDiffuseWeight = mix( vec3( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );", -THREE.ShaderChunk[ 'skinbase_vertex'] = "#ifdef USE_SKINNING\n\n mat4 boneMatX = getBoneMatrix( skinIndex.x );\n mat4 boneMatY = getBoneMatrix( skinIndex.y );\n mat4 boneMatZ = getBoneMatrix( skinIndex.z );\n mat4 boneMatW = getBoneMatrix( skinIndex.w );\n\n#endif"; + " #else", -// File:src/renderers/shaders/ShaderChunk/map_vertex.glsl + " float spotDiffuseWeight = max( dot( normal, spotVector ), 0.0 );", -THREE.ShaderChunk[ 'map_vertex'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP )\n\n vUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n\n#endif"; + " #endif", -// File:src/renderers/shaders/ShaderChunk/lightmap_fragment.glsl + " spotDiffuse += spotDistance * spotLightColor[ i ] * diffuse * spotDiffuseWeight * spotEffect;", -THREE.ShaderChunk[ 'lightmap_fragment'] = "#ifdef USE_LIGHTMAP\n\n gl_FragColor = gl_FragColor * texture2D( lightMap, vUv2 );\n\n#endif"; + // specular -// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl + " vec3 spotHalfVector = normalize( spotVector + viewPosition );", + " float spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );", + " float spotSpecularWeight = specularTex.r * max( pow( spotDotNormalHalf, shininess ), 0.0 );", -THREE.ShaderChunk[ 'shadowmap_pars_vertex'] = "#ifdef USE_SHADOWMAP\n\n varying vec4 vShadowCoord[ MAX_SHADOWS ];\n uniform mat4 shadowMatrix[ MAX_SHADOWS ];\n\n#endif"; + " float specularNormalization = ( shininess + 2.0 ) / 8.0;", -// File:src/renderers/shaders/ShaderChunk/color_fragment.glsl + " vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( spotVector, spotHalfVector ), 0.0 ), 5.0 );", + " spotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * spotDistance * specularNormalization * spotEffect;", -THREE.ShaderChunk[ 'color_fragment'] = "#ifdef USE_COLOR\n\n gl_FragColor = gl_FragColor * vec4( vColor, 1.0 );\n\n#endif"; + " }", -// File:src/renderers/shaders/ShaderChunk/morphtarget_vertex.glsl + " }", -THREE.ShaderChunk[ 'morphtarget_vertex'] = "#ifdef USE_MORPHTARGETS\n\n vec3 morphed = vec3( 0.0 );\n morphed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n morphed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n morphed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n morphed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\n #ifndef USE_MORPHNORMALS\n\n morphed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n morphed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n morphed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n morphed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\n #endif\n\n morphed += position;\n\n#endif"; + " #endif", -// File:src/renderers/shaders/ShaderChunk/envmap_vertex.glsl + // directional lights -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"; + " #if MAX_DIR_LIGHTS > 0", -// File:src/renderers/shaders/ShaderChunk/shadowmap_fragment.glsl + " vec3 dirDiffuse = vec3( 0.0 );", + " vec3 dirSpecular = vec3( 0.0 );", -THREE.ShaderChunk[ 'shadowmap_fragment'] = "#ifdef USE_SHADOWMAP\n\n #ifdef SHADOWMAP_DEBUG\n\n vec3 frustumColors[3];\n frustumColors[0] = vec3( 1.0, 0.5, 0.0 );\n frustumColors[1] = vec3( 0.0, 1.0, 0.8 );\n frustumColors[2] = vec3( 0.0, 0.5, 1.0 );\n\n #endif\n\n #ifdef SHADOWMAP_CASCADE\n\n int inFrustumCount = 0;\n\n #endif\n\n float fDepth;\n vec3 shadowColor = vec3( 1.0 );\n\n for( int i = 0; i < MAX_SHADOWS; i ++ ) {\n\n vec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;\n\n // if ( something && something ) breaks ATI OpenGL shader compiler\n // if ( all( something, something ) ) using this instead\n\n bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n bool inFrustum = all( inFrustumVec );\n\n // don't shadow pixels outside of light frustum\n // use just first frustum (for cascades)\n // don't shadow pixels behind far plane of light frustum\n\n #ifdef SHADOWMAP_CASCADE\n\n inFrustumCount += int( inFrustum );\n bvec3 frustumTestVec = bvec3( inFrustum, inFrustumCount == 1, shadowCoord.z <= 1.0 );\n\n #else\n\n bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n\n #endif\n\n bool frustumTest = all( frustumTestVec );\n\n if ( frustumTest ) {\n\n shadowCoord.z += shadowBias[ i ];\n\n #if defined( SHADOWMAP_TYPE_PCF )\n\n // Percentage-close filtering\n // (9 pixel kernel)\n // http://fabiensanglard.net/shadowmappingPCF/\n\n float shadow = 0.0;\n\n /*\n // nested loops breaks shader compiler / validator on some ATI cards when using OpenGL\n // must enroll loop manually\n\n for ( float y = -1.25; y <= 1.25; y += 1.25 )\n for ( float x = -1.25; x <= 1.25; x += 1.25 ) {\n\n vec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy );\n\n // doesn't seem to produce any noticeable visual difference compared to simple texture2D lookup\n //vec4 rgbaDepth = texture2DProj( shadowMap[ i ], vec4( vShadowCoord[ i ].w * ( vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy ), 0.05, vShadowCoord[ i ].w ) );\n\n float fDepth = unpackDepth( rgbaDepth );\n\n if ( fDepth < shadowCoord.z )\n shadow += 1.0;\n\n }\n\n shadow /= 9.0;\n\n */\n\n const float shadowDelta = 1.0 / 9.0;\n\n float xPixelOffset = 1.0 / shadowMapSize[ i ].x;\n float yPixelOffset = 1.0 / shadowMapSize[ i ].y;\n\n float dx0 = -1.25 * xPixelOffset;\n float dy0 = -1.25 * yPixelOffset;\n float dx1 = 1.25 * xPixelOffset;\n float dy1 = 1.25 * yPixelOffset;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n if ( fDepth < shadowCoord.z ) shadow += shadowDelta;\n\n shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );\n\n #elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n\n // Percentage-close filtering\n // (9 pixel kernel)\n // http://fabiensanglard.net/shadowmappingPCF/\n\n float shadow = 0.0;\n\n float xPixelOffset = 1.0 / shadowMapSize[ i ].x;\n float yPixelOffset = 1.0 / shadowMapSize[ i ].y;\n\n float dx0 = -1.0 * xPixelOffset;\n float dy0 = -1.0 * yPixelOffset;\n float dx1 = 1.0 * xPixelOffset;\n float dy1 = 1.0 * yPixelOffset;\n\n mat3 shadowKernel;\n mat3 depthKernel;\n\n depthKernel[0][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );\n depthKernel[0][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );\n depthKernel[0][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );\n depthKernel[1][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );\n depthKernel[1][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );\n depthKernel[1][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );\n depthKernel[2][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );\n depthKernel[2][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );\n depthKernel[2][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );\n\n vec3 shadowZ = vec3( shadowCoord.z );\n shadowKernel[0] = vec3(lessThan(depthKernel[0], shadowZ ));\n shadowKernel[0] *= vec3(0.25);\n\n shadowKernel[1] = vec3(lessThan(depthKernel[1], shadowZ ));\n shadowKernel[1] *= vec3(0.25);\n\n shadowKernel[2] = vec3(lessThan(depthKernel[2], shadowZ ));\n shadowKernel[2] *= vec3(0.25);\n\n vec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[i].xy );\n\n shadowKernel[0] = mix( shadowKernel[1], shadowKernel[0], fractionalCoord.x );\n shadowKernel[1] = mix( shadowKernel[2], shadowKernel[1], fractionalCoord.x );\n\n vec4 shadowValues;\n shadowValues.x = mix( shadowKernel[0][1], shadowKernel[0][0], fractionalCoord.y );\n shadowValues.y = mix( shadowKernel[0][2], shadowKernel[0][1], fractionalCoord.y );\n shadowValues.z = mix( shadowKernel[1][1], shadowKernel[1][0], fractionalCoord.y );\n shadowValues.w = mix( shadowKernel[1][2], shadowKernel[1][1], fractionalCoord.y );\n\n shadow = dot( shadowValues, vec4( 1.0 ) );\n\n shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );\n\n #else\n\n vec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );\n float fDepth = unpackDepth( rgbaDepth );\n\n if ( fDepth < shadowCoord.z )\n\n // spot with multiple shadows is darker\n\n shadowColor = shadowColor * vec3( 1.0 - shadowDarkness[ i ] );\n\n // spot with multiple shadows has the same color as single shadow spot\n\n // shadowColor = min( shadowColor, vec3( shadowDarkness[ i ] ) );\n\n #endif\n\n }\n\n\n #ifdef SHADOWMAP_DEBUG\n\n #ifdef SHADOWMAP_CASCADE\n\n if ( inFrustum && inFrustumCount == 1 ) gl_FragColor.xyz *= frustumColors[ i ];\n\n #else\n\n if ( inFrustum ) gl_FragColor.xyz *= frustumColors[ i ];\n\n #endif\n\n #endif\n\n }\n\n #ifdef GAMMA_OUTPUT\n\n shadowColor *= shadowColor;\n\n #endif\n\n gl_FragColor.xyz = gl_FragColor.xyz * shadowColor;\n\n#endif\n"; + " for( int i = 0; i < MAX_DIR_LIGHTS; i++ ) {", -// File:src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl + " vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );", + " vec3 dirVector = normalize( lDirection.xyz );", -THREE.ShaderChunk[ 'worldpos_vertex'] = "#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )\n\n #ifdef USE_SKINNING\n\n vec4 worldPosition = modelMatrix * skinned;\n\n #endif\n\n #if defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )\n\n vec4 worldPosition = modelMatrix * vec4( morphed, 1.0 );\n\n #endif\n\n #if ! defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )\n\n vec4 worldPosition = modelMatrix * vec4( position, 1.0 );\n\n #endif\n\n#endif"; + // diffuse -// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl + " #ifdef WRAP_AROUND", -THREE.ShaderChunk[ 'shadowmap_pars_fragment'] = "#ifdef USE_SHADOWMAP\n\n uniform sampler2D shadowMap[ MAX_SHADOWS ];\n uniform vec2 shadowMapSize[ MAX_SHADOWS ];\n\n uniform float shadowDarkness[ MAX_SHADOWS ];\n uniform float shadowBias[ MAX_SHADOWS ];\n\n varying vec4 vShadowCoord[ MAX_SHADOWS ];\n\n float unpackDepth( const in vec4 rgba_depth ) {\n\n const vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n float depth = dot( rgba_depth, bit_shift );\n return depth;\n\n }\n\n#endif"; + " float directionalLightWeightingFull = max( dot( normal, dirVector ), 0.0 );", + " float directionalLightWeightingHalf = max( 0.5 * dot( normal, dirVector ) + 0.5, 0.0 );", -// File:src/renderers/shaders/ShaderChunk/skinning_pars_vertex.glsl + " vec3 dirDiffuseWeight = mix( vec3( directionalLightWeightingFull ), vec3( directionalLightWeightingHalf ), wrapRGB );", -THREE.ShaderChunk[ 'skinning_pars_vertex'] = "#ifdef USE_SKINNING\n\n uniform mat4 bindMatrix;\n uniform mat4 bindMatrixInverse;\n\n #ifdef BONE_TEXTURE\n\n uniform sampler2D boneTexture;\n uniform int boneTextureWidth;\n uniform int boneTextureHeight;\n\n mat4 getBoneMatrix( const in float i ) {\n\n float j = i * 4.0;\n float x = mod( j, float( boneTextureWidth ) );\n float y = floor( j / float( boneTextureWidth ) );\n\n float dx = 1.0 / float( boneTextureWidth );\n float dy = 1.0 / float( boneTextureHeight );\n\n y = dy * ( y + 0.5 );\n\n vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n\n mat4 bone = mat4( v1, v2, v3, v4 );\n\n return bone;\n\n }\n\n #else\n\n uniform mat4 boneGlobalMatrices[ MAX_BONES ];\n\n mat4 getBoneMatrix( const in float i ) {\n\n mat4 bone = boneGlobalMatrices[ int(i) ];\n return bone;\n\n }\n\n #endif\n\n#endif\n"; + " #else", -// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_fragment.glsl + " float dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );", -THREE.ShaderChunk[ 'logdepthbuf_pars_fragment'] = "#ifdef USE_LOGDEPTHBUF\n\n uniform float logDepthBufFC;\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n #extension GL_EXT_frag_depth : enable\n varying float vFragDepth;\n\n #endif\n\n#endif"; + " #endif", -// File:src/renderers/shaders/ShaderChunk/alphamap_fragment.glsl + " dirDiffuse += directionalLightColor[ i ] * diffuse * dirDiffuseWeight;", -THREE.ShaderChunk[ 'alphamap_fragment'] = "#ifdef USE_ALPHAMAP\n\n gl_FragColor.a *= texture2D( alphaMap, vUv ).g;\n\n#endif\n"; + // specular -// File:src/renderers/shaders/ShaderChunk/alphamap_pars_fragment.glsl + " vec3 dirHalfVector = normalize( dirVector + viewPosition );", + " float dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );", + " float dirSpecularWeight = specularTex.r * max( pow( dirDotNormalHalf, shininess ), 0.0 );", -THREE.ShaderChunk[ 'alphamap_pars_fragment'] = "#ifdef USE_ALPHAMAP\n\n uniform sampler2D alphaMap;\n\n#endif\n"; + " float specularNormalization = ( shininess + 2.0 ) / 8.0;", -// File:src/renderers/shaders/UniformsUtils.js + " vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( dirVector, dirHalfVector ), 0.0 ), 5.0 );", + " dirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;", -/** - * Uniform Utilities - */ + " }", -THREE.UniformsUtils = { + " #endif", - merge: function ( uniforms ) { + // hemisphere lights - var u, p, tmp, merged = {}; + " #if MAX_HEMI_LIGHTS > 0", - for ( u = 0; u < uniforms.length; u ++ ) { + " vec3 hemiDiffuse = vec3( 0.0 );", + " vec3 hemiSpecular = vec3( 0.0 );" , - tmp = this.clone( uniforms[ u ] ); + " for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {", - for ( p in tmp ) { + " vec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );", + " vec3 lVector = normalize( lDirection.xyz );", - merged[ p ] = tmp[ p ]; + // diffuse - } + " float dotProduct = dot( normal, lVector );", + " float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;", - } + " vec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );", - return merged; + " hemiDiffuse += diffuse * hemiColor;", - }, + // specular (sky light) - clone: function ( uniforms_src ) { - var u, p, parameter, parameter_src, uniforms_dst = {}; + " vec3 hemiHalfVectorSky = normalize( lVector + viewPosition );", + " float hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;", + " float hemiSpecularWeightSky = specularTex.r * max( pow( max( hemiDotNormalHalfSky, 0.0 ), shininess ), 0.0 );", - for ( u in uniforms_src ) { + // specular (ground light) - uniforms_dst[ u ] = {}; + " vec3 lVectorGround = -lVector;", - for ( p in uniforms_src[ u ] ) { + " vec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );", + " float hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;", + " float hemiSpecularWeightGround = specularTex.r * max( pow( max( hemiDotNormalHalfGround, 0.0 ), shininess ), 0.0 );", - parameter_src = uniforms_src[ u ][ p ]; + " float dotProductGround = dot( normal, lVectorGround );", - if ( parameter_src instanceof THREE.Color || - parameter_src instanceof THREE.Vector2 || - parameter_src instanceof THREE.Vector3 || - parameter_src instanceof THREE.Vector4 || - parameter_src instanceof THREE.Matrix4 || - parameter_src instanceof THREE.Texture ) { + " float specularNormalization = ( shininess + 2.0 ) / 8.0;", - uniforms_dst[ u ][ p ] = parameter_src.clone(); + " vec3 schlickSky = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, hemiHalfVectorSky ), 0.0 ), 5.0 );", + " vec3 schlickGround = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 0.0 ), 5.0 );", + " hemiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );", - } else if ( parameter_src instanceof Array ) { + " }", - uniforms_dst[ u ][ p ] = parameter_src.slice(); + " #endif", - } else { + // all lights contribution summation - uniforms_dst[ u ][ p ] = parameter_src; + " vec3 totalDiffuse = vec3( 0.0 );", + " vec3 totalSpecular = vec3( 0.0 );", - } + " #if MAX_DIR_LIGHTS > 0", - } + " totalDiffuse += dirDiffuse;", + " totalSpecular += dirSpecular;", - } + " #endif", - return uniforms_dst; + " #if MAX_HEMI_LIGHTS > 0", - } + " totalDiffuse += hemiDiffuse;", + " totalSpecular += hemiSpecular;", -}; + " #endif", -// File:src/renderers/shaders/UniformsLib.js + " #if MAX_POINT_LIGHTS > 0", -/** - * Uniforms library for shared webgl shaders - */ + " totalDiffuse += pointDiffuse;", + " totalSpecular += pointSpecular;", -THREE.UniformsLib = { + " #endif", - common: { + " #if MAX_SPOT_LIGHTS > 0", - "diffuse" : { type: "c", value: new THREE.Color( 0xeeeeee ) }, - "opacity" : { type: "f", value: 1.0 }, + " totalDiffuse += spotDiffuse;", + " totalSpecular += spotSpecular;", - "map" : { type: "t", value: null }, - "offsetRepeat" : { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) }, + " #endif", - "lightMap" : { type: "t", value: null }, - "specularMap" : { type: "t", value: null }, - "alphaMap" : { type: "t", value: null }, + " #ifdef METAL", - "envMap" : { type: "t", value: null }, - "flipEnvMap" : { type: "f", value: - 1 }, - "useRefract" : { type: "i", value: 0 }, - "reflectivity" : { type: "f", value: 1.0 }, - "refractionRatio" : { type: "f", value: 0.98 }, - "combine" : { type: "i", value: 0 }, + " gl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * ambient + totalSpecular );", - "morphTargetInfluences" : { type: "f", value: 0 } + " #else", - }, + " gl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * ambient ) + totalSpecular;", - bump: { + " #endif", - "bumpMap" : { type: "t", value: null }, - "bumpScale" : { type: "f", value: 1 } + " if ( enableReflection ) {", - }, + " vec3 vReflect;", + " vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );", - normalmap: { + " if ( useRefract ) {", - "normalMap" : { type: "t", value: null }, - "normalScale" : { type: "v2", value: new THREE.Vector2( 1, 1 ) } - }, + " vReflect = refract( cameraToVertex, normal, refractionRatio );", - fog : { + " } else {", - "fogDensity" : { type: "f", value: 0.00025 }, - "fogNear" : { type: "f", value: 1 }, - "fogFar" : { type: "f", value: 2000 }, - "fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) } + " vReflect = reflect( cameraToVertex, normal );", - }, + " }", - lights: { + " vec4 cubeColor = textureCube( tCube, vec3( -vReflect.x, vReflect.yz ) );", - "ambientLightColor" : { type: "fv", value: [] }, + " #ifdef GAMMA_INPUT", - "directionalLightDirection" : { type: "fv", value: [] }, - "directionalLightColor" : { type: "fv", value: [] }, + " cubeColor.xyz *= cubeColor.xyz;", - "hemisphereLightDirection" : { type: "fv", value: [] }, - "hemisphereLightSkyColor" : { type: "fv", value: [] }, - "hemisphereLightGroundColor" : { type: "fv", value: [] }, + " #endif", - "pointLightColor" : { type: "fv", value: [] }, - "pointLightPosition" : { type: "fv", value: [] }, - "pointLightDistance" : { type: "fv1", value: [] }, + " gl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularTex.r * reflectivity );", - "spotLightColor" : { type: "fv", value: [] }, - "spotLightPosition" : { type: "fv", value: [] }, - "spotLightDirection" : { type: "fv", value: [] }, - "spotLightDistance" : { type: "fv1", value: [] }, - "spotLightAngleCos" : { type: "fv1", value: [] }, - "spotLightExponent" : { type: "fv1", value: [] } + " }", - }, + THREE.ShaderChunk[ "shadowmap_fragment" ], + THREE.ShaderChunk[ "linear_to_gamma_fragment" ], + THREE.ShaderChunk[ "fog_fragment" ], - particle: { + "}" - "psColor" : { type: "c", value: new THREE.Color( 0xeeeeee ) }, - "opacity" : { type: "f", value: 1.0 }, - "size" : { type: "f", value: 1.0 }, - "scale" : { type: "f", value: 1.0 }, - "map" : { type: "t", value: null }, + ].join("\n"), - "fogDensity" : { type: "f", value: 0.00025 }, - "fogNear" : { type: "f", value: 1 }, - "fogFar" : { type: "f", value: 2000 }, - "fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) } + vertexShader: [ - }, + "attribute vec4 tangent;", - shadowmap: { + "uniform vec2 uOffset;", + "uniform vec2 uRepeat;", - "shadowMap": { type: "tv", value: [] }, - "shadowMapSize": { type: "v2v", value: [] }, + "uniform bool enableDisplacement;", - "shadowBias" : { type: "fv1", value: [] }, - "shadowDarkness": { type: "fv1", value: [] }, + "#ifdef VERTEX_TEXTURES", - "shadowMatrix" : { type: "m4v", value: [] } + " uniform sampler2D tDisplacement;", + " uniform float uDisplacementScale;", + " uniform float uDisplacementBias;", - } + "#endif", -}; + "varying vec3 vTangent;", + "varying vec3 vBinormal;", + "varying vec3 vNormal;", + "varying vec2 vUv;", -// File:src/renderers/shaders/ShaderLib.js + "varying vec3 vWorldPosition;", + "varying vec3 vViewPosition;", -/** - * Webgl Shader Library for three.js - * - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - * @author mikael emtinger / http://gomo.se/ - */ + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + "void main() {", -THREE.ShaderLib = { + THREE.ShaderChunk[ "skinbase_vertex" ], + THREE.ShaderChunk[ "skinnormal_vertex" ], - 'basic': { + // normal, tangent and binormal vectors - uniforms: THREE.UniformsUtils.merge( [ + " #ifdef USE_SKINNING", - THREE.UniformsLib[ "common" ], - THREE.UniformsLib[ "fog" ], - THREE.UniformsLib[ "shadowmap" ] + " vNormal = normalize( normalMatrix * skinnedNormal.xyz );", - ] ), + " vec4 skinnedTangent = skinMatrix * vec4( tangent.xyz, 0.0 );", + " vTangent = normalize( normalMatrix * skinnedTangent.xyz );", - vertexShader: [ + " #else", - THREE.ShaderChunk[ "map_pars_vertex" ], - THREE.ShaderChunk[ "lightmap_pars_vertex" ], - THREE.ShaderChunk[ "envmap_pars_vertex" ], - THREE.ShaderChunk[ "color_pars_vertex" ], - THREE.ShaderChunk[ "morphtarget_pars_vertex" ], - THREE.ShaderChunk[ "skinning_pars_vertex" ], - THREE.ShaderChunk[ "shadowmap_pars_vertex" ], - THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + " vNormal = normalize( normalMatrix * normal );", + " vTangent = normalize( normalMatrix * tangent.xyz );", - "void main() {", + " #endif", - THREE.ShaderChunk[ "map_vertex" ], - THREE.ShaderChunk[ "lightmap_vertex" ], - THREE.ShaderChunk[ "color_vertex" ], - THREE.ShaderChunk[ "skinbase_vertex" ], + " vBinormal = normalize( cross( vNormal, vTangent ) * tangent.w );", - " #ifdef USE_ENVMAP", + " vUv = uv * uRepeat + uOffset;", - THREE.ShaderChunk[ "morphnormal_vertex" ], - THREE.ShaderChunk[ "skinnormal_vertex" ], - THREE.ShaderChunk[ "defaultnormal_vertex" ], + // displacement mapping - " #endif", + " vec3 displacedPosition;", - THREE.ShaderChunk[ "morphtarget_vertex" ], - THREE.ShaderChunk[ "skinning_vertex" ], - THREE.ShaderChunk[ "default_vertex" ], - THREE.ShaderChunk[ "logdepthbuf_vertex" ], + " #ifdef VERTEX_TEXTURES", - THREE.ShaderChunk[ "worldpos_vertex" ], - THREE.ShaderChunk[ "envmap_vertex" ], - THREE.ShaderChunk[ "shadowmap_vertex" ], + " if ( enableDisplacement ) {", - "}" + " vec3 dv = texture2D( tDisplacement, uv ).xyz;", + " float df = uDisplacementScale * dv.x + uDisplacementBias;", + " displacedPosition = position + normalize( normal ) * df;", - ].join("\n"), + " } else {", - fragmentShader: [ + " #ifdef USE_SKINNING", - "uniform vec3 diffuse;", - "uniform float opacity;", + " vec4 skinVertex = bindMatrix * vec4( position, 1.0 );", - THREE.ShaderChunk[ "color_pars_fragment" ], - THREE.ShaderChunk[ "map_pars_fragment" ], - THREE.ShaderChunk[ "alphamap_pars_fragment" ], - THREE.ShaderChunk[ "lightmap_pars_fragment" ], - THREE.ShaderChunk[ "envmap_pars_fragment" ], - THREE.ShaderChunk[ "fog_pars_fragment" ], - THREE.ShaderChunk[ "shadowmap_pars_fragment" ], - THREE.ShaderChunk[ "specularmap_pars_fragment" ], - THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + " vec4 skinned = vec4( 0.0 );", + " skinned += boneMatX * skinVertex * skinWeight.x;", + " skinned += boneMatY * skinVertex * skinWeight.y;", + " skinned += boneMatZ * skinVertex * skinWeight.z;", + " skinned += boneMatW * skinVertex * skinWeight.w;", + " skinned = bindMatrixInverse * skinned;", - "void main() {", + " displacedPosition = skinned.xyz;", - " gl_FragColor = vec4( diffuse, opacity );", + " #else", - THREE.ShaderChunk[ "logdepthbuf_fragment" ], - THREE.ShaderChunk[ "map_fragment" ], - THREE.ShaderChunk[ "alphamap_fragment" ], - THREE.ShaderChunk[ "alphatest_fragment" ], - THREE.ShaderChunk[ "specularmap_fragment" ], - THREE.ShaderChunk[ "lightmap_fragment" ], - THREE.ShaderChunk[ "color_fragment" ], - THREE.ShaderChunk[ "envmap_fragment" ], - THREE.ShaderChunk[ "shadowmap_fragment" ], + " displacedPosition = position;", - THREE.ShaderChunk[ "linear_to_gamma_fragment" ], + " #endif", - THREE.ShaderChunk[ "fog_fragment" ], + " }", - "}" + " #else", - ].join("\n") + " #ifdef USE_SKINNING", - }, + " vec4 skinVertex = bindMatrix * vec4( position, 1.0 );", - 'lambert': { + " vec4 skinned = vec4( 0.0 );", + " skinned += boneMatX * skinVertex * skinWeight.x;", + " skinned += boneMatY * skinVertex * skinWeight.y;", + " skinned += boneMatZ * skinVertex * skinWeight.z;", + " skinned += boneMatW * skinVertex * skinWeight.w;", + " skinned = bindMatrixInverse * skinned;", - uniforms: THREE.UniformsUtils.merge( [ + " displacedPosition = skinned.xyz;", - THREE.UniformsLib[ "common" ], - THREE.UniformsLib[ "fog" ], - THREE.UniformsLib[ "lights" ], - THREE.UniformsLib[ "shadowmap" ], + " #else", - { - "ambient" : { type: "c", value: new THREE.Color( 0xffffff ) }, - "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) }, - "wrapRGB" : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) } - } + " displacedPosition = position;", - ] ), + " #endif", - vertexShader: [ + " #endif", - "#define LAMBERT", + // - "varying vec3 vLightFront;", + " vec4 mvPosition = modelViewMatrix * vec4( displacedPosition, 1.0 );", + " vec4 worldPosition = modelMatrix * vec4( displacedPosition, 1.0 );", - "#ifdef DOUBLE_SIDED", + " gl_Position = projectionMatrix * mvPosition;", - " varying vec3 vLightBack;", + THREE.ShaderChunk[ "logdepthbuf_vertex" ], - "#endif", + // - THREE.ShaderChunk[ "map_pars_vertex" ], - THREE.ShaderChunk[ "lightmap_pars_vertex" ], - THREE.ShaderChunk[ "envmap_pars_vertex" ], - THREE.ShaderChunk[ "lights_lambert_pars_vertex" ], - THREE.ShaderChunk[ "color_pars_vertex" ], - THREE.ShaderChunk[ "morphtarget_pars_vertex" ], - THREE.ShaderChunk[ "skinning_pars_vertex" ], - THREE.ShaderChunk[ "shadowmap_pars_vertex" ], - THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + " vWorldPosition = worldPosition.xyz;", + " vViewPosition = -mvPosition.xyz;", - "void main() {", + // shadows - THREE.ShaderChunk[ "map_vertex" ], - THREE.ShaderChunk[ "lightmap_vertex" ], - THREE.ShaderChunk[ "color_vertex" ], + " #ifdef USE_SHADOWMAP", - THREE.ShaderChunk[ "morphnormal_vertex" ], - THREE.ShaderChunk[ "skinbase_vertex" ], - THREE.ShaderChunk[ "skinnormal_vertex" ], - THREE.ShaderChunk[ "defaultnormal_vertex" ], + " for( int i = 0; i < MAX_SHADOWS; i ++ ) {", - THREE.ShaderChunk[ "morphtarget_vertex" ], - THREE.ShaderChunk[ "skinning_vertex" ], - THREE.ShaderChunk[ "default_vertex" ], - THREE.ShaderChunk[ "logdepthbuf_vertex" ], + " vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;", - THREE.ShaderChunk[ "worldpos_vertex" ], - THREE.ShaderChunk[ "envmap_vertex" ], - THREE.ShaderChunk[ "lights_lambert_vertex" ], - THREE.ShaderChunk[ "shadowmap_vertex" ], + " }", + + " #endif", "}" - ].join("\n"), + ].join("\n") - fragmentShader: [ + }, - "uniform float opacity;", + /* ------------------------------------------------------------------------- + // Cube map shader + ------------------------------------------------------------------------- */ - "varying vec3 vLightFront;", + 'cube': { - "#ifdef DOUBLE_SIDED", + uniforms: { "tCube": { type: "t", value: null }, + "tFlip": { type: "f", value: - 1 } }, - " varying vec3 vLightBack;", + vertexShader: [ - "#endif", + "varying vec3 vWorldPosition;", - THREE.ShaderChunk[ "color_pars_fragment" ], - THREE.ShaderChunk[ "map_pars_fragment" ], - THREE.ShaderChunk[ "alphamap_pars_fragment" ], - THREE.ShaderChunk[ "lightmap_pars_fragment" ], - THREE.ShaderChunk[ "envmap_pars_fragment" ], - THREE.ShaderChunk[ "fog_pars_fragment" ], - THREE.ShaderChunk[ "shadowmap_pars_fragment" ], - THREE.ShaderChunk[ "specularmap_pars_fragment" ], - THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], "void main() {", - " gl_FragColor = vec4( vec3( 1.0 ), opacity );", + " vec4 worldPosition = modelMatrix * vec4( position, 1.0 );", + " vWorldPosition = worldPosition.xyz;", - THREE.ShaderChunk[ "logdepthbuf_fragment" ], - THREE.ShaderChunk[ "map_fragment" ], - THREE.ShaderChunk[ "alphamap_fragment" ], - THREE.ShaderChunk[ "alphatest_fragment" ], - THREE.ShaderChunk[ "specularmap_fragment" ], + " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", - " #ifdef DOUBLE_SIDED", + THREE.ShaderChunk[ "logdepthbuf_vertex" ], - //"float isFront = float( gl_FrontFacing );", - //"gl_FragColor.xyz *= isFront * vLightFront + ( 1.0 - isFront ) * vLightBack;", + "}" - " if ( gl_FrontFacing )", - " gl_FragColor.xyz *= vLightFront;", - " else", - " gl_FragColor.xyz *= vLightBack;", + ].join("\n"), - " #else", + fragmentShader: [ - " gl_FragColor.xyz *= vLightFront;", + "uniform samplerCube tCube;", + "uniform float tFlip;", - " #endif", + "varying vec3 vWorldPosition;", - THREE.ShaderChunk[ "lightmap_fragment" ], - THREE.ShaderChunk[ "color_fragment" ], - THREE.ShaderChunk[ "envmap_fragment" ], - THREE.ShaderChunk[ "shadowmap_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], - THREE.ShaderChunk[ "linear_to_gamma_fragment" ], + "void main() {", - THREE.ShaderChunk[ "fog_fragment" ], + " gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );", + + THREE.ShaderChunk[ "logdepthbuf_fragment" ], "}" @@ -17645,2468 +17590,2502 @@ THREE.ShaderLib = { }, - 'phong': { - - uniforms: THREE.UniformsUtils.merge( [ - - THREE.UniformsLib[ "common" ], - THREE.UniformsLib[ "bump" ], - THREE.UniformsLib[ "normalmap" ], - THREE.UniformsLib[ "fog" ], - THREE.UniformsLib[ "lights" ], - THREE.UniformsLib[ "shadowmap" ], + /* Depth encoding into RGBA texture + * + * based on SpiderGL shadow map example + * http://spidergl.org/example.php?id=6 + * + * originally from + * http://www.gamedev.net/topic/442138-packing-a-float-into-a-a8r8g8b8-texture-shader/page__whichpage__1%25EF%25BF%25BD + * + * see also + * http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ + */ - { - "ambient" : { type: "c", value: new THREE.Color( 0xffffff ) }, - "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) }, - "specular" : { type: "c", value: new THREE.Color( 0x111111 ) }, - "shininess": { type: "f", value: 30 }, - "wrapRGB" : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) } - } + 'depthRGBA': { - ] ), + uniforms: {}, vertexShader: [ - "#define PHONG", - - "varying vec3 vViewPosition;", - "varying vec3 vNormal;", - - THREE.ShaderChunk[ "map_pars_vertex" ], - THREE.ShaderChunk[ "lightmap_pars_vertex" ], - THREE.ShaderChunk[ "envmap_pars_vertex" ], - THREE.ShaderChunk[ "lights_phong_pars_vertex" ], - THREE.ShaderChunk[ "color_pars_vertex" ], THREE.ShaderChunk[ "morphtarget_pars_vertex" ], THREE.ShaderChunk[ "skinning_pars_vertex" ], - THREE.ShaderChunk[ "shadowmap_pars_vertex" ], THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], "void main() {", - THREE.ShaderChunk[ "map_vertex" ], - THREE.ShaderChunk[ "lightmap_vertex" ], - THREE.ShaderChunk[ "color_vertex" ], - - THREE.ShaderChunk[ "morphnormal_vertex" ], THREE.ShaderChunk[ "skinbase_vertex" ], - THREE.ShaderChunk[ "skinnormal_vertex" ], - THREE.ShaderChunk[ "defaultnormal_vertex" ], - - " vNormal = normalize( transformedNormal );", - THREE.ShaderChunk[ "morphtarget_vertex" ], THREE.ShaderChunk[ "skinning_vertex" ], THREE.ShaderChunk[ "default_vertex" ], THREE.ShaderChunk[ "logdepthbuf_vertex" ], - " vViewPosition = -mvPosition.xyz;", - - THREE.ShaderChunk[ "worldpos_vertex" ], - THREE.ShaderChunk[ "envmap_vertex" ], - THREE.ShaderChunk[ "lights_phong_vertex" ], - THREE.ShaderChunk[ "shadowmap_vertex" ], - "}" ].join("\n"), fragmentShader: [ - "uniform vec3 diffuse;", - "uniform float opacity;", + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], - "uniform vec3 ambient;", - "uniform vec3 emissive;", - "uniform vec3 specular;", - "uniform float shininess;", + "vec4 pack_depth( const in float depth ) {", - THREE.ShaderChunk[ "color_pars_fragment" ], - THREE.ShaderChunk[ "map_pars_fragment" ], - THREE.ShaderChunk[ "alphamap_pars_fragment" ], - THREE.ShaderChunk[ "lightmap_pars_fragment" ], - THREE.ShaderChunk[ "envmap_pars_fragment" ], - THREE.ShaderChunk[ "fog_pars_fragment" ], - THREE.ShaderChunk[ "lights_phong_pars_fragment" ], - THREE.ShaderChunk[ "shadowmap_pars_fragment" ], - THREE.ShaderChunk[ "bumpmap_pars_fragment" ], - THREE.ShaderChunk[ "normalmap_pars_fragment" ], - THREE.ShaderChunk[ "specularmap_pars_fragment" ], - THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + " const vec4 bit_shift = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );", + " const vec4 bit_mask = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );", + " vec4 res = mod( depth * bit_shift * vec4( 255 ), vec4( 256 ) ) / vec4( 255 );", // " vec4 res = fract( depth * bit_shift );", + " res -= res.xxyz * bit_mask;", + " return res;", - "void main() {", + "}", - " gl_FragColor = vec4( vec3( 1.0 ), opacity );", + "void main() {", THREE.ShaderChunk[ "logdepthbuf_fragment" ], - THREE.ShaderChunk[ "map_fragment" ], - THREE.ShaderChunk[ "alphamap_fragment" ], - THREE.ShaderChunk[ "alphatest_fragment" ], - THREE.ShaderChunk[ "specularmap_fragment" ], - THREE.ShaderChunk[ "lights_phong_fragment" ], + " #ifdef USE_LOGDEPTHBUF_EXT", - THREE.ShaderChunk[ "lightmap_fragment" ], - THREE.ShaderChunk[ "color_fragment" ], - THREE.ShaderChunk[ "envmap_fragment" ], - THREE.ShaderChunk[ "shadowmap_fragment" ], + " gl_FragData[ 0 ] = pack_depth( gl_FragDepthEXT );", - THREE.ShaderChunk[ "linear_to_gamma_fragment" ], + " #else", - THREE.ShaderChunk[ "fog_fragment" ], + " gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );", + + " #endif", + + //"gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z / gl_FragCoord.w );", + //"float z = ( ( gl_FragCoord.z / gl_FragCoord.w ) - 3.0 ) / ( 4000.0 - 3.0 );", + //"gl_FragData[ 0 ] = pack_depth( z );", + //"gl_FragData[ 0 ] = vec4( z, z, z, 1.0 );", "}" ].join("\n") - }, + } - 'particle_basic': { +}; - uniforms: THREE.UniformsUtils.merge( [ +// File:src/renderers/WebGLRenderer.js - THREE.UniformsLib[ "particle" ], - THREE.UniformsLib[ "shadowmap" ] +/** + * @author supereggbert / http://www.paulbrunt.co.uk/ + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author szimek / https://github.com/szimek/ + */ - ] ), +THREE.WebGLRenderer = function ( parameters ) { - vertexShader: [ + console.log( 'THREE.WebGLRenderer', THREE.REVISION ); - "uniform float size;", - "uniform float scale;", + parameters = parameters || {}; - THREE.ShaderChunk[ "color_pars_vertex" ], - THREE.ShaderChunk[ "shadowmap_pars_vertex" ], - THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElement( 'canvas' ), + _context = parameters.context !== undefined ? parameters.context : null, - "void main() {", + _precision = parameters.precision !== undefined ? parameters.precision : 'highp', - THREE.ShaderChunk[ "color_vertex" ], + _alpha = parameters.alpha !== undefined ? parameters.alpha : false, + _depth = parameters.depth !== undefined ? parameters.depth : true, + _stencil = parameters.stencil !== undefined ? parameters.stencil : true, + _antialias = parameters.antialias !== undefined ? parameters.antialias : false, + _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, + _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, + _logarithmicDepthBuffer = parameters.logarithmicDepthBuffer !== undefined ? parameters.logarithmicDepthBuffer : false, - " vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + _clearColor = new THREE.Color( 0x000000 ), + _clearAlpha = 0; - " #ifdef USE_SIZEATTENUATION", - " gl_PointSize = size * ( scale / length( mvPosition.xyz ) );", - " #else", - " gl_PointSize = size;", - " #endif", + var lights = []; - " gl_Position = projectionMatrix * mvPosition;", + var _webglObjects = {}; + var _webglObjectsImmediate = []; - THREE.ShaderChunk[ "logdepthbuf_vertex" ], - THREE.ShaderChunk[ "worldpos_vertex" ], - THREE.ShaderChunk[ "shadowmap_vertex" ], + var opaqueObjects = []; + var transparentObjects = []; - "}" + var sprites = []; + var lensFlares = []; - ].join("\n"), + // public properties - fragmentShader: [ + this.domElement = _canvas; + this.context = null; + this.devicePixelRatio = parameters.devicePixelRatio !== undefined + ? parameters.devicePixelRatio + : self.devicePixelRatio !== undefined + ? self.devicePixelRatio + : 1; - "uniform vec3 psColor;", - "uniform float opacity;", + // clearing - THREE.ShaderChunk[ "color_pars_fragment" ], - THREE.ShaderChunk[ "map_particle_pars_fragment" ], - THREE.ShaderChunk[ "fog_pars_fragment" ], - THREE.ShaderChunk[ "shadowmap_pars_fragment" ], - THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + this.autoClear = true; + this.autoClearColor = true; + this.autoClearDepth = true; + this.autoClearStencil = true; - "void main() {", + // scene graph - " gl_FragColor = vec4( psColor, opacity );", + this.sortObjects = true; - THREE.ShaderChunk[ "logdepthbuf_fragment" ], - THREE.ShaderChunk[ "map_particle_fragment" ], - THREE.ShaderChunk[ "alphatest_fragment" ], - THREE.ShaderChunk[ "color_fragment" ], - THREE.ShaderChunk[ "shadowmap_fragment" ], - THREE.ShaderChunk[ "fog_fragment" ], + // physically based shading - "}" + this.gammaInput = false; + this.gammaOutput = false; - ].join("\n") + // shadow map - }, + this.shadowMapEnabled = false; + this.shadowMapType = THREE.PCFShadowMap; + this.shadowMapCullFace = THREE.CullFaceFront; + this.shadowMapDebug = false; + this.shadowMapCascade = false; - 'dashed': { + // morphs - uniforms: THREE.UniformsUtils.merge( [ + this.maxMorphTargets = 8; + this.maxMorphNormals = 4; - THREE.UniformsLib[ "common" ], - THREE.UniformsLib[ "fog" ], + // flags - { - "scale" : { type: "f", value: 1 }, - "dashSize" : { type: "f", value: 1 }, - "totalSize": { type: "f", value: 2 } - } + this.autoScaleCubemaps = true; - ] ), + // info - vertexShader: [ + this.info = { - "uniform float scale;", - "attribute float lineDistance;", + memory: { - "varying float vLineDistance;", + programs: 0, + geometries: 0, + textures: 0 - THREE.ShaderChunk[ "color_pars_vertex" ], - THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + }, - "void main() {", + render: { - THREE.ShaderChunk[ "color_vertex" ], + calls: 0, + vertices: 0, + faces: 0, + points: 0 - " vLineDistance = scale * lineDistance;", + } - " vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", - " gl_Position = projectionMatrix * mvPosition;", + }; - THREE.ShaderChunk[ "logdepthbuf_vertex" ], + // internal properties - "}" + var _this = this, - ].join("\n"), + _programs = [], - fragmentShader: [ + // internal state cache - "uniform vec3 diffuse;", - "uniform float opacity;", + _currentProgram = null, + _currentFramebuffer = null, + _currentMaterialId = - 1, + _currentGeometryGroupHash = - 1, + _currentCamera = null, - "uniform float dashSize;", - "uniform float totalSize;", + _usedTextureUnits = 0, - "varying float vLineDistance;", + // GL state cache - THREE.ShaderChunk[ "color_pars_fragment" ], - THREE.ShaderChunk[ "fog_pars_fragment" ], - THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + _oldDoubleSided = - 1, + _oldFlipSided = - 1, - "void main() {", + _oldBlending = - 1, - " if ( mod( vLineDistance, totalSize ) > dashSize ) {", + _oldBlendEquation = - 1, + _oldBlendSrc = - 1, + _oldBlendDst = - 1, - " discard;", + _oldDepthTest = - 1, + _oldDepthWrite = - 1, - " }", + _oldPolygonOffset = null, + _oldPolygonOffsetFactor = null, + _oldPolygonOffsetUnits = null, - " gl_FragColor = vec4( diffuse, opacity );", + _oldLineWidth = null, - THREE.ShaderChunk[ "logdepthbuf_fragment" ], - THREE.ShaderChunk[ "color_fragment" ], - THREE.ShaderChunk[ "fog_fragment" ], + _viewportX = 0, + _viewportY = 0, + _viewportWidth = _canvas.width, + _viewportHeight = _canvas.height, + _currentWidth = 0, + _currentHeight = 0, - "}" + _newAttributes = new Uint8Array( 16 ), + _enabledAttributes = new Uint8Array( 16 ), - ].join("\n") + // frustum - }, + _frustum = new THREE.Frustum(), - 'depth': { + // camera matrices cache - uniforms: { + _projScreenMatrix = new THREE.Matrix4(), + _projScreenMatrixPS = new THREE.Matrix4(), - "mNear": { type: "f", value: 1.0 }, - "mFar" : { type: "f", value: 2000.0 }, - "opacity" : { type: "f", value: 1.0 } + _vector3 = new THREE.Vector3(), - }, + // light arrays cache - vertexShader: [ + _direction = new THREE.Vector3(), - THREE.ShaderChunk[ "morphtarget_pars_vertex" ], - THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + _lightsNeedUpdate = true, - "void main() {", + _lights = { - THREE.ShaderChunk[ "morphtarget_vertex" ], - THREE.ShaderChunk[ "default_vertex" ], - THREE.ShaderChunk[ "logdepthbuf_vertex" ], + ambient: [ 0, 0, 0 ], + directional: { length: 0, colors:[], positions: [] }, + point: { length: 0, colors: [], positions: [], distances: [] }, + spot: { length: 0, colors: [], positions: [], distances: [], directions: [], anglesCos: [], exponents: [] }, + hemi: { length: 0, skyColors: [], groundColors: [], positions: [] } - "}" + }; - ].join("\n"), + // initialize - fragmentShader: [ + var _gl; - "uniform float mNear;", - "uniform float mFar;", - "uniform float opacity;", + try { - THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + var attributes = { + alpha: _alpha, + depth: _depth, + stencil: _stencil, + antialias: _antialias, + premultipliedAlpha: _premultipliedAlpha, + preserveDrawingBuffer: _preserveDrawingBuffer + }; - "void main() {", + _gl = _context || _canvas.getContext( 'webgl', attributes ) || _canvas.getContext( 'experimental-webgl', attributes ); - THREE.ShaderChunk[ "logdepthbuf_fragment" ], + if ( _gl === null ) { - " #ifdef USE_LOGDEPTHBUF_EXT", + if ( _canvas.getContext( 'webgl') !== null ) { - " float depth = gl_FragDepthEXT / gl_FragCoord.w;", + throw 'Error creating WebGL context with your selected attributes.'; - " #else", + } else { - " float depth = gl_FragCoord.z / gl_FragCoord.w;", + throw 'Error creating WebGL context.'; - " #endif", + } - " float color = 1.0 - smoothstep( mNear, mFar, depth );", - " gl_FragColor = vec4( vec3( color ), opacity );", + } - "}" + } catch ( error ) { - ].join("\n") + console.error( error ); - }, + } - 'normal': { + if ( _gl.getShaderPrecisionFormat === undefined ) { - uniforms: { + _gl.getShaderPrecisionFormat = function () { - "opacity" : { type: "f", value: 1.0 } + return { + 'rangeMin': 1, + 'rangeMax': 1, + 'precision': 1 + }; - }, + } - vertexShader: [ + } - "varying vec3 vNormal;", + var extensions = new THREE.WebGLExtensions( _gl ); - THREE.ShaderChunk[ "morphtarget_pars_vertex" ], - THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + extensions.get( 'OES_texture_float' ); + extensions.get( 'OES_texture_float_linear' ); + extensions.get( 'OES_standard_derivatives' ); - "void main() {", + if ( _logarithmicDepthBuffer ) { - " vNormal = normalize( normalMatrix * normal );", + extensions.get( 'EXT_frag_depth' ); - THREE.ShaderChunk[ "morphtarget_vertex" ], - THREE.ShaderChunk[ "default_vertex" ], - THREE.ShaderChunk[ "logdepthbuf_vertex" ], + } - "}" + // - ].join("\n"), + function setDefaultGLState() { - fragmentShader: [ + _gl.clearColor( 0, 0, 0, 1 ); + _gl.clearDepth( 1 ); + _gl.clearStencil( 0 ); - "uniform float opacity;", - "varying vec3 vNormal;", + _gl.enable( _gl.DEPTH_TEST ); + _gl.depthFunc( _gl.LEQUAL ); - THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + _gl.frontFace( _gl.CCW ); + _gl.cullFace( _gl.BACK ); + _gl.enable( _gl.CULL_FACE ); - "void main() {", + _gl.enable( _gl.BLEND ); + _gl.blendEquation( _gl.FUNC_ADD ); + _gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA ); - " gl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );", + _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight ); - THREE.ShaderChunk[ "logdepthbuf_fragment" ], + _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); - "}" + } - ].join("\n") + setDefaultGLState(); - }, + this.context = _gl; - /* ------------------------------------------------------------------------- - // Normal map shader - // - Blinn-Phong - // - normal + diffuse + specular + AO + displacement + reflection + shadow maps - // - point and directional lights (use with "lights: true" material option) - ------------------------------------------------------------------------- */ + // GPU capabilities - 'normalmap' : { + 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 ); - uniforms: THREE.UniformsUtils.merge( [ + var _supportsVertexTextures = _maxVertexTextures > 0; + var _supportsBoneTextures = _supportsVertexTextures && extensions.get( 'OES_texture_float' ); - THREE.UniformsLib[ "fog" ], - THREE.UniformsLib[ "lights" ], - THREE.UniformsLib[ "shadowmap" ], + // - { + 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 ); - "enableAO" : { type: "i", value: 0 }, - "enableDiffuse" : { type: "i", value: 0 }, - "enableSpecular" : { type: "i", value: 0 }, - "enableReflection" : { type: "i", value: 0 }, - "enableDisplacement": { type: "i", value: 0 }, + 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 ); - "tDisplacement": { type: "t", value: null }, // must go first as this is vertex texture - "tDiffuse" : { type: "t", value: null }, - "tCube" : { type: "t", value: null }, - "tNormal" : { type: "t", value: null }, - "tSpecular" : { type: "t", value: null }, - "tAO" : { type: "t", value: null }, + var getCompressedTextureFormats = ( function () { - "uNormalScale": { type: "v2", value: new THREE.Vector2( 1, 1 ) }, + var array; - "uDisplacementBias": { type: "f", value: 0.0 }, - "uDisplacementScale": { type: "f", value: 1.0 }, + return function () { - "diffuse": { type: "c", value: new THREE.Color( 0xffffff ) }, - "specular": { type: "c", value: new THREE.Color( 0x111111 ) }, - "ambient": { type: "c", value: new THREE.Color( 0xffffff ) }, - "shininess": { type: "f", value: 30 }, - "opacity": { type: "f", value: 1 }, + if ( array !== undefined ) { - "useRefract": { type: "i", value: 0 }, - "refractionRatio": { type: "f", value: 0.98 }, - "reflectivity": { type: "f", value: 0.5 }, + return array; - "uOffset" : { type: "v2", value: new THREE.Vector2( 0, 0 ) }, - "uRepeat" : { type: "v2", value: new THREE.Vector2( 1, 1 ) }, + } - "wrapRGB" : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) } + array = []; - } + if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) || extensions.get( 'WEBGL_compressed_texture_s3tc' ) ) { - ] ), + var formats = _gl.getParameter( _gl.COMPRESSED_TEXTURE_FORMATS ); - fragmentShader: [ + for ( var i = 0; i < formats.length; i ++ ){ - "uniform vec3 ambient;", - "uniform vec3 diffuse;", - "uniform vec3 specular;", - "uniform float shininess;", - "uniform float opacity;", + array.push( formats[ i ] ); - "uniform bool enableDiffuse;", - "uniform bool enableSpecular;", - "uniform bool enableAO;", - "uniform bool enableReflection;", + } - "uniform sampler2D tDiffuse;", - "uniform sampler2D tNormal;", - "uniform sampler2D tSpecular;", - "uniform sampler2D tAO;", + } + + return array; - "uniform samplerCube tCube;", + }; - "uniform vec2 uNormalScale;", + } )(); - "uniform bool useRefract;", - "uniform float refractionRatio;", - "uniform float reflectivity;", + // clamp precision to maximum available - "varying vec3 vTangent;", - "varying vec3 vBinormal;", - "varying vec3 vNormal;", - "varying vec2 vUv;", + var highpAvailable = _vertexShaderPrecisionHighpFloat.precision > 0 && _fragmentShaderPrecisionHighpFloat.precision > 0; + var mediumpAvailable = _vertexShaderPrecisionMediumpFloat.precision > 0 && _fragmentShaderPrecisionMediumpFloat.precision > 0; - "uniform vec3 ambientLightColor;", + if ( _precision === 'highp' && ! highpAvailable ) { - "#if MAX_DIR_LIGHTS > 0", + if ( mediumpAvailable ) { - " uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];", - " uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];", + _precision = 'mediump'; + console.warn( 'THREE.WebGLRenderer: highp not supported, using mediump.' ); - "#endif", + } else { - "#if MAX_HEMI_LIGHTS > 0", + _precision = 'lowp'; + console.warn( 'THREE.WebGLRenderer: highp and mediump not supported, using lowp.' ); - " uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];", - " uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];", - " uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];", + } - "#endif", + } - "#if MAX_POINT_LIGHTS > 0", + if ( _precision === 'mediump' && ! mediumpAvailable ) { - " uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];", - " uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];", - " uniform float pointLightDistance[ MAX_POINT_LIGHTS ];", + _precision = 'lowp'; + console.warn( 'THREE.WebGLRenderer: mediump not supported, using lowp.' ); - "#endif", + } - "#if MAX_SPOT_LIGHTS > 0", + // Plugins - " uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];", - " uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];", - " uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];", - " uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];", - " uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];", - " uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];", + var shadowMapPlugin = new THREE.ShadowMapPlugin( this, lights, _webglObjects, _webglObjectsImmediate ); - "#endif", + var spritePlugin = new THREE.SpritePlugin( this, sprites ); + var lensFlarePlugin = new THREE.LensFlarePlugin( this, lensFlares ); - "#ifdef WRAP_AROUND", + // API - " uniform vec3 wrapRGB;", + this.getContext = function () { - "#endif", + return _gl; - "varying vec3 vWorldPosition;", - "varying vec3 vViewPosition;", + }; - THREE.ShaderChunk[ "shadowmap_pars_fragment" ], - THREE.ShaderChunk[ "fog_pars_fragment" ], - THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + this.supportsVertexTextures = function () { - "void main() {", - THREE.ShaderChunk[ "logdepthbuf_fragment" ], + return _supportsVertexTextures; - " gl_FragColor = vec4( vec3( 1.0 ), opacity );", + }; - " vec3 specularTex = vec3( 1.0 );", + this.supportsFloatTextures = function () { - " vec3 normalTex = texture2D( tNormal, vUv ).xyz * 2.0 - 1.0;", - " normalTex.xy *= uNormalScale;", - " normalTex = normalize( normalTex );", + return extensions.get( 'OES_texture_float' ); - " if( enableDiffuse ) {", + }; - " #ifdef GAMMA_INPUT", + this.supportsStandardDerivatives = function () { - " vec4 texelColor = texture2D( tDiffuse, vUv );", - " texelColor.xyz *= texelColor.xyz;", + return extensions.get( 'OES_standard_derivatives' ); - " gl_FragColor = gl_FragColor * texelColor;", + }; - " #else", + this.supportsCompressedTextureS3TC = function () { - " gl_FragColor = gl_FragColor * texture2D( tDiffuse, vUv );", + return extensions.get( 'WEBGL_compressed_texture_s3tc' ); - " #endif", + }; - " }", + this.supportsCompressedTexturePVRTC = function () { - " if( enableAO ) {", + return extensions.get( 'WEBGL_compressed_texture_pvrtc' ); - " #ifdef GAMMA_INPUT", + }; - " vec4 aoColor = texture2D( tAO, vUv );", - " aoColor.xyz *= aoColor.xyz;", + this.supportsBlendMinMax = function () { - " gl_FragColor.xyz = gl_FragColor.xyz * aoColor.xyz;", + return extensions.get( 'EXT_blend_minmax' ); - " #else", + }; - " gl_FragColor.xyz = gl_FragColor.xyz * texture2D( tAO, vUv ).xyz;", + this.getMaxAnisotropy = ( function () { - " #endif", + var value; - " }", - - THREE.ShaderChunk[ "alphatest_fragment" ], + return function () { - " if( enableSpecular )", - " specularTex = texture2D( tSpecular, vUv ).xyz;", + if ( value !== undefined ) { - " mat3 tsb = mat3( normalize( vTangent ), normalize( vBinormal ), normalize( vNormal ) );", - " vec3 finalNormal = tsb * normalTex;", + return value; - " #ifdef FLIP_SIDED", + } - " finalNormal = -finalNormal;", + var extension = extensions.get( 'EXT_texture_filter_anisotropic' ); - " #endif", + value = extension !== null ? _gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ) : 0; - " vec3 normal = normalize( finalNormal );", - " vec3 viewPosition = normalize( vViewPosition );", + return value; - // point lights + } - " #if MAX_POINT_LIGHTS > 0", + } )(); - " vec3 pointDiffuse = vec3( 0.0 );", - " vec3 pointSpecular = vec3( 0.0 );", + this.getPrecision = function () { - " for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {", + return _precision; - " vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );", - " vec3 pointVector = lPosition.xyz + vViewPosition.xyz;", + }; - " float pointDistance = 1.0;", - " if ( pointLightDistance[ i ] > 0.0 )", - " pointDistance = 1.0 - min( ( length( pointVector ) / pointLightDistance[ i ] ), 1.0 );", + this.setSize = function ( width, height, updateStyle ) { - " pointVector = normalize( pointVector );", + _canvas.width = width * this.devicePixelRatio; + _canvas.height = height * this.devicePixelRatio; - // diffuse + if ( updateStyle !== false ) { - " #ifdef WRAP_AROUND", + _canvas.style.width = width + 'px'; + _canvas.style.height = height + 'px'; - " float pointDiffuseWeightFull = max( dot( normal, pointVector ), 0.0 );", - " float pointDiffuseWeightHalf = max( 0.5 * dot( normal, pointVector ) + 0.5, 0.0 );", + } - " vec3 pointDiffuseWeight = mix( vec3( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );", + this.setViewport( 0, 0, width, height ); - " #else", + }; - " float pointDiffuseWeight = max( dot( normal, pointVector ), 0.0 );", + this.setViewport = function ( x, y, width, height ) { - " #endif", + _viewportX = x * this.devicePixelRatio; + _viewportY = y * this.devicePixelRatio; - " pointDiffuse += pointDistance * pointLightColor[ i ] * diffuse * pointDiffuseWeight;", + _viewportWidth = width * this.devicePixelRatio; + _viewportHeight = height * this.devicePixelRatio; - // specular + _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight ); - " vec3 pointHalfVector = normalize( pointVector + viewPosition );", - " float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );", - " float pointSpecularWeight = specularTex.r * max( pow( pointDotNormalHalf, shininess ), 0.0 );", + }; - " float specularNormalization = ( shininess + 2.0 ) / 8.0;", + this.setScissor = function ( x, y, width, height ) { - " vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( pointVector, pointHalfVector ), 0.0 ), 5.0 );", - " pointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * pointDistance * specularNormalization;", + _gl.scissor( + x * this.devicePixelRatio, + y * this.devicePixelRatio, + width * this.devicePixelRatio, + height * this.devicePixelRatio + ); - " }", + }; - " #endif", + this.enableScissorTest = function ( enable ) { - // spot lights + enable ? _gl.enable( _gl.SCISSOR_TEST ) : _gl.disable( _gl.SCISSOR_TEST ); - " #if MAX_SPOT_LIGHTS > 0", + }; - " vec3 spotDiffuse = vec3( 0.0 );", - " vec3 spotSpecular = vec3( 0.0 );", + // Clearing - " for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {", + this.setClearColor = function ( color, alpha ) { - " vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );", - " vec3 spotVector = lPosition.xyz + vViewPosition.xyz;", + _clearColor.set( color ); + _clearAlpha = alpha !== undefined ? alpha : 1; - " float spotDistance = 1.0;", - " if ( spotLightDistance[ i ] > 0.0 )", - " spotDistance = 1.0 - min( ( length( spotVector ) / spotLightDistance[ i ] ), 1.0 );", + _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); - " spotVector = normalize( spotVector );", + }; - " float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );", + this.setClearColorHex = function ( hex, alpha ) { - " if ( spotEffect > spotLightAngleCos[ i ] ) {", + console.warn( 'THREE.WebGLRenderer: .setClearColorHex() is being removed. Use .setClearColor() instead.' ); + this.setClearColor( hex, alpha ); - " spotEffect = max( pow( max( spotEffect, 0.0 ), spotLightExponent[ i ] ), 0.0 );", + }; - // diffuse + this.getClearColor = function () { - " #ifdef WRAP_AROUND", + return _clearColor; - " float spotDiffuseWeightFull = max( dot( normal, spotVector ), 0.0 );", - " float spotDiffuseWeightHalf = max( 0.5 * dot( normal, spotVector ) + 0.5, 0.0 );", + }; - " vec3 spotDiffuseWeight = mix( vec3( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );", + this.getClearAlpha = function () { - " #else", + return _clearAlpha; - " float spotDiffuseWeight = max( dot( normal, spotVector ), 0.0 );", + }; - " #endif", + this.clear = function ( color, depth, stencil ) { - " spotDiffuse += spotDistance * spotLightColor[ i ] * diffuse * spotDiffuseWeight * spotEffect;", + var bits = 0; - // specular + if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT; + if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT; + if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT; - " vec3 spotHalfVector = normalize( spotVector + viewPosition );", - " float spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );", - " float spotSpecularWeight = specularTex.r * max( pow( spotDotNormalHalf, shininess ), 0.0 );", + _gl.clear( bits ); - " float specularNormalization = ( shininess + 2.0 ) / 8.0;", + }; - " vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( spotVector, spotHalfVector ), 0.0 ), 5.0 );", - " spotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * spotDistance * specularNormalization * spotEffect;", + this.clearColor = function () { - " }", + _gl.clear( _gl.COLOR_BUFFER_BIT ); - " }", + }; - " #endif", + this.clearDepth = function () { - // directional lights + _gl.clear( _gl.DEPTH_BUFFER_BIT ); - " #if MAX_DIR_LIGHTS > 0", + }; - " vec3 dirDiffuse = vec3( 0.0 );", - " vec3 dirSpecular = vec3( 0.0 );", + this.clearStencil = function () { - " for( int i = 0; i < MAX_DIR_LIGHTS; i++ ) {", + _gl.clear( _gl.STENCIL_BUFFER_BIT ); - " vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );", - " vec3 dirVector = normalize( lDirection.xyz );", + }; - // diffuse + this.clearTarget = function ( renderTarget, color, depth, stencil ) { - " #ifdef WRAP_AROUND", + this.setRenderTarget( renderTarget ); + this.clear( color, depth, stencil ); - " float directionalLightWeightingFull = max( dot( normal, dirVector ), 0.0 );", - " float directionalLightWeightingHalf = max( 0.5 * dot( normal, dirVector ) + 0.5, 0.0 );", + }; - " vec3 dirDiffuseWeight = mix( vec3( directionalLightWeightingFull ), vec3( directionalLightWeightingHalf ), wrapRGB );", + // Reset - " #else", + this.resetGLState = function () { - " float dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );", + _currentProgram = null; + _currentCamera = null; - " #endif", + _oldBlending = - 1; + _oldDepthTest = - 1; + _oldDepthWrite = - 1; + _oldDoubleSided = - 1; + _oldFlipSided = - 1; + _currentGeometryGroupHash = - 1; + _currentMaterialId = - 1; - " dirDiffuse += directionalLightColor[ i ] * diffuse * dirDiffuseWeight;", + _lightsNeedUpdate = true; - // specular + }; - " vec3 dirHalfVector = normalize( dirVector + viewPosition );", - " float dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );", - " float dirSpecularWeight = specularTex.r * max( pow( dirDotNormalHalf, shininess ), 0.0 );", + // Buffer allocation - " float specularNormalization = ( shininess + 2.0 ) / 8.0;", + function createParticleBuffers ( geometry ) { - " vec3 schlick = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( dirVector, dirHalfVector ), 0.0 ), 5.0 );", - " dirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;", + geometry.__webglVertexBuffer = _gl.createBuffer(); + geometry.__webglColorBuffer = _gl.createBuffer(); - " }", + _this.info.memory.geometries ++; - " #endif", + }; - // hemisphere lights + function createLineBuffers ( geometry ) { - " #if MAX_HEMI_LIGHTS > 0", + geometry.__webglVertexBuffer = _gl.createBuffer(); + geometry.__webglColorBuffer = _gl.createBuffer(); + geometry.__webglLineDistanceBuffer = _gl.createBuffer(); - " vec3 hemiDiffuse = vec3( 0.0 );", - " vec3 hemiSpecular = vec3( 0.0 );" , + _this.info.memory.geometries ++; - " for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {", + }; - " vec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );", - " vec3 lVector = normalize( lDirection.xyz );", + function createMeshBuffers ( geometryGroup ) { - // diffuse + geometryGroup.__webglVertexBuffer = _gl.createBuffer(); + geometryGroup.__webglNormalBuffer = _gl.createBuffer(); + geometryGroup.__webglTangentBuffer = _gl.createBuffer(); + geometryGroup.__webglColorBuffer = _gl.createBuffer(); + geometryGroup.__webglUVBuffer = _gl.createBuffer(); + geometryGroup.__webglUV2Buffer = _gl.createBuffer(); - " float dotProduct = dot( normal, lVector );", - " float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;", + geometryGroup.__webglSkinIndicesBuffer = _gl.createBuffer(); + geometryGroup.__webglSkinWeightsBuffer = _gl.createBuffer(); - " vec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );", + geometryGroup.__webglFaceBuffer = _gl.createBuffer(); + geometryGroup.__webglLineBuffer = _gl.createBuffer(); - " hemiDiffuse += diffuse * hemiColor;", + var m, ml; - // specular (sky light) + if ( geometryGroup.numMorphTargets ) { + geometryGroup.__webglMorphTargetsBuffers = []; - " vec3 hemiHalfVectorSky = normalize( lVector + viewPosition );", - " float hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;", - " float hemiSpecularWeightSky = specularTex.r * max( pow( max( hemiDotNormalHalfSky, 0.0 ), shininess ), 0.0 );", + for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) { - // specular (ground light) + geometryGroup.__webglMorphTargetsBuffers.push( _gl.createBuffer() ); - " vec3 lVectorGround = -lVector;", + } - " vec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );", - " float hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;", - " float hemiSpecularWeightGround = specularTex.r * max( pow( max( hemiDotNormalHalfGround, 0.0 ), shininess ), 0.0 );", + } - " float dotProductGround = dot( normal, lVectorGround );", + if ( geometryGroup.numMorphNormals ) { - " float specularNormalization = ( shininess + 2.0 ) / 8.0;", + geometryGroup.__webglMorphNormalsBuffers = []; - " vec3 schlickSky = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVector, hemiHalfVectorSky ), 0.0 ), 5.0 );", - " vec3 schlickGround = specular + vec3( 1.0 - specular ) * pow( max( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 0.0 ), 5.0 );", - " hemiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );", + for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) { - " }", + geometryGroup.__webglMorphNormalsBuffers.push( _gl.createBuffer() ); - " #endif", + } - // all lights contribution summation + } - " vec3 totalDiffuse = vec3( 0.0 );", - " vec3 totalSpecular = vec3( 0.0 );", + _this.info.memory.geometries ++; - " #if MAX_DIR_LIGHTS > 0", + }; - " totalDiffuse += dirDiffuse;", - " totalSpecular += dirSpecular;", + // Events - " #endif", + var onObjectRemoved = function ( event ) { - " #if MAX_HEMI_LIGHTS > 0", + var object = event.target; - " totalDiffuse += hemiDiffuse;", - " totalSpecular += hemiSpecular;", + object.traverse( function ( child ) { - " #endif", + child.removeEventListener( 'remove', onObjectRemoved ); - " #if MAX_POINT_LIGHTS > 0", + removeObject( child ); - " totalDiffuse += pointDiffuse;", - " totalSpecular += pointSpecular;", + } ); - " #endif", + }; - " #if MAX_SPOT_LIGHTS > 0", + var onGeometryDispose = function ( event ) { - " totalDiffuse += spotDiffuse;", - " totalSpecular += spotSpecular;", + var geometry = event.target; - " #endif", + geometry.removeEventListener( 'dispose', onGeometryDispose ); - " #ifdef METAL", + deallocateGeometry( geometry ); - " gl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * ambient + totalSpecular );", + }; - " #else", + var onTextureDispose = function ( event ) { - " gl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * ambient ) + totalSpecular;", + var texture = event.target; - " #endif", + texture.removeEventListener( 'dispose', onTextureDispose ); - " if ( enableReflection ) {", + deallocateTexture( texture ); - " vec3 vReflect;", - " vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );", + _this.info.memory.textures --; - " if ( useRefract ) {", - " vReflect = refract( cameraToVertex, normal, refractionRatio );", + }; - " } else {", + var onRenderTargetDispose = function ( event ) { - " vReflect = reflect( cameraToVertex, normal );", + var renderTarget = event.target; - " }", + renderTarget.removeEventListener( 'dispose', onRenderTargetDispose ); - " vec4 cubeColor = textureCube( tCube, vec3( -vReflect.x, vReflect.yz ) );", + deallocateRenderTarget( renderTarget ); - " #ifdef GAMMA_INPUT", + _this.info.memory.textures --; - " cubeColor.xyz *= cubeColor.xyz;", + }; - " #endif", + var onMaterialDispose = function ( event ) { - " gl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularTex.r * reflectivity );", + var material = event.target; - " }", + material.removeEventListener( 'dispose', onMaterialDispose ); - THREE.ShaderChunk[ "shadowmap_fragment" ], - THREE.ShaderChunk[ "linear_to_gamma_fragment" ], - THREE.ShaderChunk[ "fog_fragment" ], + deallocateMaterial( material ); - "}" + }; - ].join("\n"), + // Buffer deallocation - vertexShader: [ + var deleteBuffers = function ( geometry ) { + + var buffers = [ + '__webglVertexBuffer', + '__webglNormalBuffer', + '__webglTangentBuffer', + '__webglColorBuffer', + '__webglUVBuffer', + '__webglUV2Buffer', + + '__webglSkinIndicesBuffer', + '__webglSkinWeightsBuffer', + + '__webglFaceBuffer', + '__webglLineBuffer', + + '__webglLineDistanceBuffer' + ]; - "attribute vec4 tangent;", + for ( var i = 0, l = buffers.length; i < l; i ++ ) { - "uniform vec2 uOffset;", - "uniform vec2 uRepeat;", + var name = buffers[ i ]; - "uniform bool enableDisplacement;", + if ( geometry[ name ] !== undefined ) { - "#ifdef VERTEX_TEXTURES", + _gl.deleteBuffer( geometry[ name ] ); - " uniform sampler2D tDisplacement;", - " uniform float uDisplacementScale;", - " uniform float uDisplacementBias;", + delete geometry[ name ]; - "#endif", + } - "varying vec3 vTangent;", - "varying vec3 vBinormal;", - "varying vec3 vNormal;", - "varying vec2 vUv;", + } - "varying vec3 vWorldPosition;", - "varying vec3 vViewPosition;", + // custom attributes - THREE.ShaderChunk[ "skinning_pars_vertex" ], - THREE.ShaderChunk[ "shadowmap_pars_vertex" ], - THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + if ( geometry.__webglCustomAttributesList !== undefined ) { - "void main() {", + for ( var name in geometry.__webglCustomAttributesList ) { - THREE.ShaderChunk[ "skinbase_vertex" ], - THREE.ShaderChunk[ "skinnormal_vertex" ], + _gl.deleteBuffer( geometry.__webglCustomAttributesList[ name ].buffer ); - // normal, tangent and binormal vectors + } - " #ifdef USE_SKINNING", + delete geometry.__webglCustomAttributesList; - " vNormal = normalize( normalMatrix * skinnedNormal.xyz );", + } - " vec4 skinnedTangent = skinMatrix * vec4( tangent.xyz, 0.0 );", - " vTangent = normalize( normalMatrix * skinnedTangent.xyz );", + _this.info.memory.geometries --; - " #else", + }; - " vNormal = normalize( normalMatrix * normal );", - " vTangent = normalize( normalMatrix * tangent.xyz );", + var deallocateGeometry = function ( geometry ) { - " #endif", + delete geometry.__webglInit; - " vBinormal = normalize( cross( vNormal, vTangent ) * tangent.w );", + if ( geometry instanceof THREE.BufferGeometry ) { - " vUv = uv * uRepeat + uOffset;", + for ( var name in geometry.attributes ) { + + var attribute = geometry.attributes[ name ]; - // displacement mapping + if ( attribute.buffer !== undefined ) { - " vec3 displacedPosition;", + _gl.deleteBuffer( attribute.buffer ); - " #ifdef VERTEX_TEXTURES", + delete attribute.buffer; - " if ( enableDisplacement ) {", + } - " vec3 dv = texture2D( tDisplacement, uv ).xyz;", - " float df = uDisplacementScale * dv.x + uDisplacementBias;", - " displacedPosition = position + normalize( normal ) * df;", + } - " } else {", + _this.info.memory.geometries --; - " #ifdef USE_SKINNING", + } else { - " vec4 skinVertex = bindMatrix * vec4( position, 1.0 );", + var geometryGroupsList = geometryGroups[ geometry.id ]; - " vec4 skinned = vec4( 0.0 );", - " skinned += boneMatX * skinVertex * skinWeight.x;", - " skinned += boneMatY * skinVertex * skinWeight.y;", - " skinned += boneMatZ * skinVertex * skinWeight.z;", - " skinned += boneMatW * skinVertex * skinWeight.w;", - " skinned = bindMatrixInverse * skinned;", - - " displacedPosition = skinned.xyz;", - - " #else", + if ( geometryGroupsList !== undefined ) { - " displacedPosition = position;", + for ( var i = 0,l = geometryGroupsList.length; i < l; i ++ ) { - " #endif", + var geometryGroup = geometryGroupsList[ i ]; - " }", + if ( geometryGroup.numMorphTargets !== undefined ) { - " #else", + for ( var m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) { - " #ifdef USE_SKINNING", + _gl.deleteBuffer( geometryGroup.__webglMorphTargetsBuffers[ m ] ); - " vec4 skinVertex = bindMatrix * vec4( position, 1.0 );", + } - " vec4 skinned = vec4( 0.0 );", - " skinned += boneMatX * skinVertex * skinWeight.x;", - " skinned += boneMatY * skinVertex * skinWeight.y;", - " skinned += boneMatZ * skinVertex * skinWeight.z;", - " skinned += boneMatW * skinVertex * skinWeight.w;", - " skinned = bindMatrixInverse * skinned;", + delete geometryGroup.__webglMorphTargetsBuffers; - " displacedPosition = skinned.xyz;", + } - " #else", + if ( geometryGroup.numMorphNormals !== undefined ) { - " displacedPosition = position;", + for ( var m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) { - " #endif", + _gl.deleteBuffer( geometryGroup.__webglMorphNormalsBuffers[ m ] ); - " #endif", + } - // + delete geometryGroup.__webglMorphNormalsBuffers; - " vec4 mvPosition = modelViewMatrix * vec4( displacedPosition, 1.0 );", - " vec4 worldPosition = modelMatrix * vec4( displacedPosition, 1.0 );", + } - " gl_Position = projectionMatrix * mvPosition;", + deleteBuffers( geometryGroup ); - THREE.ShaderChunk[ "logdepthbuf_vertex" ], + } - // + delete geometryGroups[ geometry.id ]; - " vWorldPosition = worldPosition.xyz;", - " vViewPosition = -mvPosition.xyz;", + } else { - // shadows + deleteBuffers( geometry ); - " #ifdef USE_SHADOWMAP", + } - " for( int i = 0; i < MAX_SHADOWS; i ++ ) {", + } - " vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;", + // TOFIX: Workaround for deleted geometry being currently bound - " }", + _currentGeometryGroupHash = - 1; - " #endif", + }; - "}" + var deallocateTexture = function ( texture ) { - ].join("\n") + if ( texture.image && texture.image.__webglTextureCube ) { - }, + // cube texture - /* ------------------------------------------------------------------------- - // Cube map shader - ------------------------------------------------------------------------- */ + _gl.deleteTexture( texture.image.__webglTextureCube ); - 'cube': { + delete texture.image.__webglTextureCube; - uniforms: { "tCube": { type: "t", value: null }, - "tFlip": { type: "f", value: - 1 } }, + } else { - vertexShader: [ + // 2D texture - "varying vec3 vWorldPosition;", + if ( texture.__webglInit === undefined ) return; - THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + _gl.deleteTexture( texture.__webglTexture ); - "void main() {", + delete texture.__webglTexture; + delete texture.__webglInit; - " vec4 worldPosition = modelMatrix * vec4( position, 1.0 );", - " vWorldPosition = worldPosition.xyz;", + } - " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + }; - THREE.ShaderChunk[ "logdepthbuf_vertex" ], + var deallocateRenderTarget = function ( renderTarget ) { - "}" + if ( ! renderTarget || renderTarget.__webglTexture === undefined ) return; - ].join("\n"), + _gl.deleteTexture( renderTarget.__webglTexture ); - fragmentShader: [ + delete renderTarget.__webglTexture; - "uniform samplerCube tCube;", - "uniform float tFlip;", + if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) { - "varying vec3 vWorldPosition;", + for ( var i = 0; i < 6; i ++ ) { - THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + _gl.deleteFramebuffer( renderTarget.__webglFramebuffer[ i ] ); + _gl.deleteRenderbuffer( renderTarget.__webglRenderbuffer[ i ] ); - "void main() {", + } - " gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );", + } else { - THREE.ShaderChunk[ "logdepthbuf_fragment" ], + _gl.deleteFramebuffer( renderTarget.__webglFramebuffer ); + _gl.deleteRenderbuffer( renderTarget.__webglRenderbuffer ); - "}" + } - ].join("\n") + delete renderTarget.__webglFramebuffer; + delete renderTarget.__webglRenderbuffer; - }, + }; - /* Depth encoding into RGBA texture - * - * based on SpiderGL shadow map example - * http://spidergl.org/example.php?id=6 - * - * originally from - * http://www.gamedev.net/topic/442138-packing-a-float-into-a-a8r8g8b8-texture-shader/page__whichpage__1%25EF%25BF%25BD - * - * see also - * http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ - */ + var deallocateMaterial = function ( material ) { - 'depthRGBA': { + var program = material.program.program; - uniforms: {}, + if ( program === undefined ) return; - vertexShader: [ + material.program = undefined; - THREE.ShaderChunk[ "morphtarget_pars_vertex" ], - THREE.ShaderChunk[ "skinning_pars_vertex" ], - THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + // only deallocate GL program if this was the last use of shared program + // assumed there is only single copy of any program in the _programs list + // (that's how it's constructed) - "void main() {", + var i, il, programInfo; + var deleteProgram = false; - THREE.ShaderChunk[ "skinbase_vertex" ], - THREE.ShaderChunk[ "morphtarget_vertex" ], - THREE.ShaderChunk[ "skinning_vertex" ], - THREE.ShaderChunk[ "default_vertex" ], - THREE.ShaderChunk[ "logdepthbuf_vertex" ], + for ( i = 0, il = _programs.length; i < il; i ++ ) { - "}" + programInfo = _programs[ i ]; - ].join("\n"), + if ( programInfo.program === program ) { - fragmentShader: [ + programInfo.usedTimes --; - THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + if ( programInfo.usedTimes === 0 ) { - "vec4 pack_depth( const in float depth ) {", + deleteProgram = true; - " const vec4 bit_shift = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );", - " const vec4 bit_mask = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );", - " vec4 res = mod( depth * bit_shift * vec4( 255 ), vec4( 256 ) ) / vec4( 255 );", // " vec4 res = fract( depth * bit_shift );", - " res -= res.xxyz * bit_mask;", - " return res;", + } - "}", + break; - "void main() {", + } - THREE.ShaderChunk[ "logdepthbuf_fragment" ], + } - " #ifdef USE_LOGDEPTHBUF_EXT", + if ( deleteProgram === true ) { - " gl_FragData[ 0 ] = pack_depth( gl_FragDepthEXT );", + // avoid using array.splice, this is costlier than creating new array from scratch - " #else", + var newPrograms = []; - " gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );", + for ( i = 0, il = _programs.length; i < il; i ++ ) { - " #endif", + programInfo = _programs[ i ]; - //"gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z / gl_FragCoord.w );", - //"float z = ( ( gl_FragCoord.z / gl_FragCoord.w ) - 3.0 ) / ( 4000.0 - 3.0 );", - //"gl_FragData[ 0 ] = pack_depth( z );", - //"gl_FragData[ 0 ] = vec4( z, z, z, 1.0 );", + if ( programInfo.program !== program ) { - "}" + newPrograms.push( programInfo ); - ].join("\n") + } - } + } -}; + _programs = newPrograms; -// File:src/renderers/WebGLRenderer.js + _gl.deleteProgram( program ); -/** - * @author supereggbert / http://www.paulbrunt.co.uk/ - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * @author szimek / https://github.com/szimek/ - */ + _this.info.memory.programs --; -THREE.WebGLRenderer = function ( parameters ) { + } - console.log( 'THREE.WebGLRenderer', THREE.REVISION ); + }; - parameters = parameters || {}; + // Buffer initialization - var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElement( 'canvas' ), - _context = parameters.context !== undefined ? parameters.context : null, + function initCustomAttributes ( object ) { - _precision = parameters.precision !== undefined ? parameters.precision : 'highp', + var geometry = object.geometry; + var material = object.material; - _alpha = parameters.alpha !== undefined ? parameters.alpha : false, - _depth = parameters.depth !== undefined ? parameters.depth : true, - _stencil = parameters.stencil !== undefined ? parameters.stencil : true, - _antialias = parameters.antialias !== undefined ? parameters.antialias : false, - _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, - _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, - _logarithmicDepthBuffer = parameters.logarithmicDepthBuffer !== undefined ? parameters.logarithmicDepthBuffer : false, + var nvertices = geometry.vertices.length; - _clearColor = new THREE.Color( 0x000000 ), - _clearAlpha = 0; - - var opaqueObjects = []; - var transparentObjects = []; + if ( material.attributes ) { - // public properties + if ( geometry.__webglCustomAttributesList === undefined ) { - this.domElement = _canvas; - this.context = null; - this.devicePixelRatio = parameters.devicePixelRatio !== undefined - ? parameters.devicePixelRatio - : self.devicePixelRatio !== undefined - ? self.devicePixelRatio - : 1; + geometry.__webglCustomAttributesList = []; - // clearing + } - this.autoClear = true; - this.autoClearColor = true; - this.autoClearDepth = true; - this.autoClearStencil = true; + for ( var name in material.attributes ) { - // scene graph + var attribute = material.attributes[ name ]; - this.sortObjects = true; + if ( ! attribute.__webglInitialized || attribute.createUniqueBuffers ) { - // physically based shading + attribute.__webglInitialized = true; - this.gammaInput = false; - this.gammaOutput = false; + var size = 1; // "f" and "i" - // shadow map + if ( attribute.type === 'v2' ) size = 2; + else if ( attribute.type === 'v3' ) size = 3; + else if ( attribute.type === 'v4' ) size = 4; + else if ( attribute.type === 'c' ) size = 3; - this.shadowMapEnabled = false; - this.shadowMapAutoUpdate = true; - this.shadowMapType = THREE.PCFShadowMap; - this.shadowMapCullFace = THREE.CullFaceFront; - this.shadowMapDebug = false; - this.shadowMapCascade = false; + attribute.size = size; - // morphs + attribute.array = new Float32Array( nvertices * size ); - this.maxMorphTargets = 8; - this.maxMorphNormals = 4; + attribute.buffer = _gl.createBuffer(); + attribute.buffer.belongsToAttribute = name; - // flags + attribute.needsUpdate = true; - this.autoScaleCubemaps = true; + } - // custom render plugins + geometry.__webglCustomAttributesList.push( attribute ); - this.renderPluginsPre = []; - this.renderPluginsPost = []; + } - // info + } - this.info = { + }; - memory: { + function initParticleBuffers ( geometry, object ) { - programs: 0, - geometries: 0, - textures: 0 + var nvertices = geometry.vertices.length; - }, + geometry.__vertexArray = new Float32Array( nvertices * 3 ); + geometry.__colorArray = new Float32Array( nvertices * 3 ); - render: { + geometry.__sortArray = []; - calls: 0, - vertices: 0, - faces: 0, - points: 0 + geometry.__webglParticleCount = nvertices; - } + initCustomAttributes( object ); }; - // internal properties + function initLineBuffers ( geometry, object ) { - var _this = this, + var nvertices = geometry.vertices.length; - _programs = [], + geometry.__vertexArray = new Float32Array( nvertices * 3 ); + geometry.__colorArray = new Float32Array( nvertices * 3 ); + geometry.__lineDistanceArray = new Float32Array( nvertices * 1 ); - // internal state cache + geometry.__webglLineCount = nvertices; - _currentProgram = null, - _currentFramebuffer = null, - _currentMaterialId = - 1, - _currentGeometryGroupHash = null, - _currentCamera = null, + initCustomAttributes( object ); - _usedTextureUnits = 0, + }; - // GL state cache + function initMeshBuffers ( geometryGroup, object ) { - _oldDoubleSided = - 1, - _oldFlipSided = - 1, + var geometry = object.geometry, + faces3 = geometryGroup.faces3, - _oldBlending = - 1, + nvertices = faces3.length * 3, + ntris = faces3.length * 1, + nlines = faces3.length * 3, - _oldBlendEquation = - 1, - _oldBlendSrc = - 1, - _oldBlendDst = - 1, + material = getBufferMaterial( object, geometryGroup ); - _oldDepthTest = - 1, - _oldDepthWrite = - 1, + geometryGroup.__vertexArray = new Float32Array( nvertices * 3 ); + geometryGroup.__normalArray = new Float32Array( nvertices * 3 ); + geometryGroup.__colorArray = new Float32Array( nvertices * 3 ); + geometryGroup.__uvArray = new Float32Array( nvertices * 2 ); - _oldPolygonOffset = null, - _oldPolygonOffsetFactor = null, - _oldPolygonOffsetUnits = null, + if ( geometry.faceVertexUvs.length > 1 ) { - _oldLineWidth = null, + geometryGroup.__uv2Array = new Float32Array( nvertices * 2 ); - _viewportX = 0, - _viewportY = 0, - _viewportWidth = _canvas.width, - _viewportHeight = _canvas.height, - _currentWidth = 0, - _currentHeight = 0, + } - _newAttributes = new Uint8Array( 16 ), - _enabledAttributes = new Uint8Array( 16 ), + if ( geometry.hasTangents ) { - // frustum + geometryGroup.__tangentArray = new Float32Array( nvertices * 4 ); - _frustum = new THREE.Frustum(), + } - // camera matrices cache + if ( object.geometry.skinWeights.length && object.geometry.skinIndices.length ) { - _projScreenMatrix = new THREE.Matrix4(), - _projScreenMatrixPS = new THREE.Matrix4(), + geometryGroup.__skinIndexArray = new Float32Array( nvertices * 4 ); + geometryGroup.__skinWeightArray = new Float32Array( nvertices * 4 ); - _vector3 = new THREE.Vector3(), + } - // light arrays cache + var UintArray = extensions.get( 'OES_element_index_uint' ) !== null && ntris > 21845 ? Uint32Array : Uint16Array; // 65535 / 3 - _direction = new THREE.Vector3(), + geometryGroup.__typeArray = UintArray; + geometryGroup.__faceArray = new UintArray( ntris * 3 ); + geometryGroup.__lineArray = new UintArray( nlines * 2 ); - _lightsNeedUpdate = true, + var m, ml; - _lights = { + if ( geometryGroup.numMorphTargets ) { - ambient: [ 0, 0, 0 ], - directional: { length: 0, colors:[], positions: [] }, - point: { length: 0, colors: [], positions: [], distances: [] }, - spot: { length: 0, colors: [], positions: [], distances: [], directions: [], anglesCos: [], exponents: [] }, - hemi: { length: 0, skyColors: [], groundColors: [], positions: [] } + geometryGroup.__morphTargetsArrays = []; - }; + for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) { - // initialize + geometryGroup.__morphTargetsArrays.push( new Float32Array( nvertices * 3 ) ); - var _gl; + } - var _glExtensionTextureFloat; - var _glExtensionTextureFloatLinear; - var _glExtensionStandardDerivatives; - var _glExtensionTextureFilterAnisotropic; - var _glExtensionCompressedTextureS3TC; - var _glExtensionElementIndexUint; - var _glExtensionFragDepth; + } + if ( geometryGroup.numMorphNormals ) { - initGL(); + geometryGroup.__morphNormalsArrays = []; - setDefaultGLState(); + for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) { - this.context = _gl; + geometryGroup.__morphNormalsArrays.push( new Float32Array( nvertices * 3 ) ); - // 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 _maxAnisotropy = _glExtensionTextureFilterAnisotropic ? _gl.getParameter( _glExtensionTextureFilterAnisotropic.MAX_TEXTURE_MAX_ANISOTROPY_EXT ) : 0; + geometryGroup.__webglFaceCount = ntris * 3; + geometryGroup.__webglLineCount = nlines * 2; - var _supportsVertexTextures = ( _maxVertexTextures > 0 ); - var _supportsBoneTextures = _supportsVertexTextures && _glExtensionTextureFloat; - var _compressedTextureFormats = _glExtensionCompressedTextureS3TC ? _gl.getParameter( _gl.COMPRESSED_TEXTURE_FORMATS ) : []; + // custom attributes - // + if ( material.attributes ) { - 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 ); + if ( geometryGroup.__webglCustomAttributesList === undefined ) { - 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 ); + geometryGroup.__webglCustomAttributesList = []; - // clamp precision to maximum available + } - var highpAvailable = _vertexShaderPrecisionHighpFloat.precision > 0 && _fragmentShaderPrecisionHighpFloat.precision > 0; - var mediumpAvailable = _vertexShaderPrecisionMediumpFloat.precision > 0 && _fragmentShaderPrecisionMediumpFloat.precision > 0; + for ( var name in material.attributes ) { - if ( _precision === 'highp' && ! highpAvailable ) { + // Do a shallow copy of the attribute object so different geometryGroup chunks use different + // attribute buffers which are correctly indexed in the setMeshBuffers function - if ( mediumpAvailable ) { + var originalAttribute = material.attributes[ name ]; - _precision = 'mediump'; - console.warn( 'THREE.WebGLRenderer: highp not supported, using mediump.' ); + var attribute = {}; - } else { + for ( var property in originalAttribute ) { - _precision = 'lowp'; - console.warn( 'THREE.WebGLRenderer: highp and mediump not supported, using lowp.' ); + attribute[ property ] = originalAttribute[ property ]; - } + } - } + if ( ! attribute.__webglInitialized || attribute.createUniqueBuffers ) { - if ( _precision === 'mediump' && ! mediumpAvailable ) { + attribute.__webglInitialized = true; - _precision = 'lowp'; - console.warn( 'THREE.WebGLRenderer: mediump not supported, using lowp.' ); + var size = 1; // "f" and "i" - } + if ( attribute.type === 'v2' ) size = 2; + else if ( attribute.type === 'v3' ) size = 3; + else if ( attribute.type === 'v4' ) size = 4; + else if ( attribute.type === 'c' ) size = 3; - // API + attribute.size = size; - this.getContext = function () { + attribute.array = new Float32Array( nvertices * size ); - return _gl; + attribute.buffer = _gl.createBuffer(); + attribute.buffer.belongsToAttribute = name; - }; + originalAttribute.needsUpdate = true; + attribute.__original = originalAttribute; - this.supportsVertexTextures = function () { + } - return _supportsVertexTextures; + geometryGroup.__webglCustomAttributesList.push( attribute ); - }; + } - this.supportsFloatTextures = function () { + } - return _glExtensionTextureFloat; + geometryGroup.__inittedArrays = true; }; - this.supportsStandardDerivatives = function () { + function getBufferMaterial( object, geometryGroup ) { - return _glExtensionStandardDerivatives; + return object.material instanceof THREE.MeshFaceMaterial + ? object.material.materials[ geometryGroup.materialIndex ] + : object.material; }; - this.supportsCompressedTextureS3TC = function () { + function materialNeedsSmoothNormals ( material ) { - return _glExtensionCompressedTextureS3TC; + return material && material.shading !== undefined && material.shading === THREE.SmoothShading; }; - this.getMaxAnisotropy = function () { + // Buffer setting - return _maxAnisotropy; + function setParticleBuffers ( geometry, hint, object ) { - }; + var v, c, vertex, offset, index, color, - this.getPrecision = function () { + vertices = geometry.vertices, + vl = vertices.length, - return _precision; + colors = geometry.colors, + cl = colors.length, - }; + vertexArray = geometry.__vertexArray, + colorArray = geometry.__colorArray, - this.setSize = function ( width, height, updateStyle ) { + sortArray = geometry.__sortArray, - _canvas.width = width * this.devicePixelRatio; - _canvas.height = height * this.devicePixelRatio; + dirtyVertices = geometry.verticesNeedUpdate, + dirtyElements = geometry.elementsNeedUpdate, + dirtyColors = geometry.colorsNeedUpdate, - if ( updateStyle !== false ) { + customAttributes = geometry.__webglCustomAttributesList, + i, il, + a, ca, cal, value, + customAttribute; - _canvas.style.width = width + 'px'; - _canvas.style.height = height + 'px'; + if ( object.sortParticles ) { - } + _projScreenMatrixPS.copy( _projScreenMatrix ); + _projScreenMatrixPS.multiply( object.matrixWorld ); - this.setViewport( 0, 0, width, height ); + for ( v = 0; v < vl; v ++ ) { - }; + vertex = vertices[ v ]; - this.setViewport = function ( x, y, width, height ) { + _vector3.copy( vertex ); + _vector3.applyProjection( _projScreenMatrixPS ); - _viewportX = x * this.devicePixelRatio; - _viewportY = y * this.devicePixelRatio; + sortArray[ v ] = [ _vector3.z, v ]; - _viewportWidth = width * this.devicePixelRatio; - _viewportHeight = height * this.devicePixelRatio; + } - _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight ); + sortArray.sort( numericalSort ); - }; + for ( v = 0; v < vl; v ++ ) { - this.setScissor = function ( x, y, width, height ) { + vertex = vertices[ sortArray[ v ][ 1 ] ]; - _gl.scissor( - x * this.devicePixelRatio, - y * this.devicePixelRatio, - width * this.devicePixelRatio, - height * this.devicePixelRatio - ); + offset = v * 3; - }; + vertexArray[ offset ] = vertex.x; + vertexArray[ offset + 1 ] = vertex.y; + vertexArray[ offset + 2 ] = vertex.z; - this.enableScissorTest = function ( enable ) { - - enable ? _gl.enable( _gl.SCISSOR_TEST ) : _gl.disable( _gl.SCISSOR_TEST ); - - }; + } - // Clearing + for ( c = 0; c < cl; c ++ ) { - this.setClearColor = function ( color, alpha ) { + offset = c * 3; - _clearColor.set( color ); - _clearAlpha = alpha !== undefined ? alpha : 1; + color = colors[ sortArray[ c ][ 1 ] ]; - _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); + colorArray[ offset ] = color.r; + colorArray[ offset + 1 ] = color.g; + colorArray[ offset + 2 ] = color.b; - }; + } - this.setClearColorHex = function ( hex, alpha ) { + if ( customAttributes ) { - console.warn( 'THREE.WebGLRenderer: .setClearColorHex() is being removed. Use .setClearColor() instead.' ); - this.setClearColor( hex, alpha ); + for ( i = 0, il = customAttributes.length; i < il; i ++ ) { - }; + customAttribute = customAttributes[ i ]; - this.getClearColor = function () { + if ( ! ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) ) continue; - return _clearColor; + offset = 0; - }; + cal = customAttribute.value.length; - this.getClearAlpha = function () { + if ( customAttribute.size === 1 ) { - return _clearAlpha; + for ( ca = 0; ca < cal; ca ++ ) { - }; + index = sortArray[ ca ][ 1 ]; - this.clear = function ( color, depth, stencil ) { + customAttribute.array[ ca ] = customAttribute.value[ index ]; - var bits = 0; + } - if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT; - if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT; - if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT; + } else if ( customAttribute.size === 2 ) { - _gl.clear( bits ); + for ( ca = 0; ca < cal; ca ++ ) { - }; + index = sortArray[ ca ][ 1 ]; - this.clearColor = function () { + value = customAttribute.value[ index ]; - _gl.clear( _gl.COLOR_BUFFER_BIT ); + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; - }; + offset += 2; - this.clearDepth = function () { + } - _gl.clear( _gl.DEPTH_BUFFER_BIT ); + } else if ( customAttribute.size === 3 ) { - }; + if ( customAttribute.type === 'c' ) { - this.clearStencil = function () { + for ( ca = 0; ca < cal; ca ++ ) { - _gl.clear( _gl.STENCIL_BUFFER_BIT ); + index = sortArray[ ca ][ 1 ]; - }; + value = customAttribute.value[ index ]; - this.clearTarget = function ( renderTarget, color, depth, stencil ) { + customAttribute.array[ offset ] = value.r; + customAttribute.array[ offset + 1 ] = value.g; + customAttribute.array[ offset + 2 ] = value.b; - this.setRenderTarget( renderTarget ); - this.clear( color, depth, stencil ); + offset += 3; - }; + } - // Plugins + } else { - this.addPostPlugin = function ( plugin ) { + for ( ca = 0; ca < cal; ca ++ ) { - plugin.init( this ); - this.renderPluginsPost.push( plugin ); + index = sortArray[ ca ][ 1 ]; - }; + value = customAttribute.value[ index ]; - this.addPrePlugin = function ( plugin ) { + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; + customAttribute.array[ offset + 2 ] = value.z; - plugin.init( this ); - this.renderPluginsPre.push( plugin ); + offset += 3; - }; + } - // Rendering + } - this.updateShadowMap = function ( scene, camera ) { + } else if ( customAttribute.size === 4 ) { - _currentProgram = null; - _oldBlending = - 1; - _oldDepthTest = - 1; - _oldDepthWrite = - 1; - _currentGeometryGroupHash = - 1; - _currentMaterialId = - 1; - _lightsNeedUpdate = true; - _oldDoubleSided = - 1; - _oldFlipSided = - 1; + for ( ca = 0; ca < cal; ca ++ ) { - initObjects( scene ); + index = sortArray[ ca ][ 1 ]; - this.shadowMapPlugin.update( scene, camera ); + value = customAttribute.value[ index ]; - }; + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; + customAttribute.array[ offset + 2 ] = value.z; + customAttribute.array[ offset + 3 ] = value.w; - // Internal functions + offset += 4; - // Buffer allocation + } - function createParticleBuffers ( geometry ) { + } - geometry.__webglVertexBuffer = _gl.createBuffer(); - geometry.__webglColorBuffer = _gl.createBuffer(); + } - _this.info.memory.geometries ++; + } - }; + } else { - function createLineBuffers ( geometry ) { + if ( dirtyVertices ) { - geometry.__webglVertexBuffer = _gl.createBuffer(); - geometry.__webglColorBuffer = _gl.createBuffer(); - geometry.__webglLineDistanceBuffer = _gl.createBuffer(); + for ( v = 0; v < vl; v ++ ) { - _this.info.memory.geometries ++; + vertex = vertices[ v ]; - }; + offset = v * 3; - function createMeshBuffers ( geometryGroup ) { + vertexArray[ offset ] = vertex.x; + vertexArray[ offset + 1 ] = vertex.y; + vertexArray[ offset + 2 ] = vertex.z; - geometryGroup.__webglVertexBuffer = _gl.createBuffer(); - geometryGroup.__webglNormalBuffer = _gl.createBuffer(); - geometryGroup.__webglTangentBuffer = _gl.createBuffer(); - geometryGroup.__webglColorBuffer = _gl.createBuffer(); - geometryGroup.__webglUVBuffer = _gl.createBuffer(); - geometryGroup.__webglUV2Buffer = _gl.createBuffer(); + } - geometryGroup.__webglSkinIndicesBuffer = _gl.createBuffer(); - geometryGroup.__webglSkinWeightsBuffer = _gl.createBuffer(); + } - geometryGroup.__webglFaceBuffer = _gl.createBuffer(); - geometryGroup.__webglLineBuffer = _gl.createBuffer(); + if ( dirtyColors ) { - var m, ml; + for ( c = 0; c < cl; c ++ ) { - if ( geometryGroup.numMorphTargets ) { + color = colors[ c ]; - geometryGroup.__webglMorphTargetsBuffers = []; + offset = c * 3; - for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) { + colorArray[ offset ] = color.r; + colorArray[ offset + 1 ] = color.g; + colorArray[ offset + 2 ] = color.b; - geometryGroup.__webglMorphTargetsBuffers.push( _gl.createBuffer() ); + } } - } + if ( customAttributes ) { - if ( geometryGroup.numMorphNormals ) { + for ( i = 0, il = customAttributes.length; i < il; i ++ ) { - geometryGroup.__webglMorphNormalsBuffers = []; + customAttribute = customAttributes[ i ]; - for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) { + if ( customAttribute.needsUpdate && + ( customAttribute.boundTo === undefined || + customAttribute.boundTo === 'vertices' ) ) { - geometryGroup.__webglMorphNormalsBuffers.push( _gl.createBuffer() ); + cal = customAttribute.value.length; - } + offset = 0; - } + if ( customAttribute.size === 1 ) { - _this.info.memory.geometries ++; + for ( ca = 0; ca < cal; ca ++ ) { - }; + customAttribute.array[ ca ] = customAttribute.value[ ca ]; - // Events + } - var onGeometryDispose = function ( event ) { + } else if ( customAttribute.size === 2 ) { - var geometry = event.target; + for ( ca = 0; ca < cal; ca ++ ) { - geometry.removeEventListener( 'dispose', onGeometryDispose ); + value = customAttribute.value[ ca ]; - deallocateGeometry( geometry ); + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; - }; + offset += 2; - var onTextureDispose = function ( event ) { + } - var texture = event.target; + } else if ( customAttribute.size === 3 ) { - texture.removeEventListener( 'dispose', onTextureDispose ); + if ( customAttribute.type === 'c' ) { - deallocateTexture( texture ); + for ( ca = 0; ca < cal; ca ++ ) { - _this.info.memory.textures --; + value = customAttribute.value[ ca ]; + customAttribute.array[ offset ] = value.r; + customAttribute.array[ offset + 1 ] = value.g; + customAttribute.array[ offset + 2 ] = value.b; - }; + offset += 3; - var onRenderTargetDispose = function ( event ) { + } - var renderTarget = event.target; + } else { - renderTarget.removeEventListener( 'dispose', onRenderTargetDispose ); + for ( ca = 0; ca < cal; ca ++ ) { - deallocateRenderTarget( renderTarget ); + value = customAttribute.value[ ca ]; - _this.info.memory.textures --; + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; + customAttribute.array[ offset + 2 ] = value.z; - }; + offset += 3; - var onMaterialDispose = function ( event ) { + } - var material = event.target; + } - material.removeEventListener( 'dispose', onMaterialDispose ); + } else if ( customAttribute.size === 4 ) { - deallocateMaterial( material ); + for ( ca = 0; ca < cal; ca ++ ) { - }; + value = customAttribute.value[ ca ]; - // Buffer deallocation + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; + customAttribute.array[ offset + 2 ] = value.z; + customAttribute.array[ offset + 3 ] = value.w; - var deleteBuffers = function ( geometry ) { + offset += 4; - 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 ); + } - if ( geometry.__webglSkinIndicesBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglSkinIndicesBuffer ); - if ( geometry.__webglSkinWeightsBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglSkinWeightsBuffer ); + } - 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 ) { + } - _gl.deleteBuffer( geometry.__webglCustomAttributesList[ id ].buffer ); + if ( dirtyVertices || object.sortParticles ) { - } + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); } - _this.info.memory.geometries --; - - }; + if ( dirtyColors || object.sortParticles ) { - var deallocateGeometry = function ( geometry ) { + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); - geometry.__webglInit = undefined; + } - if ( geometry instanceof THREE.BufferGeometry ) { + if ( customAttributes ) { - var attributes = geometry.attributes; + for ( i = 0, il = customAttributes.length; i < il; i ++ ) { - for ( var key in attributes ) { + customAttribute = customAttributes[ i ]; - if ( attributes[ key ].buffer !== undefined ) { + if ( customAttribute.needsUpdate || object.sortParticles ) { - _gl.deleteBuffer( attributes[ key ].buffer ); + _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); } } - _this.info.memory.geometries --; - - } else { - - if ( geometry.geometryGroups !== undefined ) { - - for ( var i = 0,l = geometry.geometryGroupsList.length; i 0 ) { + // morph normals - geometryGroup.__uvArray = new Float32Array( nvertices * 2 ); + if ( material.morphNormals ) { - } + if ( needsSmoothNormals ) { - if ( geometry.faceVertexUvs.length > 1 ) { + faceVertexNormals = morphNormals[ vk ].vertexNormals[ chf ]; - geometryGroup.__uv2Array = new Float32Array( nvertices * 2 ); + n1 = faceVertexNormals.a; + n2 = faceVertexNormals.b; + n3 = faceVertexNormals.c; - } + } else { - } + n1 = morphNormals[ vk ].faceNormals[ chf ]; + n2 = n1; + n3 = n1; - if ( object.geometry.skinWeights.length && object.geometry.skinIndices.length ) { + } - geometryGroup.__skinIndexArray = new Float32Array( nvertices * 4 ); - geometryGroup.__skinWeightArray = new Float32Array( nvertices * 4 ); + nka = morphNormalsArrays[ vk ]; - } + nka[ offset_morphTarget ] = n1.x; + nka[ offset_morphTarget + 1 ] = n1.y; + nka[ offset_morphTarget + 2 ] = n1.z; - var UintArray = _glExtensionElementIndexUint !== null && ntris > 21845 ? Uint32Array : Uint16Array; // 65535 / 3 + nka[ offset_morphTarget + 3 ] = n2.x; + nka[ offset_morphTarget + 4 ] = n2.y; + nka[ offset_morphTarget + 5 ] = n2.z; - geometryGroup.__typeArray = UintArray; - geometryGroup.__faceArray = new UintArray( ntris * 3 ); - geometryGroup.__lineArray = new UintArray( nlines * 2 ); + nka[ offset_morphTarget + 6 ] = n3.x; + nka[ offset_morphTarget + 7 ] = n3.y; + nka[ offset_morphTarget + 8 ] = n3.z; - var m, ml; + } - if ( geometryGroup.numMorphTargets ) { + // - geometryGroup.__morphTargetsArrays = []; + offset_morphTarget += 9; - for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) { + } - geometryGroup.__morphTargetsArrays.push( new Float32Array( nvertices * 3 ) ); + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ vk ] ); + _gl.bufferData( _gl.ARRAY_BUFFER, morphTargetsArrays[ vk ], hint ); + + if ( material.morphNormals ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ vk ] ); + _gl.bufferData( _gl.ARRAY_BUFFER, morphNormalsArrays[ vk ], hint ); + + } } } - if ( geometryGroup.numMorphNormals ) { + if ( obj_skinWeights.length ) { - geometryGroup.__morphNormalsArrays = []; + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) { + face = obj_faces[ chunk_faces3[ f ] ]; - geometryGroup.__morphNormalsArrays.push( new Float32Array( nvertices * 3 ) ); + // weights - } + sw1 = obj_skinWeights[ face.a ]; + sw2 = obj_skinWeights[ face.b ]; + sw3 = obj_skinWeights[ face.c ]; - } + skinWeightArray[ offset_skin ] = sw1.x; + skinWeightArray[ offset_skin + 1 ] = sw1.y; + skinWeightArray[ offset_skin + 2 ] = sw1.z; + skinWeightArray[ offset_skin + 3 ] = sw1.w; - geometryGroup.__webglFaceCount = ntris * 3; - geometryGroup.__webglLineCount = nlines * 2; + skinWeightArray[ offset_skin + 4 ] = sw2.x; + skinWeightArray[ offset_skin + 5 ] = sw2.y; + skinWeightArray[ offset_skin + 6 ] = sw2.z; + skinWeightArray[ offset_skin + 7 ] = sw2.w; + skinWeightArray[ offset_skin + 8 ] = sw3.x; + skinWeightArray[ offset_skin + 9 ] = sw3.y; + skinWeightArray[ offset_skin + 10 ] = sw3.z; + skinWeightArray[ offset_skin + 11 ] = sw3.w; - // custom attributes + // indices - if ( material.attributes ) { + si1 = obj_skinIndices[ face.a ]; + si2 = obj_skinIndices[ face.b ]; + si3 = obj_skinIndices[ face.c ]; - if ( geometryGroup.__webglCustomAttributesList === undefined ) { + skinIndexArray[ offset_skin ] = si1.x; + skinIndexArray[ offset_skin + 1 ] = si1.y; + skinIndexArray[ offset_skin + 2 ] = si1.z; + skinIndexArray[ offset_skin + 3 ] = si1.w; - geometryGroup.__webglCustomAttributesList = []; + skinIndexArray[ offset_skin + 4 ] = si2.x; + skinIndexArray[ offset_skin + 5 ] = si2.y; + skinIndexArray[ offset_skin + 6 ] = si2.z; + skinIndexArray[ offset_skin + 7 ] = si2.w; - } + skinIndexArray[ offset_skin + 8 ] = si3.x; + skinIndexArray[ offset_skin + 9 ] = si3.y; + skinIndexArray[ offset_skin + 10 ] = si3.z; + skinIndexArray[ offset_skin + 11 ] = si3.w; - for ( var a in material.attributes ) { + offset_skin += 12; - // 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 ]; + if ( offset_skin > 0 ) { - var attribute = {}; + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, skinIndexArray, hint ); - for ( var property in originalAttribute ) { + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, skinWeightArray, hint ); - attribute[ property ] = originalAttribute[ property ]; + } - } + } - if ( ! attribute.__webglInitialized || attribute.createUniqueBuffers ) { + if ( dirtyColors ) { - attribute.__webglInitialized = true; + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - var size = 1; // "f" and "i" + face = obj_faces[ chunk_faces3[ f ] ]; - if ( attribute.type === 'v2' ) size = 2; - else if ( attribute.type === 'v3' ) size = 3; - else if ( attribute.type === 'v4' ) size = 4; - else if ( attribute.type === 'c' ) size = 3; + vertexColors = face.vertexColors; + faceColor = face.color; - attribute.size = size; + if ( vertexColors.length === 3 && material.vertexColors === THREE.VertexColors ) { - attribute.array = new Float32Array( nvertices * size ); + c1 = vertexColors[ 0 ]; + c2 = vertexColors[ 1 ]; + c3 = vertexColors[ 2 ]; - attribute.buffer = _gl.createBuffer(); - attribute.buffer.belongsToAttribute = a; + } else { - originalAttribute.needsUpdate = true; - attribute.__original = originalAttribute; + c1 = faceColor; + c2 = faceColor; + c3 = faceColor; } - geometryGroup.__webglCustomAttributesList.push( attribute ); + colorArray[ offset_color ] = c1.r; + colorArray[ offset_color + 1 ] = c1.g; + colorArray[ offset_color + 2 ] = c1.b; + + colorArray[ offset_color + 3 ] = c2.r; + colorArray[ offset_color + 4 ] = c2.g; + colorArray[ offset_color + 5 ] = c2.b; + + colorArray[ offset_color + 6 ] = c3.r; + colorArray[ offset_color + 7 ] = c3.g; + colorArray[ offset_color + 8 ] = c3.b; + + offset_color += 9; } - } + if ( offset_color > 0 ) { - geometryGroup.__inittedArrays = true; + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); - }; + } - function getBufferMaterial( object, geometryGroup ) { + } - return object.material instanceof THREE.MeshFaceMaterial - ? object.material.materials[ geometryGroup.materialIndex ] - : object.material; + if ( dirtyTangents && geometry.hasTangents ) { - }; + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - function materialNeedsSmoothNormals ( material ) { + face = obj_faces[ chunk_faces3[ f ] ]; - return material && material.shading !== undefined && material.shading === THREE.SmoothShading; + vertexTangents = face.vertexTangents; - }; + t1 = vertexTangents[ 0 ]; + t2 = vertexTangents[ 1 ]; + t3 = vertexTangents[ 2 ]; - function bufferGuessNormalType ( material ) { + tangentArray[ offset_tangent ] = t1.x; + tangentArray[ offset_tangent + 1 ] = t1.y; + tangentArray[ offset_tangent + 2 ] = t1.z; + tangentArray[ offset_tangent + 3 ] = t1.w; - // only MeshBasicMaterial and MeshDepthMaterial don't need normals + tangentArray[ offset_tangent + 4 ] = t2.x; + tangentArray[ offset_tangent + 5 ] = t2.y; + tangentArray[ offset_tangent + 6 ] = t2.z; + tangentArray[ offset_tangent + 7 ] = t2.w; - if ( ( material instanceof THREE.MeshBasicMaterial && ! material.envMap ) || material instanceof THREE.MeshDepthMaterial ) { + tangentArray[ offset_tangent + 8 ] = t3.x; + tangentArray[ offset_tangent + 9 ] = t3.y; + tangentArray[ offset_tangent + 10 ] = t3.z; + tangentArray[ offset_tangent + 11 ] = t3.w; - return false; + offset_tangent += 12; + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, tangentArray, hint ); } - if ( materialNeedsSmoothNormals( material ) ) { + if ( dirtyNormals ) { - return THREE.SmoothShading; + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - } else { + face = obj_faces[ chunk_faces3[ f ] ]; - return THREE.FlatShading; + vertexNormals = face.vertexNormals; + faceNormal = face.normal; - } + if ( vertexNormals.length === 3 && needsSmoothNormals ) { - }; + for ( i = 0; i < 3; i ++ ) { + + vn = vertexNormals[ i ]; - function bufferGuessVertexColorType( material ) { + normalArray[ offset_normal ] = vn.x; + normalArray[ offset_normal + 1 ] = vn.y; + normalArray[ offset_normal + 2 ] = vn.z; - if ( material.vertexColors ) { + offset_normal += 3; - return material.vertexColors; + } - } + } else { - return false; + for ( i = 0; i < 3; i ++ ) { - }; + normalArray[ offset_normal ] = faceNormal.x; + normalArray[ offset_normal + 1 ] = faceNormal.y; + normalArray[ offset_normal + 2 ] = faceNormal.z; + + offset_normal += 3; - 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; + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, normalArray, hint ); } - return false; + if ( dirtyUvs && obj_uvs ) { - }; + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - // + fi = chunk_faces3[ f ]; - function initDirectBuffers( geometry ) { + uv = obj_uvs[ fi ]; - for ( var name in geometry.attributes ) { + if ( uv === undefined ) continue; - var bufferType = ( name === 'index' ) ? _gl.ELEMENT_ARRAY_BUFFER : _gl.ARRAY_BUFFER; + for ( i = 0; i < 3; i ++ ) { - var attribute = geometry.attributes[ name ]; - attribute.buffer = _gl.createBuffer(); + uvi = uv[ i ]; - _gl.bindBuffer( bufferType, attribute.buffer ); - _gl.bufferData( bufferType, attribute.array, _gl.STATIC_DRAW ); + uvArray[ offset_uv ] = uvi.x; + uvArray[ offset_uv + 1 ] = uvi.y; - } + offset_uv += 2; - } + } - // Buffer setting + } - function setParticleBuffers ( geometry, hint, object ) { + if ( offset_uv > 0 ) { - var v, c, vertex, offset, index, color, + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, uvArray, hint ); - vertices = geometry.vertices, - vl = vertices.length, + } - colors = geometry.colors, - cl = colors.length, + } - vertexArray = geometry.__vertexArray, - colorArray = geometry.__colorArray, + if ( dirtyUvs && obj_uvs2 ) { - sortArray = geometry.__sortArray, + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - dirtyVertices = geometry.verticesNeedUpdate, - dirtyElements = geometry.elementsNeedUpdate, - dirtyColors = geometry.colorsNeedUpdate, + fi = chunk_faces3[ f ]; - customAttributes = geometry.__webglCustomAttributesList, - i, il, - a, ca, cal, value, - customAttribute; + uv2 = obj_uvs2[ fi ]; - if ( object.sortParticles ) { + if ( uv2 === undefined ) continue; - _projScreenMatrixPS.copy( _projScreenMatrix ); - _projScreenMatrixPS.multiply( object.matrixWorld ); + for ( i = 0; i < 3; i ++ ) { - for ( v = 0; v < vl; v ++ ) { + uv2i = uv2[ i ]; - vertex = vertices[ v ]; + uv2Array[ offset_uv2 ] = uv2i.x; + uv2Array[ offset_uv2 + 1 ] = uv2i.y; - _vector3.copy( vertex ); - _vector3.applyProjection( _projScreenMatrixPS ); + offset_uv2 += 2; - sortArray[ v ] = [ _vector3.z, v ]; + } } - sortArray.sort( numericalSort ); + if ( offset_uv2 > 0 ) { - for ( v = 0; v < vl; v ++ ) { + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, uv2Array, hint ); - vertex = vertices[ sortArray[ v ][ 1 ] ]; + } - offset = v * 3; + } - vertexArray[ offset ] = vertex.x; - vertexArray[ offset + 1 ] = vertex.y; - vertexArray[ offset + 2 ] = vertex.z; + if ( dirtyElements ) { - } + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - for ( c = 0; c < cl; c ++ ) { + faceArray[ offset_face ] = vertexIndex; + faceArray[ offset_face + 1 ] = vertexIndex + 1; + faceArray[ offset_face + 2 ] = vertexIndex + 2; - offset = c * 3; + offset_face += 3; - color = colors[ sortArray[ c ][ 1 ] ]; + lineArray[ offset_line ] = vertexIndex; + lineArray[ offset_line + 1 ] = vertexIndex + 1; - colorArray[ offset ] = color.r; - colorArray[ offset + 1 ] = color.g; - colorArray[ offset + 2 ] = color.b; + lineArray[ offset_line + 2 ] = vertexIndex; + lineArray[ offset_line + 3 ] = vertexIndex + 2; - } + lineArray[ offset_line + 4 ] = vertexIndex + 1; + lineArray[ offset_line + 5 ] = vertexIndex + 2; - if ( customAttributes ) { + offset_line += 6; - for ( i = 0, il = customAttributes.length; i < il; i ++ ) { + vertexIndex += 3; - customAttribute = customAttributes[ i ]; + } - if ( ! ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) ) continue; + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer ); + _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, faceArray, hint ); - offset = 0; + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer ); + _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, lineArray, hint ); - cal = customAttribute.value.length; + } - if ( customAttribute.size === 1 ) { + if ( customAttributes ) { - for ( ca = 0; ca < cal; ca ++ ) { + for ( i = 0, il = customAttributes.length; i < il; i ++ ) { - index = sortArray[ ca ][ 1 ]; + customAttribute = customAttributes[ i ]; - customAttribute.array[ ca ] = customAttribute.value[ index ]; + if ( ! customAttribute.__original.needsUpdate ) continue; - } + offset_custom = 0; + offset_customSrc = 0; - } else if ( customAttribute.size === 2 ) { + if ( customAttribute.size === 1 ) { - for ( ca = 0; ca < cal; ca ++ ) { + if ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) { - index = sortArray[ ca ][ 1 ]; + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - value = customAttribute.value[ index ]; + face = obj_faces[ chunk_faces3[ f ] ]; - customAttribute.array[ offset ] = value.x; - customAttribute.array[ offset + 1 ] = value.y; + customAttribute.array[ offset_custom ] = customAttribute.value[ face.a ]; + customAttribute.array[ offset_custom + 1 ] = customAttribute.value[ face.b ]; + customAttribute.array[ offset_custom + 2 ] = customAttribute.value[ face.c ]; - offset += 2; + offset_custom += 3; } - } else if ( customAttribute.size === 3 ) { + } else if ( customAttribute.boundTo === 'faces' ) { - if ( customAttribute.type === 'c' ) { + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - for ( ca = 0; ca < cal; ca ++ ) { + value = customAttribute.value[ chunk_faces3[ f ] ]; - index = sortArray[ ca ][ 1 ]; + customAttribute.array[ offset_custom ] = value; + customAttribute.array[ offset_custom + 1 ] = value; + customAttribute.array[ offset_custom + 2 ] = value; - value = customAttribute.value[ index ]; + offset_custom += 3; - customAttribute.array[ offset ] = value.r; - customAttribute.array[ offset + 1 ] = value.g; - customAttribute.array[ offset + 2 ] = value.b; + } - offset += 3; + } - } + } else if ( customAttribute.size === 2 ) { - } else { + if ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) { - for ( ca = 0; ca < cal; ca ++ ) { + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - index = sortArray[ ca ][ 1 ]; + face = obj_faces[ chunk_faces3[ f ] ]; - value = customAttribute.value[ index ]; + v1 = customAttribute.value[ face.a ]; + v2 = customAttribute.value[ face.b ]; + v3 = customAttribute.value[ face.c ]; - customAttribute.array[ offset ] = value.x; - customAttribute.array[ offset + 1 ] = value.y; - customAttribute.array[ offset + 2 ] = value.z; + customAttribute.array[ offset_custom ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; - offset += 3; + customAttribute.array[ offset_custom + 2 ] = v2.x; + customAttribute.array[ offset_custom + 3 ] = v2.y; - } + customAttribute.array[ offset_custom + 4 ] = v3.x; + customAttribute.array[ offset_custom + 5 ] = v3.y; + + offset_custom += 6; } - } else if ( customAttribute.size === 4 ) { + } else if ( customAttribute.boundTo === 'faces' ) { - for ( ca = 0; ca < cal; ca ++ ) { + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - index = sortArray[ ca ][ 1 ]; + value = customAttribute.value[ chunk_faces3[ f ] ]; - value = customAttribute.value[ index ]; + v1 = value; + v2 = value; + v3 = value; - customAttribute.array[ offset ] = value.x; - customAttribute.array[ offset + 1 ] = value.y; - customAttribute.array[ offset + 2 ] = value.z; - customAttribute.array[ offset + 3 ] = value.w; + customAttribute.array[ offset_custom ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; - offset += 4; + customAttribute.array[ offset_custom + 2 ] = v2.x; + customAttribute.array[ offset_custom + 3 ] = v2.y; + + customAttribute.array[ offset_custom + 4 ] = v3.x; + customAttribute.array[ offset_custom + 5 ] = v3.y; + + offset_custom += 6; } } - } + } else if ( customAttribute.size === 3 ) { - } + var pp; - } else { + if ( customAttribute.type === 'c' ) { - if ( dirtyVertices ) { + pp = [ 'r', 'g', 'b' ]; - for ( v = 0; v < vl; v ++ ) { + } else { - vertex = vertices[ v ]; + pp = [ 'x', 'y', 'z' ]; - offset = v * 3; + } - vertexArray[ offset ] = vertex.x; - vertexArray[ offset + 1 ] = vertex.y; - vertexArray[ offset + 2 ] = vertex.z; + if ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) { - } + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - } + face = obj_faces[ chunk_faces3[ f ] ]; - if ( dirtyColors ) { + v1 = customAttribute.value[ face.a ]; + v2 = customAttribute.value[ face.b ]; + v3 = customAttribute.value[ face.c ]; - for ( c = 0; c < cl; c ++ ) { + customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; - color = colors[ c ]; + customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; - offset = c * 3; + customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; - colorArray[ offset ] = color.r; - colorArray[ offset + 1 ] = color.g; - colorArray[ offset + 2 ] = color.b; + offset_custom += 9; - } + } - } + } else if ( customAttribute.boundTo === 'faces' ) { - if ( customAttributes ) { + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - for ( i = 0, il = customAttributes.length; i < il; i ++ ) { + value = customAttribute.value[ chunk_faces3[ f ] ]; - customAttribute = customAttributes[ i ]; + v1 = value; + v2 = value; + v3 = value; - if ( customAttribute.needsUpdate && - ( customAttribute.boundTo === undefined || - customAttribute.boundTo === 'vertices' ) ) { + customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; - cal = customAttribute.value.length; + customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; - offset = 0; + customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; - if ( customAttribute.size === 1 ) { + offset_custom += 9; - for ( ca = 0; ca < cal; ca ++ ) { + } - customAttribute.array[ ca ] = customAttribute.value[ ca ]; + } else if ( customAttribute.boundTo === 'faceVertices' ) { - } + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - } else if ( customAttribute.size === 2 ) { + value = customAttribute.value[ chunk_faces3[ f ] ]; - for ( ca = 0; ca < cal; ca ++ ) { + v1 = value[ 0 ]; + v2 = value[ 1 ]; + v3 = value[ 2 ]; - value = customAttribute.value[ ca ]; + customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; - customAttribute.array[ offset ] = value.x; - customAttribute.array[ offset + 1 ] = value.y; + customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; - offset += 2; + customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; - } + offset_custom += 9; - } else if ( customAttribute.size === 3 ) { + } - if ( customAttribute.type === 'c' ) { + } - for ( ca = 0; ca < cal; ca ++ ) { + } else if ( customAttribute.size === 4 ) { - value = customAttribute.value[ ca ]; + if ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) { - customAttribute.array[ offset ] = value.r; - customAttribute.array[ offset + 1 ] = value.g; - customAttribute.array[ offset + 2 ] = value.b; + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - offset += 3; + face = obj_faces[ chunk_faces3[ f ] ]; - } + v1 = customAttribute.value[ face.a ]; + v2 = customAttribute.value[ face.b ]; + v3 = customAttribute.value[ face.c ]; - } else { + customAttribute.array[ offset_custom ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + customAttribute.array[ offset_custom + 2 ] = v1.z; + customAttribute.array[ offset_custom + 3 ] = v1.w; - for ( ca = 0; ca < cal; ca ++ ) { + customAttribute.array[ offset_custom + 4 ] = v2.x; + customAttribute.array[ offset_custom + 5 ] = v2.y; + customAttribute.array[ offset_custom + 6 ] = v2.z; + customAttribute.array[ offset_custom + 7 ] = v2.w; - value = customAttribute.value[ ca ]; + customAttribute.array[ offset_custom + 8 ] = v3.x; + customAttribute.array[ offset_custom + 9 ] = v3.y; + customAttribute.array[ offset_custom + 10 ] = v3.z; + customAttribute.array[ offset_custom + 11 ] = v3.w; - customAttribute.array[ offset ] = value.x; - customAttribute.array[ offset + 1 ] = value.y; - customAttribute.array[ offset + 2 ] = value.z; + offset_custom += 12; - offset += 3; + } - } + } else if ( customAttribute.boundTo === 'faces' ) { - } + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - } else if ( customAttribute.size === 4 ) { + value = customAttribute.value[ chunk_faces3[ f ] ]; - for ( ca = 0; ca < cal; ca ++ ) { + v1 = value; + v2 = value; + v3 = value; - value = customAttribute.value[ ca ]; + customAttribute.array[ offset_custom ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + customAttribute.array[ offset_custom + 2 ] = v1.z; + customAttribute.array[ offset_custom + 3 ] = v1.w; - customAttribute.array[ offset ] = value.x; - customAttribute.array[ offset + 1 ] = value.y; - customAttribute.array[ offset + 2 ] = value.z; - customAttribute.array[ offset + 3 ] = value.w; + customAttribute.array[ offset_custom + 4 ] = v2.x; + customAttribute.array[ offset_custom + 5 ] = v2.y; + customAttribute.array[ offset_custom + 6 ] = v2.z; + customAttribute.array[ offset_custom + 7 ] = v2.w; - offset += 4; + customAttribute.array[ offset_custom + 8 ] = v3.x; + customAttribute.array[ offset_custom + 9 ] = v3.y; + customAttribute.array[ offset_custom + 10 ] = v3.z; + customAttribute.array[ offset_custom + 11 ] = v3.w; - } + offset_custom += 12; + + } + + } else if ( customAttribute.boundTo === 'faceVertices' ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces3[ f ] ]; + + v1 = value[ 0 ]; + v2 = value[ 1 ]; + v3 = value[ 2 ]; + + customAttribute.array[ offset_custom ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + customAttribute.array[ offset_custom + 2 ] = v1.z; + customAttribute.array[ offset_custom + 3 ] = v1.w; + + customAttribute.array[ offset_custom + 4 ] = v2.x; + customAttribute.array[ offset_custom + 5 ] = v2.y; + customAttribute.array[ offset_custom + 6 ] = v2.z; + customAttribute.array[ offset_custom + 7 ] = v2.w; + + customAttribute.array[ offset_custom + 8 ] = v3.x; + customAttribute.array[ offset_custom + 9 ] = v3.y; + customAttribute.array[ offset_custom + 10 ] = v3.z; + customAttribute.array[ offset_custom + 11 ] = v3.w; + + offset_custom += 12; } @@ -20114,36 +20093,56 @@ THREE.WebGLRenderer = function ( parameters ) { } + _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); + } } - if ( dirtyVertices || object.sortParticles ) { + if ( dispose ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); + delete geometryGroup.__inittedArrays; + delete geometryGroup.__colorArray; + delete geometryGroup.__normalArray; + delete geometryGroup.__tangentArray; + delete geometryGroup.__uvArray; + delete geometryGroup.__uv2Array; + delete geometryGroup.__faceArray; + delete geometryGroup.__vertexArray; + delete geometryGroup.__lineArray; + delete geometryGroup.__skinIndexArray; + delete geometryGroup.__skinWeightArray; } - if ( dirtyColors || object.sortParticles ) { + }; - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); + function setDirectBuffers( geometry ) { - } + var attributes = geometry.attributes; + var attributesKeys = geometry.attributesKeys; - if ( customAttributes ) { + for ( var i = 0, l = attributesKeys.length; i < l; i ++ ) { - for ( i = 0, il = customAttributes.length; i < il; i ++ ) { + var key = attributesKeys[ i ]; + var attribute = attributes[ key ]; - customAttribute = customAttributes[ i ]; + if ( attribute.buffer === undefined ) { - if ( customAttribute.needsUpdate || object.sortParticles ) { + attribute.buffer = _gl.createBuffer(); + attribute.needsUpdate = true; - _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); + } - } + if ( attribute.needsUpdate === true ) { + + var bufferType = ( key === 'index' ) ? _gl.ELEMENT_ARRAY_BUFFER : _gl.ARRAY_BUFFER; + + _gl.bindBuffer( bufferType, attribute.buffer ); + _gl.bufferData( bufferType, attribute.array, _gl.STATIC_DRAW ); + + attribute.needsUpdate = false; } @@ -20151,5637 +20150,5937 @@ THREE.WebGLRenderer = function ( parameters ) { } - function setLineBuffers ( geometry, hint ) { + // Buffer rendering - var v, c, d, vertex, offset, color, + this.renderBufferImmediate = function ( object, program, material ) { - vertices = geometry.vertices, - colors = geometry.colors, - lineDistances = geometry.lineDistances, + initAttributes(); - vl = vertices.length, - cl = colors.length, - dl = lineDistances.length, + if ( object.hasPositions && ! object.__webglVertexBuffer ) object.__webglVertexBuffer = _gl.createBuffer(); + if ( object.hasNormals && ! object.__webglNormalBuffer ) object.__webglNormalBuffer = _gl.createBuffer(); + if ( object.hasUvs && ! object.__webglUvBuffer ) object.__webglUvBuffer = _gl.createBuffer(); + if ( object.hasColors && ! object.__webglColorBuffer ) object.__webglColorBuffer = _gl.createBuffer(); - vertexArray = geometry.__vertexArray, - colorArray = geometry.__colorArray, - lineDistanceArray = geometry.__lineDistanceArray, + if ( object.hasPositions ) { - dirtyVertices = geometry.verticesNeedUpdate, - dirtyColors = geometry.colorsNeedUpdate, - dirtyLineDistances = geometry.lineDistancesNeedUpdate, + _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglVertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW ); + enableAttribute( program.attributes.position ); + _gl.vertexAttribPointer( program.attributes.position, 3, _gl.FLOAT, false, 0, 0 ); - customAttributes = geometry.__webglCustomAttributesList, + } - i, il, - a, ca, cal, value, - customAttribute; + if ( object.hasNormals ) { - if ( dirtyVertices ) { + _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglNormalBuffer ); - for ( v = 0; v < vl; v ++ ) { + if ( material.shading === THREE.FlatShading ) { - vertex = vertices[ v ]; + var nx, ny, nz, + nax, nbx, ncx, nay, nby, ncy, naz, nbz, ncz, + normalArray, + i, il = object.count * 3; - offset = v * 3; + for ( i = 0; i < il; i += 9 ) { - vertexArray[ offset ] = vertex.x; - vertexArray[ offset + 1 ] = vertex.y; - vertexArray[ offset + 2 ] = vertex.z; + normalArray = object.normalArray; - } + nax = normalArray[ i ]; + nay = normalArray[ i + 1 ]; + naz = normalArray[ i + 2 ]; - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); + nbx = normalArray[ i + 3 ]; + nby = normalArray[ i + 4 ]; + nbz = normalArray[ i + 5 ]; - } + ncx = normalArray[ i + 6 ]; + ncy = normalArray[ i + 7 ]; + ncz = normalArray[ i + 8 ]; - if ( dirtyColors ) { + nx = ( nax + nbx + ncx ) / 3; + ny = ( nay + nby + ncy ) / 3; + nz = ( naz + nbz + ncz ) / 3; - for ( c = 0; c < cl; c ++ ) { + normalArray[ i ] = nx; + normalArray[ i + 1 ] = ny; + normalArray[ i + 2 ] = nz; - color = colors[ c ]; + normalArray[ i + 3 ] = nx; + normalArray[ i + 4 ] = ny; + normalArray[ i + 5 ] = nz; - offset = c * 3; + normalArray[ i + 6 ] = nx; + normalArray[ i + 7 ] = ny; + normalArray[ i + 8 ] = nz; - colorArray[ offset ] = color.r; - colorArray[ offset + 1 ] = color.g; - colorArray[ offset + 2 ] = color.b; + } } - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW ); + enableAttribute( program.attributes.normal ); + _gl.vertexAttribPointer( program.attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); } - if ( dirtyLineDistances ) { + if ( object.hasUvs && material.map ) { - for ( d = 0; d < dl; d ++ ) { + _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglUvBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW ); + enableAttribute( program.attributes.uv ); + _gl.vertexAttribPointer( program.attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); - lineDistanceArray[ d ] = lineDistances[ d ]; + } - } + if ( object.hasColors && material.vertexColors !== THREE.NoColors ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglLineDistanceBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, lineDistanceArray, hint ); + _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglColorBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW ); + enableAttribute( program.attributes.color ); + _gl.vertexAttribPointer( program.attributes.color, 3, _gl.FLOAT, false, 0, 0 ); } - if ( customAttributes ) { + disableUnusedAttributes(); - for ( i = 0, il = customAttributes.length; i < il; i ++ ) { + _gl.drawArrays( _gl.TRIANGLES, 0, object.count ); - customAttribute = customAttributes[ i ]; + object.count = 0; - if ( customAttribute.needsUpdate && - ( customAttribute.boundTo === undefined || - customAttribute.boundTo === 'vertices' ) ) { + }; - offset = 0; + function setupVertexAttributes( material, program, geometry, startIndex ) { - cal = customAttribute.value.length; + var geometryAttributes = geometry.attributes; - if ( customAttribute.size === 1 ) { + var programAttributes = program.attributes; + var programAttributesKeys = program.attributesKeys; - for ( ca = 0; ca < cal; ca ++ ) { + for ( var i = 0, l = programAttributesKeys.length; i < l; i ++ ) { - customAttribute.array[ ca ] = customAttribute.value[ ca ]; + var key = programAttributesKeys[ i ]; + var programAttribute = programAttributes[ key ]; - } + if ( programAttribute >= 0 ) { - } else if ( customAttribute.size === 2 ) { + var geometryAttribute = geometryAttributes[ key ]; - for ( ca = 0; ca < cal; ca ++ ) { + if ( geometryAttribute !== undefined ) { - value = customAttribute.value[ ca ]; + var size = geometryAttribute.itemSize; - customAttribute.array[ offset ] = value.x; - customAttribute.array[ offset + 1 ] = value.y; + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryAttribute.buffer ); - offset += 2; + enableAttribute( programAttribute ); - } + _gl.vertexAttribPointer( programAttribute, size, _gl.FLOAT, false, 0, startIndex * size * 4 ); // 4 bytes per Float32 - } else if ( customAttribute.size === 3 ) { + } else if ( material.defaultAttributeValues !== undefined ) { - if ( customAttribute.type === 'c' ) { + if ( material.defaultAttributeValues[ key ].length === 2 ) { - for ( ca = 0; ca < cal; ca ++ ) { + _gl.vertexAttrib2fv( programAttribute, material.defaultAttributeValues[ key ] ); - value = customAttribute.value[ ca ]; + } else if ( material.defaultAttributeValues[ key ].length === 3 ) { - customAttribute.array[ offset ] = value.r; - customAttribute.array[ offset + 1 ] = value.g; - customAttribute.array[ offset + 2 ] = value.b; + _gl.vertexAttrib3fv( programAttribute, material.defaultAttributeValues[ key ] ); - offset += 3; + } - } + } - } else { + } - for ( ca = 0; ca < cal; ca ++ ) { + } - value = customAttribute.value[ ca ]; + disableUnusedAttributes(); - customAttribute.array[ offset ] = value.x; - customAttribute.array[ offset + 1 ] = value.y; - customAttribute.array[ offset + 2 ] = value.z; + } - offset += 3; + this.renderBufferDirect = function ( camera, lights, fog, material, geometry, object ) { - } + if ( material.visible === false ) return; - } + var program = setProgram( camera, lights, fog, material, object ); - } else if ( customAttribute.size === 4 ) { + var updateBuffers = false, + wireframeBit = material.wireframe ? 1 : 0, + geometryHash = ( geometry.id * 0xffffff ) + ( program.id * 2 ) + wireframeBit; - for ( ca = 0; ca < cal; ca ++ ) { + if ( geometryHash !== _currentGeometryGroupHash ) { - value = customAttribute.value[ ca ]; + _currentGeometryGroupHash = geometryHash; + updateBuffers = true; - customAttribute.array[ offset ] = value.x; - customAttribute.array[ offset + 1 ] = value.y; - customAttribute.array[ offset + 2 ] = value.z; - customAttribute.array[ offset + 3 ] = value.w; + } - offset += 4; + if ( updateBuffers ) { - } + initAttributes(); - } + } - _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); + // render mesh - } + if ( object instanceof THREE.Mesh ) { - } + var mode = material.wireframe === true ? _gl.LINES : _gl.TRIANGLES; - } + var index = geometry.attributes.index; - } + if ( index ) { - function setMeshBuffers( geometryGroup, object, hint, dispose, material ) { + // indexed triangles - if ( ! geometryGroup.__inittedArrays ) { + var type, size; - return; + if ( index.array instanceof Uint32Array && extensions.get( 'OES_element_index_uint' ) ) { - } + type = _gl.UNSIGNED_INT; + size = 4; - var normalType = bufferGuessNormalType( material ), - vertexColorType = bufferGuessVertexColorType( material ), - uvType = bufferGuessUVType( material ), + } else { - needsSmoothNormals = ( normalType === THREE.SmoothShading ); + type = _gl.UNSIGNED_SHORT; + size = 2; - var f, fl, fi, face, - vertexNormals, faceNormal, normal, - vertexColors, faceColor, - vertexTangents, - uv, uv2, v1, v2, v3, v4, t1, t2, t3, t4, n1, n2, n3, n4, - c1, c2, c3, - sw1, sw2, sw3, sw4, - si1, si2, si3, si4, - sa1, sa2, sa3, sa4, - sb1, sb2, sb3, sb4, - m, ml, i, il, - vn, uvi, uv2i, - vk, vkl, vka, - nka, chf, faceVertexNormals, - a, + } - vertexIndex = 0, + var offsets = geometry.offsets; - offset = 0, - offset_uv = 0, - offset_uv2 = 0, - offset_face = 0, - offset_normal = 0, - offset_tangent = 0, - offset_line = 0, - offset_color = 0, - offset_skin = 0, - offset_morphTarget = 0, - offset_custom = 0, - offset_customSrc = 0, + if ( offsets.length === 0 ) { - value, + if ( updateBuffers ) { - vertexArray = geometryGroup.__vertexArray, - uvArray = geometryGroup.__uvArray, - uv2Array = geometryGroup.__uv2Array, - normalArray = geometryGroup.__normalArray, - tangentArray = geometryGroup.__tangentArray, - colorArray = geometryGroup.__colorArray, + setupVertexAttributes( material, program, geometry, 0 ); + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); - skinIndexArray = geometryGroup.__skinIndexArray, - skinWeightArray = geometryGroup.__skinWeightArray, + } - morphTargetsArrays = geometryGroup.__morphTargetsArrays, - morphNormalsArrays = geometryGroup.__morphNormalsArrays, + _gl.drawElements( mode, index.array.length, type, 0 ); - customAttributes = geometryGroup.__webglCustomAttributesList, - customAttribute, + _this.info.render.calls ++; + _this.info.render.vertices += index.array.length; // not really true, here vertices can be shared + _this.info.render.faces += index.array.length / 3; - faceArray = geometryGroup.__faceArray, - lineArray = geometryGroup.__lineArray, + } else { - geometry = object.geometry, // this is shared for all chunks + // if there is more than 1 chunk + // must set attribute pointers to use new offsets for each chunk + // even if geometry and materials didn't change - dirtyVertices = geometry.verticesNeedUpdate, - dirtyElements = geometry.elementsNeedUpdate, - dirtyUvs = geometry.uvsNeedUpdate, - dirtyNormals = geometry.normalsNeedUpdate, - dirtyTangents = geometry.tangentsNeedUpdate, - dirtyColors = geometry.colorsNeedUpdate, - dirtyMorphTargets = geometry.morphTargetsNeedUpdate, + updateBuffers = true; - vertices = geometry.vertices, - chunk_faces3 = geometryGroup.faces3, - obj_faces = geometry.faces, + for ( var i = 0, il = offsets.length; i < il; i ++ ) { - obj_uvs = geometry.faceVertexUvs[ 0 ], - obj_uvs2 = geometry.faceVertexUvs[ 1 ], + var startIndex = offsets[ i ].index; - obj_colors = geometry.colors, + if ( updateBuffers ) { - obj_skinIndices = geometry.skinIndices, - obj_skinWeights = geometry.skinWeights, + setupVertexAttributes( material, program, geometry, startIndex ); + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); - morphTargets = geometry.morphTargets, - morphNormals = geometry.morphNormals; + } - if ( dirtyVertices ) { + // render indexed triangles - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + _gl.drawElements( mode, offsets[ i ].count, type, offsets[ i ].start * size ); - face = obj_faces[ chunk_faces3[ f ] ]; + _this.info.render.calls ++; + _this.info.render.vertices += offsets[ i ].count; // not really true, here vertices can be shared + _this.info.render.faces += offsets[ i ].count / 3; - v1 = vertices[ face.a ]; - v2 = vertices[ face.b ]; - v3 = vertices[ face.c ]; + } - vertexArray[ offset ] = v1.x; - vertexArray[ offset + 1 ] = v1.y; - vertexArray[ offset + 2 ] = v1.z; + } - vertexArray[ offset + 3 ] = v2.x; - vertexArray[ offset + 4 ] = v2.y; - vertexArray[ offset + 5 ] = v2.z; + } else { - vertexArray[ offset + 6 ] = v3.x; - vertexArray[ offset + 7 ] = v3.y; - vertexArray[ offset + 8 ] = v3.z; + // non-indexed triangles - offset += 9; + if ( updateBuffers ) { - } + setupVertexAttributes( material, program, geometry, 0 ); - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); + } - } + var position = geometry.attributes[ 'position' ]; - if ( dirtyMorphTargets ) { + // render non-indexed triangles - for ( vk = 0, vkl = morphTargets.length; vk < vkl; vk ++ ) { + _gl.drawArrays( mode, 0, position.array.length / 3 ); - offset_morphTarget = 0; + _this.info.render.calls ++; + _this.info.render.vertices += position.array.length / 3; + _this.info.render.faces += position.array.length / 9; - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + } - chf = chunk_faces3[ f ]; - face = obj_faces[ chf ]; + } else if ( object instanceof THREE.PointCloud ) { - // morph positions + // render particles - v1 = morphTargets[ vk ].vertices[ face.a ]; - v2 = morphTargets[ vk ].vertices[ face.b ]; - v3 = morphTargets[ vk ].vertices[ face.c ]; + if ( updateBuffers ) { - vka = morphTargetsArrays[ vk ]; + setupVertexAttributes( material, program, geometry, 0 ); - vka[ offset_morphTarget ] = v1.x; - vka[ offset_morphTarget + 1 ] = v1.y; - vka[ offset_morphTarget + 2 ] = v1.z; + } - vka[ offset_morphTarget + 3 ] = v2.x; - vka[ offset_morphTarget + 4 ] = v2.y; - vka[ offset_morphTarget + 5 ] = v2.z; + var position = geometry.attributes.position; - vka[ offset_morphTarget + 6 ] = v3.x; - vka[ offset_morphTarget + 7 ] = v3.y; - vka[ offset_morphTarget + 8 ] = v3.z; + // render particles - // morph normals + _gl.drawArrays( _gl.POINTS, 0, position.array.length / 3 ); - if ( material.morphNormals ) { + _this.info.render.calls ++; + _this.info.render.points += position.array.length / 3; - if ( needsSmoothNormals ) { + } else if ( object instanceof THREE.Line ) { - faceVertexNormals = morphNormals[ vk ].vertexNormals[ chf ]; + var mode = ( object.mode === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES; - n1 = faceVertexNormals.a; - n2 = faceVertexNormals.b; - n3 = faceVertexNormals.c; + setLineWidth( material.linewidth ); - } else { + var index = geometry.attributes.index; - n1 = morphNormals[ vk ].faceNormals[ chf ]; - n2 = n1; - n3 = n1; + if ( index ) { - } + // indexed lines - nka = morphNormalsArrays[ vk ]; + var type, size; - nka[ offset_morphTarget ] = n1.x; - nka[ offset_morphTarget + 1 ] = n1.y; - nka[ offset_morphTarget + 2 ] = n1.z; + if ( index.array instanceof Uint32Array ) { - nka[ offset_morphTarget + 3 ] = n2.x; - nka[ offset_morphTarget + 4 ] = n2.y; - nka[ offset_morphTarget + 5 ] = n2.z; + type = _gl.UNSIGNED_INT; + size = 4; - nka[ offset_morphTarget + 6 ] = n3.x; - nka[ offset_morphTarget + 7 ] = n3.y; - nka[ offset_morphTarget + 8 ] = n3.z; + } else { - } + type = _gl.UNSIGNED_SHORT; + size = 2; - // + } - offset_morphTarget += 9; + var offsets = geometry.offsets; - } + if ( offsets.length === 0 ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ vk ] ); - _gl.bufferData( _gl.ARRAY_BUFFER, morphTargetsArrays[ vk ], hint ); + if ( updateBuffers ) { - if ( material.morphNormals ) { + setupVertexAttributes( material, program, geometry, 0 ); + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ vk ] ); - _gl.bufferData( _gl.ARRAY_BUFFER, morphNormalsArrays[ vk ], hint ); + } - } + _gl.drawElements( mode, index.array.length, type, 0 ); // 2 bytes per Uint16Array - } + _this.info.render.calls ++; + _this.info.render.vertices += index.array.length; // not really true, here vertices can be shared - } + } else { - if ( obj_skinWeights.length ) { + // if there is more than 1 chunk + // must set attribute pointers to use new offsets for each chunk + // even if geometry and materials didn't change - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + if ( offsets.length > 1 ) updateBuffers = true; - face = obj_faces[ chunk_faces3[ f ] ]; + for ( var i = 0, il = offsets.length; i < il; i ++ ) { - // weights + var startIndex = offsets[ i ].index; - sw1 = obj_skinWeights[ face.a ]; - sw2 = obj_skinWeights[ face.b ]; - sw3 = obj_skinWeights[ face.c ]; + if ( updateBuffers ) { - skinWeightArray[ offset_skin ] = sw1.x; - skinWeightArray[ offset_skin + 1 ] = sw1.y; - skinWeightArray[ offset_skin + 2 ] = sw1.z; - skinWeightArray[ offset_skin + 3 ] = sw1.w; + setupVertexAttributes( material, program, geometry, startIndex ); + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); - skinWeightArray[ offset_skin + 4 ] = sw2.x; - skinWeightArray[ offset_skin + 5 ] = sw2.y; - skinWeightArray[ offset_skin + 6 ] = sw2.z; - skinWeightArray[ offset_skin + 7 ] = sw2.w; + } - skinWeightArray[ offset_skin + 8 ] = sw3.x; - skinWeightArray[ offset_skin + 9 ] = sw3.y; - skinWeightArray[ offset_skin + 10 ] = sw3.z; - skinWeightArray[ offset_skin + 11 ] = sw3.w; + // render indexed lines - // indices + _gl.drawElements( mode, offsets[ i ].count, type, offsets[ i ].start * size ); // 2 bytes per Uint16Array - si1 = obj_skinIndices[ face.a ]; - si2 = obj_skinIndices[ face.b ]; - si3 = obj_skinIndices[ face.c ]; + _this.info.render.calls ++; + _this.info.render.vertices += offsets[ i ].count; // not really true, here vertices can be shared - skinIndexArray[ offset_skin ] = si1.x; - skinIndexArray[ offset_skin + 1 ] = si1.y; - skinIndexArray[ offset_skin + 2 ] = si1.z; - skinIndexArray[ offset_skin + 3 ] = si1.w; + } - skinIndexArray[ offset_skin + 4 ] = si2.x; - skinIndexArray[ offset_skin + 5 ] = si2.y; - skinIndexArray[ offset_skin + 6 ] = si2.z; - skinIndexArray[ offset_skin + 7 ] = si2.w; + } - skinIndexArray[ offset_skin + 8 ] = si3.x; - skinIndexArray[ offset_skin + 9 ] = si3.y; - skinIndexArray[ offset_skin + 10 ] = si3.z; - skinIndexArray[ offset_skin + 11 ] = si3.w; + } else { - offset_skin += 12; + // non-indexed lines - } + if ( updateBuffers ) { - if ( offset_skin > 0 ) { + setupVertexAttributes( material, program, geometry, 0 ); - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, skinIndexArray, hint ); + } - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, skinWeightArray, hint ); + var position = geometry.attributes.position; + + _gl.drawArrays( mode, 0, position.array.length / 3 ); + + _this.info.render.calls ++; + _this.info.render.points += position.array.length / 3; } } - if ( dirtyColors && vertexColorType ) { + }; - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + this.renderBuffer = function ( camera, lights, fog, material, geometryGroup, object ) { - face = obj_faces[ chunk_faces3[ f ] ]; + if ( material.visible === false ) return; - vertexColors = face.vertexColors; - faceColor = face.color; + var program = setProgram( camera, lights, fog, material, object ); - if ( vertexColors.length === 3 && vertexColorType === THREE.VertexColors ) { + var attributes = program.attributes; - c1 = vertexColors[ 0 ]; - c2 = vertexColors[ 1 ]; - c3 = vertexColors[ 2 ]; + var updateBuffers = false, + wireframeBit = material.wireframe ? 1 : 0, + geometryGroupHash = ( geometryGroup.id * 0xffffff ) + ( program.id * 2 ) + wireframeBit; - } else { + if ( geometryGroupHash !== _currentGeometryGroupHash ) { - c1 = faceColor; - c2 = faceColor; - c3 = faceColor; + _currentGeometryGroupHash = geometryGroupHash; + updateBuffers = true; - } + } - colorArray[ offset_color ] = c1.r; - colorArray[ offset_color + 1 ] = c1.g; - colorArray[ offset_color + 2 ] = c1.b; + if ( updateBuffers ) { - colorArray[ offset_color + 3 ] = c2.r; - colorArray[ offset_color + 4 ] = c2.g; - colorArray[ offset_color + 5 ] = c2.b; + initAttributes(); - colorArray[ offset_color + 6 ] = c3.r; - colorArray[ offset_color + 7 ] = c3.g; - colorArray[ offset_color + 8 ] = c3.b; + } - offset_color += 9; + // vertices - } + if ( ! material.morphTargets && attributes.position >= 0 ) { - if ( offset_color > 0 ) { + if ( updateBuffers ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); + enableAttribute( attributes.position ); + _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); } - } - - if ( dirtyTangents && geometry.hasTangents ) { - - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - - face = obj_faces[ chunk_faces3[ f ] ]; + } else { - vertexTangents = face.vertexTangents; + if ( object.morphTargetBase ) { - t1 = vertexTangents[ 0 ]; - t2 = vertexTangents[ 1 ]; - t3 = vertexTangents[ 2 ]; + setupMorphTargets( material, geometryGroup, object ); - tangentArray[ offset_tangent ] = t1.x; - tangentArray[ offset_tangent + 1 ] = t1.y; - tangentArray[ offset_tangent + 2 ] = t1.z; - tangentArray[ offset_tangent + 3 ] = t1.w; + } - tangentArray[ offset_tangent + 4 ] = t2.x; - tangentArray[ offset_tangent + 5 ] = t2.y; - tangentArray[ offset_tangent + 6 ] = t2.z; - tangentArray[ offset_tangent + 7 ] = t2.w; + } - tangentArray[ offset_tangent + 8 ] = t3.x; - tangentArray[ offset_tangent + 9 ] = t3.y; - tangentArray[ offset_tangent + 10 ] = t3.z; - tangentArray[ offset_tangent + 11 ] = t3.w; - offset_tangent += 12; + if ( updateBuffers ) { - } + // custom attributes - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, tangentArray, hint ); + // Use the per-geometryGroup custom attribute arrays which are setup in initMeshBuffers - } + if ( geometryGroup.__webglCustomAttributesList ) { - if ( dirtyNormals && normalType ) { + for ( var i = 0, il = geometryGroup.__webglCustomAttributesList.length; i < il; i ++ ) { - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + var attribute = geometryGroup.__webglCustomAttributesList[ i ]; - face = obj_faces[ chunk_faces3[ f ] ]; + if ( attributes[ attribute.buffer.belongsToAttribute ] >= 0 ) { - vertexNormals = face.vertexNormals; - faceNormal = face.normal; + _gl.bindBuffer( _gl.ARRAY_BUFFER, attribute.buffer ); + enableAttribute( attributes[ attribute.buffer.belongsToAttribute ] ); + _gl.vertexAttribPointer( attributes[ attribute.buffer.belongsToAttribute ], attribute.size, _gl.FLOAT, false, 0, 0 ); - if ( vertexNormals.length === 3 && needsSmoothNormals ) { + } - for ( i = 0; i < 3; i ++ ) { + } - vn = vertexNormals[ i ]; + } - normalArray[ offset_normal ] = vn.x; - normalArray[ offset_normal + 1 ] = vn.y; - normalArray[ offset_normal + 2 ] = vn.z; - offset_normal += 3; + // colors - } + if ( attributes.color >= 0 ) { - } else { + if ( object.geometry.colors.length > 0 || object.geometry.faces.length > 0 ) { - for ( i = 0; i < 3; i ++ ) { + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer ); + enableAttribute( attributes.color ); + _gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 ); - normalArray[ offset_normal ] = faceNormal.x; - normalArray[ offset_normal + 1 ] = faceNormal.y; - normalArray[ offset_normal + 2 ] = faceNormal.z; + } else if ( material.defaultAttributeValues !== undefined ) { - offset_normal += 3; - } + _gl.vertexAttrib3fv( attributes.color, material.defaultAttributeValues.color ); } } - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, normalArray, hint ); + // normals - } + if ( attributes.normal >= 0 ) { - if ( dirtyUvs && obj_uvs && uvType ) { + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer ); + enableAttribute( attributes.normal ); + _gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + } - fi = chunk_faces3[ f ]; + // tangents - uv = obj_uvs[ fi ]; + if ( attributes.tangent >= 0 ) { - if ( uv === undefined ) continue; + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer ); + enableAttribute( attributes.tangent ); + _gl.vertexAttribPointer( attributes.tangent, 4, _gl.FLOAT, false, 0, 0 ); - for ( i = 0; i < 3; i ++ ) { + } - uvi = uv[ i ]; + // uvs - uvArray[ offset_uv ] = uvi.x; - uvArray[ offset_uv + 1 ] = uvi.y; + if ( attributes.uv >= 0 ) { - offset_uv += 2; + if ( object.geometry.faceVertexUvs[ 0 ] ) { - } + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer ); + enableAttribute( attributes.uv ); + _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); - } + } else if ( material.defaultAttributeValues !== undefined ) { - if ( offset_uv > 0 ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, uvArray, hint ); + _gl.vertexAttrib2fv( attributes.uv, material.defaultAttributeValues.uv ); + + } } - } + if ( attributes.uv2 >= 0 ) { - if ( dirtyUvs && obj_uvs2 && uvType ) { + if ( object.geometry.faceVertexUvs[ 1 ] ) { - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer ); + enableAttribute( attributes.uv2 ); + _gl.vertexAttribPointer( attributes.uv2, 2, _gl.FLOAT, false, 0, 0 ); - fi = chunk_faces3[ f ]; + } else if ( material.defaultAttributeValues !== undefined ) { - uv2 = obj_uvs2[ fi ]; - if ( uv2 === undefined ) continue; + _gl.vertexAttrib2fv( attributes.uv2, material.defaultAttributeValues.uv2 ); - for ( i = 0; i < 3; i ++ ) { + } - uv2i = uv2[ i ]; + } - uv2Array[ offset_uv2 ] = uv2i.x; - uv2Array[ offset_uv2 + 1 ] = uv2i.y; + if ( material.skinning && + attributes.skinIndex >= 0 && attributes.skinWeight >= 0 ) { - offset_uv2 += 2; + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer ); + enableAttribute( attributes.skinIndex ); + _gl.vertexAttribPointer( attributes.skinIndex, 4, _gl.FLOAT, false, 0, 0 ); - } + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer ); + enableAttribute( attributes.skinWeight ); + _gl.vertexAttribPointer( attributes.skinWeight, 4, _gl.FLOAT, false, 0, 0 ); } - if ( offset_uv2 > 0 ) { + // line distances - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, uv2Array, hint ); + if ( attributes.lineDistance >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglLineDistanceBuffer ); + enableAttribute( attributes.lineDistance ); + _gl.vertexAttribPointer( attributes.lineDistance, 1, _gl.FLOAT, false, 0, 0 ); } } - if ( dirtyElements ) { + disableUnusedAttributes(); - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + // render mesh - faceArray[ offset_face ] = vertexIndex; - faceArray[ offset_face + 1 ] = vertexIndex + 1; - faceArray[ offset_face + 2 ] = vertexIndex + 2; + if ( object instanceof THREE.Mesh ) { - offset_face += 3; + var type = geometryGroup.__typeArray === Uint32Array ? _gl.UNSIGNED_INT : _gl.UNSIGNED_SHORT; - lineArray[ offset_line ] = vertexIndex; - lineArray[ offset_line + 1 ] = vertexIndex + 1; + // wireframe - lineArray[ offset_line + 2 ] = vertexIndex; - lineArray[ offset_line + 3 ] = vertexIndex + 2; + if ( material.wireframe ) { - lineArray[ offset_line + 4 ] = vertexIndex + 1; - lineArray[ offset_line + 5 ] = vertexIndex + 2; + setLineWidth( material.wireframeLinewidth ); + if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer ); + _gl.drawElements( _gl.LINES, geometryGroup.__webglLineCount, type, 0 ); - offset_line += 6; + // triangles - vertexIndex += 3; + } else { + + if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer ); + _gl.drawElements( _gl.TRIANGLES, geometryGroup.__webglFaceCount, type, 0 ); } - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer ); - _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, faceArray, hint ); + _this.info.render.calls ++; + _this.info.render.vertices += geometryGroup.__webglFaceCount; + _this.info.render.faces += geometryGroup.__webglFaceCount / 3; - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer ); - _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, lineArray, hint ); + // render lines - } + } else if ( object instanceof THREE.Line ) { - if ( customAttributes ) { + var mode = ( object.mode === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES; - for ( i = 0, il = customAttributes.length; i < il; i ++ ) { + setLineWidth( material.linewidth ); - customAttribute = customAttributes[ i ]; + _gl.drawArrays( mode, 0, geometryGroup.__webglLineCount ); - if ( ! customAttribute.__original.needsUpdate ) continue; + _this.info.render.calls ++; - offset_custom = 0; - offset_customSrc = 0; + // render particles - if ( customAttribute.size === 1 ) { + } else if ( object instanceof THREE.PointCloud ) { - if ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) { + _gl.drawArrays( _gl.POINTS, 0, geometryGroup.__webglParticleCount ); - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + _this.info.render.calls ++; + _this.info.render.points += geometryGroup.__webglParticleCount; - face = obj_faces[ chunk_faces3[ f ] ]; + } - customAttribute.array[ offset_custom ] = customAttribute.value[ face.a ]; - customAttribute.array[ offset_custom + 1 ] = customAttribute.value[ face.b ]; - customAttribute.array[ offset_custom + 2 ] = customAttribute.value[ face.c ]; + }; - offset_custom += 3; + function initAttributes() { - } + for ( var i = 0, l = _newAttributes.length; i < l; i ++ ) { - } else if ( customAttribute.boundTo === 'faces' ) { + _newAttributes[ i ] = 0; - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + } - value = customAttribute.value[ chunk_faces3[ f ] ]; + } - customAttribute.array[ offset_custom ] = value; - customAttribute.array[ offset_custom + 1 ] = value; - customAttribute.array[ offset_custom + 2 ] = value; + function enableAttribute( attribute ) { - offset_custom += 3; + _newAttributes[ attribute ] = 1; - } + if ( _enabledAttributes[ attribute ] === 0 ) { - } + _gl.enableVertexAttribArray( attribute ); + _enabledAttributes[ attribute ] = 1; - } else if ( customAttribute.size === 2 ) { + } - if ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) { + } - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + function disableUnusedAttributes() { - face = obj_faces[ chunk_faces3[ f ] ]; + for ( var i = 0, l = _enabledAttributes.length; i < l; i ++ ) { - v1 = customAttribute.value[ face.a ]; - v2 = customAttribute.value[ face.b ]; - v3 = customAttribute.value[ face.c ]; + if ( _enabledAttributes[ i ] !== _newAttributes[ i ] ) { - customAttribute.array[ offset_custom ] = v1.x; - customAttribute.array[ offset_custom + 1 ] = v1.y; + _gl.disableVertexAttribArray( i ); + _enabledAttributes[ i ] = 0; - customAttribute.array[ offset_custom + 2 ] = v2.x; - customAttribute.array[ offset_custom + 3 ] = v2.y; + } - customAttribute.array[ offset_custom + 4 ] = v3.x; - customAttribute.array[ offset_custom + 5 ] = v3.y; + } - offset_custom += 6; + } - } + function setupMorphTargets ( material, geometryGroup, object ) { - } else if ( customAttribute.boundTo === 'faces' ) { + // set base - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + var attributes = material.program.attributes; - value = customAttribute.value[ chunk_faces3[ f ] ]; + if ( object.morphTargetBase !== - 1 && attributes.position >= 0 ) { - v1 = value; - v2 = value; - v3 = value; + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ object.morphTargetBase ] ); + enableAttribute( attributes.position ); + _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); - customAttribute.array[ offset_custom ] = v1.x; - customAttribute.array[ offset_custom + 1 ] = v1.y; + } else if ( attributes.position >= 0 ) { - customAttribute.array[ offset_custom + 2 ] = v2.x; - customAttribute.array[ offset_custom + 3 ] = v2.y; + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); + enableAttribute( attributes.position ); + _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); - customAttribute.array[ offset_custom + 4 ] = v3.x; - customAttribute.array[ offset_custom + 5 ] = v3.y; + } - offset_custom += 6; + if ( object.morphTargetForcedOrder.length ) { - } + // set forced order - } + var m = 0; + var order = object.morphTargetForcedOrder; + var influences = object.morphTargetInfluences; - } else if ( customAttribute.size === 3 ) { + while ( m < material.numSupportedMorphTargets && m < order.length ) { - var pp; + if ( attributes[ 'morphTarget' + m ] >= 0 ) { - if ( customAttribute.type === 'c' ) { + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ order[ m ] ] ); + enableAttribute( attributes[ 'morphTarget' + m ] ); + _gl.vertexAttribPointer( attributes[ 'morphTarget' + m ], 3, _gl.FLOAT, false, 0, 0 ); - pp = [ 'r', 'g', 'b' ]; + } - } else { + if ( attributes[ 'morphNormal' + m ] >= 0 && material.morphNormals ) { - pp = [ 'x', 'y', 'z' ]; + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ order[ m ] ] ); + enableAttribute( attributes[ 'morphNormal' + m ] ); + _gl.vertexAttribPointer( attributes[ 'morphNormal' + m ], 3, _gl.FLOAT, false, 0, 0 ); - } + } - if ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) { + object.__webglMorphTargetInfluences[ m ] = influences[ order[ m ] ]; - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + m ++; + } - face = obj_faces[ chunk_faces3[ f ] ]; + } else { - v1 = customAttribute.value[ face.a ]; - v2 = customAttribute.value[ face.b ]; - v3 = customAttribute.value[ face.c ]; + // find the most influencing - customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; - customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; - customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; + var influence, activeInfluenceIndices = []; + var influences = object.morphTargetInfluences; + var i, il = influences.length; - customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; - customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; - customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; + for ( i = 0; i < il; i ++ ) { - customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; - customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; - customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; + influence = influences[ i ]; - offset_custom += 9; + if ( influence > 0 ) { - } + activeInfluenceIndices.push( [ influence, i ] ); - } else if ( customAttribute.boundTo === 'faces' ) { + } - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + } - value = customAttribute.value[ chunk_faces3[ f ] ]; + if ( activeInfluenceIndices.length > material.numSupportedMorphTargets ) { - v1 = value; - v2 = value; - v3 = value; + activeInfluenceIndices.sort( numericalSort ); + activeInfluenceIndices.length = material.numSupportedMorphTargets; - customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; - customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; - customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; + } else if ( activeInfluenceIndices.length > material.numSupportedMorphNormals ) { - customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; - customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; - customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; + activeInfluenceIndices.sort( numericalSort ); - customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; - customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; - customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; + } else if ( activeInfluenceIndices.length === 0 ) { - offset_custom += 9; + activeInfluenceIndices.push( [ 0, 0 ] ); - } + }; - } else if ( customAttribute.boundTo === 'faceVertices' ) { + var influenceIndex, m = 0; - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + while ( m < material.numSupportedMorphTargets ) { - value = customAttribute.value[ chunk_faces3[ f ] ]; + if ( activeInfluenceIndices[ m ] ) { - v1 = value[ 0 ]; - v2 = value[ 1 ]; - v3 = value[ 2 ]; + influenceIndex = activeInfluenceIndices[ m ][ 1 ]; - customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; - customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; - customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; + if ( attributes[ 'morphTarget' + m ] >= 0 ) { - customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; - customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; - customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ influenceIndex ] ); + enableAttribute( attributes[ 'morphTarget' + m ] ); + _gl.vertexAttribPointer( attributes[ 'morphTarget' + m ], 3, _gl.FLOAT, false, 0, 0 ); - customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; - customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; - customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; + } - offset_custom += 9; + if ( attributes[ 'morphNormal' + m ] >= 0 && material.morphNormals ) { - } + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ influenceIndex ] ); + enableAttribute( attributes[ 'morphNormal' + m ] ); + _gl.vertexAttribPointer( attributes[ 'morphNormal' + m ], 3, _gl.FLOAT, false, 0, 0 ); - } - } else if ( customAttribute.size === 4 ) { + } - if ( customAttribute.boundTo === undefined || customAttribute.boundTo === 'vertices' ) { + object.__webglMorphTargetInfluences[ m ] = influences[ influenceIndex ]; - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + } else { - face = obj_faces[ chunk_faces3[ f ] ]; + /* + _gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 ); - v1 = customAttribute.value[ face.a ]; - v2 = customAttribute.value[ face.b ]; - v3 = customAttribute.value[ face.c ]; + if ( material.morphNormals ) { - customAttribute.array[ offset_custom ] = v1.x; - customAttribute.array[ offset_custom + 1 ] = v1.y; - customAttribute.array[ offset_custom + 2 ] = v1.z; - customAttribute.array[ offset_custom + 3 ] = v1.w; + _gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 ); - customAttribute.array[ offset_custom + 4 ] = v2.x; - customAttribute.array[ offset_custom + 5 ] = v2.y; - customAttribute.array[ offset_custom + 6 ] = v2.z; - customAttribute.array[ offset_custom + 7 ] = v2.w; + } + */ - customAttribute.array[ offset_custom + 8 ] = v3.x; - customAttribute.array[ offset_custom + 9 ] = v3.y; - customAttribute.array[ offset_custom + 10 ] = v3.z; - customAttribute.array[ offset_custom + 11 ] = v3.w; + object.__webglMorphTargetInfluences[ m ] = 0; - offset_custom += 12; + } - } + m ++; - } else if ( customAttribute.boundTo === 'faces' ) { + } - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + } - value = customAttribute.value[ chunk_faces3[ f ] ]; + // load updated influences uniform - v1 = value; - v2 = value; - v3 = value; + if ( material.program.uniforms.morphTargetInfluences !== null ) { - customAttribute.array[ offset_custom ] = v1.x; - customAttribute.array[ offset_custom + 1 ] = v1.y; - customAttribute.array[ offset_custom + 2 ] = v1.z; - customAttribute.array[ offset_custom + 3 ] = v1.w; + _gl.uniform1fv( material.program.uniforms.morphTargetInfluences, object.__webglMorphTargetInfluences ); - customAttribute.array[ offset_custom + 4 ] = v2.x; - customAttribute.array[ offset_custom + 5 ] = v2.y; - customAttribute.array[ offset_custom + 6 ] = v2.z; - customAttribute.array[ offset_custom + 7 ] = v2.w; + } - customAttribute.array[ offset_custom + 8 ] = v3.x; - customAttribute.array[ offset_custom + 9 ] = v3.y; - customAttribute.array[ offset_custom + 10 ] = v3.z; - customAttribute.array[ offset_custom + 11 ] = v3.w; + } - offset_custom += 12; + // Sorting - } + function painterSortStable ( a, b ) { - } else if ( customAttribute.boundTo === 'faceVertices' ) { + if ( a.material.id !== b.material.id ) { - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + return b.material.id - a.material.id; - value = customAttribute.value[ chunk_faces3[ f ] ]; + } else if ( a.z !== b.z ) { - v1 = value[ 0 ]; - v2 = value[ 1 ]; - v3 = value[ 2 ]; + return b.z - a.z; - customAttribute.array[ offset_custom ] = v1.x; - customAttribute.array[ offset_custom + 1 ] = v1.y; - customAttribute.array[ offset_custom + 2 ] = v1.z; - customAttribute.array[ offset_custom + 3 ] = v1.w; + } else { - customAttribute.array[ offset_custom + 4 ] = v2.x; - customAttribute.array[ offset_custom + 5 ] = v2.y; - customAttribute.array[ offset_custom + 6 ] = v2.z; - customAttribute.array[ offset_custom + 7 ] = v2.w; + return a.id - b.id; - customAttribute.array[ offset_custom + 8 ] = v3.x; - customAttribute.array[ offset_custom + 9 ] = v3.y; - customAttribute.array[ offset_custom + 10 ] = v3.z; - customAttribute.array[ offset_custom + 11 ] = v3.w; + } - offset_custom += 12; + } - } + function reversePainterSortStable ( a, b ) { - } + if ( a.z !== b.z ) { - } + return a.z - b.z; - _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); + } else { - } + return a.id - b.id; } - if ( dispose ) { + } - delete geometryGroup.__inittedArrays; - delete geometryGroup.__colorArray; - delete geometryGroup.__normalArray; - delete geometryGroup.__tangentArray; - delete geometryGroup.__uvArray; - delete geometryGroup.__uv2Array; - delete geometryGroup.__faceArray; - delete geometryGroup.__vertexArray; - delete geometryGroup.__lineArray; - delete geometryGroup.__skinIndexArray; - delete geometryGroup.__skinWeightArray; + function numericalSort ( a, b ) { - } + return b[ 0 ] - a[ 0 ]; - }; + } - function setDirectBuffers( geometry, hint ) { + // Rendering - var attributes = geometry.attributes; + this.render = function ( scene, camera, renderTarget, forceClear ) { - var attributeName, attributeItem; + if ( camera instanceof THREE.Camera === false ) { - for ( attributeName in attributes ) { + console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); + return; - attributeItem = attributes[ attributeName ]; + } - if ( attributeItem.needsUpdate ) { + var fog = scene.fog; - if ( attributeName === 'index' ) { + // reset caching for this frame - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, attributeItem.buffer ); - _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, attributeItem.array, hint ); + _currentGeometryGroupHash = - 1; + _currentMaterialId = - 1; + _currentCamera = null; + _lightsNeedUpdate = true; - } else { + // update scene graph - _gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, attributeItem.array, hint ); + if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); - } + // update camera matrices and frustum + + if ( camera.parent === undefined ) camera.updateMatrixWorld(); + + // update Skeleton objects - attributeItem.needsUpdate = false; + scene.traverse( function ( object ) { + + if ( object instanceof THREE.SkinnedMesh ) { + + object.skeleton.update(); } - } + } ); - } + camera.matrixWorldInverse.getInverse( camera.matrixWorld ); - // Buffer rendering + _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); + _frustum.setFromMatrix( _projScreenMatrix ); - this.renderBufferImmediate = function ( object, program, material ) { + lights.length = 0; + opaqueObjects.length = 0; + transparentObjects.length = 0; - initAttributes(); + sprites.length = 0; + lensFlares.length = 0; - if ( object.hasPositions && ! object.__webglVertexBuffer ) object.__webglVertexBuffer = _gl.createBuffer(); - if ( object.hasNormals && ! object.__webglNormalBuffer ) object.__webglNormalBuffer = _gl.createBuffer(); - if ( object.hasUvs && ! object.__webglUvBuffer ) object.__webglUvBuffer = _gl.createBuffer(); - if ( object.hasColors && ! object.__webglColorBuffer ) object.__webglColorBuffer = _gl.createBuffer(); + projectObject( scene, scene ); - if ( object.hasPositions ) { + if ( _this.sortObjects === true ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglVertexBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW ); - enableAttribute( program.attributes.position ); - _gl.vertexAttribPointer( program.attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + opaqueObjects.sort( painterSortStable ); + transparentObjects.sort( reversePainterSortStable ); } - if ( object.hasNormals ) { + // custom render plugins (pre pass) - _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglNormalBuffer ); + shadowMapPlugin.render( scene, camera ); - if ( material.shading === THREE.FlatShading ) { + // - var nx, ny, nz, - nax, nbx, ncx, nay, nby, ncy, naz, nbz, ncz, - normalArray, - i, il = object.count * 3; + _this.info.render.calls = 0; + _this.info.render.vertices = 0; + _this.info.render.faces = 0; + _this.info.render.points = 0; - for ( i = 0; i < il; i += 9 ) { + this.setRenderTarget( renderTarget ); - normalArray = object.normalArray; + if ( this.autoClear || forceClear ) { - nax = normalArray[ i ]; - nay = normalArray[ i + 1 ]; - naz = normalArray[ i + 2 ]; + this.clear( this.autoClearColor, this.autoClearDepth, this.autoClearStencil ); - nbx = normalArray[ i + 3 ]; - nby = normalArray[ i + 4 ]; - nbz = normalArray[ i + 5 ]; + } - ncx = normalArray[ i + 6 ]; - ncy = normalArray[ i + 7 ]; - ncz = normalArray[ i + 8 ]; + // set matrices for immediate objects - nx = ( nax + nbx + ncx ) / 3; - ny = ( nay + nby + ncy ) / 3; - nz = ( naz + nbz + ncz ) / 3; + for ( var i = 0, il = _webglObjectsImmediate.length; i < il; i ++ ) { - normalArray[ i ] = nx; - normalArray[ i + 1 ] = ny; - normalArray[ i + 2 ] = nz; + var webglObject = _webglObjectsImmediate[ i ]; + var object = webglObject.object; - normalArray[ i + 3 ] = nx; - normalArray[ i + 4 ] = ny; - normalArray[ i + 5 ] = nz; + if ( object.visible ) { - normalArray[ i + 6 ] = nx; - normalArray[ i + 7 ] = ny; - normalArray[ i + 8 ] = nz; + setupMatrices( object, camera ); - } + unrollImmediateBufferMaterial( webglObject ); } - _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW ); - enableAttribute( program.attributes.normal ); - _gl.vertexAttribPointer( program.attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); - } - if ( object.hasUvs && material.map ) { + if ( scene.overrideMaterial ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglUvBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW ); - enableAttribute( program.attributes.uv ); - _gl.vertexAttribPointer( program.attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); + var material = scene.overrideMaterial; - } + this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); + this.setDepthTest( material.depthTest ); + this.setDepthWrite( material.depthWrite ); + setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); - if ( object.hasColors && material.vertexColors !== THREE.NoColors ) { + renderObjects( opaqueObjects, camera, lights, fog, true, material ); + renderObjects( transparentObjects, camera, lights, fog, true, material ); + renderObjectsImmediate( _webglObjectsImmediate, '', camera, lights, fog, false, material ); - _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglColorBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW ); - enableAttribute( program.attributes.color ); - _gl.vertexAttribPointer( program.attributes.color, 3, _gl.FLOAT, false, 0, 0 ); + } else { - } + var material = null; - disableUnusedAttributes(); + // opaque pass (front-to-back order) - _gl.drawArrays( _gl.TRIANGLES, 0, object.count ); + this.setBlending( THREE.NoBlending ); - object.count = 0; + renderObjects( opaqueObjects, camera, lights, fog, false, material ); + renderObjectsImmediate( _webglObjectsImmediate, 'opaque', camera, lights, fog, false, material ); - }; + // transparent pass (back-to-front order) - function setupVertexAttributes( material, programAttributes, geometryAttributes, startIndex ) { + renderObjects( transparentObjects, camera, lights, fog, true, material ); + renderObjectsImmediate( _webglObjectsImmediate, 'transparent', camera, lights, fog, true, material ); - for ( var attributeName in programAttributes ) { + } - var attributePointer = programAttributes[ attributeName ]; - var attributeItem = geometryAttributes[ attributeName ]; + // custom render plugins (post pass) - if ( attributePointer >= 0 ) { + spritePlugin.render( scene, camera ); + lensFlarePlugin.render( scene, camera, _currentWidth, _currentHeight ); - if ( attributeItem ) { + // Generate mipmap if we're using any kind of mipmap filtering - var attributeSize = attributeItem.itemSize; + if ( renderTarget && renderTarget.generateMipmaps && renderTarget.minFilter !== THREE.NearestFilter && renderTarget.minFilter !== THREE.LinearFilter ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer ); - enableAttribute( attributePointer ); - _gl.vertexAttribPointer( attributePointer, attributeSize, _gl.FLOAT, false, 0, startIndex * attributeSize * 4 ); // 4 bytes per Float32 + updateRenderTargetMipmap( renderTarget ); - } else if ( material.defaultAttributeValues ) { + } - if ( material.defaultAttributeValues[ attributeName ].length === 2 ) { + // Ensure depth buffer writing is enabled so it can be cleared on next render - _gl.vertexAttrib2fv( attributePointer, material.defaultAttributeValues[ attributeName ] ); + this.setDepthTest( true ); + this.setDepthWrite( true ); - } else if ( material.defaultAttributeValues[ attributeName ].length === 3 ) { + // _gl.finish(); - _gl.vertexAttrib3fv( attributePointer, material.defaultAttributeValues[ attributeName ] ); + }; - } + function projectObject( scene, object ) { - } + if ( object.visible === false ) return; - } + if ( object instanceof THREE.Scene || object instanceof THREE.Group ) { - } + // skip - disableUnusedAttributes(); + } else { - } + initObject( object, scene ); - this.renderBufferDirect = function ( camera, lights, fog, material, geometry, object ) { + if ( object instanceof THREE.Light ) { - if ( material.visible === false ) return; + lights.push( object ); - var linewidth, a, attribute; - var attributeItem, attributeName, attributePointer, attributeSize; + } else if ( object instanceof THREE.Sprite ) { - var program = setProgram( camera, lights, fog, material, object ); + sprites.push( object ); - var programAttributes = program.attributes; - var geometryAttributes = geometry.attributes; + } else if ( object instanceof THREE.LensFlare ) { - var updateBuffers = false, - wireframeBit = material.wireframe ? 1 : 0, - geometryHash = ( geometry.id * 0xffffff ) + ( program.id * 2 ) + wireframeBit; + lensFlares.push( object ); - if ( geometryHash !== _currentGeometryGroupHash ) { + } else { - _currentGeometryGroupHash = geometryHash; - updateBuffers = true; + var webglObjects = _webglObjects[ object.id ]; - } + if ( webglObjects && ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) ) { - if ( updateBuffers ) { + updateObject( object, scene ); - initAttributes(); + for ( var i = 0, l = webglObjects.length; i < l; i ++ ) { - } + var webglObject = webglObjects[i]; - // render mesh + unrollBufferMaterial( webglObject ); - if ( object instanceof THREE.Mesh ) { + webglObject.render = true; - var index = geometryAttributes[ 'index' ]; + if ( _this.sortObjects === true ) { - if ( index ) { + if ( object.renderDepth !== null ) { - // indexed triangles + webglObject.z = object.renderDepth; - var type, size; + } else { - if ( index.array instanceof Uint32Array ) { + _vector3.setFromMatrixPosition( object.matrixWorld ); + _vector3.applyProjection( _projScreenMatrix ); - type = _gl.UNSIGNED_INT; - size = 4; + webglObject.z = _vector3.z; - } else { + } - type = _gl.UNSIGNED_SHORT; - size = 2; + } + + } } - var offsets = geometry.offsets; + } - if ( offsets.length === 0 ) { + } - if ( updateBuffers ) { + for ( var i = 0, l = object.children.length; i < l; i ++ ) { - setupVertexAttributes( material, programAttributes, geometryAttributes, 0 ); - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); + projectObject( scene, object.children[ i ] ); - } + } - _gl.drawElements( _gl.TRIANGLES, index.array.length, type, 0 ); + } - _this.info.render.calls ++; - _this.info.render.vertices += index.array.length; // not really true, here vertices can be shared - _this.info.render.faces += index.array.length / 3; + function renderObjects( renderList, camera, lights, fog, useBlending, overrideMaterial ) { - } else { + var material; - // if there is more than 1 chunk - // must set attribute pointers to use new offsets for each chunk - // even if geometry and materials didn't change + for ( var i = renderList.length - 1; i !== - 1; i -- ) { - updateBuffers = true; + var webglObject = renderList[ i ]; - for ( var i = 0, il = offsets.length; i < il; i ++ ) { + var object = webglObject.object; + var buffer = webglObject.buffer; - var startIndex = offsets[ i ].index; + setupMatrices( object, camera ); - if ( updateBuffers ) { + if ( overrideMaterial ) { - setupVertexAttributes( material, programAttributes, geometryAttributes, startIndex ); - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); + material = overrideMaterial; - } + } else { - // render indexed triangles + material = webglObject.material; - _gl.drawElements( _gl.TRIANGLES, offsets[ i ].count, type, offsets[ i ].start * size ); + if ( ! material ) continue; - _this.info.render.calls ++; - _this.info.render.vertices += offsets[ i ].count; // not really true, here vertices can be shared - _this.info.render.faces += offsets[ i ].count / 3; + if ( useBlending ) _this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); - } + _this.setDepthTest( material.depthTest ); + _this.setDepthWrite( material.depthWrite ); + setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); - } + } - } else { + _this.setMaterialFaces( material ); - // non-indexed triangles + if ( buffer instanceof THREE.BufferGeometry ) { - if ( updateBuffers ) { + _this.renderBufferDirect( camera, lights, fog, material, buffer, object ); - setupVertexAttributes( material, programAttributes, geometryAttributes, 0 ); + } else { - } + _this.renderBuffer( camera, lights, fog, material, buffer, object ); - var position = geometry.attributes[ 'position' ]; + } - // render non-indexed triangles + } - _gl.drawArrays( _gl.TRIANGLES, 0, position.array.length / 3 ); + } - _this.info.render.calls ++; - _this.info.render.vertices += position.array.length / 3; - _this.info.render.faces += position.array.length / 9; + function renderObjectsImmediate ( renderList, materialType, camera, lights, fog, useBlending, overrideMaterial ) { - } + var material; - } else if ( object instanceof THREE.PointCloud ) { + for ( var i = 0, il = renderList.length; i < il; i ++ ) { - // render particles + var webglObject = renderList[ i ]; + var object = webglObject.object; - if ( updateBuffers ) { + if ( object.visible ) { - setupVertexAttributes( material, programAttributes, geometryAttributes, 0 ); + if ( overrideMaterial ) { - } + material = overrideMaterial; - var position = geometryAttributes[ 'position' ]; + } else { - // render particles + material = webglObject[ materialType ]; - _gl.drawArrays( _gl.POINTS, 0, position.array.length / 3 ); + if ( ! material ) continue; - _this.info.render.calls ++; - _this.info.render.points += position.array.length / 3; + if ( useBlending ) _this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); - } else if ( object instanceof THREE.Line ) { + _this.setDepthTest( material.depthTest ); + _this.setDepthWrite( material.depthWrite ); + setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); - var mode = ( object.type === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES; + } - setLineWidth( material.linewidth ); + _this.renderImmediateObject( camera, lights, fog, material, object ); - var index = geometryAttributes[ 'index' ]; + } - if ( index ) { + } - // indexed lines + } - var type, size; + this.renderImmediateObject = function ( camera, lights, fog, material, object ) { - if ( index.array instanceof Uint32Array ) { + var program = setProgram( camera, lights, fog, material, object ); - type = _gl.UNSIGNED_INT; - size = 4; + _currentGeometryGroupHash = - 1; - } else { + _this.setMaterialFaces( material ); - type = _gl.UNSIGNED_SHORT; - size = 2; + if ( object.immediateRenderCallback ) { - } + object.immediateRenderCallback( program, _gl, _frustum ); - var offsets = geometry.offsets; + } else { - if ( offsets.length === 0 ) { + object.render( function ( object ) { _this.renderBufferImmediate( object, program, material ); } ); - if ( updateBuffers ) { + } - setupVertexAttributes( material, programAttributes, geometryAttributes, 0 ); - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); + }; - } + function unrollImmediateBufferMaterial ( globject ) { - _gl.drawElements( mode, index.array.length, type, 0 ); // 2 bytes per Uint16Array + var object = globject.object, + material = object.material; - _this.info.render.calls ++; - _this.info.render.vertices += index.array.length; // not really true, here vertices can be shared + if ( material.transparent ) { - } else { - - // if there is more than 1 chunk - // must set attribute pointers to use new offsets for each chunk - // even if geometry and materials didn't change - - if ( offsets.length > 1 ) updateBuffers = true; - - for ( var i = 0, il = offsets.length; i < il; i ++ ) { - - var startIndex = offsets[ i ].index; - - if ( updateBuffers ) { + globject.transparent = material; + globject.opaque = null; - setupVertexAttributes( material, programAttributes, geometryAttributes, startIndex ); - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); + } else { - } + globject.opaque = material; + globject.transparent = null; - // render indexed lines + } - _gl.drawElements( mode, offsets[ i ].count, type, offsets[ i ].start * size ); // 2 bytes per Uint16Array + } - _this.info.render.calls ++; - _this.info.render.vertices += offsets[ i ].count; // not really true, here vertices can be shared + function unrollBufferMaterial ( globject ) { - } + var object = globject.object; + var buffer = globject.buffer; - } + var geometry = object.geometry; + var material = object.material; - } else { + if ( material instanceof THREE.MeshFaceMaterial ) { - // non-indexed lines + var materialIndex = geometry instanceof THREE.BufferGeometry ? 0 : buffer.materialIndex; - if ( updateBuffers ) { + material = material.materials[ materialIndex ]; - setupVertexAttributes( material, programAttributes, geometryAttributes, 0 ); + globject.material = material; - } + if ( material.transparent ) { - var position = geometryAttributes[ 'position' ]; + transparentObjects.push( globject ); - _gl.drawArrays( mode, 0, position.array.length / 3 ); + } else { - _this.info.render.calls ++; - _this.info.render.points += position.array.length / 3; + opaqueObjects.push( globject ); } - } + } else if ( material ) { - }; + globject.material = material; - this.renderBuffer = function ( camera, lights, fog, material, geometryGroup, object ) { + if ( material.transparent ) { - if ( material.visible === false ) return; + transparentObjects.push( globject ); - var linewidth, a, attribute, i, il; + } else { - var program = setProgram( camera, lights, fog, material, object ); + opaqueObjects.push( globject ); - var attributes = program.attributes; + } - var updateBuffers = false, - wireframeBit = material.wireframe ? 1 : 0, - geometryGroupHash = ( geometryGroup.id * 0xffffff ) + ( program.id * 2 ) + wireframeBit; + } - if ( geometryGroupHash !== _currentGeometryGroupHash ) { + } - _currentGeometryGroupHash = geometryGroupHash; - updateBuffers = true; + function initObject( object, scene ) { - } + if ( object.__webglInit === undefined ) { - if ( updateBuffers ) { + object.__webglInit = true; + object._modelViewMatrix = new THREE.Matrix4(); + object._normalMatrix = new THREE.Matrix3(); - initAttributes(); + object.addEventListener( 'removed', onObjectRemoved ); } - // vertices - - if ( ! material.morphTargets && attributes.position >= 0 ) { - - if ( updateBuffers ) { + var geometry = object.geometry; - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); - enableAttribute( attributes.position ); - _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + if ( geometry === undefined ) { - } + // ImmediateRenderObject - } else { + } else if ( geometry.__webglInit === undefined ) { - if ( object.morphTargetBase ) { + geometry.__webglInit = true; + geometry.addEventListener( 'dispose', onGeometryDispose ); - setupMorphTargets( material, geometryGroup, object ); + if ( geometry instanceof THREE.BufferGeometry ) { - } + // - } + } else if ( object instanceof THREE.Mesh ) { + initGeometryGroups( scene, object, geometry ); - if ( updateBuffers ) { + } else if ( object instanceof THREE.Line ) { - // custom attributes + if ( geometry.__webglVertexBuffer === undefined ) { - // Use the per-geometryGroup custom attribute arrays which are setup in initMeshBuffers + createLineBuffers( geometry ); + initLineBuffers( geometry, object ); - if ( geometryGroup.__webglCustomAttributesList ) { + geometry.verticesNeedUpdate = true; + geometry.colorsNeedUpdate = true; + geometry.lineDistancesNeedUpdate = true; - for ( i = 0, il = geometryGroup.__webglCustomAttributesList.length; i < il; i ++ ) { + } - attribute = geometryGroup.__webglCustomAttributesList[ i ]; + } else if ( object instanceof THREE.PointCloud ) { - if ( attributes[ attribute.buffer.belongsToAttribute ] >= 0 ) { + if ( geometry.__webglVertexBuffer === undefined ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, attribute.buffer ); - enableAttribute( attributes[ attribute.buffer.belongsToAttribute ] ); - _gl.vertexAttribPointer( attributes[ attribute.buffer.belongsToAttribute ], attribute.size, _gl.FLOAT, false, 0, 0 ); + createParticleBuffers( geometry ); + initParticleBuffers( geometry, object ); - } + geometry.verticesNeedUpdate = true; + geometry.colorsNeedUpdate = true; } } + } - // colors - - if ( attributes.color >= 0 ) { + if ( object.__webglActive === undefined) { - if ( object.geometry.colors.length > 0 || object.geometry.faces.length > 0 ) { + object.__webglActive = true; - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer ); - enableAttribute( attributes.color ); - _gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 ); + if ( object instanceof THREE.Mesh ) { - } else if ( material.defaultAttributeValues ) { + if ( geometry instanceof THREE.BufferGeometry ) { + addBuffer( _webglObjects, geometry, object ); - _gl.vertexAttrib3fv( attributes.color, material.defaultAttributeValues.color ); + } else if ( geometry instanceof THREE.Geometry ) { - } + var geometryGroupsList = geometryGroups[ geometry.id ]; - } + for ( var i = 0,l = geometryGroupsList.length; i < l; i ++ ) { - // normals + addBuffer( _webglObjects, geometryGroupsList[ i ], object ); - if ( attributes.normal >= 0 ) { + } - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer ); - enableAttribute( attributes.normal ); - _gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); + } - } + } else if ( object instanceof THREE.Line || object instanceof THREE.PointCloud ) { - // tangents + addBuffer( _webglObjects, geometry, object ); - if ( attributes.tangent >= 0 ) { + } else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer ); - enableAttribute( attributes.tangent ); - _gl.vertexAttribPointer( attributes.tangent, 4, _gl.FLOAT, false, 0, 0 ); + addBufferImmediate( _webglObjectsImmediate, object ); } - // uvs - - if ( attributes.uv >= 0 ) { - - if ( object.geometry.faceVertexUvs[ 0 ] ) { - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer ); - enableAttribute( attributes.uv ); - _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); + } - } else if ( material.defaultAttributeValues ) { + } + // Geometry splitting - _gl.vertexAttrib2fv( attributes.uv, material.defaultAttributeValues.uv ); + var geometryGroups = {}; + var geometryGroupCounter = 0; - } + function makeGroups( geometry, usesFaceMaterial ) { - } + var maxVerticesInGroup = extensions.get( 'OES_element_index_uint' ) ? 4294967296 : 65535; - if ( attributes.uv2 >= 0 ) { + var groupHash, hash_map = {}; - if ( object.geometry.faceVertexUvs[ 1 ] ) { + var numMorphTargets = geometry.morphTargets.length; + var numMorphNormals = geometry.morphNormals.length; - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer ); - enableAttribute( attributes.uv2 ); - _gl.vertexAttribPointer( attributes.uv2, 2, _gl.FLOAT, false, 0, 0 ); + var group; + var groups = {}; + var groupsList = []; - } else if ( material.defaultAttributeValues ) { + for ( var f = 0, fl = geometry.faces.length; f < fl; f ++ ) { + var face = geometry.faces[ f ]; + var materialIndex = usesFaceMaterial ? face.materialIndex : 0; - _gl.vertexAttrib2fv( attributes.uv2, material.defaultAttributeValues.uv2 ); + if ( ! ( materialIndex in hash_map ) ) { - } + hash_map[ materialIndex ] = { hash: materialIndex, counter: 0 }; } - if ( material.skinning && - attributes.skinIndex >= 0 && attributes.skinWeight >= 0 ) { + groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter; - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer ); - enableAttribute( attributes.skinIndex ); - _gl.vertexAttribPointer( attributes.skinIndex, 4, _gl.FLOAT, false, 0, 0 ); + if ( ! ( groupHash in groups ) ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer ); - enableAttribute( attributes.skinWeight ); - _gl.vertexAttribPointer( attributes.skinWeight, 4, _gl.FLOAT, false, 0, 0 ); + group = { + id: geometryGroupCounter ++, + faces3: [], + materialIndex: materialIndex, + vertices: 0, + numMorphTargets: numMorphTargets, + numMorphNormals: numMorphNormals + }; + + groups[ groupHash ] = group; + groupsList.push( group ); } - // line distances + if ( groups[ groupHash ].vertices + 3 > maxVerticesInGroup ) { - if ( attributes.lineDistance >= 0 ) { + hash_map[ materialIndex ].counter += 1; + groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter; - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglLineDistanceBuffer ); - enableAttribute( attributes.lineDistance ); - _gl.vertexAttribPointer( attributes.lineDistance, 1, _gl.FLOAT, false, 0, 0 ); + if ( ! ( groupHash in groups ) ) { - } + group = { + id: geometryGroupCounter ++, + faces3: [], + materialIndex: materialIndex, + vertices: 0, + numMorphTargets: numMorphTargets, + numMorphNormals: numMorphNormals + }; + + groups[ groupHash ] = group; + groupsList.push( group ); - } + } - disableUnusedAttributes(); + } - // render mesh + groups[ groupHash ].faces3.push( f ); + groups[ groupHash ].vertices += 3; - if ( object instanceof THREE.Mesh ) { + } - var type = geometryGroup.__typeArray === Uint32Array ? _gl.UNSIGNED_INT : _gl.UNSIGNED_SHORT; + return groupsList; - // wireframe + } - if ( material.wireframe ) { + function initGeometryGroups( scene, object, geometry ) { - setLineWidth( material.wireframeLinewidth ); - if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer ); - _gl.drawElements( _gl.LINES, geometryGroup.__webglLineCount, type, 0 ); + var material = object.material, addBuffers = false; - // triangles + if ( geometryGroups[ geometry.id ] === undefined || geometry.groupsNeedUpdate === true ) { - } else { + delete _webglObjects[ object.id ]; - if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer ); - _gl.drawElements( _gl.TRIANGLES, geometryGroup.__webglFaceCount, type, 0 ); + geometryGroups[ geometry.id ] = makeGroups( geometry, material instanceof THREE.MeshFaceMaterial ); - } + geometry.groupsNeedUpdate = false; - _this.info.render.calls ++; - _this.info.render.vertices += geometryGroup.__webglFaceCount; - _this.info.render.faces += geometryGroup.__webglFaceCount / 3; + } - // render lines + var geometryGroupsList = geometryGroups[ geometry.id ]; - } else if ( object instanceof THREE.Line ) { + // create separate VBOs per geometry chunk - var mode = ( object.type === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES; + for ( var i = 0, il = geometryGroupsList.length; i < il; i ++ ) { - setLineWidth( material.linewidth ); + var geometryGroup = geometryGroupsList[ i ]; - _gl.drawArrays( mode, 0, geometryGroup.__webglLineCount ); + // initialise VBO on the first access - _this.info.render.calls ++; + if ( geometryGroup.__webglVertexBuffer === undefined ) { - // render particles + createMeshBuffers( geometryGroup ); + initMeshBuffers( geometryGroup, object ); - } else if ( object instanceof THREE.PointCloud ) { + geometry.verticesNeedUpdate = true; + geometry.morphTargetsNeedUpdate = true; + geometry.elementsNeedUpdate = true; + geometry.uvsNeedUpdate = true; + geometry.normalsNeedUpdate = true; + geometry.tangentsNeedUpdate = true; + geometry.colorsNeedUpdate = true; - _gl.drawArrays( _gl.POINTS, 0, geometryGroup.__webglParticleCount ); + addBuffers = true; - _this.info.render.calls ++; - _this.info.render.points += geometryGroup.__webglParticleCount; + } else { - } + addBuffers = false; - }; + } - function initAttributes() { + if ( addBuffers || object.__webglActive === undefined ) { - for ( var i = 0, l = _newAttributes.length; i < l; i ++ ) { + addBuffer( _webglObjects, geometryGroup, object ); - _newAttributes[ i ] = 0; + } } - } - - function enableAttribute( attribute ) { - - _newAttributes[ attribute ] = 1; - - if ( _enabledAttributes[ attribute ] === 0 ) { - - _gl.enableVertexAttribArray( attribute ); - _enabledAttributes[ attribute ] = 1; - - } + object.__webglActive = true; } - function disableUnusedAttributes() { + function addBuffer( objlist, buffer, object ) { - for ( var i = 0, l = _enabledAttributes.length; i < l; i ++ ) { + var id = object.id; + objlist[id] = objlist[id] || []; + objlist[id].push( + { + id: id, + buffer: buffer, + object: object, + material: null, + z: 0 + } + ); - if ( _enabledAttributes[ i ] !== _newAttributes[ i ] ) { + }; - _gl.disableVertexAttribArray( i ); - _enabledAttributes[ i ] = 0; + function addBufferImmediate( objlist, object ) { + objlist.push( + { + id: null, + object: object, + opaque: null, + transparent: null, + z: 0 } + ); - } + }; - } + // Objects updates - function setupMorphTargets ( material, geometryGroup, object ) { + function updateObject( object, scene ) { - // set base + var geometry = object.geometry, customAttributesDirty, material; - var attributes = material.program.attributes; + if ( geometry instanceof THREE.BufferGeometry ) { - if ( object.morphTargetBase !== - 1 && attributes.position >= 0 ) { + setDirectBuffers( geometry ); - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ object.morphTargetBase ] ); - enableAttribute( attributes.position ); - _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + } else if ( object instanceof THREE.Mesh ) { - } else if ( attributes.position >= 0 ) { + // check all geometry groups - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); - enableAttribute( attributes.position ); - _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + if ( geometry.groupsNeedUpdate === true ) { - } + initGeometryGroups( scene, object, geometry ); - if ( object.morphTargetForcedOrder.length ) { + } - // set forced order + var geometryGroupsList = geometryGroups[ geometry.id ]; - var m = 0; - var order = object.morphTargetForcedOrder; - var influences = object.morphTargetInfluences; + for ( var i = 0, il = geometryGroupsList.length; i < il; i ++ ) { - while ( m < material.numSupportedMorphTargets && m < order.length ) { + var geometryGroup = geometryGroupsList[ i ]; - if ( attributes[ 'morphTarget' + m ] >= 0 ) { + material = getBufferMaterial( object, geometryGroup ); - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ order[ m ] ] ); - enableAttribute( attributes[ 'morphTarget' + m ] ); - _gl.vertexAttribPointer( attributes[ 'morphTarget' + m ], 3, _gl.FLOAT, false, 0, 0 ); + if ( geometry.groupsNeedUpdate === true ) { + + initMeshBuffers( geometryGroup, object ); } - if ( attributes[ 'morphNormal' + m ] >= 0 && material.morphNormals ) { + customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ order[ m ] ] ); - enableAttribute( attributes[ 'morphNormal' + m ] ); - _gl.vertexAttribPointer( attributes[ 'morphNormal' + m ], 3, _gl.FLOAT, false, 0, 0 ); + if ( geometry.verticesNeedUpdate || geometry.morphTargetsNeedUpdate || geometry.elementsNeedUpdate || + geometry.uvsNeedUpdate || geometry.normalsNeedUpdate || + geometry.colorsNeedUpdate || geometry.tangentsNeedUpdate || customAttributesDirty ) { - } + setMeshBuffers( geometryGroup, object, _gl.DYNAMIC_DRAW, ! geometry.dynamic, material ); - object.__webglMorphTargetInfluences[ m ] = influences[ order[ m ] ]; + } - m ++; } - } else { - - // find the most influencing + geometry.verticesNeedUpdate = false; + geometry.morphTargetsNeedUpdate = false; + geometry.elementsNeedUpdate = false; + geometry.uvsNeedUpdate = false; + geometry.normalsNeedUpdate = false; + geometry.colorsNeedUpdate = false; + geometry.tangentsNeedUpdate = false; - var influence, activeInfluenceIndices = []; - var influences = object.morphTargetInfluences; - var i, il = influences.length; + material.attributes && clearCustomAttributes( material ); - for ( i = 0; i < il; i ++ ) { + } else if ( object instanceof THREE.Line ) { - influence = influences[ i ]; + material = getBufferMaterial( object, geometry ); - if ( influence > 0 ) { + customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); - activeInfluenceIndices.push( [ influence, i ] ); + if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || geometry.lineDistancesNeedUpdate || customAttributesDirty ) { - } + setLineBuffers( geometry, _gl.DYNAMIC_DRAW ); } - if ( activeInfluenceIndices.length > material.numSupportedMorphTargets ) { - - activeInfluenceIndices.sort( numericalSort ); - activeInfluenceIndices.length = material.numSupportedMorphTargets; - - } else if ( activeInfluenceIndices.length > material.numSupportedMorphNormals ) { - - activeInfluenceIndices.sort( numericalSort ); - - } else if ( activeInfluenceIndices.length === 0 ) { - - activeInfluenceIndices.push( [ 0, 0 ] ); + geometry.verticesNeedUpdate = false; + geometry.colorsNeedUpdate = false; + geometry.lineDistancesNeedUpdate = false; - }; + material.attributes && clearCustomAttributes( material ); - var influenceIndex, m = 0; - while ( m < material.numSupportedMorphTargets ) { + } else if ( object instanceof THREE.PointCloud ) { - if ( activeInfluenceIndices[ m ] ) { + material = getBufferMaterial( object, geometry ); - influenceIndex = activeInfluenceIndices[ m ][ 1 ]; + customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); - if ( attributes[ 'morphTarget' + m ] >= 0 ) { + if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || object.sortParticles || customAttributesDirty ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ influenceIndex ] ); - enableAttribute( attributes[ 'morphTarget' + m ] ); - _gl.vertexAttribPointer( attributes[ 'morphTarget' + m ], 3, _gl.FLOAT, false, 0, 0 ); + setParticleBuffers( geometry, _gl.DYNAMIC_DRAW, object ); - } + } - if ( attributes[ 'morphNormal' + m ] >= 0 && material.morphNormals ) { + geometry.verticesNeedUpdate = false; + geometry.colorsNeedUpdate = false; - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ influenceIndex ] ); - enableAttribute( attributes[ 'morphNormal' + m ] ); - _gl.vertexAttribPointer( attributes[ 'morphNormal' + m ], 3, _gl.FLOAT, false, 0, 0 ); + material.attributes && clearCustomAttributes( material ); + } - } + } - object.__webglMorphTargetInfluences[ m ] = influences[ influenceIndex ]; + // Objects updates - custom attributes check - } else { + function areCustomAttributesDirty( material ) { - /* - _gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 ); + for ( var name in material.attributes ) { - if ( material.morphNormals ) { + if ( material.attributes[ name ].needsUpdate ) return true; - _gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 ); + } - } - */ + return false; - object.__webglMorphTargetInfluences[ m ] = 0; + } - } + function clearCustomAttributes( material ) { - m ++; + for ( var name in material.attributes ) { - } + material.attributes[ name ].needsUpdate = false; } - // load updated influences uniform + } - if ( material.program.uniforms.morphTargetInfluences !== null ) { + // Objects removal - _gl.uniform1fv( material.program.uniforms.morphTargetInfluences, object.__webglMorphTargetInfluences ); + function removeObject( object ) { - } + if ( object instanceof THREE.Mesh || + object instanceof THREE.PointCloud || + object instanceof THREE.Line ) { - }; + delete _webglObjects[ object.id ]; - // Sorting + } else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) { - function painterSortStable ( a, b ) { + removeInstances( _webglObjectsImmediate, object ); - if ( a.z !== b.z ) { + } - return b.z - a.z; + delete object.__webglInit; + delete object._modelViewMatrix; + delete object._normalMatrix; - } else { + delete object.__webglActive; - return a.id - b.id; + } - } + function removeInstances( objlist, object ) { - }; + for ( var o = objlist.length - 1; o >= 0; o -- ) { - function reversePainterSortStable ( a, b ) { + if ( objlist[ o ].object === object ) { - if ( a.z !== b.z ) { + objlist.splice( o, 1 ); - return a.z - b.z; + } - } else { + } - return a.id - b.id; + } - } + // Materials - }; + function initMaterial( material, lights, fog, object ) { - function numericalSort ( a, b ) { + material.addEventListener( 'dispose', onMaterialDispose ); - return b[ 0 ] - a[ 0 ]; + var shaderID; - }; + if ( material instanceof THREE.MeshDepthMaterial ) { + shaderID = 'depth'; - // Rendering + } else if ( material instanceof THREE.MeshNormalMaterial ) { - this.render = function ( scene, camera, renderTarget, forceClear ) { + shaderID = 'normal'; - if ( camera instanceof THREE.Camera === false ) { + } else if ( material instanceof THREE.MeshBasicMaterial ) { - console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); - return; + shaderID = 'basic'; - } + } else if ( material instanceof THREE.MeshLambertMaterial ) { - var i, il, + shaderID = 'lambert'; - webglObject, object, - renderList, + } else if ( material instanceof THREE.MeshPhongMaterial ) { - lights = scene.__lights, - fog = scene.fog; + shaderID = 'phong'; - // reset caching for this frame + } else if ( material instanceof THREE.LineBasicMaterial ) { - _currentMaterialId = - 1; - _currentCamera = null; - _lightsNeedUpdate = true; + shaderID = 'basic'; - // update scene graph + } else if ( material instanceof THREE.LineDashedMaterial ) { - if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); + shaderID = 'dashed'; - // update camera matrices and frustum + } else if ( material instanceof THREE.PointCloudMaterial ) { - if ( camera.parent === undefined ) camera.updateMatrixWorld(); + shaderID = 'particle_basic'; - // update Skeleton objects - function updateSkeletons( object ) { + } - if ( object instanceof THREE.SkinnedMesh ) { + if ( shaderID ) { - object.skeleton.update(); + var shader = THREE.ShaderLib[ shaderID ]; + material.__webglShader = { + uniforms: THREE.UniformsUtils.clone( shader.uniforms ), + vertexShader: shader.vertexShader, + fragmentShader: shader.fragmentShader } - for ( var i = 0, l = object.children.length; i < l; i ++ ) { - - updateSkeletons( object.children[ i ] ); + } else { + material.__webglShader = { + uniforms: material.uniforms, + vertexShader: material.vertexShader, + fragmentShader: material.fragmentShader } } - updateSkeletons( scene ); + // heuristics to create shader parameters according to lights in the scene + // (not to blow over maxLights budget) - camera.matrixWorldInverse.getInverse( camera.matrixWorld ); + var maxLightCount = allocateLights( lights ); + var maxShadows = allocateShadows( lights ); + var maxBones = allocateBones( object ); - _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); - _frustum.setFromMatrix( _projScreenMatrix ); + var parameters = { - initObjects( scene ); + precision: _precision, + supportsVertexTextures: _supportsVertexTextures, - opaqueObjects.length = 0; - transparentObjects.length = 0; - - projectObject( scene, scene, camera ); - - if ( _this.sortObjects === true ) { + map: !! material.map, + envMap: !! material.envMap, + lightMap: !! material.lightMap, + bumpMap: !! material.bumpMap, + normalMap: !! material.normalMap, + specularMap: !! material.specularMap, + alphaMap: !! material.alphaMap, - opaqueObjects.sort( painterSortStable ); - transparentObjects.sort( reversePainterSortStable ); + vertexColors: material.vertexColors, - } + fog: fog, + useFog: material.fog, + fogExp: fog instanceof THREE.FogExp2, - // custom render plugins (pre pass) - - renderPlugins( this.renderPluginsPre, scene, camera ); + sizeAttenuation: material.sizeAttenuation, + logarithmicDepthBuffer: _logarithmicDepthBuffer, - // + skinning: material.skinning, + maxBones: maxBones, + useVertexTexture: _supportsBoneTextures && object && object.skeleton && object.skeleton.useVertexTexture, - _this.info.render.calls = 0; - _this.info.render.vertices = 0; - _this.info.render.faces = 0; - _this.info.render.points = 0; + morphTargets: material.morphTargets, + morphNormals: material.morphNormals, + maxMorphTargets: _this.maxMorphTargets, + maxMorphNormals: _this.maxMorphNormals, - this.setRenderTarget( renderTarget ); + maxDirLights: maxLightCount.directional, + maxPointLights: maxLightCount.point, + maxSpotLights: maxLightCount.spot, + maxHemiLights: maxLightCount.hemi, - if ( this.autoClear || forceClear ) { + maxShadows: maxShadows, + shadowMapEnabled: _this.shadowMapEnabled && object.receiveShadow && maxShadows > 0, + shadowMapType: _this.shadowMapType, + shadowMapDebug: _this.shadowMapDebug, + shadowMapCascade: _this.shadowMapCascade, - this.clear( this.autoClearColor, this.autoClearDepth, this.autoClearStencil ); + alphaTest: material.alphaTest, + metal: material.metal, + wrapAround: material.wrapAround, + doubleSided: material.side === THREE.DoubleSide, + flipSided: material.side === THREE.BackSide - } + }; - // set matrices for regular objects (frustum culled) + // Generate code - + var chunks = []; + if ( shaderID ) { - // set matrices for immediate objects + chunks.push( shaderID ); - renderList = scene.__webglObjectsImmediate; + } else { - for ( i = 0, il = renderList.length; i < il; i ++ ) { + chunks.push( material.fragmentShader ); + chunks.push( material.vertexShader ); - webglObject = renderList[ i ]; - object = webglObject.object; + } - if ( object.visible ) { + if ( material.defines !== undefined ) { - setupMatrices( object, camera ); + for ( var name in material.defines ) { - unrollImmediateBufferMaterial( webglObject ); + chunks.push( name ); + chunks.push( material.defines[ name ] ); } } - if ( scene.overrideMaterial ) { + for ( var name in parameters ) { - var material = scene.overrideMaterial; + chunks.push( name ); + chunks.push( parameters[ name ] ); - this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); - this.setDepthTest( material.depthTest ); - this.setDepthWrite( material.depthWrite ); - setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); + } - renderObjects( opaqueObjects, camera, lights, fog, true, material ); - renderObjects( transparentObjects, camera, lights, fog, true, material ); - renderObjectsImmediate( scene.__webglObjectsImmediate, '', camera, lights, fog, false, material ); + var code = chunks.join(); - } else { + var program; - var material = null; + // Check if code has been already compiled - // opaque pass (front-to-back order) + for ( var p = 0, pl = _programs.length; p < pl; p ++ ) { - this.setBlending( THREE.NoBlending ); + var programInfo = _programs[ p ]; - renderObjects( opaqueObjects, camera, lights, fog, false, material ); - renderObjectsImmediate( scene.__webglObjectsImmediate, 'opaque', camera, lights, fog, false, material ); + if ( programInfo.code === code ) { - // transparent pass (back-to-front order) + program = programInfo; + program.usedTimes ++; - renderObjects( transparentObjects, camera, lights, fog, true, material ); - renderObjectsImmediate( scene.__webglObjectsImmediate, 'transparent', camera, lights, fog, true, material ); + break; + + } } - // custom render plugins (post pass) + if ( program === undefined ) { - renderPlugins( this.renderPluginsPost, scene, camera ); + program = new THREE.WebGLProgram( _this, code, material, parameters ); + _programs.push( program ); + _this.info.memory.programs = _programs.length; - // Generate mipmap if we're using any kind of mipmap filtering + } - if ( renderTarget && renderTarget.generateMipmaps && renderTarget.minFilter !== THREE.NearestFilter && renderTarget.minFilter !== THREE.LinearFilter ) { + material.program = program; - updateRenderTargetMipmap( renderTarget ); + var attributes = program.attributes; - } + if ( material.morphTargets ) { - // Ensure depth buffer writing is enabled so it can be cleared on next render + material.numSupportedMorphTargets = 0; - this.setDepthTest( true ); - this.setDepthWrite( true ); + var id, base = 'morphTarget'; - // _gl.finish(); + for ( var i = 0; i < _this.maxMorphTargets; i ++ ) { - }; - - function projectObject(scene, object,camera){ - - 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 ); + id = base + i; - webglObject.render = true; + if ( attributes[ id ] >= 0 ) { - if ( _this.sortObjects === true ) { + material.numSupportedMorphTargets ++; - if ( object.renderDepth !== null ) { + } - webglObject.z = object.renderDepth; + } - } else { + } - _vector3.setFromMatrixPosition( object.matrixWorld ); - _vector3.applyProjection( _projScreenMatrix ); + if ( material.morphNormals ) { - webglObject.z = _vector3.z; + material.numSupportedMorphNormals = 0; - } + var id, base = 'morphNormal'; + + for ( i = 0; i < _this.maxMorphNormals; i ++ ) { + + id = base + i; + + if ( attributes[ id ] >= 0 ) { + + material.numSupportedMorphNormals ++; } } } - - for ( var i = 0, l = object.children.length; i < l; i ++ ) { - projectObject( scene, object.children[ i ], camera ); + material.uniformsList = []; + + for ( var u in material.__webglShader.uniforms ) { + + var location = material.program.uniforms[ u ]; + + if ( location ) { + material.uniformsList.push( [ material.__webglShader.uniforms[ u ], location ] ); + } } - + } - function renderPlugins( plugins, scene, camera ) { + function setProgram( camera, lights, fog, material, object ) { + + _usedTextureUnits = 0; + + if ( material.needsUpdate ) { + + if ( material.program ) deallocateMaterial( material ); + + initMaterial( material, lights, fog, object ); + material.needsUpdate = false; - if ( plugins.length === 0 ) return; + } - for ( var i = 0, il = plugins.length; i < il; i ++ ) { + if ( material.morphTargets ) { - // reset state for plugin (to start from clean slate) + if ( ! object.__webglMorphTargetInfluences ) { - _currentProgram = null; - _currentCamera = null; + object.__webglMorphTargetInfluences = new Float32Array( _this.maxMorphTargets ); - _oldBlending = - 1; - _oldDepthTest = - 1; - _oldDepthWrite = - 1; - _oldDoubleSided = - 1; - _oldFlipSided = - 1; - _currentGeometryGroupHash = - 1; - _currentMaterialId = - 1; + } - _lightsNeedUpdate = true; + } - plugins[ i ].render( scene, camera, _currentWidth, _currentHeight ); + var refreshProgram = false; + var refreshMaterial = false; + var refreshLights = false; - // reset state after plugin (anything could have changed) + var program = material.program, + p_uniforms = program.uniforms, + m_uniforms = material.__webglShader.uniforms; - _currentProgram = null; - _currentCamera = null; + if ( program.id !== _currentProgram ) { - _oldBlending = - 1; - _oldDepthTest = - 1; - _oldDepthWrite = - 1; - _oldDoubleSided = - 1; - _oldFlipSided = - 1; - _currentGeometryGroupHash = - 1; - _currentMaterialId = - 1; + _gl.useProgram( program.program ); + _currentProgram = program.id; - _lightsNeedUpdate = true; + refreshProgram = true; + refreshMaterial = true; + refreshLights = true; } - }; + if ( material.id !== _currentMaterialId ) { - function renderObjects( renderList, camera, lights, fog, useBlending, overrideMaterial ) { + if ( _currentMaterialId === -1 ) refreshLights = true; + _currentMaterialId = material.id; - var webglObject, object, buffer, material; + refreshMaterial = true; - for ( var i = renderList.length - 1; i !== - 1; i -- ) { + } - webglObject = renderList[ i ]; + if ( refreshProgram || camera !== _currentCamera ) { - object = webglObject.object; - buffer = webglObject.buffer; - - setupMatrices( object, camera ); + _gl.uniformMatrix4fv( p_uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); - if ( overrideMaterial ) { + if ( _logarithmicDepthBuffer ) { - material = overrideMaterial; + _gl.uniform1f( p_uniforms.logDepthBufFC, 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); - } else { + } - material = webglObject.material; - if ( ! material ) continue; + if ( camera !== _currentCamera ) _currentCamera = camera; - if ( useBlending ) _this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); + // load material specific uniforms + // (shader material also gets them for the sake of genericity) - _this.setDepthTest( material.depthTest ); - _this.setDepthWrite( material.depthWrite ); - setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); + if ( material instanceof THREE.ShaderMaterial || + material instanceof THREE.MeshPhongMaterial || + material.envMap ) { - } + if ( p_uniforms.cameraPosition !== null ) { - _this.setMaterialFaces( material ); + _vector3.setFromMatrixPosition( camera.matrixWorld ); + _gl.uniform3f( p_uniforms.cameraPosition, _vector3.x, _vector3.y, _vector3.z ); - if ( buffer instanceof THREE.BufferGeometry ) { + } - _this.renderBufferDirect( camera, lights, fog, material, buffer, object ); + } - } else { + if ( material instanceof THREE.MeshPhongMaterial || + material instanceof THREE.MeshLambertMaterial || + material instanceof THREE.ShaderMaterial || + material.skinning ) { - _this.renderBuffer( camera, lights, fog, material, buffer, object ); + if ( p_uniforms.viewMatrix !== null ) { + + _gl.uniformMatrix4fv( p_uniforms.viewMatrix, false, camera.matrixWorldInverse.elements ); + + } } } - }; + // skinning uniforms must be set even if material didn't change + // auto-setting of texture unit for bone texture must go before other textures + // not sure why, but otherwise weird things happen - function renderObjectsImmediate ( renderList, materialType, camera, lights, fog, useBlending, overrideMaterial ) { + if ( material.skinning ) { - var webglObject, object, material, program; + if ( object.bindMatrix && p_uniforms.bindMatrix !== null ) { - for ( var i = 0, il = renderList.length; i < il; i ++ ) { + _gl.uniformMatrix4fv( p_uniforms.bindMatrix, false, object.bindMatrix.elements ); - webglObject = renderList[ i ]; - object = webglObject.object; + } - if ( object.visible ) { + if ( object.bindMatrixInverse && p_uniforms.bindMatrixInverse !== null ) { - if ( overrideMaterial ) { + _gl.uniformMatrix4fv( p_uniforms.bindMatrixInverse, false, object.bindMatrixInverse.elements ); - material = overrideMaterial; + } - } else { + if ( _supportsBoneTextures && object.skeleton && object.skeleton.useVertexTexture ) { - material = webglObject[ materialType ]; + if ( p_uniforms.boneTexture !== null ) { - if ( ! material ) continue; + var textureUnit = getTextureUnit(); - if ( useBlending ) _this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); + _gl.uniform1i( p_uniforms.boneTexture, textureUnit ); + _this.setTexture( object.skeleton.boneTexture, textureUnit ); - _this.setDepthTest( material.depthTest ); - _this.setDepthWrite( material.depthWrite ); - setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); + } + + if ( p_uniforms.boneTextureWidth !== null ) { + + _gl.uniform1i( p_uniforms.boneTextureWidth, object.skeleton.boneTextureWidth ); } - _this.renderImmediateObject( camera, lights, fog, material, object ); + if ( p_uniforms.boneTextureHeight !== null ) { - } + _gl.uniform1i( p_uniforms.boneTextureHeight, object.skeleton.boneTextureHeight ); - } + } - }; + } else if ( object.skeleton && object.skeleton.boneMatrices ) { - this.renderImmediateObject = function ( camera, lights, fog, material, object ) { + if ( p_uniforms.boneGlobalMatrices !== null ) { - var program = setProgram( camera, lights, fog, material, object ); + _gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, object.skeleton.boneMatrices ); - _currentGeometryGroupHash = - 1; + } - _this.setMaterialFaces( material ); + } - if ( object.immediateRenderCallback ) { + } - object.immediateRenderCallback( program, _gl, _frustum ); + if ( refreshMaterial ) { - } else { + // refresh uniforms common to several materials - object.render( function ( object ) { _this.renderBufferImmediate( object, program, material ); } ); + if ( fog && material.fog ) { - } + refreshUniformsFog( m_uniforms, fog ); - }; + } - function unrollImmediateBufferMaterial ( globject ) { + if ( material instanceof THREE.MeshPhongMaterial || + material instanceof THREE.MeshLambertMaterial || + material.lights ) { - var object = globject.object, - material = object.material; + if ( _lightsNeedUpdate ) { - if ( material.transparent ) { + refreshLights = true; + setupLights( lights ); + _lightsNeedUpdate = false; + } - globject.transparent = material; - globject.opaque = null; + if ( refreshLights ) { + refreshUniformsLights( m_uniforms, _lights ); + markUniformsLightsNeedsUpdate( m_uniforms, true ); + } else { + markUniformsLightsNeedsUpdate( m_uniforms, false ); + } - } else { + } - globject.opaque = material; - globject.transparent = null; + if ( material instanceof THREE.MeshBasicMaterial || + material instanceof THREE.MeshLambertMaterial || + material instanceof THREE.MeshPhongMaterial ) { - } + refreshUniformsCommon( m_uniforms, material ); - }; + } - function unrollBufferMaterial ( globject ) { + // refresh single material specific uniforms - var object = globject.object; - var buffer = globject.buffer; + if ( material instanceof THREE.LineBasicMaterial ) { - var geometry = object.geometry; - var material = object.material; + refreshUniformsLine( m_uniforms, material ); - if ( material instanceof THREE.MeshFaceMaterial ) { + } else if ( material instanceof THREE.LineDashedMaterial ) { - var materialIndex = geometry instanceof THREE.BufferGeometry ? 0 : buffer.materialIndex; + refreshUniformsLine( m_uniforms, material ); + refreshUniformsDash( m_uniforms, material ); - material = material.materials[ materialIndex ]; + } else if ( material instanceof THREE.PointCloudMaterial ) { - if ( material.transparent ) { + refreshUniformsParticle( m_uniforms, material ); - globject.material = material; - transparentObjects.push( globject ); + } else if ( material instanceof THREE.MeshPhongMaterial ) { - } else { + refreshUniformsPhong( m_uniforms, material ); - globject.material = material; - opaqueObjects.push( globject ); + } else if ( material instanceof THREE.MeshLambertMaterial ) { - } + refreshUniformsLambert( m_uniforms, material ); - } else { + } else if ( material instanceof THREE.MeshDepthMaterial ) { - if ( material ) { + m_uniforms.mNear.value = camera.near; + m_uniforms.mFar.value = camera.far; + m_uniforms.opacity.value = material.opacity; - if ( material.transparent ) { + } else if ( material instanceof THREE.MeshNormalMaterial ) { - globject.material = material; - transparentObjects.push( globject ); + m_uniforms.opacity.value = material.opacity; - } else { + } - globject.material = material; - opaqueObjects.push( globject ); + if ( object.receiveShadow && ! material._shadowPass ) { - } + refreshUniformsShadow( m_uniforms, lights ); } - } + // load common uniforms - }; + loadUniformsGeneric( material.uniformsList ); - // Objects refresh + } - var initObjects = function ( scene ) { + loadUniformsMatrices( p_uniforms, object ); - if ( ! scene.__webglObjects ) { + if ( p_uniforms.modelMatrix !== null ) { - scene.__webglObjects = {}; - scene.__webglObjectsImmediate = []; + _gl.uniformMatrix4fv( p_uniforms.modelMatrix, false, object.matrixWorld.elements ); } - while ( scene.__objectsAdded.length ) { + return program; - addObject( scene.__objectsAdded[ 0 ], scene ); - scene.__objectsAdded.splice( 0, 1 ); + } - } + // Uniforms (refresh uniforms objects) - while ( scene.__objectsRemoved.length ) { + function refreshUniformsCommon ( uniforms, material ) { - removeObject( scene.__objectsRemoved[ 0 ], scene ); - scene.__objectsRemoved.splice( 0, 1 ); + uniforms.opacity.value = material.opacity; - } + if ( _this.gammaInput ) { - }; + uniforms.diffuse.value.copyGammaToLinear( material.color ); - // Objects adding + } else { - function addObject( object, scene ) { + uniforms.diffuse.value = material.color; - var g, geometry, geometryGroup; + } - if ( object.__webglInit === undefined ) { + uniforms.map.value = material.map; + uniforms.lightMap.value = material.lightMap; + uniforms.specularMap.value = material.specularMap; + uniforms.alphaMap.value = material.alphaMap; - object.__webglInit = true; + if ( material.bumpMap ) { - object._modelViewMatrix = new THREE.Matrix4(); - object._normalMatrix = new THREE.Matrix3(); + uniforms.bumpMap.value = material.bumpMap; + uniforms.bumpScale.value = material.bumpScale; } - - geometry = object.geometry; - - if ( geometry === undefined ) { - // ImmediateRenderObject + if ( material.normalMap ) { - } else if ( geometry.__webglInit === undefined ) { + uniforms.normalMap.value = material.normalMap; + uniforms.normalScale.value.copy( material.normalScale ); - geometry.__webglInit = true; - geometry.addEventListener( 'dispose', onGeometryDispose ); + } - if ( geometry instanceof THREE.BufferGeometry ) { + // uv repeat and offset setting priorities + // 1. color map + // 2. specular map + // 3. normal map + // 4. bump map + // 5. alpha map - initDirectBuffers( geometry ); + var uvScaleMap; - } else if ( object instanceof THREE.Mesh ) { - - if ( object.__webglActive !== undefined ) { + if ( material.map ) { - removeObject( object, scene ); + uvScaleMap = material.map; - } - - initGeometryGroups(scene, object, geometry); + } else if ( material.specularMap ) { - } else if ( object instanceof THREE.Line ) { + uvScaleMap = material.specularMap; - if ( ! geometry.__webglVertexBuffer ) { + } else if ( material.normalMap ) { - createLineBuffers( geometry ); - initLineBuffers( geometry, object ); + uvScaleMap = material.normalMap; - geometry.verticesNeedUpdate = true; - geometry.colorsNeedUpdate = true; - geometry.lineDistancesNeedUpdate = true; + } else if ( material.bumpMap ) { - } + uvScaleMap = material.bumpMap; - } else if ( object instanceof THREE.PointCloud ) { + } else if ( material.alphaMap ) { - if ( ! geometry.__webglVertexBuffer ) { + uvScaleMap = material.alphaMap; - createParticleBuffers( geometry ); - initParticleBuffers( geometry, object ); + } - geometry.verticesNeedUpdate = true; - geometry.colorsNeedUpdate = true; + if ( uvScaleMap !== undefined ) { - } + var offset = uvScaleMap.offset; + var repeat = uvScaleMap.repeat; - } + uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y ); } - if ( object.__webglActive === undefined) { + uniforms.envMap.value = material.envMap; + uniforms.flipEnvMap.value = ( material.envMap instanceof THREE.WebGLRenderTargetCube ) ? 1 : - 1; - if ( object instanceof THREE.Mesh ) { + if ( _this.gammaInput ) { - geometry = object.geometry; + //uniforms.reflectivity.value = material.reflectivity * material.reflectivity; + uniforms.reflectivity.value = material.reflectivity; - if ( geometry instanceof THREE.BufferGeometry ) { + } else { - addBuffer( scene.__webglObjects, geometry, object ); + uniforms.reflectivity.value = material.reflectivity; - } else if ( geometry instanceof THREE.Geometry ) { + } - for ( var i = 0,l = geometry.geometryGroupsList.length; i= _maxTextures ) { - removeInstances( scene.__webglObjectsImmediate, object ); + console.warn( 'WebGLRenderer: trying to use ' + textureUnit + ' texture units while this GPU supports only ' + _maxTextures ); } - delete object.__webglActive; + _usedTextureUnits += 1; - }; - - + return textureUnit; - function removeInstancesWebglObjects( objlist, object ) { + } - delete objlist[ object.id ]; + function loadUniformsGeneric ( uniforms ) { - }; + var texture, textureUnit, offset; - function removeInstances( objlist, object ) { + for ( var j = 0, jl = uniforms.length; j < jl; j ++ ) { - for ( var o = objlist.length - 1; o >= 0; o -- ) { + var uniform = uniforms[ j ][ 0 ]; - if ( objlist[ o ].object === object ) { + // needsUpdate property is not added to all uniforms. + if ( uniform.needsUpdate === false ) continue; - objlist.splice( o, 1 ); + var type = uniform.type; + var value = uniform.value; + var location = uniforms[ j ][ 1 ]; - } + switch ( type ) { - } + case '1i': + _gl.uniform1i( location, value ); + break; - }; + case '1f': + _gl.uniform1f( location, value ); + break; - // Materials + case '2f': + _gl.uniform2f( location, value[ 0 ], value[ 1 ] ); + break; - this.initMaterial = function ( material, lights, fog, object ) { + case '3f': + _gl.uniform3f( location, value[ 0 ], value[ 1 ], value[ 2 ] ); + break; - material.addEventListener( 'dispose', onMaterialDispose ); + case '4f': + _gl.uniform4f( location, value[ 0 ], value[ 1 ], value[ 2 ], value[ 3 ] ); + break; - var u, a, identifiers, i, parameters, maxLightCount, maxBones, maxShadows, shaderID; + case '1iv': + _gl.uniform1iv( location, value ); + break; - if ( material instanceof THREE.MeshDepthMaterial ) { + case '3iv': + _gl.uniform3iv( location, value ); + break; - shaderID = 'depth'; + case '1fv': + _gl.uniform1fv( location, value ); + break; - } else if ( material instanceof THREE.MeshNormalMaterial ) { + case '2fv': + _gl.uniform2fv( location, value ); + break; - shaderID = 'normal'; + case '3fv': + _gl.uniform3fv( location, value ); + break; - } else if ( material instanceof THREE.MeshBasicMaterial ) { + case '4fv': + _gl.uniform4fv( location, value ); + break; - shaderID = 'basic'; + case 'Matrix3fv': + _gl.uniformMatrix3fv( location, false, value ); + break; - } else if ( material instanceof THREE.MeshLambertMaterial ) { + case 'Matrix4fv': + _gl.uniformMatrix4fv( location, false, value ); + break; - shaderID = 'lambert'; + // - } else if ( material instanceof THREE.MeshPhongMaterial ) { + case 'i': - shaderID = 'phong'; + // single integer + _gl.uniform1i( location, value ); - } else if ( material instanceof THREE.LineBasicMaterial ) { + break; - shaderID = 'basic'; + case 'f': - } else if ( material instanceof THREE.LineDashedMaterial ) { + // single float + _gl.uniform1f( location, value ); - shaderID = 'dashed'; + break; - } else if ( material instanceof THREE.PointCloudMaterial ) { + case 'v2': - shaderID = 'particle_basic'; + // single THREE.Vector2 + _gl.uniform2f( location, value.x, value.y ); - } + break; - if ( shaderID ) { + case 'v3': - var shader = THREE.ShaderLib[ shaderID ]; + // single THREE.Vector3 + _gl.uniform3f( location, value.x, value.y, value.z ); - material.__webglShader = { - uniforms: THREE.UniformsUtils.clone( shader.uniforms ), - vertexShader: shader.vertexShader, - fragmentShader: shader.fragmentShader - } + break; - } else { + case 'v4': - material.__webglShader = { - uniforms: material.uniforms, - vertexShader: material.vertexShader, - fragmentShader: material.fragmentShader - } + // single THREE.Vector4 + _gl.uniform4f( location, value.x, value.y, value.z, value.w ); - } + break; - // heuristics to create shader parameters according to lights in the scene - // (not to blow over maxLights budget) + case 'c': - maxLightCount = allocateLights( lights ); + // single THREE.Color + _gl.uniform3f( location, value.r, value.g, value.b ); - maxShadows = allocateShadows( lights ); + break; - maxBones = allocateBones( object ); + case 'iv1': - parameters = { + // flat array of integers (JS or typed array) + _gl.uniform1iv( location, value ); - precision: _precision, - supportsVertexTextures: _supportsVertexTextures, + break; - map: !! material.map, - envMap: !! material.envMap, - lightMap: !! material.lightMap, - bumpMap: !! material.bumpMap, - normalMap: !! material.normalMap, - specularMap: !! material.specularMap, - alphaMap: !! material.alphaMap, + case 'iv': - vertexColors: material.vertexColors, + // flat array of integers with 3 x N size (JS or typed array) + _gl.uniform3iv( location, value ); - fog: fog, - useFog: material.fog, - fogExp: fog instanceof THREE.FogExp2, + break; - sizeAttenuation: material.sizeAttenuation, - logarithmicDepthBuffer: _logarithmicDepthBuffer, + case 'fv1': - skinning: material.skinning, - maxBones: maxBones, - useVertexTexture: _supportsBoneTextures && object && object.skeleton && object.skeleton.useVertexTexture, + // flat array of floats (JS or typed array) + _gl.uniform1fv( location, value ); - morphTargets: material.morphTargets, - morphNormals: material.morphNormals, - maxMorphTargets: this.maxMorphTargets, - maxMorphNormals: this.maxMorphNormals, + break; - maxDirLights: maxLightCount.directional, - maxPointLights: maxLightCount.point, - maxSpotLights: maxLightCount.spot, - maxHemiLights: maxLightCount.hemi, + case 'fv': - maxShadows: maxShadows, - shadowMapEnabled: this.shadowMapEnabled && object.receiveShadow && maxShadows > 0, - shadowMapType: this.shadowMapType, - shadowMapDebug: this.shadowMapDebug, - shadowMapCascade: this.shadowMapCascade, + // flat array of floats with 3 x N size (JS or typed array) + _gl.uniform3fv( location, value ); - alphaTest: material.alphaTest, - metal: material.metal, - wrapAround: material.wrapAround, - doubleSided: material.side === THREE.DoubleSide, - flipSided: material.side === THREE.BackSide + break; - }; + case 'v2v': - // Generate code + // array of THREE.Vector2 - var chunks = []; + if ( uniform._array === undefined ) { - if ( shaderID ) { + uniform._array = new Float32Array( 2 * value.length ); - chunks.push( shaderID ); + } - } else { + for ( var i = 0, il = value.length; i < il; i ++ ) { - chunks.push( material.fragmentShader ); - chunks.push( material.vertexShader ); + offset = i * 2; - } + uniform._array[ offset ] = value[ i ].x; + uniform._array[ offset + 1 ] = value[ i ].y; - for ( var d in material.defines ) { + } - chunks.push( d ); - chunks.push( material.defines[ d ] ); + _gl.uniform2fv( location, uniform._array ); - } + break; - for ( var p in parameters ) { + case 'v3v': - chunks.push( p ); - chunks.push( parameters[ p ] ); + // array of THREE.Vector3 - } + if ( uniform._array === undefined ) { - var code = chunks.join(); + uniform._array = new Float32Array( 3 * value.length ); - var program; + } - // Check if code has been already compiled + for ( var i = 0, il = value.length; i < il; i ++ ) { - for ( var p = 0, pl = _programs.length; p < pl; p ++ ) { + offset = i * 3; - var programInfo = _programs[ p ]; + uniform._array[ offset ] = value[ i ].x; + uniform._array[ offset + 1 ] = value[ i ].y; + uniform._array[ offset + 2 ] = value[ i ].z; - if ( programInfo.code === code ) { + } - program = programInfo; - program.usedTimes ++; + _gl.uniform3fv( location, uniform._array ); - break; + break; - } + case 'v4v': - } + // array of THREE.Vector4 - if ( program === undefined ) { + if ( uniform._array === undefined ) { - program = new THREE.WebGLProgram( this, code, material, parameters ); - _programs.push( program ); + uniform._array = new Float32Array( 4 * value.length ); - _this.info.memory.programs = _programs.length; + } - } + for ( var i = 0, il = value.length; i < il; i ++ ) { - material.program = program; + offset = i * 4; - var attributes = material.program.attributes; + uniform._array[ offset ] = value[ i ].x; + uniform._array[ offset + 1 ] = value[ i ].y; + uniform._array[ offset + 2 ] = value[ i ].z; + uniform._array[ offset + 3 ] = value[ i ].w; - if ( material.morphTargets ) { + } - material.numSupportedMorphTargets = 0; + _gl.uniform4fv( location, uniform._array ); - var id, base = 'morphTarget'; + break; - for ( i = 0; i < this.maxMorphTargets; i ++ ) { + case 'm3': - id = base + i; + // single THREE.Matrix3 + _gl.uniformMatrix3fv( location, false, value.elements ); - if ( attributes[ id ] >= 0 ) { + break; - material.numSupportedMorphTargets ++; + case 'm3v': - } + // array of THREE.Matrix3 - } + if ( uniform._array === undefined ) { - } + uniform._array = new Float32Array( 9 * value.length ); - if ( material.morphNormals ) { + } - material.numSupportedMorphNormals = 0; + for ( var i = 0, il = value.length; i < il; i ++ ) { - var id, base = 'morphNormal'; + value[ i ].flattenToArrayOffset( uniform._array, i * 9 ); - for ( i = 0; i < this.maxMorphNormals; i ++ ) { + } - id = base + i; + _gl.uniformMatrix3fv( location, false, uniform._array ); - if ( attributes[ id ] >= 0 ) { + break; - material.numSupportedMorphNormals ++; + case 'm4': - } + // single THREE.Matrix4 + _gl.uniformMatrix4fv( location, false, value.elements ); - } + break; - } + case 'm4v': - material.uniformsList = []; + // array of THREE.Matrix4 - for ( u in material.__webglShader.uniforms ) { + if ( uniform._array === undefined ) { - var location = material.program.uniforms[ u ]; + uniform._array = new Float32Array( 16 * value.length ); - if ( location ) { - material.uniformsList.push( [ material.__webglShader.uniforms[ u ], location ] ); - } + } - } + for ( var i = 0, il = value.length; i < il; i ++ ) { - }; + value[ i ].flattenToArrayOffset( uniform._array, i * 16 ); - function setProgram( camera, lights, fog, material, object ) { + } - _usedTextureUnits = 0; + _gl.uniformMatrix4fv( location, false, uniform._array ); - if ( material.needsUpdate ) { + break; - if ( material.program ) deallocateMaterial( material ); + case 't': - _this.initMaterial( material, lights, fog, object ); - material.needsUpdate = false; + // single THREE.Texture (2d or cube) - } + texture = value; + textureUnit = getTextureUnit(); - if ( material.morphTargets ) { + _gl.uniform1i( location, textureUnit ); - if ( ! object.__webglMorphTargetInfluences ) { + if ( ! texture ) continue; - object.__webglMorphTargetInfluences = new Float32Array( _this.maxMorphTargets ); + if ( texture instanceof THREE.CubeTexture || + ( texture.image instanceof Array && texture.image.length === 6 ) ) { // CompressedTexture can have Array in image :/ - } + setCubeTexture( texture, textureUnit ); - } + } else if ( texture instanceof THREE.WebGLRenderTargetCube ) { - var refreshProgram = false; - var refreshMaterial = false; - var refreshLights = false; + setCubeTextureDynamic( texture, textureUnit ); - var program = material.program, - p_uniforms = program.uniforms, - m_uniforms = material.__webglShader.uniforms; + } else { - if ( program.id !== _currentProgram ) { + _this.setTexture( texture, textureUnit ); - _gl.useProgram( program.program ); - _currentProgram = program.id; + } - refreshProgram = true; - refreshMaterial = true; - refreshLights = true; + break; - } + case 'tv': - if ( material.id !== _currentMaterialId ) { + // array of THREE.Texture (2d) - if ( _currentMaterialId === -1 ) refreshLights = true; - _currentMaterialId = material.id; + if ( uniform._array === undefined ) { - refreshMaterial = true; + uniform._array = []; - } + } - if ( refreshProgram || camera !== _currentCamera ) { + for ( var i = 0, il = uniform.value.length; i < il; i ++ ) { - _gl.uniformMatrix4fv( p_uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); + uniform._array[ i ] = getTextureUnit(); - if ( _logarithmicDepthBuffer ) { + } - _gl.uniform1f( p_uniforms.logDepthBufFC, 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); + _gl.uniform1iv( location, uniform._array ); - } + for ( var i = 0, il = uniform.value.length; i < il; i ++ ) { + texture = uniform.value[ i ]; + textureUnit = uniform._array[ i ]; - if ( camera !== _currentCamera ) _currentCamera = camera; + if ( ! texture ) continue; - // load material specific uniforms - // (shader material also gets them for the sake of genericity) + _this.setTexture( texture, textureUnit ); - if ( material instanceof THREE.ShaderMaterial || - material instanceof THREE.MeshPhongMaterial || - material.envMap ) { + } - if ( p_uniforms.cameraPosition !== null ) { + break; - _vector3.setFromMatrixPosition( camera.matrixWorld ); - _gl.uniform3f( p_uniforms.cameraPosition, _vector3.x, _vector3.y, _vector3.z ); + default: - } + console.warn( 'THREE.WebGLRenderer: Unknown uniform type: ' + type ); } - if ( material instanceof THREE.MeshPhongMaterial || - material instanceof THREE.MeshLambertMaterial || - material instanceof THREE.ShaderMaterial || - material.skinning ) { + } - if ( p_uniforms.viewMatrix !== null ) { + } - _gl.uniformMatrix4fv( p_uniforms.viewMatrix, false, camera.matrixWorldInverse.elements ); + function setupMatrices ( object, camera ) { - } + object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + object._normalMatrix.getNormalMatrix( object._modelViewMatrix ); - } + } - } + // - // skinning uniforms must be set even if material didn't change - // auto-setting of texture unit for bone texture must go before other textures - // not sure why, but otherwise weird things happen + function setColorGamma( array, offset, color, intensitySq ) { - if ( material.skinning ) { + array[ offset ] = color.r * color.r * intensitySq; + array[ offset + 1 ] = color.g * color.g * intensitySq; + array[ offset + 2 ] = color.b * color.b * intensitySq; - if ( object.bindMatrix && p_uniforms.bindMatrix !== null ) { + } - _gl.uniformMatrix4fv( p_uniforms.bindMatrix, false, object.bindMatrix.elements ); + function setColorLinear( array, offset, color, intensity ) { - } + array[ offset ] = color.r * intensity; + array[ offset + 1 ] = color.g * intensity; + array[ offset + 2 ] = color.b * intensity; - if ( object.bindMatrixInverse && p_uniforms.bindMatrixInverse !== null ) { + } - _gl.uniformMatrix4fv( p_uniforms.bindMatrixInverse, false, object.bindMatrixInverse.elements ); + function setupLights ( lights ) { - } + var l, ll, light, n, + r = 0, g = 0, b = 0, + color, skyColor, groundColor, + intensity, intensitySq, + position, + distance, - if ( _supportsBoneTextures && object.skeleton && object.skeleton.useVertexTexture ) { + zlights = _lights, - if ( p_uniforms.boneTexture !== null ) { + dirColors = zlights.directional.colors, + dirPositions = zlights.directional.positions, - var textureUnit = getTextureUnit(); + pointColors = zlights.point.colors, + pointPositions = zlights.point.positions, + pointDistances = zlights.point.distances, - _gl.uniform1i( p_uniforms.boneTexture, textureUnit ); - _this.setTexture( object.skeleton.boneTexture, textureUnit ); + spotColors = zlights.spot.colors, + spotPositions = zlights.spot.positions, + spotDistances = zlights.spot.distances, + spotDirections = zlights.spot.directions, + spotAnglesCos = zlights.spot.anglesCos, + spotExponents = zlights.spot.exponents, - } + hemiSkyColors = zlights.hemi.skyColors, + hemiGroundColors = zlights.hemi.groundColors, + hemiPositions = zlights.hemi.positions, - if ( p_uniforms.boneTextureWidth !== null ) { + dirLength = 0, + pointLength = 0, + spotLength = 0, + hemiLength = 0, - _gl.uniform1i( p_uniforms.boneTextureWidth, object.skeleton.boneTextureWidth ); + dirCount = 0, + pointCount = 0, + spotCount = 0, + hemiCount = 0, - } + dirOffset = 0, + pointOffset = 0, + spotOffset = 0, + hemiOffset = 0; - if ( p_uniforms.boneTextureHeight !== null ) { + for ( l = 0, ll = lights.length; l < ll; l ++ ) { - _gl.uniform1i( p_uniforms.boneTextureHeight, object.skeleton.boneTextureHeight ); + light = lights[ l ]; - } + if ( light.onlyShadow ) continue; - } else if ( object.skeleton && object.skeleton.boneMatrices ) { + color = light.color; + intensity = light.intensity; + distance = light.distance; - if ( p_uniforms.boneGlobalMatrices !== null ) { + if ( light instanceof THREE.AmbientLight ) { - _gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, object.skeleton.boneMatrices ); + if ( ! light.visible ) continue; - } + if ( _this.gammaInput ) { - } + r += color.r * color.r; + g += color.g * color.g; + b += color.b * color.b; - } + } else { - if ( refreshMaterial ) { + r += color.r; + g += color.g; + b += color.b; - // refresh uniforms common to several materials + } - if ( fog && material.fog ) { + } else if ( light instanceof THREE.DirectionalLight ) { - refreshUniformsFog( m_uniforms, fog ); + dirCount += 1; - } + if ( ! light.visible ) continue; - if ( material instanceof THREE.MeshPhongMaterial || - material instanceof THREE.MeshLambertMaterial || - material.lights ) { + _direction.setFromMatrixPosition( light.matrixWorld ); + _vector3.setFromMatrixPosition( light.target.matrixWorld ); + _direction.sub( _vector3 ); + _direction.normalize(); - if ( _lightsNeedUpdate ) { + dirOffset = dirLength * 3; - refreshLights = true; - setupLights( lights ); - _lightsNeedUpdate = false; - } + dirPositions[ dirOffset ] = _direction.x; + dirPositions[ dirOffset + 1 ] = _direction.y; + dirPositions[ dirOffset + 2 ] = _direction.z; + + if ( _this.gammaInput ) { + + setColorGamma( dirColors, dirOffset, color, intensity * intensity ); - if ( refreshLights ) { - refreshUniformsLights( m_uniforms, _lights ); - markUniformsLightsNeedsUpdate( m_uniforms, true ); } else { - markUniformsLightsNeedsUpdate( m_uniforms, false ); - } - } + setColorLinear( dirColors, dirOffset, color, intensity ); - if ( material instanceof THREE.MeshBasicMaterial || - material instanceof THREE.MeshLambertMaterial || - material instanceof THREE.MeshPhongMaterial ) { + } - refreshUniformsCommon( m_uniforms, material ); + dirLength += 1; - } + } else if ( light instanceof THREE.PointLight ) { - // refresh single material specific uniforms + pointCount += 1; - if ( material instanceof THREE.LineBasicMaterial ) { + if ( ! light.visible ) continue; - refreshUniformsLine( m_uniforms, material ); + pointOffset = pointLength * 3; - } else if ( material instanceof THREE.LineDashedMaterial ) { + if ( _this.gammaInput ) { - refreshUniformsLine( m_uniforms, material ); - refreshUniformsDash( m_uniforms, material ); + setColorGamma( pointColors, pointOffset, color, intensity * intensity ); - } else if ( material instanceof THREE.PointCloudMaterial ) { + } else { - refreshUniformsParticle( m_uniforms, material ); + setColorLinear( pointColors, pointOffset, color, intensity ); - } else if ( material instanceof THREE.MeshPhongMaterial ) { + } - refreshUniformsPhong( m_uniforms, material ); + _vector3.setFromMatrixPosition( light.matrixWorld ); - } else if ( material instanceof THREE.MeshLambertMaterial ) { + pointPositions[ pointOffset ] = _vector3.x; + pointPositions[ pointOffset + 1 ] = _vector3.y; + pointPositions[ pointOffset + 2 ] = _vector3.z; - refreshUniformsLambert( m_uniforms, material ); + pointDistances[ pointLength ] = distance; - } else if ( material instanceof THREE.MeshDepthMaterial ) { + pointLength += 1; - m_uniforms.mNear.value = camera.near; - m_uniforms.mFar.value = camera.far; - m_uniforms.opacity.value = material.opacity; + } else if ( light instanceof THREE.SpotLight ) { - } else if ( material instanceof THREE.MeshNormalMaterial ) { + spotCount += 1; - m_uniforms.opacity.value = material.opacity; + if ( ! light.visible ) continue; - } + spotOffset = spotLength * 3; - if ( object.receiveShadow && ! material._shadowPass ) { + if ( _this.gammaInput ) { - refreshUniformsShadow( m_uniforms, lights ); + setColorGamma( spotColors, spotOffset, color, intensity * intensity ); - } + } else { - // load common uniforms + setColorLinear( spotColors, spotOffset, color, intensity ); - loadUniformsGeneric( material.uniformsList ); + } - } + _direction.setFromMatrixPosition( light.matrixWorld ); - loadUniformsMatrices( p_uniforms, object ); + spotPositions[ spotOffset ] = _direction.x; + spotPositions[ spotOffset + 1 ] = _direction.y; + spotPositions[ spotOffset + 2 ] = _direction.z; - if ( p_uniforms.modelMatrix !== null ) { + spotDistances[ spotLength ] = distance; - _gl.uniformMatrix4fv( p_uniforms.modelMatrix, false, object.matrixWorld.elements ); + _vector3.setFromMatrixPosition( light.target.matrixWorld ); + _direction.sub( _vector3 ); + _direction.normalize(); - } + spotDirections[ spotOffset ] = _direction.x; + spotDirections[ spotOffset + 1 ] = _direction.y; + spotDirections[ spotOffset + 2 ] = _direction.z; - return program; + spotAnglesCos[ spotLength ] = Math.cos( light.angle ); + spotExponents[ spotLength ] = light.exponent; - }; + spotLength += 1; - // Uniforms (refresh uniforms objects) + } else if ( light instanceof THREE.HemisphereLight ) { - function refreshUniformsCommon ( uniforms, material ) { + hemiCount += 1; - uniforms.opacity.value = material.opacity; + if ( ! light.visible ) continue; - if ( _this.gammaInput ) { + _direction.setFromMatrixPosition( light.matrixWorld ); + _direction.normalize(); - uniforms.diffuse.value.copyGammaToLinear( material.color ); + hemiOffset = hemiLength * 3; - } else { + hemiPositions[ hemiOffset ] = _direction.x; + hemiPositions[ hemiOffset + 1 ] = _direction.y; + hemiPositions[ hemiOffset + 2 ] = _direction.z; - uniforms.diffuse.value = material.color; + skyColor = light.color; + groundColor = light.groundColor; - } + if ( _this.gammaInput ) { - uniforms.map.value = material.map; - uniforms.lightMap.value = material.lightMap; - uniforms.specularMap.value = material.specularMap; - uniforms.alphaMap.value = material.alphaMap; + intensitySq = intensity * intensity; - if ( material.bumpMap ) { + setColorGamma( hemiSkyColors, hemiOffset, skyColor, intensitySq ); + setColorGamma( hemiGroundColors, hemiOffset, groundColor, intensitySq ); - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; + } else { - } + setColorLinear( hemiSkyColors, hemiOffset, skyColor, intensity ); + setColorLinear( hemiGroundColors, hemiOffset, groundColor, intensity ); - if ( material.normalMap ) { + } - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); + hemiLength += 1; + + } } - // uv repeat and offset setting priorities - // 1. color map - // 2. specular map - // 3. normal map - // 4. bump map - // 5. alpha map + // null eventual remains from removed lights + // (this is to avoid if in shader) - var uvScaleMap; + for ( l = dirLength * 3, ll = Math.max( dirColors.length, dirCount * 3 ); l < ll; l ++ ) dirColors[ l ] = 0.0; + for ( l = pointLength * 3, ll = Math.max( pointColors.length, pointCount * 3 ); l < ll; l ++ ) pointColors[ l ] = 0.0; + for ( l = spotLength * 3, ll = Math.max( spotColors.length, spotCount * 3 ); l < ll; l ++ ) spotColors[ l ] = 0.0; + for ( l = hemiLength * 3, ll = Math.max( hemiSkyColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiSkyColors[ l ] = 0.0; + for ( l = hemiLength * 3, ll = Math.max( hemiGroundColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiGroundColors[ l ] = 0.0; - if ( material.map ) { + zlights.directional.length = dirLength; + zlights.point.length = pointLength; + zlights.spot.length = spotLength; + zlights.hemi.length = hemiLength; - uvScaleMap = material.map; + zlights.ambient[ 0 ] = r; + zlights.ambient[ 1 ] = g; + zlights.ambient[ 2 ] = b; - } else if ( material.specularMap ) { + } - uvScaleMap = material.specularMap; + // GL state setting - } else if ( material.normalMap ) { + this.setFaceCulling = function ( cullFace, frontFaceDirection ) { - uvScaleMap = material.normalMap; + if ( cullFace === THREE.CullFaceNone ) { - } else if ( material.bumpMap ) { + _gl.disable( _gl.CULL_FACE ); - uvScaleMap = material.bumpMap; + } else { - } else if ( material.alphaMap ) { + if ( frontFaceDirection === THREE.FrontFaceDirectionCW ) { - uvScaleMap = material.alphaMap; + _gl.frontFace( _gl.CW ); - } + } else { - if ( uvScaleMap !== undefined ) { + _gl.frontFace( _gl.CCW ); - var offset = uvScaleMap.offset; - var repeat = uvScaleMap.repeat; + } - uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y ); + if ( cullFace === THREE.CullFaceBack ) { - } + _gl.cullFace( _gl.BACK ); - uniforms.envMap.value = material.envMap; - uniforms.flipEnvMap.value = ( material.envMap instanceof THREE.WebGLRenderTargetCube ) ? 1 : - 1; + } else if ( cullFace === THREE.CullFaceFront ) { - if ( _this.gammaInput ) { + _gl.cullFace( _gl.FRONT ); - //uniforms.reflectivity.value = material.reflectivity * material.reflectivity; - uniforms.reflectivity.value = material.reflectivity; + } else { - } else { + _gl.cullFace( _gl.FRONT_AND_BACK ); - uniforms.reflectivity.value = material.reflectivity; + } - } + _gl.enable( _gl.CULL_FACE ); - uniforms.refractionRatio.value = material.refractionRatio; - uniforms.combine.value = material.combine; - uniforms.useRefract.value = material.envMap && material.envMap.mapping instanceof THREE.CubeRefractionMapping; + } }; - function refreshUniformsLine ( uniforms, material ) { + this.setMaterialFaces = function ( material ) { - uniforms.diffuse.value = material.color; - uniforms.opacity.value = material.opacity; + var doubleSided = material.side === THREE.DoubleSide; + var flipSided = material.side === THREE.BackSide; - }; + if ( _oldDoubleSided !== doubleSided ) { - function refreshUniformsDash ( uniforms, material ) { + if ( doubleSided ) { - uniforms.dashSize.value = material.dashSize; - uniforms.totalSize.value = material.dashSize + material.gapSize; - uniforms.scale.value = material.scale; + _gl.disable( _gl.CULL_FACE ); - }; + } else { - function refreshUniformsParticle ( uniforms, material ) { + _gl.enable( _gl.CULL_FACE ); - uniforms.psColor.value = material.color; - uniforms.opacity.value = material.opacity; - uniforms.size.value = material.size; - uniforms.scale.value = _canvas.height / 2.0; // TODO: Cache this. + } - uniforms.map.value = material.map; + _oldDoubleSided = doubleSided; - }; + } - function refreshUniformsFog ( uniforms, fog ) { + if ( _oldFlipSided !== flipSided ) { - uniforms.fogColor.value = fog.color; + if ( flipSided ) { - if ( fog instanceof THREE.Fog ) { + _gl.frontFace( _gl.CW ); - uniforms.fogNear.value = fog.near; - uniforms.fogFar.value = fog.far; + } else { - } else if ( fog instanceof THREE.FogExp2 ) { + _gl.frontFace( _gl.CCW ); - uniforms.fogDensity.value = fog.density; + } + + _oldFlipSided = flipSided; } }; - function refreshUniformsPhong ( uniforms, material ) { - - uniforms.shininess.value = material.shininess; + this.setDepthTest = function ( depthTest ) { - if ( _this.gammaInput ) { + if ( _oldDepthTest !== depthTest ) { - uniforms.ambient.value.copyGammaToLinear( material.ambient ); - uniforms.emissive.value.copyGammaToLinear( material.emissive ); - uniforms.specular.value.copyGammaToLinear( material.specular ); + if ( depthTest ) { - } else { + _gl.enable( _gl.DEPTH_TEST ); - uniforms.ambient.value = material.ambient; - uniforms.emissive.value = material.emissive; - uniforms.specular.value = material.specular; + } else { - } + _gl.disable( _gl.DEPTH_TEST ); - if ( material.wrapAround ) { + } - uniforms.wrapRGB.value.copy( material.wrapRGB ); + _oldDepthTest = depthTest; } }; - function refreshUniformsLambert ( uniforms, material ) { - - if ( _this.gammaInput ) { - - uniforms.ambient.value.copyGammaToLinear( material.ambient ); - uniforms.emissive.value.copyGammaToLinear( material.emissive ); + this.setDepthWrite = function ( depthWrite ) { - } else { + if ( _oldDepthWrite !== depthWrite ) { - uniforms.ambient.value = material.ambient; - uniforms.emissive.value = material.emissive; + _gl.depthMask( depthWrite ); + _oldDepthWrite = depthWrite; } - if ( material.wrapAround ) { + }; - uniforms.wrapRGB.value.copy( material.wrapRGB ); + function setLineWidth ( width ) { - } + if ( width !== _oldLineWidth ) { - }; + _gl.lineWidth( width ); - function refreshUniformsLights ( uniforms, lights ) { + _oldLineWidth = width; - uniforms.ambientLightColor.value = lights.ambient; + } - uniforms.directionalLightColor.value = lights.directional.colors; - uniforms.directionalLightDirection.value = lights.directional.positions; + } - uniforms.pointLightColor.value = lights.point.colors; - uniforms.pointLightPosition.value = lights.point.positions; - uniforms.pointLightDistance.value = lights.point.distances; + function setPolygonOffset ( polygonoffset, factor, units ) { - uniforms.spotLightColor.value = lights.spot.colors; - uniforms.spotLightPosition.value = lights.spot.positions; - uniforms.spotLightDistance.value = lights.spot.distances; - uniforms.spotLightDirection.value = lights.spot.directions; - uniforms.spotLightAngleCos.value = lights.spot.anglesCos; - uniforms.spotLightExponent.value = lights.spot.exponents; + if ( _oldPolygonOffset !== polygonoffset ) { - uniforms.hemisphereLightSkyColor.value = lights.hemi.skyColors; - uniforms.hemisphereLightGroundColor.value = lights.hemi.groundColors; - uniforms.hemisphereLightDirection.value = lights.hemi.positions; + if ( polygonoffset ) { - }; + _gl.enable( _gl.POLYGON_OFFSET_FILL ); - // If uniforms are marked as clean, they don't need to be loaded to the GPU. + } else { - function markUniformsLightsNeedsUpdate ( uniforms, boolean ) { + _gl.disable( _gl.POLYGON_OFFSET_FILL ); - uniforms.ambientLightColor.needsUpdate = boolean; + } - uniforms.directionalLightColor.needsUpdate = boolean; - uniforms.directionalLightDirection.needsUpdate = boolean; + _oldPolygonOffset = polygonoffset; - uniforms.pointLightColor.needsUpdate = boolean; - uniforms.pointLightPosition.needsUpdate = boolean; - uniforms.pointLightDistance.needsUpdate = boolean; + } - uniforms.spotLightColor.needsUpdate = boolean; - uniforms.spotLightPosition.needsUpdate = boolean; - uniforms.spotLightDistance.needsUpdate = boolean; - uniforms.spotLightDirection.needsUpdate = boolean; - uniforms.spotLightAngleCos.needsUpdate = boolean; - uniforms.spotLightExponent.needsUpdate = boolean; + if ( polygonoffset && ( _oldPolygonOffsetFactor !== factor || _oldPolygonOffsetUnits !== units ) ) { - uniforms.hemisphereLightSkyColor.needsUpdate = boolean; - uniforms.hemisphereLightGroundColor.needsUpdate = boolean; - uniforms.hemisphereLightDirection.needsUpdate = boolean; + _gl.polygonOffset( factor, units ); - }; + _oldPolygonOffsetFactor = factor; + _oldPolygonOffsetUnits = units; - function refreshUniformsShadow ( uniforms, lights ) { + } - if ( uniforms.shadowMatrix ) { + } - var j = 0; + this.setBlending = function ( blending, blendEquation, blendSrc, blendDst ) { - for ( var i = 0, il = lights.length; i < il; i ++ ) { + if ( blending !== _oldBlending ) { - var light = lights[ i ]; + if ( blending === THREE.NoBlending ) { - if ( ! light.castShadow ) continue; + _gl.disable( _gl.BLEND ); - if ( light instanceof THREE.SpotLight || ( light instanceof THREE.DirectionalLight && ! light.shadowCascade ) ) { + } else if ( blending === THREE.AdditiveBlending ) { - uniforms.shadowMap.value[ j ] = light.shadowMap; - uniforms.shadowMapSize.value[ j ] = light.shadowMapSize; + _gl.enable( _gl.BLEND ); + _gl.blendEquation( _gl.FUNC_ADD ); + _gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE ); - uniforms.shadowMatrix.value[ j ] = light.shadowMatrix; + } else if ( blending === THREE.SubtractiveBlending ) { - uniforms.shadowDarkness.value[ j ] = light.shadowDarkness; - uniforms.shadowBias.value[ j ] = light.shadowBias; + // TODO: Find blendFuncSeparate() combination + _gl.enable( _gl.BLEND ); + _gl.blendEquation( _gl.FUNC_ADD ); + _gl.blendFunc( _gl.ZERO, _gl.ONE_MINUS_SRC_COLOR ); - j ++; + } else if ( blending === THREE.MultiplyBlending ) { - } + // TODO: Find blendFuncSeparate() combination + _gl.enable( _gl.BLEND ); + _gl.blendEquation( _gl.FUNC_ADD ); + _gl.blendFunc( _gl.ZERO, _gl.SRC_COLOR ); - } + } else if ( blending === THREE.CustomBlending ) { - } + _gl.enable( _gl.BLEND ); - }; + } else { - // Uniforms (load to GPU) + _gl.enable( _gl.BLEND ); + _gl.blendEquationSeparate( _gl.FUNC_ADD, _gl.FUNC_ADD ); + _gl.blendFuncSeparate( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA, _gl.ONE, _gl.ONE_MINUS_SRC_ALPHA ); - function loadUniformsMatrices ( uniforms, object ) { + } - _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, object._modelViewMatrix.elements ); + _oldBlending = blending; - if ( uniforms.normalMatrix ) { + } - _gl.uniformMatrix3fv( uniforms.normalMatrix, false, object._normalMatrix.elements ); + if ( blending === THREE.CustomBlending ) { - } + if ( blendEquation !== _oldBlendEquation ) { - }; + _gl.blendEquation( paramThreeToGL( blendEquation ) ); - function getTextureUnit() { + _oldBlendEquation = blendEquation; - var textureUnit = _usedTextureUnits; + } - if ( textureUnit >= _maxTextures ) { + if ( blendSrc !== _oldBlendSrc || blendDst !== _oldBlendDst ) { - console.warn( 'WebGLRenderer: trying to use ' + textureUnit + ' texture units while this GPU supports only ' + _maxTextures ); + _gl.blendFunc( paramThreeToGL( blendSrc ), paramThreeToGL( blendDst ) ); - } + _oldBlendSrc = blendSrc; + _oldBlendDst = blendDst; - _usedTextureUnits += 1; + } - return textureUnit; + } else { + + _oldBlendEquation = null; + _oldBlendSrc = null; + _oldBlendDst = null; + + } }; - function loadUniformsGeneric ( uniforms ) { + // Textures - var texture, textureUnit, offset; + function setTextureParameters ( textureType, texture, isImagePowerOfTwo ) { - for ( var j = 0, jl = uniforms.length; j < jl; j ++ ) { + var extension; - var uniform = uniforms[ j ][ 0 ]; + if ( isImagePowerOfTwo ) { - // needsUpdate property is not added to all uniforms. - if ( uniform.needsUpdate === false ) continue; + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, paramThreeToGL( texture.wrapS ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, paramThreeToGL( texture.wrapT ) ); - var type = uniform.type; - var value = uniform.value; - var location = uniforms[ j ][ 1 ]; + _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, paramThreeToGL( texture.magFilter ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, paramThreeToGL( texture.minFilter ) ); - switch ( type ) { + } else { - case '1i': - _gl.uniform1i( location, value ); - break; + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); - case '1f': - _gl.uniform1f( location, value ); - break; + _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) ); - case '2f': - _gl.uniform2f( location, value[ 0 ], value[ 1 ] ); - break; + } - case '3f': - _gl.uniform3f( location, value[ 0 ], value[ 1 ], value[ 2 ] ); - break; + extension = extensions.get( 'EXT_texture_filter_anisotropic' ); - case '4f': - _gl.uniform4f( location, value[ 0 ], value[ 1 ], value[ 2 ], value[ 3 ] ); - break; + if ( extension && texture.type !== THREE.FloatType ) { - case '1iv': - _gl.uniform1iv( location, value ); - break; + if ( texture.anisotropy > 1 || texture.__oldAnisotropy ) { - case '3iv': - _gl.uniform3iv( location, value ); - break; + _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, _this.getMaxAnisotropy() ) ); + texture.__oldAnisotropy = texture.anisotropy; - case '1fv': - _gl.uniform1fv( location, value ); - break; + } - case '2fv': - _gl.uniform2fv( location, value ); - break; + } - case '3fv': - _gl.uniform3fv( location, value ); - break; + } - case '4fv': - _gl.uniform4fv( location, value ); - break; + this.uploadTexture = function ( texture ) { - case 'Matrix3fv': - _gl.uniformMatrix3fv( location, false, value ); - break; + if ( texture.__webglInit === undefined ) { - case 'Matrix4fv': - _gl.uniformMatrix4fv( location, false, value ); - break; + texture.__webglInit = true; - // + texture.addEventListener( 'dispose', onTextureDispose ); - case 'i': + texture.__webglTexture = _gl.createTexture(); - // single integer - _gl.uniform1i( location, value ); + _this.info.memory.textures ++; - break; + } - case 'f': + _gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture ); - // single float - _gl.uniform1f( location, value ); + _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 ); - break; + texture.image = clampToMaxSize( texture.image, _maxTextureSize ); - case 'v2': + var image = texture.image, + isImagePowerOfTwo = THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height ), + glFormat = paramThreeToGL( texture.format ), + glType = paramThreeToGL( texture.type ); - // single THREE.Vector2 - _gl.uniform2f( location, value.x, value.y ); + setTextureParameters( _gl.TEXTURE_2D, texture, isImagePowerOfTwo ); - break; + var mipmap, mipmaps = texture.mipmaps; - case 'v3': + if ( texture instanceof THREE.DataTexture ) { - // single THREE.Vector3 - _gl.uniform3f( location, value.x, value.y, value.z ); + // 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 - break; + if ( mipmaps.length > 0 && isImagePowerOfTwo ) { - case 'v4': + for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { - // single THREE.Vector4 - _gl.uniform4f( location, value.x, value.y, value.z, value.w ); + mipmap = mipmaps[ i ]; + _gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - break; + } - case 'c': + texture.generateMipmaps = false; - // single THREE.Color - _gl.uniform3f( location, value.r, value.g, value.b ); + } else { - break; + _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data ); - case 'iv1': + } - // flat array of integers (JS or typed array) - _gl.uniform1iv( location, value ); + } else if ( texture instanceof THREE.CompressedTexture ) { - break; + for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { - case 'iv': + mipmap = mipmaps[ i ]; - // flat array of integers with 3 x N size (JS or typed array) - _gl.uniform3iv( location, value ); + if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) { - break; + if ( getCompressedTextureFormats().indexOf( glFormat ) > -1 ) { - case 'fv1': + _gl.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); - // flat array of floats (JS or typed array) - _gl.uniform1fv( location, value ); + } else { - break; + console.warn( "Attempt to load unsupported compressed texture format" ); - case 'fv': + } - // flat array of floats with 3 x N size (JS or typed array) - _gl.uniform3fv( location, value ); + } else { - break; + _gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - case 'v2v': + } - // array of THREE.Vector2 + } - if ( uniform._array === undefined ) { + } else { // regular Texture (image, video, canvas) - uniform._array = new Float32Array( 2 * value.length ); + // 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 ) { - for ( var i = 0, il = value.length; i < il; i ++ ) { + for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { - offset = i * 2; + mipmap = mipmaps[ i ]; + _gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap ); - uniform._array[ offset ] = value[ i ].x; - uniform._array[ offset + 1 ] = value[ i ].y; + } - } + texture.generateMipmaps = false; - _gl.uniform2fv( location, uniform._array ); + } else { - break; + _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, texture.image ); - case 'v3v': + } - // array of THREE.Vector3 + } - if ( uniform._array === undefined ) { + if ( texture.generateMipmaps && isImagePowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); - uniform._array = new Float32Array( 3 * value.length ); + texture.needsUpdate = false; - } + if ( texture.onUpdate ) texture.onUpdate(); - for ( var i = 0, il = value.length; i < il; i ++ ) { + }; - offset = i * 3; + this.setTexture = function ( texture, slot ) { - uniform._array[ offset ] = value[ i ].x; - uniform._array[ offset + 1 ] = value[ i ].y; - uniform._array[ offset + 2 ] = value[ i ].z; + _gl.activeTexture( _gl.TEXTURE0 + slot ); - } + if ( texture.needsUpdate ) { - _gl.uniform3fv( location, uniform._array ); + _this.uploadTexture( texture ); - break; + } else { - case 'v4v': + _gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture ); - // array of THREE.Vector4 + } - if ( uniform._array === undefined ) { + }; - uniform._array = new Float32Array( 4 * value.length ); + function clampToMaxSize ( image, maxSize ) { - } + if ( image.width > maxSize || image.height > maxSize ) { - for ( var i = 0, il = value.length; i < il; i ++ ) { + // Warning: Scaling through the canvas will only work with images that use + // premultiplied alpha. - offset = i * 4; + var scale = maxSize / Math.max( image.width, image.height ); - uniform._array[ offset ] = value[ i ].x; - uniform._array[ offset + 1 ] = value[ i ].y; - uniform._array[ offset + 2 ] = value[ i ].z; - uniform._array[ offset + 3 ] = value[ i ].w; + var canvas = document.createElement( 'canvas' ); + canvas.width = Math.floor( image.width * scale ); + canvas.height = Math.floor( image.height * scale ); - } + var context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height ); - _gl.uniform4fv( location, uniform._array ); + console.log( 'THREE.WebGLRenderer:', image, 'is too big (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height + '.' ); - break; + return canvas; - case 'm3': + } - // single THREE.Matrix3 - _gl.uniformMatrix3fv( location, false, value.elements ); + return image; - break; + } - case 'm3v': + function setCubeTexture ( texture, slot ) { - // array of THREE.Matrix3 + if ( texture.image.length === 6 ) { - if ( uniform._array === undefined ) { + if ( texture.needsUpdate ) { - uniform._array = new Float32Array( 9 * value.length ); + if ( ! texture.image.__webglTextureCube ) { - } + texture.addEventListener( 'dispose', onTextureDispose ); - for ( var i = 0, il = value.length; i < il; i ++ ) { + texture.image.__webglTextureCube = _gl.createTexture(); - value[ i ].flattenToArrayOffset( uniform._array, i * 9 ); + _this.info.memory.textures ++; - } + } - _gl.uniformMatrix3fv( location, false, uniform._array ); + _gl.activeTexture( _gl.TEXTURE0 + slot ); + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube ); - break; + _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); - case 'm4': + var isCompressed = texture instanceof THREE.CompressedTexture; + var isDataTexture = texture.image[ 0 ] instanceof THREE.DataTexture; - // single THREE.Matrix4 - _gl.uniformMatrix4fv( location, false, value.elements ); + var cubeImage = []; - break; + for ( var i = 0; i < 6; i ++ ) { - case 'm4v': + if ( _this.autoScaleCubemaps && ! isCompressed && ! isDataTexture ) { - // array of THREE.Matrix4 + cubeImage[ i ] = clampToMaxSize( texture.image[ i ], _maxCubemapSize ); - if ( uniform._array === undefined ) { + } else { - uniform._array = new Float32Array( 16 * value.length ); + cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; } - for ( var i = 0, il = value.length; i < il; i ++ ) { - - value[ i ].flattenToArrayOffset( uniform._array, i * 16 ); + } - } + var image = cubeImage[ 0 ], + isImagePowerOfTwo = THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height ), + glFormat = paramThreeToGL( texture.format ), + glType = paramThreeToGL( texture.type ); - _gl.uniformMatrix4fv( location, false, uniform._array ); + setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isImagePowerOfTwo ); - break; + for ( var i = 0; i < 6; i ++ ) { - case 't': + if ( ! isCompressed ) { - // single THREE.Texture (2d or cube) + if ( isDataTexture ) { - texture = value; - textureUnit = getTextureUnit(); + _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); - _gl.uniform1i( location, textureUnit ); + } else { - if ( ! texture ) continue; + _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] ); - if ( texture instanceof THREE.CubeTexture || - ( texture.image instanceof Array && texture.image.length === 6 ) ) { // CompressedTexture can have Array in image :/ + } - setCubeTexture( texture, textureUnit ); + } else { - } else if ( texture instanceof THREE.WebGLRenderTargetCube ) { + var mipmap, mipmaps = cubeImage[ i ].mipmaps; - setCubeTextureDynamic( texture, textureUnit ); + for ( var j = 0, jl = mipmaps.length; j < jl; j ++ ) { - } else { + mipmap = mipmaps[ j ]; - _this.setTexture( texture, textureUnit ); + if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) { - } + if ( getCompressedTextureFormats().indexOf( glFormat ) > -1 ) { - break; + _gl.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); - case 'tv': + } else { - // array of THREE.Texture (2d) + console.warn( "Attempt to load unsupported compressed texture format" ); - if ( uniform._array === undefined ) { + } - uniform._array = []; + } else { - } + _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - for ( var i = 0, il = uniform.value.length; i < il; i ++ ) { + } - uniform._array[ i ] = getTextureUnit(); + } } - _gl.uniform1iv( location, uniform._array ); - - for ( var i = 0, il = uniform.value.length; i < il; i ++ ) { + } - texture = uniform.value[ i ]; - textureUnit = uniform._array[ i ]; + if ( texture.generateMipmaps && isImagePowerOfTwo ) { - if ( ! texture ) continue; + _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); - _this.setTexture( texture, textureUnit ); + } - } + texture.needsUpdate = false; - break; + if ( texture.onUpdate ) texture.onUpdate(); - default: + } else { - console.warn( 'THREE.WebGLRenderer: Unknown uniform type: ' + type ); + _gl.activeTexture( _gl.TEXTURE0 + slot ); + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube ); } } - }; + } - function setupMatrices ( object, camera ) { + function setCubeTextureDynamic ( texture, slot ) { - object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); - object._normalMatrix.getNormalMatrix( object._modelViewMatrix ); + _gl.activeTexture( _gl.TEXTURE0 + slot ); + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.__webglTexture ); - }; + } - // + // Render targets - function setColorGamma( array, offset, color, intensitySq ) { + function setupFrameBuffer ( framebuffer, renderTarget, textureTarget ) { - array[ offset ] = color.r * color.r * intensitySq; - array[ offset + 1 ] = color.g * color.g * intensitySq; - array[ offset + 2 ] = color.b * color.b * intensitySq; + _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureTarget, renderTarget.__webglTexture, 0 ); - }; + } - function setColorLinear( array, offset, color, intensity ) { + function setupRenderBuffer ( renderbuffer, renderTarget ) { - array[ offset ] = color.r * intensity; - array[ offset + 1 ] = color.g * intensity; - array[ offset + 2 ] = color.b * intensity; + _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer ); - }; + if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { - function setupLights ( lights ) { + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); - var l, ll, light, n, - r = 0, g = 0, b = 0, - color, skyColor, groundColor, - intensity, intensitySq, - position, - distance, + /* For some reason this is not working. Defaulting to RGBA4. + } else if ( ! renderTarget.depthBuffer && renderTarget.stencilBuffer ) { - zlights = _lights, + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.STENCIL_INDEX8, renderTarget.width, renderTarget.height ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); + */ + } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { - dirColors = zlights.directional.colors, - dirPositions = zlights.directional.positions, + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); - pointColors = zlights.point.colors, - pointPositions = zlights.point.positions, - pointDistances = zlights.point.distances, + } else { - spotColors = zlights.spot.colors, - spotPositions = zlights.spot.positions, - spotDistances = zlights.spot.distances, - spotDirections = zlights.spot.directions, - spotAnglesCos = zlights.spot.anglesCos, - spotExponents = zlights.spot.exponents, + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height ); - hemiSkyColors = zlights.hemi.skyColors, - hemiGroundColors = zlights.hemi.groundColors, - hemiPositions = zlights.hemi.positions, + } - dirLength = 0, - pointLength = 0, - spotLength = 0, - hemiLength = 0, + } - dirCount = 0, - pointCount = 0, - spotCount = 0, - hemiCount = 0, + this.setRenderTarget = function ( renderTarget ) { - dirOffset = 0, - pointOffset = 0, - spotOffset = 0, - hemiOffset = 0; + var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube ); - for ( l = 0, ll = lights.length; l < ll; l ++ ) { + if ( renderTarget && renderTarget.__webglFramebuffer === undefined ) { - light = lights[ l ]; + if ( renderTarget.depthBuffer === undefined ) renderTarget.depthBuffer = true; + if ( renderTarget.stencilBuffer === undefined ) renderTarget.stencilBuffer = true; - if ( light.onlyShadow ) continue; + renderTarget.addEventListener( 'dispose', onRenderTargetDispose ); - color = light.color; - intensity = light.intensity; - distance = light.distance; + renderTarget.__webglTexture = _gl.createTexture(); - if ( light instanceof THREE.AmbientLight ) { + _this.info.memory.textures ++; - if ( ! light.visible ) continue; + // Setup texture, create render and frame buffers - if ( _this.gammaInput ) { + var isTargetPowerOfTwo = THREE.Math.isPowerOfTwo( renderTarget.width ) && THREE.Math.isPowerOfTwo( renderTarget.height ), + glFormat = paramThreeToGL( renderTarget.format ), + glType = paramThreeToGL( renderTarget.type ); - r += color.r * color.r; - g += color.g * color.g; - b += color.b * color.b; + if ( isCube ) { - } else { + renderTarget.__webglFramebuffer = []; + renderTarget.__webglRenderbuffer = []; - r += color.r; - g += color.g; - b += color.b; + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTarget.__webglTexture ); + setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget, isTargetPowerOfTwo ); - } + for ( var i = 0; i < 6; i ++ ) { - } else if ( light instanceof THREE.DirectionalLight ) { + renderTarget.__webglFramebuffer[ i ] = _gl.createFramebuffer(); + renderTarget.__webglRenderbuffer[ i ] = _gl.createRenderbuffer(); - dirCount += 1; + _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); - if ( ! light.visible ) continue; + setupFrameBuffer( renderTarget.__webglFramebuffer[ i ], renderTarget, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i ); + setupRenderBuffer( renderTarget.__webglRenderbuffer[ i ], renderTarget ); - _direction.setFromMatrixPosition( light.matrixWorld ); - _vector3.setFromMatrixPosition( light.target.matrixWorld ); - _direction.sub( _vector3 ); - _direction.normalize(); + } - dirOffset = dirLength * 3; + if ( isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); - dirPositions[ dirOffset ] = _direction.x; - dirPositions[ dirOffset + 1 ] = _direction.y; - dirPositions[ dirOffset + 2 ] = _direction.z; + } else { - if ( _this.gammaInput ) { + renderTarget.__webglFramebuffer = _gl.createFramebuffer(); - setColorGamma( dirColors, dirOffset, color, intensity * intensity ); + if ( renderTarget.shareDepthFrom ) { + + renderTarget.__webglRenderbuffer = renderTarget.shareDepthFrom.__webglRenderbuffer; } else { - setColorLinear( dirColors, dirOffset, color, intensity ); + renderTarget.__webglRenderbuffer = _gl.createRenderbuffer(); } - dirLength += 1; + _gl.bindTexture( _gl.TEXTURE_2D, renderTarget.__webglTexture ); + setTextureParameters( _gl.TEXTURE_2D, renderTarget, isTargetPowerOfTwo ); - } else if ( light instanceof THREE.PointLight ) { + _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); - pointCount += 1; + setupFrameBuffer( renderTarget.__webglFramebuffer, renderTarget, _gl.TEXTURE_2D ); - if ( ! light.visible ) continue; + if ( renderTarget.shareDepthFrom ) { - pointOffset = pointLength * 3; + if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { - if ( _this.gammaInput ) { + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderTarget.__webglRenderbuffer ); - setColorGamma( pointColors, pointOffset, color, intensity * intensity ); + } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderTarget.__webglRenderbuffer ); + + } } else { - setColorLinear( pointColors, pointOffset, color, intensity ); + setupRenderBuffer( renderTarget.__webglRenderbuffer, renderTarget ); } - _vector3.setFromMatrixPosition( light.matrixWorld ); + if ( isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); - pointPositions[ pointOffset ] = _vector3.x; - pointPositions[ pointOffset + 1 ] = _vector3.y; - pointPositions[ pointOffset + 2 ] = _vector3.z; + } - pointDistances[ pointLength ] = distance; + // Release everything - pointLength += 1; + if ( isCube ) { - } else if ( light instanceof THREE.SpotLight ) { + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); - spotCount += 1; + } else { - if ( ! light.visible ) continue; + _gl.bindTexture( _gl.TEXTURE_2D, null ); - spotOffset = spotLength * 3; + } - if ( _this.gammaInput ) { + _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); + _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); - setColorGamma( spotColors, spotOffset, color, intensity * intensity ); + } - } else { + var framebuffer, width, height, vx, vy; - setColorLinear( spotColors, spotOffset, color, intensity ); + if ( renderTarget ) { - } + if ( isCube ) { - _vector3.setFromMatrixPosition( light.matrixWorld ); + framebuffer = renderTarget.__webglFramebuffer[ renderTarget.activeCubeFace ]; - spotPositions[ spotOffset ] = _vector3.x; - spotPositions[ spotOffset + 1 ] = _vector3.y; - spotPositions[ spotOffset + 2 ] = _vector3.z; + } else { - spotDistances[ spotLength ] = distance; + framebuffer = renderTarget.__webglFramebuffer; - _direction.copy( _vector3 ); - _vector3.setFromMatrixPosition( light.target.matrixWorld ); - _direction.sub( _vector3 ); - _direction.normalize(); + } - spotDirections[ spotOffset ] = _direction.x; - spotDirections[ spotOffset + 1 ] = _direction.y; - spotDirections[ spotOffset + 2 ] = _direction.z; + width = renderTarget.width; + height = renderTarget.height; - spotAnglesCos[ spotLength ] = Math.cos( light.angle ); - spotExponents[ spotLength ] = light.exponent; + vx = 0; + vy = 0; - spotLength += 1; + } else { - } else if ( light instanceof THREE.HemisphereLight ) { + framebuffer = null; - hemiCount += 1; + width = _viewportWidth; + height = _viewportHeight; - if ( ! light.visible ) continue; + vx = _viewportX; + vy = _viewportY; - _direction.setFromMatrixPosition( light.matrixWorld ); - _direction.normalize(); + } - hemiOffset = hemiLength * 3; + if ( framebuffer !== _currentFramebuffer ) { - hemiPositions[ hemiOffset ] = _direction.x; - hemiPositions[ hemiOffset + 1 ] = _direction.y; - hemiPositions[ hemiOffset + 2 ] = _direction.z; + _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + _gl.viewport( vx, vy, width, height ); - skyColor = light.color; - groundColor = light.groundColor; + _currentFramebuffer = framebuffer; - if ( _this.gammaInput ) { + } - intensitySq = intensity * intensity; + _currentWidth = width; + _currentHeight = height; - setColorGamma( hemiSkyColors, hemiOffset, skyColor, intensitySq ); - setColorGamma( hemiGroundColors, hemiOffset, groundColor, intensitySq ); + }; - } else { + function updateRenderTargetMipmap ( renderTarget ) { - setColorLinear( hemiSkyColors, hemiOffset, skyColor, intensity ); - setColorLinear( hemiGroundColors, hemiOffset, groundColor, intensity ); + if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) { - } + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTarget.__webglTexture ); + _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); - hemiLength += 1; + } else { - } + _gl.bindTexture( _gl.TEXTURE_2D, renderTarget.__webglTexture ); + _gl.generateMipmap( _gl.TEXTURE_2D ); + _gl.bindTexture( _gl.TEXTURE_2D, null ); } - // null eventual remains from removed lights - // (this is to avoid if in shader) + } - for ( l = dirLength * 3, ll = Math.max( dirColors.length, dirCount * 3 ); l < ll; l ++ ) dirColors[ l ] = 0.0; - for ( l = pointLength * 3, ll = Math.max( pointColors.length, pointCount * 3 ); l < ll; l ++ ) pointColors[ l ] = 0.0; - for ( l = spotLength * 3, ll = Math.max( spotColors.length, spotCount * 3 ); l < ll; l ++ ) spotColors[ l ] = 0.0; - for ( l = hemiLength * 3, ll = Math.max( hemiSkyColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiSkyColors[ l ] = 0.0; - for ( l = hemiLength * 3, ll = Math.max( hemiGroundColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiGroundColors[ l ] = 0.0; + // Fallback filters for non-power-of-2 textures - zlights.directional.length = dirLength; - zlights.point.length = pointLength; - zlights.spot.length = spotLength; - zlights.hemi.length = hemiLength; + function filterFallback ( f ) { - zlights.ambient[ 0 ] = r; - zlights.ambient[ 1 ] = g; - zlights.ambient[ 2 ] = b; + if ( f === THREE.NearestFilter || f === THREE.NearestMipMapNearestFilter || f === THREE.NearestMipMapLinearFilter ) { - }; + return _gl.NEAREST; - // GL state setting + } - this.setFaceCulling = function ( cullFace, frontFaceDirection ) { + return _gl.LINEAR; - if ( cullFace === THREE.CullFaceNone ) { + } - _gl.disable( _gl.CULL_FACE ); + // Map three.js constants to WebGL constants - } else { + function paramThreeToGL ( p ) { - if ( frontFaceDirection === THREE.FrontFaceDirectionCW ) { + var extension; - _gl.frontFace( _gl.CW ); + if ( p === THREE.RepeatWrapping ) return _gl.REPEAT; + if ( p === THREE.ClampToEdgeWrapping ) return _gl.CLAMP_TO_EDGE; + if ( p === THREE.MirroredRepeatWrapping ) return _gl.MIRRORED_REPEAT; - } else { + if ( p === THREE.NearestFilter ) return _gl.NEAREST; + if ( p === THREE.NearestMipMapNearestFilter ) return _gl.NEAREST_MIPMAP_NEAREST; + if ( p === THREE.NearestMipMapLinearFilter ) return _gl.NEAREST_MIPMAP_LINEAR; - _gl.frontFace( _gl.CCW ); + if ( p === THREE.LinearFilter ) return _gl.LINEAR; + if ( p === THREE.LinearMipMapNearestFilter ) return _gl.LINEAR_MIPMAP_NEAREST; + if ( p === THREE.LinearMipMapLinearFilter ) return _gl.LINEAR_MIPMAP_LINEAR; - } + if ( p === THREE.UnsignedByteType ) return _gl.UNSIGNED_BYTE; + if ( p === THREE.UnsignedShort4444Type ) return _gl.UNSIGNED_SHORT_4_4_4_4; + if ( p === THREE.UnsignedShort5551Type ) return _gl.UNSIGNED_SHORT_5_5_5_1; + if ( p === THREE.UnsignedShort565Type ) return _gl.UNSIGNED_SHORT_5_6_5; - if ( cullFace === THREE.CullFaceBack ) { + if ( p === THREE.ByteType ) return _gl.BYTE; + if ( p === THREE.ShortType ) return _gl.SHORT; + if ( p === THREE.UnsignedShortType ) return _gl.UNSIGNED_SHORT; + if ( p === THREE.IntType ) return _gl.INT; + if ( p === THREE.UnsignedIntType ) return _gl.UNSIGNED_INT; + if ( p === THREE.FloatType ) return _gl.FLOAT; - _gl.cullFace( _gl.BACK ); + if ( p === THREE.AlphaFormat ) return _gl.ALPHA; + if ( p === THREE.RGBFormat ) return _gl.RGB; + if ( p === THREE.RGBAFormat ) return _gl.RGBA; + if ( p === THREE.LuminanceFormat ) return _gl.LUMINANCE; + if ( p === THREE.LuminanceAlphaFormat ) return _gl.LUMINANCE_ALPHA; - } else if ( cullFace === THREE.CullFaceFront ) { + if ( p === THREE.AddEquation ) return _gl.FUNC_ADD; + if ( p === THREE.SubtractEquation ) return _gl.FUNC_SUBTRACT; + if ( p === THREE.ReverseSubtractEquation ) return _gl.FUNC_REVERSE_SUBTRACT; - _gl.cullFace( _gl.FRONT ); + if ( p === THREE.ZeroFactor ) return _gl.ZERO; + if ( p === THREE.OneFactor ) return _gl.ONE; + if ( p === THREE.SrcColorFactor ) return _gl.SRC_COLOR; + if ( p === THREE.OneMinusSrcColorFactor ) return _gl.ONE_MINUS_SRC_COLOR; + if ( p === THREE.SrcAlphaFactor ) return _gl.SRC_ALPHA; + if ( p === THREE.OneMinusSrcAlphaFactor ) return _gl.ONE_MINUS_SRC_ALPHA; + if ( p === THREE.DstAlphaFactor ) return _gl.DST_ALPHA; + if ( p === THREE.OneMinusDstAlphaFactor ) return _gl.ONE_MINUS_DST_ALPHA; - } else { + if ( p === THREE.DstColorFactor ) return _gl.DST_COLOR; + if ( p === THREE.OneMinusDstColorFactor ) return _gl.ONE_MINUS_DST_COLOR; + if ( p === THREE.SrcAlphaSaturateFactor ) return _gl.SRC_ALPHA_SATURATE; - _gl.cullFace( _gl.FRONT_AND_BACK ); + extension = extensions.get( 'WEBGL_compressed_texture_s3tc' ); - } + if ( extension !== null ) { - _gl.enable( _gl.CULL_FACE ); + 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; } - }; - - this.setMaterialFaces = function ( material ) { - - var doubleSided = material.side === THREE.DoubleSide; - var flipSided = material.side === THREE.BackSide; - - if ( _oldDoubleSided !== doubleSided ) { + extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' ); - if ( doubleSided ) { + if ( extension !== null ) { - _gl.disable( _gl.CULL_FACE ); + 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; - } else { + } - _gl.enable( _gl.CULL_FACE ); + extension = extensions.get( 'EXT_blend_minmax' ); - } + if ( extension !== null ) { - _oldDoubleSided = doubleSided; + if ( p === THREE.MinEquation ) return extension.MIN_EXT; + if ( p === THREE.MaxEquation ) return extension.MAX_EXT; } - if ( _oldFlipSided !== flipSided ) { + return 0; - if ( flipSided ) { + } - _gl.frontFace( _gl.CW ); + // Allocations - } else { + function allocateBones ( object ) { - _gl.frontFace( _gl.CCW ); + if ( _supportsBoneTextures && object && object.skeleton && object.skeleton.useVertexTexture ) { - } + return 1024; - _oldFlipSided = flipSided; + } else { - } + // default for when object is not specified + // ( for example when prebuilding shader + // to be used with multiple objects ) + // + // - leave some extra space for other uniforms + // - limit here is ANGLE's 254 max uniform vectors + // (up to 54 should be safe) - }; + var nVertexUniforms = _gl.getParameter( _gl.MAX_VERTEX_UNIFORM_VECTORS ); + var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); - this.setDepthTest = function ( depthTest ) { + var maxBones = nVertexMatrices; - if ( _oldDepthTest !== depthTest ) { + if ( object !== undefined && object instanceof THREE.SkinnedMesh ) { - if ( depthTest ) { + maxBones = Math.min( object.skeleton.bones.length, maxBones ); - _gl.enable( _gl.DEPTH_TEST ); + if ( maxBones < object.skeleton.bones.length ) { - } else { + console.warn( 'WebGLRenderer: too many bones - ' + object.skeleton.bones.length + ', this GPU supports just ' + maxBones + ' (try OpenGL instead of ANGLE)' ); - _gl.disable( _gl.DEPTH_TEST ); + } } - _oldDepthTest = depthTest; + return maxBones; } - }; - - this.setDepthWrite = function ( depthWrite ) { - - if ( _oldDepthWrite !== depthWrite ) { - - _gl.depthMask( depthWrite ); - _oldDepthWrite = depthWrite; + } - } + function allocateLights( lights ) { - }; + var dirLights = 0; + var pointLights = 0; + var spotLights = 0; + var hemiLights = 0; - function setLineWidth ( width ) { + for ( var l = 0, ll = lights.length; l < ll; l ++ ) { - if ( width !== _oldLineWidth ) { + var light = lights[ l ]; - _gl.lineWidth( width ); + if ( light.onlyShadow || light.visible === false ) continue; - _oldLineWidth = width; + if ( light instanceof THREE.DirectionalLight ) dirLights ++; + if ( light instanceof THREE.PointLight ) pointLights ++; + if ( light instanceof THREE.SpotLight ) spotLights ++; + if ( light instanceof THREE.HemisphereLight ) hemiLights ++; } - }; - - function setPolygonOffset ( polygonoffset, factor, units ) { + return { 'directional': dirLights, 'point': pointLights, 'spot': spotLights, 'hemi': hemiLights }; - if ( _oldPolygonOffset !== polygonoffset ) { + } - if ( polygonoffset ) { + function allocateShadows( lights ) { - _gl.enable( _gl.POLYGON_OFFSET_FILL ); + var maxShadows = 0; - } else { + for ( var l = 0, ll = lights.length; l < ll; l ++ ) { - _gl.disable( _gl.POLYGON_OFFSET_FILL ); + var light = lights[ l ]; - } + if ( ! light.castShadow ) continue; - _oldPolygonOffset = polygonoffset; + if ( light instanceof THREE.SpotLight ) maxShadows ++; + if ( light instanceof THREE.DirectionalLight && ! light.shadowCascade ) maxShadows ++; } - if ( polygonoffset && ( _oldPolygonOffsetFactor !== factor || _oldPolygonOffsetUnits !== units ) ) { + return maxShadows; - _gl.polygonOffset( factor, units ); + } - _oldPolygonOffsetFactor = factor; - _oldPolygonOffsetUnits = units; + // DEPRECATED + + this.initMaterial = function () { - } + console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' ); }; - this.setBlending = function ( blending, blendEquation, blendSrc, blendDst ) { - - if ( blending !== _oldBlending ) { - - if ( blending === THREE.NoBlending ) { + this.addPrePlugin = function () { - _gl.disable( _gl.BLEND ); + console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' ); - } else if ( blending === THREE.AdditiveBlending ) { + }; - _gl.enable( _gl.BLEND ); - _gl.blendEquation( _gl.FUNC_ADD ); - _gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE ); + this.addPostPlugin = function () { - } else if ( blending === THREE.SubtractiveBlending ) { + console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' ); - // TODO: Find blendFuncSeparate() combination - _gl.enable( _gl.BLEND ); - _gl.blendEquation( _gl.FUNC_ADD ); - _gl.blendFunc( _gl.ZERO, _gl.ONE_MINUS_SRC_COLOR ); + }; - } else if ( blending === THREE.MultiplyBlending ) { + this.updateShadowMap = function () { - // TODO: Find blendFuncSeparate() combination - _gl.enable( _gl.BLEND ); - _gl.blendEquation( _gl.FUNC_ADD ); - _gl.blendFunc( _gl.ZERO, _gl.SRC_COLOR ); + console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' ); - } else if ( blending === THREE.CustomBlending ) { + }; - _gl.enable( _gl.BLEND ); +}; - } else { +// File:src/renderers/WebGLRenderTarget.js - _gl.enable( _gl.BLEND ); - _gl.blendEquationSeparate( _gl.FUNC_ADD, _gl.FUNC_ADD ); - _gl.blendFuncSeparate( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA, _gl.ONE, _gl.ONE_MINUS_SRC_ALPHA ); +/** + * @author szimek / https://github.com/szimek/ + * @author alteredq / http://alteredqualia.com/ + */ - } +THREE.WebGLRenderTarget = function ( width, height, options ) { - _oldBlending = blending; + this.width = width; + this.height = height; - } + options = options || {}; - if ( blending === THREE.CustomBlending ) { + this.wrapS = options.wrapS !== undefined ? options.wrapS : THREE.ClampToEdgeWrapping; + this.wrapT = options.wrapT !== undefined ? options.wrapT : THREE.ClampToEdgeWrapping; - if ( blendEquation !== _oldBlendEquation ) { + this.magFilter = options.magFilter !== undefined ? options.magFilter : THREE.LinearFilter; + this.minFilter = options.minFilter !== undefined ? options.minFilter : THREE.LinearMipMapLinearFilter; - _gl.blendEquation( paramThreeToGL( blendEquation ) ); + this.anisotropy = options.anisotropy !== undefined ? options.anisotropy : 1; - _oldBlendEquation = blendEquation; + this.offset = new THREE.Vector2( 0, 0 ); + this.repeat = new THREE.Vector2( 1, 1 ); - } + this.format = options.format !== undefined ? options.format : THREE.RGBAFormat; + this.type = options.type !== undefined ? options.type : THREE.UnsignedByteType; - if ( blendSrc !== _oldBlendSrc || blendDst !== _oldBlendDst ) { + this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; + this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true; - _gl.blendFunc( paramThreeToGL( blendSrc ), paramThreeToGL( blendDst ) ); + this.generateMipmaps = true; - _oldBlendSrc = blendSrc; - _oldBlendDst = blendDst; + this.shareDepthFrom = null; - } +}; - } else { +THREE.WebGLRenderTarget.prototype = { - _oldBlendEquation = null; - _oldBlendSrc = null; - _oldBlendDst = null; + constructor: THREE.WebGLRenderTarget, - } + setSize: function ( width, height ) { - }; + this.width = width; + this.height = height; - // Textures + }, - function setTextureParameters ( textureType, texture, isImagePowerOfTwo ) { + clone: function () { - if ( isImagePowerOfTwo ) { + var tmp = new THREE.WebGLRenderTarget( this.width, this.height ); - _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, paramThreeToGL( texture.wrapS ) ); - _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, paramThreeToGL( texture.wrapT ) ); + tmp.wrapS = this.wrapS; + tmp.wrapT = this.wrapT; - _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, paramThreeToGL( texture.magFilter ) ); - _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, paramThreeToGL( texture.minFilter ) ); + tmp.magFilter = this.magFilter; + tmp.minFilter = this.minFilter; - } else { + tmp.anisotropy = this.anisotropy; - _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); - _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); + tmp.offset.copy( this.offset ); + tmp.repeat.copy( this.repeat ); - _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) ); - _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) ); + tmp.format = this.format; + tmp.type = this.type; - } + tmp.depthBuffer = this.depthBuffer; + tmp.stencilBuffer = this.stencilBuffer; - if ( _glExtensionTextureFilterAnisotropic && texture.type !== THREE.FloatType ) { + tmp.generateMipmaps = this.generateMipmaps; - if ( texture.anisotropy > 1 || texture.__oldAnisotropy ) { + tmp.shareDepthFrom = this.shareDepthFrom; - _gl.texParameterf( textureType, _glExtensionTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, _maxAnisotropy ) ); - texture.__oldAnisotropy = texture.anisotropy; + return tmp; - } + }, - } + dispose: function () { - }; + this.dispatchEvent( { type: 'dispose' } ); - this.setTexture = function ( texture, slot ) { + } - if ( texture.needsUpdate ) { +}; - if ( ! texture.__webglInit ) { +THREE.EventDispatcher.prototype.apply( THREE.WebGLRenderTarget.prototype ); - texture.__webglInit = true; +// File:src/renderers/WebGLRenderTargetCube.js - texture.addEventListener( 'dispose', onTextureDispose ); +/** + * @author alteredq / http://alteredqualia.com + */ - texture.__webglTexture = _gl.createTexture(); +THREE.WebGLRenderTargetCube = function ( width, height, options ) { - _this.info.memory.textures ++; + THREE.WebGLRenderTarget.call( this, width, height, options ); - } + this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5 - _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 ); +THREE.WebGLRenderTargetCube.prototype = Object.create( THREE.WebGLRenderTarget.prototype ); - var image = texture.image, - isImagePowerOfTwo = THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height ), - glFormat = paramThreeToGL( texture.format ), - glType = paramThreeToGL( texture.type ); +// File:src/renderers/webgl/WebGLExtensions.js - setTextureParameters( _gl.TEXTURE_2D, texture, isImagePowerOfTwo ); +THREE.WebGLExtensions = function ( gl ) { - var mipmap, mipmaps = texture.mipmaps; + var extensions = {}; - if ( texture instanceof THREE.DataTexture ) { + this.get = function ( name ) { - // 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 ( extensions[ name ] !== undefined ) { - if ( mipmaps.length > 0 && isImagePowerOfTwo ) { + return extensions[ name ]; - 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 ); + var extension; - } + switch ( name ) { + + case 'OES_texture_float': + extension = gl.getExtension( 'OES_texture_float' ); + break; - texture.generateMipmaps = false; + case 'OES_texture_float_linear': + extension = gl.getExtension( 'OES_texture_float_linear' ); + break; - } else { + case 'OES_standard_derivatives': + extension = gl.getExtension( 'OES_standard_derivatives' ); + break; - _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data ); + 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; - } else if ( texture instanceof THREE.CompressedTexture ) { + case 'WEBGL_compressed_texture_pvrtc': + extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ); + break; - for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { + case 'OES_element_index_uint': + extension = gl.getExtension( 'OES_element_index_uint' ); + break; - 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 ); - } + case 'EXT_blend_minmax': + extension = gl.getExtension( 'EXT_blend_minmax' ); + break; - } + case 'EXT_frag_depth': + extension = gl.getExtension( 'EXT_frag_depth' ); + break; - } 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 + if ( extension === null ) { - if ( mipmaps.length > 0 && isImagePowerOfTwo ) { + console.log( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' ); - for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { + } - mipmap = mipmaps[ i ]; - _gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap ); + extensions[ name ] = extension; - } + return extension; - texture.generateMipmaps = false; + }; - } else { +}; - _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, texture.image ); +// File:src/renderers/webgl/WebGLProgram.js - } +THREE.WebGLProgram = ( function () { - } + var programIdCount = 0; - if ( texture.generateMipmaps && isImagePowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); + var generateDefines = function ( defines ) { - texture.needsUpdate = false; + var value, chunk, chunks = []; - if ( texture.onUpdate ) texture.onUpdate(); + for ( var d in defines ) { - } else { + value = defines[ d ]; + if ( value === false ) continue; - _gl.activeTexture( _gl.TEXTURE0 + slot ); - _gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture ); + chunk = "#define " + d + " " + value; + chunks.push( chunk ); } + return chunks.join( "\n" ); + }; - function clampToMaxSize ( image, maxSize ) { + var cacheUniformLocations = function ( gl, program, identifiers ) { - if ( image.width <= maxSize && image.height <= maxSize ) { + var uniforms = {}; - return image; + for ( var i = 0, l = identifiers.length; i < l; i ++ ) { - } + var id = identifiers[ i ]; + uniforms[ id ] = gl.getUniformLocation( program, id ); - // Warning: Scaling through the canvas will only work with images that use - // premultiplied alpha. + } - var maxDimension = Math.max( image.width, image.height ); - var newWidth = Math.floor( image.width * maxSize / maxDimension ); - var newHeight = Math.floor( image.height * maxSize / maxDimension ); + return uniforms; - var canvas = document.createElement( 'canvas' ); - canvas.width = newWidth; - canvas.height = newHeight; + }; - var ctx = canvas.getContext( '2d' ); - ctx.drawImage( image, 0, 0, image.width, image.height, 0, 0, newWidth, newHeight ); + var cacheAttributeLocations = function ( gl, program, identifiers ) { - return canvas; + var attributes = {}; - } + for ( var i = 0, l = identifiers.length; i < l; i ++ ) { - function setCubeTexture ( texture, slot ) { + var id = identifiers[ i ]; + attributes[ id ] = gl.getAttribLocation( program, id ); - if ( texture.image.length === 6 ) { + } - if ( texture.needsUpdate ) { + return attributes; - if ( ! texture.image.__webglTextureCube ) { + }; - texture.addEventListener( 'dispose', onTextureDispose ); + return function ( renderer, code, material, parameters ) { - texture.image.__webglTextureCube = _gl.createTexture(); + var _this = renderer; + var _gl = _this.context; - _this.info.memory.textures ++; + var defines = material.defines; + var uniforms = material.__webglShader.uniforms; + var attributes = material.attributes; - } + var vertexShader = material.__webglShader.vertexShader; + var fragmentShader = material.__webglShader.fragmentShader; - _gl.activeTexture( _gl.TEXTURE0 + slot ); - _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube ); + var index0AttributeName = material.index0AttributeName; - _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); + if ( index0AttributeName === undefined && parameters.morphTargets === true ) { - var isCompressed = texture instanceof THREE.CompressedTexture; + // programs with morphTargets displace position out of attribute 0 - var cubeImage = []; + index0AttributeName = 'position'; - for ( var i = 0; i < 6; i ++ ) { + } - if ( _this.autoScaleCubemaps && ! isCompressed ) { + var shadowMapTypeDefine = "SHADOWMAP_TYPE_BASIC"; - cubeImage[ i ] = clampToMaxSize( texture.image[ i ], _maxCubemapSize ); + if ( parameters.shadowMapType === THREE.PCFShadowMap ) { - } else { + shadowMapTypeDefine = "SHADOWMAP_TYPE_PCF"; - cubeImage[ i ] = texture.image[ i ]; + } else if ( parameters.shadowMapType === THREE.PCFSoftShadowMap ) { - } + shadowMapTypeDefine = "SHADOWMAP_TYPE_PCF_SOFT"; - } + } - var image = cubeImage[ 0 ], - isImagePowerOfTwo = THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height ), - glFormat = paramThreeToGL( texture.format ), - glType = paramThreeToGL( texture.type ); + // console.log( "building new program " ); - setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isImagePowerOfTwo ); + // - for ( var i = 0; i < 6; i ++ ) { + var customDefines = generateDefines( defines ); - if ( ! isCompressed ) { + // - _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] ); + var program = _gl.createProgram(); - } else { + var prefix_vertex, prefix_fragment; - var mipmap, mipmaps = cubeImage[ i ].mipmaps; + if ( material instanceof THREE.RawShaderMaterial ) { - 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 ); + prefix_vertex = ''; + prefix_fragment = ''; - } else { - _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - } + } else { - } - } - } + prefix_vertex = [ - if ( texture.generateMipmaps && isImagePowerOfTwo ) { + "precision " + parameters.precision + " float;", + "precision " + parameters.precision + " int;", - _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); + customDefines, - } + parameters.supportsVertexTextures ? "#define VERTEX_TEXTURES" : "", - texture.needsUpdate = false; + _this.gammaInput ? "#define GAMMA_INPUT" : "", + _this.gammaOutput ? "#define GAMMA_OUTPUT" : "", - if ( texture.onUpdate ) texture.onUpdate(); + "#define MAX_DIR_LIGHTS " + parameters.maxDirLights, + "#define MAX_POINT_LIGHTS " + parameters.maxPointLights, + "#define MAX_SPOT_LIGHTS " + parameters.maxSpotLights, + "#define MAX_HEMI_LIGHTS " + parameters.maxHemiLights, - } else { + "#define MAX_SHADOWS " + parameters.maxShadows, - _gl.activeTexture( _gl.TEXTURE0 + slot ); - _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube ); + "#define MAX_BONES " + parameters.maxBones, - } + parameters.map ? "#define USE_MAP" : "", + parameters.envMap ? "#define USE_ENVMAP" : "", + parameters.lightMap ? "#define USE_LIGHTMAP" : "", + parameters.bumpMap ? "#define USE_BUMPMAP" : "", + parameters.normalMap ? "#define USE_NORMALMAP" : "", + parameters.specularMap ? "#define USE_SPECULARMAP" : "", + parameters.alphaMap ? "#define USE_ALPHAMAP" : "", + parameters.vertexColors ? "#define USE_COLOR" : "", - } + parameters.skinning ? "#define USE_SKINNING" : "", + parameters.useVertexTexture ? "#define BONE_TEXTURE" : "", - }; + parameters.morphTargets ? "#define USE_MORPHTARGETS" : "", + parameters.morphNormals ? "#define USE_MORPHNORMALS" : "", + parameters.wrapAround ? "#define WRAP_AROUND" : "", + parameters.doubleSided ? "#define DOUBLE_SIDED" : "", + parameters.flipSided ? "#define FLIP_SIDED" : "", - function setCubeTextureDynamic ( texture, slot ) { + parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", + parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", + parameters.shadowMapDebug ? "#define SHADOWMAP_DEBUG" : "", + parameters.shadowMapCascade ? "#define SHADOWMAP_CASCADE" : "", - _gl.activeTexture( _gl.TEXTURE0 + slot ); - _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.__webglTexture ); + parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "", - }; + parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", + //_this._glExtensionFragDepth ? "#define USE_LOGDEPTHBUF_EXT" : "", - // Render targets - function setupFrameBuffer ( framebuffer, renderTarget, textureTarget ) { + "uniform mat4 modelMatrix;", + "uniform mat4 modelViewMatrix;", + "uniform mat4 projectionMatrix;", + "uniform mat4 viewMatrix;", + "uniform mat3 normalMatrix;", + "uniform vec3 cameraPosition;", - _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); - _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureTarget, renderTarget.__webglTexture, 0 ); + "attribute vec3 position;", + "attribute vec3 normal;", + "attribute vec2 uv;", + "attribute vec2 uv2;", - }; + "#ifdef USE_COLOR", - function setupRenderBuffer ( renderbuffer, renderTarget ) { + " attribute vec3 color;", - _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer ); + "#endif", - if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { + "#ifdef USE_MORPHTARGETS", - _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height ); - _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); + " attribute vec3 morphTarget0;", + " attribute vec3 morphTarget1;", + " attribute vec3 morphTarget2;", + " attribute vec3 morphTarget3;", - /* For some reason this is not working. Defaulting to RGBA4. - } else if ( ! renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + " #ifdef USE_MORPHNORMALS", - _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.STENCIL_INDEX8, renderTarget.width, renderTarget.height ); - _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); - */ - } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + " attribute vec3 morphNormal0;", + " attribute vec3 morphNormal1;", + " attribute vec3 morphNormal2;", + " attribute vec3 morphNormal3;", - _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height ); - _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); + " #else", - } else { + " attribute vec3 morphTarget4;", + " attribute vec3 morphTarget5;", + " attribute vec3 morphTarget6;", + " attribute vec3 morphTarget7;", - _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height ); + " #endif", - } + "#endif", - }; + "#ifdef USE_SKINNING", - this.setRenderTarget = function ( renderTarget ) { + " attribute vec4 skinIndex;", + " attribute vec4 skinWeight;", - var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube ); + "#endif", - if ( renderTarget && ! renderTarget.__webglFramebuffer ) { + "" - if ( renderTarget.depthBuffer === undefined ) renderTarget.depthBuffer = true; - if ( renderTarget.stencilBuffer === undefined ) renderTarget.stencilBuffer = true; + ].join( '\n' ); - renderTarget.addEventListener( 'dispose', onRenderTargetDispose ); + prefix_fragment = [ - renderTarget.__webglTexture = _gl.createTexture(); + "precision " + parameters.precision + " float;", + "precision " + parameters.precision + " int;", - _this.info.memory.textures ++; + ( parameters.bumpMap || parameters.normalMap ) ? "#extension GL_OES_standard_derivatives : enable" : "", - // Setup texture, create render and frame buffers + customDefines, - var isTargetPowerOfTwo = THREE.Math.isPowerOfTwo( renderTarget.width ) && THREE.Math.isPowerOfTwo( renderTarget.height ), - glFormat = paramThreeToGL( renderTarget.format ), - glType = paramThreeToGL( renderTarget.type ); + "#define MAX_DIR_LIGHTS " + parameters.maxDirLights, + "#define MAX_POINT_LIGHTS " + parameters.maxPointLights, + "#define MAX_SPOT_LIGHTS " + parameters.maxSpotLights, + "#define MAX_HEMI_LIGHTS " + parameters.maxHemiLights, - if ( isCube ) { + "#define MAX_SHADOWS " + parameters.maxShadows, - renderTarget.__webglFramebuffer = []; - renderTarget.__webglRenderbuffer = []; + parameters.alphaTest ? "#define ALPHATEST " + parameters.alphaTest: "", - _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTarget.__webglTexture ); - setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget, isTargetPowerOfTwo ); + _this.gammaInput ? "#define GAMMA_INPUT" : "", + _this.gammaOutput ? "#define GAMMA_OUTPUT" : "", - for ( var i = 0; i < 6; i ++ ) { + ( parameters.useFog && parameters.fog ) ? "#define USE_FOG" : "", + ( parameters.useFog && parameters.fogExp ) ? "#define FOG_EXP2" : "", - renderTarget.__webglFramebuffer[ i ] = _gl.createFramebuffer(); - renderTarget.__webglRenderbuffer[ i ] = _gl.createRenderbuffer(); + parameters.map ? "#define USE_MAP" : "", + parameters.envMap ? "#define USE_ENVMAP" : "", + parameters.lightMap ? "#define USE_LIGHTMAP" : "", + parameters.bumpMap ? "#define USE_BUMPMAP" : "", + parameters.normalMap ? "#define USE_NORMALMAP" : "", + parameters.specularMap ? "#define USE_SPECULARMAP" : "", + parameters.alphaMap ? "#define USE_ALPHAMAP" : "", + parameters.vertexColors ? "#define USE_COLOR" : "", - _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); + parameters.metal ? "#define METAL" : "", + parameters.wrapAround ? "#define WRAP_AROUND" : "", + parameters.doubleSided ? "#define DOUBLE_SIDED" : "", + parameters.flipSided ? "#define FLIP_SIDED" : "", - setupFrameBuffer( renderTarget.__webglFramebuffer[ i ], renderTarget, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i ); - setupRenderBuffer( renderTarget.__webglRenderbuffer[ i ], renderTarget ); + parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", + parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", + parameters.shadowMapDebug ? "#define SHADOWMAP_DEBUG" : "", + parameters.shadowMapCascade ? "#define SHADOWMAP_CASCADE" : "", - } + parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", + //_this._glExtensionFragDepth ? "#define USE_LOGDEPTHBUF_EXT" : "", - if ( isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); + "uniform mat4 viewMatrix;", + "uniform vec3 cameraPosition;", + "" - } else { + ].join( '\n' ); - renderTarget.__webglFramebuffer = _gl.createFramebuffer(); + } - if ( renderTarget.shareDepthFrom ) { + var glVertexShader = new THREE.WebGLShader( _gl, _gl.VERTEX_SHADER, prefix_vertex + vertexShader ); + var glFragmentShader = new THREE.WebGLShader( _gl, _gl.FRAGMENT_SHADER, prefix_fragment + fragmentShader ); - renderTarget.__webglRenderbuffer = renderTarget.shareDepthFrom.__webglRenderbuffer; + _gl.attachShader( program, glVertexShader ); + _gl.attachShader( program, glFragmentShader ); - } else { + if ( index0AttributeName !== undefined ) { - renderTarget.__webglRenderbuffer = _gl.createRenderbuffer(); + // Force a particular attribute to index 0. + // because potentially expensive emulation is done by browser if attribute 0 is disabled. + // And, color, for example is often automatically bound to index 0 so disabling it - } + _gl.bindAttribLocation( program, 0, index0AttributeName ); - _gl.bindTexture( _gl.TEXTURE_2D, renderTarget.__webglTexture ); - setTextureParameters( _gl.TEXTURE_2D, renderTarget, isTargetPowerOfTwo ); + } - _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); + _gl.linkProgram( program ); - setupFrameBuffer( renderTarget.__webglFramebuffer, renderTarget, _gl.TEXTURE_2D ); + if ( _gl.getProgramParameter( program, _gl.LINK_STATUS ) === false ) { - if ( renderTarget.shareDepthFrom ) { + console.error( 'THREE.WebGLProgram: Could not initialise shader.' ); + console.error( 'gl.VALIDATE_STATUS', _gl.getProgramParameter( program, _gl.VALIDATE_STATUS ) ); + console.error( 'gl.getError()', _gl.getError() ); - if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { + } - _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderTarget.__webglRenderbuffer ); + if ( _gl.getProgramInfoLog( program ) !== '' ) { - } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', _gl.getProgramInfoLog( program ) ); - _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderTarget.__webglRenderbuffer ); + } - } + // clean up - } else { + _gl.deleteShader( glVertexShader ); + _gl.deleteShader( glFragmentShader ); - setupRenderBuffer( renderTarget.__webglRenderbuffer, renderTarget ); + // cache uniform locations - } + var identifiers = [ - if ( isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); + 'viewMatrix', 'modelViewMatrix', 'projectionMatrix', 'normalMatrix', 'modelMatrix', 'cameraPosition', 'morphTargetInfluences', 'bindMatrix', 'bindMatrixInverse' - } + ]; - // Release everything + if ( parameters.useVertexTexture ) { - if ( isCube ) { + identifiers.push( 'boneTexture' ); + identifiers.push( 'boneTextureWidth' ); + identifiers.push( 'boneTextureHeight' ); - _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); + } else { - } else { + identifiers.push( 'boneGlobalMatrices' ); - _gl.bindTexture( _gl.TEXTURE_2D, null ); + } - } + if ( parameters.logarithmicDepthBuffer ) { - _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); - _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); + identifiers.push('logDepthBufFC'); } - var framebuffer, width, height, vx, vy; - if ( renderTarget ) { + for ( var u in uniforms ) { - if ( isCube ) { + identifiers.push( u ); - framebuffer = renderTarget.__webglFramebuffer[ renderTarget.activeCubeFace ]; + } - } else { + this.uniforms = cacheUniformLocations( _gl, program, identifiers ); - framebuffer = renderTarget.__webglFramebuffer; + // cache attributes locations - } + identifiers = [ - width = renderTarget.width; - height = renderTarget.height; + "position", "normal", "uv", "uv2", "tangent", "color", + "skinIndex", "skinWeight", "lineDistance" - vx = 0; - vy = 0; + ]; - } else { + for ( var i = 0; i < parameters.maxMorphTargets; i ++ ) { - framebuffer = null; + identifiers.push( "morphTarget" + i ); - width = _viewportWidth; - height = _viewportHeight; + } - vx = _viewportX; - vy = _viewportY; + for ( var i = 0; i < parameters.maxMorphNormals; i ++ ) { - } + identifiers.push( "morphNormal" + i ); - if ( framebuffer !== _currentFramebuffer ) { + } - _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); - _gl.viewport( vx, vy, width, height ); + for ( var a in attributes ) { - _currentFramebuffer = framebuffer; + identifiers.push( a ); } - _currentWidth = width; - _currentHeight = height; - - }; + this.attributes = cacheAttributeLocations( _gl, program, identifiers ); + this.attributesKeys = Object.keys( this.attributes ); - function updateRenderTargetMipmap ( renderTarget ) { + // - if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) { + this.id = programIdCount ++; + this.code = code; + this.usedTimes = 1; + this.program = program; + this.vertexShader = glVertexShader; + this.fragmentShader = glFragmentShader; - _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTarget.__webglTexture ); - _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); - _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); + return this; - } else { + }; - _gl.bindTexture( _gl.TEXTURE_2D, renderTarget.__webglTexture ); - _gl.generateMipmap( _gl.TEXTURE_2D ); - _gl.bindTexture( _gl.TEXTURE_2D, null ); +} )(); - } +// File:src/renderers/webgl/WebGLShader.js - }; +THREE.WebGLShader = ( function () { - // Fallback filters for non-power-of-2 textures + var addLineNumbers = function ( string ) { - function filterFallback ( f ) { + var lines = string.split( '\n' ); - if ( f === THREE.NearestFilter || f === THREE.NearestMipMapNearestFilter || f === THREE.NearestMipMapLinearFilter ) { + for ( var i = 0; i < lines.length; i ++ ) { - return _gl.NEAREST; + lines[ i ] = ( i + 1 ) + ': ' + lines[ i ]; } - return _gl.LINEAR; + return lines.join( '\n' ); }; - // Map three.js constants to WebGL constants + return function ( gl, type, string ) { - function paramThreeToGL ( p ) { + var shader = gl.createShader( type ); - if ( p === THREE.RepeatWrapping ) return _gl.REPEAT; - if ( p === THREE.ClampToEdgeWrapping ) return _gl.CLAMP_TO_EDGE; - if ( p === THREE.MirroredRepeatWrapping ) return _gl.MIRRORED_REPEAT; + gl.shaderSource( shader, string ); + gl.compileShader( shader ); - if ( p === THREE.NearestFilter ) return _gl.NEAREST; - if ( p === THREE.NearestMipMapNearestFilter ) return _gl.NEAREST_MIPMAP_NEAREST; - if ( p === THREE.NearestMipMapLinearFilter ) return _gl.NEAREST_MIPMAP_LINEAR; + if ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === false ) { - if ( p === THREE.LinearFilter ) return _gl.LINEAR; - if ( p === THREE.LinearMipMapNearestFilter ) return _gl.LINEAR_MIPMAP_NEAREST; - if ( p === THREE.LinearMipMapLinearFilter ) return _gl.LINEAR_MIPMAP_LINEAR; + console.error( 'THREE.WebGLShader: Shader couldn\'t compile.' ); - if ( p === THREE.UnsignedByteType ) return _gl.UNSIGNED_BYTE; - if ( p === THREE.UnsignedShort4444Type ) return _gl.UNSIGNED_SHORT_4_4_4_4; - if ( p === THREE.UnsignedShort5551Type ) return _gl.UNSIGNED_SHORT_5_5_5_1; - if ( p === THREE.UnsignedShort565Type ) return _gl.UNSIGNED_SHORT_5_6_5; + } - if ( p === THREE.ByteType ) return _gl.BYTE; - if ( p === THREE.ShortType ) return _gl.SHORT; - if ( p === THREE.UnsignedShortType ) return _gl.UNSIGNED_SHORT; - if ( p === THREE.IntType ) return _gl.INT; - if ( p === THREE.UnsignedIntType ) return _gl.UNSIGNED_INT; - if ( p === THREE.FloatType ) return _gl.FLOAT; + if ( gl.getShaderInfoLog( shader ) !== '' ) { - if ( p === THREE.AlphaFormat ) return _gl.ALPHA; - if ( p === THREE.RGBFormat ) return _gl.RGB; - if ( p === THREE.RGBAFormat ) return _gl.RGBA; - if ( p === THREE.LuminanceFormat ) return _gl.LUMINANCE; - if ( p === THREE.LuminanceAlphaFormat ) return _gl.LUMINANCE_ALPHA; + console.warn( 'THREE.WebGLShader: gl.getShaderInfoLog()', gl.getShaderInfoLog( shader ) ); + console.warn( addLineNumbers( string ) ); - if ( p === THREE.AddEquation ) return _gl.FUNC_ADD; - if ( p === THREE.SubtractEquation ) return _gl.FUNC_SUBTRACT; - if ( p === THREE.ReverseSubtractEquation ) return _gl.FUNC_REVERSE_SUBTRACT; + } - if ( p === THREE.ZeroFactor ) return _gl.ZERO; - if ( p === THREE.OneFactor ) return _gl.ONE; - if ( p === THREE.SrcColorFactor ) return _gl.SRC_COLOR; - if ( p === THREE.OneMinusSrcColorFactor ) return _gl.ONE_MINUS_SRC_COLOR; - if ( p === THREE.SrcAlphaFactor ) return _gl.SRC_ALPHA; - if ( p === THREE.OneMinusSrcAlphaFactor ) return _gl.ONE_MINUS_SRC_ALPHA; - if ( p === THREE.DstAlphaFactor ) return _gl.DST_ALPHA; - if ( p === THREE.OneMinusDstAlphaFactor ) return _gl.ONE_MINUS_DST_ALPHA; + // --enable-privileged-webgl-extension + // console.log( type, gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) ); - if ( p === THREE.DstColorFactor ) return _gl.DST_COLOR; - if ( p === THREE.OneMinusDstColorFactor ) return _gl.ONE_MINUS_DST_COLOR; - if ( p === THREE.SrcAlphaSaturateFactor ) return _gl.SRC_ALPHA_SATURATE; + return shader; - if ( _glExtensionCompressedTextureS3TC !== undefined ) { + }; - 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; +} )(); - } +// File:src/renderers/webgl/plugins/LensFlarePlugin.js - return 0; +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ - }; +THREE.LensFlarePlugin = function ( renderer, flares ) { - // Allocations + var gl = renderer.context; - function allocateBones ( object ) { + var vertexBuffer, elementBuffer; + var program, attributes, uniforms; + var hasVertexTexture; - if ( _supportsBoneTextures && object && object.skeleton && object.skeleton.useVertexTexture ) { + var tempTexture, occlusionTexture; - return 1024; + var init = function () { - } else { + var vertices = new Float32Array( [ + -1, -1, 0, 0, + 1, -1, 1, 0, + 1, 1, 1, 1, + -1, 1, 0, 1 + ] ); - // default for when object is not specified - // ( for example when prebuilding shader - // to be used with multiple objects ) - // - // - leave some extra space for other uniforms - // - limit here is ANGLE's 254 max uniform vectors - // (up to 54 should be safe) + var faces = new Uint16Array( [ + 0, 1, 2, + 0, 2, 3 + ] ); - var nVertexUniforms = _gl.getParameter( _gl.MAX_VERTEX_UNIFORM_VECTORS ); - var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); + // buffers - var maxBones = nVertexMatrices; + vertexBuffer = gl.createBuffer(); + elementBuffer = gl.createBuffer(); - if ( object !== undefined && object instanceof THREE.SkinnedMesh ) { + gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); + gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW ); - maxBones = Math.min( object.skeleton.bones.length, maxBones ); + gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); + gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW ); - if ( maxBones < object.skeleton.bones.length ) { + // textures - console.warn( 'WebGLRenderer: too many bones - ' + object.skeleton.bones.length + ', this GPU supports just ' + maxBones + ' (try OpenGL instead of ANGLE)' ); + tempTexture = gl.createTexture(); + occlusionTexture = gl.createTexture(); - } + 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 ); - } + 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 ); - return maxBones; + hasVertexTexture = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) > 0; - } + var shader; - }; + if ( hasVertexTexture ) { - function allocateLights( lights ) { + shader = { - var dirLights = 0; - var pointLights = 0; - var spotLights = 0; - var hemiLights = 0; + vertexShader: [ - for ( var l = 0, ll = lights.length; l < ll; l ++ ) { + "uniform lowp int renderType;", - var light = lights[ l ]; + "uniform vec3 screenPosition;", + "uniform vec2 scale;", + "uniform float rotation;", - if ( light.onlyShadow || light.visible === false ) continue; + "uniform sampler2D occlusionMap;", - if ( light instanceof THREE.DirectionalLight ) dirLights ++; - if ( light instanceof THREE.PointLight ) pointLights ++; - if ( light instanceof THREE.SpotLight ) spotLights ++; - if ( light instanceof THREE.HemisphereLight ) hemiLights ++; + "attribute vec2 position;", + "attribute vec2 uv;", - } + "varying vec2 vUV;", + "varying float vVisibility;", - return { 'directional': dirLights, 'point': pointLights, 'spot': spotLights, 'hemi': hemiLights }; + "void main() {", - }; + "vUV = uv;", - function allocateShadows( lights ) { + "vec2 pos = position;", - var maxShadows = 0; + "if( renderType == 2 ) {", - for ( var l = 0, ll = lights.length; l < ll; l ++ ) { + "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 ) );", - var light = lights[ l ]; + "vVisibility = visibility.r / 9.0;", + "vVisibility *= 1.0 - visibility.g / 9.0;", + "vVisibility *= visibility.b / 9.0;", + "vVisibility *= 1.0 - visibility.a / 9.0;", - if ( ! light.castShadow ) continue; + "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", + "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", - if ( light instanceof THREE.SpotLight ) maxShadows ++; - if ( light instanceof THREE.DirectionalLight && ! light.shadowCascade ) maxShadows ++; + "}", - } + "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", - return maxShadows; + "}" - }; + ].join( "\n" ), - // Initialization + fragmentShader: [ - function initGL() { + "uniform lowp int renderType;", - try { + "uniform sampler2D map;", + "uniform float opacity;", + "uniform vec3 color;", - var attributes = { - alpha: _alpha, - depth: _depth, - stencil: _stencil, - antialias: _antialias, - premultipliedAlpha: _premultipliedAlpha, - preserveDrawingBuffer: _preserveDrawingBuffer - }; + "varying vec2 vUV;", + "varying float vVisibility;", - _gl = _context || _canvas.getContext( 'webgl', attributes ) || _canvas.getContext( 'experimental-webgl', attributes ); + "void main() {", - if ( _gl === null ) { + // pink square - throw 'Error creating WebGL context.'; + "if( renderType == 0 ) {", - } + "gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );", - } catch ( error ) { + // restore - console.error( error ); + "} else if( renderType == 1 ) {", - } + "gl_FragColor = texture2D( map, vUV );", - _glExtensionTextureFloat = _gl.getExtension( 'OES_texture_float' ); - _glExtensionTextureFloatLinear = _gl.getExtension( 'OES_texture_float_linear' ); - _glExtensionStandardDerivatives = _gl.getExtension( 'OES_standard_derivatives' ); + // flare - _glExtensionTextureFilterAnisotropic = _gl.getExtension( 'EXT_texture_filter_anisotropic' ) || _gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || _gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' ); + "} else {", - _glExtensionCompressedTextureS3TC = _gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || _gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || _gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' ); + "vec4 texture = texture2D( map, vUV );", + "texture.a *= opacity * vVisibility;", + "gl_FragColor = texture;", + "gl_FragColor.rgb *= color;", - _glExtensionElementIndexUint = _gl.getExtension( 'OES_element_index_uint' ); + "}", + "}" - if ( _glExtensionTextureFloat === null ) { + ].join( "\n" ) - console.log( 'THREE.WebGLRenderer: Float textures not supported.' ); + }; - } + } else { - if ( _glExtensionStandardDerivatives === null ) { + shader = { - console.log( 'THREE.WebGLRenderer: Standard derivatives not supported.' ); + vertexShader: [ - } + "uniform lowp int renderType;", - if ( _glExtensionTextureFilterAnisotropic === null ) { + "uniform vec3 screenPosition;", + "uniform vec2 scale;", + "uniform float rotation;", - console.log( 'THREE.WebGLRenderer: Anisotropic texture filtering not supported.' ); + "attribute vec2 position;", + "attribute vec2 uv;", - } + "varying vec2 vUV;", - if ( _glExtensionCompressedTextureS3TC === null ) { + "void main() {", - console.log( 'THREE.WebGLRenderer: S3TC compressed textures not supported.' ); + "vUV = uv;", - } + "vec2 pos = position;", - if ( _glExtensionElementIndexUint === null ) { + "if( renderType == 2 ) {", - console.log( 'THREE.WebGLRenderer: elementindex as unsigned integer not supported.' ); + "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", + "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", - } + "}", - if ( _gl.getShaderPrecisionFormat === undefined ) { + "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", - _gl.getShaderPrecisionFormat = function () { + "}" - return { - 'rangeMin': 1, - 'rangeMax': 1, - 'precision': 1 - }; + ].join( "\n" ), - } - } + fragmentShader: [ - if ( _logarithmicDepthBuffer ) { + "precision mediump float;", - _glExtensionFragDepth = _gl.getExtension( 'EXT_frag_depth' ); + "uniform lowp int renderType;", - } + "uniform sampler2D map;", + "uniform sampler2D occlusionMap;", + "uniform float opacity;", + "uniform vec3 color;", - }; + "varying vec2 vUV;", - function setDefaultGLState () { + "void main() {", - _gl.clearColor( 0, 0, 0, 1 ); - _gl.clearDepth( 1 ); - _gl.clearStencil( 0 ); + // pink square - _gl.enable( _gl.DEPTH_TEST ); - _gl.depthFunc( _gl.LEQUAL ); + "if( renderType == 0 ) {", - _gl.frontFace( _gl.CCW ); - _gl.cullFace( _gl.BACK ); - _gl.enable( _gl.CULL_FACE ); + "gl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );", - _gl.enable( _gl.BLEND ); - _gl.blendEquation( _gl.FUNC_ADD ); - _gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA ); + // restore - _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight ); + "} else if( renderType == 1 ) {", - _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); + "gl_FragColor = texture2D( map, vUV );", - }; + // flare - // default plugins (order is important) + "} else {", - this.shadowMapPlugin = new THREE.ShadowMapPlugin(); - this.addPrePlugin( this.shadowMapPlugin ); + "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 );", - this.addPostPlugin( new THREE.SpritePlugin() ); - this.addPostPlugin( new THREE.LensFlarePlugin() ); + "vec4 texture = texture2D( map, vUV );", + "texture.a *= opacity * visibility;", + "gl_FragColor = texture;", + "gl_FragColor.rgb *= color;", -}; + "}", -// File:src/renderers/WebGLRenderTarget.js + "}" -/** - * @author szimek / https://github.com/szimek/ - * @author alteredq / http://alteredqualia.com/ - */ + ].join( "\n" ) -THREE.WebGLRenderTarget = function ( width, height, options ) { + }; - this.width = width; - this.height = height; + } - options = options || {}; + program = createProgram( shader ); - this.wrapS = options.wrapS !== undefined ? options.wrapS : THREE.ClampToEdgeWrapping; - this.wrapT = options.wrapT !== undefined ? options.wrapT : THREE.ClampToEdgeWrapping; + attributes = { + vertex: gl.getAttribLocation ( program, "position" ), + uv: gl.getAttribLocation ( program, "uv" ) + } - this.magFilter = options.magFilter !== undefined ? options.magFilter : THREE.LinearFilter; - this.minFilter = options.minFilter !== undefined ? options.minFilter : THREE.LinearMipMapLinearFilter; + 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" ) + }; - this.anisotropy = options.anisotropy !== undefined ? options.anisotropy : 1; + }; - this.offset = new THREE.Vector2( 0, 0 ); - this.repeat = new THREE.Vector2( 1, 1 ); + /* + * Render lens flares + * Method: renders 16x16 0xff00ff-colored points scattered over the light source area, + * reads these back and calculates occlusion. + */ - this.format = options.format !== undefined ? options.format : THREE.RGBAFormat; - this.type = options.type !== undefined ? options.type : THREE.UnsignedByteType; + this.render = function ( scene, camera, viewportWidth, viewportHeight ) { - this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; - this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true; + if ( flares.length === 0 ) return; - this.generateMipmaps = true; + var tempPosition = new THREE.Vector3(); - this.shareDepthFrom = null; + var invAspect = viewportHeight / viewportWidth, + halfViewportWidth = viewportWidth * 0.5, + halfViewportHeight = viewportHeight * 0.5; -}; + var size = 16 / viewportHeight, + scale = new THREE.Vector2( size * invAspect, size ); -THREE.WebGLRenderTarget.prototype = { + var screenPosition = new THREE.Vector3( 1, 1, 0 ), + screenPositionPixels = new THREE.Vector2( 1, 1 ); - constructor: THREE.WebGLRenderTarget, + if ( program === undefined ) { - setSize: function ( width, height ) { + init(); - this.width = width; - this.height = height; + } - }, + gl.useProgram( program ); - clone: function () { + gl.enableVertexAttribArray( attributes.vertex ); + gl.enableVertexAttribArray( attributes.uv ); - var tmp = new THREE.WebGLRenderTarget( this.width, this.height ); + // loop through all lens flares to update their occlusion and positions + // setup gl and common used attribs/unforms - tmp.wrapS = this.wrapS; - tmp.wrapT = this.wrapT; + gl.uniform1i( uniforms.occlusionMap, 0 ); + gl.uniform1i( uniforms.map, 1 ); - tmp.magFilter = this.magFilter; - tmp.minFilter = this.minFilter; + 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 ); - tmp.anisotropy = this.anisotropy; + gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); - tmp.offset.copy( this.offset ); - tmp.repeat.copy( this.repeat ); + gl.disable( gl.CULL_FACE ); + gl.depthMask( false ); - tmp.format = this.format; - tmp.type = this.type; + for ( var i = 0, l = flares.length; i < l; i ++ ) { - tmp.depthBuffer = this.depthBuffer; - tmp.stencilBuffer = this.stencilBuffer; + size = 16 / viewportHeight; + scale.set( size * invAspect, size ); - tmp.generateMipmaps = this.generateMipmaps; + // calc object screen position - tmp.shareDepthFrom = this.shareDepthFrom; + var flare = flares[ i ]; + + tempPosition.set( flare.matrixWorld.elements[12], flare.matrixWorld.elements[13], flare.matrixWorld.elements[14] ); - return tmp; + tempPosition.applyMatrix4( camera.matrixWorldInverse ); + tempPosition.applyProjection( camera.projectionMatrix ); - }, + // setup arrays for gl programs - dispose: function () { + screenPosition.copy( tempPosition ) - this.dispatchEvent( { type: 'dispose' } ); + screenPositionPixels.x = screenPosition.x * halfViewportWidth + halfViewportWidth; + screenPositionPixels.y = screenPosition.y * halfViewportHeight + halfViewportHeight; - } + // screen cull -}; + if ( hasVertexTexture || ( + screenPositionPixels.x > 0 && + screenPositionPixels.x < viewportWidth && + screenPositionPixels.y > 0 && + screenPositionPixels.y < viewportHeight ) ) { -THREE.EventDispatcher.prototype.apply( THREE.WebGLRenderTarget.prototype ); + // save current RGB to temp texture -// File:src/renderers/WebGLRenderTargetCube.js + 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 ); -/** - * @author alteredq / http://alteredqualia.com - */ -THREE.WebGLRenderTargetCube = function ( width, height, options ) { + // render pink quad - THREE.WebGLRenderTarget.call( this, width, height, options ); + gl.uniform1i( uniforms.renderType, 0 ); + gl.uniform2f( uniforms.scale, scale.x, scale.y ); + gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); - this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5 + gl.disable( gl.BLEND ); + gl.enable( gl.DEPTH_TEST ); -}; + gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); -THREE.WebGLRenderTargetCube.prototype = Object.create( THREE.WebGLRenderTarget.prototype ); -// File:src/renderers/webgl/WebGLProgram.js + // copy result to occlusionMap -THREE.WebGLProgram = ( function () { + 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 ); - var programIdCount = 0; - var generateDefines = function ( defines ) { + // restore graphics - var value, chunk, chunks = []; + gl.uniform1i( uniforms.renderType, 1 ); + gl.disable( gl.DEPTH_TEST ); - for ( var d in defines ) { + gl.activeTexture( gl.TEXTURE1 ); + gl.bindTexture( gl.TEXTURE_2D, tempTexture ); + gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - value = defines[ d ]; - if ( value === false ) continue; - chunk = "#define " + d + " " + value; - chunks.push( chunk ); + // update object positions - } + flare.positionScreen.copy( screenPosition ) - return chunks.join( "\n" ); + if ( flare.customUpdateCallback ) { - }; + flare.customUpdateCallback( flare ); - var cacheUniformLocations = function ( gl, program, identifiers ) { + } else { - var uniforms = {}; + flare.updateLensFlares(); - for ( var i = 0, l = identifiers.length; i < l; i ++ ) { + } - var id = identifiers[ i ]; - uniforms[ id ] = gl.getUniformLocation( program, id ); + // render flares - } + gl.uniform1i( uniforms.renderType, 2 ); + gl.enable( gl.BLEND ); - return uniforms; + for ( var j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) { - }; + var sprite = flare.lensFlares[ j ]; - var cacheAttributeLocations = function ( gl, program, identifiers ) { + if ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) { - var attributes = {}; + screenPosition.x = sprite.x; + screenPosition.y = sprite.y; + screenPosition.z = sprite.z; - for ( var i = 0, l = identifiers.length; i < l; i ++ ) { + size = sprite.size * sprite.scale / viewportHeight; - var id = identifiers[ i ]; - attributes[ id ] = gl.getAttribLocation( program, id ); + scale.x = size * invAspect; + scale.y = size; - } + gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); + gl.uniform2f( uniforms.scale, scale.x, scale.y ); + gl.uniform1f( uniforms.rotation, sprite.rotation ); - return attributes; + gl.uniform1f( uniforms.opacity, sprite.opacity ); + gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b ); - }; + renderer.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst ); + renderer.setTexture( sprite.texture, 1 ); - return function ( renderer, code, material, parameters ) { + gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - var _this = renderer; - var _gl = _this.context; + } - var defines = material.defines; - var uniforms = material.__webglShader.uniforms; - var attributes = material.attributes; + } - var vertexShader = material.__webglShader.vertexShader; - var fragmentShader = material.__webglShader.fragmentShader; + } - var index0AttributeName = material.index0AttributeName; + } - if ( index0AttributeName === undefined && parameters.morphTargets === true ) { + // restore gl - // programs with morphTargets displace position out of attribute 0 + gl.enable( gl.CULL_FACE ); + gl.enable( gl.DEPTH_TEST ); + gl.depthMask( true ); - index0AttributeName = 'position'; + renderer.resetGLState(); - } + }; - var shadowMapTypeDefine = "SHADOWMAP_TYPE_BASIC"; + function createProgram ( shader ) { - if ( parameters.shadowMapType === THREE.PCFShadowMap ) { + var program = gl.createProgram(); - shadowMapTypeDefine = "SHADOWMAP_TYPE_PCF"; + var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER ); + var vertexShader = gl.createShader( gl.VERTEX_SHADER ); - } else if ( parameters.shadowMapType === THREE.PCFSoftShadowMap ) { + var prefix = "precision " + renderer.getPrecision() + " float;\n"; - shadowMapTypeDefine = "SHADOWMAP_TYPE_PCF_SOFT"; + gl.shaderSource( fragmentShader, prefix + shader.fragmentShader ); + gl.shaderSource( vertexShader, prefix + shader.vertexShader ); - } + gl.compileShader( fragmentShader ); + gl.compileShader( vertexShader ); - // console.log( "building new program " ); + gl.attachShader( program, fragmentShader ); + gl.attachShader( program, vertexShader ); - // + gl.linkProgram( program ); - var customDefines = generateDefines( defines ); + return program; - // + } - var program = _gl.createProgram(); +}; - var prefix_vertex, prefix_fragment; +// File:src/renderers/webgl/plugins/ShadowMapPlugin.js - if ( material instanceof THREE.RawShaderMaterial ) { +/** + * @author alteredq / http://alteredqualia.com/ + */ - prefix_vertex = ''; - prefix_fragment = ''; +THREE.ShadowMapPlugin = function ( _renderer, _lights, _webglObjects, _webglObjectsImmediate ) { - } else { + var _gl = _renderer.context; - prefix_vertex = [ + var _depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin, - "precision " + parameters.precision + " float;", - "precision " + parameters.precision + " int;", + _frustum = new THREE.Frustum(), + _projScreenMatrix = new THREE.Matrix4(), - customDefines, + _min = new THREE.Vector3(), + _max = new THREE.Vector3(), - parameters.supportsVertexTextures ? "#define VERTEX_TEXTURES" : "", + _matrixPosition = new THREE.Vector3(), + + _renderList = []; - _this.gammaInput ? "#define GAMMA_INPUT" : "", - _this.gammaOutput ? "#define GAMMA_OUTPUT" : "", + // init - "#define MAX_DIR_LIGHTS " + parameters.maxDirLights, - "#define MAX_POINT_LIGHTS " + parameters.maxPointLights, - "#define MAX_SPOT_LIGHTS " + parameters.maxSpotLights, - "#define MAX_HEMI_LIGHTS " + parameters.maxHemiLights, + var depthShader = THREE.ShaderLib[ "depthRGBA" ]; + var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms ); - "#define MAX_SHADOWS " + parameters.maxShadows, + _depthMaterial = new THREE.ShaderMaterial( { + uniforms: depthUniforms, + vertexShader: depthShader.vertexShader, + fragmentShader: depthShader.fragmentShader + } ); - "#define MAX_BONES " + parameters.maxBones, + _depthMaterialMorph = new THREE.ShaderMaterial( { + uniforms: depthUniforms, + vertexShader: depthShader.vertexShader, + fragmentShader: depthShader.fragmentShader, + morphTargets: true + } ); - parameters.map ? "#define USE_MAP" : "", - parameters.envMap ? "#define USE_ENVMAP" : "", - parameters.lightMap ? "#define USE_LIGHTMAP" : "", - parameters.bumpMap ? "#define USE_BUMPMAP" : "", - parameters.normalMap ? "#define USE_NORMALMAP" : "", - parameters.specularMap ? "#define USE_SPECULARMAP" : "", - parameters.alphaMap ? "#define USE_ALPHAMAP" : "", - parameters.vertexColors ? "#define USE_COLOR" : "", + _depthMaterialSkin = new THREE.ShaderMaterial( { + uniforms: depthUniforms, + vertexShader: depthShader.vertexShader, + fragmentShader: depthShader.fragmentShader, + skinning: true + } ); - parameters.skinning ? "#define USE_SKINNING" : "", - parameters.useVertexTexture ? "#define BONE_TEXTURE" : "", + _depthMaterialMorphSkin = new THREE.ShaderMaterial( { + uniforms: depthUniforms, + vertexShader: depthShader.vertexShader, + fragmentShader: depthShader.fragmentShader, + morphTargets: true, + skinning: true + } ); - parameters.morphTargets ? "#define USE_MORPHTARGETS" : "", - parameters.morphNormals ? "#define USE_MORPHNORMALS" : "", - parameters.wrapAround ? "#define WRAP_AROUND" : "", - parameters.doubleSided ? "#define DOUBLE_SIDED" : "", - parameters.flipSided ? "#define FLIP_SIDED" : "", + _depthMaterial._shadowPass = true; + _depthMaterialMorph._shadowPass = true; + _depthMaterialSkin._shadowPass = true; + _depthMaterialMorphSkin._shadowPass = true; - parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", - parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", - parameters.shadowMapDebug ? "#define SHADOWMAP_DEBUG" : "", - parameters.shadowMapCascade ? "#define SHADOWMAP_CASCADE" : "", + this.render = function ( scene, camera ) { - parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "", + if ( _renderer.shadowMapEnabled === false ) return; - parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", - //_this._glExtensionFragDepth ? "#define USE_LOGDEPTHBUF_EXT" : "", + var i, il, j, jl, n, + shadowMap, shadowMatrix, shadowCamera, + program, buffer, material, + webglObject, object, light, - "uniform mat4 modelMatrix;", - "uniform mat4 modelViewMatrix;", - "uniform mat4 projectionMatrix;", - "uniform mat4 viewMatrix;", - "uniform mat3 normalMatrix;", - "uniform vec3 cameraPosition;", + lights = [], + k = 0, - "attribute vec3 position;", - "attribute vec3 normal;", - "attribute vec2 uv;", - "attribute vec2 uv2;", + fog = null; - "#ifdef USE_COLOR", + // set GL state for depth map - " attribute vec3 color;", + _gl.clearColor( 1, 1, 1, 1 ); + _gl.disable( _gl.BLEND ); - "#endif", + _gl.enable( _gl.CULL_FACE ); + _gl.frontFace( _gl.CCW ); - "#ifdef USE_MORPHTARGETS", + if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) { - " attribute vec3 morphTarget0;", - " attribute vec3 morphTarget1;", - " attribute vec3 morphTarget2;", - " attribute vec3 morphTarget3;", + _gl.cullFace( _gl.FRONT ); - " #ifdef USE_MORPHNORMALS", + } else { - " attribute vec3 morphNormal0;", - " attribute vec3 morphNormal1;", - " attribute vec3 morphNormal2;", - " attribute vec3 morphNormal3;", + _gl.cullFace( _gl.BACK ); - " #else", + } - " attribute vec3 morphTarget4;", - " attribute vec3 morphTarget5;", - " attribute vec3 morphTarget6;", - " attribute vec3 morphTarget7;", + _renderer.setDepthTest( true ); - " #endif", + // preprocess lights + // - skip lights that are not casting shadows + // - create virtual lights for cascaded shadow maps - "#endif", + for ( i = 0, il = _lights.length; i < il; i ++ ) { - "#ifdef USE_SKINNING", + light = _lights[ i ]; - " attribute vec4 skinIndex;", - " attribute vec4 skinWeight;", + if ( ! light.castShadow ) continue; - "#endif", + if ( ( light instanceof THREE.DirectionalLight ) && light.shadowCascade ) { - "" + for ( n = 0; n < light.shadowCascadeCount; n ++ ) { - ].join( '\n' ); + var virtualLight; - prefix_fragment = [ + if ( ! light.shadowCascadeArray[ n ] ) { - "precision " + parameters.precision + " float;", - "precision " + parameters.precision + " int;", + virtualLight = createVirtualLight( light, n ); + virtualLight.originalCamera = camera; - ( parameters.bumpMap || parameters.normalMap ) ? "#extension GL_OES_standard_derivatives : enable" : "", + var gyro = new THREE.Gyroscope(); + gyro.position.copy( light.shadowCascadeOffset ); - customDefines, + gyro.add( virtualLight ); + gyro.add( virtualLight.target ); - "#define MAX_DIR_LIGHTS " + parameters.maxDirLights, - "#define MAX_POINT_LIGHTS " + parameters.maxPointLights, - "#define MAX_SPOT_LIGHTS " + parameters.maxSpotLights, - "#define MAX_HEMI_LIGHTS " + parameters.maxHemiLights, + camera.add( gyro ); - "#define MAX_SHADOWS " + parameters.maxShadows, + light.shadowCascadeArray[ n ] = virtualLight; - parameters.alphaTest ? "#define ALPHATEST " + parameters.alphaTest: "", + console.log( "Created virtualLight", virtualLight ); - _this.gammaInput ? "#define GAMMA_INPUT" : "", - _this.gammaOutput ? "#define GAMMA_OUTPUT" : "", + } else { - ( parameters.useFog && parameters.fog ) ? "#define USE_FOG" : "", - ( parameters.useFog && parameters.fogExp ) ? "#define FOG_EXP2" : "", + virtualLight = light.shadowCascadeArray[ n ]; - parameters.map ? "#define USE_MAP" : "", - parameters.envMap ? "#define USE_ENVMAP" : "", - parameters.lightMap ? "#define USE_LIGHTMAP" : "", - parameters.bumpMap ? "#define USE_BUMPMAP" : "", - parameters.normalMap ? "#define USE_NORMALMAP" : "", - parameters.specularMap ? "#define USE_SPECULARMAP" : "", - parameters.alphaMap ? "#define USE_ALPHAMAP" : "", - parameters.vertexColors ? "#define USE_COLOR" : "", + } - parameters.metal ? "#define METAL" : "", - parameters.wrapAround ? "#define WRAP_AROUND" : "", - parameters.doubleSided ? "#define DOUBLE_SIDED" : "", - parameters.flipSided ? "#define FLIP_SIDED" : "", + updateVirtualLight( light, n ); - parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", - parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", - parameters.shadowMapDebug ? "#define SHADOWMAP_DEBUG" : "", - parameters.shadowMapCascade ? "#define SHADOWMAP_CASCADE" : "", + lights[ k ] = virtualLight; + k ++; - parameters.logarithmicDepthBuffer ? "#define USE_LOGDEPTHBUF" : "", - //_this._glExtensionFragDepth ? "#define USE_LOGDEPTHBUF_EXT" : "", + } - "uniform mat4 viewMatrix;", - "uniform vec3 cameraPosition;", - "" + } else { - ].join( '\n' ); + lights[ k ] = light; + k ++; + + } } - var glVertexShader = new THREE.WebGLShader( _gl, _gl.VERTEX_SHADER, prefix_vertex + vertexShader ); - var glFragmentShader = new THREE.WebGLShader( _gl, _gl.FRAGMENT_SHADER, prefix_fragment + fragmentShader ); + // render depth map - _gl.attachShader( program, glVertexShader ); - _gl.attachShader( program, glFragmentShader ); + for ( i = 0, il = lights.length; i < il; i ++ ) { - if ( index0AttributeName !== undefined ) { + light = lights[ i ]; - // Force a particular attribute to index 0. - // because potentially expensive emulation is done by browser if attribute 0 is disabled. - // And, color, for example is often automatically bound to index 0 so disabling it + if ( ! light.shadowMap ) { - _gl.bindAttribLocation( program, 0, index0AttributeName ); + var shadowFilter = THREE.LinearFilter; - } + if ( _renderer.shadowMapType === THREE.PCFSoftShadowMap ) { - _gl.linkProgram( program ); + shadowFilter = THREE.NearestFilter; - if ( _gl.getProgramParameter( program, _gl.LINK_STATUS ) === false ) { + } - console.error( 'THREE.WebGLProgram: Could not initialise shader.' ); - console.error( 'gl.VALIDATE_STATUS', _gl.getProgramParameter( program, _gl.VALIDATE_STATUS ) ); - console.error( 'gl.getError()', _gl.getError() ); + 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 ); - if ( _gl.getProgramInfoLog( program ) !== '' ) { + light.shadowMatrix = new THREE.Matrix4(); - console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', _gl.getProgramInfoLog( program ) ); + } - } + if ( ! light.shadowCamera ) { - // clean up + if ( light instanceof THREE.SpotLight ) { - _gl.deleteShader( glVertexShader ); - _gl.deleteShader( glFragmentShader ); + light.shadowCamera = new THREE.PerspectiveCamera( light.shadowCameraFov, light.shadowMapWidth / light.shadowMapHeight, light.shadowCameraNear, light.shadowCameraFar ); - // cache uniform locations + } else if ( light instanceof THREE.DirectionalLight ) { - var identifiers = [ + light.shadowCamera = new THREE.OrthographicCamera( light.shadowCameraLeft, light.shadowCameraRight, light.shadowCameraTop, light.shadowCameraBottom, light.shadowCameraNear, light.shadowCameraFar ); - 'viewMatrix', 'modelViewMatrix', 'projectionMatrix', 'normalMatrix', 'modelMatrix', 'cameraPosition', 'morphTargetInfluences', 'bindMatrix', 'bindMatrixInverse' + } else { - ]; + console.error( "Unsupported light type for shadow" ); + continue; - if ( parameters.useVertexTexture ) { + } - identifiers.push( 'boneTexture' ); - identifiers.push( 'boneTextureWidth' ); - identifiers.push( 'boneTextureHeight' ); + scene.add( light.shadowCamera ); - } else { + if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); - identifiers.push( 'boneGlobalMatrices' ); + } - } + if ( light.shadowCameraVisible && ! light.cameraHelper ) { - if ( parameters.logarithmicDepthBuffer ) { + light.cameraHelper = new THREE.CameraHelper( light.shadowCamera ); + scene.add( light.cameraHelper ); - identifiers.push('logDepthBufFC'); + } - } + if ( light.isVirtual && virtualLight.originalCamera == camera ) { + updateShadowCamera( camera, light ); - for ( var u in uniforms ) { + } - identifiers.push( u ); + shadowMap = light.shadowMap; + shadowMatrix = light.shadowMatrix; + shadowCamera = light.shadowCamera; - } + // - this.uniforms = cacheUniformLocations( _gl, program, identifiers ); + shadowCamera.position.setFromMatrixPosition( light.matrixWorld ); + _matrixPosition.setFromMatrixPosition( light.target.matrixWorld ); + shadowCamera.lookAt( _matrixPosition ); + shadowCamera.updateMatrixWorld(); - // cache attributes locations + shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld ); - identifiers = [ + // - "position", "normal", "uv", "uv2", "tangent", "color", - "skinIndex", "skinWeight", "lineDistance" + if ( light.cameraHelper ) light.cameraHelper.visible = light.shadowCameraVisible; + if ( light.shadowCameraVisible ) light.cameraHelper.update(); - ]; + // compute shadow matrix - for ( var i = 0; i < parameters.maxMorphTargets; i ++ ) { + 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 + ); - identifiers.push( "morphTarget" + i ); + shadowMatrix.multiply( shadowCamera.projectionMatrix ); + shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); - } + // update camera matrices and frustum - for ( var i = 0; i < parameters.maxMorphNormals; i ++ ) { + _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); + _frustum.setFromMatrix( _projScreenMatrix ); - identifiers.push( "morphNormal" + i ); + // render shadow map - } + _renderer.setRenderTarget( shadowMap ); + _renderer.clear(); - for ( var a in attributes ) { + // set object matrices & frustum culling - identifiers.push( a ); + _renderList.length = 0; - } + projectObject( scene, scene, shadowCamera ); - this.attributes = cacheAttributeLocations( _gl, program, identifiers ); - // + // render regular objects - this.id = programIdCount ++; - this.code = code; - this.usedTimes = 1; - this.program = program; - this.vertexShader = glVertexShader; - this.fragmentShader = glFragmentShader; + var objectMaterial, useMorphing, useSkinning; - return this; + for ( j = 0, jl = _renderList.length; j < jl; j ++ ) { - }; + webglObject = _renderList[ j ]; -} )(); + object = webglObject.object; + buffer = webglObject.buffer; -// File:src/renderers/webgl/WebGLShader.js + // culling is overriden globally for all objects + // while rendering depth map -THREE.WebGLShader = ( function () { + // 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) - var addLineNumbers = function ( string ) { + objectMaterial = getObjectMaterial( object ); - var lines = string.split( '\n' ); + useMorphing = object.geometry.morphTargets !== undefined && object.geometry.morphTargets.length > 0 && objectMaterial.morphTargets; + useSkinning = object instanceof THREE.SkinnedMesh && objectMaterial.skinning; - for ( var i = 0; i < lines.length; i ++ ) { + if ( object.customDepthMaterial ) { - lines[ i ] = ( i + 1 ) + ': ' + lines[ i ]; + material = object.customDepthMaterial; - } + } else if ( useSkinning ) { - return lines.join( '\n' ); + material = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin; - }; + } else if ( useMorphing ) { - return function ( gl, type, string ) { + material = _depthMaterialMorph; - var shader = gl.createShader( type ); + } else { - gl.shaderSource( shader, string ); - gl.compileShader( shader ); + material = _depthMaterial; - if ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === false ) { + } - console.error( 'THREE.WebGLShader: Shader couldn\'t compile.' ); + _renderer.setMaterialFaces( objectMaterial ); - } + if ( buffer instanceof THREE.BufferGeometry ) { - if ( gl.getShaderInfoLog( shader ) !== '' ) { + _renderer.renderBufferDirect( shadowCamera, _lights, fog, material, buffer, object ); - console.warn( 'THREE.WebGLShader: gl.getShaderInfoLog()', gl.getShaderInfoLog( shader ) ); - console.warn( addLineNumbers( string ) ); + } else { - } + _renderer.renderBuffer( shadowCamera, _lights, fog, material, buffer, object ); - // --enable-privileged-webgl-extension - // console.log( type, gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) ); + } - return shader; + } - }; + // set matrices and render immediate objects -} )(); + for ( j = 0, jl = _webglObjectsImmediate.length; j < jl; j ++ ) { -// File:src/renderers/renderables/RenderableVertex.js + webglObject = _webglObjectsImmediate[ j ]; + object = webglObject.object; -/** - * @author mrdoob / http://mrdoob.com/ - */ + if ( object.visible && object.castShadow ) { -THREE.RenderableVertex = function () { + object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); - this.position = new THREE.Vector3(); - this.positionWorld = new THREE.Vector3(); - this.positionScreen = new THREE.Vector4(); + _renderer.renderImmediateObject( shadowCamera, _lights, fog, _depthMaterial, object ); - this.visible = true; + } -}; + } -THREE.RenderableVertex.prototype.copy = function ( vertex ) { + } - this.positionWorld.copy( vertex.positionWorld ); - this.positionScreen.copy( vertex.positionScreen ); + // restore GL state -}; + var clearColor = _renderer.getClearColor(), + clearAlpha = _renderer.getClearAlpha(); -// File:src/renderers/renderables/RenderableFace.js + _gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha ); + _gl.enable( _gl.BLEND ); -/** - * @author mrdoob / http://mrdoob.com/ - */ + if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) { -THREE.RenderableFace = function () { + _gl.cullFace( _gl.BACK ); - this.id = 0; + } - this.v1 = new THREE.RenderableVertex(); - this.v2 = new THREE.RenderableVertex(); - this.v3 = new THREE.RenderableVertex(); + _renderer.resetGLState(); - this.normalModel = new THREE.Vector3(); + }; - this.vertexNormalsModel = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ]; - this.vertexNormalsLength = 0; + function projectObject( scene, object, shadowCamera ){ - this.color = new THREE.Color(); - this.material = null; - this.uvs = [ new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() ]; + if ( object.visible ) { - this.z = 0; + var webglObjects = _webglObjects[ object.id ]; -}; + if ( webglObjects && object.castShadow && (object.frustumCulled === false || _frustum.intersectsObject( object ) === true) ) { -// File:src/renderers/renderables/RenderableObject.js + for ( var i = 0, l = webglObjects.length; i < l; i ++ ) { -/** - * @author mrdoob / http://mrdoob.com/ - */ + var webglObject = webglObjects[ i ]; -THREE.RenderableObject = function () { + object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); + _renderList.push( webglObject ); - this.id = 0; + } - this.object = null; - this.z = 0; + } -}; + for ( var i = 0, l = object.children.length; i < l; i ++ ) { -// File:src/renderers/renderables/RenderableSprite.js + projectObject( scene, object.children[ i ], shadowCamera ); -/** - * @author mrdoob / http://mrdoob.com/ - */ + } -THREE.RenderableSprite = function () { + } - this.id = 0; + } - this.object = null; + function createVirtualLight( light, cascade ) { - this.x = 0; - this.y = 0; - this.z = 0; + var virtualLight = new THREE.DirectionalLight(); - this.rotation = 0; - this.scale = new THREE.Vector2(); + virtualLight.isVirtual = true; + + virtualLight.onlyShadow = true; + virtualLight.castShadow = true; + + virtualLight.shadowCameraNear = light.shadowCameraNear; + virtualLight.shadowCameraFar = light.shadowCameraFar; + + virtualLight.shadowCameraLeft = light.shadowCameraLeft; + virtualLight.shadowCameraRight = light.shadowCameraRight; + virtualLight.shadowCameraBottom = light.shadowCameraBottom; + virtualLight.shadowCameraTop = light.shadowCameraTop; + + virtualLight.shadowCameraVisible = light.shadowCameraVisible; + + virtualLight.shadowDarkness = light.shadowDarkness; + + 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(); + + } + + 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 ); + + return virtualLight; + + } + + // Synchronize virtual light with the original light + + function updateVirtualLight( light, cascade ) { + + var virtualLight = light.shadowCascadeArray[ cascade ]; + + virtualLight.position.copy( light.position ); + virtualLight.target.position.copy( light.target.position ); + virtualLight.lookAt( virtualLight.target ); + + virtualLight.shadowCameraVisible = light.shadowCameraVisible; + virtualLight.shadowDarkness = light.shadowDarkness; + + virtualLight.shadowBias = light.shadowCascadeBias[ cascade ]; + + var nearZ = light.shadowCascadeNearZ[ cascade ]; + var farZ = light.shadowCascadeFarZ[ cascade ]; + + var pointsFrustum = virtualLight.pointsFrustum; + + 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; + + } + + // Fit shadow camera's ortho frustum to camera frustum + + function updateShadowCamera( camera, light ) { + + var shadowCamera = light.shadowCamera, + pointsFrustum = light.pointsFrustum, + pointsWorld = light.pointsWorld; + + _min.set( Infinity, Infinity, Infinity ); + _max.set( - Infinity, - Infinity, - Infinity ); + + for ( var i = 0; i < 8; i ++ ) { + + var p = pointsWorld[ i ]; + + p.copy( pointsFrustum[ i ] ); + p.unproject( camera ); + + p.applyMatrix4( shadowCamera.matrixWorldInverse ); + + 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; + + if ( p.z < _min.z ) _min.z = p.z; + if ( p.z > _max.z ) _max.z = p.z; + + } + + shadowCamera.left = _min.x; + shadowCamera.right = _max.x; + shadowCamera.top = _max.y; + shadowCamera.bottom = _min.y; + + // can't really fit near/far + //shadowCamera.near = _min.z; + //shadowCamera.far = _max.z; + + 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 - this.material = null; + function getObjectMaterial( object ) { + + return object.material instanceof THREE.MeshFaceMaterial + ? object.material.materials[ 0 ] + : object.material; + + }; }; -// File:src/renderers/renderables/RenderableLine.js +// File:src/renderers/webgl/plugins/SpritePlugin.js /** - * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ */ -THREE.RenderableLine = function () { +THREE.SpritePlugin = function ( renderer, sprites ) { + + var gl = renderer.context; + + var vertexBuffer, elementBuffer; + var program, attributes, uniforms; + + var texture; + + var init = function () { + + 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 + ] ); + + var faces = new Uint16Array( [ + 0, 1, 2, + 0, 2, 3 + ] ); + + vertexBuffer = gl.createBuffer(); + elementBuffer = gl.createBuffer(); + + gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); + gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW ); + + gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); + gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW ); + + program = createProgram(); + + attributes = { + position: gl.getAttribLocation ( program, 'position' ), + uv: gl.getAttribLocation ( program, 'uv' ) + }; + + uniforms = { + uvOffset: gl.getUniformLocation( program, 'uvOffset' ), + uvScale: gl.getUniformLocation( program, 'uvScale' ), + + 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' ), + + modelViewMatrix: gl.getUniformLocation( program, 'modelViewMatrix' ), + projectionMatrix: gl.getUniformLocation( program, 'projectionMatrix' ), + + 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; + + var context = canvas.getContext( '2d' ); + context.fillStyle = 'white'; + context.fillRect( 0, 0, 8, 8 ); + + texture = new THREE.Texture( canvas ); + texture.needsUpdate = true; + + }; + + this.render = function ( scene, camera ) { + + if ( sprites.length === 0 ) return; + + // setup gl + + if ( program === undefined ) { + + init(); + + } + + gl.useProgram( program ); + + gl.enableVertexAttribArray( attributes.position ); + gl.enableVertexAttribArray( attributes.uv ); + + gl.disable( gl.CULL_FACE ); + gl.enable( gl.BLEND ); + + 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 ); + + gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); + + gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); + + gl.activeTexture( gl.TEXTURE0 ); + gl.uniform1i( uniforms.map, 0 ); + + var oldFogType = 0; + var sceneFogType = 0; + var fog = scene.fog; + + if ( fog ) { + + gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b ); + + 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; + + } else if ( fog instanceof THREE.FogExp2 ) { + + gl.uniform1f( uniforms.fogDensity, fog.density ); + + gl.uniform1i( uniforms.fogType, 2 ); + oldFogType = 2; + sceneFogType = 2; + + } + + } else { + + gl.uniform1i( uniforms.fogType, 0 ); + oldFogType = 0; + sceneFogType = 0; + + } + - this.id = 0; + // update positions and sort + + for ( var i = 0, l = sprites.length; i < l; i ++ ) { + + var sprite = sprites[ i ]; + + sprite._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld ); + + if ( sprite.renderDepth === null ) { + + sprite.z = - sprite._modelViewMatrix.elements[ 14 ]; + + } else { + + sprite.z = sprite.renderDepth; + + } + + } + + sprites.sort( painterSortStable ); + + // render all sprites + + var scale = []; + + for ( var i = 0, l = sprites.length; i < l; i ++ ) { + + var sprite = sprites[ i ]; + var material = sprite.material; + + 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; + + if ( scene.fog && material.fog ) { + + fogType = sceneFogType; + + } + + if ( oldFogType !== fogType ) { + + gl.uniform1i( uniforms.fogType, fogType ); + oldFogType = fogType; + + } + + 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 ); + + } else { + + gl.uniform2f( uniforms.uvOffset, 0, 0 ); + gl.uniform2f( uniforms.uvScale, 1, 1 ); + + } + + gl.uniform1f( uniforms.opacity, material.opacity ); + gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b ); + + gl.uniform1f( uniforms.rotation, material.rotation ); + gl.uniform2fv( uniforms.scale, scale ); + + renderer.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); + renderer.setDepthTest( material.depthTest ); + renderer.setDepthWrite( material.depthWrite ); + + if ( material.map && material.map.image && material.map.image.width ) { + + renderer.setTexture( material.map, 0 ); + + } else { + + renderer.setTexture( texture, 0 ); + + } + + gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); + + } + + // restore gl + + gl.enable( gl.CULL_FACE ); + + renderer.resetGLState(); + + }; + + function createProgram () { + + var program = gl.createProgram(); + + var vertexShader = gl.createShader( gl.VERTEX_SHADER ); + var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER ); + + gl.shaderSource( vertexShader, [ + + 'precision ' + renderer.getPrecision() + ' float;', + + 'uniform mat4 modelViewMatrix;', + 'uniform mat4 projectionMatrix;', + 'uniform float rotation;', + 'uniform vec2 scale;', + 'uniform vec2 uvOffset;', + 'uniform vec2 uvScale;', + + 'attribute vec2 position;', + 'attribute vec2 uv;', + + 'varying vec2 vUV;', + + 'void main() {', + + 'vUV = uvOffset + uv * uvScale;', + + 'vec2 alignedPosition = position * scale;', + + 'vec2 rotatedPosition;', + 'rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;', + 'rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;', + + 'vec4 finalPosition;', + + 'finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );', + 'finalPosition.xy += rotatedPosition;', + 'finalPosition = projectionMatrix * finalPosition;', + + 'gl_Position = finalPosition;', + + '}' + + ].join( '\n' ) ); + + gl.shaderSource( fragmentShader, [ + + 'precision ' + renderer.getPrecision() + ' float;', + + 'uniform vec3 color;', + 'uniform sampler2D map;', + 'uniform float opacity;', + + 'uniform int fogType;', + 'uniform vec3 fogColor;', + 'uniform float fogDensity;', + 'uniform float fogNear;', + 'uniform float fogFar;', + 'uniform float alphaTest;', + + 'varying vec2 vUV;', - this.v1 = new THREE.RenderableVertex(); - this.v2 = new THREE.RenderableVertex(); + 'void main() {', + + 'vec4 texture = texture2D( map, vUV );', + + 'if ( texture.a < alphaTest ) discard;', + + '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 );', + + '} else {', + + 'const float LOG2 = 1.442695;', + 'float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );', + 'fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );', + + '}', + + 'gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );', + + '}', + + '}' + + ].join( '\n' ) ); - this.vertexColors = [ new THREE.Color(), new THREE.Color() ]; - this.material = null; + gl.compileShader( vertexShader ); + gl.compileShader( fragmentShader ); - this.z = 0; + gl.attachShader( program, vertexShader ); + gl.attachShader( program, fragmentShader ); + + gl.linkProgram( program ); + + return program; + + }; + + function painterSortStable ( a, b ) { + + if ( a.z !== b.z ) { + + return b.z - a.z; + + } else { + + return b.id - a.id; + + } + + }; }; @@ -26561,107 +26860,240 @@ THREE.FontUtils.generateShapes = function ( text, parameters ) { self._typeface_js = { faces: THREE.FontUtils.faces, loadFace: THREE.FontUtils.loadFace }; THREE.typeface_js = self._typeface_js; -// File:src/extras/core/Curve.js +// File:src/extras/audio/Audio.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 - * - **/ + * @author mrdoob / http://mrdoob.com/ + */ -/************************************************************** - * Abstract Curve base class - **************************************************************/ +THREE.Audio = function ( listener ) { -THREE.Curve = function () { + THREE.Object3D.call( this ); -}; + this.type = 'Audio'; -// Virtual base class method to overwrite and implement in subclasses -// - t [0 .. 1] + this.context = listener.context; + this.source = this.context.createBufferSource(); -THREE.Curve.prototype.getPoint = function ( t ) { + this.gain = this.context.createGain(); + this.gain.connect( this.context.destination ); - console.log( "Warning, getPoint() not implemented!" ); - return null; + this.panner = this.context.createPanner(); + this.panner.connect( this.gain ); }; -// Get point at relative position in curve according to arc length -// - u [0 .. 1] +THREE.Audio.prototype = Object.create( THREE.Object3D.prototype ); -THREE.Curve.prototype.getPointAt = function ( u ) { +THREE.Audio.prototype.load = function ( file ) { - var t = this.getUtoTmapping( u ); - return this.getPoint( t ); + var scope = this; -}; + var request = new XMLHttpRequest(); + request.open( 'GET', file, true ); + request.responseType = 'arraybuffer'; + request.onload = function ( e ) { -// Get sequence of points using getPoint( t ) + scope.context.decodeAudioData( this.response, function ( buffer ) { -THREE.Curve.prototype.getPoints = function ( divisions ) { + scope.source.buffer = buffer; + scope.source.connect( scope.panner ); + scope.source.start( 0 ); - if ( ! divisions ) divisions = 5; + } ); - var d, pts = []; + }; + request.send(); - for ( d = 0; d <= divisions; d ++ ) { + return this; - pts.push( this.getPoint( d / divisions ) ); +}; - } +THREE.Audio.prototype.setLoop = function ( value ) { - return pts; + this.source.loop = value; }; -// Get sequence of points using getPointAt( u ) +THREE.Audio.prototype.setRefDistance = function ( value ) { -THREE.Curve.prototype.getSpacedPoints = function ( divisions ) { + this.panner.refDistance = value; - if ( ! divisions ) divisions = 5; +}; - var d, pts = []; +THREE.Audio.prototype.setRolloffFactor = function ( value ) { - for ( d = 0; d <= divisions; d ++ ) { + this.panner.rolloffFactor = value; - pts.push( this.getPointAt( d / divisions ) ); +}; - } +THREE.Audio.prototype.updateMatrixWorld = ( function () { - return pts; + var position = new THREE.Vector3(); -}; + return function ( force ) { -// Get total curve arc length + THREE.Object3D.prototype.updateMatrixWorld.call( this, force ); -THREE.Curve.prototype.getLength = function () { + position.setFromMatrixPosition( this.matrixWorld ); + + this.panner.setPosition( position.x, position.y, position.z ); + + }; + +} )(); + +// File:src/extras/audio/AudioListener.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.AudioListener = function () { + + THREE.Object3D.call( this ); + + this.type = 'AudioListener'; + + this.context = new ( window.AudioContext || window.webkitAudioContext )(); + +}; + +THREE.AudioListener.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.AudioListener.prototype.updateMatrixWorld = ( function () { + + var position = new THREE.Vector3(); + var quaternion = new THREE.Quaternion(); + var scale = new THREE.Vector3(); + + var orientation = new THREE.Vector3(); + var velocity = new THREE.Vector3(); + + var positionPrev = new THREE.Vector3(); + + return function ( force ) { + + 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 () { + +}; + +// Virtual base class method to overwrite and implement in subclasses +// - t [0 .. 1] + +THREE.Curve.prototype.getPoint = function ( t ) { + + console.log( "Warning, getPoint() not implemented!" ); + return null; + +}; + +// Get point at relative position in curve according to arc length +// - u [0 .. 1] + +THREE.Curve.prototype.getPointAt = function ( u ) { + + var t = this.getUtoTmapping( u ); + return this.getPoint( t ); + +}; + +// Get sequence of points using getPoint( t ) + +THREE.Curve.prototype.getPoints = function ( divisions ) { + + if ( ! divisions ) divisions = 5; + + var d, pts = []; + + for ( d = 0; d <= divisions; d ++ ) { + + pts.push( this.getPoint( d / divisions ) ); + + } + + return pts; + +}; + +// 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 ) ); + + } + + return pts; + +}; + +// Get total curve arc length + +THREE.Curve.prototype.getLength = function () { var lengths = this.getLengths(); return lengths[ lengths.length - 1 ]; @@ -26851,8 +27283,8 @@ THREE.Curve.Utils = { 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 ) { @@ -27241,54 +27673,58 @@ THREE.Gyroscope = function () { THREE.Gyroscope.prototype = Object.create( THREE.Object3D.prototype ); -THREE.Gyroscope.prototype.updateMatrixWorld = function ( force ) { +THREE.Gyroscope.prototype.updateMatrixWorld = ( function () { - this.matrixAutoUpdate && this.updateMatrix(); + var translationObject = new THREE.Vector3(); + var quaternionObject = new THREE.Quaternion(); + var scaleObject = new THREE.Vector3(); - // update matrixWorld + var translationWorld = new THREE.Vector3(); + var quaternionWorld = new THREE.Quaternion(); + var scaleWorld = new THREE.Vector3(); - if ( this.matrixWorldNeedsUpdate || force ) { + return function ( force ) { - if ( this.parent ) { + this.matrixAutoUpdate && this.updateMatrix(); - this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); + // update matrixWorld - this.matrixWorld.decompose( this.translationWorld, this.quaternionWorld, this.scaleWorld ); - this.matrix.decompose( this.translationObject, this.quaternionObject, this.scaleObject ); + if ( this.matrixWorldNeedsUpdate || force ) { - this.matrixWorld.compose( this.translationWorld, this.quaternionObject, this.scaleWorld ); + if ( this.parent ) { + this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); - } else { + this.matrixWorld.decompose( translationWorld, quaternionWorld, scaleWorld ); + this.matrix.decompose( translationObject, quaternionObject, scaleObject ); - this.matrixWorld.copy( this.matrix ); + this.matrixWorld.compose( translationWorld, quaternionObject, scaleWorld ); - } + } else { + + this.matrixWorld.copy( this.matrix ); - this.matrixWorldNeedsUpdate = false; + } - force = true; - } + this.matrixWorldNeedsUpdate = false; - // update children + force = true; - for ( var i = 0, l = this.children.length; i < l; i ++ ) { + } - this.children[ i ].updateMatrixWorld( force ); + // 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(); + } + }; + +}() ); // File:src/extras/core/Path.js @@ -28607,29 +29043,26 @@ THREE.QuadraticBezierCurve.prototype = Object.create( THREE.Curve.prototype ); THREE.QuadraticBezierCurve.prototype.getPoint = function ( t ) { - var tx, ty; + var vector = new THREE.Vector2(); - 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 ); + 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 ); - return new THREE.Vector2( tx, ty ); + return vector; }; THREE.QuadraticBezierCurve.prototype.getTangent = function( t ) { - var tx, ty; + var vector = new THREE.Vector2(); - 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 ); + 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 ); // returns unit vector - var tangent = new THREE.Vector2( tx, ty ); - tangent.normalize(); - - return tangent; + return vector.normalize(); }; @@ -28683,7 +29116,7 @@ THREE.CubicBezierCurve.prototype.getTangent = function( t ) { THREE.SplineCurve = function ( points /* array of Vector2 */ ) { - this.points = (points == undefined) ? [] : points; + this.points = ( points == undefined ) ? [] : points; }; @@ -28691,23 +29124,23 @@ THREE.SplineCurve.prototype = Object.create( THREE.Curve.prototype ); 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; + var points = this.points; + var point = ( points.length - 1 ) * t; + + var intPoint = Math.floor( point ); + var weight = point - intPoint; - intPoint = Math.floor( point ); - weight = point - intPoint; + 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 ] - 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 vector = new THREE.Vector2(); - 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 ); + 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 ); - return v; + return vector; }; @@ -28736,12 +29169,13 @@ THREE.EllipseCurve.prototype = Object.create( THREE.Curve.prototype ); THREE.EllipseCurve.prototype.getPoint = function ( t ) { - var angle; var deltaAngle = this.aEndAngle - this.aStartAngle; if ( deltaAngle < 0 ) deltaAngle += Math.PI * 2; if ( deltaAngle > Math.PI * 2 ) deltaAngle -= Math.PI * 2; + var angle; + if ( this.aClockwise === true ) { angle = this.aEndAngle + ( 1 - t ) * ( Math.PI * 2 - deltaAngle ); @@ -28751,11 +29185,13 @@ THREE.EllipseCurve.prototype.getPoint = function ( t ) { angle = this.aStartAngle + t * deltaAngle; } + + var vector = new THREE.Vector2(); - var tx = this.aX + this.xRadius * Math.cos( angle ); - var ty = this.aY + this.yRadius * Math.sin( angle ); + vector.x = this.aX + this.xRadius * Math.cos( angle ); + vector.y = this.aY + this.yRadius * Math.sin( angle ); - return new THREE.Vector2( tx, ty ); + return vector; }; @@ -28789,14 +29225,13 @@ THREE.LineCurve3 = THREE.Curve.create( function ( t ) { - var r = new THREE.Vector3(); - + var vector = new THREE.Vector3(); - r.subVectors( this.v2, this.v1 ); // diff - r.multiplyScalar( t ); - r.add( this.v1 ); + vector.subVectors( this.v2, this.v1 ); // diff + vector.multiplyScalar( t ); + vector.add( this.v1 ); - return r; + return vector; } @@ -28820,13 +29255,13 @@ THREE.QuadraticBezierCurve3 = THREE.Curve.create( function ( t ) { - var tx, ty, tz; + var vector = new THREE.Vector3(); - 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 ); + 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 ); - return new THREE.Vector3( tx, ty, tz ); + return vector; } @@ -28851,13 +29286,13 @@ THREE.CubicBezierCurve3 = THREE.Curve.create( function ( t ) { - var tx, ty, tz; + var vector = new THREE.Vector3(); - 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 ); + 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 new THREE.Vector3( tx, ty, tz ); + return vector; } @@ -28874,69 +29309,35 @@ THREE.SplineCurve3 = THREE.Curve.create( function ( points /* array of Vector3 */) { - this.points = (points == undefined) ? [] : points; + this.points = ( points == undefined ) ? [] : points; }, function ( t ) { - var v = new THREE.Vector3(); - var c = []; - var points = this.points, point, intPoint, weight; - point = ( points.length - 1 ) * t; + var points = this.points; + var point = ( points.length - 1 ) * t; - intPoint = Math.floor( point ); - weight = point - intPoint; + var intPoint = Math.floor( point ); + var weight = point - intPoint; - 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 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 pt0 = points[ c[0] ], - pt1 = points[ c[1] ], - pt2 = points[ c[2] ], - pt3 = points[ c[3] ]; + var vector = new THREE.Vector3(); - 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); + 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 v; + return vector; } ); - -// 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; - -// 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 pt0 = points[ c[0] ], -// pt1 = points[ c[1] ], -// pt2 = points[ c[2] ], -// pt3 = points[ c[3] ]; - -// // 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 ); - -// return v; - -// } - // File:src/extras/curves/ClosedSplineCurve3.js /************************************************************** @@ -28948,34 +29349,34 @@ THREE.ClosedSplineCurve3 = THREE.Curve.create( function ( points /* array of Vector3 */) { - this.points = (points == undefined) ? [] : points; + this.points = ( points == undefined ) ? [] : points; }, - function ( t ) { + function ( t ) { + + var points = this.points; + var point = ( points.length - 0 ) * t; // This needs to be from 0-length +1 - 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 + var intPoint = Math.floor( point ); + var weight = point - intPoint; - intPoint = Math.floor( point ); - weight = point - intPoint; + intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length; - 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; + 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 ]; - 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 ); + var vector = new THREE.Vector3(); + + 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 v; + return vector; - } + } ); @@ -29174,6 +29575,12 @@ THREE.AnimationHandler = { update: function ( deltaTimeMS ) { + for ( var i = 0; i < this.animations.length; i ++ ) { + + this.animations[ i ].resetBlendWeights( ); + + } + for ( var i = 0; i < this.animations.length; i ++ ) { this.animations[ i ].update( deltaTimeMS ); @@ -29245,20 +29652,26 @@ THREE.Animation.prototype.reset = function () { if ( object.animationCache === undefined ) { - object.animationCache = {}; - + object.animationCache = { + animations: {}, + blending: { + positionWeight: 0.0, + quaternionWeight: 0.0, + scaleWeight: 0.0 + } + }; } - if ( object.animationCache[this.data.name] === undefined ) { + if ( object.animationCache.animations[this.data.name] === undefined ) { - 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; + 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; } - var animationCache = object.animationCache[this.data.name]; + var animationCache = object.animationCache.animations[this.data.name]; // Get keys to match our current time @@ -29285,6 +29698,23 @@ THREE.Animation.prototype.reset = function () { }; +THREE.Animation.prototype.resetBlendWeights = function () { + + for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { + + var object = this.hierarchy[ h ]; + + if ( object.animationCache !== undefined ) { + + object.animationCache.blending.positionWeight = 0.0; + object.animationCache.blending.quaternionWeight = 0.0; + object.animationCache.blending.scaleWeight = 0.0; + + } + + } + +}; THREE.Animation.prototype.update = (function(){ @@ -29348,22 +29778,31 @@ THREE.Animation.prototype.update = (function(){ var duration = this.data.length; - if ( this.loop === true && this.currentTime > duration ) { + if ( this.currentTime > duration || this.currentTime < 0 ) { - this.currentTime %= duration; - this.reset(); + if ( this.loop ) { - } else if ( this.loop === false && this.currentTime > duration ) { + this.currentTime %= duration; - this.stop(); - return; + if ( this.currentTime < 0 ) + this.currentTime += duration; + + this.reset(); + + } else { + + this.stop(); + return; + + } } for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { var object = this.hierarchy[ h ]; - var animationCache = object.animationCache[this.data.name]; + var animationCache = object.animationCache.animations[this.data.name]; + var blending = object.animationCache.blending; // loop through pos/rot/scl @@ -29375,7 +29814,8 @@ THREE.Animation.prototype.update = (function(){ var prevKey = animationCache.prevKey[ type ]; var nextKey = animationCache.nextKey[ type ]; - if ( nextKey.time <= this.currentTime ) { + if ( ( this.timeScale > 0 && nextKey.time <= this.currentTime ) || + ( this.timeScale < 0 && prevKey.time >= this.currentTime ) ) { prevKey = this.data.hierarchy[ h ].keys[ 0 ]; nextKey = this.getNextKeyWith( type, h, 1 ); @@ -29414,17 +29854,9 @@ THREE.Animation.prototype.update = (function(){ newVector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale; // blend - if ( object instanceof THREE.Bone ) { - - var proportionalWeight = this.weight / ( this.weight + object.accumulatedPosWeight ); - object.position.lerp( newVector, proportionalWeight ); - object.accumulatedPosWeight += this.weight; - - } else { - - object.position.copy( newVector ); - - } + var proportionalWeight = this.weight / ( this.weight + blending.positionWeight ); + object.position.lerp( newVector, proportionalWeight ); + blending.positionWeight += this.weight; } else if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { @@ -29437,14 +29869,8 @@ THREE.Animation.prototype.update = (function(){ scale = scale * 0.33 + 0.33; var currentPoint = interpolateCatmullRom( points, scale ); - var proportionalWeight = 1; - - if ( object instanceof THREE.Bone ) { - - proportionalWeight = this.weight / ( this.weight + object.accumulatedPosWeight ); - object.accumulatedPosWeight += this.weight; - - } + var proportionalWeight = this.weight / ( this.weight + blending.positionWeight ); + blending.positionWeight += this.weight; // blend @@ -29475,20 +29901,16 @@ THREE.Animation.prototype.update = (function(){ THREE.Quaternion.slerp( prevXYZ, nextXYZ, newQuat, scale ); // Avoid paying the cost of an additional slerp if we don't have to - if ( ! ( object instanceof THREE.Bone ) ) { - - object.quaternion.copy(newQuat); - - } else if ( object.accumulatedRotWeight === 0 ) { + if ( blending.quaternionWeight === 0 ) { object.quaternion.copy(newQuat); - object.accumulatedRotWeight = this.weight; + blending.quaternionWeight = this.weight; } else { - var proportionalWeight = this.weight / ( this.weight + object.accumulatedRotWeight ); + var proportionalWeight = this.weight / ( this.weight + blending.quaternionWeight ); THREE.Quaternion.slerp( object.quaternion, newQuat, object.quaternion, proportionalWeight ); - object.accumulatedRotWeight += this.weight; + blending.quaternionWeight += this.weight; } @@ -29498,17 +29920,9 @@ THREE.Animation.prototype.update = (function(){ newVector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale; newVector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale; - if ( object instanceof THREE.Bone ) { - - var proportionalWeight = this.weight / ( this.weight + object.accumulatedSclWeight); - object.scale.lerp( newVector, proportionalWeight ); - object.accumulatedSclWeight += this.weight; - - } else { - - object.scale.copy( newVector ); - - } + var proportionalWeight = this.weight / ( this.weight + blending.scaleWeight ); + object.scale.lerp( newVector, proportionalWeight ); + blending.scaleWeight += this.weight; } @@ -29921,6 +30335,8 @@ THREE.BoxGeometry = function ( width, height, depth, widthSegments, heightSegmen THREE.Geometry.call( this ); + this.type = 'BoxGeometry'; + this.parameters = { width: width, height: height, @@ -30047,6 +30463,8 @@ THREE.CircleGeometry = function ( radius, segments, thetaStart, thetaLength ) { THREE.Geometry.call( this ); + this.type = 'CircleGeometry'; + this.parameters = { radius: radius, segments: segments, @@ -30105,7 +30523,7 @@ THREE.CircleGeometry.prototype = Object.create( THREE.Geometry.prototype ); THREE.CubeGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) { - console.warn( 'THEE.CubeGeometry has been renamed to THREE.BoxGeometry.' ); + console.warn( 'THREE.CubeGeometry has been renamed to THREE.BoxGeometry.' ); return new THREE.BoxGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ); }; @@ -30120,6 +30538,8 @@ THREE.CylinderGeometry = function ( radiusTop, radiusBottom, height, radialSegme THREE.Geometry.call( this ); + this.type = 'CylinderGeometry'; + this.parameters = { radiusTop: radiusTop, radiusBottom: radiusBottom, @@ -30315,6 +30735,8 @@ THREE.ExtrudeGeometry = function ( shapes, options ) { THREE.Geometry.call( this ); + this.type = 'ExtrudeGeometry'; + shapes = shapes instanceof Array ? shapes : [ shapes ]; this.addShapeList( shapes, options ); @@ -30473,7 +30895,6 @@ THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { function getBevelVec( inPt, inPrev, inNext ) { 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 @@ -30539,7 +30960,7 @@ THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { 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 ( Math.sign(v_prev_y) == Math.sign(v_next_y) ) { direction_eq = true; } } } @@ -30776,7 +31197,7 @@ THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { for ( i = 0; i < flen; i ++ ) { face = faces[ i ]; - f3( face[ 2 ]+ offset, face[ 1 ]+ offset, face[ 0 ] + offset, true ); + f3( face[ 2 ]+ offset, face[ 1 ]+ offset, face[ 0 ] + offset ); } @@ -30788,7 +31209,7 @@ THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { for ( i = 0; i < flen; i ++ ) { face = faces[ i ]; - f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset, false ); + f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset ); } @@ -30799,7 +31220,7 @@ THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { for ( i = 0; i < flen; i ++ ) { face = faces[ i ]; - f3( face[ 2 ], face[ 1 ], face[ 0 ], true ); + f3( face[ 2 ], face[ 1 ], face[ 0 ] ); } @@ -30808,7 +31229,7 @@ THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { for ( i = 0; i < flen; i ++ ) { face = faces[ i ]; - f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps, false ); + f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); } } @@ -30874,7 +31295,7 @@ THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { } - function f3( a, b, c, isBottom ) { + function f3( a, b, c ) { a += shapesOffset; b += shapesOffset; @@ -30883,7 +31304,7 @@ THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { // normal, color, material scope.faces.push( new THREE.Face3( a, b, c, null, null, material ) ); - var uvs = isBottom ? uvgen.generateBottomUV( scope, shape, options, a, b, c ) : uvgen.generateTopUV( scope, shape, options, a, b, c ); + var uvs = uvgen.generateTopUV( scope, a, b, c ); scope.faceVertexUvs[ 0 ].push( uvs ); @@ -30899,8 +31320,7 @@ THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { 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 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 ] ] ); @@ -30911,75 +31331,49 @@ THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { THREE.ExtrudeGeometry.WorldUVGenerator = { - generateTopUV: function( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC ) { - var ax = geometry.vertices[ indexA ].x, - ay = geometry.vertices[ indexA ].y, + generateTopUV: function ( geometry, indexA, indexB, indexC ) { - bx = geometry.vertices[ indexB ].x, - by = geometry.vertices[ indexB ].y, + var vertices = geometry.vertices; - cx = geometry.vertices[ indexC ].x, - cy = geometry.vertices[ indexC ].y; + var a = vertices[ indexA ]; + var b = vertices[ indexB ]; + var c = vertices[ indexC ]; return [ - new THREE.Vector2( ax, ay ), - new THREE.Vector2( bx, by ), - new THREE.Vector2( cx, cy ) + new THREE.Vector2( a.x, a.y ), + new THREE.Vector2( b.x, b.y ), + new THREE.Vector2( c.x, c.y ) ]; }, - generateBottomUV: function( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC ) { - - return this.generateTopUV( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC ); - - }, - - generateSideWallUV: function( geometry, extrudedShape, wallContour, extrudeOptions, - indexA, indexB, indexC, indexD, stepIndex, stepsLength, - contourIndex1, contourIndex2 ) { - - var ax = geometry.vertices[ indexA ].x, - ay = geometry.vertices[ indexA ].y, - az = geometry.vertices[ indexA ].z, + generateSideWallUV: function ( geometry, indexA, indexB, indexC, indexD ) { - bx = geometry.vertices[ indexB ].x, - by = geometry.vertices[ indexB ].y, - bz = geometry.vertices[ indexB ].z, - - cx = geometry.vertices[ indexC ].x, - cy = geometry.vertices[ indexC ].y, - cz = geometry.vertices[ indexC ].z, + var vertices = geometry.vertices; - dx = geometry.vertices[ indexD ].x, - dy = geometry.vertices[ indexD ].y, - dz = geometry.vertices[ indexD ].z; + var a = vertices[ indexA ]; + var b = vertices[ indexB ]; + var c = vertices[ indexC ]; + var d = vertices[ indexD ]; - if ( Math.abs( ay - by ) < 0.01 ) { + if ( Math.abs( a.y - b.y ) < 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 ) + 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( ay, 1 - az ), - new THREE.Vector2( by, 1 - bz ), - new THREE.Vector2( cy, 1 - cz ), - new THREE.Vector2( dy, 1 - dz ) + 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.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(); - // File:src/extras/geometries/ShapeGeometry.js /** @@ -31002,6 +31396,8 @@ THREE.ShapeGeometry = function ( shapes, options ) { THREE.Geometry.call( this ); + this.type = 'ShapeGeometry'; + if ( shapes instanceof Array === false ) shapes = [ shapes ]; this.addShapeList( shapes, options ); @@ -31108,7 +31504,7 @@ THREE.ShapeGeometry.prototype.addShape = function ( shape, options ) { var c = face[ 2 ] + shapesOffset; this.faces.push( new THREE.Face3( a, b, c, null, null, material ) ); - this.faceVertexUvs[ 0 ].push( uvgen.generateBottomUV( this, shape, options, a, b, c ) ); + this.faceVertexUvs[ 0 ].push( uvgen.generateTopUV( this, a, b, c ) ); } @@ -31128,10 +31524,20 @@ THREE.ShapeGeometry.prototype.addShape = function ( shape, options ) { // 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 ); + this.type = 'LatheGeometry'; + + this.parameters = { + points: points, + segments: segments, + phiStart: phiStart, + phiLength: phiLength + }; + segments = segments || 12; phiStart = phiStart || 0; phiLength = phiLength || 2 * Math.PI; @@ -31221,8 +31627,38 @@ THREE.LatheGeometry.prototype = Object.create( THREE.Geometry.prototype ); THREE.PlaneGeometry = function ( width, height, widthSegments, heightSegments ) { + console.info( 'THREE.PlaneGeometry: Consider using THREE.PlaneBufferGeometry for lower memory footprint.' ); + 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 ) ); + +}; + +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.PlaneBufferGeometry = function ( width, height, widthSegments, heightSegments ) { + + THREE.BufferGeometry.call( this ); + + this.type = 'PlaneBufferGeometry'; + this.parameters = { width: width, height: height, @@ -31230,70 +31666,83 @@ THREE.PlaneGeometry = function ( width, height, widthSegments, heightSegments ) heightSegments: heightSegments }; - var ix, iz; var width_half = width / 2; var height_half = height / 2; var gridX = widthSegments || 1; - var gridZ = heightSegments || 1; + var gridY = heightSegments || 1; var gridX1 = gridX + 1; - var gridZ1 = gridZ + 1; + var gridY1 = gridY + 1; var segment_width = width / gridX; - var segment_height = height / gridZ; + var segment_height = height / gridY; - var normal = new THREE.Vector3( 0, 0, 1 ); + var vertices = new Float32Array( gridX1 * gridY1 * 3 ); + var normals = new Float32Array( gridX1 * gridY1 * 3 ); + var uvs = new Float32Array( gridX1 * gridY1 * 2 ); - for ( iz = 0; iz < gridZ1; iz ++ ) { + var offset = 0; + var offset2 = 0; - var y = iz * segment_height - height_half; + for ( var iy = 0; iy < gridY1; iy ++ ) { - for ( ix = 0; ix < gridX1; ix ++ ) { + var y = iy * segment_height - height_half; + + for ( var ix = 0; ix < gridX1; ix ++ ) { var x = ix * segment_width - width_half; - this.vertices.push( new THREE.Vector3( x, - y, 0 ) ); + vertices[ offset ] = x; + vertices[ offset + 1 ] = - y; + + normals[ offset + 2 ] = 1; + + uvs[ offset2 ] = ix / gridX; + uvs[ offset2 + 1 ] = 1 - ( iy / gridY ); + + offset += 3; + offset2 += 2; } } - for ( iz = 0; iz < gridZ; iz ++ ) { + offset = 0; - for ( ix = 0; ix < gridX; ix ++ ) { + var indices = new ( ( vertices.length / 3 ) > 65535 ? Uint32Array : Uint16Array )( gridX * gridY * 6 ); - var a = ix + gridX1 * iz; - var b = ix + gridX1 * ( iz + 1 ); - var c = ( ix + 1 ) + gridX1 * ( iz + 1 ); - var d = ( ix + 1 ) + gridX1 * iz; + for ( var iy = 0; iy < gridY; iy ++ ) { - 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 ); + for ( var ix = 0; ix < gridX; ix ++ ) { - var face = new THREE.Face3( a, b, d ); - face.normal.copy( normal ); - face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() ); + var a = ix + gridX1 * iy; + var b = ix + gridX1 * ( iy + 1 ); + var c = ( ix + 1 ) + gridX1 * ( iy + 1 ); + var d = ( ix + 1 ) + gridX1 * iy; - this.faces.push( face ); - this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); + indices[ offset ] = a; + indices[ offset + 1 ] = b; + indices[ offset + 2 ] = d; - face = new THREE.Face3( b, c, d ); - face.normal.copy( normal ); - face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() ); + indices[ offset + 3 ] = b; + indices[ offset + 4 ] = c; + indices[ offset + 5 ] = d; - this.faces.push( face ); - this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); + offset += 6; } } + 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 ) ); + }; -THREE.PlaneGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.PlaneBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); // File:src/extras/geometries/RingGeometry.js @@ -31305,6 +31754,17 @@ THREE.RingGeometry = function ( innerRadius, outerRadius, thetaSegments, phiSegm THREE.Geometry.call( this ); + this.type = 'RingGeometry'; + + this.parameters = { + innerRadius: innerRadius, + outerRadius: outerRadius, + thetaSegments: thetaSegments, + phiSegments: phiSegments, + thetaStart: thetaStart, + thetaLength: thetaLength + }; + innerRadius = innerRadius || 0; outerRadius = outerRadius || 50; @@ -31379,6 +31839,8 @@ THREE.SphereGeometry = function ( radius, widthSegments, heightSegments, phiStar THREE.Geometry.call( this ); + this.type = 'SphereGeometry'; + this.parameters = { radius: radius, widthSegments: widthSegments, @@ -31540,6 +32002,8 @@ THREE.TextGeometry = function ( text, parameters ) { THREE.ExtrudeGeometry.call( this, textShapes, parameters ); + this.type = 'TextGeometry'; + }; THREE.TextGeometry.prototype = Object.create( THREE.ExtrudeGeometry.prototype ); @@ -31556,6 +32020,8 @@ THREE.TorusGeometry = function ( radius, tube, radialSegments, tubularSegments, THREE.Geometry.call( this ); + this.type = 'TorusGeometry'; + this.parameters = { radius: radius, tube: tube, @@ -31634,6 +32100,8 @@ THREE.TorusKnotGeometry = function ( radius, tube, radialSegments, tubularSegmen THREE.Geometry.call( this ); + this.type = 'TorusKnotGeometry'; + this.parameters = { radius: radius, tube: tube, @@ -31755,6 +32223,8 @@ THREE.TubeGeometry = function ( path, segments, radius, radialSegments, closed ) THREE.Geometry.call( this ); + this.type = 'TubeGeometry'; + this.parameters = { path: path, segments: segments, @@ -32031,6 +32501,15 @@ THREE.PolyhedronGeometry = function ( vertices, indices, radius, detail ) { THREE.Geometry.call( this ); + this.type = 'PolyhedronGeometry'; + + this.parameters = { + vertices: vertices, + indices: indices, + radius: radius, + detail: detail + }; + radius = radius || 1; detail = detail || 0; @@ -32100,3741 +32579,2150 @@ THREE.PolyhedronGeometry = function ( vertices, indices, radius, detail ) { // Merge vertices - this.mergeVertices(); - - this.computeFaceNormals(); - - this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); - - - // Project vector onto sphere's surface - - function prepare( vector ) { - - var vertex = vector.normalize().clone(); - vertex.index = that.vertices.push( vertex ) - 1; - - // Texture coords are equivalent to map coords, calculate angle and convert to fraction of a circle. - - 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 ); - - return vertex; - - } - - - // 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 ); - - centroid.copy( v1 ).add( v2 ).add( v3 ).divideScalar( 3 ); - - var azi = azimuth( centroid ); - - that.faceVertexUvs[ 0 ].push( [ - correctUV( v1.uv, v1, azi ), - correctUV( v2.uv, v2, azi ), - correctUV( v3.uv, v3, azi ) - ] ); - - } - - - // Analytically subdivide a face to the required detail level. - - function subdivide( face, detail ) { - - 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 = []; - - // Construct all of the vertices for this subdivision. - - for ( var i = 0 ; i <= cols; i ++ ) { - - v[ i ] = []; - - var aj = prepare( a.clone().lerp( c, i / cols ) ); - var bj = prepare( b.clone().lerp( c, i / cols ) ); - var rows = cols - i; - - for ( var j = 0; j <= rows; j ++) { - - if ( j == 0 && i == cols ) { - - v[ i ][ j ] = aj; - - } else { - - v[ i ][ j ] = prepare( aj.clone().lerp( bj, j / rows ) ); - - } - - } - - } - - // Construct all of the faces. - - for ( var i = 0; i < cols ; i ++ ) { - - for ( var j = 0; j < 2 * (cols - i) - 1; j ++ ) { - - var k = Math.floor( j / 2 ); - - if ( j % 2 == 0 ) { - - make( - v[ i ][ k + 1], - v[ i + 1 ][ k ], - v[ i ][ k ] - ); - - } else { - - make( - v[ i ][ k + 1 ], - v[ i + 1][ k + 1], - v[ i + 1 ][ k ] - ); - - } - - } - - } - - } - - - // Angle around the Y axis, counter-clockwise when looking from above. - - function azimuth( vector ) { - - return Math.atan2( vector.z, - vector.x ); - - } - - - // Angle above the XZ plane. - - function inclination( vector ) { - - return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) ); - - } - - - // Texture fixing helper. Spheres have some odd behaviours. - - function correctUV( uv, vector, azimuth ) { - - 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.PolyhedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); - -// File:src/extras/geometries/IcosahedronGeometry.js - -/** - * @author timothypratley / https://github.com/timothypratley - */ - -THREE.IcosahedronGeometry = function ( radius, detail ) { - - this.parameters = { - radius: radius, - detail: detail - }; - - 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 - ]; - - THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); - -}; - -THREE.IcosahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); - -// File:src/extras/geometries/OctahedronGeometry.js - -/** - * @author timothypratley / https://github.com/timothypratley - */ - -THREE.OctahedronGeometry = function ( radius, detail ) { - - this.parameters = { - radius: radius, - detail: detail - }; - - var vertices = [ - 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0,- 1, 0, 0, 0, 1, 0, 0,- 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 - ]; - - THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); -}; - -THREE.OctahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); - -// File:src/extras/geometries/TetrahedronGeometry.js - -/** - * @author timothypratley / https://github.com/timothypratley - */ - -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.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); - -}; - -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 ); - * - */ - -THREE.ParametricGeometry = function ( func, slices, stacks ) { - - THREE.Geometry.call( this ); - - var verts = this.vertices; - var faces = this.faces; - var uvs = this.faceVertexUvs[ 0 ]; - - var i, il, j, p; - var u, v; - - var stackCount = stacks + 1; - var sliceCount = slices + 1; - - for ( i = 0; i <= stacks; i ++ ) { - - v = i / stacks; - - for ( j = 0; j <= slices; j ++ ) { - - u = j / slices; - - p = func( u, v ); - verts.push( p ); - - } - } - - var a, b, c, d; - var uva, uvb, uvc, uvd; - - for ( i = 0; i < stacks; i ++ ) { - - for ( j = 0; j < slices; j ++ ) { - - 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 ] ); - - faces.push( new THREE.Face3( b, c, d ) ); - uvs.push( [ uvb.clone(), uvc, uvd.clone() ] ); - - } - - } - - // console.log(this); - - // 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.AxisHelper = function ( size ) { - - size = size || 1; - - 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 geometry = new THREE.BufferGeometry(); - geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); - geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) ); - - var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } ); - - THREE.Line.call( this, geometry, material, THREE.LinePieces ); - -}; - -THREE.AxisHelper.prototype = Object.create( THREE.Line.prototype ); - -// 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 - */ - -THREE.ArrowHelper = ( function () { - - var lineGeometry = new THREE.Geometry(); - lineGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 1, 0 ) ); - - var coneGeometry = new THREE.CylinderGeometry( 0, 0.5, 1, 5, 1 ); - coneGeometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, - 0.5, 0 ) ); - - return function ( dir, origin, length, color, headLength, headWidth ) { - - // 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 ); - - this.line = new THREE.Line( lineGeometry, new THREE.LineBasicMaterial( { color: color } ) ); - this.line.matrixAutoUpdate = false; - this.add( this.line ); - - this.cone = new THREE.Mesh( coneGeometry, new THREE.MeshBasicMaterial( { color: color } ) ); - this.cone.matrixAutoUpdate = false; - this.add( this.cone ); - - this.setDirection( dir ); - this.setLength( length, headLength, headWidth ); - - } - -}() ); - -THREE.ArrowHelper.prototype = Object.create( THREE.Object3D.prototype ); - -THREE.ArrowHelper.prototype.setDirection = ( function () { - - var axis = new THREE.Vector3(); - var radians; - - return function ( dir ) { - - // dir is assumed to be normalized - - if ( dir.y > 0.99999 ) { - - this.quaternion.set( 0, 0, 0, 1 ); - - } else if ( dir.y < - 0.99999 ) { - - this.quaternion.set( 1, 0, 0, 0 ); - - } else { - - axis.set( dir.z, 0, - dir.x ).normalize(); - - radians = Math.acos( dir.y ); - - this.quaternion.setFromAxisAngle( axis, radians ); - - } - - }; - -}() ); - -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(); - - this.cone.scale.set( headWidth, headLength, headWidth ); - this.cone.position.y = length; - this.cone.updateMatrix(); - -}; - -THREE.ArrowHelper.prototype.setColor = function ( color ) { - - this.line.material.color.set( color ); - this.cone.material.color.set( color ); - -}; - -// File:src/extras/helpers/BoxHelper.js - -/** - * @author mrdoob / http://mrdoob.com/ - */ - -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 ) { - - this.update( object ); - - } - -}; - -THREE.BoxHelper.prototype = Object.create( THREE.Line.prototype ); - -THREE.BoxHelper.prototype.update = function ( object ) { - - var geometry = object.geometry; - - if ( geometry.boundingBox === null ) { - - geometry.computeBoundingBox(); - - } - - var min = geometry.boundingBox.min; - var max = geometry.boundingBox.max; - - /* - 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 - */ - - var vertices = this.geometry.attributes.position.array; - - 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[ 6 ] = min.x; vertices[ 7 ] = max.y; vertices[ 8 ] = max.z; - vertices[ 9 ] = min.x; vertices[ 10 ] = min.y; vertices[ 11 ] = max.z; - - vertices[ 12 ] = min.x; vertices[ 13 ] = min.y; vertices[ 14 ] = max.z; - vertices[ 15 ] = max.x; vertices[ 16 ] = min.y; vertices[ 17 ] = max.z; - - vertices[ 18 ] = max.x; vertices[ 19 ] = min.y; vertices[ 20 ] = max.z; - vertices[ 21 ] = max.x; vertices[ 22 ] = max.y; vertices[ 23 ] = max.z; - - // - - 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; - - vertices[ 36 ] = min.x; vertices[ 37 ] = min.y; vertices[ 38 ] = min.z; - vertices[ 39 ] = max.x; vertices[ 40 ] = min.y; vertices[ 41 ] = min.z; - - vertices[ 42 ] = max.x; vertices[ 43 ] = min.y; vertices[ 44 ] = min.z; - vertices[ 45 ] = max.x; vertices[ 46 ] = max.y; vertices[ 47 ] = min.z; - - // - - vertices[ 48 ] = max.x; vertices[ 49 ] = max.y; vertices[ 50 ] = max.z; - vertices[ 51 ] = max.x; vertices[ 52 ] = max.y; vertices[ 53 ] = min.z; - - vertices[ 54 ] = min.x; vertices[ 55 ] = max.y; vertices[ 56 ] = max.z; - vertices[ 57 ] = min.x; vertices[ 58 ] = max.y; vertices[ 59 ] = min.z; - - vertices[ 60 ] = min.x; vertices[ 61 ] = min.y; vertices[ 62 ] = max.z; - vertices[ 63 ] = min.x; vertices[ 64 ] = min.y; vertices[ 65 ] = min.z; - - 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.geometry.attributes.position.needsUpdate = true; - - this.geometry.computeBoundingSphere(); - - this.matrixAutoUpdate = false; - this.matrixWorld = object.matrixWorld; - -}; - -// File:src/extras/helpers/BoundingBoxHelper.js - -/** - * @author WestLangley / http://github.com/WestLangley - */ - -// a helper to show the world-axis-aligned bounding box for an object - -THREE.BoundingBoxHelper = function ( object, hex ) { - - var color = ( hex !== undefined ) ? hex : 0x888888; - - this.object = object; - - this.box = new THREE.Box3(); - - THREE.Mesh.call( this, new THREE.BoxGeometry( 1, 1, 1 ), new THREE.MeshBasicMaterial( { color: color, wireframe: true } ) ); - -}; - -THREE.BoundingBoxHelper.prototype = Object.create( THREE.Mesh.prototype ); - -THREE.BoundingBoxHelper.prototype.update = function () { - - this.box.setFromObject( this.object ); - - 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 - - addLine( "n1", "f1", hexFrustum ); - addLine( "n2", "f2", hexFrustum ); - addLine( "n3", "f3", hexFrustum ); - addLine( "n4", "f4", hexFrustum ); - - // cone - - addLine( "p", "n1", hexCone ); - addLine( "p", "n2", hexCone ); - addLine( "p", "n3", hexCone ); - addLine( "p", "n4", hexCone ); - - // up - - addLine( "u1", "u2", hexUp ); - addLine( "u2", "u3", hexUp ); - addLine( "u3", "u1", hexUp ); - - // target - - addLine( "c", "t", hexTarget ); - addLine( "p", "c", hexCross ); - - // cross - - addLine( "cn1", "cn2", hexCross ); - addLine( "cn3", "cn4", hexCross ); - - addLine( "cf1", "cf2", hexCross ); - addLine( "cf3", "cf4", hexCross ); - - function addLine( a, b, hex ) { - - addPoint( a, hex ); - addPoint( b, hex ); - - } - - function addPoint( id, hex ) { - - geometry.vertices.push( new THREE.Vector3() ); - geometry.colors.push( new THREE.Color( hex ) ); - - if ( pointMap[ id ] === undefined ) { - - pointMap[ id ] = []; - - } - - pointMap[ id ].push( geometry.vertices.length - 1 ); - - } - - THREE.Line.call( this, geometry, material, THREE.LinePieces ); - - this.camera = camera; - this.matrixWorld = camera.matrixWorld; - this.matrixAutoUpdate = false; - - this.pointMap = pointMap; - - this.update(); - -}; - -THREE.CameraHelper.prototype = Object.create( THREE.Line.prototype ); - -THREE.CameraHelper.prototype.update = function () { - - var vector = new THREE.Vector3(); - var camera = new THREE.Camera(); - var projector = new THREE.Projector(); - - return function () { - - var scope = this; - - var w = 1, h = 1; - - // we need just camera projection matrix - // world matrix must be identity - - camera.projectionMatrix.copy( this.camera.projectionMatrix ); - - // center / target - - setPoint( "c", 0, 0, - 1 ); - setPoint( "t", 0, 0, 1 ); - - // near - - setPoint( "n1", - w, - h, - 1 ); - setPoint( "n2", w, - h, - 1 ); - setPoint( "n3", - w, h, - 1 ); - setPoint( "n4", w, h, - 1 ); - - // far - - setPoint( "f1", - w, - h, 1 ); - setPoint( "f2", w, - h, 1 ); - setPoint( "f3", - w, h, 1 ); - setPoint( "f4", w, h, 1 ); - - // up - - setPoint( "u1", w * 0.7, h * 1.1, - 1 ); - setPoint( "u2", - w * 0.7, h * 1.1, - 1 ); - setPoint( "u3", 0, h * 2, - 1 ); - - // cross - - setPoint( "cf1", - w, 0, 1 ); - setPoint( "cf2", w, 0, 1 ); - setPoint( "cf3", 0, - h, 1 ); - setPoint( "cf4", 0, h, 1 ); - - setPoint( "cn1", - w, 0, - 1 ); - setPoint( "cn2", w, 0, - 1 ); - setPoint( "cn3", 0, - h, - 1 ); - setPoint( "cn4", 0, h, - 1 ); - - function setPoint( point, x, y, z ) { - - vector.set( x, y, z ); - projector.unprojectVector( vector, camera ); - - var points = scope.pointMap[ point ]; - - if ( points !== undefined ) { - - for ( var i = 0, il = points.length; i < il; i ++ ) { - - scope.geometry.vertices[ points[ i ] ].copy( vector ); - - } - - } - - } - - this.geometry.verticesNeedUpdate = true; - - }; - -}(); - -// File:src/extras/helpers/DirectionalLightHelper.js - -/** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley - */ - -THREE.DirectionalLightHelper = function ( light, size ) { - - THREE.Object3D.call( this ); - - this.light = light; - this.light.updateMatrixWorld(); - - this.matrixWorld = light.matrixWorld; - this.matrixAutoUpdate = false; - - size = size || 1; - - 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 material = new THREE.LineBasicMaterial( { fog: false } ); - material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); - - this.lightPlane = new THREE.Line( geometry, material ); - this.add( this.lightPlane ); - - geometry = new THREE.Geometry(); - geometry.vertices.push( - new THREE.Vector3(), - new THREE.Vector3() - ); - - material = new THREE.LineBasicMaterial( { fog: false } ); - material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); - - this.targetLine = new THREE.Line( geometry, material ); - this.add( this.targetLine ); - - this.update(); - -}; - -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 () { - - var v1 = new THREE.Vector3(); - var v2 = new THREE.Vector3(); - var v3 = new THREE.Vector3(); - - return function () { - - 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 ); - - this.targetLine.geometry.vertices[ 1 ].copy( v3 ); - this.targetLine.geometry.verticesNeedUpdate = true; - this.targetLine.material.color.copy( this.lightPlane.material.color ); - - } - -}(); - - -// File:src/extras/helpers/EdgesHelper.js - -/** - * @author WestLangley / http://github.com/WestLangley - */ - -THREE.EdgesHelper = function ( object, hex ) { - - var color = ( hex !== undefined ) ? hex : 0xffffff; - - var edge = [ 0, 0 ], hash = {}; - var sortFunction = function ( a, b ) { return a - b }; - - var keys = [ 'a', 'b', 'c' ]; - var geometry = new THREE.BufferGeometry(); - - var geometry2 = object.geometry.clone(); - - geometry2.mergeVertices(); - geometry2.computeFaceNormals(); - - var vertices = geometry2.vertices; - var faces = geometry2.faces; - var numEdges = 0; - - for ( var i = 0, l = faces.length; i < l; i ++ ) { - - var face = faces[ i ]; - - for ( var j = 0; j < 3; j ++ ) { - - edge[ 0 ] = face[ keys[ j ] ]; - edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; - edge.sort( sortFunction ); - - var key = edge.toString(); - - if ( hash[ key ] === undefined ) { - - hash[ key ] = { vert1: edge[ 0 ], vert2: edge[ 1 ], face1: i, face2: undefined }; - numEdges ++; - - } else { - - hash[ key ].face2 = i; - - } - - } - - } - - geometry.addAttribute( 'position', new THREE.Float32Attribute( numEdges * 2 * 3, 3 ) ); - - var coords = geometry.attributes.position.array; - - var index = 0; - - for ( var key in hash ) { - - var h = hash[ key ]; - - if ( h.face2 === undefined || faces[ h.face1 ].normal.dot( faces[ h.face2 ].normal ) < 0.9999 ) { // hardwired const OK - - var vertex = vertices[ h.vert1 ]; - coords[ index ++ ] = vertex.x; - coords[ index ++ ] = vertex.y; - coords[ index ++ ] = vertex.z; - - vertex = vertices[ h.vert2 ]; - coords[ index ++ ] = vertex.x; - coords[ index ++ ] = vertex.y; - coords[ index ++ ] = vertex.z; - - } - - } - - 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 ); - -// File:src/extras/helpers/FaceNormalsHelper.js - -/** - * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley -*/ - -THREE.FaceNormalsHelper = function ( object, size, hex, linewidth ) { - - this.object = object; - - this.size = ( size !== undefined ) ? size : 1; - - var color = ( hex !== undefined ) ? hex : 0xffff00; - - var width = ( linewidth !== undefined ) ? linewidth : 1; - - var geometry = new THREE.Geometry(); - - var faces = this.object.geometry.faces; - - for ( var i = 0, l = faces.length; i < l; i ++ ) { - - geometry.vertices.push( new THREE.Vector3(), new THREE.Vector3() ); - - } - - THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces ); - - this.matrixAutoUpdate = false; - - this.normalMatrix = new THREE.Matrix3(); - - this.update(); - -}; - -THREE.FaceNormalsHelper.prototype = Object.create( THREE.Line.prototype ); - -THREE.FaceNormalsHelper.prototype.update = function () { - - var vertices = this.geometry.vertices; - - var object = this.object; - var objectVertices = object.geometry.vertices; - var objectFaces = object.geometry.faces; - var objectWorldMatrix = object.matrixWorld; - - object.updateMatrixWorld( true ); - - this.normalMatrix.getNormalMatrix( objectWorldMatrix ); - - for ( var i = 0, i2 = 0, l = objectFaces.length; i < l; i ++, i2 += 2 ) { - - var face = objectFaces[ i ]; - - vertices[ i2 ].copy( objectVertices[ face.a ] ) - .add( objectVertices[ face.b ] ) - .add( objectVertices[ face.c ] ) - .divideScalar( 3 ) - .applyMatrix4( objectWorldMatrix ); - - vertices[ i2 + 1 ].copy( face.normal ) - .applyMatrix3( this.normalMatrix ) - .normalize() - .multiplyScalar( this.size ) - .add( vertices[ i2 ] ); - - } - - this.geometry.verticesNeedUpdate = true; - - return this; - -}; - - -// File:src/extras/helpers/GridHelper.js - -/** - * @author mrdoob / http://mrdoob.com/ - */ - -THREE.GridHelper = function ( size, step ) { - - var geometry = new THREE.Geometry(); - var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } ); - - this.color1 = new THREE.Color( 0x444444 ); - this.color2 = new THREE.Color( 0x888888 ); - - for ( var i = - size; i <= size; i += step ) { - - 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 ) - ); - - var color = i === 0 ? this.color1 : this.color2; - - geometry.colors.push( color, color, color, color ); - - } - - THREE.Line.call( this, geometry, material, THREE.LinePieces ); - -}; - -THREE.GridHelper.prototype = Object.create( THREE.Line.prototype ); - -THREE.GridHelper.prototype.setColors = function( colorCenterLine, colorGrid ) { - - this.color1.set( colorCenterLine ); - this.color2.set( colorGrid ); - - this.geometry.colorsNeedUpdate = true; - -} - -// File:src/extras/helpers/HemisphereLightHelper.js - -/** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - */ - -THREE.HemisphereLightHelper = function ( light, sphereSize, arrowLength, domeSize ) { - - THREE.Object3D.call( this ); - - this.light = light; - this.light.updateMatrixWorld(); - - this.matrixWorld = light.matrixWorld; - this.matrixAutoUpdate = false; - - this.colors = [ new THREE.Color(), new THREE.Color() ]; - - var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 ); - geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) ); - - for ( var i = 0, il = 8; i < il; i ++ ) { - - geometry.faces[ i ].color = this.colors[ i < 4 ? 0 : 1 ]; - - } - - var material = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors, wireframe: true } ); - - this.lightSphere = new THREE.Mesh( geometry, material ); - this.add( this.lightSphere ); - - this.update(); - -}; - -THREE.HemisphereLightHelper.prototype = Object.create( THREE.Object3D.prototype ); - -THREE.HemisphereLightHelper.prototype.dispose = function () { - this.lightSphere.geometry.dispose(); - this.lightSphere.material.dispose(); -}; - -THREE.HemisphereLightHelper.prototype.update = function () { - - var vector = new THREE.Vector3(); - - return function () { - - this.colors[ 0 ].copy( this.light.color ).multiplyScalar( this.light.intensity ); - this.colors[ 1 ].copy( this.light.groundColor ).multiplyScalar( this.light.intensity ); - - this.lightSphere.lookAt( vector.setFromMatrixPosition( this.light.matrixWorld ).negate() ); - this.lightSphere.geometry.colorsNeedUpdate = true; - - } - -}(); - - -// File:src/extras/helpers/PointLightHelper.js - -/** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - */ - -THREE.PointLightHelper = function ( light, sphereSize ) { - - 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 ); - - THREE.Mesh.call( this, geometry, material ); - - this.matrixWorld = 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 } ); - - this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); - this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); - - var d = light.distance; - - if ( d === 0.0 ) { - - this.lightDistance.visible = false; - - } else { - - this.lightDistance.scale.set( d, d, d ); - - } - - this.add( this.lightDistance ); - */ - -}; - -THREE.PointLightHelper.prototype = Object.create( THREE.Mesh.prototype ); - -THREE.PointLightHelper.prototype.dispose = function () { - - this.geometry.dispose(); - this.material.dispose(); -}; - -THREE.PointLightHelper.prototype.update = function () { - - this.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); - - /* - var d = this.light.distance; - - if ( d === 0.0 ) { - - this.lightDistance.visible = false; - - } else { - - this.lightDistance.visible = true; - this.lightDistance.scale.set( d, d, d ); - - } - */ - -}; - - -// File:src/extras/helpers/SkeletonHelper.js - -/** - * @author Sean Griffin / http://twitter.com/sgrif - * @author Michael Guerrero / http://realitymeltdown.com - * @author mrdoob / http://mrdoob.com/ - * @author ikerr / http://verold.com - */ - -THREE.SkeletonHelper = function ( object ) { - - this.bones = this.getBoneList( object ); - - var geometry = new THREE.Geometry(); - - for ( var i = 0; i < this.bones.length; i ++ ) { - - var bone = this.bones[ i ]; - - if ( bone.parent instanceof THREE.Bone ) { - - 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 ) ); - - } - - } - - var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors, depthTest: false, depthWrite: false, transparent: true } ); - - THREE.Line.call( this, geometry, material, THREE.LinePieces ); - - this.root = object; - - this.matrixWorld = object.matrixWorld; - this.matrixAutoUpdate = false; - - this.update(); - -}; - - -THREE.SkeletonHelper.prototype = Object.create( THREE.Line.prototype ); - -THREE.SkeletonHelper.prototype.getBoneList = function( object ) { - - var boneList = []; - - if ( object instanceof THREE.Bone ) { - - boneList.push( object ); - - } - - for ( var i = 0; i < object.children.length; i ++ ) { - - boneList.push.apply( boneList, this.getBoneList( object.children[ i ] ) ); - - } - - return boneList; - -}; - -THREE.SkeletonHelper.prototype.update = function () { - - var geometry = this.geometry; - - var matrixWorldInv = new THREE.Matrix4().getInverse( this.root.matrixWorld ); - - var boneMatrix = new THREE.Matrix4(); - - var j = 0; - - for ( var i = 0; i < this.bones.length; i ++ ) { - - var bone = this.bones[ i ]; - - if ( bone.parent instanceof THREE.Bone ) { - - boneMatrix.multiplyMatrices( matrixWorldInv, bone.matrixWorld ); - geometry.vertices[ j ].setFromMatrixPosition( boneMatrix ); - - boneMatrix.multiplyMatrices( matrixWorldInv, bone.parent.matrixWorld ); - geometry.vertices[ j + 1 ].setFromMatrixPosition( boneMatrix ); - - j += 2; - - } - - } - - geometry.verticesNeedUpdate = true; - - geometry.computeBoundingSphere(); - -}; - -// 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 ) { - - THREE.Object3D.call( this ); - - this.light = light; - this.light.updateMatrixWorld(); - - this.matrixWorld = light.matrixWorld; - this.matrixAutoUpdate = false; - - var geometry = new THREE.CylinderGeometry( 0, 1, 1, 8, 1, true ); - - geometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, - 0.5, 0 ) ); - geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) ); - - var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } ); - - this.cone = new THREE.Mesh( geometry, material ); - this.add( this.cone ); - - this.update(); - -}; - -THREE.SpotLightHelper.prototype = Object.create( THREE.Object3D.prototype ); - -THREE.SpotLightHelper.prototype.dispose = function () { - this.cone.geometry.dispose(); - this.cone.material.dispose(); -}; - -THREE.SpotLightHelper.prototype.update = function () { - - 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 ); - - vector.setFromMatrixPosition( this.light.matrixWorld ); - vector2.setFromMatrixPosition( this.light.target.matrixWorld ); - - this.cone.lookAt( vector2.sub( vector ) ); - - this.cone.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); - - }; - -}(); - -// File:src/extras/helpers/VertexNormalsHelper.js - -/** - * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley -*/ - -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(); - - var vertices = object.geometry.vertices; - - var faces = object.geometry.faces; - - 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 ++ ) { - - geometry.vertices.push( new THREE.Vector3(), new THREE.Vector3() ); - - } - - } - - THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces ); - - this.matrixAutoUpdate = false; - - this.normalMatrix = new THREE.Matrix3(); - - this.update(); - -}; - -THREE.VertexNormalsHelper.prototype = Object.create( THREE.Line.prototype ); - -THREE.VertexNormalsHelper.prototype.update = ( function ( object ) { - - var v1 = new THREE.Vector3(); - - return function( object ) { - - var keys = [ 'a', 'b', 'c', 'd' ]; - - this.object.updateMatrixWorld( true ); - - this.normalMatrix.getNormalMatrix( this.object.matrixWorld ); - - var vertices = this.geometry.vertices; - - var verts = this.object.geometry.vertices; - - var faces = this.object.geometry.faces; - - var worldMatrix = this.object.matrixWorld; - - var idx = 0; - - 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 ++ ) { - - var vertexId = face[ keys[ j ] ]; - var vertex = verts[ vertexId ]; - - var normal = face.vertexNormals[ j ]; - - vertices[ idx ].copy( vertex ).applyMatrix4( worldMatrix ); - - v1.copy( normal ).applyMatrix3( this.normalMatrix ).normalize().multiplyScalar( this.size ); - - v1.add( vertices[ idx ] ); - idx = idx + 1; - - vertices[ idx ].copy( v1 ); - idx = idx + 1; - - } - - } - - this.geometry.verticesNeedUpdate = true; - - return this; - - } - -}()); - -// File:src/extras/helpers/VertexTangentsHelper.js - -/** - * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley -*/ - -THREE.VertexTangentsHelper = function ( object, size, hex, linewidth ) { - - this.object = object; - - this.size = ( size !== undefined ) ? size : 1; - - var color = ( hex !== undefined ) ? hex : 0x0000ff; - - var width = ( linewidth !== undefined ) ? linewidth : 1; - - var geometry = new THREE.Geometry(); - - var vertices = object.geometry.vertices; - - var faces = object.geometry.faces; - - for ( var i = 0, l = faces.length; i < l; i ++ ) { - - var face = faces[ i ]; - - for ( var j = 0, jl = face.vertexTangents.length; j < jl; j ++ ) { - - geometry.vertices.push( new THREE.Vector3() ); - geometry.vertices.push( new THREE.Vector3() ); - - } - - } - - THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces ); - - this.matrixAutoUpdate = false; - - this.update(); - -}; - -THREE.VertexTangentsHelper.prototype = Object.create( THREE.Line.prototype ); - -THREE.VertexTangentsHelper.prototype.update = ( function ( object ) { - - var v1 = new THREE.Vector3(); - - return function( object ) { - - var keys = [ 'a', 'b', 'c', 'd' ]; - - this.object.updateMatrixWorld( true ); - - var vertices = this.geometry.vertices; - - var verts = this.object.geometry.vertices; - - var faces = this.object.geometry.faces; - - var worldMatrix = this.object.matrixWorld; - - var idx = 0; - - for ( var i = 0, l = faces.length; i < l; i ++ ) { - - var face = faces[ i ]; - - for ( var j = 0, jl = face.vertexTangents.length; j < jl; j ++ ) { - - var vertexId = face[ keys[ j ] ]; - var vertex = verts[ vertexId ]; - - var tangent = face.vertexTangents[ j ]; - - vertices[ idx ].copy( vertex ).applyMatrix4( worldMatrix ); - - v1.copy( tangent ).transformDirection( worldMatrix ).multiplyScalar( this.size ); - - v1.add( vertices[ idx ] ); - idx = idx + 1; - - vertices[ idx ].copy( v1 ); - idx = idx + 1; - - } - - } - - this.geometry.verticesNeedUpdate = true; - - return this; - - } - -}()); - -// File:src/extras/helpers/WireframeHelper.js - -/** - * @author mrdoob / http://mrdoob.com/ - */ - -THREE.WireframeHelper = function ( object, hex ) { - - var color = ( hex !== undefined ) ? hex : 0xffffff; - - var edge = [ 0, 0 ], hash = {}; - var sortFunction = function ( a, b ) { return a - b }; - - var keys = [ 'a', 'b', 'c' ]; - var geometry = new THREE.BufferGeometry(); - - if ( object.geometry instanceof THREE.Geometry ) { - - var vertices = object.geometry.vertices; - var faces = object.geometry.faces; - var numEdges = 0; - - // allocate maximal size - var edges = new Uint32Array( 6 * faces.length ); - - for ( var i = 0, l = faces.length; i < l; i ++ ) { - - var face = faces[ i ]; - - for ( var j = 0; j < 3; j ++ ) { - - edge[ 0 ] = face[ keys[ j ] ]; - edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; - edge.sort( sortFunction ); - - var key = edge.toString(); - - if ( hash[ key ] === undefined ) { - - edges[ 2 * numEdges ] = edge[ 0 ]; - edges[ 2 * numEdges + 1 ] = edge[ 1 ]; - hash[ key ] = true; - numEdges ++; + this.mergeVertices(); - } + this.computeFaceNormals(); - } + this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); - } - var coords = new Float32Array( numEdges * 2 * 3 ); + // Project vector onto sphere's surface - for ( var i = 0, l = numEdges; i < l; i ++ ) { + function prepare( vector ) { - for ( var j = 0; j < 2; j ++ ) { + var vertex = vector.normalize().clone(); + vertex.index = that.vertices.push( vertex ) - 1; - var vertex = vertices[ edges [ 2 * i + j] ]; + // Texture coords are equivalent to map coords, calculate angle and convert to fraction of a circle. - var index = 6 * i + 3 * j; - coords[ index + 0 ] = vertex.x; - coords[ index + 1 ] = vertex.y; - coords[ index + 2 ] = vertex.z; + 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 ); - } + return vertex; - } + } - geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); - } else if ( object.geometry instanceof THREE.BufferGeometry ) { + // Approximate a curved face with recursively sub-divided triangles. - if ( object.geometry.attributes.index !== undefined ) { // Indexed BufferGeometry + function make( v1, v2, v3 ) { - var vertices = object.geometry.attributes.position.array; - var indices = object.geometry.attributes.index.array; - var offsets = object.geometry.offsets; - var numEdges = 0; + var face = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] ); + that.faces.push( face ); - // allocate maximal size - var edges = new Uint32Array( 2 * indices.length ); + centroid.copy( v1 ).add( v2 ).add( v3 ).divideScalar( 3 ); - for ( var o = 0, ol = offsets.length; o < ol; ++ o ) { + var azi = azimuth( centroid ); - var start = offsets[ o ].start; - var count = offsets[ o ].count; - var index = offsets[ o ].index; + that.faceVertexUvs[ 0 ].push( [ + correctUV( v1.uv, v1, azi ), + correctUV( v2.uv, v2, azi ), + correctUV( v3.uv, v3, azi ) + ] ); - for ( var i = start, il = start + count; i < il; i += 3 ) { + } - for ( var j = 0; j < 3; j ++ ) { - edge[ 0 ] = index + indices[ i + j ]; - edge[ 1 ] = index + indices[ i + ( j + 1 ) % 3 ]; - edge.sort( sortFunction ); + // Analytically subdivide a face to the required detail level. - var key = edge.toString(); + function subdivide( face, detail ) { - if ( hash[ key ] === undefined ) { + 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 = []; - edges[ 2 * numEdges ] = edge[ 0 ]; - edges[ 2 * numEdges + 1 ] = edge[ 1 ]; - hash[ key ] = true; - numEdges ++; + // Construct all of the vertices for this subdivision. - } + for ( var i = 0 ; i <= cols; i ++ ) { - } + v[ i ] = []; - } + var aj = prepare( a.clone().lerp( c, i / cols ) ); + var bj = prepare( b.clone().lerp( c, i / cols ) ); + var rows = cols - i; - } + for ( var j = 0; j <= rows; j ++) { - var coords = new Float32Array( numEdges * 2 * 3 ); + if ( j == 0 && i == cols ) { - for ( var i = 0, l = numEdges; i < l; i ++ ) { + v[ i ][ j ] = aj; - for ( var j = 0; j < 2; j ++ ) { + } else { - 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 ]; + v[ i ][ j ] = prepare( aj.clone().lerp( bj, j / rows ) ); } } - geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); + } - } else { // non-indexed BufferGeometry + // Construct all of the faces. - var vertices = object.geometry.attributes.position.array; - var numEdges = vertices.length / 3; - var numTris = numEdges / 3; + for ( var i = 0; i < cols ; i ++ ) { - var coords = new Float32Array( numEdges * 2 * 3 ); + for ( var j = 0; j < 2 * (cols - i) - 1; j ++ ) { - for ( var i = 0, l = numTris; i < l; i ++ ) { + var k = Math.floor( j / 2 ); - for ( var j = 0; j < 3; j ++ ) { + if ( j % 2 == 0 ) { - var index = 18 * i + 6 * j; + make( + v[ i ][ k + 1], + v[ i + 1 ][ k ], + v[ i ][ k ] + ); - var index1 = 9 * i + 3 * j; - coords[ index + 0 ] = vertices[ index1 ]; - coords[ index + 1 ] = vertices[ index1 + 1 ]; - coords[ index + 2 ] = vertices[ index1 + 2 ]; + } else { - 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 ]; + make( + v[ i ][ k + 1 ], + v[ i + 1][ k + 1], + v[ i + 1 ][ k ] + ); } } - geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); - } } - THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color } ), THREE.LinePieces ); - this.matrixAutoUpdate = false; - this.matrixWorld = object.matrixWorld; + // Angle around the Y axis, counter-clockwise when looking from above. -}; + function azimuth( vector ) { -THREE.WireframeHelper.prototype = Object.create( THREE.Line.prototype ); + return Math.atan2( vector.z, - vector.x ); -// File:src/extras/objects/ImmediateRenderObject.js + } -/** - * @author alteredq / http://alteredqualia.com/ - */ -THREE.ImmediateRenderObject = function () { + // Angle above the XZ plane. - THREE.Object3D.call( this ); + function inclination( vector ) { + + return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) ); + + } + + + // Texture fixing helper. Spheres have some odd behaviours. + + function correctUV( uv, vector, azimuth ) { + + 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(); + + } - this.render = function ( renderCallback ) {}; }; -THREE.ImmediateRenderObject.prototype = Object.create( THREE.Object3D.prototype ); +THREE.PolyhedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); -// File:src/extras/objects/LensFlare.js +// File:src/extras/geometries/DodecahedronGeometry.js /** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ + * @author Abe Pazos / https://hamoid.com */ -THREE.LensFlare = function ( texture, size, distance, blending, color ) { +THREE.DodecahedronGeometry = function ( radius, detail ) { - THREE.Object3D.call( this ); + this.parameters = { + radius: radius, + detail: detail + }; - this.lensFlares = []; + var t = ( 1 + Math.sqrt( 5 ) ) / 2; + var r = 1 / t; - this.positionScreen = new THREE.Vector3(); - this.customUpdateCallback = undefined; + var vertices = [ - if( texture !== undefined ) { + // (±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, - this.add( texture, size, distance, blending, color ); + // (0, ±1/φ, ±φ) + 0, -r, -t, 0, -r, t, + 0, r, -t, 0, r, t, - } + // (±1/φ, ±φ, 0) + -r, -t, 0, -r, t, 0, + r, -t, 0, r, t, 0, + + // (±φ, 0, ±1/φ) + -t, 0, -r, t, 0, -r, + -t, 0, r, t, 0, r + ]; + + 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 + ]; + + THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); }; -THREE.LensFlare.prototype = Object.create( THREE.Object3D.prototype ); +THREE.DodecahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); +// File:src/extras/geometries/IcosahedronGeometry.js -/* - * Add: adds another flare +/** + * @author timothypratley / https://github.com/timothypratley */ -THREE.LensFlare.prototype.add = function ( texture, size, distance, blending, color, opacity ) { +THREE.IcosahedronGeometry = function ( radius, detail ) { - 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; + var t = ( 1 + Math.sqrt( 5 ) ) / 2; - distance = Math.min( distance, Math.max( 0, distance ) ); + 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 + ]; + + THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); - 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 + this.type = 'IcosahedronGeometry'; + this.parameters = { + radius: radius, + detail: detail + }; }; +THREE.IcosahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); -/* - * 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. - */ +// File:src/extras/geometries/OctahedronGeometry.js -THREE.LensFlare.prototype.updateLensFlares = function () { +/** + * @author timothypratley / https://github.com/timothypratley + */ - var f, fl = this.lensFlares.length; - var flare; - var vecX = - this.positionScreen.x * 2; - var vecY = - this.positionScreen.y * 2; +THREE.OctahedronGeometry = function ( radius, detail ) { - for( f = 0; f < fl; f ++ ) { + this.parameters = { + radius: radius, + detail: detail + }; - flare = this.lensFlares[ f ]; + var vertices = [ + 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0,- 1, 0, 0, 0, 1, 0, 0,- 1 + ]; - flare.x = this.positionScreen.x + vecX * flare.distance; - flare.y = this.positionScreen.y + vecY * flare.distance; + 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 + ]; - flare.wantedRotation = flare.x * Math.PI * 0.25; - flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25; + THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); - } + this.type = 'OctahedronGeometry'; + this.parameters = { + radius: radius, + detail: detail + }; }; +THREE.OctahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); +// File:src/extras/geometries/TetrahedronGeometry.js +/** + * @author timothypratley / https://github.com/timothypratley + */ +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.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); + this.type = 'TetrahedronGeometry'; + this.parameters = { + radius: radius, + detail: detail + }; +}; +THREE.TetrahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); - -// File:src/extras/objects/MorphBlendMesh.js +// File:src/extras/geometries/ParametricGeometry.js /** - * @author alteredq / http://alteredqualia.com/ + * @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 ); + * */ -THREE.MorphBlendMesh = function( geometry, material ) { +THREE.ParametricGeometry = function ( func, slices, stacks ) { - THREE.Mesh.call( this, geometry, material ); + THREE.Geometry.call( this ); - this.animationsMap = {}; - this.animationsList = []; + this.type = 'ParametricGeometry'; - // prepare default animation - // (all frames played together in 1 second) + this.parameters = { + func: func, + slices: slices, + stacks: stacks + }; - var numFrames = this.geometry.morphTargets.length; + var verts = this.vertices; + var faces = this.faces; + var uvs = this.faceVertexUvs[ 0 ]; - var name = "__default"; + var i, il, j, p; + var u, v; - var startFrame = 0; - var endFrame = numFrames - 1; + var stackCount = stacks + 1; + var sliceCount = slices + 1; - var fps = numFrames / 1; + for ( i = 0; i <= stacks; i ++ ) { - this.createAnimation( name, startFrame, endFrame, fps ); - this.setAnimationWeight( name, 1 ); + v = i / stacks; -}; + for ( j = 0; j <= slices; j ++ ) { -THREE.MorphBlendMesh.prototype = Object.create( THREE.Mesh.prototype ); + u = j / slices; -THREE.MorphBlendMesh.prototype.createAnimation = function ( name, start, end, fps ) { + p = func( u, v ); + verts.push( p ); - var animation = { + } + } - startFrame: start, - endFrame: end, + var a, b, c, d; + var uva, uvb, uvc, uvd; - length: end - start + 1, + for ( i = 0; i < stacks; i ++ ) { - fps: fps, - duration: ( end - start ) / fps, + for ( j = 0; j < slices; j ++ ) { - lastFrame: 0, - currentFrame: 0, + a = i * sliceCount + j; + b = i * sliceCount + j + 1; + c = (i + 1) * sliceCount + j + 1; + d = (i + 1) * sliceCount + j; - active: false, + 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 ); - time: 0, - direction: 1, - weight: 1, + faces.push( new THREE.Face3( a, b, d ) ); + uvs.push( [ uva, uvb, uvd ] ); - directionBackwards: false, - mirroredLoop: false + faces.push( new THREE.Face3( b, c, d ) ); + uvs.push( [ uvb.clone(), uvc, uvd.clone() ] ); - }; + } - this.animationsMap[ name ] = animation; - this.animationsList.push( animation ); + } + + // console.log(this); + + // magic bullet + // var diff = this.mergeVertices(); + // console.log('removed ', diff, ' vertices by merging'); + + this.computeFaceNormals(); + this.computeVertexNormals(); }; -THREE.MorphBlendMesh.prototype.autoCreateAnimations = function ( fps ) { +THREE.ParametricGeometry.prototype = Object.create( THREE.Geometry.prototype ); - var pattern = /([a-z]+)_?(\d+)/; +// File:src/extras/helpers/AxisHelper.js - var firstAnimation, frameRanges = {}; +/** + * @author sroucheray / http://sroucheray.org/ + * @author mrdoob / http://mrdoob.com/ + */ - var geometry = this.geometry; +THREE.AxisHelper = function ( size ) { - for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) { + size = size || 1; - var morph = geometry.morphTargets[ i ]; - var chunks = morph.name.match( pattern ); + var vertices = new Float32Array( [ + 0, 0, 0, size, 0, 0, + 0, 0, 0, 0, size, 0, + 0, 0, 0, 0, 0, size + ] ); - if ( chunks && chunks.length > 1 ) { + 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 name = chunks[ 1 ]; - var num = chunks[ 2 ]; + var geometry = new THREE.BufferGeometry(); + geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); + geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) ); - if ( ! frameRanges[ name ] ) frameRanges[ name ] = { start: Infinity, end: - Infinity }; + var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } ); - var range = frameRanges[ name ]; + THREE.Line.call( this, geometry, material, THREE.LinePieces ); - if ( i < range.start ) range.start = i; - if ( i > range.end ) range.end = i; +}; - if ( ! firstAnimation ) firstAnimation = name; +THREE.AxisHelper.prototype = Object.create( THREE.Line.prototype ); - } +// 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 name in frameRanges ) { +THREE.ArrowHelper = ( function () { - var range = frameRanges[ name ]; - this.createAnimation( name, range.start, range.end, fps ); + var lineGeometry = new THREE.Geometry(); + lineGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 1, 0 ) ); - } + var coneGeometry = new THREE.CylinderGeometry( 0, 0.5, 1, 5, 1 ); + coneGeometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, - 0.5, 0 ) ); - this.firstAnimation = firstAnimation; + return function ( dir, origin, length, color, headLength, headWidth ) { -}; + // dir is assumed to be normalized -THREE.MorphBlendMesh.prototype.setAnimationDirectionForward = function ( name ) { + THREE.Object3D.call( this ); - var animation = this.animationsMap[ name ]; + if ( color === undefined ) color = 0xffff00; + if ( length === undefined ) length = 1; + if ( headLength === undefined ) headLength = 0.2 * length; + if ( headWidth === undefined ) headWidth = 0.2 * headLength; - if ( animation ) { + this.position.copy( origin ); - animation.direction = 1; - animation.directionBackwards = false; + this.line = new THREE.Line( lineGeometry, new THREE.LineBasicMaterial( { color: color } ) ); + this.line.matrixAutoUpdate = false; + this.add( this.line ); + + this.cone = new THREE.Mesh( coneGeometry, new THREE.MeshBasicMaterial( { color: color } ) ); + this.cone.matrixAutoUpdate = false; + this.add( this.cone ); + + this.setDirection( dir ); + this.setLength( length, headLength, headWidth ); } -}; +}() ); -THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward = function ( name ) { +THREE.ArrowHelper.prototype = Object.create( THREE.Object3D.prototype ); - var animation = this.animationsMap[ name ]; +THREE.ArrowHelper.prototype.setDirection = ( function () { - if ( animation ) { + var axis = new THREE.Vector3(); + var radians; - animation.direction = - 1; - animation.directionBackwards = true; + return function ( dir ) { - } + // dir is assumed to be normalized -}; + if ( dir.y > 0.99999 ) { -THREE.MorphBlendMesh.prototype.setAnimationFPS = function ( name, fps ) { + this.quaternion.set( 0, 0, 0, 1 ); - var animation = this.animationsMap[ name ]; + } else if ( dir.y < - 0.99999 ) { - if ( animation ) { + this.quaternion.set( 1, 0, 0, 0 ); - animation.fps = fps; - animation.duration = ( animation.end - animation.start ) / animation.fps; + } else { - } + axis.set( dir.z, 0, - dir.x ).normalize(); -}; + radians = Math.acos( dir.y ); -THREE.MorphBlendMesh.prototype.setAnimationDuration = function ( name, duration ) { + this.quaternion.setFromAxisAngle( axis, radians ); - var animation = this.animationsMap[ name ]; + } - if ( animation ) { + }; - animation.duration = duration; - animation.fps = ( animation.end - animation.start ) / animation.duration; +}() ); - } +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(); + + this.cone.scale.set( headWidth, headLength, headWidth ); + this.cone.position.y = length; + this.cone.updateMatrix(); }; -THREE.MorphBlendMesh.prototype.setAnimationWeight = function ( name, weight ) { +THREE.ArrowHelper.prototype.setColor = function ( color ) { - var animation = this.animationsMap[ name ]; + this.line.material.color.set( color ); + this.cone.material.color.set( color ); - if ( animation ) { +}; - animation.weight = weight; +// File:src/extras/helpers/BoxHelper.js - } +/** + * @author mrdoob / http://mrdoob.com/ + */ -}; +THREE.BoxHelper = function ( object ) { -THREE.MorphBlendMesh.prototype.setAnimationTime = function ( name, time ) { + var geometry = new THREE.BufferGeometry(); + geometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( 72 ), 3 ) ); - var animation = this.animationsMap[ name ]; + THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: 0xffff00 } ), THREE.LinePieces ); - if ( animation ) { + if ( object !== undefined ) { - animation.time = time; + this.update( object ); } }; -THREE.MorphBlendMesh.prototype.getAnimationTime = function ( name ) { +THREE.BoxHelper.prototype = Object.create( THREE.Line.prototype ); - var time = 0; +THREE.BoxHelper.prototype.update = function ( object ) { - var animation = this.animationsMap[ name ]; + var geometry = object.geometry; - if ( animation ) { + if ( geometry.boundingBox === null ) { - time = animation.time; + geometry.computeBoundingBox(); } - return time; + var min = geometry.boundingBox.min; + var max = geometry.boundingBox.max; -}; + /* + 5____4 + 1/___0/| + | 6__|_7 + 2/___3/ -THREE.MorphBlendMesh.prototype.getAnimationDuration = function ( name ) { + 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 + */ - var duration = - 1; + var vertices = this.geometry.attributes.position.array; - var animation = this.animationsMap[ name ]; + vertices[ 0 ] = max.x; vertices[ 1 ] = max.y; vertices[ 2 ] = max.z; + vertices[ 3 ] = min.x; vertices[ 4 ] = max.y; vertices[ 5 ] = max.z; - if ( animation ) { + vertices[ 6 ] = min.x; vertices[ 7 ] = max.y; vertices[ 8 ] = max.z; + vertices[ 9 ] = min.x; vertices[ 10 ] = min.y; vertices[ 11 ] = max.z; - duration = animation.duration; + vertices[ 12 ] = min.x; vertices[ 13 ] = min.y; vertices[ 14 ] = max.z; + vertices[ 15 ] = max.x; vertices[ 16 ] = min.y; vertices[ 17 ] = max.z; - } + vertices[ 18 ] = max.x; vertices[ 19 ] = min.y; vertices[ 20 ] = max.z; + vertices[ 21 ] = max.x; vertices[ 22 ] = max.y; vertices[ 23 ] = max.z; - return duration; + // -}; + 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.MorphBlendMesh.prototype.playAnimation = function ( name ) { + vertices[ 30 ] = min.x; vertices[ 31 ] = max.y; vertices[ 32 ] = min.z; + vertices[ 33 ] = min.x; vertices[ 34 ] = min.y; vertices[ 35 ] = min.z; - var animation = this.animationsMap[ name ]; + vertices[ 36 ] = min.x; vertices[ 37 ] = min.y; vertices[ 38 ] = min.z; + vertices[ 39 ] = max.x; vertices[ 40 ] = min.y; vertices[ 41 ] = min.z; - if ( animation ) { + vertices[ 42 ] = max.x; vertices[ 43 ] = min.y; vertices[ 44 ] = min.z; + vertices[ 45 ] = max.x; vertices[ 46 ] = max.y; vertices[ 47 ] = min.z; - animation.time = 0; - animation.active = true; + // - } else { + vertices[ 48 ] = max.x; vertices[ 49 ] = max.y; vertices[ 50 ] = max.z; + vertices[ 51 ] = max.x; vertices[ 52 ] = max.y; vertices[ 53 ] = min.z; - console.warn( "animation[" + name + "] undefined" ); + vertices[ 54 ] = min.x; vertices[ 55 ] = max.y; vertices[ 56 ] = max.z; + vertices[ 57 ] = min.x; vertices[ 58 ] = max.y; vertices[ 59 ] = min.z; - } + vertices[ 60 ] = min.x; vertices[ 61 ] = min.y; vertices[ 62 ] = max.z; + vertices[ 63 ] = min.x; vertices[ 64 ] = min.y; vertices[ 65 ] = min.z; -}; + vertices[ 66 ] = max.x; vertices[ 67 ] = min.y; vertices[ 68 ] = max.z; + vertices[ 69 ] = max.x; vertices[ 70 ] = min.y; vertices[ 71 ] = min.z; -THREE.MorphBlendMesh.prototype.stopAnimation = function ( name ) { + this.geometry.attributes.position.needsUpdate = true; - var animation = this.animationsMap[ name ]; + this.geometry.computeBoundingSphere(); - if ( animation ) { + this.matrix = object.matrixWorld; + this.matrixAutoUpdate = false; - animation.active = false; +}; - } +// File:src/extras/helpers/BoundingBoxHelper.js -}; +/** + * @author WestLangley / http://github.com/WestLangley + */ -THREE.MorphBlendMesh.prototype.update = function ( delta ) { +// a helper to show the world-axis-aligned bounding box for an object - for ( var i = 0, il = this.animationsList.length; i < il; i ++ ) { +THREE.BoundingBoxHelper = function ( object, hex ) { - var animation = this.animationsList[ i ]; + var color = ( hex !== undefined ) ? hex : 0x888888; - if ( ! animation.active ) continue; + this.object = object; - var frameTime = animation.duration / animation.length; + this.box = new THREE.Box3(); - animation.time += animation.direction * delta; + THREE.Mesh.call( this, new THREE.BoxGeometry( 1, 1, 1 ), new THREE.MeshBasicMaterial( { color: color, wireframe: true } ) ); - if ( animation.mirroredLoop ) { +}; - if ( animation.time > animation.duration || animation.time < 0 ) { +THREE.BoundingBoxHelper.prototype = Object.create( THREE.Mesh.prototype ); - animation.direction *= - 1; +THREE.BoundingBoxHelper.prototype.update = function () { - if ( animation.time > animation.duration ) { + this.box.setFromObject( this.object ); - animation.time = animation.duration; - animation.directionBackwards = true; + this.box.size( this.scale ); - } + this.box.center( this.position ); - if ( animation.time < 0 ) { +}; - animation.time = 0; - animation.directionBackwards = false; +// 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 ) { - } else { + var geometry = new THREE.Geometry(); + var material = new THREE.LineBasicMaterial( { color: 0xffffff, vertexColors: THREE.FaceColors } ); - animation.time = animation.time % animation.duration; + var pointMap = {}; - if ( animation.time < 0 ) animation.time += animation.duration; + // colors - } + var hexFrustum = 0xffaa00; + var hexCone = 0xff0000; + var hexUp = 0x00aaff; + var hexTarget = 0xffffff; + var hexCross = 0x333333; - var keyframe = animation.startFrame + THREE.Math.clamp( Math.floor( animation.time / frameTime ), 0, animation.length - 1 ); - var weight = animation.weight; + // near - if ( keyframe !== animation.currentFrame ) { + addLine( "n1", "n2", hexFrustum ); + addLine( "n2", "n4", hexFrustum ); + addLine( "n4", "n3", hexFrustum ); + addLine( "n3", "n1", hexFrustum ); - this.morphTargetInfluences[ animation.lastFrame ] = 0; - this.morphTargetInfluences[ animation.currentFrame ] = 1 * weight; + // far - this.morphTargetInfluences[ keyframe ] = 0; + addLine( "f1", "f2", hexFrustum ); + addLine( "f2", "f4", hexFrustum ); + addLine( "f4", "f3", hexFrustum ); + addLine( "f3", "f1", hexFrustum ); - animation.lastFrame = animation.currentFrame; - animation.currentFrame = keyframe; + // sides - } + addLine( "n1", "f1", hexFrustum ); + addLine( "n2", "f2", hexFrustum ); + addLine( "n3", "f3", hexFrustum ); + addLine( "n4", "f4", hexFrustum ); - var mix = ( animation.time % frameTime ) / frameTime; + // cone - if ( animation.directionBackwards ) mix = 1 - mix; + addLine( "p", "n1", hexCone ); + addLine( "p", "n2", hexCone ); + addLine( "p", "n3", hexCone ); + addLine( "p", "n4", hexCone ); - this.morphTargetInfluences[ animation.currentFrame ] = mix * weight; - this.morphTargetInfluences[ animation.lastFrame ] = ( 1 - mix ) * weight; + // up - } + addLine( "u1", "u2", hexUp ); + addLine( "u2", "u3", hexUp ); + addLine( "u3", "u1", hexUp ); -}; + // target -// File:src/extras/renderers/plugins/LensFlarePlugin.js + addLine( "c", "t", hexTarget ); + addLine( "p", "c", hexCross ); -/** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - */ + // cross -THREE.LensFlarePlugin = function () { + addLine( "cn1", "cn2", hexCross ); + addLine( "cn3", "cn4", hexCross ); - var flares = []; + addLine( "cf1", "cf2", hexCross ); + addLine( "cf3", "cf4", hexCross ); - var _gl, _renderer, _precision, _lensFlare = {}; + function addLine( a, b, hex ) { - this.init = function ( renderer ) { + addPoint( a, hex ); + addPoint( b, hex ); - _gl = renderer.context; - _renderer = renderer; + } - _precision = renderer.getPrecision(); + function addPoint( id, hex ) { - _lensFlare.vertices = new Float32Array( 8 + 8 ); - _lensFlare.faces = new Uint16Array( 6 ); + geometry.vertices.push( new THREE.Vector3() ); + geometry.colors.push( new THREE.Color( hex ) ); - var i = 0; - _lensFlare.vertices[ i ++ ] = - 1; _lensFlare.vertices[ i ++ ] = - 1; // vertex - _lensFlare.vertices[ i ++ ] = 0; _lensFlare.vertices[ i ++ ] = 0; // uv... etc. + if ( pointMap[ id ] === undefined ) { - _lensFlare.vertices[ i ++ ] = 1; _lensFlare.vertices[ i ++ ] = - 1; - _lensFlare.vertices[ i ++ ] = 1; _lensFlare.vertices[ i ++ ] = 0; + pointMap[ id ] = []; - _lensFlare.vertices[ i ++ ] = 1; _lensFlare.vertices[ i ++ ] = 1; - _lensFlare.vertices[ i ++ ] = 1; _lensFlare.vertices[ i ++ ] = 1; + } - _lensFlare.vertices[ i ++ ] = - 1; _lensFlare.vertices[ i ++ ] = 1; - _lensFlare.vertices[ i ++ ] = 0; _lensFlare.vertices[ i ++ ] = 1; + pointMap[ id ].push( geometry.vertices.length - 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; + } - // buffers + THREE.Line.call( this, geometry, material, THREE.LinePieces ); - _lensFlare.vertexBuffer = _gl.createBuffer(); - _lensFlare.elementBuffer = _gl.createBuffer(); + this.camera = camera; + this.matrix = camera.matrixWorld; + this.matrixAutoUpdate = false; - _gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, _lensFlare.vertices, _gl.STATIC_DRAW ); + this.pointMap = pointMap; - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.elementBuffer ); - _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.faces, _gl.STATIC_DRAW ); + this.update(); - // textures +}; - _lensFlare.tempTexture = _gl.createTexture(); - _lensFlare.occlusionTexture = _gl.createTexture(); +THREE.CameraHelper.prototype = Object.create( THREE.Line.prototype ); - _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.CameraHelper.prototype.update = function () { - _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 ); + var geometry, pointMap; + + var vector = new THREE.Vector3(); + var camera = new THREE.Camera(); - if ( _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) <= 0 ) { + var setPoint = function ( point, x, y, z ) { - _lensFlare.hasVertexTexture = false; - _lensFlare.program = createProgram( THREE.ShaderFlares[ "lensFlare" ], _precision ); + vector.set( x, y, z ).unproject( camera ); - } else { + var points = pointMap[ point ]; - _lensFlare.hasVertexTexture = true; - _lensFlare.program = createProgram( THREE.ShaderFlares[ "lensFlareVertexTexture" ], _precision ); + if ( points !== undefined ) { - } + for ( var i = 0, il = points.length; i < il; i ++ ) { - _lensFlare.attributes = {}; - _lensFlare.uniforms = {}; + geometry.vertices[ points[ i ] ].copy( vector ); - _lensFlare.attributes.vertex = _gl.getAttribLocation ( _lensFlare.program, "position" ); - _lensFlare.attributes.uv = _gl.getAttribLocation ( _lensFlare.program, "uv" ); + } - _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" ); + } }; + return function () { - /* - * 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. - * - */ - - this.render = function ( scene, camera, viewportWidth, viewportHeight ) { + geometry = this.geometry; + pointMap = this.pointMap; - flares.length = 0; + var w = 1, h = 1; - scene.traverseVisible( function ( child ) { + // we need just camera projection matrix + // world matrix must be identity - if ( child instanceof THREE.LensFlare ) { + camera.projectionMatrix.copy( this.camera.projectionMatrix ); - flares.push( child ); + // center / target - } + setPoint( "c", 0, 0, - 1 ); + setPoint( "t", 0, 0, 1 ); - } ); + // near - if ( flares.length === 0 ) return; + setPoint( "n1", - w, - h, - 1 ); + setPoint( "n2", w, - h, - 1 ); + setPoint( "n3", - w, h, - 1 ); + setPoint( "n4", w, h, - 1 ); - var tempPosition = new THREE.Vector3(); + // far - var invAspect = viewportHeight / viewportWidth, - halfViewportWidth = viewportWidth * 0.5, - halfViewportHeight = viewportHeight * 0.5; + setPoint( "f1", - w, - h, 1 ); + setPoint( "f2", w, - h, 1 ); + setPoint( "f3", - w, h, 1 ); + setPoint( "f4", w, h, 1 ); - var size = 16 / viewportHeight, - scale = new THREE.Vector2( size * invAspect, size ); + // up - var screenPosition = new THREE.Vector3( 1, 1, 0 ), - screenPositionPixels = new THREE.Vector2( 1, 1 ); + setPoint( "u1", w * 0.7, h * 1.1, - 1 ); + setPoint( "u2", - w * 0.7, h * 1.1, - 1 ); + setPoint( "u3", 0, h * 2, - 1 ); - var uniforms = _lensFlare.uniforms, - attributes = _lensFlare.attributes; + // cross - // set _lensFlare program and reset blending + setPoint( "cf1", - w, 0, 1 ); + setPoint( "cf2", w, 0, 1 ); + setPoint( "cf3", 0, - h, 1 ); + setPoint( "cf4", 0, h, 1 ); - _gl.useProgram( _lensFlare.program ); + setPoint( "cn1", - w, 0, - 1 ); + setPoint( "cn2", w, 0, - 1 ); + setPoint( "cn3", 0, - h, - 1 ); + setPoint( "cn4", 0, h, - 1 ); - _gl.enableVertexAttribArray( _lensFlare.attributes.vertex ); - _gl.enableVertexAttribArray( _lensFlare.attributes.uv ); + geometry.verticesNeedUpdate = true; - // loop through all lens flares to update their occlusion and positions - // setup gl and common used attribs/unforms + }; - _gl.uniform1i( uniforms.occlusionMap, 0 ); - _gl.uniform1i( uniforms.map, 1 ); +}(); - _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 ); +// File:src/extras/helpers/DirectionalLightHelper.js - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.elementBuffer ); +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley + */ - _gl.disable( _gl.CULL_FACE ); - _gl.depthMask( false ); +THREE.DirectionalLightHelper = function ( light, size ) { - for ( var i = 0, l = flares.length; i < l; i ++ ) { + THREE.Object3D.call( this ); - size = 16 / viewportHeight; - scale.set( size * invAspect, size ); + this.light = light; + this.light.updateMatrixWorld(); - // calc object screen position + this.matrix = light.matrixWorld; + this.matrixAutoUpdate = false; - var flare = flares[ i ]; - - tempPosition.set( flare.matrixWorld.elements[12], flare.matrixWorld.elements[13], flare.matrixWorld.elements[14] ); + size = size || 1; - tempPosition.applyMatrix4( camera.matrixWorldInverse ); - tempPosition.applyProjection( camera.projectionMatrix ); + 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 ) + ); - // setup arrays for gl programs + var material = new THREE.LineBasicMaterial( { fog: false } ); + material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); - screenPosition.copy( tempPosition ) + this.lightPlane = new THREE.Line( geometry, material ); + this.add( this.lightPlane ); - screenPositionPixels.x = screenPosition.x * halfViewportWidth + halfViewportWidth; - screenPositionPixels.y = screenPosition.y * halfViewportHeight + halfViewportHeight; + geometry = new THREE.Geometry(); + geometry.vertices.push( + new THREE.Vector3(), + new THREE.Vector3() + ); - // screen cull + material = new THREE.LineBasicMaterial( { fog: false } ); + material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); - if ( _lensFlare.hasVertexTexture || ( - screenPositionPixels.x > 0 && - screenPositionPixels.x < viewportWidth && - screenPositionPixels.y > 0 && - screenPositionPixels.y < viewportHeight ) ) { + this.targetLine = new THREE.Line( geometry, material ); + this.add( this.targetLine ); - // save current RGB to temp texture + this.update(); - _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 ); +}; +THREE.DirectionalLightHelper.prototype = Object.create( THREE.Object3D.prototype ); - // render pink quad +THREE.DirectionalLightHelper.prototype.dispose = function () { - _gl.uniform1i( uniforms.renderType, 0 ); - _gl.uniform2f( uniforms.scale, scale.x, scale.y ); - _gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); + this.lightPlane.geometry.dispose(); + this.lightPlane.material.dispose(); + this.targetLine.geometry.dispose(); + this.targetLine.material.dispose(); +}; - _gl.disable( _gl.BLEND ); - _gl.enable( _gl.DEPTH_TEST ); +THREE.DirectionalLightHelper.prototype.update = function () { - _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + var v3 = new THREE.Vector3(); + return function () { - // copy result to occlusionMap + v1.setFromMatrixPosition( this.light.matrixWorld ); + v2.setFromMatrixPosition( this.light.target.matrixWorld ); + v3.subVectors( v2, v1 ); - _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.lookAt( v3 ); + this.lightPlane.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + this.targetLine.geometry.vertices[ 1 ].copy( v3 ); + this.targetLine.geometry.verticesNeedUpdate = true; + this.targetLine.material.color.copy( this.lightPlane.material.color ); - // restore graphics + }; - _gl.uniform1i( uniforms.renderType, 1 ); - _gl.disable( _gl.DEPTH_TEST ); +}(); - _gl.activeTexture( _gl.TEXTURE1 ); - _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture ); - _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); +// File:src/extras/helpers/EdgesHelper.js +/** + * @author WestLangley / http://github.com/WestLangley + */ - // update object positions +THREE.EdgesHelper = function ( object, hex ) { - flare.positionScreen.copy( screenPosition ) + var color = ( hex !== undefined ) ? hex : 0xffffff; - if ( flare.customUpdateCallback ) { + var edge = [ 0, 0 ], hash = {}; + var sortFunction = function ( a, b ) { return a - b }; - flare.customUpdateCallback( flare ); + var keys = [ 'a', 'b', 'c' ]; + var geometry = new THREE.BufferGeometry(); - } else { + var geometry2 = object.geometry.clone(); - flare.updateLensFlares(); + geometry2.mergeVertices(); + geometry2.computeFaceNormals(); - } + var vertices = geometry2.vertices; + var faces = geometry2.faces; + var numEdges = 0; - // render flares + for ( var i = 0, l = faces.length; i < l; i ++ ) { - _gl.uniform1i( uniforms.renderType, 2 ); - _gl.enable( _gl.BLEND ); + var face = faces[ i ]; - for ( var j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) { + for ( var j = 0; j < 3; j ++ ) { - var sprite = flare.lensFlares[ j ]; + edge[ 0 ] = face[ keys[ j ] ]; + edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; + edge.sort( sortFunction ); - if ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) { + var key = edge.toString(); - screenPosition.x = sprite.x; - screenPosition.y = sprite.y; - screenPosition.z = sprite.z; + if ( hash[ key ] === undefined ) { - size = sprite.size * sprite.scale / viewportHeight; + hash[ key ] = { vert1: edge[ 0 ], vert2: edge[ 1 ], face1: i, face2: undefined }; + numEdges ++; - scale.x = size * invAspect; - scale.y = size; + } else { - _gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); - _gl.uniform2f( uniforms.scale, scale.x, scale.y ); - _gl.uniform1f( uniforms.rotation, sprite.rotation ); + hash[ key ].face2 = i; - _gl.uniform1f( uniforms.opacity, sprite.opacity ); - _gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b ); + } - _renderer.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst ); - _renderer.setTexture( sprite.texture, 1 ); + } - _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); + } - } + var coords = new Float32Array( numEdges * 2 * 3 ); - } + var index = 0; - } + for ( var key in hash ) { - } + var h = hash[ key ]; - // restore gl + if ( h.face2 === undefined || faces[ h.face1 ].normal.dot( faces[ h.face2 ].normal ) < 0.9999 ) { // hardwired const OK - _gl.enable( _gl.CULL_FACE ); - _gl.enable( _gl.DEPTH_TEST ); - _gl.depthMask( true ); + var vertex = vertices[ h.vert1 ]; + coords[ index ++ ] = vertex.x; + coords[ index ++ ] = vertex.y; + coords[ index ++ ] = vertex.z; - }; + vertex = vertices[ h.vert2 ]; + coords[ index ++ ] = vertex.x; + coords[ index ++ ] = vertex.y; + coords[ index ++ ] = vertex.z; - function createProgram ( shader, precision ) { + } - var program = _gl.createProgram(); + } - var fragmentShader = _gl.createShader( _gl.FRAGMENT_SHADER ); - var vertexShader = _gl.createShader( _gl.VERTEX_SHADER ); + geometry.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); - var prefix = "precision " + precision + " float;\n"; + THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color } ), THREE.LinePieces ); - _gl.shaderSource( fragmentShader, prefix + shader.fragmentShader ); - _gl.shaderSource( vertexShader, prefix + shader.vertexShader ); + this.matrix = object.matrixWorld; + this.matrixAutoUpdate = false; - _gl.compileShader( fragmentShader ); - _gl.compileShader( vertexShader ); +}; - _gl.attachShader( program, fragmentShader ); - _gl.attachShader( program, vertexShader ); +THREE.EdgesHelper.prototype = Object.create( THREE.Line.prototype ); - _gl.linkProgram( program ); +// File:src/extras/helpers/FaceNormalsHelper.js - return program; +/** + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley +*/ - }; +THREE.FaceNormalsHelper = function ( object, size, hex, linewidth ) { -}; + this.object = object; -// File:src/extras/renderers/plugins/ShadowMapPlugin.js + this.size = ( size !== undefined ) ? size : 1; -/** - * @author alteredq / http://alteredqualia.com/ - */ + var color = ( hex !== undefined ) ? hex : 0xffff00; -THREE.ShadowMapPlugin = function () { + var width = ( linewidth !== undefined ) ? linewidth : 1; - var _gl, - _renderer, - _depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin, + var geometry = new THREE.Geometry(); - _frustum = new THREE.Frustum(), - _projScreenMatrix = new THREE.Matrix4(), + var faces = this.object.geometry.faces; - _min = new THREE.Vector3(), - _max = new THREE.Vector3(), + for ( var i = 0, l = faces.length; i < l; i ++ ) { - _matrixPosition = new THREE.Vector3(), - - _renderList = []; + geometry.vertices.push( new THREE.Vector3(), new THREE.Vector3() ); - this.init = function ( renderer ) { + } - _gl = renderer.context; - _renderer = renderer; + THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces ); - var depthShader = THREE.ShaderLib[ "depthRGBA" ]; - var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms ); + this.matrixAutoUpdate = false; - _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 } ); + this.normalMatrix = new THREE.Matrix3(); - _depthMaterial._shadowPass = true; - _depthMaterialMorph._shadowPass = true; - _depthMaterialSkin._shadowPass = true; - _depthMaterialMorphSkin._shadowPass = true; + this.update(); - }; +}; - this.render = function ( scene, camera ) { +THREE.FaceNormalsHelper.prototype = Object.create( THREE.Line.prototype ); - if ( ! ( _renderer.shadowMapEnabled && _renderer.shadowMapAutoUpdate ) ) return; +THREE.FaceNormalsHelper.prototype.update = function () { - this.update( scene, camera ); + var vertices = this.geometry.vertices; - }; + var object = this.object; + var objectVertices = object.geometry.vertices; + var objectFaces = object.geometry.faces; + var objectWorldMatrix = object.matrixWorld; - this.update = function ( scene, camera ) { + object.updateMatrixWorld( true ); - var i, il, j, jl, n, + this.normalMatrix.getNormalMatrix( objectWorldMatrix ); - shadowMap, shadowMatrix, shadowCamera, - program, buffer, material, - webglObject, object, light, + for ( var i = 0, i2 = 0, l = objectFaces.length; i < l; i ++, i2 += 2 ) { - lights = [], - k = 0, + var face = objectFaces[ i ]; - fog = null; + vertices[ i2 ].copy( objectVertices[ face.a ] ) + .add( objectVertices[ face.b ] ) + .add( objectVertices[ face.c ] ) + .divideScalar( 3 ) + .applyMatrix4( objectWorldMatrix ); - // set GL state for depth map + vertices[ i2 + 1 ].copy( face.normal ) + .applyMatrix3( this.normalMatrix ) + .normalize() + .multiplyScalar( this.size ) + .add( vertices[ i2 ] ); - _gl.clearColor( 1, 1, 1, 1 ); - _gl.disable( _gl.BLEND ); + } - _gl.enable( _gl.CULL_FACE ); - _gl.frontFace( _gl.CCW ); + this.geometry.verticesNeedUpdate = true; - if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) { + return this; - _gl.cullFace( _gl.FRONT ); +}; - } else { - _gl.cullFace( _gl.BACK ); +// File:src/extras/helpers/GridHelper.js - } +/** + * @author mrdoob / http://mrdoob.com/ + */ - _renderer.setDepthTest( true ); +THREE.GridHelper = function ( size, step ) { - // preprocess lights - // - skip lights that are not casting shadows - // - create virtual lights for cascaded shadow maps + var geometry = new THREE.Geometry(); + var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } ); - for ( i = 0, il = scene.__lights.length; i < il; i ++ ) { + this.color1 = new THREE.Color( 0x444444 ); + this.color2 = new THREE.Color( 0x888888 ); - light = scene.__lights[ i ]; + for ( var i = - size; i <= size; i += step ) { - if ( ! light.castShadow ) continue; + 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 ) + ); - if ( ( light instanceof THREE.DirectionalLight ) && light.shadowCascade ) { + var color = i === 0 ? this.color1 : this.color2; - for ( n = 0; n < light.shadowCascadeCount; n ++ ) { + geometry.colors.push( color, color, color, color ); - var virtualLight; + } - if ( ! light.shadowCascadeArray[ n ] ) { + THREE.Line.call( this, geometry, material, THREE.LinePieces ); - virtualLight = createVirtualLight( light, n ); - virtualLight.originalCamera = camera; +}; - var gyro = new THREE.Gyroscope(); - gyro.position.copy( light.shadowCascadeOffset ); +THREE.GridHelper.prototype = Object.create( THREE.Line.prototype ); - gyro.add( virtualLight ); - gyro.add( virtualLight.target ); +THREE.GridHelper.prototype.setColors = function( colorCenterLine, colorGrid ) { - camera.add( gyro ); + this.color1.set( colorCenterLine ); + this.color2.set( colorGrid ); - light.shadowCascadeArray[ n ] = virtualLight; + this.geometry.colorsNeedUpdate = true; - console.log( "Created virtualLight", virtualLight ); +} - } else { +// File:src/extras/helpers/HemisphereLightHelper.js - virtualLight = light.shadowCascadeArray[ n ]; +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ - } +THREE.HemisphereLightHelper = function ( light, sphereSize, arrowLength, domeSize ) { - updateVirtualLight( light, n ); + THREE.Object3D.call( this ); - lights[ k ] = virtualLight; - k ++; + this.light = light; + this.light.updateMatrixWorld(); - } + this.matrix = light.matrixWorld; + this.matrixAutoUpdate = false; - } else { + this.colors = [ new THREE.Color(), new THREE.Color() ]; - lights[ k ] = light; - k ++; + var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 ); + geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) ); - } + for ( var i = 0, il = 8; i < il; i ++ ) { - } + geometry.faces[ i ].color = this.colors[ i < 4 ? 0 : 1 ]; - // render depth map + } - for ( i = 0, il = lights.length; i < il; i ++ ) { + var material = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors, wireframe: true } ); - light = lights[ i ]; + this.lightSphere = new THREE.Mesh( geometry, material ); + this.add( this.lightSphere ); - if ( ! light.shadowMap ) { + this.update(); - var shadowFilter = THREE.LinearFilter; +}; - if ( _renderer.shadowMapType === THREE.PCFSoftShadowMap ) { +THREE.HemisphereLightHelper.prototype = Object.create( THREE.Object3D.prototype ); - shadowFilter = THREE.NearestFilter; +THREE.HemisphereLightHelper.prototype.dispose = function () { + this.lightSphere.geometry.dispose(); + this.lightSphere.material.dispose(); +}; - } +THREE.HemisphereLightHelper.prototype.update = function () { - var pars = { minFilter: shadowFilter, magFilter: shadowFilter, format: THREE.RGBAFormat }; + var vector = new THREE.Vector3(); - light.shadowMap = new THREE.WebGLRenderTarget( light.shadowMapWidth, light.shadowMapHeight, pars ); - light.shadowMapSize = new THREE.Vector2( light.shadowMapWidth, light.shadowMapHeight ); + return function () { - light.shadowMatrix = new THREE.Matrix4(); + this.colors[ 0 ].copy( this.light.color ).multiplyScalar( this.light.intensity ); + this.colors[ 1 ].copy( this.light.groundColor ).multiplyScalar( this.light.intensity ); - } + this.lightSphere.lookAt( vector.setFromMatrixPosition( this.light.matrixWorld ).negate() ); + this.lightSphere.geometry.colorsNeedUpdate = true; - if ( ! light.shadowCamera ) { + } - if ( light instanceof THREE.SpotLight ) { +}(); - light.shadowCamera = new THREE.PerspectiveCamera( light.shadowCameraFov, light.shadowMapWidth / light.shadowMapHeight, light.shadowCameraNear, light.shadowCameraFar ); +// File:src/extras/helpers/PointLightHelper.js - } else if ( light instanceof THREE.DirectionalLight ) { +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ - light.shadowCamera = new THREE.OrthographicCamera( light.shadowCameraLeft, light.shadowCameraRight, light.shadowCameraTop, light.shadowCameraBottom, light.shadowCameraNear, light.shadowCameraFar ); +THREE.PointLightHelper = function ( light, sphereSize ) { - } else { + this.light = light; + this.light.updateMatrixWorld(); - console.error( "Unsupported light type for shadow" ); - continue; + 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 ); - } + THREE.Mesh.call( this, geometry, material ); - scene.add( light.shadowCamera ); + this.matrix = this.light.matrixWorld; + this.matrixAutoUpdate = false; - if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); + /* + var distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 ); + var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); - } + this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); + this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); - if ( light.shadowCameraVisible && ! light.cameraHelper ) { + var d = light.distance; - light.cameraHelper = new THREE.CameraHelper( light.shadowCamera ); - light.shadowCamera.add( light.cameraHelper ); + if ( d === 0.0 ) { - } + this.lightDistance.visible = false; - if ( light.isVirtual && virtualLight.originalCamera == camera ) { + } else { - updateShadowCamera( camera, light ); + this.lightDistance.scale.set( d, d, d ); - } + } - shadowMap = light.shadowMap; - shadowMatrix = light.shadowMatrix; - shadowCamera = light.shadowCamera; + this.add( this.lightDistance ); + */ - shadowCamera.position.setFromMatrixPosition( light.matrixWorld ); - _matrixPosition.setFromMatrixPosition( light.target.matrixWorld ); - shadowCamera.lookAt( _matrixPosition ); - shadowCamera.updateMatrixWorld(); +}; - shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld ); +THREE.PointLightHelper.prototype = Object.create( THREE.Mesh.prototype ); - if ( light.cameraHelper ) light.cameraHelper.visible = light.shadowCameraVisible; - if ( light.shadowCameraVisible ) light.cameraHelper.update(); +THREE.PointLightHelper.prototype.dispose = function () { - // compute shadow matrix + this.geometry.dispose(); + this.material.dispose(); +}; - 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 ); +THREE.PointLightHelper.prototype.update = function () { - shadowMatrix.multiply( shadowCamera.projectionMatrix ); - shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); + this.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); - // update camera matrices and frustum + /* + var d = this.light.distance; - _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); - _frustum.setFromMatrix( _projScreenMatrix ); + if ( d === 0.0 ) { - // render shadow map + this.lightDistance.visible = false; - _renderer.setRenderTarget( shadowMap ); - _renderer.clear(); + } else { - // set object matrices & frustum culling + this.lightDistance.visible = true; + this.lightDistance.scale.set( d, d, d ); - _renderList.length = 0; - projectObject(scene,scene,shadowCamera); + } + */ +}; - // render regular objects +// File:src/extras/helpers/SkeletonHelper.js - var objectMaterial, useMorphing, useSkinning; +/** + * @author Sean Griffin / http://twitter.com/sgrif + * @author Michael Guerrero / http://realitymeltdown.com + * @author mrdoob / http://mrdoob.com/ + * @author ikerr / http://verold.com + */ - for ( j = 0, jl = _renderList.length; j < jl; j ++ ) { +THREE.SkeletonHelper = function ( object ) { - webglObject = _renderList[ j ]; + this.bones = this.getBoneList( object ); - object = webglObject.object; - buffer = webglObject.buffer; + var geometry = new THREE.Geometry(); - // culling is overriden globally for all objects - // while rendering depth map + for ( var i = 0; i < this.bones.length; i ++ ) { - // 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) + var bone = this.bones[ i ]; - objectMaterial = getObjectMaterial( object ); + if ( bone.parent instanceof THREE.Bone ) { - useMorphing = object.geometry.morphTargets !== undefined && object.geometry.morphTargets.length > 0 && objectMaterial.morphTargets; - useSkinning = object instanceof THREE.SkinnedMesh && objectMaterial.skinning; + 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 ) ); - if ( object.customDepthMaterial ) { + } - material = object.customDepthMaterial; + } - } else if ( useSkinning ) { + var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors, depthTest: false, depthWrite: false, transparent: true } ); - material = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin; + THREE.Line.call( this, geometry, material, THREE.LinePieces ); - } else if ( useMorphing ) { + this.root = object; - material = _depthMaterialMorph; + this.matrix = object.matrixWorld; + this.matrixAutoUpdate = false; - } else { + this.update(); - material = _depthMaterial; +}; - } - _renderer.setMaterialFaces( objectMaterial ); +THREE.SkeletonHelper.prototype = Object.create( THREE.Line.prototype ); - if ( buffer instanceof THREE.BufferGeometry ) { +THREE.SkeletonHelper.prototype.getBoneList = function( object ) { - _renderer.renderBufferDirect( shadowCamera, scene.__lights, fog, material, buffer, object ); + var boneList = []; - } else { + if ( object instanceof THREE.Bone ) { - _renderer.renderBuffer( shadowCamera, scene.__lights, fog, material, buffer, object ); + boneList.push( object ); - } + } - } + for ( var i = 0; i < object.children.length; i ++ ) { - // set matrices and render immediate objects + boneList.push.apply( boneList, this.getBoneList( object.children[ i ] ) ); - var renderList = scene.__webglObjectsImmediate; + } - for ( j = 0, jl = renderList.length; j < jl; j ++ ) { + return boneList; - webglObject = renderList[ j ]; - object = webglObject.object; +}; - if ( object.visible && object.castShadow ) { +THREE.SkeletonHelper.prototype.update = function () { - object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); + var geometry = this.geometry; - _renderer.renderImmediateObject( shadowCamera, scene.__lights, fog, _depthMaterial, object ); + var matrixWorldInv = new THREE.Matrix4().getInverse( this.root.matrixWorld ); - } + var boneMatrix = new THREE.Matrix4(); - } + var j = 0; - } + for ( var i = 0; i < this.bones.length; i ++ ) { - // restore GL state + var bone = this.bones[ i ]; - var clearColor = _renderer.getClearColor(), - clearAlpha = _renderer.getClearAlpha(); + if ( bone.parent instanceof THREE.Bone ) { - _gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha ); - _gl.enable( _gl.BLEND ); + boneMatrix.multiplyMatrices( matrixWorldInv, bone.matrixWorld ); + geometry.vertices[ j ].setFromMatrixPosition( boneMatrix ); - if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) { + boneMatrix.multiplyMatrices( matrixWorldInv, bone.parent.matrixWorld ); + geometry.vertices[ j + 1 ].setFromMatrixPosition( boneMatrix ); - _gl.cullFace( _gl.BACK ); + j += 2; } - }; - - 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); - } - - } } - function createVirtualLight( light, cascade ) { + geometry.verticesNeedUpdate = true; - var virtualLight = new THREE.DirectionalLight(); + geometry.computeBoundingSphere(); - virtualLight.isVirtual = true; +}; - virtualLight.onlyShadow = true; - virtualLight.castShadow = true; +// File:src/extras/helpers/SpotLightHelper.js - virtualLight.shadowCameraNear = light.shadowCameraNear; - virtualLight.shadowCameraFar = light.shadowCameraFar; +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley +*/ - virtualLight.shadowCameraLeft = light.shadowCameraLeft; - virtualLight.shadowCameraRight = light.shadowCameraRight; - virtualLight.shadowCameraBottom = light.shadowCameraBottom; - virtualLight.shadowCameraTop = light.shadowCameraTop; +THREE.SpotLightHelper = function ( light ) { - virtualLight.shadowCameraVisible = light.shadowCameraVisible; + THREE.Object3D.call( this ); - virtualLight.shadowDarkness = light.shadowDarkness; + this.light = light; + this.light.updateMatrixWorld(); - virtualLight.shadowBias = light.shadowCascadeBias[ cascade ]; - virtualLight.shadowMapWidth = light.shadowCascadeWidth[ cascade ]; - virtualLight.shadowMapHeight = light.shadowCascadeHeight[ cascade ]; + this.matrix = light.matrixWorld; + this.matrixAutoUpdate = false; - virtualLight.pointsWorld = []; - virtualLight.pointsFrustum = []; + var geometry = new THREE.CylinderGeometry( 0, 1, 1, 8, 1, true ); - var pointsWorld = virtualLight.pointsWorld, - pointsFrustum = virtualLight.pointsFrustum; + geometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, - 0.5, 0 ) ); + geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) ); - for ( var i = 0; i < 8; i ++ ) { + var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } ); - pointsWorld[ i ] = new THREE.Vector3(); - pointsFrustum[ i ] = new THREE.Vector3(); + this.cone = new THREE.Mesh( geometry, material ); + this.add( this.cone ); - } + this.update(); - 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 ); +THREE.SpotLightHelper.prototype = Object.create( THREE.Object3D.prototype ); - pointsFrustum[ 4 ].set( - 1, - 1, farZ ); - pointsFrustum[ 5 ].set( 1, - 1, farZ ); - pointsFrustum[ 6 ].set( - 1, 1, farZ ); - pointsFrustum[ 7 ].set( 1, 1, farZ ); +THREE.SpotLightHelper.prototype.dispose = function () { + this.cone.geometry.dispose(); + this.cone.material.dispose(); +}; - return virtualLight; +THREE.SpotLightHelper.prototype.update = function () { - } + var vector = new THREE.Vector3(); + var vector2 = new THREE.Vector3(); - // Synchronize virtual light with the original light + return function () { - function updateVirtualLight( light, cascade ) { + var coneLength = this.light.distance ? this.light.distance : 10000; + var coneWidth = coneLength * Math.tan( this.light.angle ); - var virtualLight = light.shadowCascadeArray[ cascade ]; + this.cone.scale.set( coneWidth, coneWidth, coneLength ); - virtualLight.position.copy( light.position ); - virtualLight.target.position.copy( light.target.position ); - virtualLight.lookAt( virtualLight.target ); + vector.setFromMatrixPosition( this.light.matrixWorld ); + vector2.setFromMatrixPosition( this.light.target.matrixWorld ); - virtualLight.shadowCameraVisible = light.shadowCameraVisible; - virtualLight.shadowDarkness = light.shadowDarkness; + this.cone.lookAt( vector2.sub( vector ) ); - virtualLight.shadowBias = light.shadowCascadeBias[ cascade ]; + this.cone.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); - var nearZ = light.shadowCascadeNearZ[ cascade ]; - var farZ = light.shadowCascadeFarZ[ cascade ]; + }; - var pointsFrustum = virtualLight.pointsFrustum; +}(); - pointsFrustum[ 0 ].z = nearZ; - pointsFrustum[ 1 ].z = nearZ; - pointsFrustum[ 2 ].z = nearZ; - pointsFrustum[ 3 ].z = nearZ; +// File:src/extras/helpers/VertexNormalsHelper.js - pointsFrustum[ 4 ].z = farZ; - pointsFrustum[ 5 ].z = farZ; - pointsFrustum[ 6 ].z = farZ; - pointsFrustum[ 7 ].z = farZ; +/** + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley +*/ - } +THREE.VertexNormalsHelper = function ( object, size, hex, linewidth ) { - // Fit shadow camera's ortho frustum to camera frustum + this.object = object; - function updateShadowCamera( camera, light ) { + this.size = ( size !== undefined ) ? size : 1; - var shadowCamera = light.shadowCamera, - pointsFrustum = light.pointsFrustum, - pointsWorld = light.pointsWorld; + var color = ( hex !== undefined ) ? hex : 0xff0000; - _min.set( Infinity, Infinity, Infinity ); - _max.set( - Infinity, - Infinity, - Infinity ); + var width = ( linewidth !== undefined ) ? linewidth : 1; - for ( var i = 0; i < 8; i ++ ) { + var geometry = new THREE.Geometry(); - var p = pointsWorld[ i ]; + var vertices = object.geometry.vertices; - p.copy( pointsFrustum[ i ] ); - THREE.ShadowMapPlugin.__projector.unprojectVector( p, camera ); + var faces = object.geometry.faces; - p.applyMatrix4( shadowCamera.matrixWorldInverse ); + for ( var i = 0, l = faces.length; i < l; i ++ ) { - if ( p.x < _min.x ) _min.x = p.x; - if ( p.x > _max.x ) _max.x = p.x; + var face = faces[ i ]; - if ( p.y < _min.y ) _min.y = p.y; - if ( p.y > _max.y ) _max.y = p.y; + for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { - if ( p.z < _min.z ) _min.z = p.z; - if ( p.z > _max.z ) _max.z = p.z; + geometry.vertices.push( new THREE.Vector3(), new THREE.Vector3() ); } - shadowCamera.left = _min.x; - shadowCamera.right = _max.x; - shadowCamera.top = _max.y; - shadowCamera.bottom = _min.y; - - // can't really fit near/far - //shadowCamera.near = _min.z; - //shadowCamera.far = _max.z; - - 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 ); +}()); - } else { +// File:src/extras/helpers/WireframeHelper.js - _gl.uniform2f( uniforms.uvOffset, 0, 0 ); - _gl.uniform2f( uniforms.uvScale, 1, 1 ); +/** + * @author mrdoob / http://mrdoob.com/ + */ - } +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; }