diff --git a/build/three.js b/build/three.js index 13fe97a329c97f96aa8d573e8d31f4846a81b4ea..8cdb5481e382e5b760e2cff5fc81f07a713fe2d0 100644 --- a/build/three.js +++ b/build/three.js @@ -266,6 +266,11 @@ THREE.RGB_PVRTC_2BPPV1_Format = 2101; THREE.RGBA_PVRTC_4BPPV1_Format = 2102; THREE.RGBA_PVRTC_2BPPV1_Format = 2103; +// Loop styles for AnimationAction + +THREE.LoopOnce = 2200; +THREE.LoopRepeat = 2201; +THREE.LoopPingPong = 2202; // DEPRECATED @@ -843,7 +848,7 @@ THREE.ColorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ * @author WestLangley / http://github.com/WestLangley - * @author bhouston / http://exocortex.com + * @author bhouston / http://clara.io */ THREE.Quaternion = function ( x, y, z, w ) { @@ -3402,7 +3407,7 @@ THREE.Vector4.prototype = { /** * @author mrdoob / http://mrdoob.com/ * @author WestLangley / http://github.com/WestLangley - * @author bhouston / http://exocortex.com + * @author bhouston / http://clara.io */ THREE.Euler = function ( x, y, z, order ) { @@ -3728,7 +3733,7 @@ THREE.Euler.prototype = { // File:src/math/Line3.js /** - * @author bhouston / http://exocortex.com + * @author bhouston / http://clara.io */ THREE.Line3 = function ( start, end ) { @@ -3857,7 +3862,7 @@ THREE.Line3.prototype = { // File:src/math/Box2.js /** - * @author bhouston / http://exocortex.com + * @author bhouston / http://clara.io */ THREE.Box2 = function ( min, max ) { @@ -4096,7 +4101,7 @@ THREE.Box2.prototype = { // File:src/math/Box3.js /** - * @author bhouston / http://exocortex.com + * @author bhouston / http://clara.io * @author WestLangley / http://github.com/WestLangley */ @@ -4455,7 +4460,7 @@ THREE.Box3.prototype = { /** * @author alteredq / http://alteredqualia.com/ * @author WestLangley / http://github.com/WestLangley - * @author bhouston / http://exocortex.com + * @author bhouston / http://clara.io */ THREE.Matrix3 = function () { @@ -4759,7 +4764,7 @@ THREE.Matrix3.prototype = { * @author alteredq / http://alteredqualia.com/ * @author mikael emtinger / http://gomo.se/ * @author timknip / http://www.floorplanner.com/ - * @author bhouston / http://exocortex.com + * @author bhouston / http://clara.io * @author WestLangley / http://github.com/WestLangley */ @@ -5790,7 +5795,7 @@ THREE.Matrix4.prototype = { // File:src/math/Ray.js /** - * @author bhouston / http://exocortex.com + * @author bhouston / http://clara.io */ THREE.Ray = function ( origin, direction ) { @@ -6325,7 +6330,7 @@ THREE.Ray.prototype = { // File:src/math/Sphere.js /** - * @author bhouston / http://exocortex.com + * @author bhouston / http://clara.io * @author mrdoob / http://mrdoob.com/ */ @@ -6483,7 +6488,7 @@ THREE.Sphere.prototype = { /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ - * @author bhouston / http://exocortex.com + * @author bhouston / http://clara.io */ THREE.Frustum = function ( p0, p1, p2, p3, p4, p5 ) { @@ -6664,7 +6669,7 @@ THREE.Frustum.prototype = { // File:src/math/Plane.js /** - * @author bhouston / http://exocortex.com + * @author bhouston / http://clara.io */ THREE.Plane = function ( normal, constant ) { @@ -7250,7 +7255,7 @@ THREE.Spline = function ( points ) { // File:src/math/Triangle.js /** - * @author bhouston / http://exocortex.com + * @author bhouston / http://clara.io * @author mrdoob / http://mrdoob.com/ */ @@ -7632,7 +7637,7 @@ THREE.EventDispatcher.prototype = { /** * @author mrdoob / http://mrdoob.com/ - * @author bhouston / http://exocortex.com/ + * @author bhouston / http://clara.io/ * @author stephomi / http://stephaneginier.com/ */ @@ -9242,7 +9247,7 @@ THREE.InterleavedBufferAttribute.prototype = { * @author alteredq / http://alteredqualia.com/ * @author mikael emtinger / http://gomo.se/ * @author zz85 / http://www.lab4games.net/zz85/blog - * @author bhouston / http://exocortex.com + * @author bhouston / http://clara.io */ THREE.Geometry = function () { @@ -10724,6 +10729,8 @@ THREE.BufferGeometry.prototype = { console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' ); this.setIndex( attribute ); + return; + } this.attributes[ name ] = attribute; @@ -11728,2829 +11735,3223 @@ THREE.InstancedBufferGeometry.prototype.copy = function ( source ) { THREE.EventDispatcher.prototype.apply( THREE.InstancedBufferGeometry.prototype ); -// File:src/cameras/Camera.js +// File:src/animation/AnimationAction.js /** - * @author mrdoob / http://mrdoob.com/ - * @author mikael emtinger / http://gomo.se/ - * @author WestLangley / http://github.com/WestLangley -*/ - -THREE.Camera = function () { + * + * A clip that has been explicitly scheduled. + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ - THREE.Object3D.call( this ); +THREE.AnimationAction = function ( clip, startTime, timeScale, weight, loop ) { - this.type = 'Camera'; + if( clip === undefined ) throw new Error( 'clip is null' ); + this.clip = clip; + this.localRoot = null; + this.startTime = startTime || 0; + this.timeScale = timeScale || 1; + this.weight = weight || 1; + this.loop = loop || THREE.LoopRepeat; + this.loopCount = 0; + this.enabled = true; // allow for easy disabling of the action. - this.matrixWorldInverse = new THREE.Matrix4(); - this.projectionMatrix = new THREE.Matrix4(); + this.actionTime = - this.startTime; + this.clipTime = 0; + this.propertyBindings = []; }; -THREE.Camera.prototype = Object.create( THREE.Object3D.prototype ); -THREE.Camera.prototype.constructor = THREE.Camera; +/* +THREE.LoopOnce = 2200; +THREE.LoopRepeat = 2201; +THREE.LoopPingPing = 2202; +*/ -THREE.Camera.prototype.getWorldDirection = function () { +THREE.AnimationAction.prototype = { - var quaternion = new THREE.Quaternion(); + constructor: THREE.AnimationAction, - return function ( optionalTarget ) { + setLocalRoot: function( localRoot ) { - var result = optionalTarget || new THREE.Vector3(); + this.localRoot = localRoot; - this.getWorldQuaternion( quaternion ); + return this; + + }, - return result.set( 0, 0, - 1 ).applyQuaternion( quaternion ); + updateTime: function( clipDeltaTime ) { - }; + var previousClipTime = this.clipTime; + var previousLoopCount = this.loopCount; + var previousActionTime = this.actionTime; -}(); + var duration = this.clip.duration; + + this.actionTime = this.actionTime + clipDeltaTime; + + if( this.loop === THREE.LoopOnce ) { -THREE.Camera.prototype.lookAt = function () { + this.loopCount = 0; + this.clipTime = Math.min( Math.max( this.actionTime, 0 ), duration ); + + // if time is changed since last time, see if we have hit a start/end limit + if( this.clipTime !== previousClipTime ) { - // This routine does not support cameras with rotated and/or translated parent(s) + if( this.clipTime === duration ) { - var m1 = new THREE.Matrix4(); + this.mixer.dispatchEvent( { type: 'finished', action: this, direction: 1 } ); - return function ( vector ) { + } + else if( this.clipTime === 0 ) { - m1.lookAt( this.position, vector, this.up ); + this.mixer.dispatchEvent( { type: 'finished', action: this, direction: -1 } ); - this.quaternion.setFromRotationMatrix( m1 ); + } - }; + } -}(); + + return this.clipTime; -THREE.Camera.prototype.clone = function () { + } + + this.loopCount = Math.floor( this.actionTime / duration ); + + var newClipTime = this.actionTime - this.loopCount * duration; + newClipTime = newClipTime % duration; + + // if we are ping pong looping, ensure that we go backwards when appropriate + if( this.loop == THREE.LoopPingPong ) { - return new this.constructor().copy( this ); + if( Math.abs( this.loopCount % 2 ) === 1 ) { -}; + newClipTime = duration - newClipTime; -THREE.Camera.prototype.copy = function ( source ) { + } - THREE.Object3D.prototype.copy.call( this, source ); + } - this.matrixWorldInverse.copy( source.matrixWorldInverse ); - this.projectionMatrix.copy( source.projectionMatrix ); + this.clipTime = newClipTime; - return this; + if( this.loopCount !== previousLoopCount ) { -}; + this.mixer.dispatchEvent( { type: 'loop', action: this, loopDelta: ( this.loopCount - this.loopCount ) } ); -// File:src/cameras/CubeCamera.js + } + + return this.clipTime; -/** - * Camera for rendering cube maps - * - renders scene into axis-aligned cube - * - * @author alteredq / http://alteredqualia.com/ - */ + }, -THREE.CubeCamera = function ( near, far, cubeResolution ) { + syncWith: function( action ) { - THREE.Object3D.call( this ); + this.actionTime = action.actionTime; + this.timeScale = action.timeScale; - this.type = 'CubeCamera'; + return this; + }, - var fov = 90, aspect = 1; + warpToDuration: function( duration ) { - var cameraPX = new THREE.PerspectiveCamera( fov, aspect, near, far ); - cameraPX.up.set( 0, - 1, 0 ); - cameraPX.lookAt( new THREE.Vector3( 1, 0, 0 ) ); - this.add( cameraPX ); + this.timeScale = this.clip.duration / duration; - var cameraNX = new THREE.PerspectiveCamera( fov, aspect, near, far ); - cameraNX.up.set( 0, - 1, 0 ); - cameraNX.lookAt( new THREE.Vector3( - 1, 0, 0 ) ); - this.add( cameraNX ); + return this; + }, - var cameraPY = new THREE.PerspectiveCamera( fov, aspect, near, far ); - cameraPY.up.set( 0, 0, 1 ); - cameraPY.lookAt( new THREE.Vector3( 0, 1, 0 ) ); - this.add( cameraPY ); + init: function( time ) { - var cameraNY = new THREE.PerspectiveCamera( fov, aspect, near, far ); - cameraNY.up.set( 0, 0, - 1 ); - cameraNY.lookAt( new THREE.Vector3( 0, - 1, 0 ) ); - this.add( cameraNY ); + this.clipTime = time - this.startTime; - var cameraPZ = new THREE.PerspectiveCamera( fov, aspect, near, far ); - cameraPZ.up.set( 0, - 1, 0 ); - cameraPZ.lookAt( new THREE.Vector3( 0, 0, 1 ) ); - this.add( cameraPZ ); + return this; - var cameraNZ = new THREE.PerspectiveCamera( fov, aspect, near, far ); - cameraNZ.up.set( 0, - 1, 0 ); - cameraNZ.lookAt( new THREE.Vector3( 0, 0, - 1 ) ); - this.add( cameraNZ ); + }, - this.renderTarget = new THREE.WebGLRenderTargetCube( cubeResolution, cubeResolution, { format: THREE.RGBFormat, magFilter: THREE.LinearFilter, minFilter: THREE.LinearFilter } ); + update: function( clipDeltaTime ) { - this.updateCubeMap = function ( renderer, scene ) { + this.updateTime( clipDeltaTime ); - if ( this.parent === null ) this.updateMatrixWorld(); + var clipResults = this.clip.getAt( this.clipTime ); - var renderTarget = this.renderTarget; - var generateMipmaps = renderTarget.generateMipmaps; + return clipResults; + + }, - renderTarget.generateMipmaps = false; + getTimeScaleAt: function( time ) { - renderTarget.activeCubeFace = 0; - renderer.render( scene, cameraPX, renderTarget ); + if( this.timeScale.getAt ) { + // pass in time, not clip time, allows for fadein/fadeout across multiple loops of the clip + return this.timeScale.getAt( time ); - renderTarget.activeCubeFace = 1; - renderer.render( scene, cameraNX, renderTarget ); + } - renderTarget.activeCubeFace = 2; - renderer.render( scene, cameraPY, renderTarget ); + return this.timeScale; - renderTarget.activeCubeFace = 3; - renderer.render( scene, cameraNY, renderTarget ); + }, - renderTarget.activeCubeFace = 4; - renderer.render( scene, cameraPZ, renderTarget ); + getWeightAt: function( time ) { - renderTarget.generateMipmaps = generateMipmaps; + if( this.weight.getAt ) { + // pass in time, not clip time, allows for fadein/fadeout across multiple loops of the clip + return this.weight.getAt( time ); - renderTarget.activeCubeFace = 5; - renderer.render( scene, cameraNZ, renderTarget ); + } - renderer.setRenderTarget( null ); + return this.weight; - }; + } }; -THREE.CubeCamera.prototype = Object.create( THREE.Object3D.prototype ); -THREE.CubeCamera.prototype.constructor = THREE.CubeCamera; - -// File:src/cameras/OrthographicCamera.js +// File:src/animation/AnimationClip.js /** - * @author alteredq / http://alteredqualia.com/ + * + * Reusable set of Tracks that represent an animation. + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ */ -THREE.OrthographicCamera = function ( left, right, top, bottom, near, far ) { - - THREE.Camera.call( this ); +THREE.AnimationClip = function ( name, duration, tracks ) { - this.type = 'OrthographicCamera'; + this.name = name; + this.tracks = tracks; + this.duration = ( duration !== undefined ) ? duration : -1; - this.zoom = 1; + // this means it should figure out its duration by scanning the tracks + if( this.duration < 0 ) { + for( var i = 0; i < this.tracks.length; i ++ ) { + var track = this.tracks[i]; + this.duration = Math.max( track.keys[ track.keys.length - 1 ].time ); + } + } - this.left = left; - this.right = right; - this.top = top; - this.bottom = bottom; + // maybe only do these on demand, as doing them here could potentially slow down loading + // but leaving these here during development as this ensures a lot of testing of these functions + this.trim(); + this.optimize(); - this.near = ( near !== undefined ) ? near : 0.1; - this.far = ( far !== undefined ) ? far : 2000; + this.results = []; + +}; - this.updateProjectionMatrix(); +THREE.AnimationClip.prototype = { -}; + constructor: THREE.AnimationClip, -THREE.OrthographicCamera.prototype = Object.create( THREE.Camera.prototype ); -THREE.OrthographicCamera.prototype.constructor = THREE.OrthographicCamera; + getAt: function( clipTime ) { -THREE.OrthographicCamera.prototype.updateProjectionMatrix = function () { + clipTime = Math.max( 0, Math.min( clipTime, this.duration ) ); - 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; + for( var i = 0; i < this.tracks.length; i ++ ) { - this.projectionMatrix.makeOrthographic( cx - dx, cx + dx, cy + dy, cy - dy, this.near, this.far ); + var track = this.tracks[ i ]; -}; + this.results[ i ] = track.getAt( clipTime ); -THREE.OrthographicCamera.prototype.copy = function ( source ) { - - THREE.Camera.prototype.copy.call( this, source ); - - this.left = source.left; - this.right = source.right; - this.top = source.top; - this.bottom = source.bottom; - this.near = source.near; - this.far = source.far; - - this.zoom = source.zoom; - - return this; - -}; + } -THREE.OrthographicCamera.prototype.toJSON = function ( meta ) { + return this.results; + }, - var data = THREE.Object3D.prototype.toJSON.call( this, meta ); + trim: function() { - data.object.zoom = this.zoom; - data.object.left = this.left; - data.object.right = this.right; - data.object.top = this.top; - data.object.bottom = this.bottom; - data.object.near = this.near; - data.object.far = this.far; + for( var i = 0; i < this.tracks.length; i ++ ) { - return data; + this.tracks[ i ].trim( 0, this.duration ); -}; + } -// File:src/cameras/PerspectiveCamera.js + return this; -/** - * @author mrdoob / http://mrdoob.com/ - * @author greggman / http://games.greggman.com/ - * @author zz85 / http://www.lab4games.net/zz85/blog - */ + }, -THREE.PerspectiveCamera = function ( fov, aspect, near, far ) { + optimize: function() { - THREE.Camera.call( this ); + for( var i = 0; i < this.tracks.length; i ++ ) { - this.type = 'PerspectiveCamera'; + this.tracks[ i ].optimize(); - this.zoom = 1; + } - this.fov = fov !== undefined ? fov : 50; - this.aspect = aspect !== undefined ? aspect : 1; - this.near = near !== undefined ? near : 0.1; - this.far = far !== undefined ? far : 2000; + return this; - this.updateProjectionMatrix(); + } }; -THREE.PerspectiveCamera.prototype = Object.create( THREE.Camera.prototype ); -THREE.PerspectiveCamera.prototype.constructor = THREE.PerspectiveCamera; - -/** - * Uses Focal Length (in mm) to estimate and set FOV - * 35mm (full-frame) camera is used if frame size is not specified; - * Formula based on http://www.bobatkins.com/photography/technical/field_of_view.html - */ +THREE.AnimationClip.CreateFromMorphTargetSequence = function( name, morphTargetSequence, fps ) { -THREE.PerspectiveCamera.prototype.setLens = function ( focalLength, frameHeight ) { - if ( frameHeight === undefined ) frameHeight = 24; + var numMorphTargets = morphTargetSequence.length; + var tracks = []; - this.fov = 2 * THREE.Math.radToDeg( Math.atan( frameHeight / ( focalLength * 2 ) ) ); - this.updateProjectionMatrix(); + for( var i = 0; i < numMorphTargets; i ++ ) { -}; + var keys = []; + keys.push( { time: ( i + numMorphTargets - 1 ) % numMorphTargets, value: 0 } ); + keys.push( { time: i, value: 1 } ); + keys.push( { time: ( i + 1 ) % numMorphTargets, value: 0 } ); -/** - * Sets an offset in a larger frustum. This is useful for multi-window or - * multi-monitor/multi-machine setups. - * - * For example, if you have 3x2 monitors and each monitor is 1920x1080 and - * the monitors are in grid like this - * - * +---+---+---+ - * | A | B | C | - * +---+---+---+ - * | D | E | F | - * +---+---+---+ - * - * then for each monitor you would call it like this - * - * var w = 1920; - * var h = 1080; - * var fullWidth = w * 3; - * var fullHeight = h * 2; - * - * --A-- - * camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); - * --B-- - * camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); - * --C-- - * camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); - * --D-- - * camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); - * --E-- - * camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); - * --F-- - * camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); - * - * Note there is no reason monitors have to be the same size or in a grid. - */ + keys.sort( THREE.KeyframeTrack.keyComparer ); -THREE.PerspectiveCamera.prototype.setViewOffset = function ( fullWidth, fullHeight, x, y, width, height ) { + // if there is a key at the first frame, duplicate it as the last frame as well for perfect loop. + if( keys[0].time === 0 ) { + keys.push( { + time: numMorphTargets, + value: keys[0].value + }); + } - this.fullWidth = fullWidth; - this.fullHeight = fullHeight; - this.x = x; - this.y = y; - this.width = width; - this.height = height; + tracks.push( new THREE.NumberKeyframeTrack( '.morphTargetInfluences[' + morphTargetSequence[i].name + ']', keys ).scale( 1.0 / fps ) ); + } - this.updateProjectionMatrix(); + return new THREE.AnimationClip( name, -1, tracks ); }; +THREE.AnimationClip.findByName = function( clipArray, name ) { -THREE.PerspectiveCamera.prototype.updateProjectionMatrix = function () { + for( var i = 0; i < clipArray.length; i ++ ) { - var fov = THREE.Math.radToDeg( 2 * Math.atan( Math.tan( THREE.Math.degToRad( this.fov ) * 0.5 ) / this.zoom ) ); + if( clipArray[i].name === name ) { - if ( this.fullWidth ) { + return clipArray[i]; - var aspect = this.fullWidth / this.fullHeight; - var top = Math.tan( THREE.Math.degToRad( fov * 0.5 ) ) * this.near; - var bottom = - top; - var left = aspect * bottom; - var right = aspect * top; - var width = Math.abs( right - left ); - var height = Math.abs( top - bottom ); + } + } - this.projectionMatrix.makeFrustum( - left + this.x * width / this.fullWidth, - left + ( this.x + this.width ) * width / this.fullWidth, - top - ( this.y + this.height ) * height / this.fullHeight, - top - this.y * height / this.fullHeight, - this.near, - this.far - ); + return null; - } else { +}; - this.projectionMatrix.makePerspective( fov, this.aspect, this.near, this.far ); +THREE.AnimationClip.CreateClipsFromMorphTargetSequences = function( morphTargets, fps ) { + + var animationToMorphTargets = {}; - } + // tested with https://regex101.com/ on trick sequences such flamingo_flyA_003, flamingo_run1_003, crdeath0059 + var pattern = /^([\w-]*?)([\d]+)$/; -}; + // sort morph target names into animation groups based patterns like Walk_001, Walk_002, Run_001, Run_002 + for ( var i = 0, il = morphTargets.length; i < il; i ++ ) { -THREE.PerspectiveCamera.prototype.copy = function ( source ) { - - THREE.Camera.prototype.copy.call( this, source ); - - this.fov = source.fov; - this.aspect = source.aspect; - this.near = source.near; - this.far = source.far; - - this.zoom = source.zoom; + var morphTarget = morphTargets[ i ]; + var parts = morphTarget.name.match( pattern ); - return this; - -}; + if ( parts && parts.length > 1 ) { -THREE.PerspectiveCamera.prototype.toJSON = function ( meta ) { + var name = parts[ 1 ]; - var data = THREE.Object3D.prototype.toJSON.call( this, meta ); + var animationMorphTargets = animationToMorphTargets[ name ]; + if( ! animationMorphTargets ) { + animationToMorphTargets[ name ] = animationMorphTargets = []; + } - data.object.zoom = this.zoom; - data.object.fov = this.fov; - data.object.aspect = this.aspect; - data.object.near = this.near; - data.object.far = this.far; + animationMorphTargets.push( morphTarget ); - return data; + } -}; + } -// File:src/lights/Light.js + var clips = []; -/** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ + for( var name in animationToMorphTargets ) { -THREE.Light = function ( color ) { + clips.push( THREE.AnimationClip.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps ) ); + } - THREE.Object3D.call( this ); + return clips; - this.type = 'Light'; +}; - this.color = new THREE.Color( color ); +// parse the standard JSON format for clips +THREE.AnimationClip.parse = function( json ) { -}; + var tracks = []; -THREE.Light.prototype = Object.create( THREE.Object3D.prototype ); -THREE.Light.prototype.constructor = THREE.Light; + for( var i = 0; i < json.tracks.length; i ++ ) { -THREE.Light.prototype.copy = function ( source ) { - - THREE.Object3D.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - - return this; + tracks.push( THREE.KeyframeTrack.parse( json.tracks[i] ).scale( 1.0 / json.fps ) ); + + } + + return new THREE.AnimationClip( json.name, json.duration, tracks ); }; -// File:src/lights/AmbientLight.js -/** - * @author mrdoob / http://mrdoob.com/ - */ -THREE.AmbientLight = function ( color ) { +// parse the animation.hierarchy format +THREE.AnimationClip.parseAnimation = function( animation, bones, nodeName ) { - THREE.Light.call( this, color ); + if( ! animation ) { + console.error( " no animation in JSONLoader data" ); + return null; + } - this.type = 'AmbientLight'; + var convertTrack = function( trackName, animationKeys, propertyName, trackType, animationKeyToValueFunc ) { -}; + var keys = []; -THREE.AmbientLight.prototype = Object.create( THREE.Light.prototype ); -THREE.AmbientLight.prototype.constructor = THREE.AmbientLight; + for( var k = 0; k < animationKeys.length; k ++ ) { -THREE.AmbientLight.prototype.toJSON = function ( meta ) { + var animationKey = animationKeys[k]; - var data = THREE.Object3D.prototype.toJSON.call( this, meta ); + if( animationKey[propertyName] !== undefined ) { - data.object.color = this.color.getHex(); + keys.push( { time: animationKey.time, value: animationKeyToValueFunc( animationKey ) } ); + } + + } - return data; + // only return track if there are actually keys. + if( keys.length > 0 ) { + + return new trackType( trackName, keys ); -}; + } -// File:src/lights/DirectionalLight.js + return null; -/** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ + }; -THREE.DirectionalLight = function ( color, intensity ) { + var tracks = []; - THREE.Light.call( this, color ); + var clipName = animation.name || 'default'; + var duration = animation.length || -1; // automatic length determination in AnimationClip. + var fps = animation.fps || 30; - this.type = 'DirectionalLight'; + var hierarchyTracks = animation.hierarchy || []; - this.position.set( 0, 1, 0 ); - this.updateMatrix(); + for ( var h = 0; h < hierarchyTracks.length; h ++ ) { - this.target = new THREE.Object3D(); + var animationKeys = hierarchyTracks[ h ].keys; - this.intensity = ( intensity !== undefined ) ? intensity : 1; + // skip empty tracks + if( ! animationKeys || animationKeys.length == 0 ) { + continue; + } - this.castShadow = false; - this.onlyShadow = false; + // process morph targets in a way exactly compatible with AnimationHandler.init( animation ) + if( animationKeys[0].morphTargets ) { - this.shadowCameraNear = 50; - this.shadowCameraFar = 5000; + // figure out all morph targets used in this track + var morphTargetNames = {}; + for( var k = 0; k < animationKeys.length; k ++ ) { - this.shadowCameraLeft = - 500; - this.shadowCameraRight = 500; - this.shadowCameraTop = 500; - this.shadowCameraBottom = - 500; + if( animationKeys[k].morphTargets ) { + for( var m = 0; m < animationKeys[k].morphTargets.length; m ++ ) { - this.shadowCameraVisible = false; + morphTargetNames[ animationKeys[k].morphTargets[m] ] = -1; + } + } - this.shadowBias = 0; - this.shadowDarkness = 0.5; + } - this.shadowMapWidth = 512; - this.shadowMapHeight = 512; + // create a track for each morph target with all zero morphTargetInfluences except for the keys in which the morphTarget is named. + for( var morphTargetName in morphTargetNames ) { - this.shadowMap = null; - this.shadowMapSize = null; - this.shadowCamera = null; - this.shadowMatrix = null; + var keys = []; -}; + for( var m = 0; m < animationKeys[k].morphTargets.length; m ++ ) { -THREE.DirectionalLight.prototype = Object.create( THREE.Light.prototype ); -THREE.DirectionalLight.prototype.constructor = THREE.DirectionalLight; + var animationKey = animationKeys[k]; -THREE.DirectionalLight.prototype.copy = function ( source ) { + keys.push( { + time: animationKey.time, + value: (( animationKey.morphTarget === morphTargetName ) ? 1 : 0 ) + }); + + } - THREE.Light.prototype.copy.call( this, source ); + tracks.push( new THREE.NumberKeyframeTrack( nodeName + '.morphTargetInfluence[' + morphTargetName + ']', keys ) ); - this.intensity = source.intensity; - this.target = source.target.clone(); + } - this.castShadow = source.castShadow; - this.onlyShadow = source.onlyShadow; + duration = morphTargetNames.length * ( fps || 1.0 ); - this.shadowCameraNear = source.shadowCameraNear; - this.shadowCameraFar = source.shadowCameraFar; + } + else { - this.shadowCameraLeft = source.shadowCameraLeft; - this.shadowCameraRight = source.shadowCameraRight; - this.shadowCameraTop = source.shadowCameraTop; - this.shadowCameraBottom = source.shadowCameraBottom; + var boneName = nodeName + '.bones[' + bones[ h ].name + ']'; + + // track contains positions... + var positionTrack = convertTrack( boneName + '.position', animationKeys, 'pos', THREE.VectorKeyframeTrack, function( animationKey ) { + return new THREE.Vector3().fromArray( animationKey.pos ) + } ); - this.shadowCameraVisible = source.shadowCameraVisible; + if( positionTrack ) tracks.push( positionTrack ); + + // track contains quaternions... + var quaternionTrack = convertTrack( boneName + '.quaternion', animationKeys, 'rot', THREE.QuaternionKeyframeTrack, function( animationKey ) { + if( animationKey.rot.slerp ) { + return animationKey.rot.clone(); + } + else { + return new THREE.Quaternion().fromArray( animationKey.rot ); + } + } ); - this.shadowBias = source.shadowBias; - this.shadowDarkness = source.shadowDarkness; + if( quaternionTrack ) tracks.push( quaternionTrack ); - this.shadowMapWidth = source.shadowMapWidth; - this.shadowMapHeight = source.shadowMapHeight; + // track contains quaternions... + var scaleTrack = convertTrack( boneName + '.scale', animationKeys, 'scl', THREE.VectorKeyframeTrack, function( animationKey ) { + return new THREE.Vector3().fromArray( animationKey.scl ) + } ); - return this; + if( scaleTrack ) tracks.push( scaleTrack ); -}; + } + } -THREE.DirectionalLight.prototype.toJSON = function ( meta ) { + if( tracks.length === 0 ) { - var data = THREE.Object3D.prototype.toJSON.call( this, meta ); + return null; - data.object.color = this.color.getHex(); - data.object.intensity = this.intensity; + } - return data; + var clip = new THREE.AnimationClip( clipName, duration, tracks ); + + return clip; }; -// File:src/lights/HemisphereLight.js +// File:src/animation/AnimationMixer.js /** - * @author alteredq / http://alteredqualia.com/ + * + * Mixes together the AnimationClips scheduled by AnimationActions and applies them to the root and subtree + * + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ */ -THREE.HemisphereLight = function ( skyColor, groundColor, intensity ) { +THREE.AnimationMixer = function( root ) { - THREE.Light.call( this, skyColor ); + this.root = root; + this.time = 0; + this.timeScale = 1.0; + this.actions = []; + this.propertyBindingMap = {}; - this.type = 'HemisphereLight'; +}; - this.position.set( 0, 1, 0 ); - this.updateMatrix(); +THREE.AnimationMixer.prototype = { - this.groundColor = new THREE.Color( groundColor ); - this.intensity = ( intensity !== undefined ) ? intensity : 1; + constructor: THREE.AnimationMixer, -}; + addAction: function( action ) { -THREE.HemisphereLight.prototype = Object.create( THREE.Light.prototype ); -THREE.HemisphereLight.prototype.constructor = THREE.HemisphereLight; + // TODO: check for duplicate action names? Or provide each action with a UUID? -THREE.HemisphereLight.prototype.copy = function ( source ) { + this.actions.push( action ); + action.init( this.time ); + action.mixer = this; - THREE.Light.prototype.copy.call( this, source ); + var tracks = action.clip.tracks; - this.groundColor.copy( source.groundColor ); - this.intensity = source.intensity; + var root = action.localRoot || this.root; - return this; + for( var i = 0; i < tracks.length; i ++ ) { -}; + var track = tracks[ i ]; -THREE.HemisphereLight.prototype.toJSON = function ( meta ) { + var propertyBindingKey = root.uuid + '-' + track.name; + var propertyBinding = this.propertyBindingMap[ propertyBindingKey ]; - var data = THREE.Object3D.prototype.toJSON.call( this, meta ); + if( propertyBinding === undefined ) { + + propertyBinding = new THREE.PropertyBinding( root, track.name ); + this.propertyBindingMap[ propertyBindingKey ] = propertyBinding; + + } - data.object.color = this.color.getHex(); - data.object.groundColor = this.groundColor.getHex(); - data.object.intensity = this.intensity; + // push in the same order as the tracks. + action.propertyBindings.push( propertyBinding ); + + // track usages of shared property bindings, because if we leave too many around, the mixer can get slow + propertyBinding.referenceCount += 1; - return data; + } -}; + }, -// File:src/lights/PointLight.js + removeAllActions: function() { -/** - * @author mrdoob / http://mrdoob.com/ - */ + for( var i = 0; i < this.actions.length; i ++ ) { -THREE.PointLight = function ( color, intensity, distance, decay ) { + this.actions[i].mixer = null; + + } - THREE.Light.call( this, color ); + // unbind all property bindings + for( var properyBindingKey in this.propertyBindingMap ) { - this.type = 'PointLight'; + this.propertyBindingMap[ properyBindingKey ].unbind(); - this.intensity = ( intensity !== undefined ) ? intensity : 1; - this.distance = ( distance !== undefined ) ? distance : 0; - this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. + } -}; + this.actions = []; + this.propertyBindingMap = {}; -THREE.PointLight.prototype = Object.create( THREE.Light.prototype ); -THREE.PointLight.prototype.constructor = THREE.PointLight; + return this; -THREE.PointLight.prototype.copy = function ( source ) { + }, - THREE.Light.prototype.copy.call( this, source ); + removeAction: function( action ) { - this.intensity = source.intensity; - this.distance = source.distance; - this.decay = source.decay; + var index = this.actions.indexOf( action ); - return this; + if ( index !== - 1 ) { -}; + this.actions.splice( index, 1 ); + action.mixer = null; -THREE.PointLight.prototype.toJSON = function ( meta ) { + } - var data = THREE.Object3D.prototype.toJSON.call( this, meta ); - data.object.color = this.color.getHex(); - data.object.intensity = this.intensity; - data.object.distance = this.distance; - data.object.decay = this.decay; - - return data; - -}; - -// File:src/lights/SpotLight.js + // remove unused property bindings because if we leave them around the mixer can get slow + var root = action.localRoot || this.root; + var tracks = action.clip.tracks; -/** - * @author alteredq / http://alteredqualia.com/ - */ + for( var i = 0; i < tracks.length; i ++ ) { + + var track = tracks[ i ]; -THREE.SpotLight = function ( color, intensity, distance, angle, exponent, decay ) { + var propertyBindingKey = root.uuid + '-' + track.name; + var propertyBinding = this.propertyBindingMap[ propertyBindingKey ]; + + propertyBinding.referenceCount -= 1; - THREE.Light.call( this, color ); + if( propertyBinding.referenceCount <= 0 ) { - this.type = 'SpotLight'; + propertyBinding.unbind(); - this.position.set( 0, 1, 0 ); - this.updateMatrix(); + delete this.propertyBindingMap[ propertyBindingKey ]; - this.target = new THREE.Object3D(); + } + } - this.intensity = ( intensity !== undefined ) ? intensity : 1; - this.distance = ( distance !== undefined ) ? distance : 0; - this.angle = ( angle !== undefined ) ? angle : Math.PI / 3; - this.exponent = ( exponent !== undefined ) ? exponent : 10; - this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. + return this; - this.castShadow = false; - this.onlyShadow = false; + }, - this.shadowCameraNear = 50; - this.shadowCameraFar = 5000; - this.shadowCameraFov = 50; + // can be optimized if needed + findActionByName: function( name ) { - this.shadowCameraVisible = false; + for( var i = 0; i < this.actions.length; i ++ ) { - this.shadowBias = 0; - this.shadowDarkness = 0.5; + if( this.actions[i].name === name ) return this.actions[i]; - this.shadowMapWidth = 512; - this.shadowMapHeight = 512; + } - this.shadowMap = null; - this.shadowMapSize = null; - this.shadowCamera = null; - this.shadowMatrix = null; + return null; -}; + }, -THREE.SpotLight.prototype = Object.create( THREE.Light.prototype ); -THREE.SpotLight.prototype.constructor = THREE.SpotLight; + play: function( action, optionalFadeInDuration ) { -THREE.SpotLight.prototype.copy = function ( source ) { + action.startTime = this.time; + this.addAction( action ); - THREE.Light.prototype.copy.call( this, source ); + return this; - this.intensity = source.intensity; - this.distance = source.distance; - this.angle = source.angle; - this.exponent = source.exponent; - this.decay = source.decay; + }, - this.target = source.target.clone(); + fadeOut: function( action, duration ) { - this.castShadow = source.castShadow; - this.onlyShadow = source.onlyShadow; + var keys = []; - this.shadowCameraNear = source.shadowCameraNear; - this.shadowCameraFar = source.shadowCameraFar; - this.shadowCameraFov = source.shadowCameraFov; + keys.push( { time: this.time, value: 1 } ); + keys.push( { time: this.time + duration, value: 0 } ); + + action.weight = new THREE.NumberKeyframeTrack( "weight", keys ); - this.shadowCameraVisible = source.shadowCameraVisible; + return this; - this.shadowBias = source.shadowBias; - this.shadowDarkness = source.shadowDarkness; + }, - this.shadowMapWidth = source.shadowMapWidth; - this.shadowMapHeight = source.shadowMapHeight; + fadeIn: function( action, duration ) { + + var keys = []; + + keys.push( { time: this.time, value: 0 } ); + keys.push( { time: this.time + duration, value: 1 } ); + + action.weight = new THREE.NumberKeyframeTrack( "weight", keys ); - return this; -} + return this; -THREE.SpotLight.prototype.toJSON = function ( meta ) { + }, - var data = THREE.Object3D.prototype.toJSON.call( this, meta ); + warp: function( action, startTimeScale, endTimeScale, duration ) { - data.object.color = this.color.getHex(); - data.object.intensity = this.intensity; - data.object.distance = this.distance; - data.object.angle = this.angle; - data.object.exponent = this.exponent; - data.object.decay = this.decay; + var keys = []; + + keys.push( { time: this.time, value: startTimeScale } ); + keys.push( { time: this.time + duration, value: endTimeScale } ); + + action.timeScale = new THREE.NumberKeyframeTrack( "timeScale", keys ); - return data; + return this; -}; + }, -// File:src/loaders/Cache.js + crossFade: function( fadeOutAction, fadeInAction, duration, warp ) { -/** - * @author mrdoob / http://mrdoob.com/ - */ + this.fadeOut( fadeOutAction, duration ); + this.fadeIn( fadeInAction, duration ); -THREE.Cache = { + if( warp ) { + + var startEndRatio = fadeOutAction.clip.duration / fadeInAction.clip.duration; + var endStartRatio = 1.0 / startEndRatio; - enabled: false, + this.warp( fadeOutAction, 1.0, startEndRatio, duration ); + this.warp( fadeInAction, endStartRatio, 1.0, duration ); - files: {}, + } - add: function ( key, file ) { + return this; + + }, - if ( this.enabled === false ) return; + update: function( deltaTime ) { - // console.log( 'THREE.Cache', 'Adding key:', key ); + var mixerDeltaTime = deltaTime * this.timeScale; + this.time += mixerDeltaTime; - this.files[ key ] = file; + for( var i = 0; i < this.actions.length; i ++ ) { - }, + var action = this.actions[i]; - get: function ( key ) { + var weight = action.getWeightAt( this.time ); - if ( this.enabled === false ) return; + var actionTimeScale = action.getTimeScaleAt( this.time ); + var actionDeltaTime = mixerDeltaTime * actionTimeScale; + + var actionResults = action.update( actionDeltaTime ); - // console.log( 'THREE.Cache', 'Checking key:', key ); + if( action.weight <= 0 || ! action.enabled ) continue; - return this.files[ key ]; + for( var j = 0; j < actionResults.length; j ++ ) { - }, + var name = action.clip.tracks[j].name; - remove: function ( key ) { + action.propertyBindings[ j ].accumulate( actionResults[j], weight ); - delete this.files[ key ]; + } - }, + } + + // apply to nodes + for( var propertyBindingKey in this.propertyBindingMap ) { - clear: function () { + this.propertyBindingMap[ propertyBindingKey ].apply(); - this.files = {}; + } + return this; + } }; -// File:src/loaders/Loader.js +THREE.EventDispatcher.prototype.apply( THREE.AnimationMixer.prototype ); + +// File:src/animation/AnimationUtils.js /** - * @author alteredq / http://alteredqualia.com/ + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ */ -THREE.Loader = function () { - - this.onLoadStart = function () {}; - this.onLoadProgress = function () {}; - this.onLoadComplete = function () {}; - -}; - -THREE.Loader.prototype = { + THREE.AnimationUtils = { - constructor: THREE.Loader, + getEqualsFunc: function( exemplarValue ) { - crossOrigin: undefined, + if( exemplarValue.equals ) { + return function equals_object( a, b ) { + return a.equals( b ); + } + } - extractUrlBase: function ( url ) { + return function equals_primitive( a, b ) { + return ( a === b ); + }; - var parts = url.split( '/' ); + }, - if ( parts.length === 1 ) return './'; + clone: function( exemplarValue ) { - parts.pop(); + var typeName = typeof exemplarValue; + if( typeName === "object" ) { + if( exemplarValue.clone ) { + return exemplarValue.clone(); + } + console.error( "can not figure out how to copy exemplarValue", exemplarValue ); + } - return parts.join( '/' ) + '/'; + return exemplarValue; }, - initMaterials: function ( materials, texturePath, crossOrigin ) { + lerp: function( a, b, alpha, interTrack ) { - var array = []; + var lerpFunc = THREE.AnimationUtils.getLerpFunc( a, interTrack ); - for ( var i = 0; i < materials.length; ++ i ) { + return lerpFunc( a, b, alpha ); - array[ i ] = this.createMaterial( materials[ i ], texturePath, crossOrigin ); + }, - } + lerp_object: function( a, b, alpha ) { + return a.lerp( b, alpha ); + }, + + slerp_object: function( a, b, alpha ) { + return a.slerp( b, alpha ); + }, - return array; + lerp_number: function( a, b, alpha ) { + return a * ( 1 - alpha ) + b * alpha; + }, + lerp_boolean: function( a, b, alpha ) { + return ( alpha < 0.5 ) ? a : b; }, - createMaterial: ( function () { + lerp_boolean_immediate: function( a, b, alpha ) { + return a; + }, + + lerp_string: function( a, b, alpha ) { + return ( alpha < 0.5 ) ? a : b; + }, + + lerp_string_immediate: function( a, b, alpha ) { + return a; + }, - var imageLoader; + // NOTE: this is an accumulator function that modifies the first argument (e.g. a). This is to minimize memory alocations. + getLerpFunc: function( exemplarValue, interTrack ) { - return function createMaterial( m, texturePath, crossOrigin ) { + if( exemplarValue === undefined || exemplarValue === null ) throw new Error( "examplarValue is null" ); - var scope = this; + var typeName = typeof exemplarValue; + switch( typeName ) { + case "object": { - if ( crossOrigin === undefined && scope.crossOrigin !== undefined ) crossOrigin = scope.crossOrigin; + if( exemplarValue.lerp ) { - if ( imageLoader === undefined ) imageLoader = new THREE.ImageLoader(); + return THREE.AnimationUtils.lerp_object; - function nearest_pow2( n ) { + } + if( exemplarValue.slerp ) { - var l = Math.log( n ) / Math.LN2; - return Math.pow( 2, Math.round( l ) ); + return THREE.AnimationUtils.slerp_object; + } + break; } + case "number": { + return THREE.AnimationUtils.lerp_number; + } + case "boolean": { + if( interTrack ) { + return THREE.AnimationUtils.lerp_boolean; + } + else { + return THREE.AnimationUtils.lerp_boolean_immediate; + } + } + case "string": { + if( interTrack ) { + return THREE.AnimationUtils.lerp_string; + } + else { + return THREE.AnimationUtils.lerp_string_immediate; + } + } + }; - function create_texture( where, name, sourceFile, repeat, offset, wrap, anisotropy ) { + } + +}; +// File:src/animation/KeyframeTrack.js - var fullPath = texturePath + sourceFile; +/** + * + * A Track that returns a keyframe interpolated value, currently linearly interpolated + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ - var texture; +THREE.KeyframeTrack = function ( name, keys ) { - var loader = THREE.Loader.Handlers.get( fullPath ); + if( name === undefined ) throw new Error( "track name is undefined" ); + if( keys === undefined || keys.length === 0 ) throw new Error( "no keys in track named " + name ); - if ( loader !== null ) { + this.name = name; + this.keys = keys; // time in seconds, value as value - texture = loader.load( fullPath ); + // the index of the last result, used as a starting point for local search. + this.lastIndex = 0; - } else { + this.validate(); + this.optimize(); +}; - texture = new THREE.Texture(); +THREE.KeyframeTrack.prototype = { - loader = imageLoader; - loader.setCrossOrigin( crossOrigin ); - loader.load( fullPath, function ( image ) { + constructor: THREE.KeyframeTrack, - if ( THREE.Math.isPowerOfTwo( image.width ) === false || - THREE.Math.isPowerOfTwo( image.height ) === false ) { + getAt: function( time ) { - var width = nearest_pow2( image.width ); - var height = nearest_pow2( image.height ); - var canvas = document.createElement( 'canvas' ); - canvas.width = width; - canvas.height = height; + // this can not go higher than this.keys.length. + while( ( this.lastIndex < this.keys.length ) && ( time >= this.keys[this.lastIndex].time ) ) { + this.lastIndex ++; + }; - var context = canvas.getContext( '2d' ); - context.drawImage( image, 0, 0, width, height ); + // this can not go lower than 0. + while( ( this.lastIndex > 0 ) && ( time < this.keys[this.lastIndex - 1].time ) ) { + this.lastIndex --; + } - texture.image = canvas; + if( this.lastIndex >= this.keys.length ) { - } else { + this.setResult( this.keys[ this.keys.length - 1 ].value ); - texture.image = image; + return this.result; - } + } - texture.needsUpdate = true; + if( this.lastIndex === 0 ) { - } ); + this.setResult( this.keys[ 0 ].value ); - } + return this.result; - texture.sourceFile = sourceFile; + } - if ( repeat ) { + var prevKey = this.keys[ this.lastIndex - 1 ]; + this.setResult( prevKey.value ); - texture.repeat.set( repeat[ 0 ], repeat[ 1 ] ); + // if true, means that prev/current keys are identical, thus no interpolation required. + if( prevKey.constantToNext ) { - if ( repeat[ 0 ] !== 1 ) texture.wrapS = THREE.RepeatWrapping; - if ( repeat[ 1 ] !== 1 ) texture.wrapT = THREE.RepeatWrapping; + return this.result; - } + } - if ( offset ) { + // linear interpolation to start with + var currentKey = this.keys[ this.lastIndex ]; + var alpha = ( time - prevKey.time ) / ( currentKey.time - prevKey.time ); + this.result = this.lerpValues( this.result, currentKey.value, alpha ); - texture.offset.set( offset[ 0 ], offset[ 1 ] ); + return this.result; - } + }, - if ( wrap ) { + // move all keyframes either forwards or backwards in time + shift: function( timeOffset ) { - var wrapMap = { - 'repeat': THREE.RepeatWrapping, - 'mirror': THREE.MirroredRepeatWrapping - }; + if( timeOffset !== 0.0 ) { - if ( wrapMap[ wrap[ 0 ] ] !== undefined ) texture.wrapS = wrapMap[ wrap[ 0 ] ]; - if ( wrapMap[ wrap[ 1 ] ] !== undefined ) texture.wrapT = wrapMap[ wrap[ 1 ] ]; + for( var i = 0; i < this.keys.length; i ++ ) { + this.keys[i].time += timeOffset; + } - } + } - if ( anisotropy ) { + return this; - texture.anisotropy = anisotropy; + }, - } + // scale all keyframe times by a factor (useful for frame <-> seconds conversions) + scale: function( timeScale ) { - where[ name ] = texture; + if( timeScale !== 1.0 ) { + for( var i = 0; i < this.keys.length; i ++ ) { + this.keys[i].time *= timeScale; } - function rgb2hex( rgb ) { + } - return ( rgb[ 0 ] * 255 << 16 ) + ( rgb[ 1 ] * 255 << 8 ) + rgb[ 2 ] * 255; + return this; - } + }, - // defaults + // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. + // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values + trim: function( startTime, endTime ) { - var mtype = 'MeshLambertMaterial'; - var mpars = {}; + var firstKeysToRemove = 0; + for( var i = 1; i < this.keys.length; i ++ ) { + if( this.keys[i] <= startTime ) { + firstKeysToRemove ++; + } + } - // parameters from model file + var lastKeysToRemove = 0; + for( var i = this.keys.length - 2; i > 0; i ++ ) { + if( this.keys[i] >= endTime ) { + lastKeysToRemove ++; + } + else { + break; + } + } - if ( m.shading ) { + // remove last keys first because it doesn't affect the position of the first keys (the otherway around doesn't work as easily) + if( ( firstKeysToRemove + lastKeysToRemove ) > 0 ) { + this.keys = this.keys.splice( firstKeysToRemove, this.keys.length - lastKeysToRemove - firstKeysToRemove );; + } - var shading = m.shading.toLowerCase(); + return this; - if ( shading === 'phong' ) mtype = 'MeshPhongMaterial'; - else if ( shading === 'basic' ) mtype = 'MeshBasicMaterial'; + }, - } + /* NOTE: This is commented out because we really shouldn't have to handle unsorted key lists + Tracks with out of order keys should be considered to be invalid. - bhouston + sort: function() { - if ( m.blending !== undefined && THREE[ m.blending ] !== undefined ) { + this.keys.sort( THREE.KeyframeTrack.keyComparer ); - mpars.blending = THREE[ m.blending ]; + return this; - } + },*/ - if ( m.transparent !== undefined ) { + // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable + // One could eventually ensure that all key.values in a track are all of the same type (otherwise interpolation makes no sense.) + validate: function() { - mpars.transparent = m.transparent; + var prevKey = null; - } + if( this.keys.length === 0 ) { + console.error( " track is empty, no keys", this ); + return; + } - if ( m.opacity !== undefined && m.opacity < 1.0 ) { + for( var i = 0; i < this.keys.length; i ++ ) { - mpars.transparent = true; + var currKey = this.keys[i]; + if( ! currKey ) { + console.error( " key is null in track", this, i ); + return; } - if ( m.depthTest !== undefined ) { + if( ( typeof currKey.time ) !== 'number' || Number.isNaN( currKey.time ) ) { + console.error( " key.time is not a valid number", this, i, currKey ); + return; + } - mpars.depthTest = m.depthTest; + if( currKey.value === undefined || currKey.value === null) { + console.error( " key.value is null in track", this, i, currKey ); + return; + } + if( prevKey && prevKey.time > currKey.time ) { + console.error( " key.time is less than previous key time, out of order keys", this, i, currKey, prevKey ); + return; } - if ( m.depthWrite !== undefined ) { + prevKey = currKey; - mpars.depthWrite = m.depthWrite; + } - } + return this; - if ( m.visible !== undefined ) { + }, - mpars.visible = m.visible; + // currently only removes equivalent sequential keys (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0), which are common in morph target animations + optimize: function() { - } + var newKeys = []; + var prevKey = this.keys[0]; + newKeys.push( prevKey ); - if ( m.flipSided !== undefined ) { + var equalsFunc = THREE.AnimationUtils.getEqualsFunc( prevKey.value ); - mpars.side = THREE.BackSide; + for( var i = 1; i < this.keys.length - 1; i ++ ) { + var currKey = this.keys[i]; + var nextKey = this.keys[i+1]; + + // if prevKey & currKey are the same time, remove currKey. If you want immediate adjacent keys, use an epsilon offset + // it is not possible to have two keys at the same time as we sort them. The sort is not stable on keys with the same time. + if( ( prevKey.time === currKey.time ) ) { + + continue; } - if ( m.doubleSided !== undefined ) { + // remove completely unnecessary keyframes that are the same as their prev and next keys + if( this.compareValues( prevKey.value, currKey.value ) && this.compareValues( currKey.value, nextKey.value ) ) { - mpars.side = THREE.DoubleSide; + continue; } - if ( m.wireframe !== undefined ) { + // determine if interpolation is required + prevKey.constantToNext = this.compareValues( prevKey.value, currKey.value ); - mpars.wireframe = m.wireframe; + newKeys.push( currKey ); + prevKey = currKey; + } + newKeys.push( this.keys[ this.keys.length - 1 ] ); - } + this.keys = newKeys; - if ( m.vertexColors !== undefined ) { + return this; - if ( m.vertexColors === 'face' ) { + } - mpars.vertexColors = THREE.FaceColors; +}; - } else if ( m.vertexColors ) { +THREE.KeyframeTrack.keyComparer = function keyComparator(key0, key1) { + return key0.time - key1.time; +}; - mpars.vertexColors = THREE.VertexColors; +THREE.KeyframeTrack.parse = function( json ) { - } + if( json.type === undefined ) throw new Error( "track type undefined, can not parse" ); - } + var trackType = THREE.KeyframeTrack.GetTrackTypeForTypeName( json.type ); - // colors + return trackType.parse( json ); - if ( m.colorDiffuse ) { +}; - mpars.color = rgb2hex( m.colorDiffuse ); +THREE.KeyframeTrack.GetTrackTypeForTypeName = function( typeName ) { + switch( typeName.toLowerCase() ) { + case "vector": + case "vector2": + case "vector3": + case "vector4": + return THREE.VectorKeyframeTrack; - } else if ( m.DbgColor ) { + case "quaternion": + return THREE.QuaternionKeyframeTrack; - mpars.color = m.DbgColor; + case "integer": + case "scalar": + case "double": + case "float": + case "number": + return THREE.NumberKeyframeTrack; - } + case "bool": + case "boolean": + return THREE.BooleanKeyframeTrack; - if ( m.colorEmissive ) { + case "string": + return THREE.StringKeyframeTrack; + }; - mpars.emissive = rgb2hex( m.colorEmissive ); + throw new Error( "Unsupported typeName: " + typeName ); +}; +// File:src/animation/PropertyBinding.js - } +/** + * + * A track bound to a real value in the scene graph. + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ - if ( mtype === 'MeshPhongMaterial' ) { +THREE.PropertyBinding = function ( rootNode, trackName ) { - if ( m.colorSpecular ) { + this.rootNode = rootNode; + this.trackName = trackName; + this.referenceCount = 0; + this.originalValue = null; // the value of the property before it was controlled by this binding - mpars.specular = rgb2hex( m.colorSpecular ); + var parseResults = THREE.PropertyBinding.parseTrackName( trackName ); - } + this.directoryName = parseResults.directoryName; + this.nodeName = parseResults.nodeName; + this.objectName = parseResults.objectName; + this.objectIndex = parseResults.objectIndex; + this.propertyName = parseResults.propertyName; + this.propertyIndex = parseResults.propertyIndex; - if ( m.specularCoef ) { + this.node = THREE.PropertyBinding.findNode( rootNode, this.nodeName ) || rootNode; + + this.cumulativeValue = null; + this.cumulativeWeight = 0; +}; - mpars.shininess = m.specularCoef; +THREE.PropertyBinding.prototype = { - } + constructor: THREE.PropertyBinding, - } + reset: function() { - // modifiers + this.cumulativeValue = null; + this.cumulativeWeight = 0; - if ( m.transparency !== undefined ) { + }, - console.warn( 'THREE.Loader: transparency has been renamed to opacity' ); - m.opacity = m.transparency; + accumulate: function( value, weight ) { + + if( ! this.isBound ) this.bind(); - } + if( this.cumulativeWeight === 0 ) { - if ( m.opacity !== undefined ) { + if( weight > 0 ) { - mpars.opacity = m.opacity; + if( this.cumulativeValue === null ) { + this.cumulativeValue = THREE.AnimationUtils.clone( value ); + } + this.cumulativeWeight = weight; } - // textures + } + else { - if ( texturePath ) { + var lerpAlpha = weight / ( this.cumulativeWeight + weight ); + this.cumulativeValue = this.lerpValue( this.cumulativeValue, value, lerpAlpha ); + this.cumulativeWeight += weight; - if ( m.mapDiffuse ) { + } - create_texture( mpars, 'map', m.mapDiffuse, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy ); + }, - } + unbind: function() { - if ( m.mapLight ) { + if( ! this.isBound ) return; - create_texture( mpars, 'lightMap', m.mapLight, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy ); + this.setValue( this.originalValue ); - } + this.setValue = null; + this.getValue = null; + this.lerpValue = null; + this.equalsValue = null; + this.triggerDirty = null; + this.isBound = false; - if ( m.mapAO ) { + }, - create_texture( mpars, 'aoMap', m.mapAO, m.mapAORepeat, m.mapAOOffset, m.mapAOWrap, m.mapAOAnisotropy ); + // bind to the real property in the scene graph, remember original value, memorize various accessors for speed/inefficiency + bind: function() { - } + if( this.isBound ) return; - if ( m.mapBump ) { + var targetObject = this.node; - create_texture( mpars, 'bumpMap', m.mapBump, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy ); + // ensure there is a value node + if( ! targetObject ) { + console.error( " trying to update node for track: " + this.trackName + " but it wasn't found." ); + return; + } + if( this.objectName ) { + // special case were we need to reach deeper into the hierarchy to get the face materials.... + if( this.objectName === "materials" ) { + if( ! targetObject.material ) { + console.error( ' can not bind to material as node does not have a material', this ); + return; + } + if( ! targetObject.material.materials ) { + console.error( ' can not bind to material.materials as node.material does not have a materials array', this ); + return; + } + targetObject = targetObject.material.materials; + } + else if( this.objectName === "bones" ) { + if( ! targetObject.skeleton ) { + console.error( ' can not bind to bones as node does not have a skeleton', this ); + return; + } + // potential future optimization: skip this if propertyIndex is already an integer, and convert the integer string to a true integer. + + targetObject = targetObject.skeleton.bones; + + // support resolving morphTarget names into indices. + for( var i = 0; i < targetObject.length; i ++ ) { + if( targetObject[i].name === this.objectIndex ) { + this.objectIndex = i; + break; + } } + } + else { - if ( m.mapNormal ) { + if( targetObject[ this.objectName ] === undefined ) { + console.error( ' can not bind to objectName of node, undefined', this ); + return; + } + targetObject = targetObject[ this.objectName ]; + } + + if( this.objectIndex !== undefined ) { + if( targetObject[ this.objectIndex ] === undefined ) { + console.error( " trying to bind to objectIndex of objectName, but is undefined:", this, targetObject ); + return; + } - create_texture( mpars, 'normalMap', m.mapNormal, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy ); + targetObject = targetObject[ this.objectIndex ]; + } - } + } - if ( m.mapSpecular ) { + // special case mappings + var nodeProperty = targetObject[ this.propertyName ]; + if( ! nodeProperty ) { + console.error( " trying to update property for track: " + this.nodeName + '.' + this.propertyName + " but it wasn't found.", targetObject ); + return; + } - create_texture( mpars, 'specularMap', m.mapSpecular, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy ); + // access a sub element of the property array (only primitives are supported right now) + if( this.propertyIndex !== undefined ) { + if( this.propertyName === "morphTargetInfluences" ) { + // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. + + // support resolving morphTarget names into indices. + if( ! targetObject.geometry ) { + console.error( ' can not bind to morphTargetInfluences becasuse node does not have a geometry', this ); + } + if( ! targetObject.geometry.morphTargets ) { + console.error( ' can not bind to morphTargetInfluences becasuse node does not have a geometry.morphTargets', this ); + } + + for( var i = 0; i < this.node.geometry.morphTargets.length; i ++ ) { + if( targetObject.geometry.morphTargets[i].name === this.propertyIndex ) { + this.propertyIndex = i; + break; + } } + } - if ( m.mapAlpha ) { + this.setValue = function setValue_propertyIndexed( value ) { + if( ! this.equalsValue( nodeProperty[ this.propertyIndex ], value ) ) { + nodeProperty[ this.propertyIndex ] = value; + return true; + } + return false; + }; - create_texture( mpars, 'alphaMap', m.mapAlpha, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy ); + this.getValue = function getValue_propertyIndexed() { + return nodeProperty[ this.propertyIndex ]; + }; + } + // must use copy for Object3D.Euler/Quaternion + else if( nodeProperty.copy ) { + + this.setValue = function setValue_propertyObject( value ) { + if( ! this.equalsValue( nodeProperty, value ) ) { + nodeProperty.copy( value ); + return true; } - + return false; } - // - - if ( m.mapBumpScale ) { + this.getValue = function getValue_propertyObject() { + return nodeProperty; + }; - mpars.bumpScale = m.mapBumpScale; + } + // otherwise just set the property directly on the node (do not use nodeProperty as it may not be a reference object) + else { + this.setValue = function setValue_property( value ) { + if( ! this.equalsValue( targetObject[ this.propertyName ], value ) ) { + targetObject[ this.propertyName ] = value; + return true; + } + return false; } - if ( m.mapNormalFactor ) { + this.getValue = function getValue_property() { + return targetObject[ this.propertyName ]; + }; - mpars.normalScale = new THREE.Vector2( m.mapNormalFactor, m.mapNormalFactor ); + } + // trigger node dirty + if( targetObject.needsUpdate !== undefined ) { // material + + this.triggerDirty = function triggerDirty_needsUpdate() { + this.node.needsUpdate = true; } - var material = new THREE[ mtype ]( mpars ); + } + else if( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform + + this.triggerDirty = function triggerDirty_matrixWorldNeedsUpdate() { + targetObject.matrixWorldNeedsUpdate = true; + } - if ( m.DbgName !== undefined ) material.name = m.DbgName; + } - return material; + this.originalValue = this.getValue(); - }; + this.equalsValue = THREE.AnimationUtils.getEqualsFunc( this.originalValue ); + this.lerpValue = THREE.AnimationUtils.getLerpFunc( this.originalValue, true ); - } )() + this.isBound = true; -}; + }, -THREE.Loader.Handlers = { + apply: function() { - handlers: [], + // for speed capture the setter pattern as a closure (sort of a memoization pattern: https://en.wikipedia.org/wiki/Memoization) + if( ! this.isBound ) this.bind(); - add: function ( regex, loader ) { + // early exit if there is nothing to apply. + if( this.cumulativeWeight > 0 ) { + + // blend with original value + if( this.cumulativeWeight < 1 ) { - this.handlers.push( regex, loader ); + var remainingWeight = 1 - this.cumulativeWeight; + var lerpAlpha = remainingWeight / ( this.cumulativeWeight + remainingWeight ); + this.cumulativeValue = this.lerpValue( this.cumulativeValue, this.originalValue, lerpAlpha ); - }, + } - get: function ( file ) { + var valueChanged = this.setValue( this.cumulativeValue ); - for ( var i = 0, l = this.handlers.length; i < l; i += 2 ) { + if( valueChanged && this.triggerDirty ) { + this.triggerDirty(); + } - var regex = this.handlers[ i ]; - var loader = this.handlers[ i + 1 ]; + // reset accumulator + this.cumulativeValue = null; + this.cumulativeWeight = 0; - if ( regex.test( file ) ) { + } + } - return loader; +}; - } - } +THREE.PropertyBinding.parseTrackName = function( trackName ) { - return null; + // matches strings in the form of: + // nodeName.property + // nodeName.property[accessor] + // nodeName.material.property[accessor] + // uuid.property[accessor] + // uuid.objectName[objectIndex].propertyName[propertyIndex] + // parentName/nodeName.property + // parentName/parentName/nodeName.property[index] + // .bone[Armature.DEF_cog].position + // created and tested via https://regex101.com/#javascript - } + var re = /^(([\w]+\/)*)([\w-\d]+)?(\.([\w]+)(\[([\w\d\[\]\_. ]+)\])?)?(\.([\w.]+)(\[([\w\d\[\]\_. ]+)\])?)$/; + var matches = re.exec(trackName); -}; + if( ! matches ) { + throw new Error( "cannot parse trackName at all: " + trackName ); + } -// File:src/loaders/XHRLoader.js + if (matches.index === re.lastIndex) { + re.lastIndex++; + } -/** - * @author mrdoob / http://mrdoob.com/ - */ + var results = { + directoryName: matches[1], + nodeName: matches[3], // allowed to be null, specified root node. + objectName: matches[5], + objectIndex: matches[7], + propertyName: matches[9], + propertyIndex: matches[11] // allowed to be null, specifies that the whole property is set. + }; -THREE.XHRLoader = function ( manager ) { + if( results.propertyName === null || results.propertyName.length === 0 ) { + throw new Error( "can not parse propertyName from trackName: " + trackName ); + } - this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + return results; }; -THREE.XHRLoader.prototype = { +THREE.PropertyBinding.findNode = function( root, nodeName ) { - constructor: THREE.XHRLoader, + if( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid ) { - load: function ( url, onLoad, onProgress, onError ) { + return root; - var scope = this; + } - var cached = THREE.Cache.get( url ); + // search into skeleton bones. + if( root.skeleton ) { - if ( cached !== undefined ) { + var searchSkeleton = function( skeleton ) { - if ( onLoad ) { + for( var i = 0; i < skeleton.bones.length; i ++ ) { - setTimeout( function () { + var bone = skeleton.bones[i]; - onLoad( cached ); + if( bone.name === nodeName ) { - }, 0 ); + return bone; + } } - return cached; + return null; - } + }; - var request = new XMLHttpRequest(); - request.open( 'GET', url, true ); + var bone = searchSkeleton( root.skeleton ); - request.addEventListener( 'load', function ( event ) { + if( bone ) { - THREE.Cache.add( url, this.response ); + return bone; - if ( onLoad ) onLoad( this.response ); + } + } - scope.manager.itemEnd( url ); + // search into node subtree. + if( root.children ) { - }, false ); + var searchNodeSubtree = function( children ) { - if ( onProgress !== undefined ) { + for( var i = 0; i < children.length; i ++ ) { - request.addEventListener( 'progress', function ( event ) { + var childNode = children[i]; - onProgress( event ); + if( childNode.name === nodeName || childNode.uuid === nodeName ) { - }, false ); + return childNode; - } + } - request.addEventListener( 'error', function ( event ) { + var result = searchNodeSubtree( childNode.children ); - if ( onError ) onError( event ); + if( result ) return result; - scope.manager.itemError( url ); + } - }, false ); + return null; - if ( this.crossOrigin !== undefined ) request.crossOrigin = this.crossOrigin; - if ( this.responseType !== undefined ) request.responseType = this.responseType; - if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials; + }; - request.send( null ); + var subTreeNode = searchNodeSubtree( root.children ); - scope.manager.itemStart( url ); + if( subTreeNode ) { - return request; + return subTreeNode; - }, + } - setResponseType: function ( value ) { + } - this.responseType = value; + return null; +} - }, +// File:src/animation/tracks/VectorKeyframeTrack.js - setCrossOrigin: function ( value ) { - - this.crossOrigin = value; - - }, +/** + * + * A Track that interpolates Vectors + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ - setWithCredentials: function ( value ) { +THREE.VectorKeyframeTrack = function ( name, keys ) { - this.withCredentials = value; + THREE.KeyframeTrack.call( this, name, keys ); - } + // local cache of value type to avoid allocations during runtime. + this.result = this.keys[0].value.clone(); }; -// File:src/loaders/ImageLoader.js +THREE.VectorKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype ); -/** - * @author mrdoob / http://mrdoob.com/ - */ +THREE.VectorKeyframeTrack.prototype.constructor = THREE.VectorKeyframeTrack; -THREE.ImageLoader = function ( manager ) { +THREE.VectorKeyframeTrack.prototype.setResult = function( value ) { - this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + this.result.copy( value ); }; -THREE.ImageLoader.prototype = { - - constructor: THREE.ImageLoader, +// memoization of the lerp function for speed. +// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis. +THREE.VectorKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) { - load: function ( url, onLoad, onProgress, onError ) { + return value0.lerp( value1, alpha ); - var scope = this; +}; - var cached = THREE.Cache.get( url ); +THREE.VectorKeyframeTrack.prototype.compareValues = function( value0, value1 ) { - if ( cached !== undefined ) { + return value0.equals( value1 ); - if ( onLoad ) { +}; - setTimeout( function () { +THREE.VectorKeyframeTrack.prototype.clone = function() { - onLoad( cached ); + var clonedKeys = []; - }, 0 ); + for( var i = 0; i < this.keys.length; i ++ ) { + + var key = this.keys[i]; + clonedKeys.push( { + time: key.time, + value: key.value.clone() + } ); + } - } + return new THREE.VectorKeyframeTrack( this.name, clonedKeys ); - return cached; +}; - } +THREE.VectorKeyframeTrack.parse = function( json ) { - var image = document.createElement( 'img' ); + var elementCount = json.keys[0].value.length; + var valueType = THREE[ 'Vector' + elementCount ]; - image.addEventListener( 'load', function ( event ) { + var keys = []; - THREE.Cache.add( url, this ); + for( var i = 0; i < json.keys.length; i ++ ) { + var jsonKey = json.keys[i]; + keys.push( { + value: new valueType().fromArray( jsonKey.value ), + time: jsonKey.time + } ); + } - if ( onLoad ) onLoad( this ); + return new THREE.VectorKeyframeTrack( json.name, keys ); - scope.manager.itemEnd( url ); +}; + +// File:src/animation/tracks/QuaternionKeyframeTrack.js - }, false ); +/** + * + * A Track that interpolates Quaternion + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ - if ( onProgress !== undefined ) { +THREE.QuaternionKeyframeTrack = function ( name, keys ) { - image.addEventListener( 'progress', function ( event ) { + THREE.KeyframeTrack.call( this, name, keys ); - onProgress( event ); + // local cache of value type to avoid allocations during runtime. + this.result = this.keys[0].value.clone(); - }, false ); +}; + +THREE.QuaternionKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype ); - } +THREE.QuaternionKeyframeTrack.prototype.constructor = THREE.QuaternionKeyframeTrack; - image.addEventListener( 'error', function ( event ) { +THREE.QuaternionKeyframeTrack.prototype.setResult = function( value ) { - if ( onError ) onError( event ); + this.result.copy( value ); - scope.manager.itemError( url ); +}; - }, false ); +// memoization of the lerp function for speed. +// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis. +THREE.QuaternionKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) { - if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; + return value0.slerp( value1, alpha ); - scope.manager.itemStart( url ); +}; - image.src = url; +THREE.QuaternionKeyframeTrack.prototype.compareValues = function( value0, value1 ) { - return image; + return value0.equals( value1 ); - }, +}; - setCrossOrigin: function ( value ) { +THREE.QuaternionKeyframeTrack.prototype.multiply = function( quat ) { - this.crossOrigin = value; + for( var i = 0; i < this.keys.length; i ++ ) { + this.keys[i].value.multiply( quat ); + } -}; - -// File:src/loaders/JSONLoader.js - -/** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ + return this; -THREE.JSONLoader = function ( manager ) { +}; - if ( typeof manager === 'boolean' ) { +THREE.QuaternionKeyframeTrack.prototype.clone = function() { - console.warn( 'THREE.JSONLoader: showStatus parameter has been removed from constructor.' ); - manager = undefined; + var clonedKeys = []; + for( var i = 0; i < this.keys.length; i ++ ) { + + var key = this.keys[i]; + clonedKeys.push( { + time: key.time, + value: key.value.clone() + } ); } - this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; - - this.withCredentials = false; + return new THREE.QuaternionKeyframeTrack( this.name, clonedKeys ); }; -THREE.JSONLoader.prototype = { +THREE.QuaternionKeyframeTrack.parse = function( json ) { - constructor: THREE.JSONLoader, + var keys = []; - // Deprecated - - get statusDomElement () { + for( var i = 0; i < json.keys.length; i ++ ) { + var jsonKey = json.keys[i]; + keys.push( { + value: new THREE.Quaternion().fromArray( jsonKey.value ), + time: jsonKey.time + } ); + } - if ( this._statusDomElement === undefined ) { + return new THREE.QuaternionKeyframeTrack( json.name, keys ); - this._statusDomElement = document.createElement( 'div' ); +}; + +// File:src/animation/tracks/StringKeyframeTrack.js - } +/** + * + * A Track that interpolates Strings + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ - console.warn( 'THREE.JSONLoader: .statusDomElement has been removed.' ); - return this._statusDomElement; +THREE.StringKeyframeTrack = function ( name, keys ) { - }, + THREE.KeyframeTrack.call( this, name, keys ); - load: function( url, onLoad, onProgress, onError ) { + // local cache of value type to avoid allocations during runtime. + this.result = this.keys[0].value; - var scope = this; +}; - var texturePath = this.texturePath && ( typeof this.texturePath === "string" ) ? this.texturePath : THREE.Loader.prototype.extractUrlBase( url ); +THREE.StringKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype ); - var loader = new THREE.XHRLoader( this.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.setWithCredentials( this.withCredentials ); - loader.load( url, function ( text ) { +THREE.StringKeyframeTrack.prototype.constructor = THREE.StringKeyframeTrack; - var json = JSON.parse( text ); - var metadata = json.metadata; +THREE.StringKeyframeTrack.prototype.setResult = function( value ) { - if ( metadata !== undefined ) { + this.result = value; - if ( metadata.type === 'object' ) { +}; - console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.ObjectLoader instead.' ); - return; +// memoization of the lerp function for speed. +// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis. +THREE.StringKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) { - } + return ( alpha < 1.0 ) ? value0 : value1; - if ( metadata.type === 'scene' ) { +}; - console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.SceneLoader instead.' ); - return; +THREE.StringKeyframeTrack.prototype.compareValues = function( value0, value1 ) { - } + return ( value0 === value1 ); - } +}; - var object = scope.parse( json, texturePath ); - onLoad( object.geometry, object.materials ); +THREE.StringKeyframeTrack.prototype.clone = function() { + var clonedKeys = []; + + for( var i = 0; i < this.keys.length; i ++ ) { + + var key = this.keys[i]; + clonedKeys.push( { + time: key.time, + value: key.value } ); + } - }, + return new THREE.StringKeyframeTrack( this.name, clonedKeys ); - setCrossOrigin: function ( value ) { +}; - this.crossOrigin = value; +THREE.StringKeyframeTrack.parse = function( json ) { - }, + return new THREE.StringKeyframeTrack( json.name, json.keys ); - setTexturePath: function ( value ) { +}; + +// File:src/animation/tracks/BooleanKeyframeTrack.js - this.texturePath = value; +/** + * + * A Track that interpolates Boolean + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ - }, +THREE.BooleanKeyframeTrack = function ( name, keys ) { - parse: function ( json, texturePath ) { + THREE.KeyframeTrack.call( this, name, keys ); - var geometry = new THREE.Geometry(), - scale = ( json.scale !== undefined ) ? 1.0 / json.scale : 1.0; + // local cache of value type to avoid allocations during runtime. + this.result = this.keys[0].value; - parseModel( scale ); +}; - parseSkin(); - parseMorphing( scale ); +THREE.BooleanKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype ); - geometry.computeFaceNormals(); - geometry.computeBoundingSphere(); +THREE.BooleanKeyframeTrack.prototype.constructor = THREE.BooleanKeyframeTrack; - function parseModel( scale ) { +THREE.BooleanKeyframeTrack.prototype.setResult = function( value ) { - function isBitSet( value, position ) { + this.result = value; - return value & ( 1 << position ); +}; - } +// memoization of the lerp function for speed. +// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis. +THREE.BooleanKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) { - var i, j, fi, + return ( alpha < 1.0 ) ? value0 : value1; - offset, zLength, +}; - colorIndex, normalIndex, uvIndex, materialIndex, +THREE.BooleanKeyframeTrack.prototype.compareValues = function( value0, value1 ) { - type, - isQuad, - hasMaterial, - hasFaceVertexUv, - hasFaceNormal, hasFaceVertexNormal, - hasFaceColor, hasFaceVertexColor, + return ( value0 === value1 ); - vertex, face, faceA, faceB, hex, normal, +}; - uvLayer, uv, u, v, +THREE.BooleanKeyframeTrack.prototype.clone = function() { - faces = json.faces, - vertices = json.vertices, - normals = json.normals, - colors = json.colors, + var clonedKeys = []; - nUvLayers = 0; + for( var i = 0; i < this.keys.length; i ++ ) { + + var key = this.keys[i]; + clonedKeys.push( { + time: key.time, + value: key.value + } ); + } - if ( json.uvs !== undefined ) { + return new THREE.BooleanKeyframeTrack( this.name, clonedKeys ); - // disregard empty arrays +}; - for ( i = 0; i < json.uvs.length; i ++ ) { +THREE.BooleanKeyframeTrack.parse = function( json ) { - if ( json.uvs[ i ].length ) nUvLayers ++; + return new THREE.BooleanKeyframeTrack( json.name, json.keys ); - } +}; + +// File:src/animation/tracks/NumberKeyframeTrack.js - for ( i = 0; i < nUvLayers; i ++ ) { +/** + * + * A Track that interpolates Numbers + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ - geometry.faceVertexUvs[ i ] = []; +THREE.NumberKeyframeTrack = function ( name, keys ) { - } + THREE.KeyframeTrack.call( this, name, keys ); - } + // local cache of value type to avoid allocations during runtime. + this.result = this.keys[0].value; - offset = 0; - zLength = vertices.length; +}; - while ( offset < zLength ) { +THREE.NumberKeyframeTrack.prototype = Object.create( THREE.KeyframeTrack.prototype ); - vertex = new THREE.Vector3(); +THREE.NumberKeyframeTrack.prototype.constructor = THREE.NumberKeyframeTrack; - vertex.x = vertices[ offset ++ ] * scale; - vertex.y = vertices[ offset ++ ] * scale; - vertex.z = vertices[ offset ++ ] * scale; +THREE.NumberKeyframeTrack.prototype.setResult = function( value ) { - geometry.vertices.push( vertex ); + this.result = value; - } +}; - offset = 0; - zLength = faces.length; +// memoization of the lerp function for speed. +// NOTE: Do not optimize as a prototype initialization closure, as value0 will be different on a per class basis. +THREE.NumberKeyframeTrack.prototype.lerpValues = function( value0, value1, alpha ) { - while ( offset < zLength ) { + return value0 * ( 1 - alpha ) + value1 * alpha; - type = faces[ offset ++ ]; +}; +THREE.NumberKeyframeTrack.prototype.compareValues = function( value0, value1 ) { - isQuad = isBitSet( type, 0 ); - hasMaterial = isBitSet( type, 1 ); - hasFaceVertexUv = isBitSet( type, 3 ); - hasFaceNormal = isBitSet( type, 4 ); - hasFaceVertexNormal = isBitSet( type, 5 ); - hasFaceColor = isBitSet( type, 6 ); - hasFaceVertexColor = isBitSet( type, 7 ); + return ( value0 === value1 ); - // console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor); +}; - if ( isQuad ) { +THREE.NumberKeyframeTrack.prototype.clone = function() { - faceA = new THREE.Face3(); - faceA.a = faces[ offset ]; - faceA.b = faces[ offset + 1 ]; - faceA.c = faces[ offset + 3 ]; + var clonedKeys = []; - faceB = new THREE.Face3(); - faceB.a = faces[ offset + 1 ]; - faceB.b = faces[ offset + 2 ]; - faceB.c = faces[ offset + 3 ]; + for( var i = 0; i < this.keys.length; i ++ ) { + + var key = this.keys[i]; + clonedKeys.push( { + time: key.time, + value: key.value + } ); + } - offset += 4; + return new THREE.NumberKeyframeTrack( this.name, clonedKeys ); - if ( hasMaterial ) { +}; - materialIndex = faces[ offset ++ ]; - faceA.materialIndex = materialIndex; - faceB.materialIndex = materialIndex; +THREE.NumberKeyframeTrack.parse = function( json ) { - } + return new THREE.NumberKeyframeTrack( json.name, json.keys ); - // to get face <=> uv index correspondence +}; + +// File:src/cameras/Camera.js - fi = geometry.faces.length; +/** + * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + * @author WestLangley / http://github.com/WestLangley +*/ - if ( hasFaceVertexUv ) { +THREE.Camera = function () { - for ( i = 0; i < nUvLayers; i ++ ) { + THREE.Object3D.call( this ); - uvLayer = json.uvs[ i ]; + this.type = 'Camera'; - geometry.faceVertexUvs[ i ][ fi ] = []; - geometry.faceVertexUvs[ i ][ fi + 1 ] = []; + this.matrixWorldInverse = new THREE.Matrix4(); + this.projectionMatrix = new THREE.Matrix4(); - for ( j = 0; j < 4; j ++ ) { +}; - uvIndex = faces[ offset ++ ]; +THREE.Camera.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Camera.prototype.constructor = THREE.Camera; - u = uvLayer[ uvIndex * 2 ]; - v = uvLayer[ uvIndex * 2 + 1 ]; +THREE.Camera.prototype.getWorldDirection = function () { - uv = new THREE.Vector2( u, v ); + var quaternion = new THREE.Quaternion(); - if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv ); - if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv ); + return function ( optionalTarget ) { - } + var result = optionalTarget || new THREE.Vector3(); - } + this.getWorldQuaternion( quaternion ); - } + return result.set( 0, 0, - 1 ).applyQuaternion( quaternion ); - if ( hasFaceNormal ) { + }; - normalIndex = faces[ offset ++ ] * 3; +}(); - faceA.normal.set( - normals[ normalIndex ++ ], - normals[ normalIndex ++ ], - normals[ normalIndex ] - ); +THREE.Camera.prototype.lookAt = function () { - faceB.normal.copy( faceA.normal ); + // This routine does not support cameras with rotated and/or translated parent(s) - } + var m1 = new THREE.Matrix4(); - if ( hasFaceVertexNormal ) { + return function ( vector ) { - for ( i = 0; i < 4; i ++ ) { + m1.lookAt( this.position, vector, this.up ); - normalIndex = faces[ offset ++ ] * 3; + this.quaternion.setFromRotationMatrix( m1 ); - normal = new THREE.Vector3( - normals[ normalIndex ++ ], - normals[ normalIndex ++ ], - normals[ normalIndex ] - ); + }; +}(); - if ( i !== 2 ) faceA.vertexNormals.push( normal ); - if ( i !== 0 ) faceB.vertexNormals.push( normal ); +THREE.Camera.prototype.clone = function () { - } + return new this.constructor().copy( this ); - } +}; +THREE.Camera.prototype.copy = function ( source ) { - if ( hasFaceColor ) { + THREE.Object3D.prototype.copy.call( this, source ); - colorIndex = faces[ offset ++ ]; - hex = colors[ colorIndex ]; + this.matrixWorldInverse.copy( source.matrixWorldInverse ); + this.projectionMatrix.copy( source.projectionMatrix ); - faceA.color.setHex( hex ); - faceB.color.setHex( hex ); + return this; - } +}; +// File:src/cameras/CubeCamera.js - if ( hasFaceVertexColor ) { +/** + * Camera for rendering cube maps + * - renders scene into axis-aligned cube + * + * @author alteredq / http://alteredqualia.com/ + */ - for ( i = 0; i < 4; i ++ ) { +THREE.CubeCamera = function ( near, far, cubeResolution ) { - colorIndex = faces[ offset ++ ]; - hex = colors[ colorIndex ]; + THREE.Object3D.call( this ); - if ( i !== 2 ) faceA.vertexColors.push( new THREE.Color( hex ) ); - if ( i !== 0 ) faceB.vertexColors.push( new THREE.Color( hex ) ); + this.type = 'CubeCamera'; - } + var fov = 90, aspect = 1; - } + var cameraPX = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraPX.up.set( 0, - 1, 0 ); + cameraPX.lookAt( new THREE.Vector3( 1, 0, 0 ) ); + this.add( cameraPX ); - geometry.faces.push( faceA ); - geometry.faces.push( faceB ); + var cameraNX = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraNX.up.set( 0, - 1, 0 ); + cameraNX.lookAt( new THREE.Vector3( - 1, 0, 0 ) ); + this.add( cameraNX ); - } else { + var cameraPY = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraPY.up.set( 0, 0, 1 ); + cameraPY.lookAt( new THREE.Vector3( 0, 1, 0 ) ); + this.add( cameraPY ); - face = new THREE.Face3(); - face.a = faces[ offset ++ ]; - face.b = faces[ offset ++ ]; - face.c = faces[ offset ++ ]; + var cameraNY = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraNY.up.set( 0, 0, - 1 ); + cameraNY.lookAt( new THREE.Vector3( 0, - 1, 0 ) ); + this.add( cameraNY ); - if ( hasMaterial ) { + var cameraPZ = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraPZ.up.set( 0, - 1, 0 ); + cameraPZ.lookAt( new THREE.Vector3( 0, 0, 1 ) ); + this.add( cameraPZ ); - materialIndex = faces[ offset ++ ]; - face.materialIndex = materialIndex; + var cameraNZ = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraNZ.up.set( 0, - 1, 0 ); + cameraNZ.lookAt( new THREE.Vector3( 0, 0, - 1 ) ); + this.add( cameraNZ ); - } + this.renderTarget = new THREE.WebGLRenderTargetCube( cubeResolution, cubeResolution, { format: THREE.RGBFormat, magFilter: THREE.LinearFilter, minFilter: THREE.LinearFilter } ); - // to get face <=> uv index correspondence + this.updateCubeMap = function ( renderer, scene ) { - fi = geometry.faces.length; + if ( this.parent === null ) this.updateMatrixWorld(); - if ( hasFaceVertexUv ) { + var renderTarget = this.renderTarget; + var generateMipmaps = renderTarget.generateMipmaps; - for ( i = 0; i < nUvLayers; i ++ ) { + renderTarget.generateMipmaps = false; - uvLayer = json.uvs[ i ]; + renderTarget.activeCubeFace = 0; + renderer.render( scene, cameraPX, renderTarget ); - geometry.faceVertexUvs[ i ][ fi ] = []; + renderTarget.activeCubeFace = 1; + renderer.render( scene, cameraNX, renderTarget ); - for ( j = 0; j < 3; j ++ ) { + renderTarget.activeCubeFace = 2; + renderer.render( scene, cameraPY, renderTarget ); - uvIndex = faces[ offset ++ ]; + renderTarget.activeCubeFace = 3; + renderer.render( scene, cameraNY, renderTarget ); - u = uvLayer[ uvIndex * 2 ]; - v = uvLayer[ uvIndex * 2 + 1 ]; + renderTarget.activeCubeFace = 4; + renderer.render( scene, cameraPZ, renderTarget ); - uv = new THREE.Vector2( u, v ); + renderTarget.generateMipmaps = generateMipmaps; - geometry.faceVertexUvs[ i ][ fi ].push( uv ); + renderTarget.activeCubeFace = 5; + renderer.render( scene, cameraNZ, renderTarget ); - } + renderer.setRenderTarget( null ); - } + }; - } +}; - if ( hasFaceNormal ) { +THREE.CubeCamera.prototype = Object.create( THREE.Object3D.prototype ); +THREE.CubeCamera.prototype.constructor = THREE.CubeCamera; - normalIndex = faces[ offset ++ ] * 3; +// File:src/cameras/OrthographicCamera.js - face.normal.set( - normals[ normalIndex ++ ], - normals[ normalIndex ++ ], - normals[ normalIndex ] - ); +/** + * @author alteredq / http://alteredqualia.com/ + */ - } +THREE.OrthographicCamera = function ( left, right, top, bottom, near, far ) { - if ( hasFaceVertexNormal ) { + THREE.Camera.call( this ); - for ( i = 0; i < 3; i ++ ) { + this.type = 'OrthographicCamera'; - normalIndex = faces[ offset ++ ] * 3; + this.zoom = 1; - normal = new THREE.Vector3( - normals[ normalIndex ++ ], - normals[ normalIndex ++ ], - normals[ normalIndex ] - ); + this.left = left; + this.right = right; + this.top = top; + this.bottom = bottom; - face.vertexNormals.push( normal ); + this.near = ( near !== undefined ) ? near : 0.1; + this.far = ( far !== undefined ) ? far : 2000; - } + this.updateProjectionMatrix(); - } +}; +THREE.OrthographicCamera.prototype = Object.create( THREE.Camera.prototype ); +THREE.OrthographicCamera.prototype.constructor = THREE.OrthographicCamera; - if ( hasFaceColor ) { +THREE.OrthographicCamera.prototype.updateProjectionMatrix = function () { - colorIndex = faces[ offset ++ ]; - face.color.setHex( colors[ colorIndex ] ); + 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 ); +}; - if ( hasFaceVertexColor ) { +THREE.OrthographicCamera.prototype.copy = function ( source ) { + + THREE.Camera.prototype.copy.call( this, source ); + + this.left = source.left; + this.right = source.right; + this.top = source.top; + this.bottom = source.bottom; + this.near = source.near; + this.far = source.far; + + this.zoom = source.zoom; + + return this; + +}; - for ( i = 0; i < 3; i ++ ) { +THREE.OrthographicCamera.prototype.toJSON = function ( meta ) { - colorIndex = faces[ offset ++ ]; - face.vertexColors.push( new THREE.Color( colors[ colorIndex ] ) ); + var data = THREE.Object3D.prototype.toJSON.call( this, meta ); - } + data.object.zoom = this.zoom; + data.object.left = this.left; + data.object.right = this.right; + data.object.top = this.top; + data.object.bottom = this.bottom; + data.object.near = this.near; + data.object.far = this.far; - } + return data; - geometry.faces.push( face ); +}; - } +// File:src/cameras/PerspectiveCamera.js - } +/** + * @author mrdoob / http://mrdoob.com/ + * @author greggman / http://games.greggman.com/ + * @author zz85 / http://www.lab4games.net/zz85/blog + */ - }; +THREE.PerspectiveCamera = function ( fov, aspect, near, far ) { - function parseSkin() { + THREE.Camera.call( this ); - var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2; + this.type = 'PerspectiveCamera'; - if ( json.skinWeights ) { + this.zoom = 1; - for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) { + this.fov = fov !== undefined ? fov : 50; + this.aspect = aspect !== undefined ? aspect : 1; + this.near = near !== undefined ? near : 0.1; + this.far = far !== undefined ? far : 2000; - var x = json.skinWeights[ i ]; - var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0; - var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0; - var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0; + this.updateProjectionMatrix(); - geometry.skinWeights.push( new THREE.Vector4( x, y, z, w ) ); +}; - } +THREE.PerspectiveCamera.prototype = Object.create( THREE.Camera.prototype ); +THREE.PerspectiveCamera.prototype.constructor = THREE.PerspectiveCamera; - } - if ( json.skinIndices ) { +/** + * Uses Focal Length (in mm) to estimate and set FOV + * 35mm (full-frame) camera is used if frame size is not specified; + * Formula based on http://www.bobatkins.com/photography/technical/field_of_view.html + */ - for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) { +THREE.PerspectiveCamera.prototype.setLens = function ( focalLength, frameHeight ) { - var a = json.skinIndices[ i ]; - var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0; - var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0; - var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0; + if ( frameHeight === undefined ) frameHeight = 24; - geometry.skinIndices.push( new THREE.Vector4( a, b, c, d ) ); + this.fov = 2 * THREE.Math.radToDeg( Math.atan( frameHeight / ( focalLength * 2 ) ) ); + this.updateProjectionMatrix(); - } +}; - } - geometry.bones = json.bones; +/** + * Sets an offset in a larger frustum. This is useful for multi-window or + * multi-monitor/multi-machine setups. + * + * For example, if you have 3x2 monitors and each monitor is 1920x1080 and + * the monitors are in grid like this + * + * +---+---+---+ + * | A | B | C | + * +---+---+---+ + * | D | E | F | + * +---+---+---+ + * + * then for each monitor you would call it like this + * + * var w = 1920; + * var h = 1080; + * var fullWidth = w * 3; + * var fullHeight = h * 2; + * + * --A-- + * camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); + * --B-- + * camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); + * --C-- + * camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); + * --D-- + * camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); + * --E-- + * camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); + * --F-- + * camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); + * + * Note there is no reason monitors have to be the same size or in a grid. + */ - if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) { +THREE.PerspectiveCamera.prototype.setViewOffset = function ( fullWidth, fullHeight, x, y, width, height ) { - console.warn( 'When skinning, number of vertices (' + geometry.vertices.length + '), skinIndices (' + - geometry.skinIndices.length + '), and skinWeights (' + geometry.skinWeights.length + ') should match.' ); + this.fullWidth = fullWidth; + this.fullHeight = fullHeight; + this.x = x; + this.y = y; + this.width = width; + this.height = height; - } + this.updateProjectionMatrix(); +}; - // could change this to json.animations[0] or remove completely - geometry.animation = json.animation; - geometry.animations = json.animations; +THREE.PerspectiveCamera.prototype.updateProjectionMatrix = function () { - }; + var fov = THREE.Math.radToDeg( 2 * Math.atan( Math.tan( THREE.Math.degToRad( this.fov ) * 0.5 ) / this.zoom ) ); - function parseMorphing( scale ) { + if ( this.fullWidth ) { - if ( json.morphTargets !== undefined ) { + var aspect = this.fullWidth / this.fullHeight; + var top = Math.tan( THREE.Math.degToRad( fov * 0.5 ) ) * this.near; + var bottom = - top; + var left = aspect * bottom; + var right = aspect * top; + var width = Math.abs( right - left ); + var height = Math.abs( top - bottom ); - var i, l, v, vl, dstVertices, srcVertices; + this.projectionMatrix.makeFrustum( + left + this.x * width / this.fullWidth, + left + ( this.x + this.width ) * width / this.fullWidth, + top - ( this.y + this.height ) * height / this.fullHeight, + top - this.y * height / this.fullHeight, + this.near, + this.far + ); - for ( i = 0, l = json.morphTargets.length; i < l; i ++ ) { + } else { - geometry.morphTargets[ i ] = {}; - geometry.morphTargets[ i ].name = json.morphTargets[ i ].name; - geometry.morphTargets[ i ].vertices = []; + this.projectionMatrix.makePerspective( fov, this.aspect, this.near, this.far ); - dstVertices = geometry.morphTargets[ i ].vertices; - srcVertices = json.morphTargets[ i ].vertices; + } - for ( v = 0, vl = srcVertices.length; v < vl; v += 3 ) { +}; - var vertex = new THREE.Vector3(); - vertex.x = srcVertices[ v ] * scale; - vertex.y = srcVertices[ v + 1 ] * scale; - vertex.z = srcVertices[ v + 2 ] * scale; +THREE.PerspectiveCamera.prototype.copy = function ( source ) { + + THREE.Camera.prototype.copy.call( this, source ); + + this.fov = source.fov; + this.aspect = source.aspect; + this.near = source.near; + this.far = source.far; + + this.zoom = source.zoom; + + return this; + +}; - dstVertices.push( vertex ); +THREE.PerspectiveCamera.prototype.toJSON = function ( meta ) { - } + var data = THREE.Object3D.prototype.toJSON.call( this, meta ); - } + data.object.zoom = this.zoom; + data.object.fov = this.fov; + data.object.aspect = this.aspect; + data.object.near = this.near; + data.object.far = this.far; - } + return data; - if ( json.morphColors !== undefined ) { +}; - var i, l, c, cl, dstColors, srcColors, color; +// File:src/lights/Light.js - for ( i = 0, l = json.morphColors.length; i < l; i ++ ) { +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ - geometry.morphColors[ i ] = {}; - geometry.morphColors[ i ].name = json.morphColors[ i ].name; - geometry.morphColors[ i ].colors = []; +THREE.Light = function ( color ) { - dstColors = geometry.morphColors[ i ].colors; - srcColors = json.morphColors[ i ].colors; + THREE.Object3D.call( this ); - for ( c = 0, cl = srcColors.length; c < cl; c += 3 ) { + this.type = 'Light'; - color = new THREE.Color( 0xffaa00 ); - color.setRGB( srcColors[ c ], srcColors[ c + 1 ], srcColors[ c + 2 ] ); - dstColors.push( color ); + this.color = new THREE.Color( color ); - } +}; - } +THREE.Light.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Light.prototype.constructor = THREE.Light; - } +THREE.Light.prototype.copy = function ( source ) { + + THREE.Object3D.prototype.copy.call( this, source ); + + this.color.copy( source.color ); + + return this; - }; +}; +// File:src/lights/AmbientLight.js - if ( json.materials === undefined || json.materials.length === 0 ) { +/** + * @author mrdoob / http://mrdoob.com/ + */ - return { geometry: geometry }; +THREE.AmbientLight = function ( color ) { - } else { + THREE.Light.call( this, color ); - var materials = THREE.Loader.prototype.initMaterials( json.materials, texturePath, this.crossOrigin ); + this.type = 'AmbientLight'; - return { geometry: geometry, materials: materials }; +}; - } +THREE.AmbientLight.prototype = Object.create( THREE.Light.prototype ); +THREE.AmbientLight.prototype.constructor = THREE.AmbientLight; - } +THREE.AmbientLight.prototype.toJSON = function ( meta ) { + + var data = THREE.Object3D.prototype.toJSON.call( this, meta ); + + data.object.color = this.color.getHex(); + + return data; }; -// File:src/loaders/LoadingManager.js +// File:src/lights/DirectionalLight.js /** * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ */ -THREE.LoadingManager = function ( onLoad, onProgress, onError ) { +THREE.DirectionalLight = function ( color, intensity ) { - var scope = this; + THREE.Light.call( this, color ); - var isLoading = false, itemsLoaded = 0, itemsTotal = 0; + this.type = 'DirectionalLight'; - this.onStart = undefined; - this.onLoad = onLoad; - this.onProgress = onProgress; - this.onError = onError; + this.position.set( 0, 1, 0 ); + this.updateMatrix(); - this.itemStart = function ( url ) { + this.target = new THREE.Object3D(); - itemsTotal ++; + this.intensity = ( intensity !== undefined ) ? intensity : 1; - if ( isLoading === false ) { + this.castShadow = false; + this.onlyShadow = false; - if ( scope.onStart !== undefined ) { + this.shadowCameraNear = 50; + this.shadowCameraFar = 5000; - scope.onStart( url, itemsLoaded, itemsTotal ); + this.shadowCameraLeft = - 500; + this.shadowCameraRight = 500; + this.shadowCameraTop = 500; + this.shadowCameraBottom = - 500; - } + this.shadowCameraVisible = false; - } + this.shadowBias = 0; + this.shadowDarkness = 0.5; - isLoading = true; + this.shadowMapWidth = 512; + this.shadowMapHeight = 512; - }; + this.shadowMap = null; + this.shadowMapSize = null; + this.shadowCamera = null; + this.shadowMatrix = null; - this.itemEnd = function ( url ) { +}; - itemsLoaded ++; +THREE.DirectionalLight.prototype = Object.create( THREE.Light.prototype ); +THREE.DirectionalLight.prototype.constructor = THREE.DirectionalLight; - if ( scope.onProgress !== undefined ) { +THREE.DirectionalLight.prototype.copy = function ( source ) { - scope.onProgress( url, itemsLoaded, itemsTotal ); + THREE.Light.prototype.copy.call( this, source ); - } + this.intensity = source.intensity; + this.target = source.target.clone(); - if ( itemsLoaded === itemsTotal ) { + this.castShadow = source.castShadow; + this.onlyShadow = source.onlyShadow; - isLoading = false; + this.shadowCameraNear = source.shadowCameraNear; + this.shadowCameraFar = source.shadowCameraFar; - if ( scope.onLoad !== undefined ) { + this.shadowCameraLeft = source.shadowCameraLeft; + this.shadowCameraRight = source.shadowCameraRight; + this.shadowCameraTop = source.shadowCameraTop; + this.shadowCameraBottom = source.shadowCameraBottom; - scope.onLoad(); + this.shadowCameraVisible = source.shadowCameraVisible; - } + this.shadowBias = source.shadowBias; + this.shadowDarkness = source.shadowDarkness; - } + this.shadowMapWidth = source.shadowMapWidth; + this.shadowMapHeight = source.shadowMapHeight; - }; + return this; - this.itemError = function ( url ) { +}; - if ( scope.onError !== undefined ) { +THREE.DirectionalLight.prototype.toJSON = function ( meta ) { - scope.onError( url ); + var data = THREE.Object3D.prototype.toJSON.call( this, meta ); - } + data.object.color = this.color.getHex(); + data.object.intensity = this.intensity; - }; + return data; }; -THREE.DefaultLoadingManager = new THREE.LoadingManager(); - -// File:src/loaders/BufferGeometryLoader.js +// File:src/lights/HemisphereLight.js /** - * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ */ -THREE.BufferGeometryLoader = function ( manager ) { +THREE.HemisphereLight = function ( skyColor, groundColor, intensity ) { - this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + THREE.Light.call( this, skyColor ); + + this.type = 'HemisphereLight'; + + this.position.set( 0, 1, 0 ); + this.updateMatrix(); + + this.groundColor = new THREE.Color( groundColor ); + this.intensity = ( intensity !== undefined ) ? intensity : 1; }; -THREE.BufferGeometryLoader.prototype = { +THREE.HemisphereLight.prototype = Object.create( THREE.Light.prototype ); +THREE.HemisphereLight.prototype.constructor = THREE.HemisphereLight; - constructor: THREE.BufferGeometryLoader, +THREE.HemisphereLight.prototype.copy = function ( source ) { - load: function ( url, onLoad, onProgress, onError ) { + THREE.Light.prototype.copy.call( this, source ); - var scope = this; + this.groundColor.copy( source.groundColor ); + this.intensity = source.intensity; - var loader = new THREE.XHRLoader( scope.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.load( url, function ( text ) { + return this; - onLoad( scope.parse( JSON.parse( text ) ) ); +}; - }, onProgress, onError ); +THREE.HemisphereLight.prototype.toJSON = function ( meta ) { - }, + var data = THREE.Object3D.prototype.toJSON.call( this, meta ); - setCrossOrigin: function ( value ) { + data.object.color = this.color.getHex(); + data.object.groundColor = this.groundColor.getHex(); + data.object.intensity = this.intensity; - this.crossOrigin = value; + return data; - }, +}; - parse: function ( json ) { +// File:src/lights/PointLight.js - var geometry = new THREE.BufferGeometry(); +/** + * @author mrdoob / http://mrdoob.com/ + */ - var index = json.data.index; +THREE.PointLight = function ( color, intensity, distance, decay ) { - if ( index !== undefined ) { + THREE.Light.call( this, color ); - var typedArray = new self[ index.type ]( index.array ); - geometry.setIndex( new THREE.BufferAttribute( typedArray, 1 ) ); + this.type = 'PointLight'; - } + this.intensity = ( intensity !== undefined ) ? intensity : 1; + this.distance = ( distance !== undefined ) ? distance : 0; + this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. - var attributes = json.data.attributes; +}; - for ( var key in attributes ) { +THREE.PointLight.prototype = Object.create( THREE.Light.prototype ); +THREE.PointLight.prototype.constructor = THREE.PointLight; - var attribute = attributes[ key ]; - var typedArray = new self[ attribute.type ]( attribute.array ); +THREE.PointLight.prototype.copy = function ( source ) { - geometry.addAttribute( key, new THREE.BufferAttribute( typedArray, attribute.itemSize ) ); + THREE.Light.prototype.copy.call( this, source ); - } + this.intensity = source.intensity; + this.distance = source.distance; + this.decay = source.decay; - var groups = json.data.groups || json.data.drawcalls || json.data.offsets; + return this; - if ( groups !== undefined ) { +}; - for ( var i = 0, n = groups.length; i !== n; ++ i ) { +THREE.PointLight.prototype.toJSON = function ( meta ) { - var group = groups[ i ]; + var data = THREE.Object3D.prototype.toJSON.call( this, meta ); - geometry.addGroup( group.start, group.count ); + data.object.color = this.color.getHex(); + data.object.intensity = this.intensity; + data.object.distance = this.distance; + data.object.decay = this.decay; - } + return data; - } +}; - var boundingSphere = json.data.boundingSphere; +// File:src/lights/SpotLight.js - if ( boundingSphere !== undefined ) { +/** + * @author alteredq / http://alteredqualia.com/ + */ - var center = new THREE.Vector3(); +THREE.SpotLight = function ( color, intensity, distance, angle, exponent, decay ) { - if ( boundingSphere.center !== undefined ) { + THREE.Light.call( this, color ); - center.fromArray( boundingSphere.center ); + this.type = 'SpotLight'; - } + this.position.set( 0, 1, 0 ); + this.updateMatrix(); - geometry.boundingSphere = new THREE.Sphere( center, boundingSphere.radius ); + this.target = new THREE.Object3D(); - } + this.intensity = ( intensity !== undefined ) ? intensity : 1; + this.distance = ( distance !== undefined ) ? distance : 0; + this.angle = ( angle !== undefined ) ? angle : Math.PI / 3; + this.exponent = ( exponent !== undefined ) ? exponent : 10; + this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. - return geometry; + this.castShadow = false; + this.onlyShadow = false; - } + this.shadowCameraNear = 50; + this.shadowCameraFar = 5000; + this.shadowCameraFov = 50; + + this.shadowCameraVisible = false; + + this.shadowBias = 0; + this.shadowDarkness = 0.5; + + this.shadowMapWidth = 512; + this.shadowMapHeight = 512; + + this.shadowMap = null; + this.shadowMapSize = null; + this.shadowCamera = null; + this.shadowMatrix = null; }; -// File:src/loaders/MaterialLoader.js +THREE.SpotLight.prototype = Object.create( THREE.Light.prototype ); +THREE.SpotLight.prototype.constructor = THREE.SpotLight; -/** - * @author mrdoob / http://mrdoob.com/ - */ +THREE.SpotLight.prototype.copy = function ( source ) { -THREE.MaterialLoader = function ( manager ) { + THREE.Light.prototype.copy.call( this, source ); - this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; - this.textures = {}; + this.intensity = source.intensity; + this.distance = source.distance; + this.angle = source.angle; + this.exponent = source.exponent; + this.decay = source.decay; -}; + this.target = source.target.clone(); -THREE.MaterialLoader.prototype = { + this.castShadow = source.castShadow; + this.onlyShadow = source.onlyShadow; - constructor: THREE.MaterialLoader, + this.shadowCameraNear = source.shadowCameraNear; + this.shadowCameraFar = source.shadowCameraFar; + this.shadowCameraFov = source.shadowCameraFov; - load: function ( url, onLoad, onProgress, onError ) { + this.shadowCameraVisible = source.shadowCameraVisible; - var scope = this; + this.shadowBias = source.shadowBias; + this.shadowDarkness = source.shadowDarkness; - var loader = new THREE.XHRLoader( scope.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.load( url, function ( text ) { + this.shadowMapWidth = source.shadowMapWidth; + this.shadowMapHeight = source.shadowMapHeight; - onLoad( scope.parse( JSON.parse( text ) ) ); + return this; +} - }, onProgress, onError ); +THREE.SpotLight.prototype.toJSON = function ( meta ) { - }, + var data = THREE.Object3D.prototype.toJSON.call( this, meta ); - setCrossOrigin: function ( value ) { + data.object.color = this.color.getHex(); + data.object.intensity = this.intensity; + data.object.distance = this.distance; + data.object.angle = this.angle; + data.object.exponent = this.exponent; + data.object.decay = this.decay; - this.crossOrigin = value; + return data; - }, +}; - setTextures: function ( value ) { +// File:src/loaders/Cache.js - this.textures = value; +/** + * @author mrdoob / http://mrdoob.com/ + */ - }, +THREE.Cache = { - getTexture: function ( name ) { + enabled: false, - var textures = this.textures; + files: {}, - if ( textures[ name ] === undefined ) { + add: function ( key, file ) { - console.warn( 'THREE.MaterialLoader: Undefined texture', name ); + if ( this.enabled === false ) return; - } + // console.log( 'THREE.Cache', 'Adding key:', key ); - return textures[ name ]; + this.files[ key ] = file; }, - parse: function ( json ) { + get: function ( key ) { - var material = new THREE[ json.type ]; - material.uuid = json.uuid; + if ( this.enabled === false ) return; - if ( json.name !== undefined ) material.name = json.name; - if ( json.color !== undefined ) material.color.setHex( json.color ); - if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive ); - if ( json.specular !== undefined ) material.specular.setHex( json.specular ); - if ( json.shininess !== undefined ) material.shininess = json.shininess; - if ( json.uniforms !== undefined ) material.uniforms = json.uniforms; - if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader; - if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader; - if ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors; - if ( json.shading !== undefined ) material.shading = json.shading; - if ( json.blending !== undefined ) material.blending = json.blending; - if ( json.side !== undefined ) material.side = json.side; - if ( json.opacity !== undefined ) material.opacity = json.opacity; - if ( json.transparent !== undefined ) material.transparent = json.transparent; - if ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest; - if ( json.depthTest !== undefined ) material.depthTest = json.depthTest; - if ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite; - if ( json.wireframe !== undefined ) material.wireframe = json.wireframe; - if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth; - - // for PointsMaterial - if ( json.size !== undefined ) material.size = json.size; - if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation; + // console.log( 'THREE.Cache', 'Checking key:', key ); - // maps + return this.files[ key ]; - if ( json.map !== undefined ) material.map = this.getTexture( json.map ); + }, - if ( json.alphaMap !== undefined ) { + remove: function ( key ) { - material.alphaMap = this.getTexture( json.alphaMap ); - material.transparent = true; + delete this.files[ key ]; - } + }, - if ( json.bumpMap !== undefined ) material.bumpMap = this.getTexture( json.bumpMap ); - if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale; + clear: function () { - if ( json.normalMap !== undefined ) material.normalMap = this.getTexture( json.normalMap ); - if ( json.normalScale ) material.normalScale = new THREE.Vector2( json.normalScale, json.normalScale ); + this.files = {}; - if ( json.displacementMap !== undefined ) material.displacementMap = this.getTexture( json.displacementMap ); - if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale; - if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias; + } - if ( json.specularMap !== undefined ) material.specularMap = this.getTexture( json.specularMap ); +}; - if ( json.envMap !== undefined ) { +// File:src/loaders/Loader.js - material.envMap = this.getTexture( json.envMap ); - material.combine = THREE.MultiplyOperation; +/** + * @author alteredq / http://alteredqualia.com/ + */ - } +THREE.Loader = function () { - if ( json.reflectivity ) material.reflectivity = json.reflectivity; + this.onLoadStart = function () {}; + this.onLoadProgress = function () {}; + this.onLoadComplete = function () {}; - if ( json.lightMap !== undefined ) material.lightMap = this.getTexture( json.lightMap ); - if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity; +}; - if ( json.aoMap !== undefined ) material.aoMap = this.getTexture( json.aoMap ); - if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity; +THREE.Loader.prototype = { - // MeshFaceMaterial + constructor: THREE.Loader, - if ( json.materials !== undefined ) { + crossOrigin: undefined, - for ( var i = 0, l = json.materials.length; i < l; i ++ ) { + extractUrlBase: function ( url ) { - material.materials.push( this.parse( json.materials[ i ] ) ); + var parts = url.split( '/' ); - } + if ( parts.length === 1 ) return './'; - } + parts.pop(); - return material; + return parts.join( '/' ) + '/'; - } + }, -}; + initMaterials: function ( materials, texturePath, crossOrigin ) { -// File:src/loaders/ObjectLoader.js + var array = []; -/** - * @author mrdoob / http://mrdoob.com/ - */ + for ( var i = 0; i < materials.length; ++ i ) { -THREE.ObjectLoader = function ( manager ) { + array[ i ] = this.createMaterial( materials[ i ], texturePath, crossOrigin ); - this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; - this.texturePath = ''; + } -}; + return array; -THREE.ObjectLoader.prototype = { + }, - constructor: THREE.ObjectLoader, + createMaterial: ( function () { - load: function ( url, onLoad, onProgress, onError ) { + var imageLoader; - if ( this.texturePath === '' ) { + return function createMaterial( m, texturePath, crossOrigin ) { - this.texturePath = url.substring( 0, url.lastIndexOf( '/' ) + 1 ); + var scope = this; - } + if ( crossOrigin === undefined && scope.crossOrigin !== undefined ) crossOrigin = scope.crossOrigin; - var scope = this; + if ( imageLoader === undefined ) imageLoader = new THREE.ImageLoader(); - var loader = new THREE.XHRLoader( scope.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.load( url, function ( text ) { + function nearest_pow2( n ) { - scope.parse( JSON.parse( text ), onLoad ); + var l = Math.log( n ) / Math.LN2; + return Math.pow( 2, Math.round( l ) ); - }, onProgress, onError ); + } - }, + function create_texture( where, name, sourceFile, repeat, offset, wrap, anisotropy ) { - setTexturePath: function ( value ) { + var fullPath = texturePath + sourceFile; - this.texturePath = value; + var texture; - }, + var loader = THREE.Loader.Handlers.get( fullPath ); - setCrossOrigin: function ( value ) { + if ( loader !== null ) { - this.crossOrigin = value; + texture = loader.load( fullPath ); - }, + } else { - parse: function ( json, onLoad ) { + texture = new THREE.Texture(); - var geometries = this.parseGeometries( json.geometries ); + loader = imageLoader; + loader.setCrossOrigin( crossOrigin ); + loader.load( fullPath, function ( image ) { - var images = this.parseImages( json.images, function () { + if ( THREE.Math.isPowerOfTwo( image.width ) === false || + THREE.Math.isPowerOfTwo( image.height ) === false ) { - if ( onLoad !== undefined ) onLoad( object ); + var width = nearest_pow2( image.width ); + var height = nearest_pow2( image.height ); - } ); + var canvas = document.createElement( 'canvas' ); + canvas.width = width; + canvas.height = height; - var textures = this.parseTextures( json.textures, images ); - var materials = this.parseMaterials( json.materials, textures ); - var object = this.parseObject( json.object, geometries, materials ); + var context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0, width, height ); - if ( json.images === undefined || json.images.length === 0 ) { + texture.image = canvas; - if ( onLoad !== undefined ) onLoad( object ); + } else { - } + texture.image = image; - return object; + } - }, + texture.needsUpdate = true; - parseGeometries: function ( json ) { + } ); - var geometries = {}; + } - if ( json !== undefined ) { + texture.sourceFile = sourceFile; - var geometryLoader = new THREE.JSONLoader(); - var bufferGeometryLoader = new THREE.BufferGeometryLoader(); + if ( repeat ) { - for ( var i = 0, l = json.length; i < l; i ++ ) { + texture.repeat.set( repeat[ 0 ], repeat[ 1 ] ); - var geometry; - var data = json[ i ]; + if ( repeat[ 0 ] !== 1 ) texture.wrapS = THREE.RepeatWrapping; + if ( repeat[ 1 ] !== 1 ) texture.wrapT = THREE.RepeatWrapping; - switch ( data.type ) { + } - case 'PlaneGeometry': - case 'PlaneBufferGeometry': + if ( offset ) { - geometry = new THREE[ data.type ]( - data.width, - data.height, - data.widthSegments, - data.heightSegments - ); + texture.offset.set( offset[ 0 ], offset[ 1 ] ); - break; + } - case 'BoxGeometry': - case 'CubeGeometry': // backwards compatible + if ( wrap ) { - geometry = new THREE.BoxGeometry( - data.width, - data.height, - data.depth, - data.widthSegments, - data.heightSegments, - data.depthSegments - ); + var wrapMap = { + 'repeat': THREE.RepeatWrapping, + 'mirror': THREE.MirroredRepeatWrapping + }; - break; + if ( wrapMap[ wrap[ 0 ] ] !== undefined ) texture.wrapS = wrapMap[ wrap[ 0 ] ]; + if ( wrapMap[ wrap[ 1 ] ] !== undefined ) texture.wrapT = wrapMap[ wrap[ 1 ] ]; - case 'CircleBufferGeometry': + } - geometry = new THREE.CircleBufferGeometry( - data.radius, - data.segments, - data.thetaStart, - data.thetaLength - ); + if ( anisotropy ) { - break; + texture.anisotropy = anisotropy; - case 'CircleGeometry': + } - geometry = new THREE.CircleGeometry( - data.radius, - data.segments, - data.thetaStart, - data.thetaLength - ); + where[ name ] = texture; - break; + } - case 'CylinderGeometry': + function rgb2hex( rgb ) { - geometry = new THREE.CylinderGeometry( - data.radiusTop, - data.radiusBottom, - data.height, - data.radialSegments, - data.heightSegments, - data.openEnded, - data.thetaStart, - data.thetaLength - ); + return ( rgb[ 0 ] * 255 << 16 ) + ( rgb[ 1 ] * 255 << 8 ) + rgb[ 2 ] * 255; - break; + } - case 'SphereGeometry': + // defaults - geometry = new THREE.SphereGeometry( - data.radius, - data.widthSegments, - data.heightSegments, - data.phiStart, - data.phiLength, - data.thetaStart, - data.thetaLength - ); + var mtype = 'MeshLambertMaterial'; + var mpars = {}; - break; + // parameters from model file - case 'SphereBufferGeometry': + if ( m.shading ) { - geometry = new THREE.SphereBufferGeometry( - data.radius, - data.widthSegments, - data.heightSegments, - data.phiStart, - data.phiLength, - data.thetaStart, - data.thetaLength - ); + var shading = m.shading.toLowerCase(); - break; + if ( shading === 'phong' ) mtype = 'MeshPhongMaterial'; + else if ( shading === 'basic' ) mtype = 'MeshBasicMaterial'; - case 'DodecahedronGeometry': + } - geometry = new THREE.DodecahedronGeometry( - data.radius, - data.detail - ); + if ( m.blending !== undefined && THREE[ m.blending ] !== undefined ) { - break; + mpars.blending = THREE[ m.blending ]; - case 'IcosahedronGeometry': + } - geometry = new THREE.IcosahedronGeometry( - data.radius, - data.detail - ); + if ( m.transparent !== undefined ) { - break; + mpars.transparent = m.transparent; - case 'OctahedronGeometry': + } - geometry = new THREE.OctahedronGeometry( - data.radius, - data.detail - ); + if ( m.opacity !== undefined && m.opacity < 1.0 ) { - break; + mpars.transparent = true; - case 'TetrahedronGeometry': + } - geometry = new THREE.TetrahedronGeometry( - data.radius, - data.detail - ); + if ( m.depthTest !== undefined ) { - break; + mpars.depthTest = m.depthTest; - case 'RingGeometry': + } - geometry = new THREE.RingGeometry( - data.innerRadius, - data.outerRadius, - data.thetaSegments, - data.phiSegments, - data.thetaStart, - data.thetaLength - ); + if ( m.depthWrite !== undefined ) { - break; + mpars.depthWrite = m.depthWrite; - case 'TorusGeometry': + } - geometry = new THREE.TorusGeometry( - data.radius, - data.tube, - data.radialSegments, - data.tubularSegments, - data.arc - ); + if ( m.visible !== undefined ) { - break; + mpars.visible = m.visible; - case 'TorusKnotGeometry': + } - geometry = new THREE.TorusKnotGeometry( - data.radius, - data.tube, - data.radialSegments, - data.tubularSegments, - data.p, - data.q, - data.heightScale - ); + if ( m.flipSided !== undefined ) { - break; + mpars.side = THREE.BackSide; - case 'TextGeometry': + } - geometry = new THREE.TextGeometry( - data.text, - data.data - ); + if ( m.doubleSided !== undefined ) { - break; + mpars.side = THREE.DoubleSide; - case 'BufferGeometry': + } - geometry = bufferGeometryLoader.parse( data ); + if ( m.wireframe !== undefined ) { - break; + mpars.wireframe = m.wireframe; - case 'Geometry': + } - geometry = geometryLoader.parse( data.data, this.texturePath ).geometry; + if ( m.vertexColors !== undefined ) { - break; + if ( m.vertexColors === 'face' ) { - default: + mpars.vertexColors = THREE.FaceColors; - console.warn( 'THREE.ObjectLoader: Unsupported geometry type "' + data.type + '"' ); + } else if ( m.vertexColors ) { - continue; + mpars.vertexColors = THREE.VertexColors; } - geometry.uuid = data.uuid; - - if ( data.name !== undefined ) geometry.name = data.name; - - geometries[ data.uuid ] = geometry; - } - } - - return geometries; + // colors - }, + if ( m.colorDiffuse ) { - parseMaterials: function ( json, textures ) { + mpars.color = rgb2hex( m.colorDiffuse ); - var materials = {}; + } else if ( m.DbgColor ) { - if ( json !== undefined ) { + mpars.color = m.DbgColor; - var loader = new THREE.MaterialLoader(); - loader.setTextures( textures ); + } - for ( var i = 0, l = json.length; i < l; i ++ ) { + if ( m.colorEmissive ) { - var material = loader.parse( json[ i ] ); - materials[ material.uuid ] = material; + mpars.emissive = rgb2hex( m.colorEmissive ); } - } + if ( mtype === 'MeshPhongMaterial' ) { - return materials; + if ( m.colorSpecular ) { - }, + mpars.specular = rgb2hex( m.colorSpecular ); - parseImages: function ( json, onLoad ) { + } - var scope = this; - var images = {}; + if ( m.specularCoef ) { - function loadImage( url ) { + mpars.shininess = m.specularCoef; - scope.manager.itemStart( url ); + } - return loader.load( url, function () { + } - scope.manager.itemEnd( url ); + // modifiers - } ); + if ( m.transparency !== undefined ) { - } + console.warn( 'THREE.Loader: transparency has been renamed to opacity' ); + m.opacity = m.transparency; - if ( json !== undefined && json.length > 0 ) { + } - var manager = new THREE.LoadingManager( onLoad ); + if ( m.opacity !== undefined ) { - var loader = new THREE.ImageLoader( manager ); - loader.setCrossOrigin( this.crossOrigin ); + mpars.opacity = m.opacity; - for ( var i = 0, l = json.length; i < l; i ++ ) { + } - var image = json[ i ]; - var path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( image.url ) ? image.url : scope.texturePath + image.url; + // textures - images[ image.uuid ] = loadImage( path ); + if ( texturePath ) { - } + if ( m.mapDiffuse ) { - } + create_texture( mpars, 'map', m.mapDiffuse, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy ); - return images; + } - }, + if ( m.mapLight ) { - parseTextures: function ( json, images ) { + create_texture( mpars, 'lightMap', m.mapLight, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy ); - function parseConstant( value ) { + } - if ( typeof( value ) === 'number' ) return value; + if ( m.mapAO ) { - console.warn( 'THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value ); + create_texture( mpars, 'aoMap', m.mapAO, m.mapAORepeat, m.mapAOOffset, m.mapAOWrap, m.mapAOAnisotropy ); - return THREE[ value ]; + } - } + if ( m.mapBump ) { - var textures = {}; + create_texture( mpars, 'bumpMap', m.mapBump, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy ); - if ( json !== undefined ) { + } - for ( var i = 0, l = json.length; i < l; i ++ ) { + if ( m.mapNormal ) { - var data = json[ i ]; + create_texture( mpars, 'normalMap', m.mapNormal, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy ); - if ( data.image === undefined ) { + } - console.warn( 'THREE.ObjectLoader: No "image" specified for', data.uuid ); + if ( m.mapSpecular ) { + + create_texture( mpars, 'specularMap', m.mapSpecular, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy ); } - if ( images[ data.image ] === undefined ) { + if ( m.mapAlpha ) { - console.warn( 'THREE.ObjectLoader: Undefined image', data.image ); + create_texture( mpars, 'alphaMap', m.mapAlpha, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy ); } - var texture = new THREE.Texture( images[ data.image ] ); - texture.needsUpdate = true; + } - texture.uuid = data.uuid; + // - if ( data.name !== undefined ) texture.name = data.name; - if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping ); - if ( data.offset !== undefined ) texture.offset = new THREE.Vector2( data.offset[ 0 ], data.offset[ 1 ] ); - if ( data.repeat !== undefined ) texture.repeat = new THREE.Vector2( data.repeat[ 0 ], data.repeat[ 1 ] ); - if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter ); - if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter ); - if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy; - if ( Array.isArray( data.wrap ) ) { + if ( m.mapBumpScale ) { - texture.wrapS = parseConstant( data.wrap[ 0 ] ); - texture.wrapT = parseConstant( data.wrap[ 1 ] ); + mpars.bumpScale = m.mapBumpScale; - } + } - textures[ data.uuid ] = texture; + if ( m.mapNormalFactor ) { + + mpars.normalScale = new THREE.Vector2( m.mapNormalFactor, m.mapNormalFactor ); } - } + var material = new THREE[ mtype ]( mpars ); - return textures; + if ( m.DbgName !== undefined ) material.name = m.DbgName; - }, + return material; - parseObject: function () { - - var matrix = new THREE.Matrix4(); - - return function ( data, geometries, materials ) { + }; - var object; + } )() - var getGeometry = function ( name ) { +}; - if ( geometries[ name ] === undefined ) { +THREE.Loader.Handlers = { - console.warn( 'THREE.ObjectLoader: Undefined geometry', name ); + handlers: [], - } + add: function ( regex, loader ) { - return geometries[ name ]; + this.handlers.push( regex, loader ); - }; + }, - var getMaterial = function ( name ) { + get: function ( file ) { - if ( materials[ name ] === undefined ) { + for ( var i = 0, l = this.handlers.length; i < l; i += 2 ) { - console.warn( 'THREE.ObjectLoader: Undefined material', name ); + var regex = this.handlers[ i ]; + var loader = this.handlers[ i + 1 ]; - } + if ( regex.test( file ) ) { - return materials[ name ]; + return loader; - }; + } - switch ( data.type ) { + } - case 'Scene': + return null; - object = new THREE.Scene(); + } - break; +}; - case 'PerspectiveCamera': +// File:src/loaders/XHRLoader.js - object = new THREE.PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); +/** + * @author mrdoob / http://mrdoob.com/ + */ - break; +THREE.XHRLoader = function ( manager ) { - case 'OrthographicCamera': + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; - object = new THREE.OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); +}; - break; +THREE.XHRLoader.prototype = { - case 'AmbientLight': + constructor: THREE.XHRLoader, - object = new THREE.AmbientLight( data.color ); + load: function ( url, onLoad, onProgress, onError ) { - break; + var scope = this; - case 'DirectionalLight': + var cached = THREE.Cache.get( url ); - object = new THREE.DirectionalLight( data.color, data.intensity ); + if ( cached !== undefined ) { - break; + if ( onLoad ) { - case 'PointLight': + setTimeout( function () { - object = new THREE.PointLight( data.color, data.intensity, data.distance, data.decay ); + onLoad( cached ); - break; + }, 0 ); - case 'SpotLight': + } - object = new THREE.SpotLight( data.color, data.intensity, data.distance, data.angle, data.exponent, data.decay ); + return cached; - break; + } - case 'HemisphereLight': + var request = new XMLHttpRequest(); + request.open( 'GET', url, true ); - object = new THREE.HemisphereLight( data.color, data.groundColor, data.intensity ); + request.addEventListener( 'load', function ( event ) { - break; + THREE.Cache.add( url, this.response ); - case 'Mesh': + if ( onLoad ) onLoad( this.response ); - object = new THREE.Mesh( getGeometry( data.geometry ), getMaterial( data.material ) ); + scope.manager.itemEnd( url ); - break; + }, false ); - case 'LOD': + if ( onProgress !== undefined ) { - object = new THREE.LOD(); + request.addEventListener( 'progress', function ( event ) { - break; + onProgress( event ); - case 'Line': + }, false ); - object = new THREE.Line( getGeometry( data.geometry ), getMaterial( data.material ), data.mode ); + } - break; + request.addEventListener( 'error', function ( event ) { - case 'PointCloud': - case 'Points': + if ( onError ) onError( event ); - object = new THREE.Points( getGeometry( data.geometry ), getMaterial( data.material ) ); + scope.manager.itemError( url ); - break; + }, false ); - case 'Sprite': + if ( this.crossOrigin !== undefined ) request.crossOrigin = this.crossOrigin; + if ( this.responseType !== undefined ) request.responseType = this.responseType; + if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials; - object = new THREE.Sprite( getMaterial( data.material ) ); + request.send( null ); - break; + scope.manager.itemStart( url ); - case 'Group': + return request; - object = new THREE.Group(); + }, - break; + setResponseType: function ( value ) { - default: + this.responseType = value; - object = new THREE.Object3D(); + }, - } + setCrossOrigin: function ( value ) { - object.uuid = data.uuid; + this.crossOrigin = value; - if ( data.name !== undefined ) object.name = data.name; - if ( data.matrix !== undefined ) { + }, - matrix.fromArray( data.matrix ); - matrix.decompose( object.position, object.quaternion, object.scale ); + setWithCredentials: function ( value ) { - } else { + this.withCredentials = value; - if ( data.position !== undefined ) object.position.fromArray( data.position ); - if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation ); - if ( data.scale !== undefined ) object.scale.fromArray( data.scale ); + } - } +}; - if ( data.castShadow !== undefined ) object.castShadow = data.castShadow; - if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow; +// File:src/loaders/ImageLoader.js - if ( data.visible !== undefined ) object.visible = data.visible; - if ( data.userData !== undefined ) object.userData = data.userData; +/** + * @author mrdoob / http://mrdoob.com/ + */ - if ( data.children !== undefined ) { +THREE.ImageLoader = function ( manager ) { - for ( var child in data.children ) { + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; - object.add( this.parseObject( data.children[ child ], geometries, materials ) ); +}; - } +THREE.ImageLoader.prototype = { - } + constructor: THREE.ImageLoader, - if ( data.type === 'LOD' ) { + load: function ( url, onLoad, onProgress, onError ) { - var levels = data.levels; + var scope = this; - for ( var l = 0; l < levels.length; l ++ ) { + var cached = THREE.Cache.get( url ); - var level = levels[ l ]; - var child = object.getObjectByProperty( 'uuid', level.object ); + if ( cached !== undefined ) { - if ( child !== undefined ) { + if ( onLoad ) { - object.addLevel( child, level.distance ); + setTimeout( function () { - } + onLoad( cached ); - } + }, 0 ); } - return object; + return cached; } - }() + var image = document.createElement( 'img' ); -}; + image.addEventListener( 'load', function ( event ) { -// File:src/loaders/TextureLoader.js + THREE.Cache.add( url, this ); -/** - * @author mrdoob / http://mrdoob.com/ - */ + if ( onLoad ) onLoad( this ); -THREE.TextureLoader = function ( manager ) { + scope.manager.itemEnd( url ); - this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + }, false ); -}; + if ( onProgress !== undefined ) { -THREE.TextureLoader.prototype = { + image.addEventListener( 'progress', function ( event ) { - constructor: THREE.TextureLoader, + onProgress( event ); - load: function ( url, onLoad, onProgress, onError ) { + }, false ); - var scope = this; + } - var loader = new THREE.ImageLoader( scope.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.load( url, function ( image ) { + image.addEventListener( 'error', function ( event ) { - var texture = new THREE.Texture( image ); - texture.needsUpdate = true; + if ( onError ) onError( event ); - if ( onLoad !== undefined ) { + scope.manager.itemError( url ); - onLoad( texture ); + }, false ); - } + if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; - }, onProgress, onError ); + scope.manager.itemStart( url ); + + image.src = url; + + return image; }, @@ -14562,3442 +14963,3607 @@ THREE.TextureLoader.prototype = { }; -// File:src/loaders/BinaryTextureLoader.js +// File:src/loaders/JSONLoader.js /** - * @author Nikos M. / https://github.com/foo123/ - * - * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ */ -THREE.DataTextureLoader = THREE.BinaryTextureLoader = function ( manager ) { +THREE.JSONLoader = function ( manager ) { + + if ( typeof manager === 'boolean' ) { + + console.warn( 'THREE.JSONLoader: showStatus parameter has been removed from constructor.' ); + manager = undefined; + + } this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; - // override in sub classes - this._parser = null; + this.withCredentials = false; }; -THREE.BinaryTextureLoader.prototype = { +THREE.JSONLoader.prototype = { - constructor: THREE.BinaryTextureLoader, + constructor: THREE.JSONLoader, - load: function ( url, onLoad, onProgress, onError ) { + // Deprecated + + get statusDomElement () { + + if ( this._statusDomElement === undefined ) { + + this._statusDomElement = document.createElement( 'div' ); + + } + + console.warn( 'THREE.JSONLoader: .statusDomElement has been removed.' ); + return this._statusDomElement; + + }, + + load: function( url, onLoad, onProgress, onError ) { var scope = this; - var texture = new THREE.DataTexture(); + var texturePath = this.texturePath && ( typeof this.texturePath === "string" ) ? this.texturePath : THREE.Loader.prototype.extractUrlBase( url ); var loader = new THREE.XHRLoader( this.manager ); loader.setCrossOrigin( this.crossOrigin ); - loader.setResponseType( 'arraybuffer' ); + loader.setWithCredentials( this.withCredentials ); + loader.load( url, function ( text ) { - loader.load( url, function ( buffer ) { + var json = JSON.parse( text ); + var metadata = json.metadata; - var texData = scope._parser( buffer ); + if ( metadata !== undefined ) { - if ( ! texData ) return; + if ( metadata.type === 'object' ) { - if ( undefined !== texData.image ) { + console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.ObjectLoader instead.' ); + return; - texture.image = texData.image; + } - } else if ( undefined !== texData.data ) { + if ( metadata.type === 'scene' ) { - texture.image.width = texData.width; - texture.image.height = texData.height; - texture.image.data = texData.data; + console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.SceneLoader instead.' ); + return; + + } } - texture.wrapS = undefined !== texData.wrapS ? texData.wrapS : THREE.ClampToEdgeWrapping; - texture.wrapT = undefined !== texData.wrapT ? texData.wrapT : THREE.ClampToEdgeWrapping; + var object = scope.parse( json, texturePath ); + onLoad( object.geometry, object.materials ); - texture.magFilter = undefined !== texData.magFilter ? texData.magFilter : THREE.LinearFilter; - texture.minFilter = undefined !== texData.minFilter ? texData.minFilter : THREE.LinearMipMapLinearFilter; + } ); - texture.anisotropy = undefined !== texData.anisotropy ? texData.anisotropy : 1; + }, - if ( undefined !== texData.format ) { + setCrossOrigin: function ( value ) { - texture.format = texData.format; + this.crossOrigin = value; - } - if ( undefined !== texData.type ) { + }, - texture.type = texData.type; + setTexturePath: function ( value ) { - } + this.texturePath = value; - if ( undefined !== texData.mipmaps ) { + }, - texture.mipmaps = texData.mipmaps; + parse: function ( json, texturePath ) { - } + var geometry = new THREE.Geometry(), + scale = ( json.scale !== undefined ) ? 1.0 / json.scale : 1.0; - if ( 1 === texData.mipmapCount ) { + parseModel( scale ); - texture.minFilter = THREE.LinearFilter; + parseSkin(); + parseMorphing( scale ); + parseAnimations(); - } + geometry.computeFaceNormals(); + geometry.computeBoundingSphere(); - texture.needsUpdate = true; + function parseModel( scale ) { - if ( onLoad ) onLoad( texture, texData ); + function isBitSet( value, position ) { - }, onProgress, onError ); + return value & ( 1 << position ); + } - return texture; + var i, j, fi, - }, + offset, zLength, - setCrossOrigin: function ( value ) { + colorIndex, normalIndex, uvIndex, materialIndex, - this.crossOrigin = value; + type, + isQuad, + hasMaterial, + hasFaceVertexUv, + hasFaceNormal, hasFaceVertexNormal, + hasFaceColor, hasFaceVertexColor, - } + vertex, face, faceA, faceB, hex, normal, -}; + uvLayer, uv, u, v, -// File:src/loaders/CompressedTextureLoader.js + faces = json.faces, + vertices = json.vertices, + normals = json.normals, + colors = json.colors, -/** - * @author mrdoob / http://mrdoob.com/ - * - * Abstract Base class to block based textures loader (dds, pvr, ...) - */ + nUvLayers = 0; -THREE.CompressedTextureLoader = function ( manager ) { + if ( json.uvs !== undefined ) { - this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + // disregard empty arrays - // override in sub classes - this._parser = null; + for ( i = 0; i < json.uvs.length; i ++ ) { -}; + if ( json.uvs[ i ].length ) nUvLayers ++; + } -THREE.CompressedTextureLoader.prototype = { + for ( i = 0; i < nUvLayers; i ++ ) { - constructor: THREE.CompressedTextureLoader, + geometry.faceVertexUvs[ i ] = []; - load: function ( url, onLoad, onProgress, onError ) { + } - var scope = this; + } - var images = []; + offset = 0; + zLength = vertices.length; - var texture = new THREE.CompressedTexture(); - texture.image = images; + while ( offset < zLength ) { - var loader = new THREE.XHRLoader( this.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.setResponseType( 'arraybuffer' ); + vertex = new THREE.Vector3(); - if ( Array.isArray( url ) ) { + vertex.x = vertices[ offset ++ ] * scale; + vertex.y = vertices[ offset ++ ] * scale; + vertex.z = vertices[ offset ++ ] * scale; - var loaded = 0; + geometry.vertices.push( vertex ); - var loadTexture = function ( i ) { + } - loader.load( url[ i ], function ( buffer ) { + offset = 0; + zLength = faces.length; - var texDatas = scope._parser( buffer, true ); + while ( offset < zLength ) { - images[ i ] = { - width: texDatas.width, - height: texDatas.height, - format: texDatas.format, - mipmaps: texDatas.mipmaps - }; + type = faces[ offset ++ ]; - loaded += 1; - if ( loaded === 6 ) { + isQuad = isBitSet( type, 0 ); + hasMaterial = isBitSet( type, 1 ); + hasFaceVertexUv = isBitSet( type, 3 ); + hasFaceNormal = isBitSet( type, 4 ); + hasFaceVertexNormal = isBitSet( type, 5 ); + hasFaceColor = isBitSet( type, 6 ); + hasFaceVertexColor = isBitSet( type, 7 ); - if ( texDatas.mipmapCount === 1 ) - texture.minFilter = THREE.LinearFilter; + // console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor); - texture.format = texDatas.format; - texture.needsUpdate = true; + if ( isQuad ) { - if ( onLoad ) onLoad( texture ); + faceA = new THREE.Face3(); + faceA.a = faces[ offset ]; + faceA.b = faces[ offset + 1 ]; + faceA.c = faces[ offset + 3 ]; - } + faceB = new THREE.Face3(); + faceB.a = faces[ offset + 1 ]; + faceB.b = faces[ offset + 2 ]; + faceB.c = faces[ offset + 3 ]; - }, onProgress, onError ); + offset += 4; - }; + if ( hasMaterial ) { - for ( var i = 0, il = url.length; i < il; ++ i ) { + materialIndex = faces[ offset ++ ]; + faceA.materialIndex = materialIndex; + faceB.materialIndex = materialIndex; - loadTexture( i ); + } - } + // to get face <=> uv index correspondence - } else { + fi = geometry.faces.length; - // compressed cubemap texture stored in a single DDS file + if ( hasFaceVertexUv ) { - loader.load( url, function ( buffer ) { + for ( i = 0; i < nUvLayers; i ++ ) { - var texDatas = scope._parser( buffer, true ); + uvLayer = json.uvs[ i ]; - if ( texDatas.isCubemap ) { + geometry.faceVertexUvs[ i ][ fi ] = []; + geometry.faceVertexUvs[ i ][ fi + 1 ] = []; - var faces = texDatas.mipmaps.length / texDatas.mipmapCount; + for ( j = 0; j < 4; j ++ ) { - for ( var f = 0; f < faces; f ++ ) { + uvIndex = faces[ offset ++ ]; - images[ f ] = { mipmaps : [] }; + u = uvLayer[ uvIndex * 2 ]; + v = uvLayer[ uvIndex * 2 + 1 ]; - for ( var i = 0; i < texDatas.mipmapCount; i ++ ) { + uv = new THREE.Vector2( u, v ); - 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; + if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv ); + if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv ); + + } } } - } else { + if ( hasFaceNormal ) { - texture.image.width = texDatas.width; - texture.image.height = texDatas.height; - texture.mipmaps = texDatas.mipmaps; + normalIndex = faces[ offset ++ ] * 3; - } + faceA.normal.set( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); - if ( texDatas.mipmapCount === 1 ) { + faceB.normal.copy( faceA.normal ); - texture.minFilter = THREE.LinearFilter; + } - } + if ( hasFaceVertexNormal ) { - texture.format = texDatas.format; - texture.needsUpdate = true; + for ( i = 0; i < 4; i ++ ) { - if ( onLoad ) onLoad( texture ); + normalIndex = faces[ offset ++ ] * 3; - }, onProgress, onError ); + normal = new THREE.Vector3( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); - } - return texture; + if ( i !== 2 ) faceA.vertexNormals.push( normal ); + if ( i !== 0 ) faceB.vertexNormals.push( normal ); - }, + } - setCrossOrigin: function ( value ) { + } - this.crossOrigin = value; - } + if ( hasFaceColor ) { -}; + colorIndex = faces[ offset ++ ]; + hex = colors[ colorIndex ]; -// File:src/materials/Material.js + faceA.color.setHex( hex ); + faceB.color.setHex( hex ); -/** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ + } -THREE.Material = function () { - Object.defineProperty( this, 'id', { value: THREE.MaterialIdCount ++ } ); + if ( hasFaceVertexColor ) { - this.uuid = THREE.Math.generateUUID(); + for ( i = 0; i < 4; i ++ ) { - this.name = ''; - this.type = 'Material'; + colorIndex = faces[ offset ++ ]; + hex = colors[ colorIndex ]; - this.side = THREE.FrontSide; + if ( i !== 2 ) faceA.vertexColors.push( new THREE.Color( hex ) ); + if ( i !== 0 ) faceB.vertexColors.push( new THREE.Color( hex ) ); - this.opacity = 1; - this.transparent = false; + } - this.blending = THREE.NormalBlending; + } - this.blendSrc = THREE.SrcAlphaFactor; - this.blendDst = THREE.OneMinusSrcAlphaFactor; - this.blendEquation = THREE.AddEquation; - this.blendSrcAlpha = null; - this.blendDstAlpha = null; - this.blendEquationAlpha = null; + geometry.faces.push( faceA ); + geometry.faces.push( faceB ); - this.depthFunc = THREE.LessEqualDepth; - this.depthTest = true; - this.depthWrite = true; + } else { - this.colorWrite = true; + face = new THREE.Face3(); + face.a = faces[ offset ++ ]; + face.b = faces[ offset ++ ]; + face.c = faces[ offset ++ ]; - this.precision = null; // override the renderer's default precision for this material + if ( hasMaterial ) { - this.polygonOffset = false; - this.polygonOffsetFactor = 0; - this.polygonOffsetUnits = 0; + materialIndex = faces[ offset ++ ]; + face.materialIndex = materialIndex; - this.alphaTest = 0; + } - this.overdraw = 0; // Overdrawn pixels (typically between 0 and 1) for fixing antialiasing gaps in CanvasRenderer + // to get face <=> uv index correspondence - this.visible = true; + fi = geometry.faces.length; - this._needsUpdate = true; + if ( hasFaceVertexUv ) { -}; + for ( i = 0; i < nUvLayers; i ++ ) { -THREE.Material.prototype = { + uvLayer = json.uvs[ i ]; - constructor: THREE.Material, + geometry.faceVertexUvs[ i ][ fi ] = []; - get needsUpdate () { + for ( j = 0; j < 3; j ++ ) { - return this._needsUpdate; + uvIndex = faces[ offset ++ ]; - }, + u = uvLayer[ uvIndex * 2 ]; + v = uvLayer[ uvIndex * 2 + 1 ]; - set needsUpdate ( value ) { + uv = new THREE.Vector2( u, v ); - if ( value === true ) this.update(); + geometry.faceVertexUvs[ i ][ fi ].push( uv ); - this._needsUpdate = value; + } - }, + } - setValues: function ( values ) { + } - if ( values === undefined ) return; + if ( hasFaceNormal ) { - for ( var key in values ) { + normalIndex = faces[ offset ++ ] * 3; - var newValue = values[ key ]; + face.normal.set( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); - if ( newValue === undefined ) { + } - console.warn( "THREE.Material: '" + key + "' parameter is undefined." ); - continue; + if ( hasFaceVertexNormal ) { - } + for ( i = 0; i < 3; i ++ ) { - var currentValue = this[ key ]; + normalIndex = faces[ offset ++ ] * 3; - if ( currentValue === undefined ) { + normal = new THREE.Vector3( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); - console.warn( "THREE." + this.type + ": '" + key + "' is not a property of this material." ); - continue; + face.vertexNormals.push( normal ); - } + } - if ( currentValue instanceof THREE.Color ) { + } - currentValue.set( newValue ); - } else if ( currentValue instanceof THREE.Vector3 && newValue instanceof THREE.Vector3 ) { + if ( hasFaceColor ) { - currentValue.copy( newValue ); + colorIndex = faces[ offset ++ ]; + face.color.setHex( colors[ colorIndex ] ); - } else if ( key === 'overdraw' ) { + } - // ensure overdraw is backwards-compatible with legacy boolean type - this[ key ] = Number( newValue ); - } else { + if ( hasFaceVertexColor ) { - this[ key ] = newValue; + for ( i = 0; i < 3; i ++ ) { - } + colorIndex = faces[ offset ++ ]; + face.vertexColors.push( new THREE.Color( colors[ colorIndex ] ) ); - } + } - }, + } - toJSON: function ( meta ) { + geometry.faces.push( face ); + + } - var data = { - metadata: { - version: 4.4, - type: 'Material', - generator: 'Material.toJSON' } + }; - // standard Material serialization - data.uuid = this.uuid; - data.type = this.type; - if ( this.name !== '' ) data.name = this.name; + function parseSkin() { - if ( this.color instanceof THREE.Color ) data.color = this.color.getHex(); - if ( this.emissive instanceof THREE.Color ) data.emissive = this.emissive.getHex(); - if ( this.specular instanceof THREE.Color ) data.specular = this.specular.getHex(); - if ( this.shininess !== undefined ) data.shininess = this.shininess; + var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2; - if ( this.map instanceof THREE.Texture ) data.map = this.map.toJSON( meta ).uuid; - if ( this.alphaMap instanceof THREE.Texture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid; - if ( this.lightMap instanceof THREE.Texture ) data.lightMap = this.lightMap.toJSON( meta ).uuid; - if ( this.bumpMap instanceof THREE.Texture ) { + if ( json.skinWeights ) { - data.bumpMap = this.bumpMap.toJSON( meta ).uuid; - data.bumpScale = this.bumpScale; + for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) { - } - if ( this.normalMap instanceof THREE.Texture ) { + var x = json.skinWeights[ i ]; + var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0; + var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0; + var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0; - data.normalMap = this.normalMap.toJSON( meta ).uuid; - data.normalScale = this.normalScale; // Removed for now, causes issue in editor ui.js + geometry.skinWeights.push( new THREE.Vector4( x, y, z, w ) ); - } - if ( this.displacementMap instanceof THREE.Texture ) { + } - data.displacementMap = this.displacementMap.toJSON( meta ).uuid; - data.displacementScale = this.displacementScale; - data.displacementBias = this.displacementBias; + } - } - if ( this.specularMap instanceof THREE.Texture ) data.specularMap = this.specularMap.toJSON( meta ).uuid; - if ( this.envMap instanceof THREE.Texture ) { + if ( json.skinIndices ) { - data.envMap = this.envMap.toJSON( meta ).uuid; - data.reflectivity = this.reflectivity; // Scale behind envMap + for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) { - } + var a = json.skinIndices[ i ]; + var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0; + var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0; + var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0; - if ( this.size !== undefined ) data.size = this.size; - if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation; + geometry.skinIndices.push( new THREE.Vector4( a, b, c, d ) ); - if ( this.vertexColors !== undefined && this.vertexColors !== THREE.NoColors ) data.vertexColors = this.vertexColors; - if ( this.shading !== undefined && this.shading !== THREE.SmoothShading ) data.shading = this.shading; - if ( this.blending !== undefined && this.blending !== THREE.NormalBlending ) data.blending = this.blending; - if ( this.side !== undefined && this.side !== THREE.FrontSide ) data.side = this.side; + } - if ( this.opacity < 1 ) data.opacity = this.opacity; - if ( this.transparent === true ) data.transparent = this.transparent; - if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest; - if ( this.wireframe === true ) data.wireframe = this.wireframe; - if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth; + } - return data; + geometry.bones = json.bones; - }, + if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) { - clone: function () { + console.warn( 'When skinning, number of vertices (' + geometry.vertices.length + '), skinIndices (' + + geometry.skinIndices.length + '), and skinWeights (' + geometry.skinWeights.length + ') should match.' ); - return new this.constructor().copy( this ); + } - }, + }; - copy: function ( source ) { + function parseMorphing( scale ) { - this.name = source.name; + if ( json.morphTargets !== undefined ) { - this.side = source.side; + var i, l, v, vl, dstVertices, srcVertices; - this.opacity = source.opacity; - this.transparent = source.transparent; + for ( i = 0, l = json.morphTargets.length; i < l; i ++ ) { - this.blending = source.blending; + geometry.morphTargets[ i ] = {}; + geometry.morphTargets[ i ].name = json.morphTargets[ i ].name; + geometry.morphTargets[ i ].vertices = []; - this.blendSrc = source.blendSrc; - this.blendDst = source.blendDst; - this.blendEquation = source.blendEquation; - this.blendSrcAlpha = source.blendSrcAlpha; - this.blendDstAlpha = source.blendDstAlpha; - this.blendEquationAlpha = source.blendEquationAlpha; + dstVertices = geometry.morphTargets[ i ].vertices; + srcVertices = json.morphTargets[ i ].vertices; - this.depthFunc = source.depthFunc; - this.depthTest = source.depthTest; - this.depthWrite = source.depthWrite; + for ( v = 0, vl = srcVertices.length; v < vl; v += 3 ) { - this.precision = source.precision; + var vertex = new THREE.Vector3(); + vertex.x = srcVertices[ v ] * scale; + vertex.y = srcVertices[ v + 1 ] * scale; + vertex.z = srcVertices[ v + 2 ] * scale; - this.polygonOffset = source.polygonOffset; - this.polygonOffsetFactor = source.polygonOffsetFactor; - this.polygonOffsetUnits = source.polygonOffsetUnits; + dstVertices.push( vertex ); - this.alphaTest = source.alphaTest; + } - this.overdraw = source.overdraw; + } - this.visible = source.visible; + } - return this; + if ( json.morphColors !== undefined ) { - }, + var i, l, c, cl, dstColors, srcColors, color; - update: function () { + for ( i = 0, l = json.morphColors.length; i < l; i ++ ) { - this.dispatchEvent( { type: 'update' } ); + geometry.morphColors[ i ] = {}; + geometry.morphColors[ i ].name = json.morphColors[ i ].name; + geometry.morphColors[ i ].colors = []; - }, + dstColors = geometry.morphColors[ i ].colors; + srcColors = json.morphColors[ i ].colors; - dispose: function () { + for ( c = 0, cl = srcColors.length; c < cl; c += 3 ) { - this.dispatchEvent( { type: 'dispose' } ); + color = new THREE.Color( 0xffaa00 ); + color.setRGB( srcColors[ c ], srcColors[ c + 1 ], srcColors[ c + 2 ] ); + dstColors.push( color ); - }, + } - // Deprecated + } - get wrapAround () { + } + } - console.warn( 'THREE.' + this.type + ': .wrapAround has been removed.' ); + function parseAnimations() { - }, + var outputAnimations = []; - set wrapAround ( boolean ) { + // parse old style Bone/Hierarchy animations + var animations = []; + if( json.animation !== undefined ) { + animations.push( json.animation ); + } + if( json.animations !== undefined ) { + if( json.animations.length ) { + animations = animations.concat( json.animations ); + } + else { + animations.push( json.animations ); + } + } - console.warn( 'THREE.' + this.type + ': .wrapAround has been removed.' ); + for( var i = 0; i < animations.length; i ++ ) { - }, + var clip = THREE.AnimationClip.parseAnimation( animations[i], geometry.bones ); + if( clip ) outputAnimations.push( clip ); - get wrapRGB () { + } - console.warn( 'THREE.' + this.type + ': .wrapRGB has been removed.' ); - return new THREE.Color(); + // parse implicit morph animations + if( geometry.morphTargets ) { - } + // TODO: Figure out what an appropraite FPS is for morph target animations -- defaulting to 10, but really it is completely arbitrary. + var morphAnimationClips = THREE.AnimationClip.CreateClipsFromMorphTargetSequences( geometry.morphTargets, 10 ); + outputAnimations = outputAnimations.concat( morphAnimationClips ); -}; + } -THREE.EventDispatcher.prototype.apply( THREE.Material.prototype ); + if( outputAnimations.length > 0 ) geometry.animations = outputAnimations; -THREE.MaterialIdCount = 0; + }; -// File:src/materials/LineBasicMaterial.js + if ( json.materials === undefined || json.materials.length === 0 ) { -/** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * opacity: , - * - * blending: THREE.NormalBlending, - * depthTest: , - * depthWrite: , - * - * linewidth: , - * linecap: "round", - * linejoin: "round", - * - * vertexColors: - * - * fog: - * } - */ + return { geometry: geometry }; -THREE.LineBasicMaterial = function ( parameters ) { + } else { - THREE.Material.call( this ); + var materials = THREE.Loader.prototype.initMaterials( json.materials, texturePath, this.crossOrigin ); - this.type = 'LineBasicMaterial'; + return { geometry: geometry, materials: materials }; - this.color = new THREE.Color( 0xffffff ); + } - this.linewidth = 1; - this.linecap = 'round'; - this.linejoin = 'round'; + } - this.vertexColors = THREE.NoColors; +}; - this.fog = true; +// File:src/loaders/LoadingManager.js - this.setValues( parameters ); +/** + * @author mrdoob / http://mrdoob.com/ + */ -}; +THREE.LoadingManager = function ( onLoad, onProgress, onError ) { -THREE.LineBasicMaterial.prototype = Object.create( THREE.Material.prototype ); -THREE.LineBasicMaterial.prototype.constructor = THREE.LineBasicMaterial; + var scope = this; -THREE.LineBasicMaterial.prototype.copy = function ( source ) { + var isLoading = false, itemsLoaded = 0, itemsTotal = 0; - THREE.Material.prototype.copy.call( this, source ); + this.onStart = undefined; + this.onLoad = onLoad; + this.onProgress = onProgress; + this.onError = onError; - this.color.copy( source.color ); + this.itemStart = function ( url ) { - this.linewidth = source.linewidth; - this.linecap = source.linecap; - this.linejoin = source.linejoin; + itemsTotal ++; - this.vertexColors = source.vertexColors; + if ( isLoading === false ) { - this.fog = source.fog; + if ( scope.onStart !== undefined ) { - return this; + scope.onStart( url, itemsLoaded, itemsTotal ); -}; + } -// File:src/materials/LineDashedMaterial.js + } -/** - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * opacity: , - * - * blending: THREE.NormalBlending, - * depthTest: , - * depthWrite: , - * - * linewidth: , - * - * scale: , - * dashSize: , - * gapSize: , - * - * vertexColors: - * - * fog: - * } - */ + isLoading = true; -THREE.LineDashedMaterial = function ( parameters ) { + }; - THREE.Material.call( this ); + this.itemEnd = function ( url ) { - this.type = 'LineDashedMaterial'; + itemsLoaded ++; - this.color = new THREE.Color( 0xffffff ); + if ( scope.onProgress !== undefined ) { - this.linewidth = 1; + scope.onProgress( url, itemsLoaded, itemsTotal ); - this.scale = 1; - this.dashSize = 3; - this.gapSize = 1; + } - this.vertexColors = false; + if ( itemsLoaded === itemsTotal ) { - this.fog = true; + isLoading = false; - this.setValues( parameters ); + if ( scope.onLoad !== undefined ) { -}; + scope.onLoad(); -THREE.LineDashedMaterial.prototype = Object.create( THREE.Material.prototype ); -THREE.LineDashedMaterial.prototype.constructor = THREE.LineDashedMaterial; + } -THREE.LineDashedMaterial.prototype.copy = function ( source ) { + } - THREE.Material.prototype.copy.call( this, source ); + }; - this.color.copy( source.color ); - - this.linewidth = source.linewidth; + this.itemError = function ( url ) { - this.scale = source.scale; - this.dashSize = source.dashSize; - this.gapSize = source.gapSize; + if ( scope.onError !== undefined ) { - this.vertexColors = source.vertexColors; + scope.onError( url ); - this.fog = source.fog; + } - return this; + }; }; -// File:src/materials/MeshBasicMaterial.js +THREE.DefaultLoadingManager = new THREE.LoadingManager(); + +// File:src/loaders/BufferGeometryLoader.js /** * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * opacity: , - * map: new THREE.Texture( ), - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.Multiply, - * reflectivity: , - * refractionRatio: , - * - * shading: THREE.SmoothShading, - * blending: THREE.NormalBlending, - * depthTest: , - * depthWrite: , - * - * wireframe: , - * wireframeLinewidth: , - * - * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, - * - * skinning: , - * morphTargets: , - * - * fog: - * } */ -THREE.MeshBasicMaterial = function ( parameters ) { +THREE.BufferGeometryLoader = function ( manager ) { - THREE.Material.call( this ); + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; - this.type = 'MeshBasicMaterial'; +}; - this.color = new THREE.Color( 0xffffff ); // emissive +THREE.BufferGeometryLoader.prototype = { - this.map = null; + constructor: THREE.BufferGeometryLoader, - this.aoMap = null; - this.aoMapIntensity = 1.0; + load: function ( url, onLoad, onProgress, onError ) { - this.specularMap = null; + var scope = this; - this.alphaMap = null; + var loader = new THREE.XHRLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { - this.envMap = null; - this.combine = THREE.MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; + onLoad( scope.parse( JSON.parse( text ) ) ); - this.fog = true; + }, onProgress, onError ); - 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.setValues( parameters ); + setCrossOrigin: function ( value ) { -}; + this.crossOrigin = value; -THREE.MeshBasicMaterial.prototype = Object.create( THREE.Material.prototype ); -THREE.MeshBasicMaterial.prototype.constructor = THREE.MeshBasicMaterial; + }, -THREE.MeshBasicMaterial.prototype.copy = function ( source ) { - - THREE.Material.prototype.copy.call( this, source ); + parse: function ( json ) { - this.color.copy( source.color ); + var geometry = new THREE.BufferGeometry(); - this.map = source.map; + var index = json.data.index; - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; + if ( index !== undefined ) { - this.specularMap = source.specularMap; + var typedArray = new self[ index.type ]( index.array ); + geometry.setIndex( new THREE.BufferAttribute( typedArray, 1 ) ); - this.alphaMap = source.alphaMap; + } - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; + var attributes = json.data.attributes; - this.fog = source.fog; + for ( var key in attributes ) { - this.shading = source.shading; + var attribute = attributes[ key ]; + var typedArray = new self[ attribute.type ]( attribute.array ); - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; + geometry.addAttribute( key, new THREE.BufferAttribute( typedArray, attribute.itemSize ) ); - this.vertexColors = source.vertexColors; + } - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - - return this; + var groups = json.data.groups || json.data.drawcalls || json.data.offsets; -}; + if ( groups !== undefined ) { -// File:src/materials/MeshLambertMaterial.js + for ( var i = 0, n = groups.length; i !== n; ++ i ) { -/** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * emissive: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.Multiply, - * reflectivity: , - * refractionRatio: , - * - * blending: THREE.NormalBlending, - * depthTest: , - * depthWrite: , - * - * wireframe: , - * wireframeLinewidth: , - * - * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, - * - * skinning: , - * morphTargets: , - * morphNormals: , - * - * fog: - * } - */ + var group = groups[ i ]; -THREE.MeshLambertMaterial = function ( parameters ) { + geometry.addGroup( group.start, group.count ); - THREE.Material.call( this ); + } - this.type = 'MeshLambertMaterial'; + } - this.color = new THREE.Color( 0xffffff ); // diffuse - this.emissive = new THREE.Color( 0x000000 ); + var boundingSphere = json.data.boundingSphere; - this.map = null; + if ( boundingSphere !== undefined ) { - this.specularMap = null; + var center = new THREE.Vector3(); - this.alphaMap = null; + if ( boundingSphere.center !== undefined ) { - this.envMap = null; - this.combine = THREE.MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; + center.fromArray( boundingSphere.center ); - this.fog = true; + } - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; + geometry.boundingSphere = new THREE.Sphere( center, boundingSphere.radius ); - this.vertexColors = THREE.NoColors; + } - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + return geometry; - this.setValues( parameters ); + } }; -THREE.MeshLambertMaterial.prototype = Object.create( THREE.Material.prototype ); -THREE.MeshLambertMaterial.prototype.constructor = THREE.MeshLambertMaterial; - -THREE.MeshLambertMaterial.prototype.copy = function ( source ) { - - THREE.Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - this.emissive.copy( source.emissive ); - - this.map = source.map; - - this.specularMap = source.specularMap; - - this.alphaMap = source.alphaMap; +// File:src/loaders/MaterialLoader.js - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; +/** + * @author mrdoob / http://mrdoob.com/ + */ - this.fog = source.fog; +THREE.MaterialLoader = function ( manager ) { - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + this.textures = {}; - this.vertexColors = source.vertexColors; +}; - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; +THREE.MaterialLoader.prototype = { - return this; + constructor: THREE.MaterialLoader, -}; + load: function ( url, onLoad, onProgress, onError ) { -// File:src/materials/MeshPhongMaterial.js + var scope = this; -/** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * emissive: , - * specular: , - * shininess: , - * opacity: , - * - * map: new THREE.Texture( ), - * - * lightMap: new THREE.Texture( ), - * lightMapIntensity: - * - * aoMap: new THREE.Texture( ), - * aoMapIntensity: - * - * emissiveMap: new THREE.Texture( ), - * - * bumpMap: new THREE.Texture( ), - * bumpScale: , - * - * normalMap: new THREE.Texture( ), - * normalScale: , - * - * displacementMap: new THREE.Texture( ), - * displacementScale: , - * displacementBias: , - * - * specularMap: new THREE.Texture( ), - * - * alphaMap: new THREE.Texture( ), - * - * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), - * combine: THREE.Multiply, - * reflectivity: , - * refractionRatio: , - * - * shading: THREE.SmoothShading, - * blending: THREE.NormalBlending, - * depthTest: , - * depthWrite: , - * - * wireframe: , - * wireframeLinewidth: , - * - * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, - * - * skinning: , - * morphTargets: , - * morphNormals: , - * - * fog: - * } - */ + var loader = new THREE.XHRLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { -THREE.MeshPhongMaterial = function ( parameters ) { + onLoad( scope.parse( JSON.parse( text ) ) ); - THREE.Material.call( this ); + }, onProgress, onError ); - this.type = 'MeshPhongMaterial'; + }, - this.color = new THREE.Color( 0xffffff ); // diffuse - this.emissive = new THREE.Color( 0x000000 ); - this.specular = new THREE.Color( 0x111111 ); - this.shininess = 30; + setCrossOrigin: function ( value ) { - this.metal = false; + this.crossOrigin = value; - this.map = null; + }, - this.lightMap = null; - this.lightMapIntensity = 1.0; + setTextures: function ( value ) { - this.aoMap = null; - this.aoMapIntensity = 1.0; + this.textures = value; - this.emissiveMap = null; + }, - this.bumpMap = null; - this.bumpScale = 1; + getTexture: function ( name ) { - this.normalMap = null; - this.normalScale = new THREE.Vector2( 1, 1 ); + var textures = this.textures; - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; + if ( textures[ name ] === undefined ) { - this.specularMap = null; + console.warn( 'THREE.MaterialLoader: Undefined texture', name ); - this.alphaMap = null; + } - this.envMap = null; - this.combine = THREE.MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; + return textures[ name ]; - this.fog = true; + }, - this.shading = THREE.SmoothShading; + parse: function ( json ) { - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; + var material = new THREE[ json.type ]; + material.uuid = json.uuid; - this.vertexColors = THREE.NoColors; + if ( json.name !== undefined ) material.name = json.name; + if ( json.color !== undefined ) material.color.setHex( json.color ); + if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive ); + if ( json.specular !== undefined ) material.specular.setHex( json.specular ); + if ( json.shininess !== undefined ) material.shininess = json.shininess; + if ( json.uniforms !== undefined ) material.uniforms = json.uniforms; + if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader; + if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader; + if ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors; + if ( json.shading !== undefined ) material.shading = json.shading; + if ( json.blending !== undefined ) material.blending = json.blending; + if ( json.side !== undefined ) material.side = json.side; + if ( json.opacity !== undefined ) material.opacity = json.opacity; + if ( json.transparent !== undefined ) material.transparent = json.transparent; + if ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest; + if ( json.depthTest !== undefined ) material.depthTest = json.depthTest; + if ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite; + if ( json.wireframe !== undefined ) material.wireframe = json.wireframe; + if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth; - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + // for PointsMaterial + if ( json.size !== undefined ) material.size = json.size; + if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation; - this.setValues( parameters ); + // maps -}; + if ( json.map !== undefined ) material.map = this.getTexture( json.map ); -THREE.MeshPhongMaterial.prototype = Object.create( THREE.Material.prototype ); -THREE.MeshPhongMaterial.prototype.constructor = THREE.MeshPhongMaterial; + if ( json.alphaMap !== undefined ) { -THREE.MeshPhongMaterial.prototype.copy = function ( source ) { + material.alphaMap = this.getTexture( json.alphaMap ); + material.transparent = true; - THREE.Material.prototype.copy.call( this, source ); + } - this.color.copy( source.color ); - this.emissive.copy( source.emissive ); - this.specular.copy( source.specular ); - this.shininess = source.shininess; + if ( json.bumpMap !== undefined ) material.bumpMap = this.getTexture( json.bumpMap ); + if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale; - this.metal = source.metal; + if ( json.normalMap !== undefined ) material.normalMap = this.getTexture( json.normalMap ); + if ( json.normalScale ) material.normalScale = new THREE.Vector2( json.normalScale, json.normalScale ); - this.map = source.map; + if ( json.displacementMap !== undefined ) material.displacementMap = this.getTexture( json.displacementMap ); + if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale; + if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias; - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; + if ( json.specularMap !== undefined ) material.specularMap = this.getTexture( json.specularMap ); - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; + if ( json.envMap !== undefined ) { - this.emissiveMap = source.emissiveMap; + material.envMap = this.getTexture( json.envMap ); + material.combine = THREE.MultiplyOperation; - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; + } - this.normalMap = source.normalMap; - this.normalScale.copy( source.normalScale ); + if ( json.reflectivity ) material.reflectivity = json.reflectivity; - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; + if ( json.lightMap !== undefined ) material.lightMap = this.getTexture( json.lightMap ); + if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity; - this.specularMap = source.specularMap; + if ( json.aoMap !== undefined ) material.aoMap = this.getTexture( json.aoMap ); + if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity; - this.alphaMap = source.alphaMap; + // MeshFaceMaterial - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; + if ( json.materials !== undefined ) { - this.fog = source.fog; + for ( var i = 0, l = json.materials.length; i < l; i ++ ) { - this.shading = source.shading; + material.materials.push( this.parse( json.materials[ i ] ) ); - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; + } - this.vertexColors = source.vertexColors; + } - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; + return material; - return this; + } }; -// File:src/materials/MeshDepthMaterial.js +// File:src/loaders/ObjectLoader.js /** * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * opacity: , - * - * blending: THREE.NormalBlending, - * depthTest: , - * depthWrite: , - * - * wireframe: , - * wireframeLinewidth: - * } */ -THREE.MeshDepthMaterial = function ( parameters ) { - - THREE.Material.call( this ); - - this.type = 'MeshDepthMaterial'; - - this.morphTargets = false; - this.wireframe = false; - this.wireframeLinewidth = 1; +THREE.ObjectLoader = function ( manager ) { - this.setValues( parameters ); + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + this.texturePath = ''; }; -THREE.MeshDepthMaterial.prototype = Object.create( THREE.Material.prototype ); -THREE.MeshDepthMaterial.prototype.constructor = THREE.MeshDepthMaterial; +THREE.ObjectLoader.prototype = { -THREE.MeshDepthMaterial.prototype.copy = function ( source ) { + constructor: THREE.ObjectLoader, - THREE.Material.prototype.copy.call( this, source ); + load: function ( url, onLoad, onProgress, onError ) { - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; + if ( this.texturePath === '' ) { - return this; + this.texturePath = url.substring( 0, url.lastIndexOf( '/' ) + 1 ); -}; + } -// File:src/materials/MeshNormalMaterial.js + var scope = this; -/** - * @author mrdoob / http://mrdoob.com/ - * - * parameters = { - * opacity: , - * - * shading: THREE.FlatShading, - * blending: THREE.NormalBlending, - * depthTest: , - * depthWrite: , - * - * wireframe: , - * wireframeLinewidth: - * } - */ + var loader = new THREE.XHRLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( text ) { -THREE.MeshNormalMaterial = function ( parameters ) { + scope.parse( JSON.parse( text ), onLoad ); - THREE.Material.call( this, parameters ); + }, onProgress, onError ); - this.type = 'MeshNormalMaterial'; + }, - this.wireframe = false; - this.wireframeLinewidth = 1; + setTexturePath: function ( value ) { - this.morphTargets = false; + this.texturePath = value; - this.setValues( parameters ); + }, -}; + setCrossOrigin: function ( value ) { -THREE.MeshNormalMaterial.prototype = Object.create( THREE.Material.prototype ); -THREE.MeshNormalMaterial.prototype.constructor = THREE.MeshNormalMaterial; + this.crossOrigin = value; -THREE.MeshNormalMaterial.prototype.copy = function ( source ) { + }, - THREE.Material.prototype.copy.call( this, source ); + parse: function ( json, onLoad ) { - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; + var geometries = this.parseGeometries( json.geometries ); - return this; + var images = this.parseImages( json.images, function () { -}; + if ( onLoad !== undefined ) onLoad( object ); -// File:src/materials/MultiMaterial.js + } ); -/** - * @author mrdoob / http://mrdoob.com/ - */ + var textures = this.parseTextures( json.textures, images ); + var materials = this.parseMaterials( json.materials, textures ); -THREE.MultiMaterial = function ( materials ) { + var object = this.parseObject( json.object, geometries, materials ); - this.uuid = THREE.Math.generateUUID(); + if( json.animations ) { - this.type = 'MultiMaterial'; + object.animations = this.parseAnimations( json.animations ); - this.materials = materials instanceof Array ? materials : []; + } - this.visible = true; + if ( json.images === undefined || json.images.length === 0 ) { -}; + if ( onLoad !== undefined ) onLoad( object ); -THREE.MultiMaterial.prototype = { + } - constructor: THREE.MultiMaterial, + return object; - toJSON: function () { + }, - var output = { - metadata: { - version: 4.2, - type: 'material', - generator: 'MaterialExporter' - }, - uuid: this.uuid, - type: this.type, - materials: [] - }; + parseGeometries: function ( json ) { - for ( var i = 0, l = this.materials.length; i < l; i ++ ) { + var geometries = {}; - output.materials.push( this.materials[ i ].toJSON() ); + if ( json !== undefined ) { - } + var geometryLoader = new THREE.JSONLoader(); + var bufferGeometryLoader = new THREE.BufferGeometryLoader(); - output.visible = this.visible; + for ( var i = 0, l = json.length; i < l; i ++ ) { - return output; + var geometry; + var data = json[ i ]; - }, + switch ( data.type ) { - clone: function () { + case 'PlaneGeometry': + case 'PlaneBufferGeometry': - var material = new this.constructor(); + geometry = new THREE[ data.type ]( + data.width, + data.height, + data.widthSegments, + data.heightSegments + ); - for ( var i = 0; i < this.materials.length; i ++ ) { + break; - material.materials.push( this.materials[ i ].clone() ); + case 'BoxGeometry': + case 'CubeGeometry': // backwards compatible - } + geometry = new THREE.BoxGeometry( + data.width, + data.height, + data.depth, + data.widthSegments, + data.heightSegments, + data.depthSegments + ); - material.visible = this.visible; + break; - return material; + case 'CircleBufferGeometry': - } + geometry = new THREE.CircleBufferGeometry( + data.radius, + data.segments, + data.thetaStart, + data.thetaLength + ); -}; + break; -// backwards compatibility + case 'CircleGeometry': -THREE.MeshFaceMaterial = THREE.MultiMaterial; + geometry = new THREE.CircleGeometry( + data.radius, + data.segments, + data.thetaStart, + data.thetaLength + ); -// File:src/materials/PointsMaterial.js + break; -/** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * opacity: , - * map: new THREE.Texture( ), - * - * size: , - * sizeAttenuation: , - * - * blending: THREE.NormalBlending, - * depthTest: , - * depthWrite: , - * - * vertexColors: , - * - * fog: - * } - */ + case 'CylinderGeometry': -THREE.PointsMaterial = function ( parameters ) { + geometry = new THREE.CylinderGeometry( + data.radiusTop, + data.radiusBottom, + data.height, + data.radialSegments, + data.heightSegments, + data.openEnded, + data.thetaStart, + data.thetaLength + ); - THREE.Material.call( this ); + break; - this.type = 'PointsMaterial'; + case 'SphereGeometry': - this.color = new THREE.Color( 0xffffff ); + geometry = new THREE.SphereGeometry( + data.radius, + data.widthSegments, + data.heightSegments, + data.phiStart, + data.phiLength, + data.thetaStart, + data.thetaLength + ); - this.map = null; + break; - this.size = 1; - this.sizeAttenuation = true; + case 'SphereBufferGeometry': - this.vertexColors = THREE.NoColors; + geometry = new THREE.SphereBufferGeometry( + data.radius, + data.widthSegments, + data.heightSegments, + data.phiStart, + data.phiLength, + data.thetaStart, + data.thetaLength + ); - this.fog = true; + break; - this.setValues( parameters ); + case 'DodecahedronGeometry': -}; + geometry = new THREE.DodecahedronGeometry( + data.radius, + data.detail + ); -THREE.PointsMaterial.prototype = Object.create( THREE.Material.prototype ); -THREE.PointsMaterial.prototype.constructor = THREE.PointsMaterial; + break; -THREE.PointsMaterial.prototype.copy = function ( source ) { + case 'IcosahedronGeometry': - THREE.Material.prototype.copy.call( this, source ); + geometry = new THREE.IcosahedronGeometry( + data.radius, + data.detail + ); - this.color.copy( source.color ); + break; - this.map = source.map; + case 'OctahedronGeometry': - this.size = source.size; - this.sizeAttenuation = source.sizeAttenuation; + geometry = new THREE.OctahedronGeometry( + data.radius, + data.detail + ); - this.vertexColors = source.vertexColors; + break; - this.fog = source.fog; + case 'TetrahedronGeometry': - return this; + geometry = new THREE.TetrahedronGeometry( + data.radius, + data.detail + ); -}; + break; -// backwards compatibility + case 'RingGeometry': -THREE.PointCloudMaterial = function ( parameters ) { + geometry = new THREE.RingGeometry( + data.innerRadius, + data.outerRadius, + data.thetaSegments, + data.phiSegments, + data.thetaStart, + data.thetaLength + ); - console.warn( 'THREE.PointCloudMaterial has been renamed to THREE.PointsMaterial.' ); - return new THREE.PointsMaterial( parameters ); + break; -}; + case 'TorusGeometry': -THREE.ParticleBasicMaterial = function ( parameters ) { + geometry = new THREE.TorusGeometry( + data.radius, + data.tube, + data.radialSegments, + data.tubularSegments, + data.arc + ); - console.warn( 'THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial.' ); - return new THREE.PointsMaterial( parameters ); + break; -}; + case 'TorusKnotGeometry': -THREE.ParticleSystemMaterial = function ( parameters ) { + geometry = new THREE.TorusKnotGeometry( + data.radius, + data.tube, + data.radialSegments, + data.tubularSegments, + data.p, + data.q, + data.heightScale + ); - console.warn( 'THREE.ParticleSystemMaterial has been renamed to THREE.PointsMaterial.' ); - return new THREE.PointsMaterial( parameters ); + break; -}; + case 'TextGeometry': -// File:src/materials/ShaderMaterial.js + geometry = new THREE.TextGeometry( + data.text, + data.data + ); -/** - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * defines: { "label" : "value" }, - * uniforms: { "parameter1": { type: "f", value: 1.0 }, "parameter2": { type: "i" value2: 2 } }, - * - * fragmentShader: , - * vertexShader: , - * - * shading: THREE.SmoothShading, - * blending: THREE.NormalBlending, - * depthTest: , - * depthWrite: , - * - * wireframe: , - * wireframeLinewidth: , - * - * lights: , - * - * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, - * - * skinning: , - * morphTargets: , - * morphNormals: , - * - * fog: - * } - */ + break; -THREE.ShaderMaterial = function ( parameters ) { + case 'BufferGeometry': - THREE.Material.call( this ); + geometry = bufferGeometryLoader.parse( data ); - this.type = 'ShaderMaterial'; + break; - this.defines = {}; - this.uniforms = {}; + case 'Geometry': - this.vertexShader = 'void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}'; - this.fragmentShader = 'void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}'; + geometry = geometryLoader.parse( data.data, this.texturePath ).geometry; - this.shading = THREE.SmoothShading; + break; - this.linewidth = 1; + default: - this.wireframe = false; - this.wireframeLinewidth = 1; + console.warn( 'THREE.ObjectLoader: Unsupported geometry type "' + data.type + '"' ); - this.fog = false; // set to use scene fog + continue; - this.lights = false; // set to use scene lights + } - this.vertexColors = THREE.NoColors; // set to use "color" attribute stream + geometry.uuid = data.uuid; - this.skinning = false; // set to use skinning attribute streams + if ( data.name !== undefined ) geometry.name = data.name; - this.morphTargets = false; // set to use morph targets - this.morphNormals = false; // set to use morph normals + geometries[ data.uuid ] = geometry; - this.derivatives = false; // set to use derivatives + } - // When rendered geometry doesn't include these attributes but the material does, - // use these default values in WebGL. This avoids errors when buffer data is missing. - this.defaultAttributeValues = { - 'color': [ 1, 1, 1 ], - 'uv': [ 0, 0 ], - 'uv2': [ 0, 0 ] - }; + } - this.index0AttributeName = undefined; + return geometries; - if ( parameters !== undefined ) { + }, - if ( parameters.attributes !== undefined ) { + parseMaterials: function ( json, textures ) { - console.error( 'THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead.' ); + var materials = {}; - } + if ( json !== undefined ) { - this.setValues( parameters ); + var loader = new THREE.MaterialLoader(); + loader.setTextures( textures ); - } + for ( var i = 0, l = json.length; i < l; i ++ ) { -}; + var material = loader.parse( json[ i ] ); + materials[ material.uuid ] = material; -THREE.ShaderMaterial.prototype = Object.create( THREE.Material.prototype ); -THREE.ShaderMaterial.prototype.constructor = THREE.ShaderMaterial; + } -THREE.ShaderMaterial.prototype.copy = function ( source ) { + } - THREE.Material.prototype.copy.call( this, source ); + return materials; - this.fragmentShader = source.fragmentShader; - this.vertexShader = source.vertexShader; + }, - this.uniforms = THREE.UniformsUtils.clone( source.uniforms ); + parseAnimations: function ( json ) { - this.attributes = source.attributes; - this.defines = source.defines; + var animations = []; - this.shading = source.shading; + for( var i = 0; i < json.length; i ++ ) { - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; + var clip = THREE.AnimationClip.parse( json[i] ); - this.fog = source.fog; + animations.push( clip ); - this.lights = source.lights; + } - this.vertexColors = source.vertexColors; + return animations; - this.skinning = source.skinning; + }, - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; + parseImages: function ( json, onLoad ) { - this.derivatives = source.derivatives; + var scope = this; + var images = {}; - return this; + function loadImage( url ) { -}; + scope.manager.itemStart( url ); -THREE.ShaderMaterial.prototype.toJSON = function ( meta ) { + return loader.load( url, function () { - var data = THREE.Material.prototype.toJSON.call( this, meta ); + scope.manager.itemEnd( url ); - data.uniforms = this.uniforms; - data.attributes = this.attributes; - data.vertexShader = this.vertexShader; - data.fragmentShader = this.fragmentShader; + } ); - return data; + } -}; + if ( json !== undefined && json.length > 0 ) { -// File:src/materials/RawShaderMaterial.js + var manager = new THREE.LoadingManager( onLoad ); -/** - * @author mrdoob / http://mrdoob.com/ - */ + var loader = new THREE.ImageLoader( manager ); + loader.setCrossOrigin( this.crossOrigin ); -THREE.RawShaderMaterial = function ( parameters ) { + for ( var i = 0, l = json.length; i < l; i ++ ) { - THREE.ShaderMaterial.call( this, parameters ); + var image = json[ i ]; + var path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( image.url ) ? image.url : scope.texturePath + image.url; - this.type = 'RawShaderMaterial'; + images[ image.uuid ] = loadImage( path ); -}; + } -THREE.RawShaderMaterial.prototype = Object.create( THREE.ShaderMaterial.prototype ); -THREE.RawShaderMaterial.prototype.constructor = THREE.RawShaderMaterial; -// File:src/materials/SpriteMaterial.js + } -/** - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * opacity: , - * map: new THREE.Texture( ), - * - * blending: THREE.NormalBlending, - * depthTest: , - * depthWrite: , - * - * uvOffset: new THREE.Vector2(), - * uvScale: new THREE.Vector2(), - * - * fog: - * } - */ + return images; -THREE.SpriteMaterial = function ( parameters ) { + }, - THREE.Material.call( this ); + parseTextures: function ( json, images ) { - this.type = 'SpriteMaterial'; + function parseConstant( value ) { - this.color = new THREE.Color( 0xffffff ); - this.map = null; + if ( typeof( value ) === 'number' ) return value; - this.rotation = 0; + console.warn( 'THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value ); - this.fog = false; + return THREE[ value ]; - // set parameters + } - this.setValues( parameters ); + var textures = {}; -}; + if ( json !== undefined ) { -THREE.SpriteMaterial.prototype = Object.create( THREE.Material.prototype ); -THREE.SpriteMaterial.prototype.constructor = THREE.SpriteMaterial; + for ( var i = 0, l = json.length; i < l; i ++ ) { -THREE.SpriteMaterial.prototype.copy = function ( source ) { + var data = json[ i ]; - THREE.Material.prototype.copy.call( this, source ); + if ( data.image === undefined ) { - this.color.copy( source.color ); - this.map = source.map; + console.warn( 'THREE.ObjectLoader: No "image" specified for', data.uuid ); - this.rotation = source.rotation; + } - this.fog = source.fog; + if ( images[ data.image ] === undefined ) { - return this; + console.warn( 'THREE.ObjectLoader: Undefined image', data.image ); -}; + } -// File:src/textures/Texture.js + var texture = new THREE.Texture( images[ data.image ] ); + texture.needsUpdate = true; -/** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * @author szimek / https://github.com/szimek/ - */ + texture.uuid = data.uuid; -THREE.Texture = function ( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { + if ( data.name !== undefined ) texture.name = data.name; + if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping ); + if ( data.offset !== undefined ) texture.offset = new THREE.Vector2( data.offset[ 0 ], data.offset[ 1 ] ); + if ( data.repeat !== undefined ) texture.repeat = new THREE.Vector2( data.repeat[ 0 ], data.repeat[ 1 ] ); + if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter ); + if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter ); + if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy; + if ( Array.isArray( data.wrap ) ) { - Object.defineProperty( this, 'id', { value: THREE.TextureIdCount ++ } ); + texture.wrapS = parseConstant( data.wrap[ 0 ] ); + texture.wrapT = parseConstant( data.wrap[ 1 ] ); - this.uuid = THREE.Math.generateUUID(); + } - this.name = ''; - this.sourceFile = ''; + textures[ data.uuid ] = texture; - this.image = image !== undefined ? image : THREE.Texture.DEFAULT_IMAGE; - this.mipmaps = []; + } - this.mapping = mapping !== undefined ? mapping : THREE.Texture.DEFAULT_MAPPING; + } - this.wrapS = wrapS !== undefined ? wrapS : THREE.ClampToEdgeWrapping; - this.wrapT = wrapT !== undefined ? wrapT : THREE.ClampToEdgeWrapping; + return textures; - this.magFilter = magFilter !== undefined ? magFilter : THREE.LinearFilter; - this.minFilter = minFilter !== undefined ? minFilter : THREE.LinearMipMapLinearFilter; + }, - this.anisotropy = anisotropy !== undefined ? anisotropy : 1; + parseObject: function () { - this.format = format !== undefined ? format : THREE.RGBAFormat; - this.type = type !== undefined ? type : THREE.UnsignedByteType; + var matrix = new THREE.Matrix4(); - this.offset = new THREE.Vector2( 0, 0 ); - this.repeat = new THREE.Vector2( 1, 1 ); + return function ( data, geometries, materials ) { - this.generateMipmaps = true; - this.premultiplyAlpha = false; - this.flipY = true; - this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) + var object; - this.version = 0; - this.onUpdate = null; + var getGeometry = function ( name ) { -}; + if ( geometries[ name ] === undefined ) { -THREE.Texture.DEFAULT_IMAGE = undefined; -THREE.Texture.DEFAULT_MAPPING = THREE.UVMapping; + console.warn( 'THREE.ObjectLoader: Undefined geometry', name ); -THREE.Texture.prototype = { + } - constructor: THREE.Texture, + return geometries[ name ]; - set needsUpdate ( value ) { + }; - if ( value === true ) this.version ++; + var getMaterial = function ( name ) { - }, + if ( materials[ name ] === undefined ) { - clone: function () { + console.warn( 'THREE.ObjectLoader: Undefined material', name ); - return new this.constructor().copy( this ); + } - }, + return materials[ name ]; - copy: function ( source ) { + }; - this.image = source.image; - this.mipmaps = source.mipmaps.slice( 0 ); + switch ( data.type ) { - this.mapping = source.mapping; + case 'Scene': - this.wrapS = source.wrapS; - this.wrapT = source.wrapT; + object = new THREE.Scene(); - this.magFilter = source.magFilter; - this.minFilter = source.minFilter; + break; - this.anisotropy = source.anisotropy; + case 'PerspectiveCamera': - this.format = source.format; - this.type = source.type; + object = new THREE.PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); - this.offset.copy( source.offset ); - this.repeat.copy( source.repeat ); + break; - this.generateMipmaps = source.generateMipmaps; - this.premultiplyAlpha = source.premultiplyAlpha; - this.flipY = source.flipY; - this.unpackAlignment = source.unpackAlignment; + case 'OrthographicCamera': - return this; + object = new THREE.OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); - }, + break; - toJSON: function ( meta ) { + case 'AmbientLight': - if ( meta.textures[ this.uuid ] !== undefined ) { + object = new THREE.AmbientLight( data.color ); - return meta.textures[ this.uuid ]; + break; - } + case 'DirectionalLight': - function getDataURL( image ) { + object = new THREE.DirectionalLight( data.color, data.intensity ); - var canvas; + break; - if ( image.toDataURL !== undefined ) { + case 'PointLight': - canvas = image; + object = new THREE.PointLight( data.color, data.intensity, data.distance, data.decay ); - } else { + break; - canvas = document.createElement( 'canvas' ); - canvas.width = image.width; - canvas.height = image.height; + case 'SpotLight': - canvas.getContext( '2d' ).drawImage( image, 0, 0, image.width, image.height ); + object = new THREE.SpotLight( data.color, data.intensity, data.distance, data.angle, data.exponent, data.decay ); - } + break; - if ( canvas.width > 2048 || canvas.height > 2048 ) { + case 'HemisphereLight': - return canvas.toDataURL( 'image/jpeg', 0.6 ); + object = new THREE.HemisphereLight( data.color, data.groundColor, data.intensity ); - } else { + break; - return canvas.toDataURL( 'image/png' ); + case 'Mesh': - } + object = new THREE.Mesh( getGeometry( data.geometry ), getMaterial( data.material ) ); - } + break; - var output = { - metadata: { - version: 4.4, - type: 'Texture', - generator: 'Texture.toJSON' - }, + case 'LOD': - uuid: this.uuid, - name: this.name, + object = new THREE.LOD(); - mapping: this.mapping, + break; - repeat: [ this.repeat.x, this.repeat.y ], - offset: [ this.offset.x, this.offset.y ], - wrap: [ this.wrapS, this.wrapT ], + case 'Line': - minFilter: this.minFilter, - magFilter: this.magFilter, - anisotropy: this.anisotropy - }; + object = new THREE.Line( getGeometry( data.geometry ), getMaterial( data.material ), data.mode ); - if ( this.image !== undefined ) { + break; - // TODO: Move to THREE.Image + case 'PointCloud': + case 'Points': - var image = this.image; + object = new THREE.Points( getGeometry( data.geometry ), getMaterial( data.material ) ); - if ( image.uuid === undefined ) { + break; - image.uuid = THREE.Math.generateUUID(); // UGH + case 'Sprite': - } + object = new THREE.Sprite( getMaterial( data.material ) ); - if ( meta.images[ image.uuid ] === undefined ) { + break; - meta.images[ image.uuid ] = { - uuid: image.uuid, - url: getDataURL( image ) - }; + case 'Group': - } + object = new THREE.Group(); - output.image = image.uuid; + break; - } + default: - meta.textures[ this.uuid ] = output; + object = new THREE.Object3D(); - return output; + } - }, + object.uuid = data.uuid; - dispose: function () { + if ( data.name !== undefined ) object.name = data.name; + if ( data.matrix !== undefined ) { - this.dispatchEvent( { type: 'dispose' } ); + matrix.fromArray( data.matrix ); + matrix.decompose( object.position, object.quaternion, object.scale ); - }, + } else { - transformUv: function ( uv ) { + if ( data.position !== undefined ) object.position.fromArray( data.position ); + if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation ); + if ( data.scale !== undefined ) object.scale.fromArray( data.scale ); - if ( this.mapping !== THREE.UVMapping ) return; + } - uv.multiply( this.repeat ); - uv.add( this.offset ); + if ( data.castShadow !== undefined ) object.castShadow = data.castShadow; + if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow; - if ( uv.x < 0 || uv.x > 1 ) { + if ( data.visible !== undefined ) object.visible = data.visible; + if ( data.userData !== undefined ) object.userData = data.userData; - switch ( this.wrapS ) { + if ( data.children !== undefined ) { - case THREE.RepeatWrapping: + for ( var child in data.children ) { - uv.x = uv.x - Math.floor( uv.x ); - break; + object.add( this.parseObject( data.children[ child ], geometries, materials ) ); - case THREE.ClampToEdgeWrapping: + } - uv.x = uv.x < 0 ? 0 : 1; - break; + } - case THREE.MirroredRepeatWrapping: + if ( data.type === 'LOD' ) { - if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { + var levels = data.levels; - uv.x = Math.ceil( uv.x ) - uv.x; + for ( var l = 0; l < levels.length; l ++ ) { - } else { + var level = levels[ l ]; + var child = object.getObjectByProperty( 'uuid', level.object ); - uv.x = uv.x - Math.floor( uv.x ); + if ( child !== undefined ) { + + object.addLevel( child, level.distance ); } - break; + + } } + return object; + } - if ( uv.y < 0 || uv.y > 1 ) { + }() - switch ( this.wrapT ) { +}; - case THREE.RepeatWrapping: +// File:src/loaders/TextureLoader.js - uv.y = uv.y - Math.floor( uv.y ); - break; +/** + * @author mrdoob / http://mrdoob.com/ + */ - case THREE.ClampToEdgeWrapping: +THREE.TextureLoader = function ( manager ) { - uv.y = uv.y < 0 ? 0 : 1; - break; + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; - case THREE.MirroredRepeatWrapping: +}; - if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { +THREE.TextureLoader.prototype = { - uv.y = Math.ceil( uv.y ) - uv.y; + constructor: THREE.TextureLoader, - } else { + load: function ( url, onLoad, onProgress, onError ) { - uv.y = uv.y - Math.floor( uv.y ); + var scope = this; - } - break; + var loader = new THREE.ImageLoader( scope.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.load( url, function ( image ) { + + var texture = new THREE.Texture( image ); + texture.needsUpdate = true; + + if ( onLoad !== undefined ) { + + onLoad( texture ); } - } + }, onProgress, onError ); - if ( this.flipY ) { + }, - uv.y = 1 - uv.y; + setCrossOrigin: function ( value ) { - } + this.crossOrigin = value; } }; -THREE.EventDispatcher.prototype.apply( THREE.Texture.prototype ); - -THREE.TextureIdCount = 0; - -// File:src/textures/CanvasTexture.js +// File:src/loaders/BinaryTextureLoader.js /** - * @author mrdoob / http://mrdoob.com/ + * @author Nikos M. / https://github.com/foo123/ + * + * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) */ -THREE.CanvasTexture = function ( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { +THREE.DataTextureLoader = THREE.BinaryTextureLoader = function ( manager ) { - THREE.Texture.call( this, canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; - this.needsUpdate = true; + // override in sub classes + this._parser = null; }; -THREE.CanvasTexture.prototype = Object.create( THREE.Texture.prototype ); -THREE.CanvasTexture.prototype.constructor = THREE.CanvasTexture; - -// File:src/textures/CubeTexture.js - -/** - * @author mrdoob / http://mrdoob.com/ - */ - -THREE.CubeTexture = function ( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { - - mapping = mapping !== undefined ? mapping : THREE.CubeReflectionMapping; - - THREE.Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - - this.images = images; - this.flipY = false; +THREE.BinaryTextureLoader.prototype = { -}; + constructor: THREE.BinaryTextureLoader, -THREE.CubeTexture.prototype = Object.create( THREE.Texture.prototype ); -THREE.CubeTexture.prototype.constructor = THREE.CubeTexture; + load: function ( url, onLoad, onProgress, onError ) { -THREE.CubeTexture.prototype.copy = function ( source ) { + var scope = this; - THREE.Texture.prototype.copy.call( this, source ); - - this.images = source.images; - - return this; + var texture = new THREE.DataTexture(); -}; -// File:src/textures/CompressedTexture.js + var loader = new THREE.XHRLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.setResponseType( 'arraybuffer' ); -/** - * @author alteredq / http://alteredqualia.com/ - */ + loader.load( url, function ( buffer ) { -THREE.CompressedTexture = function ( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) { + var texData = scope._parser( buffer ); - THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + if ( ! texData ) return; - this.image = { width: width, height: height }; - this.mipmaps = mipmaps; + if ( undefined !== texData.image ) { - // no flipping for cube textures - // (also flipping doesn't work for compressed textures ) + texture.image = texData.image; - this.flipY = false; + } else if ( undefined !== texData.data ) { - // can't generate mipmaps for compressed textures - // mips must be embedded in DDS files + texture.image.width = texData.width; + texture.image.height = texData.height; + texture.image.data = texData.data; - this.generateMipmaps = false; + } -}; + texture.wrapS = undefined !== texData.wrapS ? texData.wrapS : THREE.ClampToEdgeWrapping; + texture.wrapT = undefined !== texData.wrapT ? texData.wrapT : THREE.ClampToEdgeWrapping; -THREE.CompressedTexture.prototype = Object.create( THREE.Texture.prototype ); -THREE.CompressedTexture.prototype.constructor = THREE.CompressedTexture; + texture.magFilter = undefined !== texData.magFilter ? texData.magFilter : THREE.LinearFilter; + texture.minFilter = undefined !== texData.minFilter ? texData.minFilter : THREE.LinearMipMapLinearFilter; -// File:src/textures/DataTexture.js + texture.anisotropy = undefined !== texData.anisotropy ? texData.anisotropy : 1; -/** - * @author alteredq / http://alteredqualia.com/ - */ + if ( undefined !== texData.format ) { -THREE.DataTexture = function ( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) { + texture.format = texData.format; - THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + } + if ( undefined !== texData.type ) { - this.image = { data: data, width: width, height: height }; + texture.type = texData.type; - this.magFilter = magFilter !== undefined ? magFilter : THREE.NearestFilter; - this.minFilter = minFilter !== undefined ? minFilter : THREE.NearestFilter; - - this.flipY = false; - this.generateMipmaps = false; + } -}; + if ( undefined !== texData.mipmaps ) { -THREE.DataTexture.prototype = Object.create( THREE.Texture.prototype ); -THREE.DataTexture.prototype.constructor = THREE.DataTexture; + texture.mipmaps = texData.mipmaps; -// File:src/textures/VideoTexture.js + } -/** - * @author mrdoob / http://mrdoob.com/ - */ + if ( 1 === texData.mipmapCount ) { -THREE.VideoTexture = function ( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { + texture.minFilter = THREE.LinearFilter; - THREE.Texture.call( this, video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); + } - this.generateMipmaps = false; + texture.needsUpdate = true; - var scope = this; + if ( onLoad ) onLoad( texture, texData ); - var update = function () { + }, onProgress, onError ); - requestAnimationFrame( update ); - if ( video.readyState === video.HAVE_ENOUGH_DATA ) { + return texture; - scope.needsUpdate = true; + }, - } + setCrossOrigin: function ( value ) { - }; + this.crossOrigin = value; - update(); + } }; -THREE.VideoTexture.prototype = Object.create( THREE.Texture.prototype ); -THREE.VideoTexture.prototype.constructor = THREE.VideoTexture; - -// File:src/objects/Group.js +// File:src/loaders/CompressedTextureLoader.js /** * @author mrdoob / http://mrdoob.com/ + * + * Abstract Base class to block based textures loader (dds, pvr, ...) */ -THREE.Group = function () { +THREE.CompressedTextureLoader = function ( manager ) { - THREE.Object3D.call( this ); + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; - this.type = 'Group'; + // override in sub classes + this._parser = null; }; -THREE.Group.prototype = Object.create( THREE.Object3D.prototype ); -THREE.Group.prototype.constructor = THREE.Group; -// File:src/objects/Points.js - -/** - * @author alteredq / http://alteredqualia.com/ - */ - -THREE.Points = function ( geometry, material ) { - - THREE.Object3D.call( this ); - - this.type = 'Points'; - this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); - this.material = material !== undefined ? material : new THREE.PointsMaterial( { color: Math.random() * 0xffffff } ); +THREE.CompressedTextureLoader.prototype = { -}; + constructor: THREE.CompressedTextureLoader, -THREE.Points.prototype = Object.create( THREE.Object3D.prototype ); -THREE.Points.prototype.constructor = THREE.Points; + load: function ( url, onLoad, onProgress, onError ) { -THREE.Points.prototype.raycast = ( function () { + var scope = this; - var inverseMatrix = new THREE.Matrix4(); - var ray = new THREE.Ray(); + var images = []; - return function raycast( raycaster, intersects ) { + var texture = new THREE.CompressedTexture(); + texture.image = images; - var object = this; - var geometry = object.geometry; - var threshold = raycaster.params.Points.threshold; + var loader = new THREE.XHRLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.setResponseType( 'arraybuffer' ); - inverseMatrix.getInverse( this.matrixWorld ); - ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); + if ( Array.isArray( url ) ) { - if ( geometry.boundingBox !== null ) { + var loaded = 0; - if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) { + var loadTexture = function ( i ) { - return; + 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 + }; - var localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); - var localThresholdSq = localThreshold * localThreshold; - var position = new THREE.Vector3(); + loaded += 1; - function testPoint( point, index ) { + if ( loaded === 6 ) { - var rayPointDistanceSq = ray.distanceSqToPoint( point ); + if ( texDatas.mipmapCount === 1 ) + texture.minFilter = THREE.LinearFilter; - if ( rayPointDistanceSq < localThresholdSq ) { + texture.format = texDatas.format; + texture.needsUpdate = true; - var intersectPoint = ray.closestPointToPoint( point ); - intersectPoint.applyMatrix4( object.matrixWorld ); + if ( onLoad ) onLoad( texture ); - var distance = raycaster.ray.origin.distanceTo( intersectPoint ); + } - if ( distance < raycaster.near || distance > raycaster.far ) return; + }, onProgress, onError ); - intersects.push( { + }; - distance: distance, - distanceToRay: Math.sqrt( rayPointDistanceSq ), - point: intersectPoint.clone(), - index: index, - face: null, - object: object + for ( var i = 0, il = url.length; i < il; ++ i ) { - } ); + loadTexture( i ); } - } + } else { - if ( geometry instanceof THREE.BufferGeometry ) { + // compressed cubemap texture stored in a single DDS file - var index = geometry.index; - var attributes = geometry.attributes; - var positions = attributes.position.array; + loader.load( url, function ( buffer ) { - if ( index !== null ) { + var texDatas = scope._parser( buffer, true ); - var indices = index.array; + if ( texDatas.isCubemap ) { - for ( var i = 0, il = indices.length; i < il; i ++ ) { + var faces = texDatas.mipmaps.length / texDatas.mipmapCount; - var a = indices[ i ]; + for ( var f = 0; f < faces; f ++ ) { - position.fromArray( positions, a * 3 ); + images[ f ] = { mipmaps : [] }; - testPoint( position, a ); + 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 { + } - for ( var i = 0, l = positions.length / 3; i < l; i ++ ) { + } - position.fromArray( positions, i * 3 ); + } else { - testPoint( position, i ); + texture.image.width = texDatas.width; + texture.image.height = texDatas.height; + texture.mipmaps = texDatas.mipmaps; } - } + if ( texDatas.mipmapCount === 1 ) { - } else { + texture.minFilter = THREE.LinearFilter; - var vertices = geometry.vertices; + } - for ( var i = 0, l = vertices.length; i < l; i ++ ) { + texture.format = texDatas.format; + texture.needsUpdate = true; - testPoint( vertices[ i ], i ); + if ( onLoad ) onLoad( texture ); - } + }, onProgress, onError ); } - }; + return texture; -}() ); + }, -THREE.Points.prototype.clone = function () { + setCrossOrigin: function ( value ) { - return new this.constructor( this.geometry, this.material ).copy( this ); + this.crossOrigin = value; -}; + } -THREE.Points.prototype.toJSON = function ( meta ) { +}; - var data = THREE.Object3D.prototype.toJSON.call( this, meta ); +// File:src/materials/Material.js - // only serialize if not in meta geometries cache - if ( meta.geometries[ this.geometry.uuid ] === undefined ) { +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ - meta.geometries[ this.geometry.uuid ] = this.geometry.toJSON(); +THREE.Material = function () { - } + Object.defineProperty( this, 'id', { value: THREE.MaterialIdCount ++ } ); - // only serialize if not in meta materials cache - if ( meta.materials[ this.material.uuid ] === undefined ) { + this.uuid = THREE.Math.generateUUID(); - meta.materials[ this.material.uuid ] = this.material.toJSON(); + this.name = ''; + this.type = 'Material'; - } + this.side = THREE.FrontSide; - data.object.geometry = this.geometry.uuid; - data.object.material = this.material.uuid; + this.opacity = 1; + this.transparent = false; - return data; + this.blending = THREE.NormalBlending; -}; + this.blendSrc = THREE.SrcAlphaFactor; + this.blendDst = THREE.OneMinusSrcAlphaFactor; + this.blendEquation = THREE.AddEquation; + this.blendSrcAlpha = null; + this.blendDstAlpha = null; + this.blendEquationAlpha = null; -// Backwards compatibility + this.depthFunc = THREE.LessEqualDepth; + this.depthTest = true; + this.depthWrite = true; -THREE.PointCloud = function ( geometry, material ) { + this.colorWrite = true; - console.warn( 'THREE.PointCloud has been renamed to THREE.Points.' ); - return new THREE.Points( geometry, material ); + this.precision = null; // override the renderer's default precision for this material -}; + this.polygonOffset = false; + this.polygonOffsetFactor = 0; + this.polygonOffsetUnits = 0; -THREE.ParticleSystem = function ( geometry, material ) { + this.alphaTest = 0; - console.warn( 'THREE.ParticleSystem has been renamed to THREE.Points.' ); - return new THREE.Points( geometry, material ); + this.overdraw = 0; // Overdrawn pixels (typically between 0 and 1) for fixing antialiasing gaps in CanvasRenderer + + this.visible = true; + + this._needsUpdate = true; }; -// File:src/objects/Line.js +THREE.Material.prototype = { -/** - * @author mrdoob / http://mrdoob.com/ - */ + constructor: THREE.Material, -THREE.Line = function ( geometry, material, mode ) { + get needsUpdate () { - if ( mode === 1 ) { + return this._needsUpdate; - console.warn( 'THREE.Line: parameter THREE.LinePieces no longer supported. Created THREE.LineSegments instead.' ); - return new THREE.LineSegments( geometry, material ); + }, - } + set needsUpdate ( value ) { - THREE.Object3D.call( this ); + if ( value === true ) this.update(); - this.type = 'Line'; + this._needsUpdate = value; - this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); - this.material = material !== undefined ? material : new THREE.LineBasicMaterial( { color: Math.random() * 0xffffff } ); + }, -}; + setValues: function ( values ) { -THREE.Line.prototype = Object.create( THREE.Object3D.prototype ); -THREE.Line.prototype.constructor = THREE.Line; + if ( values === undefined ) return; -THREE.Line.prototype.raycast = ( function () { + for ( var key in values ) { - var inverseMatrix = new THREE.Matrix4(); - var ray = new THREE.Ray(); - var sphere = new THREE.Sphere(); + var newValue = values[ key ]; - return function raycast( raycaster, intersects ) { + if ( newValue === undefined ) { - var precision = raycaster.linePrecision; - var precisionSq = precision * precision; + console.warn( "THREE.Material: '" + key + "' parameter is undefined." ); + continue; - var geometry = this.geometry; + } - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + var currentValue = this[ key ]; - // Checking boundingSphere distance to ray + if ( currentValue === undefined ) { - sphere.copy( geometry.boundingSphere ); - sphere.applyMatrix4( this.matrixWorld ); + console.warn( "THREE." + this.type + ": '" + key + "' is not a property of this material." ); + continue; - if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) { + } - return; + if ( currentValue instanceof THREE.Color ) { + + currentValue.set( newValue ); + + } else if ( currentValue instanceof THREE.Vector3 && newValue instanceof THREE.Vector3 ) { + + currentValue.copy( newValue ); + + } else if ( key === 'overdraw' ) { + + // ensure overdraw is backwards-compatible with legacy boolean type + this[ key ] = Number( newValue ); + + } else { + + this[ key ] = newValue; + + } } - inverseMatrix.getInverse( this.matrixWorld ); - ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); + }, - var vStart = new THREE.Vector3(); - var vEnd = new THREE.Vector3(); - var interSegment = new THREE.Vector3(); - var interRay = new THREE.Vector3(); - var step = this instanceof THREE.LineSegments ? 2 : 1; + toJSON: function ( meta ) { - if ( geometry instanceof THREE.BufferGeometry ) { + var data = { + metadata: { + version: 4.4, + type: 'Material', + generator: 'Material.toJSON' + } + }; - var index = geometry.index; - var attributes = geometry.attributes; + // standard Material serialization + data.uuid = this.uuid; + data.type = this.type; + if ( this.name !== '' ) data.name = this.name; - if ( index !== null ) { + if ( this.color instanceof THREE.Color ) data.color = this.color.getHex(); + if ( this.emissive instanceof THREE.Color ) data.emissive = this.emissive.getHex(); + if ( this.specular instanceof THREE.Color ) data.specular = this.specular.getHex(); + if ( this.shininess !== undefined ) data.shininess = this.shininess; - var indices = index.array; - var positions = attributes.position.array; + if ( this.map instanceof THREE.Texture ) data.map = this.map.toJSON( meta ).uuid; + if ( this.alphaMap instanceof THREE.Texture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid; + if ( this.lightMap instanceof THREE.Texture ) data.lightMap = this.lightMap.toJSON( meta ).uuid; + if ( this.bumpMap instanceof THREE.Texture ) { - for ( var i = 0, l = indices.length - 1; i < l; i += step ) { + data.bumpMap = this.bumpMap.toJSON( meta ).uuid; + data.bumpScale = this.bumpScale; - var a = indices[ i ]; - var b = indices[ i + 1 ]; + } + if ( this.normalMap instanceof THREE.Texture ) { - vStart.fromArray( positions, a * 3 ); - vEnd.fromArray( positions, b * 3 ); + data.normalMap = this.normalMap.toJSON( meta ).uuid; + data.normalScale = this.normalScale; // Removed for now, causes issue in editor ui.js - var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); + } + if ( this.displacementMap instanceof THREE.Texture ) { - if ( distSq > precisionSq ) continue; + data.displacementMap = this.displacementMap.toJSON( meta ).uuid; + data.displacementScale = this.displacementScale; + data.displacementBias = this.displacementBias; - interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation + } + if ( this.specularMap instanceof THREE.Texture ) data.specularMap = this.specularMap.toJSON( meta ).uuid; + if ( this.envMap instanceof THREE.Texture ) { - var distance = raycaster.ray.origin.distanceTo( interRay ); + data.envMap = this.envMap.toJSON( meta ).uuid; + data.reflectivity = this.reflectivity; // Scale behind envMap - if ( distance < raycaster.near || distance > raycaster.far ) continue; + } - intersects.push( { + if ( this.size !== undefined ) data.size = this.size; + if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation; - distance: distance, - // What do we want? intersection point on the ray or on the segment?? - // point: raycaster.ray.at( distance ), - point: interSegment.clone().applyMatrix4( this.matrixWorld ), - index: i, - face: null, - faceIndex: null, - object: this + if ( this.vertexColors !== undefined && this.vertexColors !== THREE.NoColors ) data.vertexColors = this.vertexColors; + if ( this.shading !== undefined && this.shading !== THREE.SmoothShading ) data.shading = this.shading; + if ( this.blending !== undefined && this.blending !== THREE.NormalBlending ) data.blending = this.blending; + if ( this.side !== undefined && this.side !== THREE.FrontSide ) data.side = this.side; - } ); + if ( this.opacity < 1 ) data.opacity = this.opacity; + if ( this.transparent === true ) data.transparent = this.transparent; + if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest; + if ( this.wireframe === true ) data.wireframe = this.wireframe; + if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth; - } + return data; - } else { + }, - var positions = attributes.position.array; + clone: function () { - for ( var i = 0, l = positions.length / 3 - 1; i < l; i += step ) { + return new this.constructor().copy( this ); - vStart.fromArray( positions, 3 * i ); - vEnd.fromArray( positions, 3 * i + 3 ); + }, - var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); + copy: function ( source ) { - if ( distSq > precisionSq ) continue; + this.name = source.name; - interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation + this.side = source.side; - var distance = raycaster.ray.origin.distanceTo( interRay ); + this.opacity = source.opacity; + this.transparent = source.transparent; - if ( distance < raycaster.near || distance > raycaster.far ) continue; + this.blending = source.blending; - intersects.push( { + this.blendSrc = source.blendSrc; + this.blendDst = source.blendDst; + this.blendEquation = source.blendEquation; + this.blendSrcAlpha = source.blendSrcAlpha; + this.blendDstAlpha = source.blendDstAlpha; + this.blendEquationAlpha = source.blendEquationAlpha; - distance: distance, - // What do we want? intersection point on the ray or on the segment?? - // point: raycaster.ray.at( distance ), - point: interSegment.clone().applyMatrix4( this.matrixWorld ), - index: i, - face: null, - faceIndex: null, - object: this + this.depthFunc = source.depthFunc; + this.depthTest = source.depthTest; + this.depthWrite = source.depthWrite; - } ); + this.precision = source.precision; - } + this.polygonOffset = source.polygonOffset; + this.polygonOffsetFactor = source.polygonOffsetFactor; + this.polygonOffsetUnits = source.polygonOffsetUnits; - } + this.alphaTest = source.alphaTest; - } else if ( geometry instanceof THREE.Geometry ) { + this.overdraw = source.overdraw; - var vertices = geometry.vertices; - var nbVertices = vertices.length; + this.visible = source.visible; - for ( var i = 0; i < nbVertices - 1; i += step ) { + return this; - var distSq = ray.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment ); + }, - if ( distSq > precisionSq ) continue; + update: function () { - interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation + this.dispatchEvent( { type: 'update' } ); - var distance = raycaster.ray.origin.distanceTo( interRay ); + }, - if ( distance < raycaster.near || distance > raycaster.far ) continue; + dispose: function () { - intersects.push( { + this.dispatchEvent( { type: 'dispose' } ); - distance: distance, - // What do we want? intersection point on the ray or on the segment?? - // point: raycaster.ray.at( distance ), - point: interSegment.clone().applyMatrix4( this.matrixWorld ), - index: i, - face: null, - faceIndex: null, - object: this + }, - } ); + // Deprecated - } + get wrapAround () { - } + console.warn( 'THREE.' + this.type + ': .wrapAround has been removed.' ); - }; + }, -}() ); + set wrapAround ( boolean ) { -THREE.Line.prototype.clone = function () { + console.warn( 'THREE.' + this.type + ': .wrapAround has been removed.' ); - return new this.constructor( this.geometry, this.material ).copy( this ); + }, + + get wrapRGB () { + + console.warn( 'THREE.' + this.type + ': .wrapRGB has been removed.' ); + return new THREE.Color(); + + } }; -THREE.Line.prototype.toJSON = function ( meta ) { +THREE.EventDispatcher.prototype.apply( THREE.Material.prototype ); - var data = THREE.Object3D.prototype.toJSON.call( this, meta ); +THREE.MaterialIdCount = 0; - // only serialize if not in meta geometries cache - if ( meta.geometries[ this.geometry.uuid ] === undefined ) { +// File:src/materials/LineBasicMaterial.js - meta.geometries[ this.geometry.uuid ] = this.geometry.toJSON(); +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * linewidth: , + * linecap: "round", + * linejoin: "round", + * + * vertexColors: + * + * fog: + * } + */ - } +THREE.LineBasicMaterial = function ( parameters ) { - // only serialize if not in meta materials cache - if ( meta.materials[ this.material.uuid ] === undefined ) { + THREE.Material.call( this ); - meta.materials[ this.material.uuid ] = this.material.toJSON(); + this.type = 'LineBasicMaterial'; - } + this.color = new THREE.Color( 0xffffff ); - data.object.geometry = this.geometry.uuid; - data.object.material = this.material.uuid; + this.linewidth = 1; + this.linecap = 'round'; + this.linejoin = 'round'; - return data; + this.vertexColors = THREE.NoColors; + + this.fog = true; + + this.setValues( parameters ); }; -// DEPRECATED +THREE.LineBasicMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.LineBasicMaterial.prototype.constructor = THREE.LineBasicMaterial; -THREE.LineStrip = 0; -THREE.LinePieces = 1; +THREE.LineBasicMaterial.prototype.copy = function ( source ) { -// File:src/objects/LineSegments.js + THREE.Material.prototype.copy.call( this, source ); + + this.color.copy( source.color ); + + this.linewidth = source.linewidth; + this.linecap = source.linecap; + this.linejoin = source.linejoin; + + this.vertexColors = source.vertexColors; + + this.fog = source.fog; + + return this; + +}; + +// File:src/materials/LineDashedMaterial.js /** - * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * linewidth: , + * + * scale: , + * dashSize: , + * gapSize: , + * + * vertexColors: + * + * fog: + * } */ -THREE.LineSegments = function ( geometry, material ) { +THREE.LineDashedMaterial = function ( parameters ) { - THREE.Line.call( this, geometry, material ); + THREE.Material.call( this ); - this.type = 'LineSegments'; + this.type = 'LineDashedMaterial'; + + this.color = new THREE.Color( 0xffffff ); + + this.linewidth = 1; + + this.scale = 1; + this.dashSize = 3; + this.gapSize = 1; + + this.vertexColors = false; + + this.fog = true; + + this.setValues( parameters ); }; -THREE.LineSegments.prototype = Object.create( THREE.Line.prototype ); -THREE.LineSegments.prototype.constructor = THREE.LineSegments; +THREE.LineDashedMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.LineDashedMaterial.prototype.constructor = THREE.LineDashedMaterial; -// File:src/objects/Mesh.js +THREE.LineDashedMaterial.prototype.copy = function ( source ) { + + THREE.Material.prototype.copy.call( this, source ); + + this.color.copy( source.color ); + + this.linewidth = source.linewidth; + + this.scale = source.scale; + this.dashSize = source.dashSize; + this.gapSize = source.gapSize; + + this.vertexColors = source.vertexColors; + + this.fog = source.fog; + + return this; + +}; + +// File:src/materials/MeshBasicMaterial.js /** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ - * @author mikael emtinger / http://gomo.se/ - * @author jonobr1 / http://jonobr1.com/ + * + * parameters = { + * color: , + * opacity: , + * map: new THREE.Texture( ), + * + * aoMap: new THREE.Texture( ), + * aoMapIntensity: + * + * specularMap: new THREE.Texture( ), + * + * alphaMap: new THREE.Texture( ), + * + * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.Multiply, + * reflectivity: , + * refractionRatio: , + * + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: , + * + * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, + * + * skinning: , + * morphTargets: , + * + * fog: + * } */ -THREE.Mesh = function ( geometry, material ) { +THREE.MeshBasicMaterial = function ( parameters ) { - THREE.Object3D.call( this ); + THREE.Material.call( this ); - this.type = 'Mesh'; + this.type = 'MeshBasicMaterial'; - this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); - this.material = material !== undefined ? material : new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff } ); + this.color = new THREE.Color( 0xffffff ); // emissive - this.updateMorphTargets(); + this.map = null; + + this.aoMap = null; + this.aoMapIntensity = 1.0; + + 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.setValues( parameters ); }; -THREE.Mesh.prototype = Object.create( THREE.Object3D.prototype ); -THREE.Mesh.prototype.constructor = THREE.Mesh; +THREE.MeshBasicMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.MeshBasicMaterial.prototype.constructor = THREE.MeshBasicMaterial; -THREE.Mesh.prototype.updateMorphTargets = function () { +THREE.MeshBasicMaterial.prototype.copy = function ( source ) { + + THREE.Material.prototype.copy.call( this, source ); - if ( this.geometry.morphTargets !== undefined && this.geometry.morphTargets.length > 0 ) { + this.color.copy( source.color ); - this.morphTargetBase = - 1; - this.morphTargetInfluences = []; - this.morphTargetDictionary = {}; + this.map = source.map; - for ( var m = 0, ml = this.geometry.morphTargets.length; m < ml; m ++ ) { + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; - this.morphTargetInfluences.push( 0 ); - this.morphTargetDictionary[ this.geometry.morphTargets[ m ].name ] = m; + this.specularMap = source.specularMap; - } + this.alphaMap = source.alphaMap; - } + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; + + this.fog = source.fog; + + this.shading = source.shading; + + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; + + this.vertexColors = source.vertexColors; + + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; + + return this; }; -THREE.Mesh.prototype.getMorphTargetIndexByName = function ( name ) { +// File:src/materials/MeshLambertMaterial.js - if ( this.morphTargetDictionary[ name ] !== undefined ) { +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * emissive: , + * opacity: , + * + * map: new THREE.Texture( ), + * + * specularMap: new THREE.Texture( ), + * + * alphaMap: new THREE.Texture( ), + * + * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.Multiply, + * reflectivity: , + * refractionRatio: , + * + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: , + * + * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, + * + * skinning: , + * morphTargets: , + * morphNormals: , + * + * fog: + * } + */ - return this.morphTargetDictionary[ name ]; +THREE.MeshLambertMaterial = function ( parameters ) { - } + THREE.Material.call( this ); - console.warn( 'THREE.Mesh.getMorphTargetIndexByName: morph target ' + name + ' does not exist. Returning 0.' ); + this.type = 'MeshLambertMaterial'; - return 0; + this.color = new THREE.Color( 0xffffff ); // diffuse + this.emissive = new THREE.Color( 0x000000 ); + + this.map = 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.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.constructor = THREE.MeshLambertMaterial; -THREE.Mesh.prototype.raycast = ( function () { +THREE.MeshLambertMaterial.prototype.copy = function ( source ) { - var inverseMatrix = new THREE.Matrix4(); - var ray = new THREE.Ray(); - var sphere = new THREE.Sphere(); + THREE.Material.prototype.copy.call( this, source ); - var vA = new THREE.Vector3(); - var vB = new THREE.Vector3(); - var vC = new THREE.Vector3(); + this.color.copy( source.color ); + this.emissive.copy( source.emissive ); - var tempA = new THREE.Vector3(); - var tempB = new THREE.Vector3(); - var tempC = new THREE.Vector3(); + this.map = source.map; - var uvA = new THREE.Vector2(); - var uvB = new THREE.Vector2(); - var uvC = new THREE.Vector2(); + this.specularMap = source.specularMap; - var barycoord = new THREE.Vector3(); + this.alphaMap = source.alphaMap; - var intersectionPoint = new THREE.Vector3(); - var intersectionPointWorld = new THREE.Vector3(); + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; - function uvIntersection( point, p1, p2, p3, uv1, uv2, uv3 ) { + this.fog = source.fog; - THREE.Triangle.barycoordFromPoint( point, p1, p2, p3, barycoord ); + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; - uv1.multiplyScalar( barycoord.x ); - uv2.multiplyScalar( barycoord.y ); - uv3.multiplyScalar( barycoord.z ); + this.vertexColors = source.vertexColors; - uv1.add( uv2 ).add( uv3 ); + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; + this.morphNormals = source.morphNormals; - return uv1.clone(); + return this; - } +}; - return function raycast( raycaster, intersects ) { +// File:src/materials/MeshPhongMaterial.js - var geometry = this.geometry; - var material = this.material; +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * emissive: , + * specular: , + * shininess: , + * opacity: , + * + * map: new THREE.Texture( ), + * + * lightMap: new THREE.Texture( ), + * lightMapIntensity: + * + * aoMap: new THREE.Texture( ), + * aoMapIntensity: + * + * emissiveMap: new THREE.Texture( ), + * + * bumpMap: new THREE.Texture( ), + * bumpScale: , + * + * normalMap: new THREE.Texture( ), + * normalScale: , + * + * displacementMap: new THREE.Texture( ), + * displacementScale: , + * displacementBias: , + * + * 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: + * } + */ - if ( material === undefined ) return; +THREE.MeshPhongMaterial = function ( parameters ) { - // Checking boundingSphere distance to ray + THREE.Material.call( this ); - if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + this.type = 'MeshPhongMaterial'; - sphere.copy( geometry.boundingSphere ); - sphere.applyMatrix4( this.matrixWorld ); + this.color = new THREE.Color( 0xffffff ); // diffuse + this.emissive = new THREE.Color( 0x000000 ); + this.specular = new THREE.Color( 0x111111 ); + this.shininess = 30; - if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) { + this.metal = false; - return; + this.map = null; - } + this.lightMap = null; + this.lightMapIntensity = 1.0; - // Check boundingBox before continuing + this.aoMap = null; + this.aoMapIntensity = 1.0; - inverseMatrix.getInverse( this.matrixWorld ); - ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); + this.emissiveMap = null; - if ( geometry.boundingBox !== null ) { + this.bumpMap = null; + this.bumpScale = 1; - if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) { + this.normalMap = null; + this.normalScale = new THREE.Vector2( 1, 1 ); - return; + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - } + this.specularMap = null; - } + this.alphaMap = null; - var a, b, c; + this.envMap = null; + this.combine = THREE.MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; - if ( geometry instanceof THREE.BufferGeometry ) { + this.fog = true; - var index = geometry.index; - var attributes = geometry.attributes; + this.shading = THREE.SmoothShading; - if ( index !== null ) { + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; - var indices = index.array; - var positions = attributes.position.array; + this.vertexColors = THREE.NoColors; - for ( var i = 0, l = indices.length; i < l; i += 3 ) { + this.skinning = false; + this.morphTargets = false; + this.morphNormals = false; - a = indices[ i ]; - b = indices[ i + 1 ]; - c = indices[ i + 2 ]; + this.setValues( parameters ); - vA.fromArray( positions, a * 3 ); - vB.fromArray( positions, b * 3 ); - vC.fromArray( positions, c * 3 ); +}; - if ( material.side === THREE.BackSide ) { +THREE.MeshPhongMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.MeshPhongMaterial.prototype.constructor = THREE.MeshPhongMaterial; - if ( ray.intersectTriangle( vC, vB, vA, true, intersectionPoint ) === null ) continue; +THREE.MeshPhongMaterial.prototype.copy = function ( source ) { - } else { + THREE.Material.prototype.copy.call( this, source ); - if ( ray.intersectTriangle( vA, vB, vC, material.side !== THREE.DoubleSide, intersectionPoint ) === null ) continue; + this.color.copy( source.color ); + this.emissive.copy( source.emissive ); + this.specular.copy( source.specular ); + this.shininess = source.shininess; - } + this.metal = source.metal; - intersectionPointWorld.copy( intersectionPoint ); - intersectionPointWorld.applyMatrix4( this.matrixWorld ); + this.map = source.map; - var distance = raycaster.ray.origin.distanceTo( intersectionPointWorld ); + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; - if ( distance < raycaster.near || distance > raycaster.far ) continue; + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; - var uv; + this.emissiveMap = source.emissiveMap; - if ( attributes.uv !== undefined ) { + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; - var uvs = attributes.uv.array; - uvA.fromArray( uvs, a * 2 ); - uvB.fromArray( uvs, b * 2 ); - uvC.fromArray( uvs, c * 2 ); - uv = uvIntersection( intersectionPoint, vA, vB, vC, uvA, uvB, uvC ); + this.normalMap = source.normalMap; + this.normalScale.copy( source.normalScale ); - } + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; - intersects.push( { + this.specularMap = source.specularMap; - distance: distance, - point: intersectionPointWorld.clone(), - uv: uv, - face: new THREE.Face3( a, b, c, THREE.Triangle.normal( vA, vB, vC ) ), - faceIndex: Math.floor( i / 3 ), // triangle number in indices buffer semantics - object: this + this.alphaMap = source.alphaMap; - } ); + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; - } + this.fog = source.fog; - } else { + this.shading = source.shading; - var positions = attributes.position.array; + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; - for ( var i = 0, l = positions.length; i < l; i += 9 ) { + this.vertexColors = source.vertexColors; - vA.fromArray( positions, i ); - vB.fromArray( positions, i + 3 ); - vC.fromArray( positions, i + 6 ); + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; + this.morphNormals = source.morphNormals; - if ( material.side === THREE.BackSide ) { + return this; - if ( ray.intersectTriangle( vC, vB, vA, true, intersectionPoint ) === null ) continue; +}; - } else { +// File:src/materials/MeshDepthMaterial.js - if ( ray.intersectTriangle( vA, vB, vC, material.side !== THREE.DoubleSide, intersectionPoint ) === null ) continue; +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * opacity: , + * + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: + * } + */ - } +THREE.MeshDepthMaterial = function ( parameters ) { - intersectionPointWorld.copy( intersectionPoint ); - intersectionPointWorld.applyMatrix4( this.matrixWorld ); + THREE.Material.call( this ); - var distance = raycaster.ray.origin.distanceTo( intersectionPointWorld ); + this.type = 'MeshDepthMaterial'; - if ( distance < raycaster.near || distance > raycaster.far ) continue; + this.morphTargets = false; + this.wireframe = false; + this.wireframeLinewidth = 1; - var uv; + this.setValues( parameters ); - if ( attributes.uv !== undefined ) { +}; - var uvs = attributes.uv.array; - uvA.fromArray( uvs, i ); - uvB.fromArray( uvs, i + 2 ); - uvC.fromArray( uvs, i + 4 ); - uv = uvIntersection( intersectionPoint, vA, vB, vC, uvA, uvB, uvC ); +THREE.MeshDepthMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.MeshDepthMaterial.prototype.constructor = THREE.MeshDepthMaterial; - } +THREE.MeshDepthMaterial.prototype.copy = function ( source ) { - a = i / 3; - b = a + 1; - c = a + 2; + THREE.Material.prototype.copy.call( this, source ); - intersects.push( { + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; - distance: distance, - point: intersectionPointWorld.clone(), - uv: uv, - face: new THREE.Face3( a, b, c, THREE.Triangle.normal( vA, vB, vC ) ), - index: a, // triangle number in positions buffer semantics - object: this + return this; - } ); +}; - } +// File:src/materials/MeshNormalMaterial.js - } +/** + * @author mrdoob / http://mrdoob.com/ + * + * parameters = { + * opacity: , + * + * shading: THREE.FlatShading, + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: + * } + */ - } else if ( geometry instanceof THREE.Geometry ) { +THREE.MeshNormalMaterial = function ( parameters ) { - var isFaceMaterial = material instanceof THREE.MeshFaceMaterial; - var materials = isFaceMaterial === true ? material.materials : null; + THREE.Material.call( this, parameters ); - var vertices = geometry.vertices; - var faces = geometry.faces; + this.type = 'MeshNormalMaterial'; - for ( var f = 0, fl = faces.length; f < fl; f ++ ) { + this.wireframe = false; + this.wireframeLinewidth = 1; - var face = faces[ f ]; - var faceMaterial = isFaceMaterial === true ? materials[ face.materialIndex ] : material; + this.morphTargets = false; - if ( faceMaterial === undefined ) continue; + this.setValues( parameters ); - a = vertices[ face.a ]; - b = vertices[ face.b ]; - c = vertices[ face.c ]; +}; - if ( faceMaterial.morphTargets === true ) { +THREE.MeshNormalMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.MeshNormalMaterial.prototype.constructor = THREE.MeshNormalMaterial; - var morphTargets = geometry.morphTargets; - var morphInfluences = this.morphTargetInfluences; +THREE.MeshNormalMaterial.prototype.copy = function ( source ) { - vA.set( 0, 0, 0 ); - vB.set( 0, 0, 0 ); - vC.set( 0, 0, 0 ); + THREE.Material.prototype.copy.call( this, source ); - for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) { + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; - var influence = morphInfluences[ t ]; + return this; - if ( influence === 0 ) continue; +}; - var targets = morphTargets[ t ].vertices; +// File:src/materials/MultiMaterial.js - vA.addScaledVector( tempA.subVectors( targets[ face.a ], a ), influence ); - vB.addScaledVector( tempB.subVectors( targets[ face.b ], b ), influence ); - vC.addScaledVector( tempC.subVectors( targets[ face.c ], c ), influence ); +/** + * @author mrdoob / http://mrdoob.com/ + */ - } +THREE.MultiMaterial = function ( materials ) { + + this.uuid = THREE.Math.generateUUID(); + + this.type = 'MultiMaterial'; + + this.materials = materials instanceof Array ? materials : []; + + this.visible = true; + +}; + +THREE.MultiMaterial.prototype = { + + constructor: THREE.MultiMaterial, + + toJSON: function () { + + 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 ++ ) { + + output.materials.push( this.materials[ i ].toJSON() ); + + } + + output.visible = this.visible; - vA.add( a ); - vB.add( b ); - vC.add( c ); + return output; - a = vA; - b = vB; - c = vC; + }, - } + clone: function () { - if ( faceMaterial.side === THREE.BackSide ) { + var material = new this.constructor(); - if ( ray.intersectTriangle( c, b, a, true, intersectionPoint ) === null ) continue; + for ( var i = 0; i < this.materials.length; i ++ ) { - } else { + material.materials.push( this.materials[ i ].clone() ); - if ( ray.intersectTriangle( a, b, c, faceMaterial.side !== THREE.DoubleSide, intersectionPoint ) === null ) continue; + } - } + material.visible = this.visible; - intersectionPointWorld.copy( intersectionPoint ); - intersectionPointWorld.applyMatrix4( this.matrixWorld ); + return material; - var distance = raycaster.ray.origin.distanceTo( intersectionPointWorld ); + } - if ( distance < raycaster.near || distance > raycaster.far ) continue; +}; - var uv; +// backwards compatibility - if ( geometry.faceVertexUvs[ 0 ].length > 0 ) { +THREE.MeshFaceMaterial = THREE.MultiMaterial; - var uvs = geometry.faceVertexUvs[ 0 ][ f ]; - uvA.copy( uvs[ 0 ] ); - uvB.copy( uvs[ 1 ] ); - uvC.copy( uvs[ 2 ] ); - uv = uvIntersection( intersectionPoint, a, b, c, uvA, uvB, uvC ); +// File:src/materials/PointsMaterial.js - } +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * map: new THREE.Texture( ), + * + * size: , + * sizeAttenuation: , + * + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * vertexColors: , + * + * fog: + * } + */ - intersects.push( { +THREE.PointsMaterial = function ( parameters ) { - distance: distance, - point: intersectionPointWorld.clone(), - uv: uv, - face: face, - faceIndex: f, - object: this + THREE.Material.call( this ); - } ); + this.type = 'PointsMaterial'; - } + this.color = new THREE.Color( 0xffffff ); - } + this.map = null; - }; + this.size = 1; + this.sizeAttenuation = true; -}() ); + this.vertexColors = THREE.NoColors; -THREE.Mesh.prototype.clone = function () { + this.fog = true; - return new this.constructor( this.geometry, this.material ).copy( this ); + this.setValues( parameters ); }; -THREE.Mesh.prototype.toJSON = function ( meta ) { - - var data = THREE.Object3D.prototype.toJSON.call( this, meta ); +THREE.PointsMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.PointsMaterial.prototype.constructor = THREE.PointsMaterial; - // only serialize if not in meta geometries cache - if ( meta.geometries[ this.geometry.uuid ] === undefined ) { +THREE.PointsMaterial.prototype.copy = function ( source ) { - meta.geometries[ this.geometry.uuid ] = this.geometry.toJSON( meta ); + THREE.Material.prototype.copy.call( this, source ); - } + this.color.copy( source.color ); - // only serialize if not in meta materials cache - if ( meta.materials[ this.material.uuid ] === undefined ) { + this.map = source.map; - meta.materials[ this.material.uuid ] = this.material.toJSON( meta ); + this.size = source.size; + this.sizeAttenuation = source.sizeAttenuation; - } + this.vertexColors = source.vertexColors; - data.object.geometry = this.geometry.uuid; - data.object.material = this.material.uuid; + this.fog = source.fog; - return data; + return this; }; -// File:src/objects/Bone.js +// backwards compatibility -/** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - * @author ikerr / http://verold.com - */ +THREE.PointCloudMaterial = function ( parameters ) { -THREE.Bone = function ( skin ) { + console.warn( 'THREE.PointCloudMaterial has been renamed to THREE.PointsMaterial.' ); + return new THREE.PointsMaterial( parameters ); - THREE.Object3D.call( this ); +}; - this.type = 'Bone'; +THREE.ParticleBasicMaterial = function ( parameters ) { - this.skin = skin; + console.warn( 'THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial.' ); + return new THREE.PointsMaterial( parameters ); }; -THREE.Bone.prototype = Object.create( THREE.Object3D.prototype ); -THREE.Bone.prototype.constructor = THREE.Bone; +THREE.ParticleSystemMaterial = function ( parameters ) { -THREE.Bone.prototype.copy = function ( source ) { - - THREE.Object3D.prototype.copy.call( this, source ); - - this.skin = source.skin; - - return this; + console.warn( 'THREE.ParticleSystemMaterial has been renamed to THREE.PointsMaterial.' ); + return new THREE.PointsMaterial( parameters ); }; -// File:src/objects/Skeleton.js +// File:src/materials/ShaderMaterial.js /** - * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ - * @author michael guerrero / http://realitymeltdown.com - * @author ikerr / http://verold.com + * + * parameters = { + * defines: { "label" : "value" }, + * uniforms: { "parameter1": { type: "f", value: 1.0 }, "parameter2": { type: "i" value2: 2 } }, + * + * fragmentShader: , + * vertexShader: , + * + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: , + * + * lights: , + * + * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, + * + * skinning: , + * morphTargets: , + * morphNormals: , + * + * fog: + * } */ -THREE.Skeleton = function ( bones, boneInverses, useVertexTexture ) { - - this.useVertexTexture = useVertexTexture !== undefined ? useVertexTexture : true; - - this.identityMatrix = new THREE.Matrix4(); - - // copy the bone array - - bones = bones || []; - - this.bones = bones.slice( 0 ); - - // create a bone texture or an array of floats - - if ( this.useVertexTexture ) { - - // layout (1 matrix = 4 pixels) - // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) - // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) - // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) - // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) - // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) +THREE.ShaderMaterial = function ( parameters ) { - - var size = Math.sqrt( this.bones.length * 4 ); // 4 pixels needed for 1 matrix - size = THREE.Math.nextPowerOfTwo( Math.ceil( size ) ); - size = Math.max( size, 4 ); + THREE.Material.call( this ); - this.boneTextureWidth = size; - this.boneTextureHeight = size; + this.type = 'ShaderMaterial'; - this.boneMatrices = new Float32Array( this.boneTextureWidth * this.boneTextureHeight * 4 ); // 4 floats per RGBA pixel - this.boneTexture = new THREE.DataTexture( this.boneMatrices, this.boneTextureWidth, this.boneTextureHeight, THREE.RGBAFormat, THREE.FloatType ); + this.defines = {}; + this.uniforms = {}; - } else { + this.vertexShader = 'void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}'; + this.fragmentShader = 'void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}'; - this.boneMatrices = new Float32Array( 16 * this.bones.length ); + this.shading = THREE.SmoothShading; - } + this.linewidth = 1; - // use the supplied bone inverses or calculate the inverses + this.wireframe = false; + this.wireframeLinewidth = 1; - if ( boneInverses === undefined ) { + this.fog = false; // set to use scene fog - this.calculateInverses(); + this.lights = false; // set to use scene lights - } else { + this.vertexColors = THREE.NoColors; // set to use "color" attribute stream - if ( this.bones.length === boneInverses.length ) { + this.skinning = false; // set to use skinning attribute streams - this.boneInverses = boneInverses.slice( 0 ); + this.morphTargets = false; // set to use morph targets + this.morphNormals = false; // set to use morph normals - } else { + this.derivatives = false; // set to use derivatives - console.warn( 'THREE.Skeleton bonInverses is the wrong length.' ); + // When rendered geometry doesn't include these attributes but the material does, + // use these default values in WebGL. This avoids errors when buffer data is missing. + this.defaultAttributeValues = { + 'color': [ 1, 1, 1 ], + 'uv': [ 0, 0 ], + 'uv2': [ 0, 0 ] + }; - this.boneInverses = []; + this.index0AttributeName = undefined; - for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + if ( parameters !== undefined ) { - this.boneInverses.push( new THREE.Matrix4() ); + if ( parameters.attributes !== undefined ) { - } + console.error( 'THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead.' ); } + this.setValues( parameters ); + } }; -THREE.Skeleton.prototype.calculateInverses = function () { - - this.boneInverses = []; - - for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { - - var inverse = new THREE.Matrix4(); +THREE.ShaderMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.ShaderMaterial.prototype.constructor = THREE.ShaderMaterial; - if ( this.bones[ b ] ) { +THREE.ShaderMaterial.prototype.copy = function ( source ) { - inverse.getInverse( this.bones[ b ].matrixWorld ); + THREE.Material.prototype.copy.call( this, source ); - } + this.fragmentShader = source.fragmentShader; + this.vertexShader = source.vertexShader; - this.boneInverses.push( inverse ); + this.uniforms = THREE.UniformsUtils.clone( source.uniforms ); - } + this.attributes = source.attributes; + this.defines = source.defines; -}; + this.shading = source.shading; -THREE.Skeleton.prototype.pose = function () { + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; - var bone; + this.fog = source.fog; - // recover the bind-time world matrices + this.lights = source.lights; - for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + this.vertexColors = source.vertexColors; - bone = this.bones[ b ]; + this.skinning = source.skinning; - if ( bone ) { + this.morphTargets = source.morphTargets; + this.morphNormals = source.morphNormals; - bone.matrixWorld.getInverse( this.boneInverses[ b ] ); + this.derivatives = source.derivatives; - } + return this; - } +}; - // compute the local matrices, positions, rotations and scales +THREE.ShaderMaterial.prototype.toJSON = function ( meta ) { - for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + var data = THREE.Material.prototype.toJSON.call( this, meta ); - bone = this.bones[ b ]; + data.uniforms = this.uniforms; + data.attributes = this.attributes; + data.vertexShader = this.vertexShader; + data.fragmentShader = this.fragmentShader; - if ( bone ) { + return data; - if ( bone.parent ) { +}; - bone.matrix.getInverse( bone.parent.matrixWorld ); - bone.matrix.multiply( bone.matrixWorld ); +// File:src/materials/RawShaderMaterial.js - } else { +/** + * @author mrdoob / http://mrdoob.com/ + */ - bone.matrix.copy( bone.matrixWorld ); +THREE.RawShaderMaterial = function ( parameters ) { - } + THREE.ShaderMaterial.call( this, parameters ); - bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); + this.type = 'RawShaderMaterial'; - } +}; - } +THREE.RawShaderMaterial.prototype = Object.create( THREE.ShaderMaterial.prototype ); +THREE.RawShaderMaterial.prototype.constructor = THREE.RawShaderMaterial; +// File:src/materials/SpriteMaterial.js -}; +/** + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * map: new THREE.Texture( ), + * + * blending: THREE.NormalBlending, + * depthTest: , + * depthWrite: , + * + * uvOffset: new THREE.Vector2(), + * uvScale: new THREE.Vector2(), + * + * fog: + * } + */ -THREE.Skeleton.prototype.update = ( function () { +THREE.SpriteMaterial = function ( parameters ) { - var offsetMatrix = new THREE.Matrix4(); + THREE.Material.call( this ); - return function update() { + this.type = 'SpriteMaterial'; - // flatten bone matrices to array + this.color = new THREE.Color( 0xffffff ); + this.map = null; - for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + this.rotation = 0; - // compute the offset between the current and the original transform + this.fog = false; - var matrix = this.bones[ b ] ? this.bones[ b ].matrixWorld : this.identityMatrix; + // set parameters - offsetMatrix.multiplyMatrices( matrix, this.boneInverses[ b ] ); - offsetMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 ); + this.setValues( parameters ); - } +}; - if ( this.useVertexTexture ) { +THREE.SpriteMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.SpriteMaterial.prototype.constructor = THREE.SpriteMaterial; - this.boneTexture.needsUpdate = true; +THREE.SpriteMaterial.prototype.copy = function ( source ) { - } + THREE.Material.prototype.copy.call( this, source ); - }; + this.color.copy( source.color ); + this.map = source.map; -} )(); + this.rotation = source.rotation; -THREE.Skeleton.prototype.clone = function () { + this.fog = source.fog; - return new THREE.Skeleton( this.bones, this.boneInverses, this.useVertexTexture ); + return this; }; -// File:src/objects/SkinnedMesh.js +// File:src/textures/Texture.js /** - * @author mikael emtinger / http://gomo.se/ + * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ - * @author ikerr / http://verold.com + * @author szimek / https://github.com/szimek/ */ -THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) { +THREE.Texture = function ( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { - THREE.Mesh.call( this, geometry, material ); + Object.defineProperty( this, 'id', { value: THREE.TextureIdCount ++ } ); - this.type = 'SkinnedMesh'; + this.uuid = THREE.Math.generateUUID(); - this.bindMode = "attached"; - this.bindMatrix = new THREE.Matrix4(); - this.bindMatrixInverse = new THREE.Matrix4(); + this.name = ''; + this.sourceFile = ''; - // init bones + this.image = image !== undefined ? image : THREE.Texture.DEFAULT_IMAGE; + this.mipmaps = []; - // TODO: remove bone creation as there is no reason (other than - // convenience) for THREE.SkinnedMesh to do this. + this.mapping = mapping !== undefined ? mapping : THREE.Texture.DEFAULT_MAPPING; - var bones = []; + this.wrapS = wrapS !== undefined ? wrapS : THREE.ClampToEdgeWrapping; + this.wrapT = wrapT !== undefined ? wrapT : THREE.ClampToEdgeWrapping; - if ( this.geometry && this.geometry.bones !== undefined ) { + this.magFilter = magFilter !== undefined ? magFilter : THREE.LinearFilter; + this.minFilter = minFilter !== undefined ? minFilter : THREE.LinearMipMapLinearFilter; - var bone, gbone; + this.anisotropy = anisotropy !== undefined ? anisotropy : 1; - for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++ b ) { + this.format = format !== undefined ? format : THREE.RGBAFormat; + this.type = type !== undefined ? type : THREE.UnsignedByteType; - gbone = this.geometry.bones[ b ]; + this.offset = new THREE.Vector2( 0, 0 ); + this.repeat = new THREE.Vector2( 1, 1 ); - bone = new THREE.Bone( this ); - bones.push( bone ); + this.generateMipmaps = true; + this.premultiplyAlpha = false; + this.flipY = true; + this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) - bone.name = gbone.name; - bone.position.fromArray( gbone.pos ); - bone.quaternion.fromArray( gbone.rotq ); - if ( gbone.scl !== undefined ) bone.scale.fromArray( gbone.scl ); + this.version = 0; + this.onUpdate = null; - } +}; - for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++ b ) { +THREE.Texture.DEFAULT_IMAGE = undefined; +THREE.Texture.DEFAULT_MAPPING = THREE.UVMapping; - gbone = this.geometry.bones[ b ]; +THREE.Texture.prototype = { - if ( gbone.parent !== - 1 ) { + constructor: THREE.Texture, - bones[ gbone.parent ].add( bones[ b ] ); + set needsUpdate ( value ) { - } else { + if ( value === true ) this.version ++; - this.add( bones[ b ] ); + }, - } + clone: function () { - } + return new this.constructor().copy( this ); - } + }, - this.normalizeSkinWeights(); + copy: function ( source ) { - this.updateMatrixWorld( true ); - this.bind( new THREE.Skeleton( bones, undefined, useVertexTexture ), this.matrixWorld ); + this.image = source.image; + this.mipmaps = source.mipmaps.slice( 0 ); -}; + this.mapping = source.mapping; + this.wrapS = source.wrapS; + this.wrapT = source.wrapT; -THREE.SkinnedMesh.prototype = Object.create( THREE.Mesh.prototype ); -THREE.SkinnedMesh.prototype.constructor = THREE.SkinnedMesh; + this.magFilter = source.magFilter; + this.minFilter = source.minFilter; -THREE.SkinnedMesh.prototype.bind = function( skeleton, bindMatrix ) { + this.anisotropy = source.anisotropy; - this.skeleton = skeleton; + this.format = source.format; + this.type = source.type; - if ( bindMatrix === undefined ) { + this.offset.copy( source.offset ); + this.repeat.copy( source.repeat ); - this.updateMatrixWorld( true ); - - this.skeleton.calculateInverses(); + this.generateMipmaps = source.generateMipmaps; + this.premultiplyAlpha = source.premultiplyAlpha; + this.flipY = source.flipY; + this.unpackAlignment = source.unpackAlignment; - bindMatrix = this.matrixWorld; + return this; - } + }, - this.bindMatrix.copy( bindMatrix ); - this.bindMatrixInverse.getInverse( bindMatrix ); + toJSON: function ( meta ) { -}; + if ( meta.textures[ this.uuid ] !== undefined ) { -THREE.SkinnedMesh.prototype.pose = function () { + return meta.textures[ this.uuid ]; - this.skeleton.pose(); + } -}; + function getDataURL( image ) { -THREE.SkinnedMesh.prototype.normalizeSkinWeights = function () { + var canvas; - if ( this.geometry instanceof THREE.Geometry ) { + if ( image.toDataURL !== undefined ) { - for ( var i = 0; i < this.geometry.skinIndices.length; i ++ ) { + canvas = image; - var sw = this.geometry.skinWeights[ i ]; + } else { - var scale = 1.0 / sw.lengthManhattan(); + canvas = document.createElement( 'canvas' ); + canvas.width = image.width; + canvas.height = image.height; - if ( scale !== Infinity ) { + canvas.getContext( '2d' ).drawImage( image, 0, 0, image.width, image.height ); - sw.multiplyScalar( scale ); + } + + if ( canvas.width > 2048 || canvas.height > 2048 ) { + + return canvas.toDataURL( 'image/jpeg', 0.6 ); } else { - sw.set( 1 ); // this will be normalized by the shader anyway + return canvas.toDataURL( 'image/png' ); } } - } else { - - // skinning weights assumed to be normalized for THREE.BufferGeometry + var output = { + metadata: { + version: 4.4, + type: 'Texture', + generator: 'Texture.toJSON' + }, - } + uuid: this.uuid, + name: this.name, -}; + mapping: this.mapping, -THREE.SkinnedMesh.prototype.updateMatrixWorld = function( force ) { + repeat: [ this.repeat.x, this.repeat.y ], + offset: [ this.offset.x, this.offset.y ], + wrap: [ this.wrapS, this.wrapT ], - THREE.Mesh.prototype.updateMatrixWorld.call( this, true ); + minFilter: this.minFilter, + magFilter: this.magFilter, + anisotropy: this.anisotropy + }; - if ( this.bindMode === "attached" ) { + if ( this.image !== undefined ) { - this.bindMatrixInverse.getInverse( this.matrixWorld ); + // TODO: Move to THREE.Image - } else if ( this.bindMode === "detached" ) { + var image = this.image; - this.bindMatrixInverse.getInverse( this.bindMatrix ); + if ( image.uuid === undefined ) { - } else { + image.uuid = THREE.Math.generateUUID(); // UGH - console.warn( 'THREE.SkinnedMesh unrecognized bindMode: ' + this.bindMode ); + } - } + if ( meta.images[ image.uuid ] === undefined ) { -}; + meta.images[ image.uuid ] = { + uuid: image.uuid, + url: getDataURL( image ) + }; -THREE.SkinnedMesh.prototype.clone = function() { + } - return new this.constructor( this.geometry, this.material, this.useVertexTexture ).copy( this ); + output.image = image.uuid; -}; + } -// File:src/objects/MorphAnimMesh.js + meta.textures[ this.uuid ] = output; -/** - * @author alteredq / http://alteredqualia.com/ - */ + return output; -THREE.MorphAnimMesh = function ( geometry, material ) { + }, - THREE.Mesh.call( this, geometry, material ); + dispose: function () { - this.type = 'MorphAnimMesh'; + this.dispatchEvent( { type: 'dispose' } ); - // API + }, - this.duration = 1000; // milliseconds - this.mirroredLoop = false; - this.time = 0; + transformUv: function ( uv ) { - // internals + if ( this.mapping !== THREE.UVMapping ) return; - this.lastKeyframe = 0; - this.currentKeyframe = 0; + uv.multiply( this.repeat ); + uv.add( this.offset ); - this.direction = 1; - this.directionBackwards = false; + if ( uv.x < 0 || uv.x > 1 ) { - this.setFrameRange( 0, geometry.morphTargets.length - 1 ); + switch ( this.wrapS ) { -}; + case THREE.RepeatWrapping: -THREE.MorphAnimMesh.prototype = Object.create( THREE.Mesh.prototype ); -THREE.MorphAnimMesh.prototype.constructor = THREE.MorphAnimMesh; + uv.x = uv.x - Math.floor( uv.x ); + break; -THREE.MorphAnimMesh.prototype.setFrameRange = function ( start, end ) { + case THREE.ClampToEdgeWrapping: - this.startKeyframe = start; - this.endKeyframe = end; + uv.x = uv.x < 0 ? 0 : 1; + break; - this.length = this.endKeyframe - this.startKeyframe + 1; + case THREE.MirroredRepeatWrapping: -}; + if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { -THREE.MorphAnimMesh.prototype.setDirectionForward = function () { + uv.x = Math.ceil( uv.x ) - uv.x; - this.direction = 1; - this.directionBackwards = false; + } else { -}; + uv.x = uv.x - Math.floor( uv.x ); -THREE.MorphAnimMesh.prototype.setDirectionBackward = function () { + } + break; - this.direction = - 1; - this.directionBackwards = true; + } -}; + } -THREE.MorphAnimMesh.prototype.parseAnimations = function () { + if ( uv.y < 0 || uv.y > 1 ) { - var geometry = this.geometry; + switch ( this.wrapT ) { - if ( ! geometry.animations ) geometry.animations = {}; + case THREE.RepeatWrapping: - var firstAnimation, animations = geometry.animations; + uv.y = uv.y - Math.floor( uv.y ); + break; - var pattern = /([a-z]+)_?(\d+)/; + case THREE.ClampToEdgeWrapping: - for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) { + uv.y = uv.y < 0 ? 0 : 1; + break; - var morph = geometry.morphTargets[ i ]; - var parts = morph.name.match( pattern ); + case THREE.MirroredRepeatWrapping: - if ( parts && parts.length > 1 ) { + if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { - var label = parts[ 1 ]; + uv.y = Math.ceil( uv.y ) - uv.y; - if ( ! animations[ label ] ) animations[ label ] = { start: Infinity, end: - Infinity }; + } else { - var animation = animations[ label ]; + uv.y = uv.y - Math.floor( uv.y ); - if ( i < animation.start ) animation.start = i; - if ( i > animation.end ) animation.end = i; + } + break; - if ( ! firstAnimation ) firstAnimation = label; + } } - } - - geometry.firstAnimation = firstAnimation; - -}; + if ( this.flipY ) { -THREE.MorphAnimMesh.prototype.setAnimationLabel = function ( label, start, end ) { + uv.y = 1 - uv.y; - if ( ! this.geometry.animations ) this.geometry.animations = {}; + } - this.geometry.animations[ label ] = { start: start, end: end }; + } }; -THREE.MorphAnimMesh.prototype.playAnimation = function ( label, fps ) { +THREE.EventDispatcher.prototype.apply( THREE.Texture.prototype ); - var animation = this.geometry.animations[ label ]; +THREE.TextureIdCount = 0; - if ( animation ) { +// File:src/textures/CanvasTexture.js - this.setFrameRange( animation.start, animation.end ); - this.duration = 1000 * ( ( animation.end - animation.start ) / fps ); - this.time = 0; +/** + * @author mrdoob / http://mrdoob.com/ + */ - } else { +THREE.CanvasTexture = function ( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { - console.warn( 'THREE.MorphAnimMesh: animation[' + label + '] undefined in .playAnimation()' ); + THREE.Texture.call( this, canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - } + this.needsUpdate = true; }; -THREE.MorphAnimMesh.prototype.updateAnimation = function ( delta ) { +THREE.CanvasTexture.prototype = Object.create( THREE.Texture.prototype ); +THREE.CanvasTexture.prototype.constructor = THREE.CanvasTexture; - var frameTime = this.duration / this.length; +// File:src/textures/CubeTexture.js - this.time += this.direction * delta; +/** + * @author mrdoob / http://mrdoob.com/ + */ - if ( this.mirroredLoop ) { +THREE.CubeTexture = function ( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { - if ( this.time > this.duration || this.time < 0 ) { + mapping = mapping !== undefined ? mapping : THREE.CubeReflectionMapping; - this.direction *= - 1; + THREE.Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - if ( this.time > this.duration ) { + this.images = images; + this.flipY = false; - this.time = this.duration; - this.directionBackwards = true; +}; - } +THREE.CubeTexture.prototype = Object.create( THREE.Texture.prototype ); +THREE.CubeTexture.prototype.constructor = THREE.CubeTexture; - if ( this.time < 0 ) { +THREE.CubeTexture.prototype.copy = function ( source ) { - this.time = 0; - this.directionBackwards = false; + THREE.Texture.prototype.copy.call( this, source ); + + this.images = source.images; + + return this; - } +}; +// File:src/textures/CompressedTexture.js - } +/** + * @author alteredq / http://alteredqualia.com/ + */ - } else { +THREE.CompressedTexture = function ( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) { - this.time = this.time % this.duration; + THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - if ( this.time < 0 ) this.time += this.duration; + this.image = { width: width, height: height }; + this.mipmaps = mipmaps; - } + // no flipping for cube textures + // (also flipping doesn't work for compressed textures ) - var keyframe = this.startKeyframe + THREE.Math.clamp( Math.floor( this.time / frameTime ), 0, this.length - 1 ); + this.flipY = false; - var influences = this.morphTargetInfluences; + // can't generate mipmaps for compressed textures + // mips must be embedded in DDS files - if ( keyframe !== this.currentKeyframe ) { + this.generateMipmaps = false; - influences[ this.lastKeyframe ] = 0; - influences[ this.currentKeyframe ] = 1; - influences[ keyframe ] = 0; +}; - this.lastKeyframe = this.currentKeyframe; - this.currentKeyframe = keyframe; +THREE.CompressedTexture.prototype = Object.create( THREE.Texture.prototype ); +THREE.CompressedTexture.prototype.constructor = THREE.CompressedTexture; - } +// File:src/textures/DataTexture.js - var mix = ( this.time % frameTime ) / frameTime; +/** + * @author alteredq / http://alteredqualia.com/ + */ - if ( this.directionBackwards ) { +THREE.DataTexture = function ( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) { - mix = 1 - mix; + THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - } + this.image = { data: data, width: width, height: height }; - influences[ this.currentKeyframe ] = mix; - influences[ this.lastKeyframe ] = 1 - mix; + this.magFilter = magFilter !== undefined ? magFilter : THREE.NearestFilter; + this.minFilter = minFilter !== undefined ? minFilter : THREE.NearestFilter; + + this.flipY = false; + this.generateMipmaps = false; }; -THREE.MorphAnimMesh.prototype.interpolateTargets = function ( a, b, t ) { +THREE.DataTexture.prototype = Object.create( THREE.Texture.prototype ); +THREE.DataTexture.prototype.constructor = THREE.DataTexture; - var influences = this.morphTargetInfluences; +// File:src/textures/VideoTexture.js - for ( var i = 0, l = influences.length; i < l; i ++ ) { +/** + * @author mrdoob / http://mrdoob.com/ + */ - influences[ i ] = 0; +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 ); - if ( a > - 1 ) influences[ a ] = 1 - t; - if ( b > - 1 ) influences[ b ] = t; + this.generateMipmaps = false; -}; + var scope = this; + + var update = function () { -THREE.MorphAnimMesh.prototype.copy = function ( source ) { + requestAnimationFrame( update ); - THREE.Mesh.prototype.copy.call( this, source ); + if ( video.readyState === video.HAVE_ENOUGH_DATA ) { - this.duration = source.duration; - this.mirroredLoop = source.mirroredLoop; - this.time = source.time; + scope.needsUpdate = true; - this.lastKeyframe = source.lastKeyframe; - this.currentKeyframe = source.currentKeyframe; + } - this.direction = source.direction; - this.directionBackwards = source.directionBackwards; + }; - return this; + update(); }; -// File:src/objects/LOD.js +THREE.VideoTexture.prototype = Object.create( THREE.Texture.prototype ); +THREE.VideoTexture.prototype.constructor = THREE.VideoTexture; + +// File:src/objects/Group.js /** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ * @author mrdoob / http://mrdoob.com/ */ -THREE.LOD = function () { +THREE.Group = function () { THREE.Object3D.call( this ); - this.type = 'LOD'; - - Object.defineProperties( this, { - levels: { - enumerable: true, - value: [] - }, - objects: { - get: function () { - - console.warn( 'THREE.LOD: .objects has been renamed to .levels.' ); - return this.levels; - - } - } - } ); + this.type = 'Group'; }; +THREE.Group.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Group.prototype.constructor = THREE.Group; +// File:src/objects/Points.js -THREE.LOD.prototype = Object.create( THREE.Object3D.prototype ); -THREE.LOD.prototype.constructor = THREE.LOD; - -THREE.LOD.prototype.addLevel = function ( object, distance ) { - - if ( distance === undefined ) distance = 0; +/** + * @author alteredq / http://alteredqualia.com/ + */ - distance = Math.abs( distance ); +THREE.Points = function ( geometry, material ) { - var levels = this.levels; + THREE.Object3D.call( this ); - for ( var l = 0; l < levels.length; l ++ ) { + this.type = 'Points'; - if ( distance < levels[ l ].distance ) { + this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); + this.material = material !== undefined ? material : new THREE.PointsMaterial( { color: Math.random() * 0xffffff } ); - break; +}; - } +THREE.Points.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Points.prototype.constructor = THREE.Points; - } +THREE.Points.prototype.raycast = ( function () { - levels.splice( l, 0, { distance: distance, object: object } ); + var inverseMatrix = new THREE.Matrix4(); + var ray = new THREE.Ray(); - this.add( object ); + return function raycast( raycaster, intersects ) { -}; + var object = this; + var geometry = object.geometry; + var threshold = raycaster.params.Points.threshold; -THREE.LOD.prototype.getObjectForDistance = function ( distance ) { + inverseMatrix.getInverse( this.matrixWorld ); + ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); - var levels = this.levels; + if ( geometry.boundingBox !== null ) { - for ( var i = 1, l = levels.length; i < l; i ++ ) { + if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) { - if ( distance < levels[ i ].distance ) { + return; - break; + } } - } + var localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); + var localThresholdSq = localThreshold * localThreshold; + var position = new THREE.Vector3(); - return levels[ i - 1 ].object; + function testPoint( point, index ) { -}; + var rayPointDistanceSq = ray.distanceSqToPoint( point ); -THREE.LOD.prototype.raycast = ( function () { + if ( rayPointDistanceSq < localThresholdSq ) { - var matrixPosition = new THREE.Vector3(); + var intersectPoint = ray.closestPointToPoint( point ); + intersectPoint.applyMatrix4( object.matrixWorld ); - return function raycast( raycaster, intersects ) { + var distance = raycaster.ray.origin.distanceTo( intersectPoint ); - matrixPosition.setFromMatrixPosition( this.matrixWorld ); + if ( distance < raycaster.near || distance > raycaster.far ) return; - var distance = raycaster.ray.origin.distanceTo( matrixPosition ); + intersects.push( { - this.getObjectForDistance( distance ).raycast( raycaster, intersects ); + distance: distance, + distanceToRay: Math.sqrt( rayPointDistanceSq ), + point: intersectPoint.clone(), + index: index, + face: null, + object: object - }; + } ); -}() ); + } -THREE.LOD.prototype.update = function () { + } - var v1 = new THREE.Vector3(); - var v2 = new THREE.Vector3(); + if ( geometry instanceof THREE.BufferGeometry ) { - return function update( camera ) { + var index = geometry.index; + var attributes = geometry.attributes; + var positions = attributes.position.array; - var levels = this.levels; + if ( index !== null ) { - if ( levels.length > 1 ) { + var indices = index.array; - v1.setFromMatrixPosition( camera.matrixWorld ); - v2.setFromMatrixPosition( this.matrixWorld ); + for ( var i = 0, il = indices.length; i < il; i ++ ) { - var distance = v1.distanceTo( v2 ); + var a = indices[ i ]; - levels[ 0 ].object.visible = true; + position.fromArray( positions, a * 3 ); - for ( var i = 1, l = levels.length; i < l; i ++ ) { + testPoint( position, a ); - if ( distance >= levels[ i ].distance ) { + } - levels[ i - 1 ].object.visible = false; - levels[ i ].object.visible = true; + } else { - } else { + for ( var i = 0, l = positions.length / 3; i < l; i ++ ) { - break; + position.fromArray( positions, i * 3 ); + + testPoint( position, i ); } } - for ( ; i < l; i ++ ) { + } else { - levels[ i ].object.visible = false; + var vertices = geometry.vertices; + + for ( var i = 0, l = vertices.length; i < l; i ++ ) { + + testPoint( vertices[ i ], i ); } @@ -18005,13067 +18571,13274 @@ THREE.LOD.prototype.update = function () { }; -}(); +}() ); -THREE.LOD.prototype.copy = function ( source ) { +THREE.Points.prototype.clone = function () { - THREE.Object3D.prototype.copy.call( this, source, false ); + return new this.constructor( this.geometry, this.material ).copy( this ); - var levels = source.levels; +}; - for ( var i = 0, l = levels.length; i < l; i ++ ) { +THREE.Points.prototype.toJSON = function ( meta ) { - var level = levels[ i ]; + var data = THREE.Object3D.prototype.toJSON.call( this, meta ); - this.addLevel( level.object.clone(), level.distance ); + // only serialize if not in meta geometries cache + if ( meta.geometries[ this.geometry.uuid ] === undefined ) { + + meta.geometries[ this.geometry.uuid ] = this.geometry.toJSON(); } - return this; + // only serialize if not in meta materials cache + if ( meta.materials[ this.material.uuid ] === undefined ) { -}; + meta.materials[ this.material.uuid ] = this.material.toJSON(); -THREE.LOD.prototype.toJSON = function ( meta ) { + } - var data = THREE.Object3D.prototype.toJSON.call( this, meta ); + data.object.geometry = this.geometry.uuid; + data.object.material = this.material.uuid; - data.object.levels = []; + return data; - var levels = this.levels; +}; - for ( var i = 0, l = levels.length; i < l; i ++ ) { +// Backwards compatibility - var level = levels[ i ]; +THREE.PointCloud = function ( geometry, material ) { - data.object.levels.push( { - object: level.object.uuid, - distance: level.distance - } ); + console.warn( 'THREE.PointCloud has been renamed to THREE.Points.' ); + return new THREE.Points( geometry, material ); - } +}; - return data; +THREE.ParticleSystem = function ( geometry, material ) { + + console.warn( 'THREE.ParticleSystem has been renamed to THREE.Points.' ); + return new THREE.Points( geometry, material ); }; -// File:src/objects/Sprite.js +// File:src/objects/Line.js /** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ */ -THREE.Sprite = ( function () { - - 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 ] ); +THREE.Line = function ( geometry, material, mode ) { - var geometry = new THREE.BufferGeometry(); - geometry.setIndex( new THREE.BufferAttribute( indices, 1 ) ); - geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); - geometry.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); + if ( mode === 1 ) { - return function Sprite( material ) { + console.warn( 'THREE.Line: parameter THREE.LinePieces no longer supported. Created THREE.LineSegments instead.' ); + return new THREE.LineSegments( geometry, material ); - THREE.Object3D.call( this ); + } - this.type = 'Sprite'; + THREE.Object3D.call( this ); - this.geometry = geometry; - this.material = ( material !== undefined ) ? material : new THREE.SpriteMaterial(); + this.type = 'Line'; - }; + this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); + this.material = material !== undefined ? material : new THREE.LineBasicMaterial( { color: Math.random() * 0xffffff } ); -} )(); +}; -THREE.Sprite.prototype = Object.create( THREE.Object3D.prototype ); -THREE.Sprite.prototype.constructor = THREE.Sprite; +THREE.Line.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Line.prototype.constructor = THREE.Line; -THREE.Sprite.prototype.raycast = ( function () { +THREE.Line.prototype.raycast = ( function () { - var matrixPosition = new THREE.Vector3(); + var inverseMatrix = new THREE.Matrix4(); + var ray = new THREE.Ray(); + var sphere = new THREE.Sphere(); return function raycast( raycaster, intersects ) { - matrixPosition.setFromMatrixPosition( this.matrixWorld ); + var precision = raycaster.linePrecision; + var precisionSq = precision * precision; - var distanceSq = raycaster.ray.distanceSqToPoint( matrixPosition ); - var guessSizeSq = this.scale.x * this.scale.y; + var geometry = this.geometry; - if ( distanceSq > guessSizeSq ) { + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); + + // Checking boundingSphere distance to ray + + sphere.copy( geometry.boundingSphere ); + sphere.applyMatrix4( this.matrixWorld ); + + if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) { return; } - intersects.push( { + inverseMatrix.getInverse( this.matrixWorld ); + ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); - distance: Math.sqrt( distanceSq ), - point: this.position, - face: null, - object: this + var vStart = new THREE.Vector3(); + var vEnd = new THREE.Vector3(); + var interSegment = new THREE.Vector3(); + var interRay = new THREE.Vector3(); + var step = this instanceof THREE.LineSegments ? 2 : 1; - } ); + if ( geometry instanceof THREE.BufferGeometry ) { - }; + var index = geometry.index; + var attributes = geometry.attributes; -}() ); + if ( index !== null ) { -THREE.Sprite.prototype.clone = function () { + var indices = index.array; + var positions = attributes.position.array; + + for ( var i = 0, l = indices.length - 1; i < l; i += step ) { + + var a = indices[ i ]; + var b = indices[ i + 1 ]; + + vStart.fromArray( positions, a * 3 ); + vEnd.fromArray( positions, b * 3 ); + + var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); + + if ( distSq > precisionSq ) continue; + + interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation + + var distance = raycaster.ray.origin.distanceTo( interRay ); + + if ( distance < raycaster.near || distance > raycaster.far ) continue; + + intersects.push( { + + distance: distance, + // What do we want? intersection point on the ray or on the segment?? + // point: raycaster.ray.at( distance ), + point: interSegment.clone().applyMatrix4( this.matrixWorld ), + index: i, + face: null, + faceIndex: null, + object: this + + } ); + + } + + } else { + + var positions = attributes.position.array; - return new this.constructor( this.material ).copy( this ); + for ( var i = 0, l = positions.length / 3 - 1; i < l; i += step ) { -}; + vStart.fromArray( positions, 3 * i ); + vEnd.fromArray( positions, 3 * i + 3 ); -THREE.Sprite.prototype.toJSON = function ( meta ) { + var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); - var data = THREE.Object3D.prototype.toJSON.call( this, meta ); + if ( distSq > precisionSq ) continue; - // only serialize if not in meta materials cache - if ( meta.materials[ this.material.uuid ] === undefined ) { + interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation - meta.materials[ this.material.uuid ] = this.material.toJSON(); + var distance = raycaster.ray.origin.distanceTo( interRay ); - } + if ( distance < raycaster.near || distance > raycaster.far ) continue; - data.object.material = this.material.uuid; + intersects.push( { - return data; + distance: distance, + // What do we want? intersection point on the ray or on the segment?? + // point: raycaster.ray.at( distance ), + point: interSegment.clone().applyMatrix4( this.matrixWorld ), + index: i, + face: null, + faceIndex: null, + object: this -}; + } ); -// Backwards compatibility + } -THREE.Particle = THREE.Sprite; + } -// File:src/objects/LensFlare.js + } else if ( geometry instanceof THREE.Geometry ) { -/** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - */ + var vertices = geometry.vertices; + var nbVertices = vertices.length; -THREE.LensFlare = function ( texture, size, distance, blending, color ) { + for ( var i = 0; i < nbVertices - 1; i += step ) { - THREE.Object3D.call( this ); + var distSq = ray.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment ); - this.lensFlares = []; + if ( distSq > precisionSq ) continue; - this.positionScreen = new THREE.Vector3(); - this.customUpdateCallback = undefined; + interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation - if ( texture !== undefined ) { + var distance = raycaster.ray.origin.distanceTo( interRay ); - this.add( texture, size, distance, blending, color ); + if ( distance < raycaster.near || distance > raycaster.far ) continue; - } + intersects.push( { -}; + distance: distance, + // What do we want? intersection point on the ray or on the segment?? + // point: raycaster.ray.at( distance ), + point: interSegment.clone().applyMatrix4( this.matrixWorld ), + index: i, + face: null, + faceIndex: null, + object: this -THREE.LensFlare.prototype = Object.create( THREE.Object3D.prototype ); -THREE.LensFlare.prototype.constructor = THREE.LensFlare; + } ); + } -/* - * Add: adds another flare - */ + } -THREE.LensFlare.prototype.add = function ( texture, size, distance, blending, color, opacity ) { + }; - if ( size === undefined ) size = - 1; - if ( distance === undefined ) distance = 0; - if ( opacity === undefined ) opacity = 1; - if ( color === undefined ) color = new THREE.Color( 0xffffff ); - if ( blending === undefined ) blending = THREE.NormalBlending; +}() ); - distance = Math.min( distance, Math.max( 0, distance ) ); +THREE.Line.prototype.clone = function () { - 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 in front z = 1 is back - scale: 1, // scale - rotation: 0, // rotation - opacity: opacity, // opacity - color: color, // color - blending: blending // blending - } ); + return new this.constructor( this.geometry, this.material ).copy( this ); }; -/* - * 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. - */ +THREE.Line.prototype.toJSON = function ( meta ) { -THREE.LensFlare.prototype.updateLensFlares = function () { + var data = THREE.Object3D.prototype.toJSON.call( this, meta ); - var f, fl = this.lensFlares.length; - var flare; - var vecX = - this.positionScreen.x * 2; - var vecY = - this.positionScreen.y * 2; + // only serialize if not in meta geometries cache + if ( meta.geometries[ this.geometry.uuid ] === undefined ) { - for ( f = 0; f < fl; f ++ ) { + meta.geometries[ this.geometry.uuid ] = this.geometry.toJSON(); - flare = this.lensFlares[ f ]; + } - flare.x = this.positionScreen.x + vecX * flare.distance; - flare.y = this.positionScreen.y + vecY * flare.distance; + // only serialize if not in meta materials cache + if ( meta.materials[ this.material.uuid ] === undefined ) { - flare.wantedRotation = flare.x * Math.PI * 0.25; - flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25; + meta.materials[ this.material.uuid ] = this.material.toJSON(); } + data.object.geometry = this.geometry.uuid; + data.object.material = this.material.uuid; + + return data; + }; -THREE.LensFlare.prototype.copy = function ( source ) { +// DEPRECATED - THREE.Object3D.prototype.copy.call( this, source ); +THREE.LineStrip = 0; +THREE.LinePieces = 1; - this.positionScreen.copy( source.positionScreen ); - this.customUpdateCallback = source.customUpdateCallback; +// File:src/objects/LineSegments.js - for ( var i = 0, l = source.lensFlares.length; i < l; i ++ ) { +/** + * @author mrdoob / http://mrdoob.com/ + */ - this.lensFlares.push( source.lensFlares[ i ] ); +THREE.LineSegments = function ( geometry, material ) { - } + THREE.Line.call( this, geometry, material ); - return this; + this.type = 'LineSegments'; }; -// File:src/scenes/Scene.js +THREE.LineSegments.prototype = Object.create( THREE.Line.prototype ); +THREE.LineSegments.prototype.constructor = THREE.LineSegments; + +// File:src/objects/Mesh.js /** * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author mikael emtinger / http://gomo.se/ + * @author jonobr1 / http://jonobr1.com/ */ -THREE.Scene = function () { +THREE.Mesh = function ( geometry, material ) { THREE.Object3D.call( this ); - this.type = 'Scene'; + this.type = 'Mesh'; - this.fog = null; - this.overrideMaterial = null; + this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); + this.material = material !== undefined ? material : new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff } ); - this.autoUpdate = true; // checked by the renderer + this.updateMorphTargets(); }; -THREE.Scene.prototype = Object.create( THREE.Object3D.prototype ); -THREE.Scene.prototype.constructor = THREE.Scene; - -THREE.Scene.prototype.copy = function ( source ) { +THREE.Mesh.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Mesh.prototype.constructor = THREE.Mesh; - THREE.Object3D.prototype.copy.call( this, source ); +THREE.Mesh.prototype.updateMorphTargets = function () { - if ( source.fog !== null ) this.fog = source.fog.clone(); - if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone(); + if ( this.geometry.morphTargets !== undefined && this.geometry.morphTargets.length > 0 ) { - this.autoUpdate = source.autoUpdate; - this.matrixAutoUpdate = source.matrixAutoUpdate; + this.morphTargetBase = - 1; + this.morphTargetInfluences = []; + this.morphTargetDictionary = {}; - return this; + for ( var m = 0, ml = this.geometry.morphTargets.length; m < ml; m ++ ) { -}; + this.morphTargetInfluences.push( 0 ); + this.morphTargetDictionary[ this.geometry.morphTargets[ m ].name ] = m; -// File:src/scenes/Fog.js + } -/** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ + } -THREE.Fog = function ( color, near, far ) { +}; - this.name = ''; +THREE.Mesh.prototype.getMorphTargetIndexByName = function ( name ) { - this.color = new THREE.Color( color ); + if ( this.morphTargetDictionary[ name ] !== undefined ) { - this.near = ( near !== undefined ) ? near : 1; - this.far = ( far !== undefined ) ? far : 1000; + return this.morphTargetDictionary[ name ]; -}; + } -THREE.Fog.prototype.clone = function () { + console.warn( 'THREE.Mesh.getMorphTargetIndexByName: morph target ' + name + ' does not exist. Returning 0.' ); - return new THREE.Fog( this.color.getHex(), this.near, this.far ); + return 0; }; -// File:src/scenes/FogExp2.js - -/** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ - -THREE.FogExp2 = function ( color, density ) { - - this.name = ''; - this.color = new THREE.Color( color ); - this.density = ( density !== undefined ) ? density : 0.00025; +THREE.Mesh.prototype.raycast = ( function () { -}; + var inverseMatrix = new THREE.Matrix4(); + var ray = new THREE.Ray(); + var sphere = new THREE.Sphere(); -THREE.FogExp2.prototype.clone = function () { + var vA = new THREE.Vector3(); + var vB = new THREE.Vector3(); + var vC = new THREE.Vector3(); - return new THREE.FogExp2( this.color.getHex(), this.density ); + var tempA = new THREE.Vector3(); + var tempB = new THREE.Vector3(); + var tempC = new THREE.Vector3(); -}; + var uvA = new THREE.Vector2(); + var uvB = new THREE.Vector2(); + var uvC = new THREE.Vector2(); -// File:src/renderers/shaders/ShaderChunk.js + var barycoord = new THREE.Vector3(); -THREE.ShaderChunk = {}; + var intersectionPoint = new THREE.Vector3(); + var intersectionPointWorld = new THREE.Vector3(); -// File:src/renderers/shaders/ShaderChunk/alphamap_fragment.glsl + function uvIntersection( point, p1, p2, p3, uv1, uv2, uv3 ) { -THREE.ShaderChunk[ 'alphamap_fragment'] = "#ifdef USE_ALPHAMAP\n\n diffuseColor.a *= texture2D( alphaMap, vUv ).g;\n\n#endif\n"; + THREE.Triangle.barycoordFromPoint( point, p1, p2, p3, barycoord ); -// File:src/renderers/shaders/ShaderChunk/alphamap_pars_fragment.glsl + uv1.multiplyScalar( barycoord.x ); + uv2.multiplyScalar( barycoord.y ); + uv3.multiplyScalar( barycoord.z ); -THREE.ShaderChunk[ 'alphamap_pars_fragment'] = "#ifdef USE_ALPHAMAP\n\n uniform sampler2D alphaMap;\n\n#endif\n"; + uv1.add( uv2 ).add( uv3 ); -// File:src/renderers/shaders/ShaderChunk/alphatest_fragment.glsl + return uv1.clone(); -THREE.ShaderChunk[ 'alphatest_fragment'] = "#ifdef ALPHATEST\n\n if ( diffuseColor.a < ALPHATEST ) discard;\n\n#endif\n"; + } -// File:src/renderers/shaders/ShaderChunk/aomap_fragment.glsl + return function raycast( raycaster, intersects ) { -THREE.ShaderChunk[ 'aomap_fragment'] = "#ifdef USE_AOMAP\n\n totalAmbientLight *= ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n\n#endif\n"; + var geometry = this.geometry; + var material = this.material; -// File:src/renderers/shaders/ShaderChunk/aomap_pars_fragment.glsl + if ( material === undefined ) return; -THREE.ShaderChunk[ 'aomap_pars_fragment'] = "#ifdef USE_AOMAP\n\n uniform sampler2D aoMap;\n uniform float aoMapIntensity;\n\n#endif"; + // Checking boundingSphere distance to ray -// File:src/renderers/shaders/ShaderChunk/begin_vertex.glsl + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); -THREE.ShaderChunk[ 'begin_vertex'] = "\nvec3 transformed = vec3( position );\n"; + sphere.copy( geometry.boundingSphere ); + sphere.applyMatrix4( this.matrixWorld ); -// File:src/renderers/shaders/ShaderChunk/beginnormal_vertex.glsl + if ( raycaster.ray.isIntersectionSphere( sphere ) === false ) { -THREE.ShaderChunk[ 'beginnormal_vertex'] = "\nvec3 objectNormal = vec3( normal );\n"; + return; -// File:src/renderers/shaders/ShaderChunk/bumpmap_pars_fragment.glsl + } -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\n"; + // Check boundingBox before continuing -// File:src/renderers/shaders/ShaderChunk/color_fragment.glsl + inverseMatrix.getInverse( this.matrixWorld ); + ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); -THREE.ShaderChunk[ 'color_fragment'] = "#ifdef USE_COLOR\n\n diffuseColor.rgb *= vColor;\n\n#endif"; + if ( geometry.boundingBox !== null ) { -// File:src/renderers/shaders/ShaderChunk/color_pars_fragment.glsl + if ( ray.isIntersectionBox( geometry.boundingBox ) === false ) { -THREE.ShaderChunk[ 'color_pars_fragment'] = "#ifdef USE_COLOR\n\n varying vec3 vColor;\n\n#endif\n"; + return; -// File:src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl + } -THREE.ShaderChunk[ 'color_pars_vertex'] = "#ifdef USE_COLOR\n\n varying vec3 vColor;\n\n#endif"; + } -// File:src/renderers/shaders/ShaderChunk/color_vertex.glsl + var a, b, c; -THREE.ShaderChunk[ 'color_vertex'] = "#ifdef USE_COLOR\n\n vColor.xyz = color.xyz;\n\n#endif"; + if ( geometry instanceof THREE.BufferGeometry ) { -// File:src/renderers/shaders/ShaderChunk/common.glsl + var index = geometry.index; + var attributes = geometry.attributes; -THREE.ShaderChunk[ 'common'] = "#define PI 3.14159\n#define PI2 6.28318\n#define RECIPROCAL_PI2 0.15915494\n#define LOG2 1.442695\n#define EPSILON 1e-6\n\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#define whiteCompliment(a) ( 1.0 - saturate( a ) )\n\nvec3 transformDirection( in vec3 normal, in mat4 matrix ) {\n\n return normalize( ( matrix * vec4( normal, 0.0 ) ).xyz );\n\n}\n\n// http://en.wikibooks.org/wiki/GLSL_Programming/Applying_Matrix_Transformations\nvec3 inverseTransformDirection( in vec3 normal, in mat4 matrix ) {\n\n return normalize( ( vec4( normal, 0.0 ) * matrix ).xyz );\n\n}\n\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n float distance = dot( planeNormal, point - pointOnPlane );\n\n return - distance * planeNormal + point;\n\n}\n\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n return sign( dot( point - pointOnPlane, planeNormal ) );\n\n}\n\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n return lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n\n}\n\nfloat calcLightAttenuation( float lightDistance, float cutoffDistance, float decayExponent ) {\n\n if ( decayExponent > 0.0 ) {\n\n return pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\n }\n\n return 1.0;\n\n}\n\nvec3 F_Schlick( in vec3 specularColor, in float dotLH ) {\n\n // Original approximation by Christophe Schlick '94\n //;float fresnel = pow( 1.0 - dotLH, 5.0 );\n\n // Optimized variant (presented by Epic at SIGGRAPH '13)\n float fresnel = exp2( ( -5.55437 * dotLH - 6.98316 ) * dotLH );\n\n return ( 1.0 - specularColor ) * fresnel + specularColor;\n\n}\n\nfloat G_BlinnPhong_Implicit( /* in float dotNL, in float dotNV */ ) {\n\n // geometry term is (n⋅l)(n⋅v) / 4(n⋅l)(n⋅v)\n\n return 0.25;\n\n}\n\nfloat D_BlinnPhong( in float shininess, in float dotNH ) {\n\n // factor of 1/PI in distribution term omitted\n\n return ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n\n}\n\nvec3 BRDF_BlinnPhong( in vec3 specularColor, in float shininess, in vec3 normal, in vec3 lightDir, in vec3 viewDir ) {\n\n vec3 halfDir = normalize( lightDir + viewDir );\n\n //float dotNL = saturate( dot( normal, lightDir ) );\n //float dotNV = saturate( dot( normal, viewDir ) );\n float dotNH = saturate( dot( normal, halfDir ) );\n float dotLH = saturate( dot( lightDir, halfDir ) );\n\n vec3 F = F_Schlick( specularColor, dotLH );\n\n float G = G_BlinnPhong_Implicit( /* dotNL, dotNV */ );\n\n float D = D_BlinnPhong( shininess, dotNH );\n\n return F * G * D;\n\n}\n\nvec3 inputToLinear( in vec3 a ) {\n\n #ifdef GAMMA_INPUT\n\n return pow( a, vec3( float( GAMMA_FACTOR ) ) );\n\n #else\n\n return a;\n\n #endif\n\n}\n\nvec3 linearToOutput( in vec3 a ) {\n\n #ifdef GAMMA_OUTPUT\n\n return pow( a, vec3( 1.0 / float( GAMMA_FACTOR ) ) );\n\n #else\n\n return a;\n\n #endif\n\n}\n"; + if ( index !== null ) { -// File:src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl + var indices = index.array; + var positions = attributes.position.array; -THREE.ShaderChunk[ 'defaultnormal_vertex'] = "#ifdef FLIP_SIDED\n\n objectNormal = -objectNormal;\n\n#endif\n\nvec3 transformedNormal = normalMatrix * objectNormal;\n"; + for ( var i = 0, l = indices.length; i < l; i += 3 ) { -// File:src/renderers/shaders/ShaderChunk/displacementmap_vertex.glsl + a = indices[ i ]; + b = indices[ i + 1 ]; + c = indices[ i + 2 ]; -THREE.ShaderChunk[ 'displacementmap_vertex'] = "#ifdef USE_DISPLACEMENTMAP\n\n transformed += normal * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );\n\n#endif\n"; + vA.fromArray( positions, a * 3 ); + vB.fromArray( positions, b * 3 ); + vC.fromArray( positions, c * 3 ); -// File:src/renderers/shaders/ShaderChunk/displacementmap_pars_vertex.glsl + if ( material.side === THREE.BackSide ) { -THREE.ShaderChunk[ 'displacementmap_pars_vertex'] = "#ifdef USE_DISPLACEMENTMAP\n\n uniform sampler2D displacementMap;\n uniform float displacementScale;\n uniform float displacementBias;\n\n#endif\n"; + if ( ray.intersectTriangle( vC, vB, vA, true, intersectionPoint ) === null ) continue; -// File:src/renderers/shaders/ShaderChunk/emissivemap_fragment.glsl + } else { -THREE.ShaderChunk[ 'emissivemap_fragment'] = "#ifdef USE_EMISSIVEMAP\n\n vec4 emissiveColor = texture2D( emissiveMap, vUv );\n\n emissiveColor.rgb = inputToLinear( emissiveColor.rgb );\n\n totalEmissiveLight *= emissiveColor.rgb;\n\n#endif\n"; + if ( ray.intersectTriangle( vA, vB, vC, material.side !== THREE.DoubleSide, intersectionPoint ) === null ) continue; -// File:src/renderers/shaders/ShaderChunk/emissivemap_pars_fragment.glsl + } -THREE.ShaderChunk[ 'emissivemap_pars_fragment'] = "#ifdef USE_EMISSIVEMAP\n\n uniform sampler2D emissiveMap;\n\n#endif\n"; + intersectionPointWorld.copy( intersectionPoint ); + intersectionPointWorld.applyMatrix4( this.matrixWorld ); -// File:src/renderers/shaders/ShaderChunk/envmap_fragment.glsl + var distance = raycaster.ray.origin.distanceTo( intersectionPointWorld ); -THREE.ShaderChunk[ 'envmap_fragment'] = "#ifdef USE_ENVMAP\n\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\n // Transforming Normal Vectors with the Inverse Transformation\n vec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\n #ifdef ENVMAP_MODE_REFLECTION\n\n vec3 reflectVec = reflect( cameraToVertex, worldNormal );\n\n #else\n\n vec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\n #endif\n\n #else\n\n vec3 reflectVec = vReflect;\n\n #endif\n\n #ifdef DOUBLE_SIDED\n float flipNormal = ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n #else\n float flipNormal = 1.0;\n #endif\n\n #ifdef ENVMAP_TYPE_CUBE\n vec4 envColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\n #elif defined( ENVMAP_TYPE_EQUIREC )\n vec2 sampleUV;\n sampleUV.y = saturate( flipNormal * reflectVec.y * 0.5 + 0.5 );\n sampleUV.x = atan( flipNormal * reflectVec.z, flipNormal * reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n vec4 envColor = texture2D( envMap, sampleUV );\n\n #elif defined( ENVMAP_TYPE_SPHERE )\n vec3 reflectView = flipNormal * normalize((viewMatrix * vec4( reflectVec, 0.0 )).xyz + vec3(0.0,0.0,1.0));\n vec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\n #endif\n\n envColor.xyz = inputToLinear( envColor.xyz );\n\n #ifdef ENVMAP_BLENDING_MULTIPLY\n\n outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\n #elif defined( ENVMAP_BLENDING_MIX )\n\n outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\n #elif defined( ENVMAP_BLENDING_ADD )\n\n outgoingLight += envColor.xyz * specularStrength * reflectivity;\n\n #endif\n\n#endif\n"; + if ( distance < raycaster.near || distance > raycaster.far ) continue; -// File:src/renderers/shaders/ShaderChunk/envmap_pars_fragment.glsl + var uv; -THREE.ShaderChunk[ 'envmap_pars_fragment'] = "#ifdef USE_ENVMAP\n\n uniform float reflectivity;\n #ifdef ENVMAP_TYPE_CUBE\n uniform samplerCube envMap;\n #else\n uniform sampler2D envMap;\n #endif\n uniform float flipEnvMap;\n\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n uniform float refractionRatio;\n\n #else\n\n varying vec3 vReflect;\n\n #endif\n\n#endif\n"; + if ( attributes.uv !== undefined ) { -// File:src/renderers/shaders/ShaderChunk/envmap_pars_vertex.glsl + var uvs = attributes.uv.array; + uvA.fromArray( uvs, a * 2 ); + uvB.fromArray( uvs, b * 2 ); + uvC.fromArray( uvs, c * 2 ); + uv = uvIntersection( intersectionPoint, vA, vB, vC, uvA, uvB, uvC ); -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\n#endif\n"; + } -// File:src/renderers/shaders/ShaderChunk/envmap_vertex.glsl + intersects.push( { -THREE.ShaderChunk[ 'envmap_vertex'] = "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n vec3 worldNormal = transformDirection( objectNormal, modelMatrix );\n\n vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\n #ifdef ENVMAP_MODE_REFLECTION\n\n vReflect = reflect( cameraToVertex, worldNormal );\n\n #else\n\n vReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\n #endif\n\n#endif\n"; + distance: distance, + point: intersectionPointWorld.clone(), + uv: uv, + face: new THREE.Face3( a, b, c, THREE.Triangle.normal( vA, vB, vC ) ), + faceIndex: Math.floor( i / 3 ), // triangle number in indices buffer semantics + object: this -// File:src/renderers/shaders/ShaderChunk/fog_fragment.glsl + } ); -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 float fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * depth * depth * LOG2 ) );\n\n #else\n\n float fogFactor = smoothstep( fogNear, fogFar, depth );\n\n #endif\n \n outgoingLight = mix( outgoingLight, fogColor, fogFactor );\n\n#endif"; + } -// File:src/renderers/shaders/ShaderChunk/fog_pars_fragment.glsl + } else { -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"; + var positions = attributes.position.array; -// File:src/renderers/shaders/ShaderChunk/hemilight_fragment.glsl + for ( var i = 0, l = positions.length; i < l; i += 9 ) { -THREE.ShaderChunk[ 'hemilight_fragment'] = "#if MAX_HEMI_LIGHTS > 0\n\n for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n vec3 lightDir = hemisphereLightDirection[ i ];\n\n float dotProduct = dot( normal, lightDir );\n\n float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n vec3 lightColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n totalAmbientLight += lightColor;\n\n }\n\n#endif\n\n"; + vA.fromArray( positions, i ); + vB.fromArray( positions, i + 3 ); + vC.fromArray( positions, i + 6 ); -// File:src/renderers/shaders/ShaderChunk/lightmap_fragment.glsl + if ( material.side === THREE.BackSide ) { -THREE.ShaderChunk[ 'lightmap_fragment'] = "#ifdef USE_LIGHTMAP\n\n totalAmbientLight += texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\n#endif\n"; + if ( ray.intersectTriangle( vC, vB, vA, true, intersectionPoint ) === null ) continue; -// File:src/renderers/shaders/ShaderChunk/lightmap_pars_fragment.glsl + } else { -THREE.ShaderChunk[ 'lightmap_pars_fragment'] = "#ifdef USE_LIGHTMAP\n\n uniform sampler2D lightMap;\n uniform float lightMapIntensity;\n\n#endif"; + if ( ray.intersectTriangle( vA, vB, vC, material.side !== THREE.DoubleSide, intersectionPoint ) === null ) continue; -// File:src/renderers/shaders/ShaderChunk/lights_lambert_pars_vertex.glsl + } -THREE.ShaderChunk[ 'lights_lambert_pars_vertex'] = "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 uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n uniform float pointLightDecay[ 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 uniform float spotLightDecay[ MAX_SPOT_LIGHTS ];\n\n#endif\n"; + intersectionPointWorld.copy( intersectionPoint ); + intersectionPointWorld.applyMatrix4( this.matrixWorld ); -// File:src/renderers/shaders/ShaderChunk/lights_lambert_vertex.glsl + var distance = raycaster.ray.origin.distanceTo( intersectionPointWorld ); -THREE.ShaderChunk[ 'lights_lambert_vertex'] = "vLightFront = vec3( 0.0 );\n\n#ifdef DOUBLE_SIDED\n\n vLightBack = vec3( 0.0 );\n\n#endif\n\nvec3 normal = normalize( transformedNormal );\n\n#if MAX_POINT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n vec3 lightColor = pointLightColor[ i ];\n\n vec3 lVector = pointLightPosition[ i ] - mvPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n // attenuation\n\n float attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[ i ] );\n\n // diffuse\n\n float dotProduct = dot( normal, lightDir );\n\n vLightFront += lightColor * attenuation * saturate( dotProduct );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += lightColor * attenuation * saturate( - dotProduct );\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 vec3 lightColor = spotLightColor[ i ];\n\n vec3 lightPosition = spotLightPosition[ i ];\n vec3 lVector = lightPosition - mvPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n float spotEffect = dot( spotLightDirection[ i ], lightDir );\n\n if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n spotEffect = saturate( pow( saturate( spotEffect ), spotLightExponent[ i ] ) );\n\n // attenuation\n\n float attenuation = calcLightAttenuation( length( lVector ), spotLightDistance[ i ], spotLightDecay[ i ] );\n\n attenuation *= spotEffect;\n\n // diffuse\n\n float dotProduct = dot( normal, lightDir );\n\n vLightFront += lightColor * attenuation * saturate( dotProduct );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += lightColor * attenuation * saturate( - dotProduct );\n\n #endif\n\n }\n\n }\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n for ( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n vec3 lightColor = directionalLightColor[ i ];\n\n vec3 lightDir = directionalLightDirection[ i ];\n\n // diffuse\n\n float dotProduct = dot( normal, lightDir );\n\n vLightFront += lightColor * saturate( dotProduct );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += lightColor * saturate( - dotProduct );\n\n #endif\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 vec3 lightDir = hemisphereLightDirection[ i ];\n\n // diffuse\n\n float dotProduct = dot( normal, lightDir );\n\n float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n vLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n #ifdef DOUBLE_SIDED\n\n float hemiDiffuseWeightBack = - 0.5 * dotProduct + 0.5;\n\n vLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );\n\n #endif\n\n }\n\n#endif\n\nvLightFront += ambientLightColor;\n\n#ifdef DOUBLE_SIDED\n\n vLightBack += ambientLightColor;\n\n#endif\n"; + if ( distance < raycaster.near || distance > raycaster.far ) continue; -// File:src/renderers/shaders/ShaderChunk/lights_phong_fragment.glsl + var uv; -THREE.ShaderChunk[ 'lights_phong_fragment'] = "vec3 viewDir = normalize( vViewPosition );\n\nvec3 totalDiffuseLight = vec3( 0.0 );\nvec3 totalSpecularLight = vec3( 0.0 );\n\n#if MAX_POINT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n vec3 lightColor = pointLightColor[ i ];\n\n vec3 lightPosition = pointLightPosition[ i ];\n vec3 lVector = lightPosition + vViewPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n // attenuation\n\n float attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[ i ] );\n\n // diffuse\n\n float cosineTerm = saturate( dot( normal, lightDir ) );\n\n totalDiffuseLight += lightColor * attenuation * cosineTerm;\n\n // specular\n\n vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n totalSpecularLight += brdf * specularStrength * lightColor * attenuation * cosineTerm;\n\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 vec3 lightColor = spotLightColor[ i ];\n\n vec3 lightPosition = spotLightPosition[ i ];\n vec3 lVector = lightPosition + vViewPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n float spotEffect = dot( spotLightDirection[ i ], lightDir );\n\n if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n spotEffect = saturate( pow( saturate( spotEffect ), spotLightExponent[ i ] ) );\n\n // attenuation\n\n float attenuation = calcLightAttenuation( length( lVector ), spotLightDistance[ i ], spotLightDecay[ i ] );\n\n attenuation *= spotEffect;\n\n // diffuse\n\n float cosineTerm = saturate( dot( normal, lightDir ) );\n\n totalDiffuseLight += lightColor * attenuation * cosineTerm;\n\n // specular\n\n vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n totalSpecularLight += brdf * specularStrength * lightColor * attenuation * cosineTerm;\n\n }\n\n }\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n for( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n vec3 lightColor = directionalLightColor[ i ];\n\n vec3 lightDir = directionalLightDirection[ i ];\n\n // diffuse\n\n float cosineTerm = saturate( dot( normal, lightDir ) );\n\n totalDiffuseLight += lightColor * cosineTerm;\n\n // specular\n\n vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n totalSpecularLight += brdf * specularStrength * lightColor * cosineTerm;\n\n }\n\n#endif\n\n#ifdef METAL\n\n outgoingLight += diffuseColor.rgb * ( totalDiffuseLight + totalAmbientLight ) * specular + totalSpecularLight + totalEmissiveLight;\n\n#else\n\n outgoingLight += diffuseColor.rgb * ( totalDiffuseLight + totalAmbientLight ) + totalSpecularLight + totalEmissiveLight;\n\n#endif\n"; + if ( attributes.uv !== undefined ) { -// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_fragment.glsl + var uvs = attributes.uv.array; + uvA.fromArray( uvs, i ); + uvB.fromArray( uvs, i + 2 ); + uvC.fromArray( uvs, i + 4 ); + uv = uvIntersection( intersectionPoint, vA, vB, vC, uvA, uvB, uvC ); -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 uniform float pointLightDecay[ 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 uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDecay[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n varying vec3 vWorldPosition;\n\n#endif\n\nvarying vec3 vViewPosition;\n\n#ifndef FLAT_SHADED\n\n varying vec3 vNormal;\n\n#endif\n"; + } -// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_vertex.glsl + a = i / 3; + b = a + 1; + c = a + 2; -THREE.ShaderChunk[ 'lights_phong_pars_vertex'] = "#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n varying vec3 vWorldPosition;\n\n#endif\n"; + intersects.push( { -// File:src/renderers/shaders/ShaderChunk/lights_phong_vertex.glsl + distance: distance, + point: intersectionPointWorld.clone(), + uv: uv, + face: new THREE.Face3( a, b, c, THREE.Triangle.normal( vA, vB, vC ) ), + index: a, // triangle number in positions buffer semantics + object: this -THREE.ShaderChunk[ 'lights_phong_vertex'] = "#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n vWorldPosition = worldPosition.xyz;\n\n#endif\n"; + } ); -// File:src/renderers/shaders/ShaderChunk/linear_to_gamma_fragment.glsl + } -THREE.ShaderChunk[ 'linear_to_gamma_fragment'] = "\n outgoingLight = linearToOutput( outgoingLight );\n"; + } -// File:src/renderers/shaders/ShaderChunk/logdepthbuf_fragment.glsl + } else if ( geometry instanceof THREE.Geometry ) { -THREE.ShaderChunk[ 'logdepthbuf_fragment'] = "#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)\n\n gl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;\n\n#endif"; + var isFaceMaterial = material instanceof THREE.MeshFaceMaterial; + var materials = isFaceMaterial === true ? material.materials : null; -// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_fragment.glsl + var vertices = geometry.vertices; + var faces = geometry.faces; -THREE.ShaderChunk[ 'logdepthbuf_pars_fragment'] = "#ifdef USE_LOGDEPTHBUF\n\n uniform float logDepthBufFC;\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n varying float vFragDepth;\n\n #endif\n\n#endif\n"; + for ( var f = 0, fl = faces.length; f < fl; f ++ ) { -// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_vertex.glsl + var face = faces[ f ]; + var faceMaterial = isFaceMaterial === true ? materials[ face.materialIndex ] : material; -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 ( faceMaterial === undefined ) continue; -// File:src/renderers/shaders/ShaderChunk/logdepthbuf_vertex.glsl + a = vertices[ face.a ]; + b = vertices[ face.b ]; + c = vertices[ face.c ]; -THREE.ShaderChunk[ 'logdepthbuf_vertex'] = "#ifdef USE_LOGDEPTHBUF\n\n gl_Position.z = log2(max( EPSILON, 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"; + if ( faceMaterial.morphTargets === true ) { -// File:src/renderers/shaders/ShaderChunk/map_fragment.glsl + var morphTargets = geometry.morphTargets; + var morphInfluences = this.morphTargetInfluences; -THREE.ShaderChunk[ 'map_fragment'] = "#ifdef USE_MAP\n\n vec4 texelColor = texture2D( map, vUv );\n\n texelColor.xyz = inputToLinear( texelColor.xyz );\n\n diffuseColor *= texelColor;\n\n#endif\n"; + vA.set( 0, 0, 0 ); + vB.set( 0, 0, 0 ); + vC.set( 0, 0, 0 ); -// File:src/renderers/shaders/ShaderChunk/map_pars_fragment.glsl + for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) { -THREE.ShaderChunk[ 'map_pars_fragment'] = "#ifdef USE_MAP\n\n uniform sampler2D map;\n\n#endif"; + var influence = morphInfluences[ t ]; -// File:src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl + if ( influence === 0 ) continue; -THREE.ShaderChunk[ 'map_particle_fragment'] = "#ifdef USE_MAP\n\n diffuseColor *= texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) * offsetRepeat.zw + offsetRepeat.xy );\n\n#endif\n"; + var targets = morphTargets[ t ].vertices; -// File:src/renderers/shaders/ShaderChunk/map_particle_pars_fragment.glsl + vA.addScaledVector( tempA.subVectors( targets[ face.a ], a ), influence ); + vB.addScaledVector( tempB.subVectors( targets[ face.b ], b ), influence ); + vC.addScaledVector( tempC.subVectors( targets[ face.c ], c ), influence ); -THREE.ShaderChunk[ 'map_particle_pars_fragment'] = "#ifdef USE_MAP\n\n uniform vec4 offsetRepeat;\n uniform sampler2D map;\n\n#endif\n"; + } -// File:src/renderers/shaders/ShaderChunk/morphnormal_vertex.glsl + vA.add( a ); + vB.add( b ); + vC.add( c ); -THREE.ShaderChunk[ 'morphnormal_vertex'] = "#ifdef USE_MORPHNORMALS\n\n objectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n objectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n objectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n objectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n\n#endif\n"; + a = vA; + b = vB; + c = vC; -// 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 ( faceMaterial.side === THREE.BackSide ) { -// File:src/renderers/shaders/ShaderChunk/morphtarget_vertex.glsl + if ( ray.intersectTriangle( c, b, a, true, intersectionPoint ) === null ) continue; -THREE.ShaderChunk[ 'morphtarget_vertex'] = "#ifdef USE_MORPHTARGETS\n\n transformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n transformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n transformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n transformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\n #ifndef USE_MORPHNORMALS\n\n transformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n transformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n transformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n transformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\n #endif\n\n#endif\n"; + } else { -// File:src/renderers/shaders/ShaderChunk/normal_phong_fragment.glsl + if ( ray.intersectTriangle( a, b, c, faceMaterial.side !== THREE.DoubleSide, intersectionPoint ) === null ) continue; -THREE.ShaderChunk[ 'normal_phong_fragment'] = "#ifndef FLAT_SHADED\n\n vec3 normal = normalize( vNormal );\n\n #ifdef DOUBLE_SIDED\n\n normal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n\n #endif\n\n#else\n\n vec3 fdx = dFdx( vViewPosition );\n vec3 fdy = dFdy( vViewPosition );\n vec3 normal = normalize( cross( fdx, fdy ) );\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"; + } -// File:src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl + intersectionPointWorld.copy( intersectionPoint ); + intersectionPointWorld.applyMatrix4( this.matrixWorld ); -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"; + var distance = raycaster.ray.origin.distanceTo( intersectionPointWorld ); -// File:src/renderers/shaders/ShaderChunk/project_vertex.glsl + if ( distance < raycaster.near || distance > raycaster.far ) continue; -THREE.ShaderChunk[ 'project_vertex'] = "#ifdef USE_SKINNING\n\n vec4 mvPosition = modelViewMatrix * skinned;\n\n#else\n\n vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\n\n#endif\n\ngl_Position = projectionMatrix * mvPosition;\n"; + var uv; -// File:src/renderers/shaders/ShaderChunk/shadowmap_fragment.glsl + if ( geometry.faceVertexUvs[ 0 ].length > 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 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 bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\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 if ( inFrustum ) outgoingLight *= frustumColors[ i ];\n\n #endif\n\n }\n\n outgoingLight = outgoingLight * shadowColor;\n\n#endif\n"; + var uvs = geometry.faceVertexUvs[ 0 ][ f ]; + uvA.copy( uvs[ 0 ] ); + uvB.copy( uvs[ 1 ] ); + uvC.copy( uvs[ 2 ] ); + uv = uvIntersection( intersectionPoint, a, b, c, uvA, uvB, uvC ); -// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl + } -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"; + intersects.push( { -// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl + distance: distance, + point: intersectionPointWorld.clone(), + uv: uv, + face: face, + faceIndex: f, + object: this -THREE.ShaderChunk[ 'shadowmap_pars_vertex'] = "#ifdef USE_SHADOWMAP\n\n varying vec4 vShadowCoord[ MAX_SHADOWS ];\n uniform mat4 shadowMatrix[ MAX_SHADOWS ];\n\n#endif"; + } ); -// 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"; + } -// File:src/renderers/shaders/ShaderChunk/skinbase_vertex.glsl + }; -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"; +}() ); -// File:src/renderers/shaders/ShaderChunk/skinning_pars_vertex.glsl +THREE.Mesh.prototype.clone = function () { -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"; + return new this.constructor( this.geometry, this.material ).copy( this ); -// File:src/renderers/shaders/ShaderChunk/skinning_vertex.glsl +}; -THREE.ShaderChunk[ 'skinning_vertex'] = "#ifdef USE_SKINNING\n\n vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\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"; +THREE.Mesh.prototype.toJSON = function ( meta ) { -// File:src/renderers/shaders/ShaderChunk/skinnormal_vertex.glsl + var data = THREE.Object3D.prototype.toJSON.call( this, meta ); -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 objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n\n#endif\n"; + // only serialize if not in meta geometries cache + if ( meta.geometries[ this.geometry.uuid ] === undefined ) { -// File:src/renderers/shaders/ShaderChunk/specularmap_fragment.glsl + meta.geometries[ this.geometry.uuid ] = this.geometry.toJSON( meta ); -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"; + } -// File:src/renderers/shaders/ShaderChunk/specularmap_pars_fragment.glsl + // only serialize if not in meta materials cache + if ( meta.materials[ this.material.uuid ] === undefined ) { -THREE.ShaderChunk[ 'specularmap_pars_fragment'] = "#ifdef USE_SPECULARMAP\n\n uniform sampler2D specularMap;\n\n#endif"; + meta.materials[ this.material.uuid ] = this.material.toJSON( meta ); -// File:src/renderers/shaders/ShaderChunk/uv2_pars_fragment.glsl + } -THREE.ShaderChunk[ 'uv2_pars_fragment'] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n varying vec2 vUv2;\n\n#endif"; + data.object.geometry = this.geometry.uuid; + data.object.material = this.material.uuid; -// File:src/renderers/shaders/ShaderChunk/uv2_pars_vertex.glsl + return data; -THREE.ShaderChunk[ 'uv2_pars_vertex'] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n attribute vec2 uv2;\n varying vec2 vUv2;\n\n#endif"; +}; -// File:src/renderers/shaders/ShaderChunk/uv2_vertex.glsl +// File:src/objects/Bone.js -THREE.ShaderChunk[ 'uv2_vertex'] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n vUv2 = uv2;\n\n#endif"; +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author ikerr / http://verold.com + */ -// File:src/renderers/shaders/ShaderChunk/uv_pars_fragment.glsl +THREE.Bone = function ( skin ) { -THREE.ShaderChunk[ 'uv_pars_fragment'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n varying vec2 vUv;\n\n#endif"; + THREE.Object3D.call( this ); -// File:src/renderers/shaders/ShaderChunk/uv_pars_vertex.glsl + this.type = 'Bone'; -THREE.ShaderChunk[ 'uv_pars_vertex'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n varying vec2 vUv;\n uniform vec4 offsetRepeat;\n\n#endif\n"; + this.skin = skin; -// File:src/renderers/shaders/ShaderChunk/uv_vertex.glsl +}; -THREE.ShaderChunk[ 'uv_vertex'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n vUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n\n#endif"; +THREE.Bone.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Bone.prototype.constructor = THREE.Bone; -// File:src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl +THREE.Bone.prototype.copy = function ( source ) { + + THREE.Object3D.prototype.copy.call( this, source ); + + this.skin = source.skin; + + return this; -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 #else\n\n vec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\n\n #endif\n\n#endif\n"; +}; -// File:src/renderers/shaders/UniformsUtils.js +// File:src/objects/Skeleton.js /** - * Uniform Utilities + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author michael guerrero / http://realitymeltdown.com + * @author ikerr / http://verold.com */ -THREE.UniformsUtils = { +THREE.Skeleton = function ( bones, boneInverses, useVertexTexture ) { - merge: function ( uniforms ) { + this.useVertexTexture = useVertexTexture !== undefined ? useVertexTexture : true; - var merged = {}; + this.identityMatrix = new THREE.Matrix4(); - for ( var u = 0; u < uniforms.length; u ++ ) { + // copy the bone array - var tmp = this.clone( uniforms[ u ] ); + bones = bones || []; - for ( var p in tmp ) { + this.bones = bones.slice( 0 ); - merged[ p ] = tmp[ p ]; + // create a bone texture or an array of floats - } + if ( this.useVertexTexture ) { - } + // layout (1 matrix = 4 pixels) + // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) + // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) + // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) + // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) + // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) - return merged; + + var size = Math.sqrt( this.bones.length * 4 ); // 4 pixels needed for 1 matrix + size = THREE.Math.nextPowerOfTwo( Math.ceil( size ) ); + size = Math.max( size, 4 ); - }, + this.boneTextureWidth = size; + this.boneTextureHeight = size; - clone: function ( uniforms_src ) { + this.boneMatrices = new Float32Array( this.boneTextureWidth * this.boneTextureHeight * 4 ); // 4 floats per RGBA pixel + this.boneTexture = new THREE.DataTexture( this.boneMatrices, this.boneTextureWidth, this.boneTextureHeight, THREE.RGBAFormat, THREE.FloatType ); - var uniforms_dst = {}; + } else { - for ( var u in uniforms_src ) { + this.boneMatrices = new Float32Array( 16 * this.bones.length ); - uniforms_dst[ u ] = {}; + } - for ( var p in uniforms_src[ u ] ) { + // use the supplied bone inverses or calculate the inverses - var parameter_src = uniforms_src[ u ][ p ]; + if ( boneInverses === undefined ) { - 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.Matrix3 || - parameter_src instanceof THREE.Matrix4 || - parameter_src instanceof THREE.Texture ) { + this.calculateInverses(); - uniforms_dst[ u ][ p ] = parameter_src.clone(); + } else { - } else if ( Array.isArray( parameter_src ) ) { + if ( this.bones.length === boneInverses.length ) { - uniforms_dst[ u ][ p ] = parameter_src.slice(); + this.boneInverses = boneInverses.slice( 0 ); - } else { + } else { - uniforms_dst[ u ][ p ] = parameter_src; + console.warn( 'THREE.Skeleton bonInverses is the wrong length.' ); - } + this.boneInverses = []; + + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + + this.boneInverses.push( new THREE.Matrix4() ); } } - return uniforms_dst; - } }; -// File:src/renderers/shaders/UniformsLib.js +THREE.Skeleton.prototype.calculateInverses = function () { -/** - * Uniforms library for shared webgl shaders - */ + this.boneInverses = []; -THREE.UniformsLib = { + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { - common: { + var inverse = new THREE.Matrix4(); - "diffuse" : { type: "c", value: new THREE.Color( 0xeeeeee ) }, - "opacity" : { type: "f", value: 1.0 }, + if ( this.bones[ b ] ) { - "map" : { type: "t", value: null }, - "offsetRepeat" : { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) }, + inverse.getInverse( this.bones[ b ].matrixWorld ); - "specularMap" : { type: "t", value: null }, - "alphaMap" : { type: "t", value: null }, + } - "envMap" : { type: "t", value: null }, - "flipEnvMap" : { type: "f", value: - 1 }, - "reflectivity" : { type: "f", value: 1.0 }, - "refractionRatio" : { type: "f", value: 0.98 } + this.boneInverses.push( inverse ); - }, + } - aomap: { +}; - "aoMap" : { type: "t", value: null }, - "aoMapIntensity" : { type: "f", value: 1 }, +THREE.Skeleton.prototype.pose = function () { - }, + var bone; - lightmap: { + // recover the bind-time world matrices - "lightMap" : { type: "t", value: null }, - "lightMapIntensity" : { type: "f", value: 1 }, + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { - }, + bone = this.bones[ b ]; - emissivemap: { + if ( bone ) { - "emissiveMap" : { type: "t", value: null }, + bone.matrixWorld.getInverse( this.boneInverses[ b ] ); - }, + } - bumpmap: { + } - "bumpMap" : { type: "t", value: null }, - "bumpScale" : { type: "f", value: 1 } + // compute the local matrices, positions, rotations and scales - }, + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { - normalmap: { + bone = this.bones[ b ]; - "normalMap" : { type: "t", value: null }, - "normalScale" : { type: "v2", value: new THREE.Vector2( 1, 1 ) } + if ( bone ) { - }, + if ( bone.parent ) { - displacementmap: { + bone.matrix.getInverse( bone.parent.matrixWorld ); + bone.matrix.multiply( bone.matrixWorld ); - "displacementMap" : { type: "t", value: null }, - "displacementScale" : { type: "f", value: 1 }, - "displacementBias" : { type: "f", value: 0 } + } else { - }, + bone.matrix.copy( bone.matrixWorld ); - 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 ) } + bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); - }, + } - lights: { + } - "ambientLightColor" : { type: "fv", value: [] }, +}; - "directionalLightDirection" : { type: "fv", value: [] }, - "directionalLightColor" : { type: "fv", value: [] }, +THREE.Skeleton.prototype.update = ( function () { - "hemisphereLightDirection" : { type: "fv", value: [] }, - "hemisphereLightSkyColor" : { type: "fv", value: [] }, - "hemisphereLightGroundColor" : { type: "fv", value: [] }, + var offsetMatrix = new THREE.Matrix4(); - "pointLightColor" : { type: "fv", value: [] }, - "pointLightPosition" : { type: "fv", value: [] }, - "pointLightDistance" : { type: "fv1", value: [] }, - "pointLightDecay" : { type: "fv1", value: [] }, + return function update() { - "spotLightColor" : { type: "fv", value: [] }, - "spotLightPosition" : { type: "fv", value: [] }, - "spotLightDirection" : { type: "fv", value: [] }, - "spotLightDistance" : { type: "fv1", value: [] }, - "spotLightAngleCos" : { type: "fv1", value: [] }, - "spotLightExponent" : { type: "fv1", value: [] }, - "spotLightDecay" : { type: "fv1", value: [] } + // flatten bone matrices to array - }, + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { - points: { + // compute the offset between the current and the original transform - "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 }, - "offsetRepeat" : { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) }, + var matrix = this.bones[ b ] ? this.bones[ b ].matrixWorld : this.identityMatrix; - "fogDensity" : { type: "f", value: 0.00025 }, - "fogNear" : { type: "f", value: 1 }, - "fogFar" : { type: "f", value: 2000 }, - "fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) } + offsetMatrix.multiplyMatrices( matrix, this.boneInverses[ b ] ); + offsetMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 ); - }, + } - shadowmap: { + if ( this.useVertexTexture ) { - "shadowMap": { type: "tv", value: [] }, - "shadowMapSize": { type: "v2v", value: [] }, + this.boneTexture.needsUpdate = true; - "shadowBias" : { type: "fv1", value: [] }, - "shadowDarkness": { type: "fv1", value: [] }, + } - "shadowMatrix" : { type: "m4v", value: [] } + }; - } +} )(); + +THREE.Skeleton.prototype.clone = function () { + + return new THREE.Skeleton( this.bones, this.boneInverses, this.useVertexTexture ); }; -// File:src/renderers/shaders/ShaderLib.js +// File:src/objects/SkinnedMesh.js /** - * Webgl Shader Library for three.js - * - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author ikerr / http://verold.com */ +THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) { -THREE.ShaderLib = { + THREE.Mesh.call( this, geometry, material ); - 'basic': { + this.type = 'SkinnedMesh'; - uniforms: THREE.UniformsUtils.merge( [ + this.bindMode = "attached"; + this.bindMatrix = new THREE.Matrix4(); + this.bindMatrixInverse = new THREE.Matrix4(); - THREE.UniformsLib[ "common" ], - THREE.UniformsLib[ "aomap" ], - THREE.UniformsLib[ "fog" ], - THREE.UniformsLib[ "shadowmap" ] + // init bones - ] ), + // TODO: remove bone creation as there is no reason (other than + // convenience) for THREE.SkinnedMesh to do this. - vertexShader: [ + var bones = []; - THREE.ShaderChunk[ "common" ], - THREE.ShaderChunk[ "uv_pars_vertex" ], - THREE.ShaderChunk[ "uv2_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" ], + if ( this.geometry && this.geometry.bones !== undefined ) { - "void main() {", + var bone, gbone; - THREE.ShaderChunk[ "uv_vertex" ], - THREE.ShaderChunk[ "uv2_vertex" ], - THREE.ShaderChunk[ "color_vertex" ], - THREE.ShaderChunk[ "skinbase_vertex" ], + for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++ b ) { - " #ifdef USE_ENVMAP", + gbone = this.geometry.bones[ b ]; - THREE.ShaderChunk[ "beginnormal_vertex" ], - THREE.ShaderChunk[ "morphnormal_vertex" ], - THREE.ShaderChunk[ "skinnormal_vertex" ], - THREE.ShaderChunk[ "defaultnormal_vertex" ], + bone = new THREE.Bone( this ); + bones.push( bone ); - " #endif", + bone.name = gbone.name; + bone.position.fromArray( gbone.pos ); + bone.quaternion.fromArray( gbone.rotq ); + if ( gbone.scl !== undefined ) bone.scale.fromArray( gbone.scl ); - THREE.ShaderChunk[ "begin_vertex" ], - THREE.ShaderChunk[ "morphtarget_vertex" ], - THREE.ShaderChunk[ "skinning_vertex" ], - THREE.ShaderChunk[ "project_vertex" ], - THREE.ShaderChunk[ "logdepthbuf_vertex" ], + } - THREE.ShaderChunk[ "worldpos_vertex" ], - THREE.ShaderChunk[ "envmap_vertex" ], - THREE.ShaderChunk[ "shadowmap_vertex" ], + for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++ b ) { - "}" + gbone = this.geometry.bones[ b ]; - ].join( "\n" ), + if ( gbone.parent !== - 1 ) { - fragmentShader: [ + bones[ gbone.parent ].add( bones[ b ] ); - "uniform vec3 diffuse;", - "uniform float opacity;", + } else { - THREE.ShaderChunk[ "common" ], - THREE.ShaderChunk[ "color_pars_fragment" ], - THREE.ShaderChunk[ "uv_pars_fragment" ], - THREE.ShaderChunk[ "uv2_pars_fragment" ], - THREE.ShaderChunk[ "map_pars_fragment" ], - THREE.ShaderChunk[ "alphamap_pars_fragment" ], - THREE.ShaderChunk[ "aomap_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" ], + this.add( bones[ b ] ); - "void main() {", + } - " vec3 outgoingLight = vec3( 0.0 );", - " vec4 diffuseColor = vec4( diffuse, opacity );", - " vec3 totalAmbientLight = vec3( 1.0 );", // hardwired + } - THREE.ShaderChunk[ "logdepthbuf_fragment" ], - THREE.ShaderChunk[ "map_fragment" ], - THREE.ShaderChunk[ "color_fragment" ], - THREE.ShaderChunk[ "alphamap_fragment" ], - THREE.ShaderChunk[ "alphatest_fragment" ], - THREE.ShaderChunk[ "specularmap_fragment" ], - THREE.ShaderChunk[ "aomap_fragment" ], + } - " outgoingLight = diffuseColor.rgb * totalAmbientLight;", // simple shader + this.normalizeSkinWeights(); - THREE.ShaderChunk[ "envmap_fragment" ], - THREE.ShaderChunk[ "shadowmap_fragment" ], // TODO: Shadows on an otherwise unlit surface doesn't make sense. + this.updateMatrixWorld( true ); + this.bind( new THREE.Skeleton( bones, undefined, useVertexTexture ), this.matrixWorld ); - THREE.ShaderChunk[ "linear_to_gamma_fragment" ], +}; - THREE.ShaderChunk[ "fog_fragment" ], - " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", +THREE.SkinnedMesh.prototype = Object.create( THREE.Mesh.prototype ); +THREE.SkinnedMesh.prototype.constructor = THREE.SkinnedMesh; - "}" +THREE.SkinnedMesh.prototype.bind = function( skeleton, bindMatrix ) { - ].join( "\n" ) + this.skeleton = skeleton; - }, + if ( bindMatrix === undefined ) { - 'lambert': { + this.updateMatrixWorld( true ); + + this.skeleton.calculateInverses(); - uniforms: THREE.UniformsUtils.merge( [ + bindMatrix = this.matrixWorld; - THREE.UniformsLib[ "common" ], - THREE.UniformsLib[ "fog" ], - THREE.UniformsLib[ "lights" ], - THREE.UniformsLib[ "shadowmap" ], + } - { - "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) } - } + this.bindMatrix.copy( bindMatrix ); + this.bindMatrixInverse.getInverse( bindMatrix ); - ] ), +}; - vertexShader: [ +THREE.SkinnedMesh.prototype.pose = function () { - "#define LAMBERT", + this.skeleton.pose(); - "varying vec3 vLightFront;", +}; - "#ifdef DOUBLE_SIDED", +THREE.SkinnedMesh.prototype.normalizeSkinWeights = function () { - " varying vec3 vLightBack;", + if ( this.geometry instanceof THREE.Geometry ) { - "#endif", + for ( var i = 0; i < this.geometry.skinIndices.length; i ++ ) { - THREE.ShaderChunk[ "common" ], - THREE.ShaderChunk[ "uv_pars_vertex" ], - THREE.ShaderChunk[ "uv2_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" ], + var sw = this.geometry.skinWeights[ i ]; - "void main() {", + var scale = 1.0 / sw.lengthManhattan(); - THREE.ShaderChunk[ "uv_vertex" ], - THREE.ShaderChunk[ "uv2_vertex" ], - THREE.ShaderChunk[ "color_vertex" ], + if ( scale !== Infinity ) { - THREE.ShaderChunk[ "beginnormal_vertex" ], - THREE.ShaderChunk[ "morphnormal_vertex" ], - THREE.ShaderChunk[ "skinbase_vertex" ], - THREE.ShaderChunk[ "skinnormal_vertex" ], - THREE.ShaderChunk[ "defaultnormal_vertex" ], + sw.multiplyScalar( scale ); - THREE.ShaderChunk[ "begin_vertex" ], - THREE.ShaderChunk[ "morphtarget_vertex" ], - THREE.ShaderChunk[ "skinning_vertex" ], - THREE.ShaderChunk[ "project_vertex" ], - THREE.ShaderChunk[ "logdepthbuf_vertex" ], + } else { - THREE.ShaderChunk[ "worldpos_vertex" ], - THREE.ShaderChunk[ "envmap_vertex" ], - THREE.ShaderChunk[ "lights_lambert_vertex" ], - THREE.ShaderChunk[ "shadowmap_vertex" ], + sw.set( 1 ); // this will be normalized by the shader anyway - "}" + } - ].join( "\n" ), + } - fragmentShader: [ + } else { - "uniform vec3 diffuse;", - "uniform vec3 emissive;", - "uniform float opacity;", + // skinning weights assumed to be normalized for THREE.BufferGeometry - "varying vec3 vLightFront;", + } - "#ifdef DOUBLE_SIDED", +}; - " varying vec3 vLightBack;", +THREE.SkinnedMesh.prototype.updateMatrixWorld = function( force ) { - "#endif", + THREE.Mesh.prototype.updateMatrixWorld.call( this, true ); - THREE.ShaderChunk[ "common" ], - THREE.ShaderChunk[ "color_pars_fragment" ], - THREE.ShaderChunk[ "uv_pars_fragment" ], - THREE.ShaderChunk[ "uv2_pars_fragment" ], - THREE.ShaderChunk[ "map_pars_fragment" ], - THREE.ShaderChunk[ "alphamap_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" ], + if ( this.bindMode === "attached" ) { - "void main() {", + this.bindMatrixInverse.getInverse( this.matrixWorld ); - " vec3 outgoingLight = vec3( 0.0 );", // outgoing light does not have an alpha, the surface does - " vec4 diffuseColor = vec4( diffuse, opacity );", + } else if ( this.bindMode === "detached" ) { - THREE.ShaderChunk[ "logdepthbuf_fragment" ], - THREE.ShaderChunk[ "map_fragment" ], - THREE.ShaderChunk[ "color_fragment" ], - THREE.ShaderChunk[ "alphamap_fragment" ], - THREE.ShaderChunk[ "alphatest_fragment" ], - THREE.ShaderChunk[ "specularmap_fragment" ], + this.bindMatrixInverse.getInverse( this.bindMatrix ); - " #ifdef DOUBLE_SIDED", + } else { - " if ( gl_FrontFacing )", - " outgoingLight += diffuseColor.rgb * vLightFront + emissive;", - " else", - " outgoingLight += diffuseColor.rgb * vLightBack + emissive;", + console.warn( 'THREE.SkinnedMesh unrecognized bindMode: ' + this.bindMode ); + + } + +}; + +THREE.SkinnedMesh.prototype.clone = function() { - " #else", + return new this.constructor( this.geometry, this.material, this.useVertexTexture ).copy( this ); - " outgoingLight += diffuseColor.rgb * vLightFront + emissive;", +}; - " #endif", +// File:src/objects/LOD.js - THREE.ShaderChunk[ "envmap_fragment" ], - THREE.ShaderChunk[ "shadowmap_fragment" ], +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ - THREE.ShaderChunk[ "linear_to_gamma_fragment" ], +THREE.LOD = function () { - THREE.ShaderChunk[ "fog_fragment" ], + THREE.Object3D.call( this ); - " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", + this.type = 'LOD'; - "}" + Object.defineProperties( this, { + levels: { + enumerable: true, + value: [] + }, + objects: { + get: function () { - ].join( "\n" ) + console.warn( 'THREE.LOD: .objects has been renamed to .levels.' ); + return this.levels; - }, + } + } + } ); - 'phong': { +}; - uniforms: THREE.UniformsUtils.merge( [ - THREE.UniformsLib[ "common" ], - THREE.UniformsLib[ "aomap" ], - THREE.UniformsLib[ "lightmap" ], - THREE.UniformsLib[ "emissivemap" ], - THREE.UniformsLib[ "bumpmap" ], - THREE.UniformsLib[ "normalmap" ], - THREE.UniformsLib[ "displacementmap" ], - THREE.UniformsLib[ "fog" ], - THREE.UniformsLib[ "lights" ], - THREE.UniformsLib[ "shadowmap" ], +THREE.LOD.prototype = Object.create( THREE.Object3D.prototype ); +THREE.LOD.prototype.constructor = THREE.LOD; - { - "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) }, - "specular" : { type: "c", value: new THREE.Color( 0x111111 ) }, - "shininess": { type: "f", value: 30 } - } +THREE.LOD.prototype.addLevel = function ( object, distance ) { - ] ), + if ( distance === undefined ) distance = 0; - vertexShader: [ + distance = Math.abs( distance ); - "#define PHONG", + var levels = this.levels; - "varying vec3 vViewPosition;", + for ( var l = 0; l < levels.length; l ++ ) { - "#ifndef FLAT_SHADED", + if ( distance < levels[ l ].distance ) { - " varying vec3 vNormal;", + break; - "#endif", + } - THREE.ShaderChunk[ "common" ], - THREE.ShaderChunk[ "uv_pars_vertex" ], - THREE.ShaderChunk[ "uv2_pars_vertex" ], - THREE.ShaderChunk[ "displacementmap_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() {", + levels.splice( l, 0, { distance: distance, object: object } ); - THREE.ShaderChunk[ "uv_vertex" ], - THREE.ShaderChunk[ "uv2_vertex" ], - THREE.ShaderChunk[ "color_vertex" ], + this.add( object ); - THREE.ShaderChunk[ "beginnormal_vertex" ], - THREE.ShaderChunk[ "morphnormal_vertex" ], - THREE.ShaderChunk[ "skinbase_vertex" ], - THREE.ShaderChunk[ "skinnormal_vertex" ], - THREE.ShaderChunk[ "defaultnormal_vertex" ], +}; - "#ifndef FLAT_SHADED", // Normal computed with derivatives when FLAT_SHADED +THREE.LOD.prototype.getObjectForDistance = function ( distance ) { - " vNormal = normalize( transformedNormal );", + var levels = this.levels; - "#endif", + for ( var i = 1, l = levels.length; i < l; i ++ ) { - THREE.ShaderChunk[ "begin_vertex" ], - THREE.ShaderChunk[ "displacementmap_vertex" ], - THREE.ShaderChunk[ "morphtarget_vertex" ], - THREE.ShaderChunk[ "skinning_vertex" ], - THREE.ShaderChunk[ "project_vertex" ], - THREE.ShaderChunk[ "logdepthbuf_vertex" ], + if ( distance < levels[ i ].distance ) { - " vViewPosition = - mvPosition.xyz;", + break; - THREE.ShaderChunk[ "worldpos_vertex" ], - THREE.ShaderChunk[ "envmap_vertex" ], - THREE.ShaderChunk[ "lights_phong_vertex" ], - THREE.ShaderChunk[ "shadowmap_vertex" ], + } - "}" + } - ].join( "\n" ), + return levels[ i - 1 ].object; - fragmentShader: [ +}; - "#define PHONG", +THREE.LOD.prototype.raycast = ( function () { - "uniform vec3 diffuse;", - "uniform vec3 emissive;", - "uniform vec3 specular;", - "uniform float shininess;", - "uniform float opacity;", + var matrixPosition = new THREE.Vector3(); - THREE.ShaderChunk[ "common" ], - THREE.ShaderChunk[ "color_pars_fragment" ], - THREE.ShaderChunk[ "uv_pars_fragment" ], - THREE.ShaderChunk[ "uv2_pars_fragment" ], - THREE.ShaderChunk[ "map_pars_fragment" ], - THREE.ShaderChunk[ "alphamap_pars_fragment" ], - THREE.ShaderChunk[ "aomap_pars_fragment" ], - THREE.ShaderChunk[ "lightmap_pars_fragment" ], - THREE.ShaderChunk[ "emissivemap_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" ], + return function raycast( raycaster, intersects ) { - "void main() {", + matrixPosition.setFromMatrixPosition( this.matrixWorld ); - " vec3 outgoingLight = vec3( 0.0 );", - " vec4 diffuseColor = vec4( diffuse, opacity );", - " vec3 totalAmbientLight = ambientLightColor;", - " vec3 totalEmissiveLight = emissive;", + var distance = raycaster.ray.origin.distanceTo( matrixPosition ); - THREE.ShaderChunk[ "logdepthbuf_fragment" ], - THREE.ShaderChunk[ "map_fragment" ], - THREE.ShaderChunk[ "color_fragment" ], - THREE.ShaderChunk[ "alphamap_fragment" ], - THREE.ShaderChunk[ "alphatest_fragment" ], - THREE.ShaderChunk[ "specularmap_fragment" ], - THREE.ShaderChunk[ "normal_phong_fragment" ], - THREE.ShaderChunk[ "lightmap_fragment" ], - THREE.ShaderChunk[ "hemilight_fragment" ], - THREE.ShaderChunk[ "aomap_fragment" ], - THREE.ShaderChunk[ "emissivemap_fragment" ], + this.getObjectForDistance( distance ).raycast( raycaster, intersects ); - THREE.ShaderChunk[ "lights_phong_fragment" ], + }; - THREE.ShaderChunk[ "envmap_fragment" ], - THREE.ShaderChunk[ "shadowmap_fragment" ], +}() ); - THREE.ShaderChunk[ "linear_to_gamma_fragment" ], +THREE.LOD.prototype.update = function () { - THREE.ShaderChunk[ "fog_fragment" ], + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); - " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", + return function update( camera ) { - "}" + var levels = this.levels; - ].join( "\n" ) + if ( levels.length > 1 ) { - }, + v1.setFromMatrixPosition( camera.matrixWorld ); + v2.setFromMatrixPosition( this.matrixWorld ); - 'points': { + var distance = v1.distanceTo( v2 ); - uniforms: THREE.UniformsUtils.merge( [ + levels[ 0 ].object.visible = true; - THREE.UniformsLib[ "points" ], - THREE.UniformsLib[ "shadowmap" ] + for ( var i = 1, l = levels.length; i < l; i ++ ) { - ] ), + if ( distance >= levels[ i ].distance ) { - vertexShader: [ + levels[ i - 1 ].object.visible = false; + levels[ i ].object.visible = true; - "uniform float size;", - "uniform float scale;", + } else { - THREE.ShaderChunk[ "common" ], - THREE.ShaderChunk[ "color_pars_vertex" ], - THREE.ShaderChunk[ "shadowmap_pars_vertex" ], - THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + break; - "void main() {", + } - THREE.ShaderChunk[ "color_vertex" ], + } - " vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + for ( ; i < l; i ++ ) { - " #ifdef USE_SIZEATTENUATION", - " gl_PointSize = size * ( scale / length( mvPosition.xyz ) );", - " #else", - " gl_PointSize = size;", - " #endif", + levels[ i ].object.visible = false; - " gl_Position = projectionMatrix * mvPosition;", + } - THREE.ShaderChunk[ "logdepthbuf_vertex" ], - THREE.ShaderChunk[ "worldpos_vertex" ], - THREE.ShaderChunk[ "shadowmap_vertex" ], + } - "}" + }; - ].join( "\n" ), +}(); - fragmentShader: [ +THREE.LOD.prototype.copy = function ( source ) { - "uniform vec3 psColor;", - "uniform float opacity;", + THREE.Object3D.prototype.copy.call( this, source, false ); - THREE.ShaderChunk[ "common" ], - 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 levels = source.levels; - "void main() {", + for ( var i = 0, l = levels.length; i < l; i ++ ) { - " vec3 outgoingLight = vec3( 0.0 );", - " vec4 diffuseColor = vec4( psColor, opacity );", + var level = levels[ i ]; - THREE.ShaderChunk[ "logdepthbuf_fragment" ], - THREE.ShaderChunk[ "map_particle_fragment" ], - THREE.ShaderChunk[ "color_fragment" ], - THREE.ShaderChunk[ "alphatest_fragment" ], + this.addLevel( level.object.clone(), level.distance ); - " outgoingLight = diffuseColor.rgb;", // simple shader + } - THREE.ShaderChunk[ "shadowmap_fragment" ], - THREE.ShaderChunk[ "fog_fragment" ], + return this; - " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", +}; - "}" +THREE.LOD.prototype.toJSON = function ( meta ) { - ].join( "\n" ) + var data = THREE.Object3D.prototype.toJSON.call( this, meta ); - }, + data.object.levels = []; - 'dashed': { + var levels = this.levels; - uniforms: THREE.UniformsUtils.merge( [ + for ( var i = 0, l = levels.length; i < l; i ++ ) { - THREE.UniformsLib[ "common" ], - THREE.UniformsLib[ "fog" ], + var level = levels[ i ]; - { - "scale" : { type: "f", value: 1 }, - "dashSize" : { type: "f", value: 1 }, - "totalSize": { type: "f", value: 2 } - } + data.object.levels.push( { + object: level.object.uuid, + distance: level.distance + } ); - ] ), + } - vertexShader: [ + return data; - "uniform float scale;", - "attribute float lineDistance;", +}; - "varying float vLineDistance;", +// File:src/objects/Sprite.js - THREE.ShaderChunk[ "common" ], - THREE.ShaderChunk[ "color_pars_vertex" ], - THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ - "void main() {", +THREE.Sprite = ( function () { - THREE.ShaderChunk[ "color_vertex" ], + 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 ] ); - " vLineDistance = scale * lineDistance;", + var geometry = new THREE.BufferGeometry(); + geometry.setIndex( new THREE.BufferAttribute( indices, 1 ) ); + geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); + geometry.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); - " vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", - " gl_Position = projectionMatrix * mvPosition;", + return function Sprite( material ) { - THREE.ShaderChunk[ "logdepthbuf_vertex" ], + THREE.Object3D.call( this ); - "}" + this.type = 'Sprite'; - ].join( "\n" ), + this.geometry = geometry; + this.material = ( material !== undefined ) ? material : new THREE.SpriteMaterial(); - fragmentShader: [ + }; - "uniform vec3 diffuse;", - "uniform float opacity;", +} )(); - "uniform float dashSize;", - "uniform float totalSize;", +THREE.Sprite.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Sprite.prototype.constructor = THREE.Sprite; - "varying float vLineDistance;", +THREE.Sprite.prototype.raycast = ( function () { - THREE.ShaderChunk[ "common" ], - THREE.ShaderChunk[ "color_pars_fragment" ], - THREE.ShaderChunk[ "fog_pars_fragment" ], - THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], + var matrixPosition = new THREE.Vector3(); - "void main() {", + return function raycast( raycaster, intersects ) { - " if ( mod( vLineDistance, totalSize ) > dashSize ) {", + matrixPosition.setFromMatrixPosition( this.matrixWorld ); - " discard;", + var distanceSq = raycaster.ray.distanceSqToPoint( matrixPosition ); + var guessSizeSq = this.scale.x * this.scale.y; - " }", + if ( distanceSq > guessSizeSq ) { - " vec3 outgoingLight = vec3( 0.0 );", - " vec4 diffuseColor = vec4( diffuse, opacity );", + return; - THREE.ShaderChunk[ "logdepthbuf_fragment" ], - THREE.ShaderChunk[ "color_fragment" ], + } - " outgoingLight = diffuseColor.rgb;", // simple shader + intersects.push( { - THREE.ShaderChunk[ "fog_fragment" ], + distance: Math.sqrt( distanceSq ), + point: this.position, + face: null, + object: this - " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", + } ); - "}" + }; - ].join( "\n" ) +}() ); - }, +THREE.Sprite.prototype.clone = function () { - 'depth': { + return new this.constructor( this.material ).copy( this ); - uniforms: { +}; - "mNear": { type: "f", value: 1.0 }, - "mFar" : { type: "f", value: 2000.0 }, - "opacity" : { type: "f", value: 1.0 } +THREE.Sprite.prototype.toJSON = function ( meta ) { - }, + var data = THREE.Object3D.prototype.toJSON.call( this, meta ); - vertexShader: [ + // only serialize if not in meta materials cache + if ( meta.materials[ this.material.uuid ] === undefined ) { - THREE.ShaderChunk[ "common" ], - THREE.ShaderChunk[ "morphtarget_pars_vertex" ], - THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + meta.materials[ this.material.uuid ] = this.material.toJSON(); - "void main() {", + } - THREE.ShaderChunk[ "begin_vertex" ], - THREE.ShaderChunk[ "morphtarget_vertex" ], - THREE.ShaderChunk[ "project_vertex" ], - THREE.ShaderChunk[ "logdepthbuf_vertex" ], + data.object.material = this.material.uuid; - "}" + return data; - ].join( "\n" ), +}; - fragmentShader: [ +// Backwards compatibility - "uniform float mNear;", - "uniform float mFar;", - "uniform float opacity;", +THREE.Particle = THREE.Sprite; - THREE.ShaderChunk[ "common" ], - THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], +// File:src/objects/LensFlare.js - "void main() {", +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ - THREE.ShaderChunk[ "logdepthbuf_fragment" ], +THREE.LensFlare = function ( texture, size, distance, blending, color ) { - " #ifdef USE_LOGDEPTHBUF_EXT", + THREE.Object3D.call( this ); - " float depth = gl_FragDepthEXT / gl_FragCoord.w;", + this.lensFlares = []; - " #else", + this.positionScreen = new THREE.Vector3(); + this.customUpdateCallback = undefined; - " float depth = gl_FragCoord.z / gl_FragCoord.w;", + if ( texture !== undefined ) { - " #endif", + this.add( texture, size, distance, blending, color ); - " float color = 1.0 - smoothstep( mNear, mFar, depth );", - " gl_FragColor = vec4( vec3( color ), opacity );", + } - "}" +}; - ].join( "\n" ) +THREE.LensFlare.prototype = Object.create( THREE.Object3D.prototype ); +THREE.LensFlare.prototype.constructor = THREE.LensFlare; - }, - 'normal': { +/* + * Add: adds another flare + */ - uniforms: { +THREE.LensFlare.prototype.add = function ( texture, size, distance, blending, color, opacity ) { - "opacity" : { type: "f", value: 1.0 } + if ( size === undefined ) size = - 1; + if ( distance === undefined ) distance = 0; + if ( opacity === undefined ) opacity = 1; + if ( color === undefined ) color = new THREE.Color( 0xffffff ); + if ( blending === undefined ) blending = THREE.NormalBlending; - }, + distance = Math.min( distance, Math.max( 0, distance ) ); - vertexShader: [ + 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 in front z = 1 is back + scale: 1, // scale + rotation: 0, // rotation + opacity: opacity, // opacity + color: color, // color + blending: blending // blending + } ); - "varying vec3 vNormal;", +}; - THREE.ShaderChunk[ "common" ], - THREE.ShaderChunk[ "morphtarget_pars_vertex" ], - THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], +/* + * 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. + */ - "void main() {", +THREE.LensFlare.prototype.updateLensFlares = function () { - " vNormal = normalize( normalMatrix * normal );", + var f, fl = this.lensFlares.length; + var flare; + var vecX = - this.positionScreen.x * 2; + var vecY = - this.positionScreen.y * 2; - THREE.ShaderChunk[ "begin_vertex" ], - THREE.ShaderChunk[ "morphtarget_vertex" ], - THREE.ShaderChunk[ "project_vertex" ], - THREE.ShaderChunk[ "logdepthbuf_vertex" ], + for ( f = 0; f < fl; f ++ ) { - "}" + flare = this.lensFlares[ f ]; - ].join( "\n" ), + flare.x = this.positionScreen.x + vecX * flare.distance; + flare.y = this.positionScreen.y + vecY * flare.distance; - fragmentShader: [ + flare.wantedRotation = flare.x * Math.PI * 0.25; + flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25; - "uniform float opacity;", - "varying vec3 vNormal;", + } - THREE.ShaderChunk[ "common" ], - THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], +}; - "void main() {", +THREE.LensFlare.prototype.copy = function ( source ) { - " gl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );", + THREE.Object3D.prototype.copy.call( this, source ); - THREE.ShaderChunk[ "logdepthbuf_fragment" ], + this.positionScreen.copy( source.positionScreen ); + this.customUpdateCallback = source.customUpdateCallback; - "}" + for ( var i = 0, l = source.lensFlares.length; i < l; i ++ ) { - ].join( "\n" ) + this.lensFlares.push( source.lensFlares[ i ] ); - }, + } - /* ------------------------------------------------------------------------- - // Cube map shader - ------------------------------------------------------------------------- */ + return this; - 'cube': { +}; - uniforms: { "tCube": { type: "t", value: null }, - "tFlip": { type: "f", value: - 1 } }, +// File:src/scenes/Scene.js - vertexShader: [ +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Scene = function () { - "varying vec3 vWorldPosition;", + THREE.Object3D.call( this ); - THREE.ShaderChunk[ "common" ], - THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], + this.type = 'Scene'; - "void main() {", + this.fog = null; + this.overrideMaterial = null; - " vWorldPosition = transformDirection( position, modelMatrix );", + this.autoUpdate = true; // checked by the renderer - " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", +}; - THREE.ShaderChunk[ "logdepthbuf_vertex" ], +THREE.Scene.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Scene.prototype.constructor = THREE.Scene; - "}" +THREE.Scene.prototype.copy = function ( source ) { - ].join( "\n" ), + THREE.Object3D.prototype.copy.call( this, source ); - fragmentShader: [ + if ( source.fog !== null ) this.fog = source.fog.clone(); + if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone(); - "uniform samplerCube tCube;", - "uniform float tFlip;", + this.autoUpdate = source.autoUpdate; + this.matrixAutoUpdate = source.matrixAutoUpdate; - "varying vec3 vWorldPosition;", + return this; - THREE.ShaderChunk[ "common" ], - THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], +}; - "void main() {", +// File:src/scenes/Fog.js - " gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );", +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ - THREE.ShaderChunk[ "logdepthbuf_fragment" ], +THREE.Fog = function ( color, near, far ) { - "}" + this.name = ''; - ].join( "\n" ) + this.color = new THREE.Color( color ); - }, + this.near = ( near !== undefined ) ? near : 1; + this.far = ( far !== undefined ) ? far : 1000; - /* ------------------------------------------------------------------------- - // Cube map shader - ------------------------------------------------------------------------- */ +}; - 'equirect': { +THREE.Fog.prototype.clone = function () { - uniforms: { "tEquirect": { type: "t", value: null }, - "tFlip": { type: "f", value: - 1 } }, + return new THREE.Fog( this.color.getHex(), this.near, this.far ); - vertexShader: [ +}; - "varying vec3 vWorldPosition;", +// File:src/scenes/FogExp2.js - THREE.ShaderChunk[ "common" ], - THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ - "void main() {", +THREE.FogExp2 = function ( color, density ) { - " vWorldPosition = transformDirection( position, modelMatrix );", + this.name = ''; - " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + this.color = new THREE.Color( color ); + this.density = ( density !== undefined ) ? density : 0.00025; - THREE.ShaderChunk[ "logdepthbuf_vertex" ], +}; - "}" +THREE.FogExp2.prototype.clone = function () { - ].join( "\n" ), + return new THREE.FogExp2( this.color.getHex(), this.density ); - fragmentShader: [ +}; - "uniform sampler2D tEquirect;", - "uniform float tFlip;", +// File:src/renderers/shaders/ShaderChunk.js - "varying vec3 vWorldPosition;", +THREE.ShaderChunk = {}; - THREE.ShaderChunk[ "common" ], - THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], +// File:src/renderers/shaders/ShaderChunk/alphamap_fragment.glsl - "void main() {", +THREE.ShaderChunk[ 'alphamap_fragment'] = "#ifdef USE_ALPHAMAP\n\n diffuseColor.a *= texture2D( alphaMap, vUv ).g;\n\n#endif\n"; - // " gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );", - "vec3 direction = normalize( vWorldPosition );", - "vec2 sampleUV;", - "sampleUV.y = saturate( tFlip * direction.y * -0.5 + 0.5 );", - "sampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;", - "gl_FragColor = texture2D( tEquirect, sampleUV );", +// File:src/renderers/shaders/ShaderChunk/alphamap_pars_fragment.glsl - THREE.ShaderChunk[ "logdepthbuf_fragment" ], +THREE.ShaderChunk[ 'alphamap_pars_fragment'] = "#ifdef USE_ALPHAMAP\n\n uniform sampler2D alphaMap;\n\n#endif\n"; - "}" +// File:src/renderers/shaders/ShaderChunk/alphatest_fragment.glsl - ].join( "\n" ) +THREE.ShaderChunk[ 'alphatest_fragment'] = "#ifdef ALPHATEST\n\n if ( diffuseColor.a < ALPHATEST ) discard;\n\n#endif\n"; - }, +// File:src/renderers/shaders/ShaderChunk/aomap_fragment.glsl - /* 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/ - */ +THREE.ShaderChunk[ 'aomap_fragment'] = "#ifdef USE_AOMAP\n\n totalAmbientLight *= ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n\n#endif\n"; - 'depthRGBA': { +// File:src/renderers/shaders/ShaderChunk/aomap_pars_fragment.glsl - uniforms: {}, +THREE.ShaderChunk[ 'aomap_pars_fragment'] = "#ifdef USE_AOMAP\n\n uniform sampler2D aoMap;\n uniform float aoMapIntensity;\n\n#endif"; - vertexShader: [ +// File:src/renderers/shaders/ShaderChunk/begin_vertex.glsl - THREE.ShaderChunk[ "common" ], - THREE.ShaderChunk[ "morphtarget_pars_vertex" ], - THREE.ShaderChunk[ "skinning_pars_vertex" ], - THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], +THREE.ShaderChunk[ 'begin_vertex'] = "\nvec3 transformed = vec3( position );\n"; - "void main() {", +// File:src/renderers/shaders/ShaderChunk/beginnormal_vertex.glsl - THREE.ShaderChunk[ "skinbase_vertex" ], +THREE.ShaderChunk[ 'beginnormal_vertex'] = "\nvec3 objectNormal = vec3( normal );\n"; - THREE.ShaderChunk[ "begin_vertex" ], - THREE.ShaderChunk[ "morphtarget_vertex" ], - THREE.ShaderChunk[ "skinning_vertex" ], - THREE.ShaderChunk[ "project_vertex" ], - THREE.ShaderChunk[ "logdepthbuf_vertex" ], +// File:src/renderers/shaders/ShaderChunk/bumpmap_pars_fragment.glsl - "}" +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\n"; - ].join( "\n" ), +// File:src/renderers/shaders/ShaderChunk/color_fragment.glsl - fragmentShader: [ +THREE.ShaderChunk[ 'color_fragment'] = "#ifdef USE_COLOR\n\n diffuseColor.rgb *= vColor;\n\n#endif"; - THREE.ShaderChunk[ "common" ], - THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], +// File:src/renderers/shaders/ShaderChunk/color_pars_fragment.glsl - "vec4 pack_depth( const in float depth ) {", +THREE.ShaderChunk[ 'color_pars_fragment'] = "#ifdef USE_COLOR\n\n varying vec3 vColor;\n\n#endif\n"; - " 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;", +// File:src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl - "}", +THREE.ShaderChunk[ 'color_pars_vertex'] = "#ifdef USE_COLOR\n\n varying vec3 vColor;\n\n#endif"; - "void main() {", +// File:src/renderers/shaders/ShaderChunk/color_vertex.glsl - THREE.ShaderChunk[ "logdepthbuf_fragment" ], +THREE.ShaderChunk[ 'color_vertex'] = "#ifdef USE_COLOR\n\n vColor.xyz = color.xyz;\n\n#endif"; - " #ifdef USE_LOGDEPTHBUF_EXT", +// File:src/renderers/shaders/ShaderChunk/common.glsl - " gl_FragData[ 0 ] = pack_depth( gl_FragDepthEXT );", +THREE.ShaderChunk[ 'common'] = "#define PI 3.14159\n#define PI2 6.28318\n#define RECIPROCAL_PI2 0.15915494\n#define LOG2 1.442695\n#define EPSILON 1e-6\n\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#define whiteCompliment(a) ( 1.0 - saturate( a ) )\n\nvec3 transformDirection( in vec3 normal, in mat4 matrix ) {\n\n return normalize( ( matrix * vec4( normal, 0.0 ) ).xyz );\n\n}\n\n// http://en.wikibooks.org/wiki/GLSL_Programming/Applying_Matrix_Transformations\nvec3 inverseTransformDirection( in vec3 normal, in mat4 matrix ) {\n\n return normalize( ( vec4( normal, 0.0 ) * matrix ).xyz );\n\n}\n\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n float distance = dot( planeNormal, point - pointOnPlane );\n\n return - distance * planeNormal + point;\n\n}\n\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n return sign( dot( point - pointOnPlane, planeNormal ) );\n\n}\n\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n\n return lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n\n}\n\nfloat calcLightAttenuation( float lightDistance, float cutoffDistance, float decayExponent ) {\n\n if ( decayExponent > 0.0 ) {\n\n return pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n\n }\n\n return 1.0;\n\n}\n\nvec3 F_Schlick( in vec3 specularColor, in float dotLH ) {\n\n // Original approximation by Christophe Schlick '94\n //;float fresnel = pow( 1.0 - dotLH, 5.0 );\n\n // Optimized variant (presented by Epic at SIGGRAPH '13)\n float fresnel = exp2( ( -5.55437 * dotLH - 6.98316 ) * dotLH );\n\n return ( 1.0 - specularColor ) * fresnel + specularColor;\n\n}\n\nfloat G_BlinnPhong_Implicit( /* in float dotNL, in float dotNV */ ) {\n\n // geometry term is (n⋅l)(n⋅v) / 4(n⋅l)(n⋅v)\n\n return 0.25;\n\n}\n\nfloat D_BlinnPhong( in float shininess, in float dotNH ) {\n\n // factor of 1/PI in distribution term omitted\n\n return ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n\n}\n\nvec3 BRDF_BlinnPhong( in vec3 specularColor, in float shininess, in vec3 normal, in vec3 lightDir, in vec3 viewDir ) {\n\n vec3 halfDir = normalize( lightDir + viewDir );\n\n //float dotNL = saturate( dot( normal, lightDir ) );\n //float dotNV = saturate( dot( normal, viewDir ) );\n float dotNH = saturate( dot( normal, halfDir ) );\n float dotLH = saturate( dot( lightDir, halfDir ) );\n\n vec3 F = F_Schlick( specularColor, dotLH );\n\n float G = G_BlinnPhong_Implicit( /* dotNL, dotNV */ );\n\n float D = D_BlinnPhong( shininess, dotNH );\n\n return F * G * D;\n\n}\n\nvec3 inputToLinear( in vec3 a ) {\n\n #ifdef GAMMA_INPUT\n\n return pow( a, vec3( float( GAMMA_FACTOR ) ) );\n\n #else\n\n return a;\n\n #endif\n\n}\n\nvec3 linearToOutput( in vec3 a ) {\n\n #ifdef GAMMA_OUTPUT\n\n return pow( a, vec3( 1.0 / float( GAMMA_FACTOR ) ) );\n\n #else\n\n return a;\n\n #endif\n\n}\n"; - " #else", +// File:src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl - " gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );", +THREE.ShaderChunk[ 'defaultnormal_vertex'] = "#ifdef FLIP_SIDED\n\n objectNormal = -objectNormal;\n\n#endif\n\nvec3 transformedNormal = normalMatrix * objectNormal;\n"; - " #endif", +// File:src/renderers/shaders/ShaderChunk/displacementmap_vertex.glsl - //"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 );", +THREE.ShaderChunk[ 'displacementmap_vertex'] = "#ifdef USE_DISPLACEMENTMAP\n\n transformed += normal * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );\n\n#endif\n"; - "}" +// File:src/renderers/shaders/ShaderChunk/displacementmap_pars_vertex.glsl - ].join( "\n" ) +THREE.ShaderChunk[ 'displacementmap_pars_vertex'] = "#ifdef USE_DISPLACEMENTMAP\n\n uniform sampler2D displacementMap;\n uniform float displacementScale;\n uniform float displacementBias;\n\n#endif\n"; - } +// File:src/renderers/shaders/ShaderChunk/emissivemap_fragment.glsl -}; +THREE.ShaderChunk[ 'emissivemap_fragment'] = "#ifdef USE_EMISSIVEMAP\n\n vec4 emissiveColor = texture2D( emissiveMap, vUv );\n\n emissiveColor.rgb = inputToLinear( emissiveColor.rgb );\n\n totalEmissiveLight *= emissiveColor.rgb;\n\n#endif\n"; -// File:src/renderers/WebGLRenderer.js +// File:src/renderers/shaders/ShaderChunk/emissivemap_pars_fragment.glsl -/** - * @author supereggbert / http://www.paulbrunt.co.uk/ - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * @author szimek / https://github.com/szimek/ - */ +THREE.ShaderChunk[ 'emissivemap_pars_fragment'] = "#ifdef USE_EMISSIVEMAP\n\n uniform sampler2D emissiveMap;\n\n#endif\n"; -THREE.WebGLRenderer = function ( parameters ) { +// File:src/renderers/shaders/ShaderChunk/envmap_fragment.glsl - console.log( 'THREE.WebGLRenderer', THREE.REVISION ); +THREE.ShaderChunk[ 'envmap_fragment'] = "#ifdef USE_ENVMAP\n\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n\n // Transforming Normal Vectors with the Inverse Transformation\n vec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n\n #ifdef ENVMAP_MODE_REFLECTION\n\n vec3 reflectVec = reflect( cameraToVertex, worldNormal );\n\n #else\n\n vec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n\n #endif\n\n #else\n\n vec3 reflectVec = vReflect;\n\n #endif\n\n #ifdef DOUBLE_SIDED\n float flipNormal = ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n #else\n float flipNormal = 1.0;\n #endif\n\n #ifdef ENVMAP_TYPE_CUBE\n vec4 envColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n\n #elif defined( ENVMAP_TYPE_EQUIREC )\n vec2 sampleUV;\n sampleUV.y = saturate( flipNormal * reflectVec.y * 0.5 + 0.5 );\n sampleUV.x = atan( flipNormal * reflectVec.z, flipNormal * reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n vec4 envColor = texture2D( envMap, sampleUV );\n\n #elif defined( ENVMAP_TYPE_SPHERE )\n vec3 reflectView = flipNormal * normalize((viewMatrix * vec4( reflectVec, 0.0 )).xyz + vec3(0.0,0.0,1.0));\n vec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\n #endif\n\n envColor.xyz = inputToLinear( envColor.xyz );\n\n #ifdef ENVMAP_BLENDING_MULTIPLY\n\n outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n\n #elif defined( ENVMAP_BLENDING_MIX )\n\n outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n\n #elif defined( ENVMAP_BLENDING_ADD )\n\n outgoingLight += envColor.xyz * specularStrength * reflectivity;\n\n #endif\n\n#endif\n"; - parameters = parameters || {}; +// File:src/renderers/shaders/ShaderChunk/envmap_pars_fragment.glsl - var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElement( 'canvas' ), - _context = parameters.context !== undefined ? parameters.context : null, +THREE.ShaderChunk[ 'envmap_pars_fragment'] = "#ifdef USE_ENVMAP\n\n uniform float reflectivity;\n #ifdef ENVMAP_TYPE_CUBE\n uniform samplerCube envMap;\n #else\n uniform sampler2D envMap;\n #endif\n uniform float flipEnvMap;\n\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n\n uniform float refractionRatio;\n\n #else\n\n varying vec3 vReflect;\n\n #endif\n\n#endif\n"; - _width = _canvas.width, - _height = _canvas.height, +// File:src/renderers/shaders/ShaderChunk/envmap_pars_vertex.glsl - pixelRatio = 1, +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\n#endif\n"; - _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, +// File:src/renderers/shaders/ShaderChunk/envmap_vertex.glsl - _clearColor = new THREE.Color( 0x000000 ), - _clearAlpha = 0; +THREE.ShaderChunk[ 'envmap_vertex'] = "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG )\n\n vec3 worldNormal = transformDirection( objectNormal, modelMatrix );\n\n vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n\n #ifdef ENVMAP_MODE_REFLECTION\n\n vReflect = reflect( cameraToVertex, worldNormal );\n\n #else\n\n vReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n\n #endif\n\n#endif\n"; - var lights = []; +// File:src/renderers/shaders/ShaderChunk/fog_fragment.glsl - var opaqueObjects = []; - var opaqueObjectsLastIndex = -1; - var transparentObjects = []; - var transparentObjectsLastIndex = -1; +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 float fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * depth * depth * LOG2 ) );\n\n #else\n\n float fogFactor = smoothstep( fogNear, fogFar, depth );\n\n #endif\n \n outgoingLight = mix( outgoingLight, fogColor, fogFactor );\n\n#endif"; - var morphInfluences = new Float32Array( 8 ); +// File:src/renderers/shaders/ShaderChunk/fog_pars_fragment.glsl +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"; - var sprites = []; - var lensFlares = []; +// File:src/renderers/shaders/ShaderChunk/hemilight_fragment.glsl - // public properties +THREE.ShaderChunk[ 'hemilight_fragment'] = "#if MAX_HEMI_LIGHTS > 0\n\n for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {\n\n vec3 lightDir = hemisphereLightDirection[ i ];\n\n float dotProduct = dot( normal, lightDir );\n\n float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n vec3 lightColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n totalAmbientLight += lightColor;\n\n }\n\n#endif\n\n"; - this.domElement = _canvas; - this.context = null; +// File:src/renderers/shaders/ShaderChunk/lightmap_fragment.glsl - // clearing +THREE.ShaderChunk[ 'lightmap_fragment'] = "#ifdef USE_LIGHTMAP\n\n totalAmbientLight += texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n\n#endif\n"; - this.autoClear = true; - this.autoClearColor = true; - this.autoClearDepth = true; - this.autoClearStencil = true; +// File:src/renderers/shaders/ShaderChunk/lightmap_pars_fragment.glsl - // scene graph +THREE.ShaderChunk[ 'lightmap_pars_fragment'] = "#ifdef USE_LIGHTMAP\n\n uniform sampler2D lightMap;\n uniform float lightMapIntensity;\n\n#endif"; - this.sortObjects = true; +// File:src/renderers/shaders/ShaderChunk/lights_lambert_pars_vertex.glsl - // physically based shading +THREE.ShaderChunk[ 'lights_lambert_pars_vertex'] = "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 uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\n uniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n uniform float pointLightDecay[ 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 uniform float spotLightDecay[ MAX_SPOT_LIGHTS ];\n\n#endif\n"; - this.gammaFactor = 2.0; // for backwards compatibility - this.gammaInput = false; - this.gammaOutput = false; +// File:src/renderers/shaders/ShaderChunk/lights_lambert_vertex.glsl - // morphs +THREE.ShaderChunk[ 'lights_lambert_vertex'] = "vLightFront = vec3( 0.0 );\n\n#ifdef DOUBLE_SIDED\n\n vLightBack = vec3( 0.0 );\n\n#endif\n\nvec3 normal = normalize( transformedNormal );\n\n#if MAX_POINT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n vec3 lightColor = pointLightColor[ i ];\n\n vec3 lVector = pointLightPosition[ i ] - mvPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n // attenuation\n\n float attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[ i ] );\n\n // diffuse\n\n float dotProduct = dot( normal, lightDir );\n\n vLightFront += lightColor * attenuation * saturate( dotProduct );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += lightColor * attenuation * saturate( - dotProduct );\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 vec3 lightColor = spotLightColor[ i ];\n\n vec3 lightPosition = spotLightPosition[ i ];\n vec3 lVector = lightPosition - mvPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n float spotEffect = dot( spotLightDirection[ i ], lightDir );\n\n if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n spotEffect = saturate( pow( saturate( spotEffect ), spotLightExponent[ i ] ) );\n\n // attenuation\n\n float attenuation = calcLightAttenuation( length( lVector ), spotLightDistance[ i ], spotLightDecay[ i ] );\n\n attenuation *= spotEffect;\n\n // diffuse\n\n float dotProduct = dot( normal, lightDir );\n\n vLightFront += lightColor * attenuation * saturate( dotProduct );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += lightColor * attenuation * saturate( - dotProduct );\n\n #endif\n\n }\n\n }\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n for ( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n vec3 lightColor = directionalLightColor[ i ];\n\n vec3 lightDir = directionalLightDirection[ i ];\n\n // diffuse\n\n float dotProduct = dot( normal, lightDir );\n\n vLightFront += lightColor * saturate( dotProduct );\n\n #ifdef DOUBLE_SIDED\n\n vLightBack += lightColor * saturate( - dotProduct );\n\n #endif\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 vec3 lightDir = hemisphereLightDirection[ i ];\n\n // diffuse\n\n float dotProduct = dot( normal, lightDir );\n\n float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;\n\n vLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );\n\n #ifdef DOUBLE_SIDED\n\n float hemiDiffuseWeightBack = - 0.5 * dotProduct + 0.5;\n\n vLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );\n\n #endif\n\n }\n\n#endif\n\nvLightFront += ambientLightColor;\n\n#ifdef DOUBLE_SIDED\n\n vLightBack += ambientLightColor;\n\n#endif\n"; - this.maxMorphTargets = 8; - this.maxMorphNormals = 4; +// File:src/renderers/shaders/ShaderChunk/lights_phong_fragment.glsl - // flags +THREE.ShaderChunk[ 'lights_phong_fragment'] = "vec3 viewDir = normalize( vViewPosition );\n\nvec3 totalDiffuseLight = vec3( 0.0 );\nvec3 totalSpecularLight = vec3( 0.0 );\n\n#if MAX_POINT_LIGHTS > 0\n\n for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\n\n vec3 lightColor = pointLightColor[ i ];\n\n vec3 lightPosition = pointLightPosition[ i ];\n vec3 lVector = lightPosition + vViewPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n // attenuation\n\n float attenuation = calcLightAttenuation( length( lVector ), pointLightDistance[ i ], pointLightDecay[ i ] );\n\n // diffuse\n\n float cosineTerm = saturate( dot( normal, lightDir ) );\n\n totalDiffuseLight += lightColor * attenuation * cosineTerm;\n\n // specular\n\n vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n totalSpecularLight += brdf * specularStrength * lightColor * attenuation * cosineTerm;\n\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 vec3 lightColor = spotLightColor[ i ];\n\n vec3 lightPosition = spotLightPosition[ i ];\n vec3 lVector = lightPosition + vViewPosition.xyz;\n vec3 lightDir = normalize( lVector );\n\n float spotEffect = dot( spotLightDirection[ i ], lightDir );\n\n if ( spotEffect > spotLightAngleCos[ i ] ) {\n\n spotEffect = saturate( pow( saturate( spotEffect ), spotLightExponent[ i ] ) );\n\n // attenuation\n\n float attenuation = calcLightAttenuation( length( lVector ), spotLightDistance[ i ], spotLightDecay[ i ] );\n\n attenuation *= spotEffect;\n\n // diffuse\n\n float cosineTerm = saturate( dot( normal, lightDir ) );\n\n totalDiffuseLight += lightColor * attenuation * cosineTerm;\n\n // specular\n\n vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n totalSpecularLight += brdf * specularStrength * lightColor * attenuation * cosineTerm;\n\n }\n\n }\n\n#endif\n\n#if MAX_DIR_LIGHTS > 0\n\n for( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {\n\n vec3 lightColor = directionalLightColor[ i ];\n\n vec3 lightDir = directionalLightDirection[ i ];\n\n // diffuse\n\n float cosineTerm = saturate( dot( normal, lightDir ) );\n\n totalDiffuseLight += lightColor * cosineTerm;\n\n // specular\n\n vec3 brdf = BRDF_BlinnPhong( specular, shininess, normal, lightDir, viewDir );\n\n totalSpecularLight += brdf * specularStrength * lightColor * cosineTerm;\n\n }\n\n#endif\n\n#ifdef METAL\n\n outgoingLight += diffuseColor.rgb * ( totalDiffuseLight + totalAmbientLight ) * specular + totalSpecularLight + totalEmissiveLight;\n\n#else\n\n outgoingLight += diffuseColor.rgb * ( totalDiffuseLight + totalAmbientLight ) + totalSpecularLight + totalEmissiveLight;\n\n#endif\n"; - this.autoScaleCubemaps = true; +// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_fragment.glsl - // internal properties +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 uniform float pointLightDecay[ 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 uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];\n uniform float spotLightDecay[ MAX_SPOT_LIGHTS ];\n\n#endif\n\n#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n varying vec3 vWorldPosition;\n\n#endif\n\nvarying vec3 vViewPosition;\n\n#ifndef FLAT_SHADED\n\n varying vec3 vNormal;\n\n#endif\n"; - var _this = this, +// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_vertex.glsl - // internal state cache +THREE.ShaderChunk[ 'lights_phong_pars_vertex'] = "#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n varying vec3 vWorldPosition;\n\n#endif\n"; - _currentProgram = null, - _currentFramebuffer = null, - _currentMaterialId = - 1, - _currentGeometryProgram = '', - _currentCamera = null, +// File:src/renderers/shaders/ShaderChunk/lights_phong_vertex.glsl - _usedTextureUnits = 0, +THREE.ShaderChunk[ 'lights_phong_vertex'] = "#if MAX_SPOT_LIGHTS > 0 || defined( USE_ENVMAP )\n\n vWorldPosition = worldPosition.xyz;\n\n#endif\n"; - _viewportX = 0, - _viewportY = 0, - _viewportWidth = _canvas.width, - _viewportHeight = _canvas.height, - _currentWidth = 0, - _currentHeight = 0, +// File:src/renderers/shaders/ShaderChunk/linear_to_gamma_fragment.glsl - // frustum +THREE.ShaderChunk[ 'linear_to_gamma_fragment'] = "\n outgoingLight = linearToOutput( outgoingLight );\n"; - _frustum = new THREE.Frustum(), +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_fragment.glsl - // camera matrices cache +THREE.ShaderChunk[ 'logdepthbuf_fragment'] = "#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)\n\n gl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;\n\n#endif"; - _projScreenMatrix = new THREE.Matrix4(), +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_fragment.glsl - _vector3 = new THREE.Vector3(), +THREE.ShaderChunk[ 'logdepthbuf_pars_fragment'] = "#ifdef USE_LOGDEPTHBUF\n\n uniform float logDepthBufFC;\n\n #ifdef USE_LOGDEPTHBUF_EXT\n\n varying float vFragDepth;\n\n #endif\n\n#endif\n"; - // light arrays cache +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_vertex.glsl - _direction = new THREE.Vector3(), +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"; - _lightsNeedUpdate = true, +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_vertex.glsl - _lights = { +THREE.ShaderChunk[ 'logdepthbuf_vertex'] = "#ifdef USE_LOGDEPTHBUF\n\n gl_Position.z = log2(max( EPSILON, 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"; - ambient: [ 0, 0, 0 ], - directional: { length: 0, colors: [], positions: [] }, - point: { length: 0, colors: [], positions: [], distances: [], decays: [] }, - spot: { length: 0, colors: [], positions: [], distances: [], directions: [], anglesCos: [], exponents: [], decays: [] }, - hemi: { length: 0, skyColors: [], groundColors: [], positions: [] } +// File:src/renderers/shaders/ShaderChunk/map_fragment.glsl - }, +THREE.ShaderChunk[ 'map_fragment'] = "#ifdef USE_MAP\n\n vec4 texelColor = texture2D( map, vUv );\n\n texelColor.xyz = inputToLinear( texelColor.xyz );\n\n diffuseColor *= texelColor;\n\n#endif\n"; - // info +// File:src/renderers/shaders/ShaderChunk/map_pars_fragment.glsl - _infoMemory = { +THREE.ShaderChunk[ 'map_pars_fragment'] = "#ifdef USE_MAP\n\n uniform sampler2D map;\n\n#endif"; - geometries: 0, - textures: 0 +// File:src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl - }, +THREE.ShaderChunk[ 'map_particle_fragment'] = "#ifdef USE_MAP\n\n diffuseColor *= texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) * offsetRepeat.zw + offsetRepeat.xy );\n\n#endif\n"; - _infoRender = { +// File:src/renderers/shaders/ShaderChunk/map_particle_pars_fragment.glsl - calls: 0, - vertices: 0, - faces: 0, - points: 0 +THREE.ShaderChunk[ 'map_particle_pars_fragment'] = "#ifdef USE_MAP\n\n uniform vec4 offsetRepeat;\n uniform sampler2D map;\n\n#endif\n"; - }; +// File:src/renderers/shaders/ShaderChunk/morphnormal_vertex.glsl - this.info = { +THREE.ShaderChunk[ 'morphnormal_vertex'] = "#ifdef USE_MORPHNORMALS\n\n objectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n objectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n objectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n objectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n\n#endif\n"; - render: _infoRender, - memory: _infoMemory, - programs: null +// 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"; +// File:src/renderers/shaders/ShaderChunk/morphtarget_vertex.glsl - // initialize +THREE.ShaderChunk[ 'morphtarget_vertex'] = "#ifdef USE_MORPHTARGETS\n\n transformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n transformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n transformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n transformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n\n #ifndef USE_MORPHNORMALS\n\n transformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n transformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n transformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n transformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n\n #endif\n\n#endif\n"; - var _gl; +// File:src/renderers/shaders/ShaderChunk/normal_phong_fragment.glsl - try { +THREE.ShaderChunk[ 'normal_phong_fragment'] = "#ifndef FLAT_SHADED\n\n vec3 normal = normalize( vNormal );\n\n #ifdef DOUBLE_SIDED\n\n normal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n\n #endif\n\n#else\n\n vec3 fdx = dFdx( vViewPosition );\n vec3 fdy = dFdy( vViewPosition );\n vec3 normal = normalize( cross( fdx, fdy ) );\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"; - var attributes = { - alpha: _alpha, - depth: _depth, - stencil: _stencil, - antialias: _antialias, - premultipliedAlpha: _premultipliedAlpha, - preserveDrawingBuffer: _preserveDrawingBuffer - }; +// File:src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl - _gl = _context || _canvas.getContext( 'webgl', attributes ) || _canvas.getContext( 'experimental-webgl', attributes ); +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"; - if ( _gl === null ) { +// File:src/renderers/shaders/ShaderChunk/project_vertex.glsl - if ( _canvas.getContext( 'webgl' ) !== null ) { +THREE.ShaderChunk[ 'project_vertex'] = "#ifdef USE_SKINNING\n\n vec4 mvPosition = modelViewMatrix * skinned;\n\n#else\n\n vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\n\n#endif\n\ngl_Position = projectionMatrix * mvPosition;\n"; - throw 'Error creating WebGL context with your selected attributes.'; +// File:src/renderers/shaders/ShaderChunk/shadowmap_fragment.glsl - } else { +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 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 bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\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 if ( inFrustum ) outgoingLight *= frustumColors[ i ];\n\n #endif\n\n }\n\n outgoingLight = outgoingLight * shadowColor;\n\n#endif\n"; - throw 'Error creating WebGL context.'; +// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl - } +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"; - } +// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl - _canvas.addEventListener( 'webglcontextlost', onContextLost, false ); +THREE.ShaderChunk[ 'shadowmap_pars_vertex'] = "#ifdef USE_SHADOWMAP\n\n varying vec4 vShadowCoord[ MAX_SHADOWS ];\n uniform mat4 shadowMatrix[ MAX_SHADOWS ];\n\n#endif"; - } catch ( error ) { +// File:src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl - console.error( 'THREE.WebGLRenderer: ' + error ); +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"; - } +// File:src/renderers/shaders/ShaderChunk/skinbase_vertex.glsl - var extensions = new THREE.WebGLExtensions( _gl ); +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"; - extensions.get( 'OES_texture_float' ); - extensions.get( 'OES_texture_float_linear' ); - extensions.get( 'OES_texture_half_float' ); - extensions.get( 'OES_texture_half_float_linear' ); - extensions.get( 'OES_standard_derivatives' ); - extensions.get( 'ANGLE_instanced_arrays' ); +// File:src/renderers/shaders/ShaderChunk/skinning_pars_vertex.glsl - if ( extensions.get( 'OES_element_index_uint' ) ) { +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"; - THREE.BufferGeometry.MaxIndex = 4294967296; +// File:src/renderers/shaders/ShaderChunk/skinning_vertex.glsl - } +THREE.ShaderChunk[ 'skinning_vertex'] = "#ifdef USE_SKINNING\n\n vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\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"; - var capabilities = new THREE.WebGLCapabilities( _gl, extensions, parameters ); +// File:src/renderers/shaders/ShaderChunk/skinnormal_vertex.glsl - var state = new THREE.WebGLState( _gl, extensions, paramThreeToGL ); - var properties = new THREE.WebGLProperties(); - var objects = new THREE.WebGLObjects( _gl, properties, this.info ); - var programCache = new THREE.WebGLPrograms( this, capabilities ); +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 objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n\n#endif\n"; - this.info.programs = programCache.programs; +// File:src/renderers/shaders/ShaderChunk/specularmap_fragment.glsl - var bufferRenderer = new THREE.WebGLBufferRenderer( _gl, extensions, _infoRender ); - var indexedBufferRenderer = new THREE.WebGLIndexedBufferRenderer( _gl, extensions, _infoRender ); +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"; - // +// File:src/renderers/shaders/ShaderChunk/specularmap_pars_fragment.glsl - function glClearColor( r, g, b, a ) { +THREE.ShaderChunk[ 'specularmap_pars_fragment'] = "#ifdef USE_SPECULARMAP\n\n uniform sampler2D specularMap;\n\n#endif"; - if ( _premultipliedAlpha === true ) { +// File:src/renderers/shaders/ShaderChunk/uv2_pars_fragment.glsl - r *= a; g *= a; b *= a; +THREE.ShaderChunk[ 'uv2_pars_fragment'] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n varying vec2 vUv2;\n\n#endif"; - } +// File:src/renderers/shaders/ShaderChunk/uv2_pars_vertex.glsl - _gl.clearColor( r, g, b, a ); +THREE.ShaderChunk[ 'uv2_pars_vertex'] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n attribute vec2 uv2;\n varying vec2 vUv2;\n\n#endif"; - } +// File:src/renderers/shaders/ShaderChunk/uv2_vertex.glsl - function setDefaultGLState() { +THREE.ShaderChunk[ 'uv2_vertex'] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n\n vUv2 = uv2;\n\n#endif"; - state.init(); +// File:src/renderers/shaders/ShaderChunk/uv_pars_fragment.glsl - _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight ); +THREE.ShaderChunk[ 'uv_pars_fragment'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n varying vec2 vUv;\n\n#endif"; - glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); +// File:src/renderers/shaders/ShaderChunk/uv_pars_vertex.glsl - } +THREE.ShaderChunk[ 'uv_pars_vertex'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n varying vec2 vUv;\n uniform vec4 offsetRepeat;\n\n#endif\n"; - function resetGLState() { +// File:src/renderers/shaders/ShaderChunk/uv_vertex.glsl - _currentProgram = null; - _currentCamera = null; +THREE.ShaderChunk[ 'uv_vertex'] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP )\n\n vUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n\n#endif"; - _currentGeometryProgram = ''; - _currentMaterialId = - 1; +// File:src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl - _lightsNeedUpdate = true; +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 #else\n\n vec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\n\n #endif\n\n#endif\n"; - state.reset(); +// File:src/renderers/shaders/UniformsUtils.js - } +/** + * Uniform Utilities + */ - setDefaultGLState(); +THREE.UniformsUtils = { - this.context = _gl; - this.capabilities = capabilities; - this.extensions = extensions; - this.state = state; + merge: function ( uniforms ) { - // shadow map + var merged = {}; - var shadowMap = new THREE.WebGLShadowMap( this, lights, objects ); + for ( var u = 0; u < uniforms.length; u ++ ) { - this.shadowMap = shadowMap; + var tmp = this.clone( uniforms[ u ] ); + for ( var p in tmp ) { - // Plugins + merged[ p ] = tmp[ p ]; - var spritePlugin = new THREE.SpritePlugin( this, sprites ); - var lensFlarePlugin = new THREE.LensFlarePlugin( this, lensFlares ); + } - // API + } - this.getContext = function () { + return merged; - return _gl; + }, - }; + clone: function ( uniforms_src ) { - this.getContextAttributes = function () { + var uniforms_dst = {}; - return _gl.getContextAttributes(); + for ( var u in uniforms_src ) { - }; + uniforms_dst[ u ] = {}; - this.forceContextLoss = function () { + for ( var p in uniforms_src[ u ] ) { - extensions.get( 'WEBGL_lose_context' ).loseContext(); + var parameter_src = uniforms_src[ u ][ p ]; - }; + 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.Matrix3 || + parameter_src instanceof THREE.Matrix4 || + parameter_src instanceof THREE.Texture ) { - this.getMaxAnisotropy = ( function () { + uniforms_dst[ u ][ p ] = parameter_src.clone(); - var value; + } else if ( Array.isArray( parameter_src ) ) { - return function getMaxAnisotropy() { + uniforms_dst[ u ][ p ] = parameter_src.slice(); - if ( value !== undefined ) return value; + } else { - var extension = extensions.get( 'EXT_texture_filter_anisotropic' ); + uniforms_dst[ u ][ p ] = parameter_src; - if ( extension !== null ) { + } - value = _gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ); + } - } else { + } - value = 0; + return uniforms_dst; - } + } - return value; +}; - } +// File:src/renderers/shaders/UniformsLib.js - } )(); +/** + * Uniforms library for shared webgl shaders + */ - this.getPrecision = function () { +THREE.UniformsLib = { - return capabilities.precision; + common: { - }; + "diffuse" : { type: "c", value: new THREE.Color( 0xeeeeee ) }, + "opacity" : { type: "f", value: 1.0 }, - this.getPixelRatio = function () { + "map" : { type: "t", value: null }, + "offsetRepeat" : { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) }, - return pixelRatio; + "specularMap" : { type: "t", value: null }, + "alphaMap" : { type: "t", value: null }, - }; + "envMap" : { type: "t", value: null }, + "flipEnvMap" : { type: "f", value: - 1 }, + "reflectivity" : { type: "f", value: 1.0 }, + "refractionRatio" : { type: "f", value: 0.98 } - this.setPixelRatio = function ( value ) { + }, - if ( value !== undefined ) pixelRatio = value; + aomap: { - }; + "aoMap" : { type: "t", value: null }, + "aoMapIntensity" : { type: "f", value: 1 }, - this.getSize = function () { + }, - return { - width: _width, - height: _height - }; + lightmap: { - }; + "lightMap" : { type: "t", value: null }, + "lightMapIntensity" : { type: "f", value: 1 }, - this.setSize = function ( width, height, updateStyle ) { + }, - _width = width; - _height = height; + emissivemap: { - _canvas.width = width * pixelRatio; - _canvas.height = height * pixelRatio; + "emissiveMap" : { type: "t", value: null }, - if ( updateStyle !== false ) { + }, - _canvas.style.width = width + 'px'; - _canvas.style.height = height + 'px'; + bumpmap: { - } + "bumpMap" : { type: "t", value: null }, + "bumpScale" : { type: "f", value: 1 } - this.setViewport( 0, 0, width, height ); + }, - }; + normalmap: { - this.setViewport = function ( x, y, width, height ) { + "normalMap" : { type: "t", value: null }, + "normalScale" : { type: "v2", value: new THREE.Vector2( 1, 1 ) } - _viewportX = x * pixelRatio; - _viewportY = y * pixelRatio; + }, - _viewportWidth = width * pixelRatio; - _viewportHeight = height * pixelRatio; + displacementmap: { - _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight ); + "displacementMap" : { type: "t", value: null }, + "displacementScale" : { type: "f", value: 1 }, + "displacementBias" : { type: "f", value: 0 } - }; + }, - this.setScissor = function ( x, y, width, height ) { + fog : { - _gl.scissor( - x * pixelRatio, - y * pixelRatio, - width * pixelRatio, - height * pixelRatio - ); + "fogDensity" : { type: "f", value: 0.00025 }, + "fogNear" : { type: "f", value: 1 }, + "fogFar" : { type: "f", value: 2000 }, + "fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) } - }; + }, - this.enableScissorTest = function ( boolean ) { + lights: { - state.setScissorTest( boolean ); + "ambientLightColor" : { type: "fv", value: [] }, - }; + "directionalLightDirection" : { type: "fv", value: [] }, + "directionalLightColor" : { type: "fv", value: [] }, - // Clearing + "hemisphereLightDirection" : { type: "fv", value: [] }, + "hemisphereLightSkyColor" : { type: "fv", value: [] }, + "hemisphereLightGroundColor" : { type: "fv", value: [] }, - this.getClearColor = function () { + "pointLightColor" : { type: "fv", value: [] }, + "pointLightPosition" : { type: "fv", value: [] }, + "pointLightDistance" : { type: "fv1", value: [] }, + "pointLightDecay" : { type: "fv1", value: [] }, - return _clearColor; + "spotLightColor" : { type: "fv", value: [] }, + "spotLightPosition" : { type: "fv", value: [] }, + "spotLightDirection" : { type: "fv", value: [] }, + "spotLightDistance" : { type: "fv1", value: [] }, + "spotLightAngleCos" : { type: "fv1", value: [] }, + "spotLightExponent" : { type: "fv1", value: [] }, + "spotLightDecay" : { type: "fv1", value: [] } - }; + }, - this.setClearColor = function ( color, alpha ) { + points: { - _clearColor.set( color ); + "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 }, + "offsetRepeat" : { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) }, - _clearAlpha = alpha !== undefined ? alpha : 1; + "fogDensity" : { type: "f", value: 0.00025 }, + "fogNear" : { type: "f", value: 1 }, + "fogFar" : { type: "f", value: 2000 }, + "fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) } - glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); + }, - }; + shadowmap: { - this.getClearAlpha = function () { + "shadowMap": { type: "tv", value: [] }, + "shadowMapSize": { type: "v2v", value: [] }, - return _clearAlpha; + "shadowBias" : { type: "fv1", value: [] }, + "shadowDarkness": { type: "fv1", value: [] }, - }; + "shadowMatrix" : { type: "m4v", value: [] } - this.setClearAlpha = function ( alpha ) { + } - _clearAlpha = alpha; +}; - glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); +// File:src/renderers/shaders/ShaderLib.js - }; +/** + * Webgl Shader Library for three.js + * + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + */ - this.clear = function ( color, depth, stencil ) { - var bits = 0; +THREE.ShaderLib = { - 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; + 'basic': { - _gl.clear( bits ); + uniforms: THREE.UniformsUtils.merge( [ - }; + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "aomap" ], + THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "shadowmap" ] - this.clearColor = function () { + ] ), - _gl.clear( _gl.COLOR_BUFFER_BIT ); + vertexShader: [ - }; + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "uv_pars_vertex" ], + THREE.ShaderChunk[ "uv2_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" ], - this.clearDepth = function () { + "void main() {", - _gl.clear( _gl.DEPTH_BUFFER_BIT ); + THREE.ShaderChunk[ "uv_vertex" ], + THREE.ShaderChunk[ "uv2_vertex" ], + THREE.ShaderChunk[ "color_vertex" ], + THREE.ShaderChunk[ "skinbase_vertex" ], - }; + " #ifdef USE_ENVMAP", - this.clearStencil = function () { + THREE.ShaderChunk[ "beginnormal_vertex" ], + THREE.ShaderChunk[ "morphnormal_vertex" ], + THREE.ShaderChunk[ "skinnormal_vertex" ], + THREE.ShaderChunk[ "defaultnormal_vertex" ], - _gl.clear( _gl.STENCIL_BUFFER_BIT ); + " #endif", - }; + THREE.ShaderChunk[ "begin_vertex" ], + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "project_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], - this.clearTarget = function ( renderTarget, color, depth, stencil ) { + THREE.ShaderChunk[ "worldpos_vertex" ], + THREE.ShaderChunk[ "envmap_vertex" ], + THREE.ShaderChunk[ "shadowmap_vertex" ], - this.setRenderTarget( renderTarget ); - this.clear( color, depth, stencil ); + "}" - }; + ].join( "\n" ), - // Reset + fragmentShader: [ - this.resetGLState = resetGLState; + "uniform vec3 diffuse;", + "uniform float opacity;", - this.dispose = function() { + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "uv_pars_fragment" ], + THREE.ShaderChunk[ "uv2_pars_fragment" ], + THREE.ShaderChunk[ "map_pars_fragment" ], + THREE.ShaderChunk[ "alphamap_pars_fragment" ], + THREE.ShaderChunk[ "aomap_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" ], - _canvas.removeEventListener( 'webglcontextlost', onContextLost, false ); + "void main() {", - }; + " vec3 outgoingLight = vec3( 0.0 );", + " vec4 diffuseColor = vec4( diffuse, opacity );", + " vec3 totalAmbientLight = vec3( 1.0 );", // hardwired - // Events + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "map_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "alphamap_fragment" ], + THREE.ShaderChunk[ "alphatest_fragment" ], + THREE.ShaderChunk[ "specularmap_fragment" ], + THREE.ShaderChunk[ "aomap_fragment" ], - function onContextLost( event ) { + " outgoingLight = diffuseColor.rgb * totalAmbientLight;", // simple shader - event.preventDefault(); + THREE.ShaderChunk[ "envmap_fragment" ], + THREE.ShaderChunk[ "shadowmap_fragment" ], // TODO: Shadows on an otherwise unlit surface doesn't make sense. - resetGLState(); - setDefaultGLState(); + THREE.ShaderChunk[ "linear_to_gamma_fragment" ], - properties.clear(); + THREE.ShaderChunk[ "fog_fragment" ], - }; + " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", - function onTextureDispose( event ) { + "}" - var texture = event.target; + ].join( "\n" ) - texture.removeEventListener( 'dispose', onTextureDispose ); + }, - deallocateTexture( texture ); + 'lambert': { - _infoMemory.textures --; + uniforms: THREE.UniformsUtils.merge( [ + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "lights" ], + THREE.UniformsLib[ "shadowmap" ], - } + { + "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) } + } - function onRenderTargetDispose( event ) { + ] ), - var renderTarget = event.target; + vertexShader: [ - renderTarget.removeEventListener( 'dispose', onRenderTargetDispose ); + "#define LAMBERT", - deallocateRenderTarget( renderTarget ); + "varying vec3 vLightFront;", - _infoMemory.textures --; + "#ifdef DOUBLE_SIDED", - } + " varying vec3 vLightBack;", - function onMaterialDispose( event ) { + "#endif", - var material = event.target; + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "uv_pars_vertex" ], + THREE.ShaderChunk[ "uv2_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" ], - material.removeEventListener( 'dispose', onMaterialDispose ); + "void main() {", - deallocateMaterial( material ); + THREE.ShaderChunk[ "uv_vertex" ], + THREE.ShaderChunk[ "uv2_vertex" ], + THREE.ShaderChunk[ "color_vertex" ], - } + THREE.ShaderChunk[ "beginnormal_vertex" ], + THREE.ShaderChunk[ "morphnormal_vertex" ], + THREE.ShaderChunk[ "skinbase_vertex" ], + THREE.ShaderChunk[ "skinnormal_vertex" ], + THREE.ShaderChunk[ "defaultnormal_vertex" ], - // Buffer deallocation + THREE.ShaderChunk[ "begin_vertex" ], + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "project_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], - function deallocateTexture( texture ) { + THREE.ShaderChunk[ "worldpos_vertex" ], + THREE.ShaderChunk[ "envmap_vertex" ], + THREE.ShaderChunk[ "lights_lambert_vertex" ], + THREE.ShaderChunk[ "shadowmap_vertex" ], - var textureProperties = properties.get( texture ); + "}" - if ( texture.image && textureProperties.__image__webglTextureCube ) { + ].join( "\n" ), - // cube texture + fragmentShader: [ - _gl.deleteTexture( textureProperties.__image__webglTextureCube ); + "uniform vec3 diffuse;", + "uniform vec3 emissive;", + "uniform float opacity;", - } else { + "varying vec3 vLightFront;", - // 2D texture + "#ifdef DOUBLE_SIDED", - if ( textureProperties.__webglInit === undefined ) return; + " varying vec3 vLightBack;", - _gl.deleteTexture( textureProperties.__webglTexture ); + "#endif", - } + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "uv_pars_fragment" ], + THREE.ShaderChunk[ "uv2_pars_fragment" ], + THREE.ShaderChunk[ "map_pars_fragment" ], + THREE.ShaderChunk[ "alphamap_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" ], - // remove all webgl properties - properties.delete( texture ); + "void main() {", - } + " vec3 outgoingLight = vec3( 0.0 );", // outgoing light does not have an alpha, the surface does + " vec4 diffuseColor = vec4( diffuse, opacity );", - function deallocateRenderTarget( renderTarget ) { + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "map_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "alphamap_fragment" ], + THREE.ShaderChunk[ "alphatest_fragment" ], + THREE.ShaderChunk[ "specularmap_fragment" ], - var renderTargetProperties = properties.get( renderTarget ); + " #ifdef DOUBLE_SIDED", - if ( ! renderTarget || renderTargetProperties.__webglTexture === undefined ) return; + " if ( gl_FrontFacing )", + " outgoingLight += diffuseColor.rgb * vLightFront + emissive;", + " else", + " outgoingLight += diffuseColor.rgb * vLightBack + emissive;", - _gl.deleteTexture( renderTargetProperties.__webglTexture ); + " #else", - if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) { + " outgoingLight += diffuseColor.rgb * vLightFront + emissive;", - for ( var i = 0; i < 6; i ++ ) { + " #endif", - _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] ); - _gl.deleteRenderbuffer( renderTargetProperties.__webglRenderbuffer[ i ] ); + THREE.ShaderChunk[ "envmap_fragment" ], + THREE.ShaderChunk[ "shadowmap_fragment" ], - } + THREE.ShaderChunk[ "linear_to_gamma_fragment" ], - } else { + THREE.ShaderChunk[ "fog_fragment" ], - _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer ); - _gl.deleteRenderbuffer( renderTargetProperties.__webglRenderbuffer ); + " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", - } + "}" - properties.delete( renderTarget ); + ].join( "\n" ) - } + }, - function deallocateMaterial( material ) { + 'phong': { - releaseMaterialProgramReference( material ); + uniforms: THREE.UniformsUtils.merge( [ - properties.delete( material ); + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "aomap" ], + THREE.UniformsLib[ "lightmap" ], + THREE.UniformsLib[ "emissivemap" ], + THREE.UniformsLib[ "bumpmap" ], + THREE.UniformsLib[ "normalmap" ], + THREE.UniformsLib[ "displacementmap" ], + THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "lights" ], + THREE.UniformsLib[ "shadowmap" ], - } + { + "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) }, + "specular" : { type: "c", value: new THREE.Color( 0x111111 ) }, + "shininess": { type: "f", value: 30 } + } + ] ), - function releaseMaterialProgramReference( material ) { + vertexShader: [ - var programInfo = properties.get( material ).program; + "#define PHONG", - material.program = undefined; + "varying vec3 vViewPosition;", - if ( programInfo !== undefined ) { + "#ifndef FLAT_SHADED", - programCache.releaseProgram( programInfo ); - } + " varying vec3 vNormal;", - } + "#endif", - // Buffer rendering + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "uv_pars_vertex" ], + THREE.ShaderChunk[ "uv2_pars_vertex" ], + THREE.ShaderChunk[ "displacementmap_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" ], - this.renderBufferImmediate = function ( object, program, material ) { + "void main() {", - state.initAttributes(); + THREE.ShaderChunk[ "uv_vertex" ], + THREE.ShaderChunk[ "uv2_vertex" ], + THREE.ShaderChunk[ "color_vertex" ], - var buffers = properties.get( object ); + THREE.ShaderChunk[ "beginnormal_vertex" ], + THREE.ShaderChunk[ "morphnormal_vertex" ], + THREE.ShaderChunk[ "skinbase_vertex" ], + THREE.ShaderChunk[ "skinnormal_vertex" ], + THREE.ShaderChunk[ "defaultnormal_vertex" ], - if ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer(); - if ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer(); - if ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer(); - if ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer(); + "#ifndef FLAT_SHADED", // Normal computed with derivatives when FLAT_SHADED - var attributes = program.getAttributes(); + " vNormal = normalize( transformedNormal );", - if ( object.hasPositions ) { + "#endif", - _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.position ); - _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW ); + THREE.ShaderChunk[ "begin_vertex" ], + THREE.ShaderChunk[ "displacementmap_vertex" ], + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "project_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], - state.enableAttribute( attributes.position ); - _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + " vViewPosition = - mvPosition.xyz;", - } + THREE.ShaderChunk[ "worldpos_vertex" ], + THREE.ShaderChunk[ "envmap_vertex" ], + THREE.ShaderChunk[ "lights_phong_vertex" ], + THREE.ShaderChunk[ "shadowmap_vertex" ], - if ( object.hasNormals ) { + "}" - _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.normal ); + ].join( "\n" ), - if ( material.type !== 'MeshPhongMaterial' && material.shading === THREE.FlatShading ) { + fragmentShader: [ - for ( var i = 0, l = object.count * 3; i < l; i += 9 ) { + "#define PHONG", - var array = object.normalArray; + "uniform vec3 diffuse;", + "uniform vec3 emissive;", + "uniform vec3 specular;", + "uniform float shininess;", + "uniform float opacity;", - var nx = ( array[ i + 0 ] + array[ i + 3 ] + array[ i + 6 ] ) / 3; - var ny = ( array[ i + 1 ] + array[ i + 4 ] + array[ i + 7 ] ) / 3; - var nz = ( array[ i + 2 ] + array[ i + 5 ] + array[ i + 8 ] ) / 3; + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "uv_pars_fragment" ], + THREE.ShaderChunk[ "uv2_pars_fragment" ], + THREE.ShaderChunk[ "map_pars_fragment" ], + THREE.ShaderChunk[ "alphamap_pars_fragment" ], + THREE.ShaderChunk[ "aomap_pars_fragment" ], + THREE.ShaderChunk[ "lightmap_pars_fragment" ], + THREE.ShaderChunk[ "emissivemap_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" ], - array[ i + 0 ] = nx; - array[ i + 1 ] = ny; - array[ i + 2 ] = nz; + "void main() {", - array[ i + 3 ] = nx; - array[ i + 4 ] = ny; - array[ i + 5 ] = nz; + " vec3 outgoingLight = vec3( 0.0 );", + " vec4 diffuseColor = vec4( diffuse, opacity );", + " vec3 totalAmbientLight = ambientLightColor;", + " vec3 totalEmissiveLight = emissive;", - array[ i + 6 ] = nx; - array[ i + 7 ] = ny; - array[ i + 8 ] = nz; + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "map_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "alphamap_fragment" ], + THREE.ShaderChunk[ "alphatest_fragment" ], + THREE.ShaderChunk[ "specularmap_fragment" ], + THREE.ShaderChunk[ "normal_phong_fragment" ], + THREE.ShaderChunk[ "lightmap_fragment" ], + THREE.ShaderChunk[ "hemilight_fragment" ], + THREE.ShaderChunk[ "aomap_fragment" ], + THREE.ShaderChunk[ "emissivemap_fragment" ], - } + THREE.ShaderChunk[ "lights_phong_fragment" ], - } + THREE.ShaderChunk[ "envmap_fragment" ], + THREE.ShaderChunk[ "shadowmap_fragment" ], - _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW ); + THREE.ShaderChunk[ "linear_to_gamma_fragment" ], - state.enableAttribute( attributes.normal ); + THREE.ShaderChunk[ "fog_fragment" ], - _gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); + " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", - } + "}" - if ( object.hasUvs && material.map ) { + ].join( "\n" ) - _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.uv ); - _gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW ); + }, - state.enableAttribute( attributes.uv ); + 'points': { - _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); + uniforms: THREE.UniformsUtils.merge( [ - } + THREE.UniformsLib[ "points" ], + THREE.UniformsLib[ "shadowmap" ] - if ( object.hasColors && material.vertexColors !== THREE.NoColors ) { + ] ), - _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.color ); - _gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW ); + vertexShader: [ - state.enableAttribute( attributes.color ); + "uniform float size;", + "uniform float scale;", - _gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 ); + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], - } + "void main() {", - state.disableUnusedAttributes(); + THREE.ShaderChunk[ "color_vertex" ], - _gl.drawArrays( _gl.TRIANGLES, 0, object.count ); + " vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", - object.count = 0; + " #ifdef USE_SIZEATTENUATION", + " gl_PointSize = size * ( scale / length( mvPosition.xyz ) );", + " #else", + " gl_PointSize = size;", + " #endif", - }; + " gl_Position = projectionMatrix * mvPosition;", - this.renderBufferDirect = function ( camera, lights, fog, geometry, material, object, group ) { + THREE.ShaderChunk[ "logdepthbuf_vertex" ], + THREE.ShaderChunk[ "worldpos_vertex" ], + THREE.ShaderChunk[ "shadowmap_vertex" ], - setMaterial( material ); + "}" - var program = setProgram( camera, lights, fog, material, object ); + ].join( "\n" ), - var updateBuffers = false; - var geometryProgram = geometry.id + '_' + program.id + '_' + material.wireframe; + fragmentShader: [ - if ( geometryProgram !== _currentGeometryProgram ) { + "uniform vec3 psColor;", + "uniform float opacity;", - _currentGeometryProgram = geometryProgram; - updateBuffers = true; + THREE.ShaderChunk[ "common" ], + 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" ], - } + "void main() {", - // morph targets + " vec3 outgoingLight = vec3( 0.0 );", + " vec4 diffuseColor = vec4( psColor, opacity );", - var morphTargetInfluences = object.morphTargetInfluences; + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "map_particle_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "alphatest_fragment" ], - if ( morphTargetInfluences !== undefined ) { + " outgoingLight = diffuseColor.rgb;", // simple shader - var activeInfluences = []; + THREE.ShaderChunk[ "shadowmap_fragment" ], + THREE.ShaderChunk[ "fog_fragment" ], - for ( var i = 0, l = morphTargetInfluences.length; i < l; i ++ ) { + " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", - var influence = morphTargetInfluences[ i ]; - activeInfluences.push( [ influence, i ] ); + "}" - } + ].join( "\n" ) - activeInfluences.sort( numericalSort ); + }, - if ( activeInfluences.length > 8 ) { + 'dashed': { - activeInfluences.length = 8; + uniforms: THREE.UniformsUtils.merge( [ + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "fog" ], + + { + "scale" : { type: "f", value: 1 }, + "dashSize" : { type: "f", value: 1 }, + "totalSize": { type: "f", value: 2 } } - var morphAttributes = geometry.morphAttributes; + ] ), - for ( var i = 0, l = activeInfluences.length; i < l; i ++ ) { + vertexShader: [ - var influence = activeInfluences[ i ]; - morphInfluences[ i ] = influence[ 0 ]; + "uniform float scale;", + "attribute float lineDistance;", - if ( influence[ 0 ] !== 0 ) { + "varying float vLineDistance;", - var index = influence[ 1 ]; + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], - if ( material.morphTargets === true && morphAttributes.position ) geometry.addAttribute( 'morphTarget' + i, morphAttributes.position[ index ] ); - if ( material.morphNormals === true && morphAttributes.normal ) geometry.addAttribute( 'morphNormal' + i, morphAttributes.normal[ index ] ); + "void main() {", - } else { + THREE.ShaderChunk[ "color_vertex" ], - if ( material.morphTargets === true ) geometry.removeAttribute( 'morphTarget' + i ); - if ( material.morphNormals === true ) geometry.removeAttribute( 'morphNormal' + i ); + " vLineDistance = scale * lineDistance;", - } + " vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + " gl_Position = projectionMatrix * mvPosition;", - } + THREE.ShaderChunk[ "logdepthbuf_vertex" ], - var uniforms = program.getUniforms(); + "}" - if ( uniforms.morphTargetInfluences !== null ) { + ].join( "\n" ), - _gl.uniform1fv( uniforms.morphTargetInfluences, morphInfluences ); + fragmentShader: [ - } + "uniform vec3 diffuse;", + "uniform float opacity;", - updateBuffers = true; + "uniform float dashSize;", + "uniform float totalSize;", - } + "varying float vLineDistance;", - // + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], - var index = geometry.index; - var position = geometry.attributes.position; + "void main() {", - if ( material.wireframe === true ) { + " if ( mod( vLineDistance, totalSize ) > dashSize ) {", - index = objects.getWireframeAttribute( geometry ); + " discard;", - } + " }", - var renderer; + " vec3 outgoingLight = vec3( 0.0 );", + " vec4 diffuseColor = vec4( diffuse, opacity );", - if ( index !== null ) { + THREE.ShaderChunk[ "logdepthbuf_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], - renderer = indexedBufferRenderer; - renderer.setIndex( index ); + " outgoingLight = diffuseColor.rgb;", // simple shader - } else { + THREE.ShaderChunk[ "fog_fragment" ], - renderer = bufferRenderer; + " gl_FragColor = vec4( outgoingLight, diffuseColor.a );", - } + "}" - if ( updateBuffers ) { + ].join( "\n" ) - setupVertexAttributes( material, program, geometry ); + }, - if ( index !== null ) { + 'depth': { - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, objects.getAttributeBuffer( index ) ); + uniforms: { - } + "mNear": { type: "f", value: 1.0 }, + "mFar" : { type: "f", value: 2000.0 }, + "opacity" : { type: "f", value: 1.0 } - } + }, - if ( group === null ) { + vertexShader: [ - var count; + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], - if ( index !== null ) { + "void main() {", - count = index.count; + THREE.ShaderChunk[ "begin_vertex" ], + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "project_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], - } else if ( position instanceof THREE.InterleavedBufferAttribute ) { + "}" - count = position.data.array.length / 3; + ].join( "\n" ), - } else { + fragmentShader: [ - count = position.count; + "uniform float mNear;", + "uniform float mFar;", + "uniform float opacity;", - } + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], - var drawRange = geometry.drawRange; + "void main() {", - group = { - start: drawRange.start, - count: Math.min( drawRange.count, count ) - }; + THREE.ShaderChunk[ "logdepthbuf_fragment" ], - } + " #ifdef USE_LOGDEPTHBUF_EXT", - if ( object instanceof THREE.Mesh ) { + " float depth = gl_FragDepthEXT / gl_FragCoord.w;", - if ( material.wireframe === true ) { + " #else", - state.setLineWidth( material.wireframeLinewidth * pixelRatio ); - renderer.setMode( _gl.LINES ); + " float depth = gl_FragCoord.z / gl_FragCoord.w;", - } else { + " #endif", - renderer.setMode( _gl.TRIANGLES ); + " float color = 1.0 - smoothstep( mNear, mFar, depth );", + " gl_FragColor = vec4( vec3( color ), opacity );", - } + "}" - if ( geometry instanceof THREE.InstancedBufferGeometry && geometry.maxInstancedCount > 0 ) { + ].join( "\n" ) - renderer.renderInstances( geometry ); + }, - } else { + 'normal': { - renderer.render( group.start, group.count ); + uniforms: { - } + "opacity" : { type: "f", value: 1.0 } - } else if ( object instanceof THREE.Line ) { + }, - var lineWidth = material.linewidth; + vertexShader: [ - if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material + "varying vec3 vNormal;", - state.setLineWidth( lineWidth * pixelRatio ); + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], - if ( object instanceof THREE.LineSegments ) { + "void main() {", - renderer.setMode( _gl.LINES ); + " vNormal = normalize( normalMatrix * normal );", - } else { + THREE.ShaderChunk[ "begin_vertex" ], + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "project_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], - renderer.setMode( _gl.LINE_STRIP ); + "}" - } + ].join( "\n" ), - renderer.render( group.start, group.count ); + fragmentShader: [ - } else if ( object instanceof THREE.Points ) { + "uniform float opacity;", + "varying vec3 vNormal;", - renderer.setMode( _gl.POINTS ); - renderer.render( group.start, group.count ); + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], - } + "void main() {", - }; + " gl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );", - function setupVertexAttributes( material, program, geometry, startIndex ) { + THREE.ShaderChunk[ "logdepthbuf_fragment" ], - var extension; + "}" - if ( geometry instanceof THREE.InstancedBufferGeometry ) { + ].join( "\n" ) - extension = extensions.get( 'ANGLE_instanced_arrays' ); + }, - if ( extension === null ) { + /* ------------------------------------------------------------------------- + // Cube map shader + ------------------------------------------------------------------------- */ - console.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); - return; + 'cube': { - } + uniforms: { "tCube": { type: "t", value: null }, + "tFlip": { type: "f", value: - 1 } }, - } + vertexShader: [ - if ( startIndex === undefined ) startIndex = 0; + "varying vec3 vWorldPosition;", - state.initAttributes(); + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], - var geometryAttributes = geometry.attributes; + "void main() {", - var programAttributes = program.getAttributes(); + " vWorldPosition = transformDirection( position, modelMatrix );", - var materialDefaultAttributeValues = material.defaultAttributeValues; + " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", - for ( var name in programAttributes ) { + THREE.ShaderChunk[ "logdepthbuf_vertex" ], - var programAttribute = programAttributes[ name ]; + "}" - if ( programAttribute >= 0 ) { + ].join( "\n" ), - var geometryAttribute = geometryAttributes[ name ]; + fragmentShader: [ - if ( geometryAttribute !== undefined ) { + "uniform samplerCube tCube;", + "uniform float tFlip;", - state.enableAttribute( programAttribute ); + "varying vec3 vWorldPosition;", - var size = geometryAttribute.itemSize; - var buffer = objects.getAttributeBuffer( geometryAttribute ); + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], - if ( geometryAttribute instanceof THREE.InterleavedBufferAttribute ) { + "void main() {", - var data = geometryAttribute.data; - var stride = data.stride; - var offset = geometryAttribute.offset; + " gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );", - _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); - _gl.vertexAttribPointer( programAttribute, size, _gl.FLOAT, false, stride * data.array.BYTES_PER_ELEMENT, ( startIndex * stride + offset ) * data.array.BYTES_PER_ELEMENT ); + THREE.ShaderChunk[ "logdepthbuf_fragment" ], - if ( data instanceof THREE.InstancedInterleavedBuffer ) { + "}" - if ( extension === null ) { + ].join( "\n" ) - console.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferAttribute but hardware does not support extension ANGLE_instanced_arrays.' ); - return; + }, - } + /* ------------------------------------------------------------------------- + // Cube map shader + ------------------------------------------------------------------------- */ - extension.vertexAttribDivisorANGLE( programAttribute, data.meshPerAttribute ); + 'equirect': { - if ( geometry.maxInstancedCount === undefined ) { + uniforms: { "tEquirect": { type: "t", value: null }, + "tFlip": { type: "f", value: - 1 } }, - geometry.maxInstancedCount = data.meshPerAttribute * data.count; + vertexShader: [ - } + "varying vec3 vWorldPosition;", - } + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], - } else { + "void main() {", - _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); - _gl.vertexAttribPointer( programAttribute, size, _gl.FLOAT, false, 0, startIndex * size * 4 ); // 4 bytes per Float32 + " vWorldPosition = transformDirection( position, modelMatrix );", - if ( geometryAttribute instanceof THREE.InstancedBufferAttribute ) { + " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", - if ( extension === null ) { + THREE.ShaderChunk[ "logdepthbuf_vertex" ], - console.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferAttribute but hardware does not support extension ANGLE_instanced_arrays.' ); - return; + "}" - } + ].join( "\n" ), - extension.vertexAttribDivisorANGLE( programAttribute, geometryAttribute.meshPerAttribute ); + fragmentShader: [ - if ( geometry.maxInstancedCount === undefined ) { + "uniform sampler2D tEquirect;", + "uniform float tFlip;", - geometry.maxInstancedCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; + "varying vec3 vWorldPosition;", - } + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], - } + "void main() {", - } + // " gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );", + "vec3 direction = normalize( vWorldPosition );", + "vec2 sampleUV;", + "sampleUV.y = saturate( tFlip * direction.y * -0.5 + 0.5 );", + "sampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;", + "gl_FragColor = texture2D( tEquirect, sampleUV );", - } else if ( materialDefaultAttributeValues !== undefined ) { + THREE.ShaderChunk[ "logdepthbuf_fragment" ], - var value = materialDefaultAttributeValues[ name ]; + "}" - if ( value !== undefined ) { + ].join( "\n" ) - switch ( value.length ) { + }, - case 2: - _gl.vertexAttrib2fv( programAttribute, value ); - break; + /* 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/ + */ - case 3: - _gl.vertexAttrib3fv( programAttribute, value ); - break; + 'depthRGBA': { - case 4: - _gl.vertexAttrib4fv( programAttribute, value ); - break; + uniforms: {}, - default: - _gl.vertexAttrib1fv( programAttribute, value ); + vertexShader: [ - } + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], - } + "void main() {", - } + THREE.ShaderChunk[ "skinbase_vertex" ], - } + THREE.ShaderChunk[ "begin_vertex" ], + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "project_vertex" ], + THREE.ShaderChunk[ "logdepthbuf_vertex" ], - } + "}" - state.disableUnusedAttributes(); + ].join( "\n" ), - } + fragmentShader: [ - // Sorting + THREE.ShaderChunk[ "common" ], + THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], - function numericalSort ( a, b ) { + "vec4 pack_depth( const in float depth ) {", - return b[ 0 ] - a[ 0 ]; + " 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;", - } + "}", - function painterSortStable ( a, b ) { + "void main() {", - if ( a.object.renderOrder !== b.object.renderOrder ) { + THREE.ShaderChunk[ "logdepthbuf_fragment" ], - return a.object.renderOrder - b.object.renderOrder; + " #ifdef USE_LOGDEPTHBUF_EXT", - } else if ( a.material.id !== b.material.id ) { + " gl_FragData[ 0 ] = pack_depth( gl_FragDepthEXT );", - return a.material.id - b.material.id; + " #else", - } else if ( a.z !== b.z ) { + " gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );", - return a.z - b.z; + " #endif", - } else { + //"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 );", - return a.id - b.id; + "}" - } + ].join( "\n" ) } - function reversePainterSortStable ( a, b ) { +}; - if ( a.object.renderOrder !== b.object.renderOrder ) { +// File:src/renderers/WebGLRenderer.js - return a.object.renderOrder - b.object.renderOrder; +/** + * @author supereggbert / http://www.paulbrunt.co.uk/ + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author szimek / https://github.com/szimek/ + */ - } if ( a.z !== b.z ) { +THREE.WebGLRenderer = function ( parameters ) { - return b.z - a.z; + console.log( 'THREE.WebGLRenderer', THREE.REVISION ); - } else { + parameters = parameters || {}; - return a.id - b.id; + var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElement( 'canvas' ), + _context = parameters.context !== undefined ? parameters.context : null, - } + _width = _canvas.width, + _height = _canvas.height, - } + pixelRatio = 1, - // Rendering + _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, - this.render = function ( scene, camera, renderTarget, forceClear ) { + _clearColor = new THREE.Color( 0x000000 ), + _clearAlpha = 0; - if ( camera instanceof THREE.Camera === false ) { + var lights = []; - console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); - return; + var opaqueObjects = []; + var opaqueObjectsLastIndex = -1; + var transparentObjects = []; + var transparentObjectsLastIndex = -1; - } + var morphInfluences = new Float32Array( 8 ); - var fog = scene.fog; - // reset caching for this frame + var sprites = []; + var lensFlares = []; - _currentGeometryProgram = ''; - _currentMaterialId = - 1; - _currentCamera = null; - _lightsNeedUpdate = true; + // public properties - // update scene graph + this.domElement = _canvas; + this.context = null; - if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); + // clearing - // update camera matrices and frustum + this.autoClear = true; + this.autoClearColor = true; + this.autoClearDepth = true; + this.autoClearStencil = true; - if ( camera.parent === null ) camera.updateMatrixWorld(); + // scene graph - camera.matrixWorldInverse.getInverse( camera.matrixWorld ); + this.sortObjects = true; - _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); - _frustum.setFromMatrix( _projScreenMatrix ); + // physically based shading - lights.length = 0; + this.gammaFactor = 2.0; // for backwards compatibility + this.gammaInput = false; + this.gammaOutput = false; - opaqueObjectsLastIndex = -1; - transparentObjectsLastIndex = -1; + // morphs - sprites.length = 0; - lensFlares.length = 0; + this.maxMorphTargets = 8; + this.maxMorphNormals = 4; - projectObject( scene ); + // flags - opaqueObjects.length = opaqueObjectsLastIndex + 1; - transparentObjects.length = transparentObjectsLastIndex + 1; + this.autoScaleCubemaps = true; - if ( _this.sortObjects === true ) { + // internal properties - opaqueObjects.sort( painterSortStable ); - transparentObjects.sort( reversePainterSortStable ); + var _this = this, - } + // internal state cache - // + _currentProgram = null, + _currentFramebuffer = null, + _currentMaterialId = - 1, + _currentGeometryProgram = '', + _currentCamera = null, - shadowMap.render( scene, camera ); + _usedTextureUnits = 0, - // + _viewportX = 0, + _viewportY = 0, + _viewportWidth = _canvas.width, + _viewportHeight = _canvas.height, + _currentWidth = 0, + _currentHeight = 0, - _infoRender.calls = 0; - _infoRender.vertices = 0; - _infoRender.faces = 0; - _infoRender.points = 0; + // frustum - this.setRenderTarget( renderTarget ); + _frustum = new THREE.Frustum(), - if ( this.autoClear || forceClear ) { + // camera matrices cache - this.clear( this.autoClearColor, this.autoClearDepth, this.autoClearStencil ); + _projScreenMatrix = new THREE.Matrix4(), - } + _vector3 = new THREE.Vector3(), - // + // light arrays cache - if ( scene.overrideMaterial ) { + _direction = new THREE.Vector3(), - var overrideMaterial = scene.overrideMaterial; + _lightsNeedUpdate = true, - renderObjects( opaqueObjects, camera, lights, fog, overrideMaterial ); - renderObjects( transparentObjects, camera, lights, fog, overrideMaterial ); + _lights = { - } else { + ambient: [ 0, 0, 0 ], + directional: { length: 0, colors: [], positions: [] }, + point: { length: 0, colors: [], positions: [], distances: [], decays: [] }, + spot: { length: 0, colors: [], positions: [], distances: [], directions: [], anglesCos: [], exponents: [], decays: [] }, + hemi: { length: 0, skyColors: [], groundColors: [], positions: [] } - // opaque pass (front-to-back order) + }, - state.setBlending( THREE.NoBlending ); - renderObjects( opaqueObjects, camera, lights, fog ); + // info - // transparent pass (back-to-front order) + _infoMemory = { - renderObjects( transparentObjects, camera, lights, fog ); + geometries: 0, + textures: 0 - } + }, - // custom render plugins (post pass) + _infoRender = { - spritePlugin.render( scene, camera ); - lensFlarePlugin.render( scene, camera, _currentWidth, _currentHeight ); + calls: 0, + vertices: 0, + faces: 0, + points: 0 - // Generate mipmap if we're using any kind of mipmap filtering + }; - if ( renderTarget && renderTarget.generateMipmaps && renderTarget.minFilter !== THREE.NearestFilter && renderTarget.minFilter !== THREE.LinearFilter ) { + this.info = { - updateRenderTargetMipmap( renderTarget ); + render: _infoRender, + memory: _infoMemory, + programs: null - } + }; - // Ensure depth buffer writing is enabled so it can be cleared on next render - state.setDepthTest( true ); - state.setDepthWrite( true ); - state.setColorWrite( true ); + // initialize - // _gl.finish(); + var _gl; - }; + try { - function pushRenderItem( object, geometry, material, z, group ) { + var attributes = { + alpha: _alpha, + depth: _depth, + stencil: _stencil, + antialias: _antialias, + premultipliedAlpha: _premultipliedAlpha, + preserveDrawingBuffer: _preserveDrawingBuffer + }; - var array, index; + _gl = _context || _canvas.getContext( 'webgl', attributes ) || _canvas.getContext( 'experimental-webgl', attributes ); - // allocate the next position in the appropriate array + if ( _gl === null ) { - if ( material.transparent ) { + if ( _canvas.getContext( 'webgl' ) !== null ) { - array = transparentObjects; - index = ++ transparentObjectsLastIndex; + throw 'Error creating WebGL context with your selected attributes.'; - } else { + } else { - array = opaqueObjects; - index = ++ opaqueObjectsLastIndex; + throw 'Error creating WebGL context.'; + + } } - // recycle existing render item or grow the array + _canvas.addEventListener( 'webglcontextlost', onContextLost, false ); - var renderItem = array[ index ]; + } catch ( error ) { - if ( renderItem !== undefined ) { + console.error( 'THREE.WebGLRenderer: ' + error ); - renderItem.id = object.id; - renderItem.object = object; - renderItem.geometry = geometry; - renderItem.material = material; - renderItem.z = _vector3.z; - renderItem.group = group; + } - } else { + var extensions = new THREE.WebGLExtensions( _gl ); - renderItem = { - id: object.id, - object: object, - geometry: geometry, - material: material, - z: _vector3.z, - group: group - }; + extensions.get( 'OES_texture_float' ); + extensions.get( 'OES_texture_float_linear' ); + extensions.get( 'OES_texture_half_float' ); + extensions.get( 'OES_texture_half_float_linear' ); + extensions.get( 'OES_standard_derivatives' ); + extensions.get( 'ANGLE_instanced_arrays' ); - // assert( index === array.length ); - array.push( renderItem ); + if ( extensions.get( 'OES_element_index_uint' ) ) { - } + THREE.BufferGeometry.MaxIndex = 4294967296; } - function projectObject( object ) { + var capabilities = new THREE.WebGLCapabilities( _gl, extensions, parameters ); - if ( object.visible === false ) return; + var state = new THREE.WebGLState( _gl, extensions, paramThreeToGL ); + var properties = new THREE.WebGLProperties(); + var objects = new THREE.WebGLObjects( _gl, properties, this.info ); + var programCache = new THREE.WebGLPrograms( this, capabilities ); - if ( object instanceof THREE.Light ) { + this.info.programs = programCache.programs; - lights.push( object ); + var bufferRenderer = new THREE.WebGLBufferRenderer( _gl, extensions, _infoRender ); + var indexedBufferRenderer = new THREE.WebGLIndexedBufferRenderer( _gl, extensions, _infoRender ); - } else if ( object instanceof THREE.Sprite ) { + // - sprites.push( object ); + function glClearColor( r, g, b, a ) { - } else if ( object instanceof THREE.LensFlare ) { + if ( _premultipliedAlpha === true ) { - lensFlares.push( object ); + r *= a; g *= a; b *= a; - } else if ( object instanceof THREE.ImmediateRenderObject ) { + } - if ( _this.sortObjects === true ) { + _gl.clearColor( r, g, b, a ); - _vector3.setFromMatrixPosition( object.matrixWorld ); - _vector3.applyProjection( _projScreenMatrix ); + } - } + function setDefaultGLState() { - pushRenderItem( object, null, object.material, _vector3.z, null ); + state.init(); - } else if ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Points ) { + _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight ); - if ( object instanceof THREE.SkinnedMesh ) { + glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); - object.skeleton.update(); + } - } + function resetGLState() { - if ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) { + _currentProgram = null; + _currentCamera = null; - var material = object.material; + _currentGeometryProgram = ''; + _currentMaterialId = - 1; - if ( material.visible === true ) { + _lightsNeedUpdate = true; - if ( _this.sortObjects === true ) { + state.reset(); - _vector3.setFromMatrixPosition( object.matrixWorld ); - _vector3.applyProjection( _projScreenMatrix ); + } - } + setDefaultGLState(); - var geometry = objects.update( object ); + this.context = _gl; + this.capabilities = capabilities; + this.extensions = extensions; + this.state = state; - if ( material instanceof THREE.MeshFaceMaterial ) { + // shadow map - var groups = geometry.groups; - var materials = material.materials; + var shadowMap = new THREE.WebGLShadowMap( this, lights, objects ); - for ( var i = 0, l = groups.length; i < l; i ++ ) { + this.shadowMap = shadowMap; - var group = groups[ i ]; - var groupMaterial = materials[ group.materialIndex ]; - if ( groupMaterial.visible === true ) { + // Plugins - pushRenderItem( object, geometry, groupMaterial, _vector3.z, group ); + var spritePlugin = new THREE.SpritePlugin( this, sprites ); + var lensFlarePlugin = new THREE.LensFlarePlugin( this, lensFlares ); - } + // API - } + this.getContext = function () { - } else { + return _gl; - pushRenderItem( object, geometry, material, _vector3.z, null ); + }; - } + this.getContextAttributes = function () { - } + return _gl.getContextAttributes(); - } + }; - } + this.forceContextLoss = function () { - var children = object.children; + extensions.get( 'WEBGL_lose_context' ).loseContext(); - for ( var i = 0, l = children.length; i < l; i ++ ) { + }; - projectObject( children[ i ] ); + this.getMaxAnisotropy = ( function () { - } + var value; - } + return function getMaxAnisotropy() { - function renderObjects( renderList, camera, lights, fog, overrideMaterial ) { + if ( value !== undefined ) return value; - for ( var i = 0, l = renderList.length; i < l; i ++ ) { + var extension = extensions.get( 'EXT_texture_filter_anisotropic' ); - var renderItem = renderList[ i ]; + if ( extension !== null ) { - var object = renderItem.object; - var geometry = renderItem.geometry; - var material = overrideMaterial === undefined ? renderItem.material : overrideMaterial; - var group = renderItem.group; + value = _gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ); - object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); - object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); + } else { - if ( object instanceof THREE.ImmediateRenderObject ) { + value = 0; - setMaterial( material ); + } - var program = setProgram( camera, lights, fog, material, object ); + return value; - _currentGeometryProgram = ''; + } - object.render( function ( object ) { + } )(); - _this.renderBufferImmediate( object, program, material ); + this.getPrecision = function () { - } ); + return capabilities.precision; - } else { + }; - _this.renderBufferDirect( camera, lights, fog, geometry, material, object, group ); + this.getPixelRatio = function () { - } + return pixelRatio; - } + }; - } + this.setPixelRatio = function ( value ) { - function initMaterial( material, lights, fog, object ) { + if ( value !== undefined ) pixelRatio = value; - var materialProperties = properties.get( material ); + }; - var parameters = programCache.getParameters( material, lights, fog, object ); - var code = programCache.getProgramCode( material, parameters ); + this.getSize = function () { - var program = materialProperties.program; - var programChange = true; + return { + width: _width, + height: _height + }; - if ( program === undefined ) { + }; - // new material - material.addEventListener( 'dispose', onMaterialDispose ); + this.setSize = function ( width, height, updateStyle ) { - } else if ( program.code !== code ) { + _width = width; + _height = height; - // changed glsl or parameters - releaseMaterialProgramReference( material ); + _canvas.width = width * pixelRatio; + _canvas.height = height * pixelRatio; - } else if ( parameters.shaderID !== undefined ) { + if ( updateStyle !== false ) { - // same glsl and uniform list - return; + _canvas.style.width = width + 'px'; + _canvas.style.height = height + 'px'; - } else { + } - // only rebuild uniform list - programChange = false; + this.setViewport( 0, 0, width, height ); - } + }; - if ( programChange ) { + this.setViewport = function ( x, y, width, height ) { - if ( parameters.shaderID ) { + _viewportX = x * pixelRatio; + _viewportY = y * pixelRatio; - var shader = THREE.ShaderLib[ parameters.shaderID ]; + _viewportWidth = width * pixelRatio; + _viewportHeight = height * pixelRatio; - materialProperties.__webglShader = { - name: material.type, - uniforms: THREE.UniformsUtils.clone( shader.uniforms ), - vertexShader: shader.vertexShader, - fragmentShader: shader.fragmentShader - }; + _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight ); - } else { + }; - materialProperties.__webglShader = { - name: material.type, - uniforms: material.uniforms, - vertexShader: material.vertexShader, - fragmentShader: material.fragmentShader - }; + this.setScissor = function ( x, y, width, height ) { - } + _gl.scissor( + x * pixelRatio, + y * pixelRatio, + width * pixelRatio, + height * pixelRatio + ); - material.__webglShader = materialProperties.__webglShader; + }; - program = programCache.acquireProgram( material, parameters, code ); + this.enableScissorTest = function ( boolean ) { - materialProperties.program = program; - material.program = program; + state.setScissorTest( boolean ); - } + }; - var attributes = program.getAttributes(); + // Clearing - if ( material.morphTargets ) { + this.getClearColor = function () { - material.numSupportedMorphTargets = 0; + return _clearColor; - for ( var i = 0; i < _this.maxMorphTargets; i ++ ) { + }; - if ( attributes[ 'morphTarget' + i ] >= 0 ) { + this.setClearColor = function ( color, alpha ) { - material.numSupportedMorphTargets ++; + _clearColor.set( color ); - } + _clearAlpha = alpha !== undefined ? alpha : 1; - } + glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); - } + }; - if ( material.morphNormals ) { + this.getClearAlpha = function () { - material.numSupportedMorphNormals = 0; + return _clearAlpha; - for ( i = 0; i < _this.maxMorphNormals; i ++ ) { + }; - if ( attributes[ 'morphNormal' + i ] >= 0 ) { + this.setClearAlpha = function ( alpha ) { - material.numSupportedMorphNormals ++; + _clearAlpha = alpha; - } + glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); - } + }; - } + this.clear = function ( color, depth, stencil ) { - materialProperties.uniformsList = []; + var bits = 0; - var uniformLocations = materialProperties.program.getUniforms(); + 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; - for ( var u in materialProperties.__webglShader.uniforms ) { + _gl.clear( bits ); - var location = uniformLocations[ u ]; + }; - if ( location ) { + this.clearColor = function () { - materialProperties.uniformsList.push( [ materialProperties.__webglShader.uniforms[ u ], location ] ); + _gl.clear( _gl.COLOR_BUFFER_BIT ); - } + }; - } + this.clearDepth = function () { - } + _gl.clear( _gl.DEPTH_BUFFER_BIT ); - function setMaterial( material ) { + }; - setMaterialFaces( material ); + this.clearStencil = function () { - if ( material.transparent === true ) { + _gl.clear( _gl.STENCIL_BUFFER_BIT ); - state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha ); + }; - } else { + this.clearTarget = function ( renderTarget, color, depth, stencil ) { - state.setBlending( THREE.NoBlending ); + this.setRenderTarget( renderTarget ); + this.clear( color, depth, stencil ); - } + }; - state.setDepthFunc( material.depthFunc ); - state.setDepthTest( material.depthTest ); - state.setDepthWrite( material.depthWrite ); - state.setColorWrite( material.colorWrite ); - state.setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); + // Reset - } + this.resetGLState = resetGLState; - function setMaterialFaces( material ) { + this.dispose = function() { - material.side !== THREE.DoubleSide ? state.enable( _gl.CULL_FACE ) : state.disable( _gl.CULL_FACE ); - state.setFlipSided( material.side === THREE.BackSide ); + _canvas.removeEventListener( 'webglcontextlost', onContextLost, false ); - } + }; - function setProgram( camera, lights, fog, material, object ) { + // Events - _usedTextureUnits = 0; + function onContextLost( event ) { - var materialProperties = properties.get( material ); + event.preventDefault(); - if ( material.needsUpdate || ! materialProperties.program ) { + resetGLState(); + setDefaultGLState(); - initMaterial( material, lights, fog, object ); - material.needsUpdate = false; + properties.clear(); - } + }; - var refreshProgram = false; - var refreshMaterial = false; - var refreshLights = false; + function onTextureDispose( event ) { - var program = materialProperties.program, - p_uniforms = program.getUniforms(), - m_uniforms = materialProperties.__webglShader.uniforms; + var texture = event.target; - if ( program.id !== _currentProgram ) { + texture.removeEventListener( 'dispose', onTextureDispose ); - _gl.useProgram( program.program ); - _currentProgram = program.id; + deallocateTexture( texture ); - refreshProgram = true; - refreshMaterial = true; - refreshLights = true; + _infoMemory.textures --; - } - if ( material.id !== _currentMaterialId ) { + } - if ( _currentMaterialId === - 1 ) refreshLights = true; - _currentMaterialId = material.id; + function onRenderTargetDispose( event ) { - refreshMaterial = true; + var renderTarget = event.target; - } + renderTarget.removeEventListener( 'dispose', onRenderTargetDispose ); - if ( refreshProgram || camera !== _currentCamera ) { + deallocateRenderTarget( renderTarget ); - _gl.uniformMatrix4fv( p_uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); + _infoMemory.textures --; - if ( capabilities.logarithmicDepthBuffer ) { + } - _gl.uniform1f( p_uniforms.logDepthBufFC, 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); + function onMaterialDispose( event ) { - } + var material = event.target; + material.removeEventListener( 'dispose', onMaterialDispose ); - if ( camera !== _currentCamera ) _currentCamera = camera; + deallocateMaterial( material ); - // load material specific uniforms - // (shader material also gets them for the sake of genericity) + } - if ( material instanceof THREE.ShaderMaterial || - material instanceof THREE.MeshPhongMaterial || - material.envMap ) { + // Buffer deallocation - if ( p_uniforms.cameraPosition !== undefined ) { + function deallocateTexture( texture ) { - _vector3.setFromMatrixPosition( camera.matrixWorld ); - _gl.uniform3f( p_uniforms.cameraPosition, _vector3.x, _vector3.y, _vector3.z ); + var textureProperties = properties.get( texture ); - } + if ( texture.image && textureProperties.__image__webglTextureCube ) { - } + // cube texture - if ( material instanceof THREE.MeshPhongMaterial || - material instanceof THREE.MeshLambertMaterial || - material instanceof THREE.MeshBasicMaterial || - material instanceof THREE.ShaderMaterial || - material.skinning ) { + _gl.deleteTexture( textureProperties.__image__webglTextureCube ); - if ( p_uniforms.viewMatrix !== undefined ) { + } else { - _gl.uniformMatrix4fv( p_uniforms.viewMatrix, false, camera.matrixWorldInverse.elements ); + // 2D texture - } + if ( textureProperties.__webglInit === undefined ) return; - } + _gl.deleteTexture( textureProperties.__webglTexture ); } - // 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 + // remove all webgl properties + properties.delete( texture ); - if ( material.skinning ) { + } - if ( object.bindMatrix && p_uniforms.bindMatrix !== undefined ) { + function deallocateRenderTarget( renderTarget ) { - _gl.uniformMatrix4fv( p_uniforms.bindMatrix, false, object.bindMatrix.elements ); + var renderTargetProperties = properties.get( renderTarget ); - } + if ( ! renderTarget || renderTargetProperties.__webglTexture === undefined ) return; - if ( object.bindMatrixInverse && p_uniforms.bindMatrixInverse !== undefined ) { + _gl.deleteTexture( renderTargetProperties.__webglTexture ); - _gl.uniformMatrix4fv( p_uniforms.bindMatrixInverse, false, object.bindMatrixInverse.elements ); + if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) { - } + for ( var i = 0; i < 6; i ++ ) { - if ( capabilities.floatVertexTextures && object.skeleton && object.skeleton.useVertexTexture ) { + _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] ); + _gl.deleteRenderbuffer( renderTargetProperties.__webglRenderbuffer[ i ] ); - if ( p_uniforms.boneTexture !== undefined ) { + } - var textureUnit = getTextureUnit(); + } else { - _gl.uniform1i( p_uniforms.boneTexture, textureUnit ); - _this.setTexture( object.skeleton.boneTexture, textureUnit ); + _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer ); + _gl.deleteRenderbuffer( renderTargetProperties.__webglRenderbuffer ); - } + } - if ( p_uniforms.boneTextureWidth !== undefined ) { + properties.delete( renderTarget ); - _gl.uniform1i( p_uniforms.boneTextureWidth, object.skeleton.boneTextureWidth ); + } - } + function deallocateMaterial( material ) { - if ( p_uniforms.boneTextureHeight !== undefined ) { + releaseMaterialProgramReference( material ); - _gl.uniform1i( p_uniforms.boneTextureHeight, object.skeleton.boneTextureHeight ); + properties.delete( material ); - } + } - } else if ( object.skeleton && object.skeleton.boneMatrices ) { - if ( p_uniforms.boneGlobalMatrices !== undefined ) { + function releaseMaterialProgramReference( material ) { - _gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, object.skeleton.boneMatrices ); + var programInfo = properties.get( material ).program; - } + material.program = undefined; - } + if ( programInfo !== undefined ) { + programCache.releaseProgram( programInfo ); } - if ( refreshMaterial ) { - - // refresh uniforms common to several materials - - if ( fog && material.fog ) { + } - refreshUniformsFog( m_uniforms, fog ); + // Buffer rendering - } + this.renderBufferImmediate = function ( object, program, material ) { - if ( material instanceof THREE.MeshPhongMaterial || - material instanceof THREE.MeshLambertMaterial || - material.lights ) { + state.initAttributes(); - if ( _lightsNeedUpdate ) { + var buffers = properties.get( object ); - refreshLights = true; - setupLights( lights, camera ); - _lightsNeedUpdate = false; + if ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer(); + if ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer(); + if ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer(); + if ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer(); - } + var attributes = program.getAttributes(); - if ( refreshLights ) { + if ( object.hasPositions ) { - refreshUniformsLights( m_uniforms, _lights ); - markUniformsLightsNeedsUpdate( m_uniforms, true ); + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.position ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW ); - } else { + state.enableAttribute( attributes.position ); + _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); - markUniformsLightsNeedsUpdate( m_uniforms, false ); + } - } + if ( object.hasNormals ) { - } + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.normal ); - if ( material instanceof THREE.MeshBasicMaterial || - material instanceof THREE.MeshLambertMaterial || - material instanceof THREE.MeshPhongMaterial ) { + if ( material.type !== 'MeshPhongMaterial' && material.shading === THREE.FlatShading ) { - refreshUniformsCommon( m_uniforms, material ); + for ( var i = 0, l = object.count * 3; i < l; i += 9 ) { - } + var array = object.normalArray; - // refresh single material specific uniforms + var nx = ( array[ i + 0 ] + array[ i + 3 ] + array[ i + 6 ] ) / 3; + var ny = ( array[ i + 1 ] + array[ i + 4 ] + array[ i + 7 ] ) / 3; + var nz = ( array[ i + 2 ] + array[ i + 5 ] + array[ i + 8 ] ) / 3; - if ( material instanceof THREE.LineBasicMaterial ) { + array[ i + 0 ] = nx; + array[ i + 1 ] = ny; + array[ i + 2 ] = nz; - refreshUniformsLine( m_uniforms, material ); + array[ i + 3 ] = nx; + array[ i + 4 ] = ny; + array[ i + 5 ] = nz; - } else if ( material instanceof THREE.LineDashedMaterial ) { + array[ i + 6 ] = nx; + array[ i + 7 ] = ny; + array[ i + 8 ] = nz; - refreshUniformsLine( m_uniforms, material ); - refreshUniformsDash( m_uniforms, material ); + } - } else if ( material instanceof THREE.PointsMaterial ) { + } - refreshUniformsParticle( m_uniforms, material ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW ); - } else if ( material instanceof THREE.MeshPhongMaterial ) { + state.enableAttribute( attributes.normal ); - refreshUniformsPhong( m_uniforms, material ); + _gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); - } else if ( material instanceof THREE.MeshDepthMaterial ) { + } - m_uniforms.mNear.value = camera.near; - m_uniforms.mFar.value = camera.far; - m_uniforms.opacity.value = material.opacity; + if ( object.hasUvs && material.map ) { - } else if ( material instanceof THREE.MeshNormalMaterial ) { + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.uv ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW ); - m_uniforms.opacity.value = material.opacity; + state.enableAttribute( attributes.uv ); - } + _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); - if ( object.receiveShadow && ! material._shadowPass ) { + } - refreshUniformsShadow( m_uniforms, lights ); + if ( object.hasColors && material.vertexColors !== THREE.NoColors ) { - } + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.color ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW ); - // load common uniforms + state.enableAttribute( attributes.color ); - loadUniformsGeneric( materialProperties.uniformsList ); + _gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 ); } - loadUniformsMatrices( p_uniforms, object ); + state.disableUnusedAttributes(); - if ( p_uniforms.modelMatrix !== undefined ) { + _gl.drawArrays( _gl.TRIANGLES, 0, object.count ); - _gl.uniformMatrix4fv( p_uniforms.modelMatrix, false, object.matrixWorld.elements ); + object.count = 0; - } + }; - return program; + this.renderBufferDirect = function ( camera, lights, fog, geometry, material, object, group ) { - } + setMaterial( material ); - // Uniforms (refresh uniforms objects) + var program = setProgram( camera, lights, fog, material, object ); - function refreshUniformsCommon ( uniforms, material ) { + var updateBuffers = false; + var geometryProgram = geometry.id + '_' + program.id + '_' + material.wireframe; - uniforms.opacity.value = material.opacity; + if ( geometryProgram !== _currentGeometryProgram ) { - uniforms.diffuse.value = material.color; + _currentGeometryProgram = geometryProgram; + updateBuffers = true; - if ( material.emissive ) { + } - uniforms.emissive.value = material.emissive; + // morph targets - } + var morphTargetInfluences = object.morphTargetInfluences; - uniforms.map.value = material.map; - uniforms.specularMap.value = material.specularMap; - uniforms.alphaMap.value = material.alphaMap; + if ( morphTargetInfluences !== undefined ) { - if ( material.aoMap ) { + var activeInfluences = []; - uniforms.aoMap.value = material.aoMap; - uniforms.aoMapIntensity.value = material.aoMapIntensity; + for ( var i = 0, l = morphTargetInfluences.length; i < l; i ++ ) { - } + var influence = morphTargetInfluences[ i ]; + activeInfluences.push( [ influence, i ] ); - // uv repeat and offset setting priorities - // 1. color map - // 2. specular map - // 3. normal map - // 4. bump map - // 5. alpha map - // 6. emissive map + } - var uvScaleMap; + activeInfluences.sort( numericalSort ); - if ( material.map ) { + if ( activeInfluences.length > 8 ) { - uvScaleMap = material.map; + activeInfluences.length = 8; - } else if ( material.specularMap ) { + } - uvScaleMap = material.specularMap; + var morphAttributes = geometry.morphAttributes; - } else if ( material.displacementMap ) { + for ( var i = 0, l = activeInfluences.length; i < l; i ++ ) { - uvScaleMap = material.displacementMap; + var influence = activeInfluences[ i ]; + morphInfluences[ i ] = influence[ 0 ]; - } else if ( material.normalMap ) { + if ( influence[ 0 ] !== 0 ) { - uvScaleMap = material.normalMap; + var index = influence[ 1 ]; - } else if ( material.bumpMap ) { + if ( material.morphTargets === true && morphAttributes.position ) geometry.addAttribute( 'morphTarget' + i, morphAttributes.position[ index ] ); + if ( material.morphNormals === true && morphAttributes.normal ) geometry.addAttribute( 'morphNormal' + i, morphAttributes.normal[ index ] ); - uvScaleMap = material.bumpMap; + } else { - } else if ( material.alphaMap ) { + if ( material.morphTargets === true ) geometry.removeAttribute( 'morphTarget' + i ); + if ( material.morphNormals === true ) geometry.removeAttribute( 'morphNormal' + i ); - uvScaleMap = material.alphaMap; + } - } else if ( material.emissiveMap ) { + } - uvScaleMap = material.emissiveMap; + var uniforms = program.getUniforms(); - } + if ( uniforms.morphTargetInfluences !== null ) { - if ( uvScaleMap !== undefined ) { + _gl.uniform1fv( uniforms.morphTargetInfluences, morphInfluences ); - var offset = uvScaleMap.offset; - var repeat = uvScaleMap.repeat; + } - uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y ); + updateBuffers = true; } - uniforms.envMap.value = material.envMap; - uniforms.flipEnvMap.value = ( material.envMap instanceof THREE.WebGLRenderTargetCube ) ? 1 : - 1; + // - uniforms.reflectivity.value = material.reflectivity; - uniforms.refractionRatio.value = material.refractionRatio; + var index = geometry.index; + var position = geometry.attributes.position; - } + if ( material.wireframe === true ) { - function refreshUniformsLine ( uniforms, material ) { + index = objects.getWireframeAttribute( geometry ); - uniforms.diffuse.value = material.color; - uniforms.opacity.value = material.opacity; + } - } + var renderer; - function refreshUniformsDash ( uniforms, material ) { + if ( index !== null ) { - uniforms.dashSize.value = material.dashSize; - uniforms.totalSize.value = material.dashSize + material.gapSize; - uniforms.scale.value = material.scale; + renderer = indexedBufferRenderer; + renderer.setIndex( index ); - } + } else { - function refreshUniformsParticle ( uniforms, material ) { + renderer = bufferRenderer; - 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; + if ( updateBuffers ) { - if ( material.map !== null ) { + setupVertexAttributes( material, program, geometry ); - var offset = material.map.offset; - var repeat = material.map.repeat; + if ( index !== null ) { - uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y ); + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, objects.getAttributeBuffer( index ) ); + + } } - } + if ( group === null ) { - function refreshUniformsFog ( uniforms, fog ) { + var count; - uniforms.fogColor.value = fog.color; + if ( index !== null ) { - if ( fog instanceof THREE.Fog ) { + count = index.array.length; - uniforms.fogNear.value = fog.near; - uniforms.fogFar.value = fog.far; + } else { - } else if ( fog instanceof THREE.FogExp2 ) { + count = position.count; - uniforms.fogDensity.value = fog.density; + } - } + var drawRange = geometry.drawRange; - } + group = { + start: drawRange.start, + count: Math.min( drawRange.count, count ) + }; - function refreshUniformsPhong ( uniforms, material ) { + } - uniforms.specular.value = material.specular; - uniforms.shininess.value = material.shininess; + if ( object instanceof THREE.Mesh ) { - if ( material.lightMap ) { + if ( material.wireframe === true ) { - uniforms.lightMap.value = material.lightMap; - uniforms.lightMapIntensity.value = material.lightMapIntensity; + state.setLineWidth( material.wireframeLinewidth * pixelRatio ); + renderer.setMode( _gl.LINES ); - } + } else { - if ( material.emissiveMap ) { + renderer.setMode( _gl.TRIANGLES ); - uniforms.emissiveMap.value = material.emissiveMap; + } - } + if ( geometry instanceof THREE.InstancedBufferGeometry && geometry.maxInstancedCount > 0 ) { - if ( material.bumpMap ) { + renderer.renderInstances( geometry ); - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; + } else { - } + renderer.render( group.start, group.count ); - if ( material.normalMap ) { + } - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); + } else if ( object instanceof THREE.Line ) { - } + var lineWidth = material.linewidth; - if ( material.displacementMap ) { + if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material - uniforms.displacementMap.value = material.displacementMap; - uniforms.displacementScale.value = material.displacementScale; - uniforms.displacementBias.value = material.displacementBias; + state.setLineWidth( lineWidth * pixelRatio ); - } + if ( object instanceof THREE.LineSegments ) { - } + renderer.setMode( _gl.LINES ); - function refreshUniformsLights ( uniforms, lights ) { + } else { - uniforms.ambientLightColor.value = lights.ambient; + renderer.setMode( _gl.LINE_STRIP ); - 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; - uniforms.pointLightDecay.value = lights.point.decays; + renderer.render( group.start, group.count ); - 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; - uniforms.spotLightDecay.value = lights.spot.decays; + } else if ( object instanceof THREE.Points ) { - uniforms.hemisphereLightSkyColor.value = lights.hemi.skyColors; - uniforms.hemisphereLightGroundColor.value = lights.hemi.groundColors; - uniforms.hemisphereLightDirection.value = lights.hemi.positions; + renderer.setMode( _gl.POINTS ); + renderer.render( group.start, group.count ); - } + } - // If uniforms are marked as clean, they don't need to be loaded to the GPU. + }; - function markUniformsLightsNeedsUpdate ( uniforms, value ) { + function setupVertexAttributes( material, program, geometry, startIndex ) { - uniforms.ambientLightColor.needsUpdate = value; + var extension; - uniforms.directionalLightColor.needsUpdate = value; - uniforms.directionalLightDirection.needsUpdate = value; + if ( geometry instanceof THREE.InstancedBufferGeometry ) { - uniforms.pointLightColor.needsUpdate = value; - uniforms.pointLightPosition.needsUpdate = value; - uniforms.pointLightDistance.needsUpdate = value; - uniforms.pointLightDecay.needsUpdate = value; + extension = extensions.get( 'ANGLE_instanced_arrays' ); - uniforms.spotLightColor.needsUpdate = value; - uniforms.spotLightPosition.needsUpdate = value; - uniforms.spotLightDistance.needsUpdate = value; - uniforms.spotLightDirection.needsUpdate = value; - uniforms.spotLightAngleCos.needsUpdate = value; - uniforms.spotLightExponent.needsUpdate = value; - uniforms.spotLightDecay.needsUpdate = value; + if ( extension === null ) { - uniforms.hemisphereLightSkyColor.needsUpdate = value; - uniforms.hemisphereLightGroundColor.needsUpdate = value; - uniforms.hemisphereLightDirection.needsUpdate = value; + console.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); + return; + + } - } + } - function refreshUniformsShadow ( uniforms, lights ) { + if ( startIndex === undefined ) startIndex = 0; - if ( uniforms.shadowMatrix ) { + state.initAttributes(); - var j = 0; + var geometryAttributes = geometry.attributes; - for ( var i = 0, il = lights.length; i < il; i ++ ) { + var programAttributes = program.getAttributes(); - var light = lights[ i ]; + var materialDefaultAttributeValues = material.defaultAttributeValues; - if ( ! light.castShadow ) continue; + for ( var name in programAttributes ) { - if ( light instanceof THREE.SpotLight || ( light instanceof THREE.DirectionalLight ) ) { + var programAttribute = programAttributes[ name ]; - uniforms.shadowMap.value[ j ] = light.shadowMap; - uniforms.shadowMapSize.value[ j ] = light.shadowMapSize; + if ( programAttribute >= 0 ) { - uniforms.shadowMatrix.value[ j ] = light.shadowMatrix; + var geometryAttribute = geometryAttributes[ name ]; - uniforms.shadowDarkness.value[ j ] = light.shadowDarkness; - uniforms.shadowBias.value[ j ] = light.shadowBias; + if ( geometryAttribute !== undefined ) { - j ++; + state.enableAttribute( programAttribute ); - } + var size = geometryAttribute.itemSize; + var buffer = objects.getAttributeBuffer( geometryAttribute ); - } + if ( geometryAttribute instanceof THREE.InterleavedBufferAttribute ) { - } + var data = geometryAttribute.data; + var stride = data.stride; + var offset = geometryAttribute.offset; - } + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); + _gl.vertexAttribPointer( programAttribute, size, _gl.FLOAT, false, stride * data.array.BYTES_PER_ELEMENT, ( startIndex * stride + offset ) * data.array.BYTES_PER_ELEMENT ); - // Uniforms (load to GPU) + if ( data instanceof THREE.InstancedInterleavedBuffer ) { - function loadUniformsMatrices ( uniforms, object ) { + if ( extension === null ) { - _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, object.modelViewMatrix.elements ); + console.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferAttribute but hardware does not support extension ANGLE_instanced_arrays.' ); + return; - if ( uniforms.normalMatrix ) { + } - _gl.uniformMatrix3fv( uniforms.normalMatrix, false, object.normalMatrix.elements ); + extension.vertexAttribDivisorANGLE( programAttribute, data.meshPerAttribute ); - } + if ( geometry.maxInstancedCount === undefined ) { - } + geometry.maxInstancedCount = data.meshPerAttribute * data.count; - function getTextureUnit() { + } - var textureUnit = _usedTextureUnits; + } - if ( textureUnit >= capabilities.maxTextures ) { + } else { - console.warn( 'WebGLRenderer: trying to use ' + textureUnit + ' texture units while this GPU supports only ' + capabilities.maxTextures ); + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); + _gl.vertexAttribPointer( programAttribute, size, _gl.FLOAT, false, 0, startIndex * size * 4 ); // 4 bytes per Float32 - } + if ( geometryAttribute instanceof THREE.InstancedBufferAttribute ) { - _usedTextureUnits += 1; + if ( extension === null ) { - return textureUnit; + console.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferAttribute but hardware does not support extension ANGLE_instanced_arrays.' ); + return; - } + } - function loadUniformsGeneric ( uniforms ) { + extension.vertexAttribDivisorANGLE( programAttribute, geometryAttribute.meshPerAttribute ); - var texture, textureUnit; + if ( geometry.maxInstancedCount === undefined ) { - for ( var j = 0, jl = uniforms.length; j < jl; j ++ ) { + geometry.maxInstancedCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; - var uniform = uniforms[ j ][ 0 ]; + } - // needsUpdate property is not added to all uniforms. - if ( uniform.needsUpdate === false ) continue; + } - var type = uniform.type; - var value = uniform.value; - var location = uniforms[ j ][ 1 ]; + } - switch ( type ) { + } else if ( materialDefaultAttributeValues !== undefined ) { - case '1i': - _gl.uniform1i( location, value ); - break; + var value = materialDefaultAttributeValues[ name ]; - case '1f': - _gl.uniform1f( location, value ); - break; + if ( value !== undefined ) { - case '2f': - _gl.uniform2f( location, value[ 0 ], value[ 1 ] ); - break; + switch ( value.length ) { - case '3f': - _gl.uniform3f( location, value[ 0 ], value[ 1 ], value[ 2 ] ); - break; + case 2: + _gl.vertexAttrib2fv( programAttribute, value ); + break; - case '4f': - _gl.uniform4f( location, value[ 0 ], value[ 1 ], value[ 2 ], value[ 3 ] ); - break; + case 3: + _gl.vertexAttrib3fv( programAttribute, value ); + break; - case '1iv': - _gl.uniform1iv( location, value ); - break; + case 4: + _gl.vertexAttrib4fv( programAttribute, value ); + break; - case '3iv': - _gl.uniform3iv( location, value ); - break; + default: + _gl.vertexAttrib1fv( programAttribute, value ); - 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; + } - case 'Matrix3fv': - _gl.uniformMatrix3fv( location, false, value ); - break; + } - case 'Matrix4fv': - _gl.uniformMatrix4fv( location, false, value ); - break; + state.disableUnusedAttributes(); - // + } - case 'i': + // Sorting - // single integer - _gl.uniform1i( location, value ); + function numericalSort ( a, b ) { - break; + return b[ 0 ] - a[ 0 ]; - case 'f': + } - // single float - _gl.uniform1f( location, value ); + function painterSortStable ( a, b ) { - break; + if ( a.object.renderOrder !== b.object.renderOrder ) { - case 'v2': + return a.object.renderOrder - b.object.renderOrder; - // single THREE.Vector2 - _gl.uniform2f( location, value.x, value.y ); + } else if ( a.material.id !== b.material.id ) { - break; + return a.material.id - b.material.id; - case 'v3': + } else if ( a.z !== b.z ) { - // single THREE.Vector3 - _gl.uniform3f( location, value.x, value.y, value.z ); + return a.z - b.z; - break; + } else { - case 'v4': + return a.id - b.id; - // single THREE.Vector4 - _gl.uniform4f( location, value.x, value.y, value.z, value.w ); + } - break; + } - case 'c': + function reversePainterSortStable ( a, b ) { - // single THREE.Color - _gl.uniform3f( location, value.r, value.g, value.b ); + if ( a.object.renderOrder !== b.object.renderOrder ) { - break; + return a.object.renderOrder - b.object.renderOrder; - case 'iv1': + } if ( a.z !== b.z ) { - // flat array of integers (JS or typed array) - _gl.uniform1iv( location, value ); + return b.z - a.z; - break; + } else { - case 'iv': + return a.id - b.id; - // flat array of integers with 3 x N size (JS or typed array) - _gl.uniform3iv( location, value ); + } - break; + } - case 'fv1': + // Rendering - // flat array of floats (JS or typed array) - _gl.uniform1fv( location, value ); + this.render = function ( scene, camera, renderTarget, forceClear ) { - break; + if ( camera instanceof THREE.Camera === false ) { - case 'fv': + console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); + return; - // flat array of floats with 3 x N size (JS or typed array) - _gl.uniform3fv( location, value ); + } - break; + var fog = scene.fog; - case 'v2v': + // reset caching for this frame - // array of THREE.Vector2 + _currentGeometryProgram = ''; + _currentMaterialId = - 1; + _currentCamera = null; + _lightsNeedUpdate = true; - if ( uniform._array === undefined ) { + // update scene graph - uniform._array = new Float32Array( 2 * value.length ); + if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); - } + // update camera matrices and frustum - for ( var i = 0, i2 = 0, il = value.length; i < il; i ++, i2 += 2 ) { + if ( camera.parent === null ) camera.updateMatrixWorld(); - uniform._array[ i2 + 0 ] = value[ i ].x; - uniform._array[ i2 + 1 ] = value[ i ].y; + camera.matrixWorldInverse.getInverse( camera.matrixWorld ); - } + _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); + _frustum.setFromMatrix( _projScreenMatrix ); - _gl.uniform2fv( location, uniform._array ); + lights.length = 0; - break; + opaqueObjectsLastIndex = -1; + transparentObjectsLastIndex = -1; - case 'v3v': + sprites.length = 0; + lensFlares.length = 0; - // array of THREE.Vector3 + projectObject( scene ); - if ( uniform._array === undefined ) { + opaqueObjects.length = opaqueObjectsLastIndex + 1; + transparentObjects.length = transparentObjectsLastIndex + 1; - uniform._array = new Float32Array( 3 * value.length ); + if ( _this.sortObjects === true ) { - } + opaqueObjects.sort( painterSortStable ); + transparentObjects.sort( reversePainterSortStable ); - for ( var i = 0, i3 = 0, il = value.length; i < il; i ++, i3 += 3 ) { + } - uniform._array[ i3 + 0 ] = value[ i ].x; - uniform._array[ i3 + 1 ] = value[ i ].y; - uniform._array[ i3 + 2 ] = value[ i ].z; + // - } + shadowMap.render( scene ); - _gl.uniform3fv( location, uniform._array ); + // - break; + _infoRender.calls = 0; + _infoRender.vertices = 0; + _infoRender.faces = 0; + _infoRender.points = 0; - case 'v4v': + this.setRenderTarget( renderTarget ); - // array of THREE.Vector4 + if ( this.autoClear || forceClear ) { - if ( uniform._array === undefined ) { + this.clear( this.autoClearColor, this.autoClearDepth, this.autoClearStencil ); - uniform._array = new Float32Array( 4 * value.length ); + } - } + // - for ( var i = 0, i4 = 0, il = value.length; i < il; i ++, i4 += 4 ) { + if ( scene.overrideMaterial ) { - uniform._array[ i4 + 0 ] = value[ i ].x; - uniform._array[ i4 + 1 ] = value[ i ].y; - uniform._array[ i4 + 2 ] = value[ i ].z; - uniform._array[ i4 + 3 ] = value[ i ].w; + var overrideMaterial = scene.overrideMaterial; - } + renderObjects( opaqueObjects, camera, lights, fog, overrideMaterial ); + renderObjects( transparentObjects, camera, lights, fog, overrideMaterial ); - _gl.uniform4fv( location, uniform._array ); + } else { - break; + // opaque pass (front-to-back order) - case 'm3': + state.setBlending( THREE.NoBlending ); + renderObjects( opaqueObjects, camera, lights, fog ); - // single THREE.Matrix3 - _gl.uniformMatrix3fv( location, false, value.elements ); + // transparent pass (back-to-front order) - break; + renderObjects( transparentObjects, camera, lights, fog ); - case 'm3v': + } - // array of THREE.Matrix3 + // custom render plugins (post pass) - if ( uniform._array === undefined ) { + spritePlugin.render( scene, camera ); + lensFlarePlugin.render( scene, camera, _currentWidth, _currentHeight ); - uniform._array = new Float32Array( 9 * value.length ); + // Generate mipmap if we're using any kind of mipmap filtering - } + if ( renderTarget && renderTarget.generateMipmaps && renderTarget.minFilter !== THREE.NearestFilter && renderTarget.minFilter !== THREE.LinearFilter ) { - for ( var i = 0, il = value.length; i < il; i ++ ) { + updateRenderTargetMipmap( renderTarget ); - value[ i ].flattenToArrayOffset( uniform._array, i * 9 ); + } - } + // Ensure depth buffer writing is enabled so it can be cleared on next render - _gl.uniformMatrix3fv( location, false, uniform._array ); + state.setDepthTest( true ); + state.setDepthWrite( true ); + state.setColorWrite( true ); - break; + // _gl.finish(); - case 'm4': + }; - // single THREE.Matrix4 - _gl.uniformMatrix4fv( location, false, value.elements ); + function pushRenderItem( object, geometry, material, z, group ) { - break; + var array, index; - case 'm4v': + // allocate the next position in the appropriate array - // array of THREE.Matrix4 + if ( material.transparent ) { - if ( uniform._array === undefined ) { + array = transparentObjects; + index = ++ transparentObjectsLastIndex; - uniform._array = new Float32Array( 16 * value.length ); + } else { - } + array = opaqueObjects; + index = ++ opaqueObjectsLastIndex; - for ( var i = 0, il = value.length; i < il; i ++ ) { + } - value[ i ].flattenToArrayOffset( uniform._array, i * 16 ); + // recycle existing render item or grow the array - } + var renderItem = array[ index ]; - _gl.uniformMatrix4fv( location, false, uniform._array ); + if ( renderItem !== undefined ) { - break; + renderItem.id = object.id; + renderItem.object = object; + renderItem.geometry = geometry; + renderItem.material = material; + renderItem.z = _vector3.z; + renderItem.group = group; - case 't': + } else { - // single THREE.Texture (2d or cube) + renderItem = { + id: object.id, + object: object, + geometry: geometry, + material: material, + z: _vector3.z, + group: group + }; - texture = value; - textureUnit = getTextureUnit(); + // assert( index === array.length ); + array.push( renderItem ); - _gl.uniform1i( location, textureUnit ); + } - if ( ! texture ) continue; + } - if ( texture instanceof THREE.CubeTexture || - ( Array.isArray( texture.image ) && texture.image.length === 6 ) ) { + function projectObject( object ) { - // CompressedTexture can have Array in image :/ + if ( object.visible === false ) return; - setCubeTexture( texture, textureUnit ); + if ( object instanceof THREE.Light ) { - } else if ( texture instanceof THREE.WebGLRenderTargetCube ) { + lights.push( object ); - setCubeTextureDynamic( texture, textureUnit ); + } else if ( object instanceof THREE.Sprite ) { - } else { + sprites.push( object ); - _this.setTexture( texture, textureUnit ); + } else if ( object instanceof THREE.LensFlare ) { - } + lensFlares.push( object ); - break; + } else if ( object instanceof THREE.ImmediateRenderObject ) { - case 'tv': + if ( _this.sortObjects === true ) { - // array of THREE.Texture (2d) + _vector3.setFromMatrixPosition( object.matrixWorld ); + _vector3.applyProjection( _projScreenMatrix ); - if ( uniform._array === undefined ) { + } - uniform._array = []; + pushRenderItem( object, null, object.material, _vector3.z, null ); - } + } else if ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Points ) { - for ( var i = 0, il = uniform.value.length; i < il; i ++ ) { + if ( object instanceof THREE.SkinnedMesh ) { - uniform._array[ i ] = getTextureUnit(); + object.skeleton.update(); - } + } - _gl.uniform1iv( location, uniform._array ); + if ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) { - for ( var i = 0, il = uniform.value.length; i < il; i ++ ) { + var material = object.material; - texture = uniform.value[ i ]; - textureUnit = uniform._array[ i ]; + if ( material.visible === true ) { - if ( ! texture ) continue; + if ( _this.sortObjects === true ) { - _this.setTexture( texture, textureUnit ); + _vector3.setFromMatrixPosition( object.matrixWorld ); + _vector3.applyProjection( _projScreenMatrix ); } - break; + var geometry = objects.update( object ); - default: + if ( material instanceof THREE.MeshFaceMaterial ) { - console.warn( 'THREE.WebGLRenderer: Unknown uniform type: ' + type ); + var groups = geometry.groups; + var materials = material.materials; - } + for ( var i = 0, l = groups.length; i < l; i ++ ) { - } + var group = groups[ i ]; + var groupMaterial = materials[ group.materialIndex ]; - } + if ( groupMaterial.visible === true ) { - function setColorLinear( array, offset, color, intensity ) { + pushRenderItem( object, geometry, groupMaterial, _vector3.z, group ); - array[ offset + 0 ] = color.r * intensity; - array[ offset + 1 ] = color.g * intensity; - array[ offset + 2 ] = color.b * intensity; + } - } + } - function setupLights ( lights, camera ) { + } else { - var l, ll, light, - r = 0, g = 0, b = 0, - color, skyColor, groundColor, - intensity, - distance, + pushRenderItem( object, geometry, material, _vector3.z, null ); - zlights = _lights, + } - viewMatrix = camera.matrixWorldInverse, + } - dirColors = zlights.directional.colors, - dirPositions = zlights.directional.positions, + } - pointColors = zlights.point.colors, - pointPositions = zlights.point.positions, - pointDistances = zlights.point.distances, - pointDecays = zlights.point.decays, + } - spotColors = zlights.spot.colors, - spotPositions = zlights.spot.positions, - spotDistances = zlights.spot.distances, - spotDirections = zlights.spot.directions, - spotAnglesCos = zlights.spot.anglesCos, - spotExponents = zlights.spot.exponents, - spotDecays = zlights.spot.decays, + var children = object.children; - hemiSkyColors = zlights.hemi.skyColors, - hemiGroundColors = zlights.hemi.groundColors, - hemiPositions = zlights.hemi.positions, + for ( var i = 0, l = children.length; i < l; i ++ ) { - dirLength = 0, - pointLength = 0, - spotLength = 0, - hemiLength = 0, + projectObject( children[ i ] ); - dirCount = 0, - pointCount = 0, - spotCount = 0, - hemiCount = 0, + } - dirOffset = 0, - pointOffset = 0, - spotOffset = 0, - hemiOffset = 0; + } - for ( l = 0, ll = lights.length; l < ll; l ++ ) { + function renderObjects( renderList, camera, lights, fog, overrideMaterial ) { - light = lights[ l ]; + for ( var i = 0, l = renderList.length; i < l; i ++ ) { + + var renderItem = renderList[ i ]; + + var object = renderItem.object; + var geometry = renderItem.geometry; + var material = overrideMaterial === undefined ? renderItem.material : overrideMaterial; + var group = renderItem.group; + + object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); - if ( light.onlyShadow ) continue; + if ( object instanceof THREE.ImmediateRenderObject ) { - color = light.color; - intensity = light.intensity; - distance = light.distance; + setMaterial( material ); - if ( light instanceof THREE.AmbientLight ) { + var program = setProgram( camera, lights, fog, material, object ); - if ( ! light.visible ) continue; + _currentGeometryProgram = ''; - r += color.r; - g += color.g; - b += color.b; + object.render( function ( object ) { - } else if ( light instanceof THREE.DirectionalLight ) { + _this.renderBufferImmediate( object, program, material ); - dirCount += 1; + } ); - if ( ! light.visible ) continue; + } else { - _direction.setFromMatrixPosition( light.matrixWorld ); - _vector3.setFromMatrixPosition( light.target.matrixWorld ); - _direction.sub( _vector3 ); - _direction.transformDirection( viewMatrix ); + _this.renderBufferDirect( camera, lights, fog, geometry, material, object, group ); - dirOffset = dirLength * 3; + } - dirPositions[ dirOffset + 0 ] = _direction.x; - dirPositions[ dirOffset + 1 ] = _direction.y; - dirPositions[ dirOffset + 2 ] = _direction.z; + } - setColorLinear( dirColors, dirOffset, color, intensity ); + } - dirLength += 1; + function initMaterial( material, lights, fog, object ) { - } else if ( light instanceof THREE.PointLight ) { + var materialProperties = properties.get( material ); - pointCount += 1; + var parameters = programCache.getParameters( material, lights, fog, object ); + var code = programCache.getProgramCode( material, parameters ); - if ( ! light.visible ) continue; + var program = materialProperties.program; + var programChange = true; - pointOffset = pointLength * 3; + if ( program === undefined ) { - setColorLinear( pointColors, pointOffset, color, intensity ); + // new material + material.addEventListener( 'dispose', onMaterialDispose ); - _vector3.setFromMatrixPosition( light.matrixWorld ); - _vector3.applyMatrix4( viewMatrix ); + } else if ( program.code !== code ) { - pointPositions[ pointOffset + 0 ] = _vector3.x; - pointPositions[ pointOffset + 1 ] = _vector3.y; - pointPositions[ pointOffset + 2 ] = _vector3.z; + // changed glsl or parameters + releaseMaterialProgramReference( material ); - // distance is 0 if decay is 0, because there is no attenuation at all. - pointDistances[ pointLength ] = distance; - pointDecays[ pointLength ] = ( light.distance === 0 ) ? 0.0 : light.decay; + } else if ( parameters.shaderID !== undefined ) { - pointLength += 1; + // same glsl and uniform list + return; - } else if ( light instanceof THREE.SpotLight ) { + } else { - spotCount += 1; + // only rebuild uniform list + programChange = false; - if ( ! light.visible ) continue; + } - spotOffset = spotLength * 3; + if ( programChange ) { - setColorLinear( spotColors, spotOffset, color, intensity ); + if ( parameters.shaderID ) { - _direction.setFromMatrixPosition( light.matrixWorld ); - _vector3.copy( _direction ).applyMatrix4( viewMatrix ); + var shader = THREE.ShaderLib[ parameters.shaderID ]; - spotPositions[ spotOffset + 0 ] = _vector3.x; - spotPositions[ spotOffset + 1 ] = _vector3.y; - spotPositions[ spotOffset + 2 ] = _vector3.z; + materialProperties.__webglShader = { + name: material.type, + uniforms: THREE.UniformsUtils.clone( shader.uniforms ), + vertexShader: shader.vertexShader, + fragmentShader: shader.fragmentShader + }; - spotDistances[ spotLength ] = distance; + } else { - _vector3.setFromMatrixPosition( light.target.matrixWorld ); - _direction.sub( _vector3 ); - _direction.transformDirection( viewMatrix ); + materialProperties.__webglShader = { + name: material.type, + uniforms: material.uniforms, + vertexShader: material.vertexShader, + fragmentShader: material.fragmentShader + }; - spotDirections[ spotOffset + 0 ] = _direction.x; - spotDirections[ spotOffset + 1 ] = _direction.y; - spotDirections[ spotOffset + 2 ] = _direction.z; + } - spotAnglesCos[ spotLength ] = Math.cos( light.angle ); - spotExponents[ spotLength ] = light.exponent; - spotDecays[ spotLength ] = ( light.distance === 0 ) ? 0.0 : light.decay; + material.__webglShader = materialProperties.__webglShader; - spotLength += 1; + program = programCache.acquireProgram( material, parameters, code ); - } else if ( light instanceof THREE.HemisphereLight ) { + materialProperties.program = program; + material.program = program; - hemiCount += 1; + } - if ( ! light.visible ) continue; + var attributes = program.getAttributes(); - _direction.setFromMatrixPosition( light.matrixWorld ); - _direction.transformDirection( viewMatrix ); + if ( material.morphTargets ) { - hemiOffset = hemiLength * 3; + material.numSupportedMorphTargets = 0; - hemiPositions[ hemiOffset + 0 ] = _direction.x; - hemiPositions[ hemiOffset + 1 ] = _direction.y; - hemiPositions[ hemiOffset + 2 ] = _direction.z; + for ( var i = 0; i < _this.maxMorphTargets; i ++ ) { - skyColor = light.color; - groundColor = light.groundColor; + if ( attributes[ 'morphTarget' + i ] >= 0 ) { - setColorLinear( hemiSkyColors, hemiOffset, skyColor, intensity ); - setColorLinear( hemiGroundColors, hemiOffset, groundColor, intensity ); + material.numSupportedMorphTargets ++; - hemiLength += 1; + } } } - // null eventual remains from removed lights - // (this is to avoid if in shader) + if ( material.morphNormals ) { - 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; + material.numSupportedMorphNormals = 0; - zlights.directional.length = dirLength; - zlights.point.length = pointLength; - zlights.spot.length = spotLength; - zlights.hemi.length = hemiLength; + for ( i = 0; i < _this.maxMorphNormals; i ++ ) { - zlights.ambient[ 0 ] = r; - zlights.ambient[ 1 ] = g; - zlights.ambient[ 2 ] = b; + if ( attributes[ 'morphNormal' + i ] >= 0 ) { - } + material.numSupportedMorphNormals ++; - // GL state setting + } - this.setFaceCulling = function ( cullFace, frontFaceDirection ) { + } - if ( cullFace === THREE.CullFaceNone ) { + } - state.disable( _gl.CULL_FACE ); + materialProperties.uniformsList = []; - } else { + var uniformLocations = materialProperties.program.getUniforms(); - if ( frontFaceDirection === THREE.FrontFaceDirectionCW ) { + for ( var u in materialProperties.__webglShader.uniforms ) { - _gl.frontFace( _gl.CW ); + var location = uniformLocations[ u ]; - } else { + if ( location ) { - _gl.frontFace( _gl.CCW ); + materialProperties.uniformsList.push( [ materialProperties.__webglShader.uniforms[ u ], location ] ); } - if ( cullFace === THREE.CullFaceBack ) { + } - _gl.cullFace( _gl.BACK ); + } - } else if ( cullFace === THREE.CullFaceFront ) { + function setMaterial( material ) { - _gl.cullFace( _gl.FRONT ); + setMaterialFaces( material ); - } else { + if ( material.transparent === true ) { - _gl.cullFace( _gl.FRONT_AND_BACK ); + state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha ); - } + } else { - state.enable( _gl.CULL_FACE ); + state.setBlending( THREE.NoBlending ); } - }; + state.setDepthFunc( material.depthFunc ); + state.setDepthTest( material.depthTest ); + state.setDepthWrite( material.depthWrite ); + state.setColorWrite( material.colorWrite ); + state.setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); - // Textures + } - function setTextureParameters ( textureType, texture, isImagePowerOfTwo ) { + function setMaterialFaces( material ) { - var extension; + material.side !== THREE.DoubleSide ? state.enable( _gl.CULL_FACE ) : state.disable( _gl.CULL_FACE ); + state.setFlipSided( material.side === THREE.BackSide ); - if ( isImagePowerOfTwo ) { + } - _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, paramThreeToGL( texture.wrapS ) ); - _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, paramThreeToGL( texture.wrapT ) ); + function setProgram( camera, lights, fog, material, object ) { - _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, paramThreeToGL( texture.magFilter ) ); - _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, paramThreeToGL( texture.minFilter ) ); + _usedTextureUnits = 0; - } else { + var materialProperties = properties.get( material ); - _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); - _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); + if ( material.needsUpdate || ! materialProperties.program ) { - if ( texture.wrapS !== THREE.ClampToEdgeWrapping || texture.wrapT !== THREE.ClampToEdgeWrapping ) { + initMaterial( material, lights, fog, object ); + material.needsUpdate = false; - console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping. ( ' + texture.sourceFile + ' )' ); + } - } + var refreshProgram = false; + var refreshMaterial = false; + var refreshLights = false; - _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) ); - _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) ); + var program = materialProperties.program, + p_uniforms = program.getUniforms(), + m_uniforms = materialProperties.__webglShader.uniforms; - if ( texture.minFilter !== THREE.NearestFilter && texture.minFilter !== THREE.LinearFilter ) { + if ( program.id !== _currentProgram ) { - console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter. ( ' + texture.sourceFile + ' )' ); + _gl.useProgram( program.program ); + _currentProgram = program.id; - } + refreshProgram = true; + refreshMaterial = true; + refreshLights = true; } - extension = extensions.get( 'EXT_texture_filter_anisotropic' ); - - if ( extension ) { - - if ( texture.type === THREE.FloatType && extensions.get( 'OES_texture_float_linear' ) === null ) return; - if ( texture.type === THREE.HalfFloatType && extensions.get( 'OES_texture_half_float_linear' ) === null ) return; - - if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) { + if ( material.id !== _currentMaterialId ) { - _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, _this.getMaxAnisotropy() ) ); - properties.get( texture ).__currentAnisotropy = texture.anisotropy; + if ( _currentMaterialId === - 1 ) refreshLights = true; + _currentMaterialId = material.id; - } + refreshMaterial = true; } - } - - function uploadTexture( textureProperties, texture, slot ) { + if ( refreshProgram || camera !== _currentCamera ) { - if ( textureProperties.__webglInit === undefined ) { + _gl.uniformMatrix4fv( p_uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); - textureProperties.__webglInit = true; + if ( capabilities.logarithmicDepthBuffer ) { - texture.__webglInit = true; + _gl.uniform1f( p_uniforms.logDepthBufFC, 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); - texture.addEventListener( 'dispose', onTextureDispose ); + } - textureProperties.__webglTexture = _gl.createTexture(); - _infoMemory.textures ++; + if ( camera !== _currentCamera ) _currentCamera = camera; - } + // load material specific uniforms + // (shader material also gets them for the sake of genericity) - state.activeTexture( _gl.TEXTURE0 + slot ); - state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); + if ( material instanceof THREE.ShaderMaterial || + material instanceof THREE.MeshPhongMaterial || + material.envMap ) { - _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 ); + if ( p_uniforms.cameraPosition !== undefined ) { - texture.image = clampToMaxSize( texture.image, capabilities.maxTextureSize ); + _vector3.setFromMatrixPosition( camera.matrixWorld ); + _gl.uniform3f( p_uniforms.cameraPosition, _vector3.x, _vector3.y, _vector3.z ); - var image = texture.image, - isImagePowerOfTwo = THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height ), - glFormat = paramThreeToGL( texture.format ), - glType = paramThreeToGL( texture.type ); + } - setTextureParameters( _gl.TEXTURE_2D, texture, isImagePowerOfTwo ); + } - var mipmap, mipmaps = texture.mipmaps; + if ( material instanceof THREE.MeshPhongMaterial || + material instanceof THREE.MeshLambertMaterial || + material instanceof THREE.MeshBasicMaterial || + material instanceof THREE.ShaderMaterial || + material.skinning ) { - if ( texture instanceof THREE.DataTexture ) { + if ( p_uniforms.viewMatrix !== undefined ) { - // 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 + _gl.uniformMatrix4fv( p_uniforms.viewMatrix, false, camera.matrixWorldInverse.elements ); - if ( mipmaps.length > 0 && isImagePowerOfTwo ) { + } - for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { + } - mipmap = mipmaps[ i ]; - state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + } - } + // 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 - texture.generateMipmaps = false; + if ( material.skinning ) { - } else { + if ( object.bindMatrix && p_uniforms.bindMatrix !== undefined ) { - state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data ); + _gl.uniformMatrix4fv( p_uniforms.bindMatrix, false, object.bindMatrix.elements ); } - } else if ( texture instanceof THREE.CompressedTexture ) { - - for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { + if ( object.bindMatrixInverse && p_uniforms.bindMatrixInverse !== undefined ) { - mipmap = mipmaps[ i ]; + _gl.uniformMatrix4fv( p_uniforms.bindMatrixInverse, false, object.bindMatrixInverse.elements ); - if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) { + } - if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) { + if ( capabilities.floatVertexTextures && object.skeleton && object.skeleton.useVertexTexture ) { - state.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + if ( p_uniforms.boneTexture !== undefined ) { - } else { + var textureUnit = getTextureUnit(); - console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()" ); + _gl.uniform1i( p_uniforms.boneTexture, textureUnit ); + _this.setTexture( object.skeleton.boneTexture, textureUnit ); - } + } - } else { + if ( p_uniforms.boneTextureWidth !== undefined ) { - state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + _gl.uniform1i( p_uniforms.boneTextureWidth, object.skeleton.boneTextureWidth ); } - } - - } else { + if ( p_uniforms.boneTextureHeight !== undefined ) { - // regular Texture (image, video, canvas) + _gl.uniform1i( p_uniforms.boneTextureHeight, object.skeleton.boneTextureHeight ); - // 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 ) { + } else if ( object.skeleton && object.skeleton.boneMatrices ) { - for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { + if ( p_uniforms.boneGlobalMatrices !== undefined ) { - mipmap = mipmaps[ i ]; - state.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap ); + _gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, object.skeleton.boneMatrices ); } - texture.generateMipmaps = false; - - } else { - - state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, texture.image ); - } } - if ( texture.generateMipmaps && isImagePowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); + if ( refreshMaterial ) { - textureProperties.__version = texture.version; + // refresh uniforms common to several materials - if ( texture.onUpdate ) texture.onUpdate( texture ); + if ( fog && material.fog ) { - } + refreshUniformsFog( m_uniforms, fog ); - this.setTexture = function ( texture, slot ) { + } - var textureProperties = properties.get( texture ); + if ( material instanceof THREE.MeshPhongMaterial || + material instanceof THREE.MeshLambertMaterial || + material.lights ) { - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { + if ( _lightsNeedUpdate ) { - var image = texture.image; + refreshLights = true; + setupLights( lights, camera ); + _lightsNeedUpdate = false; - if ( image === undefined ) { + } - console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is undefined', texture ); - return; + if ( refreshLights ) { - } + refreshUniformsLights( m_uniforms, _lights ); + markUniformsLightsNeedsUpdate( m_uniforms, true ); - if ( image.complete === false ) { + } else { - console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete', texture ); - return; + markUniformsLightsNeedsUpdate( m_uniforms, false ); + + } } - uploadTexture( textureProperties, texture, slot ); - return; + if ( material instanceof THREE.MeshBasicMaterial || + material instanceof THREE.MeshLambertMaterial || + material instanceof THREE.MeshPhongMaterial ) { - } + refreshUniformsCommon( m_uniforms, material ); - state.activeTexture( _gl.TEXTURE0 + slot ); - state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); + } - }; + // refresh single material specific uniforms - function clampToMaxSize ( image, maxSize ) { + if ( material instanceof THREE.LineBasicMaterial ) { - if ( image.width > maxSize || image.height > maxSize ) { + refreshUniformsLine( m_uniforms, material ); - // Warning: Scaling through the canvas will only work with images that use - // premultiplied alpha. + } else if ( material instanceof THREE.LineDashedMaterial ) { - var scale = maxSize / Math.max( image.width, image.height ); + refreshUniformsLine( m_uniforms, material ); + refreshUniformsDash( m_uniforms, material ); - var canvas = document.createElement( 'canvas' ); - canvas.width = Math.floor( image.width * scale ); - canvas.height = Math.floor( image.height * scale ); + } else if ( material instanceof THREE.PointsMaterial ) { - var context = canvas.getContext( '2d' ); - context.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height ); + refreshUniformsParticle( m_uniforms, material ); - console.warn( 'THREE.WebGLRenderer: image is too big (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height, image ); + } else if ( material instanceof THREE.MeshPhongMaterial ) { - return canvas; + refreshUniformsPhong( m_uniforms, material ); - } + } else if ( material instanceof THREE.MeshDepthMaterial ) { - return image; + m_uniforms.mNear.value = camera.near; + m_uniforms.mFar.value = camera.far; + m_uniforms.opacity.value = material.opacity; - } + } else if ( material instanceof THREE.MeshNormalMaterial ) { - function setCubeTexture ( texture, slot ) { + m_uniforms.opacity.value = material.opacity; - var textureProperties = properties.get( texture ); + } - if ( texture.image.length === 6 ) { + if ( object.receiveShadow && ! material._shadowPass ) { - if ( texture.version > 0 && textureProperties.__version !== texture.version ) { + refreshUniformsShadow( m_uniforms, lights ); - if ( ! textureProperties.__image__webglTextureCube ) { + } - texture.addEventListener( 'dispose', onTextureDispose ); + // load common uniforms - textureProperties.__image__webglTextureCube = _gl.createTexture(); + loadUniformsGeneric( materialProperties.uniformsList ); - _infoMemory.textures ++; + } - } + loadUniformsMatrices( p_uniforms, object ); - state.activeTexture( _gl.TEXTURE0 + slot ); - state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube ); + if ( p_uniforms.modelMatrix !== undefined ) { - _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); + _gl.uniformMatrix4fv( p_uniforms.modelMatrix, false, object.matrixWorld.elements ); - var isCompressed = texture instanceof THREE.CompressedTexture; - var isDataTexture = texture.image[ 0 ] instanceof THREE.DataTexture; + } - var cubeImage = []; + return program; - for ( var i = 0; i < 6; i ++ ) { + } - if ( _this.autoScaleCubemaps && ! isCompressed && ! isDataTexture ) { + // Uniforms (refresh uniforms objects) - cubeImage[ i ] = clampToMaxSize( texture.image[ i ], capabilities.maxCubemapSize ); + function refreshUniformsCommon ( uniforms, material ) { - } else { + uniforms.opacity.value = material.opacity; - cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; + uniforms.diffuse.value = material.color; - } + if ( material.emissive ) { - } + uniforms.emissive.value = material.emissive; - var image = cubeImage[ 0 ], - isImagePowerOfTwo = THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height ), - glFormat = paramThreeToGL( texture.format ), - glType = paramThreeToGL( texture.type ); + } - setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isImagePowerOfTwo ); + uniforms.map.value = material.map; + uniforms.specularMap.value = material.specularMap; + uniforms.alphaMap.value = material.alphaMap; - for ( var i = 0; i < 6; i ++ ) { + if ( material.aoMap ) { - if ( ! isCompressed ) { + uniforms.aoMap.value = material.aoMap; + uniforms.aoMapIntensity.value = material.aoMapIntensity; - if ( isDataTexture ) { + } - state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); + // uv repeat and offset setting priorities + // 1. color map + // 2. specular map + // 3. normal map + // 4. bump map + // 5. alpha map + // 6. emissive map - } else { + var uvScaleMap; - state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] ); + if ( material.map ) { - } + uvScaleMap = material.map; - } else { + } else if ( material.specularMap ) { - var mipmap, mipmaps = cubeImage[ i ].mipmaps; + uvScaleMap = material.specularMap; - for ( var j = 0, jl = mipmaps.length; j < jl; j ++ ) { + } else if ( material.displacementMap ) { - mipmap = mipmaps[ j ]; + uvScaleMap = material.displacementMap; - if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) { + } else if ( material.normalMap ) { - if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) { + uvScaleMap = material.normalMap; - state.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + } else if ( material.bumpMap ) { - } else { + uvScaleMap = material.bumpMap; - console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setCubeTexture()" ); + } else if ( material.alphaMap ) { - } + uvScaleMap = material.alphaMap; - } else { + } else if ( material.emissiveMap ) { - state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + uvScaleMap = material.emissiveMap; - } + } - } + if ( uvScaleMap !== undefined ) { - } + var offset = uvScaleMap.offset; + var repeat = uvScaleMap.repeat; - } + uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y ); - if ( texture.generateMipmaps && isImagePowerOfTwo ) { + } - _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); + uniforms.envMap.value = material.envMap; + uniforms.flipEnvMap.value = ( material.envMap instanceof THREE.WebGLRenderTargetCube ) ? 1 : - 1; - } + uniforms.reflectivity.value = material.reflectivity; + uniforms.refractionRatio.value = material.refractionRatio; - textureProperties.__version = texture.version; + } - if ( texture.onUpdate ) texture.onUpdate( texture ); + function refreshUniformsLine ( uniforms, material ) { - } else { + uniforms.diffuse.value = material.color; + uniforms.opacity.value = material.opacity; - state.activeTexture( _gl.TEXTURE0 + slot ); - state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube ); + } - } + function refreshUniformsDash ( uniforms, material ) { - } + uniforms.dashSize.value = material.dashSize; + uniforms.totalSize.value = material.dashSize + material.gapSize; + uniforms.scale.value = material.scale; } - function setCubeTextureDynamic ( texture, slot ) { + function refreshUniformsParticle ( uniforms, material ) { - state.activeTexture( _gl.TEXTURE0 + slot ); - state.bindTexture( _gl.TEXTURE_CUBE_MAP, properties.get( texture ).__webglTexture ); + 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; - // Render targets + if ( material.map !== null ) { - function setupFrameBuffer ( framebuffer, renderTarget, textureTarget ) { + var offset = material.map.offset; + var repeat = material.map.repeat; - _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); - _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureTarget, properties.get( renderTarget ).__webglTexture, 0 ); + uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y ); + + } } - function setupRenderBuffer ( renderbuffer, renderTarget ) { + function refreshUniformsFog ( uniforms, fog ) { - _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer ); + uniforms.fogColor.value = fog.color; - if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { + if ( fog instanceof THREE.Fog ) { - _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height ); - _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); + uniforms.fogNear.value = fog.near; + uniforms.fogFar.value = fog.far; - /* For some reason this is not working. Defaulting to RGBA4. - } else if ( ! renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + } else if ( fog instanceof THREE.FogExp2 ) { - _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.STENCIL_INDEX8, renderTarget.width, renderTarget.height ); - _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); - */ + uniforms.fogDensity.value = fog.density; - } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + } - _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height ); - _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); + } - } else { + function refreshUniformsPhong ( uniforms, material ) { - _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height ); + uniforms.specular.value = material.specular; + uniforms.shininess.value = material.shininess; + + if ( material.lightMap ) { + + uniforms.lightMap.value = material.lightMap; + uniforms.lightMapIntensity.value = material.lightMapIntensity; } - } + if ( material.emissiveMap ) { - this.setRenderTarget = function ( renderTarget ) { + uniforms.emissiveMap.value = material.emissiveMap; - var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube ); + } - if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) { + if ( material.bumpMap ) { - var renderTargetProperties = properties.get( renderTarget ); + uniforms.bumpMap.value = material.bumpMap; + uniforms.bumpScale.value = material.bumpScale; - if ( renderTarget.depthBuffer === undefined ) renderTarget.depthBuffer = true; - if ( renderTarget.stencilBuffer === undefined ) renderTarget.stencilBuffer = true; + } - renderTarget.addEventListener( 'dispose', onRenderTargetDispose ); + if ( material.normalMap ) { - renderTargetProperties.__webglTexture = _gl.createTexture(); + uniforms.normalMap.value = material.normalMap; + uniforms.normalScale.value.copy( material.normalScale ); - _infoMemory.textures ++; + } - // Setup texture, create render and frame buffers + if ( material.displacementMap ) { - var isTargetPowerOfTwo = THREE.Math.isPowerOfTwo( renderTarget.width ) && THREE.Math.isPowerOfTwo( renderTarget.height ), - glFormat = paramThreeToGL( renderTarget.format ), - glType = paramThreeToGL( renderTarget.type ); + uniforms.displacementMap.value = material.displacementMap; + uniforms.displacementScale.value = material.displacementScale; + uniforms.displacementBias.value = material.displacementBias; - if ( isCube ) { + } - renderTargetProperties.__webglFramebuffer = []; - renderTargetProperties.__webglRenderbuffer = []; + } - state.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTargetProperties.__webglTexture ); + function refreshUniformsLights ( uniforms, lights ) { - setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget, isTargetPowerOfTwo ); + uniforms.ambientLightColor.value = lights.ambient; - for ( var i = 0; i < 6; i ++ ) { + uniforms.directionalLightColor.value = lights.directional.colors; + uniforms.directionalLightDirection.value = lights.directional.positions; - renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer(); - renderTargetProperties.__webglRenderbuffer[ i ] = _gl.createRenderbuffer(); + uniforms.pointLightColor.value = lights.point.colors; + uniforms.pointLightPosition.value = lights.point.positions; + uniforms.pointLightDistance.value = lights.point.distances; + uniforms.pointLightDecay.value = lights.point.decays; - state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); + 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; + uniforms.spotLightDecay.value = lights.spot.decays; - setupFrameBuffer( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i ); - setupRenderBuffer( renderTargetProperties.__webglRenderbuffer[ i ], renderTarget ); + uniforms.hemisphereLightSkyColor.value = lights.hemi.skyColors; + uniforms.hemisphereLightGroundColor.value = lights.hemi.groundColors; + uniforms.hemisphereLightDirection.value = lights.hemi.positions; - } + } - if ( renderTarget.generateMipmaps && isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); + // If uniforms are marked as clean, they don't need to be loaded to the GPU. - } else { + function markUniformsLightsNeedsUpdate ( uniforms, value ) { - renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); + uniforms.ambientLightColor.needsUpdate = value; - if ( renderTarget.shareDepthFrom ) { + uniforms.directionalLightColor.needsUpdate = value; + uniforms.directionalLightDirection.needsUpdate = value; - renderTargetProperties.__webglRenderbuffer = renderTarget.shareDepthFrom.__webglRenderbuffer; + uniforms.pointLightColor.needsUpdate = value; + uniforms.pointLightPosition.needsUpdate = value; + uniforms.pointLightDistance.needsUpdate = value; + uniforms.pointLightDecay.needsUpdate = value; - } else { + uniforms.spotLightColor.needsUpdate = value; + uniforms.spotLightPosition.needsUpdate = value; + uniforms.spotLightDistance.needsUpdate = value; + uniforms.spotLightDirection.needsUpdate = value; + uniforms.spotLightAngleCos.needsUpdate = value; + uniforms.spotLightExponent.needsUpdate = value; + uniforms.spotLightDecay.needsUpdate = value; - renderTargetProperties.__webglRenderbuffer = _gl.createRenderbuffer(); + uniforms.hemisphereLightSkyColor.needsUpdate = value; + uniforms.hemisphereLightGroundColor.needsUpdate = value; + uniforms.hemisphereLightDirection.needsUpdate = value; - } + } - state.bindTexture( _gl.TEXTURE_2D, renderTargetProperties.__webglTexture ); - setTextureParameters( _gl.TEXTURE_2D, renderTarget, isTargetPowerOfTwo ); + function refreshUniformsShadow ( uniforms, lights ) { - state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); + if ( uniforms.shadowMatrix ) { - setupFrameBuffer( renderTargetProperties.__webglFramebuffer, renderTarget, _gl.TEXTURE_2D ); + var j = 0; - if ( renderTarget.shareDepthFrom ) { + for ( var i = 0, il = lights.length; i < il; i ++ ) { - if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { + var light = lights[ i ]; - _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderTargetProperties.__webglRenderbuffer ); + if ( ! light.castShadow ) continue; - } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + if ( light instanceof THREE.SpotLight || ( light instanceof THREE.DirectionalLight ) ) { - _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderTargetProperties.__webglRenderbuffer ); + uniforms.shadowMap.value[ j ] = light.shadowMap; + uniforms.shadowMapSize.value[ j ] = light.shadowMapSize; - } + uniforms.shadowMatrix.value[ j ] = light.shadowMatrix; - } else { + uniforms.shadowDarkness.value[ j ] = light.shadowDarkness; + uniforms.shadowBias.value[ j ] = light.shadowBias; - setupRenderBuffer( renderTargetProperties.__webglRenderbuffer, renderTarget ); + j ++; } - if ( renderTarget.generateMipmaps && isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); - } - // Release everything + } - if ( isCube ) { + } - state.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); + // Uniforms (load to GPU) - } else { + function loadUniformsMatrices ( uniforms, object ) { - state.bindTexture( _gl.TEXTURE_2D, null ); + _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, object.modelViewMatrix.elements ); - } + if ( uniforms.normalMatrix ) { - _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); - _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); + _gl.uniformMatrix3fv( uniforms.normalMatrix, false, object.normalMatrix.elements ); } - var framebuffer, width, height, vx, vy; + } - if ( renderTarget ) { + function getTextureUnit() { - var renderTargetProperties = properties.get( renderTarget ); + var textureUnit = _usedTextureUnits; - if ( isCube ) { + if ( textureUnit >= capabilities.maxTextures ) { - framebuffer = renderTargetProperties.__webglFramebuffer[ renderTarget.activeCubeFace ]; + console.warn( 'WebGLRenderer: trying to use ' + textureUnit + ' texture units while this GPU supports only ' + capabilities.maxTextures ); - } else { + } - framebuffer = renderTargetProperties.__webglFramebuffer; + _usedTextureUnits += 1; - } + return textureUnit; - width = renderTarget.width; - height = renderTarget.height; + } - vx = 0; - vy = 0; + function loadUniformsGeneric ( uniforms ) { - } else { + var texture, textureUnit; - framebuffer = null; + for ( var j = 0, jl = uniforms.length; j < jl; j ++ ) { - width = _viewportWidth; - height = _viewportHeight; + var uniform = uniforms[ j ][ 0 ]; - vx = _viewportX; - vy = _viewportY; + // needsUpdate property is not added to all uniforms. + if ( uniform.needsUpdate === false ) continue; - } + var type = uniform.type; + var value = uniform.value; + var location = uniforms[ j ][ 1 ]; - if ( framebuffer !== _currentFramebuffer ) { + switch ( type ) { - _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); - _gl.viewport( vx, vy, width, height ); + case '1i': + _gl.uniform1i( location, value ); + break; - _currentFramebuffer = framebuffer; + case '1f': + _gl.uniform1f( location, value ); + break; - } + case '2f': + _gl.uniform2f( location, value[ 0 ], value[ 1 ] ); + break; - _currentWidth = width; - _currentHeight = height; + case '3f': + _gl.uniform3f( location, value[ 0 ], value[ 1 ], value[ 2 ] ); + break; - }; + case '4f': + _gl.uniform4f( location, value[ 0 ], value[ 1 ], value[ 2 ], value[ 3 ] ); + break; - this.readRenderTargetPixels = function( renderTarget, x, y, width, height, buffer ) { + case '1iv': + _gl.uniform1iv( location, value ); + break; - if ( ! ( renderTarget instanceof THREE.WebGLRenderTarget ) ) { + case '3iv': + _gl.uniform3iv( location, value ); + break; - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); - return; + case '1fv': + _gl.uniform1fv( location, value ); + break; - } + case '2fv': + _gl.uniform2fv( location, value ); + break; - if ( properties.get( renderTarget ).__webglFramebuffer ) { + case '3fv': + _gl.uniform3fv( location, value ); + break; - if ( renderTarget.format !== THREE.RGBAFormat ) { + case '4fv': + _gl.uniform4fv( location, value ); + break; - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA format. readPixels can read only RGBA format.' ); - return; + case 'Matrix3fv': + _gl.uniformMatrix3fv( location, false, value ); + break; - } + case 'Matrix4fv': + _gl.uniformMatrix4fv( location, false, value ); + break; - var restore = false; + // - if ( properties.get( renderTarget ).__webglFramebuffer !== _currentFramebuffer ) { + case 'i': - _gl.bindFramebuffer( _gl.FRAMEBUFFER, properties.get( renderTarget ).__webglFramebuffer ); + // single integer + _gl.uniform1i( location, value ); - restore = true; + break; - } + case 'f': - if ( _gl.checkFramebufferStatus( _gl.FRAMEBUFFER ) === _gl.FRAMEBUFFER_COMPLETE ) { + // single float + _gl.uniform1f( location, value ); - _gl.readPixels( x, y, width, height, _gl.RGBA, _gl.UNSIGNED_BYTE, buffer ); + break; - } else { + case 'v2': - console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' ); + // single THREE.Vector2 + _gl.uniform2f( location, value.x, value.y ); - } + break; - if ( restore ) { + case 'v3': - _gl.bindFramebuffer( _gl.FRAMEBUFFER, _currentFramebuffer ); + // single THREE.Vector3 + _gl.uniform3f( location, value.x, value.y, value.z ); - } + break; - } + case 'v4': - }; + // single THREE.Vector4 + _gl.uniform4f( location, value.x, value.y, value.z, value.w ); - function updateRenderTargetMipmap ( renderTarget ) { + break; - if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) { + case 'c': - state.bindTexture( _gl.TEXTURE_CUBE_MAP, properties.get( renderTarget ).__webglTexture ); - _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); - state.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); + // single THREE.Color + _gl.uniform3f( location, value.r, value.g, value.b ); - } else { + break; - state.bindTexture( _gl.TEXTURE_2D, properties.get( renderTarget ).__webglTexture ); - _gl.generateMipmap( _gl.TEXTURE_2D ); - state.bindTexture( _gl.TEXTURE_2D, null ); + case 'iv1': - } + // flat array of integers (JS or typed array) + _gl.uniform1iv( location, value ); - } + break; - // Fallback filters for non-power-of-2 textures + case 'iv': - function filterFallback ( f ) { + // flat array of integers with 3 x N size (JS or typed array) + _gl.uniform3iv( location, value ); - if ( f === THREE.NearestFilter || f === THREE.NearestMipMapNearestFilter || f === THREE.NearestMipMapLinearFilter ) { + break; - return _gl.NEAREST; + case 'fv1': - } + // flat array of floats (JS or typed array) + _gl.uniform1fv( location, value ); - return _gl.LINEAR; + break; - } + case 'fv': - // Map three.js constants to WebGL constants + // flat array of floats with 3 x N size (JS or typed array) + _gl.uniform3fv( location, value ); - function paramThreeToGL ( p ) { + break; - var extension; + case 'v2v': - if ( p === THREE.RepeatWrapping ) return _gl.REPEAT; - if ( p === THREE.ClampToEdgeWrapping ) return _gl.CLAMP_TO_EDGE; - if ( p === THREE.MirroredRepeatWrapping ) return _gl.MIRRORED_REPEAT; + // array of THREE.Vector2 - 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 ( uniform._array === undefined ) { - 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; + uniform._array = new Float32Array( 2 * value.length ); - 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; + for ( var i = 0, i2 = 0, il = value.length; i < il; i ++, i2 += 2 ) { - extension = extensions.get( 'OES_texture_half_float' ); + uniform._array[ i2 + 0 ] = value[ i ].x; + uniform._array[ i2 + 1 ] = value[ i ].y; - if ( extension !== null ) { + } - if ( p === THREE.HalfFloatType ) return extension.HALF_FLOAT_OES; + _gl.uniform2fv( location, uniform._array ); - } + break; - 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; + case 'v3v': - 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; + // array of THREE.Vector3 - 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; + if ( uniform._array === undefined ) { - 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; + uniform._array = new Float32Array( 3 * value.length ); - extension = extensions.get( 'WEBGL_compressed_texture_s3tc' ); + } - if ( extension !== null ) { + for ( var i = 0, i3 = 0, il = value.length; i < il; i ++, i3 += 3 ) { - 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; + uniform._array[ i3 + 0 ] = value[ i ].x; + uniform._array[ i3 + 1 ] = value[ i ].y; + uniform._array[ i3 + 2 ] = value[ i ].z; - } + } - extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' ); + _gl.uniform3fv( location, uniform._array ); - if ( extension !== null ) { + break; - 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; + case 'v4v': - } + // array of THREE.Vector4 - extension = extensions.get( 'EXT_blend_minmax' ); + if ( uniform._array === undefined ) { - if ( extension !== null ) { + uniform._array = new Float32Array( 4 * value.length ); - if ( p === THREE.MinEquation ) return extension.MIN_EXT; - if ( p === THREE.MaxEquation ) return extension.MAX_EXT; + } - } + for ( var i = 0, i4 = 0, il = value.length; i < il; i ++, i4 += 4 ) { - return 0; + uniform._array[ i4 + 0 ] = value[ i ].x; + uniform._array[ i4 + 1 ] = value[ i ].y; + uniform._array[ i4 + 2 ] = value[ i ].z; + uniform._array[ i4 + 3 ] = value[ i ].w; - } + } - // DEPRECATED + _gl.uniform4fv( location, uniform._array ); + + break; - this.supportsFloatTextures = function () { + case 'm3': - console.warn( 'THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( \'OES_texture_float\' ).' ); - return extensions.get( 'OES_texture_float' ); + // single THREE.Matrix3 + _gl.uniformMatrix3fv( location, false, value.elements ); - }; + break; - this.supportsHalfFloatTextures = function () { + case 'm3v': - console.warn( 'THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( \'OES_texture_half_float\' ).' ); - return extensions.get( 'OES_texture_half_float' ); + // array of THREE.Matrix3 - }; + if ( uniform._array === undefined ) { - this.supportsStandardDerivatives = function () { + uniform._array = new Float32Array( 9 * value.length ); - console.warn( 'THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( \'OES_standard_derivatives\' ).' ); - return extensions.get( 'OES_standard_derivatives' ); + } - }; + for ( var i = 0, il = value.length; i < il; i ++ ) { - this.supportsCompressedTextureS3TC = function () { + value[ i ].flattenToArrayOffset( uniform._array, i * 9 ); - console.warn( 'THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( \'WEBGL_compressed_texture_s3tc\' ).' ); - return extensions.get( 'WEBGL_compressed_texture_s3tc' ); + } - }; + _gl.uniformMatrix3fv( location, false, uniform._array ); - this.supportsCompressedTexturePVRTC = function () { + break; - console.warn( 'THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( \'WEBGL_compressed_texture_pvrtc\' ).' ); - return extensions.get( 'WEBGL_compressed_texture_pvrtc' ); + case 'm4': - }; + // single THREE.Matrix4 + _gl.uniformMatrix4fv( location, false, value.elements ); - this.supportsBlendMinMax = function () { + break; - console.warn( 'THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( \'EXT_blend_minmax\' ).' ); - return extensions.get( 'EXT_blend_minmax' ); + case 'm4v': - }; + // array of THREE.Matrix4 - this.supportsVertexTextures = function () { + if ( uniform._array === undefined ) { - return capabilities.vertexTextures; + uniform._array = new Float32Array( 16 * value.length ); - }; + } - this.supportsInstancedArrays = function () { + for ( var i = 0, il = value.length; i < il; i ++ ) { - console.warn( 'THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( \'ANGLE_instanced_arrays\' ).' ); - return extensions.get( 'ANGLE_instanced_arrays' ); + value[ i ].flattenToArrayOffset( uniform._array, i * 16 ); - }; + } - // + _gl.uniformMatrix4fv( location, false, uniform._array ); - this.initMaterial = function () { + break; - console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' ); + case 't': - }; + // single THREE.Texture (2d or cube) - this.addPrePlugin = function () { + texture = value; + textureUnit = getTextureUnit(); - console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' ); + _gl.uniform1i( location, textureUnit ); - }; + if ( ! texture ) continue; - this.addPostPlugin = function () { + if ( texture instanceof THREE.CubeTexture || + ( Array.isArray( texture.image ) && texture.image.length === 6 ) ) { - console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' ); + // CompressedTexture can have Array in image :/ - }; + setCubeTexture( texture, textureUnit ); - this.updateShadowMap = function () { + } else if ( texture instanceof THREE.WebGLRenderTargetCube ) { - console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' ); + setCubeTextureDynamic( texture, textureUnit ); - }; + } else { - Object.defineProperties( this, { - shadowMapEnabled: { - get: function () { + _this.setTexture( texture, textureUnit ); - return shadowMap.enabled; + } - }, - set: function ( value ) { + break; - console.warn( 'THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled.' ); - shadowMap.enabled = value; + case 'tv': - } - }, - shadowMapType: { - get: function () { + // array of THREE.Texture (2d) - return shadowMap.type; + if ( uniform._array === undefined ) { - }, - set: function ( value ) { + uniform._array = []; - console.warn( 'THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type.' ); - shadowMap.type = value; + } - } - }, - shadowMapCullFace: { - get: function () { + for ( var i = 0, il = uniform.value.length; i < il; i ++ ) { - return shadowMap.cullFace; + uniform._array[ i ] = getTextureUnit(); - }, - set: function ( value ) { + } - console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace is now .shadowMap.cullFace.' ); - shadowMap.cullFace = value; + _gl.uniform1iv( location, uniform._array ); - } - }, - shadowMapDebug: { - get: function () { + for ( var i = 0, il = uniform.value.length; i < il; i ++ ) { - return shadowMap.debug; + texture = uniform.value[ i ]; + textureUnit = uniform._array[ i ]; - }, - set: function ( value ) { + if ( ! texture ) continue; - console.warn( 'THREE.WebGLRenderer: .shadowMapDebug is now .shadowMap.debug.' ); - shadowMap.debug = value; + _this.setTexture( texture, textureUnit ); - } - } - } ); + } -}; + break; -// File:src/renderers/WebGLRenderTarget.js + default: -/** - * @author szimek / https://github.com/szimek/ - * @author alteredq / http://alteredqualia.com/ - */ + console.warn( 'THREE.WebGLRenderer: Unknown uniform type: ' + type ); -THREE.WebGLRenderTarget = function ( width, height, options ) { + } - this.uuid = THREE.Math.generateUUID(); + } - this.width = width; - this.height = height; + } - options = options || {}; + function setColorLinear( array, offset, color, intensity ) { - this.wrapS = options.wrapS !== undefined ? options.wrapS : THREE.ClampToEdgeWrapping; - this.wrapT = options.wrapT !== undefined ? options.wrapT : THREE.ClampToEdgeWrapping; + array[ offset + 0 ] = color.r * intensity; + array[ offset + 1 ] = color.g * intensity; + array[ offset + 2 ] = color.b * intensity; - this.magFilter = options.magFilter !== undefined ? options.magFilter : THREE.LinearFilter; - this.minFilter = options.minFilter !== undefined ? options.minFilter : THREE.LinearMipMapLinearFilter; + } - this.anisotropy = options.anisotropy !== undefined ? options.anisotropy : 1; + function setupLights ( lights, camera ) { - this.offset = new THREE.Vector2( 0, 0 ); - this.repeat = new THREE.Vector2( 1, 1 ); + var l, ll, light, + r = 0, g = 0, b = 0, + color, skyColor, groundColor, + intensity, + distance, - this.format = options.format !== undefined ? options.format : THREE.RGBAFormat; - this.type = options.type !== undefined ? options.type : THREE.UnsignedByteType; + zlights = _lights, - this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; - this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true; + viewMatrix = camera.matrixWorldInverse, - this.generateMipmaps = true; + dirColors = zlights.directional.colors, + dirPositions = zlights.directional.positions, - this.shareDepthFrom = options.shareDepthFrom !== undefined ? options.shareDepthFrom : null; + pointColors = zlights.point.colors, + pointPositions = zlights.point.positions, + pointDistances = zlights.point.distances, + pointDecays = zlights.point.decays, -}; + spotColors = zlights.spot.colors, + spotPositions = zlights.spot.positions, + spotDistances = zlights.spot.distances, + spotDirections = zlights.spot.directions, + spotAnglesCos = zlights.spot.anglesCos, + spotExponents = zlights.spot.exponents, + spotDecays = zlights.spot.decays, -THREE.WebGLRenderTarget.prototype = { + hemiSkyColors = zlights.hemi.skyColors, + hemiGroundColors = zlights.hemi.groundColors, + hemiPositions = zlights.hemi.positions, - constructor: THREE.WebGLRenderTarget, + dirLength = 0, + pointLength = 0, + spotLength = 0, + hemiLength = 0, - setSize: function ( width, height ) { + dirCount = 0, + pointCount = 0, + spotCount = 0, + hemiCount = 0, - if ( this.width !== width || this.height !== height ) { + dirOffset = 0, + pointOffset = 0, + spotOffset = 0, + hemiOffset = 0; - this.width = width; - this.height = height; + for ( l = 0, ll = lights.length; l < ll; l ++ ) { - this.dispose(); + light = lights[ l ]; - } + if ( light.onlyShadow ) continue; - }, + color = light.color; + intensity = light.intensity; + distance = light.distance; - clone: function () { + if ( light instanceof THREE.AmbientLight ) { - return new this.constructor().copy( this ); + if ( ! light.visible ) continue; - }, + r += color.r; + g += color.g; + b += color.b; - copy: function ( source ) { + } else if ( light instanceof THREE.DirectionalLight ) { - this.width = source.width; - this.height = source.height; + dirCount += 1; - this.wrapS = source.wrapS; - this.wrapT = source.wrapT; + if ( ! light.visible ) continue; - this.magFilter = source.magFilter; - this.minFilter = source.minFilter; + _direction.setFromMatrixPosition( light.matrixWorld ); + _vector3.setFromMatrixPosition( light.target.matrixWorld ); + _direction.sub( _vector3 ); + _direction.transformDirection( viewMatrix ); - this.anisotropy = source.anisotropy; + dirOffset = dirLength * 3; - this.offset.copy( source.offset ); - this.repeat.copy( source.repeat ); + dirPositions[ dirOffset + 0 ] = _direction.x; + dirPositions[ dirOffset + 1 ] = _direction.y; + dirPositions[ dirOffset + 2 ] = _direction.z; - this.format = source.format; - this.type = source.type; + setColorLinear( dirColors, dirOffset, color, intensity ); - this.depthBuffer = source.depthBuffer; - this.stencilBuffer = source.stencilBuffer; + dirLength += 1; - this.generateMipmaps = source.generateMipmaps; + } else if ( light instanceof THREE.PointLight ) { - this.shareDepthFrom = source.shareDepthFrom; + pointCount += 1; - return this; + if ( ! light.visible ) continue; - }, + pointOffset = pointLength * 3; - dispose: function () { + setColorLinear( pointColors, pointOffset, color, intensity ); - this.dispatchEvent( { type: 'dispose' } ); + _vector3.setFromMatrixPosition( light.matrixWorld ); + _vector3.applyMatrix4( viewMatrix ); - } + pointPositions[ pointOffset + 0 ] = _vector3.x; + pointPositions[ pointOffset + 1 ] = _vector3.y; + pointPositions[ pointOffset + 2 ] = _vector3.z; -}; + // distance is 0 if decay is 0, because there is no attenuation at all. + pointDistances[ pointLength ] = distance; + pointDecays[ pointLength ] = ( light.distance === 0 ) ? 0.0 : light.decay; -THREE.EventDispatcher.prototype.apply( THREE.WebGLRenderTarget.prototype ); + pointLength += 1; -// File:src/renderers/WebGLRenderTargetCube.js + } else if ( light instanceof THREE.SpotLight ) { -/** - * @author alteredq / http://alteredqualia.com - */ + spotCount += 1; -THREE.WebGLRenderTargetCube = function ( width, height, options ) { + if ( ! light.visible ) continue; - THREE.WebGLRenderTarget.call( this, width, height, options ); + spotOffset = spotLength * 3; - this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5 + setColorLinear( spotColors, spotOffset, color, intensity ); -}; + _direction.setFromMatrixPosition( light.matrixWorld ); + _vector3.copy( _direction ).applyMatrix4( viewMatrix ); -THREE.WebGLRenderTargetCube.prototype = Object.create( THREE.WebGLRenderTarget.prototype ); -THREE.WebGLRenderTargetCube.prototype.constructor = THREE.WebGLRenderTargetCube; + spotPositions[ spotOffset + 0 ] = _vector3.x; + spotPositions[ spotOffset + 1 ] = _vector3.y; + spotPositions[ spotOffset + 2 ] = _vector3.z; -// File:src/renderers/webgl/WebGLBufferRenderer.js + spotDistances[ spotLength ] = distance; -/** -* @author mrdoob / http://mrdoob.com/ -*/ + _vector3.setFromMatrixPosition( light.target.matrixWorld ); + _direction.sub( _vector3 ); + _direction.transformDirection( viewMatrix ); -THREE.WebGLBufferRenderer = function ( _gl, extensions, _infoRender ) { + spotDirections[ spotOffset + 0 ] = _direction.x; + spotDirections[ spotOffset + 1 ] = _direction.y; + spotDirections[ spotOffset + 2 ] = _direction.z; - var mode; + spotAnglesCos[ spotLength ] = Math.cos( light.angle ); + spotExponents[ spotLength ] = light.exponent; + spotDecays[ spotLength ] = ( light.distance === 0 ) ? 0.0 : light.decay; - function setMode( value ) { + spotLength += 1; - mode = value; + } else if ( light instanceof THREE.HemisphereLight ) { - } + hemiCount += 1; - function render( start, count ) { + if ( ! light.visible ) continue; - _gl.drawArrays( mode, start, count ); + _direction.setFromMatrixPosition( light.matrixWorld ); + _direction.transformDirection( viewMatrix ); - _infoRender.calls ++; - _infoRender.vertices += count; - if ( mode === _gl.TRIANGLES ) _infoRender.faces += count / 3; + hemiOffset = hemiLength * 3; - } + hemiPositions[ hemiOffset + 0 ] = _direction.x; + hemiPositions[ hemiOffset + 1 ] = _direction.y; + hemiPositions[ hemiOffset + 2 ] = _direction.z; - function renderInstances( geometry ) { + skyColor = light.color; + groundColor = light.groundColor; - var extension = extensions.get( 'ANGLE_instanced_arrays' ); + setColorLinear( hemiSkyColors, hemiOffset, skyColor, intensity ); + setColorLinear( hemiGroundColors, hemiOffset, groundColor, intensity ); - if ( extension === null ) { + hemiLength += 1; - console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); - return; + } } - var position = geometry.attributes.position; + // null eventual remains from removed lights + // (this is to avoid if in shader) - if ( position instanceof THREE.InterleavedBufferAttribute ) { + 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; - extension.drawArraysInstancedANGLE( mode, 0, position.data.count, geometry.maxInstancedCount ); + zlights.directional.length = dirLength; + zlights.point.length = pointLength; + zlights.spot.length = spotLength; + zlights.hemi.length = hemiLength; - } else { + zlights.ambient[ 0 ] = r; + zlights.ambient[ 1 ] = g; + zlights.ambient[ 2 ] = b; - extension.drawArraysInstancedANGLE( mode, 0, position.count, geometry.maxInstancedCount ); + } - } + // GL state setting - } + this.setFaceCulling = function ( cullFace, frontFaceDirection ) { - this.setMode = setMode; - this.render = render; - this.renderInstances = renderInstances; + if ( cullFace === THREE.CullFaceNone ) { -}; + state.disable( _gl.CULL_FACE ); -// File:src/renderers/webgl/WebGLIndexedBufferRenderer.js + } else { -/** -* @author mrdoob / http://mrdoob.com/ -*/ + if ( frontFaceDirection === THREE.FrontFaceDirectionCW ) { -THREE.WebGLIndexedBufferRenderer = function ( _gl, extensions, _infoRender ) { + _gl.frontFace( _gl.CW ); - var mode; + } else { - function setMode( value ) { + _gl.frontFace( _gl.CCW ); - mode = value; + } - } + if ( cullFace === THREE.CullFaceBack ) { - var type, size; + _gl.cullFace( _gl.BACK ); - function setIndex( index ) { + } else if ( cullFace === THREE.CullFaceFront ) { - if ( index.array instanceof Uint32Array && extensions.get( 'OES_element_index_uint' ) ) { + _gl.cullFace( _gl.FRONT ); - type = _gl.UNSIGNED_INT; - size = 4; + } else { - } else { + _gl.cullFace( _gl.FRONT_AND_BACK ); - type = _gl.UNSIGNED_SHORT; - size = 2; + } + + state.enable( _gl.CULL_FACE ); } - } + }; - function render( start, count ) { + // Textures - _gl.drawElements( mode, count, type, start * size ); + function setTextureParameters ( textureType, texture, isImagePowerOfTwo ) { - _infoRender.calls ++; - _infoRender.vertices += count; - if ( mode === _gl.TRIANGLES ) _infoRender.faces += count / 3; + var extension; - } + if ( isImagePowerOfTwo ) { - function renderInstances( geometry ) { + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, paramThreeToGL( texture.wrapS ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, paramThreeToGL( texture.wrapT ) ); - var extension = extensions.get( 'ANGLE_instanced_arrays' ); + _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, paramThreeToGL( texture.magFilter ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, paramThreeToGL( texture.minFilter ) ); - if ( extension === null ) { + } else { - console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); - return; + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); - } + if ( texture.wrapS !== THREE.ClampToEdgeWrapping || texture.wrapT !== THREE.ClampToEdgeWrapping ) { - var index = geometry.index; + console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping. ( ' + texture.sourceFile + ' )' ); - extension.drawElementsInstancedANGLE( mode, index.array.length, type, 0, geometry.maxInstancedCount ); + } - } + _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) ); - this.setMode = setMode; - this.setIndex = setIndex; - this.render = render; - this.renderInstances = renderInstances; + if ( texture.minFilter !== THREE.NearestFilter && texture.minFilter !== THREE.LinearFilter ) { -}; + console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter. ( ' + texture.sourceFile + ' )' ); -// File:src/renderers/webgl/WebGLExtensions.js + } -/** -* @author mrdoob / http://mrdoob.com/ -*/ + } -THREE.WebGLExtensions = function ( gl ) { + extension = extensions.get( 'EXT_texture_filter_anisotropic' ); - var extensions = {}; + if ( extension ) { - this.get = function ( name ) { + if ( texture.type === THREE.FloatType && extensions.get( 'OES_texture_float_linear' ) === null ) return; + if ( texture.type === THREE.HalfFloatType && extensions.get( 'OES_texture_half_float_linear' ) === null ) return; - if ( extensions[ name ] !== undefined ) { + if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) { - return extensions[ name ]; + _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, _this.getMaxAnisotropy() ) ); + properties.get( texture ).__currentAnisotropy = texture.anisotropy; - } + } - var extension; + } - switch ( name ) { + } - 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; + function uploadTexture( textureProperties, texture, slot ) { - 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; + if ( textureProperties.__webglInit === undefined ) { - case 'WEBGL_compressed_texture_pvrtc': - extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ); - break; + textureProperties.__webglInit = true; - default: - extension = gl.getExtension( name ); + texture.__webglInit = true; - } + texture.addEventListener( 'dispose', onTextureDispose ); - if ( extension === null ) { + textureProperties.__webglTexture = _gl.createTexture(); - console.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' ); + _infoMemory.textures ++; } - extensions[ name ] = extension; - - return extension; + state.activeTexture( _gl.TEXTURE0 + slot ); + state.bindTexture( _gl.TEXTURE_2D, textureProperties.__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 ); -}; + texture.image = clampToMaxSize( texture.image, capabilities.maxTextureSize ); -// File:src/renderers/webgl/WebGLCapabilities.js + var image = texture.image, + isImagePowerOfTwo = THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height ), + glFormat = paramThreeToGL( texture.format ), + glType = paramThreeToGL( texture.type ); -THREE.WebGLCapabilities = function ( gl, extensions, parameters ) { + setTextureParameters( _gl.TEXTURE_2D, texture, isImagePowerOfTwo ); - function getMaxPrecision( precision ) { + var mipmap, mipmaps = texture.mipmaps; - if ( precision === 'highp' ) { + if ( texture instanceof THREE.DataTexture ) { - if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.HIGH_FLOAT ).precision > 0 && - gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).precision > 0 ) { + // 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 - return 'highp'; + if ( mipmaps.length > 0 && isImagePowerOfTwo ) { - } + for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { - precision = 'mediump'; + mipmap = mipmaps[ i ]; + state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - } + } - if ( precision === 'mediump' ) { + texture.generateMipmaps = false; - if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).precision > 0 && - gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).precision > 0 ) { + } else { - return 'mediump'; + state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data ); } - } + } else if ( texture instanceof THREE.CompressedTexture ) { - return 'lowp'; + for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { - } + mipmap = mipmaps[ i ]; - this.getMaxPrecision = getMaxPrecision; + if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) { - this.precision = parameters.precision !== undefined ? parameters.precision : 'highp', - this.logarithmicDepthBuffer = parameters.logarithmicDepthBuffer !== undefined ? parameters.logarithmicDepthBuffer : false; + if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) { - this.maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS ); - this.maxVertexTextures = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ); - this.maxTextureSize = gl.getParameter( gl.MAX_TEXTURE_SIZE ); - this.maxCubemapSize = gl.getParameter( gl.MAX_CUBE_MAP_TEXTURE_SIZE ); + state.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); - this.maxAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); - this.maxVertexUniforms = gl.getParameter( gl.MAX_VERTEX_UNIFORM_VECTORS ); - this.maxVaryings = gl.getParameter( gl.MAX_VARYING_VECTORS ); - this.maxFragmentUniforms = gl.getParameter( gl.MAX_FRAGMENT_UNIFORM_VECTORS ); + } else { - this.vertexTextures = this.maxVertexTextures > 0; - this.floatFragmentTextures = !! extensions.get( 'OES_texture_float' ); - this.floatVertexTextures = this.vertexTextures && this.floatFragmentTextures; + console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()" ); - var _maxPrecision = getMaxPrecision( this.precision ); + } - if ( _maxPrecision !== this.precision ) { + } else { - console.warn( 'THREE.WebGLRenderer:', this.precision, 'not supported, using', _maxPrecision, 'instead.' ); - this.precision = _maxPrecision; + state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - } + } - if ( this.logarithmicDepthBuffer ) { + } - this.logarithmicDepthBuffer = !! extensions.get( 'EXT_frag_depth' ); + } 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 -// File:src/renderers/webgl/WebGLGeometries.js + if ( mipmaps.length > 0 && isImagePowerOfTwo ) { -/** -* @author mrdoob / http://mrdoob.com/ -*/ + for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { -THREE.WebGLGeometries = function ( gl, properties, info ) { + mipmap = mipmaps[ i ]; + state.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap ); - var geometries = {}; + } - function get( object ) { + texture.generateMipmaps = false; - var geometry = object.geometry; + } else { - if ( geometries[ geometry.id ] !== undefined ) { + state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, texture.image ); - return geometries[ geometry.id ]; + } } - geometry.addEventListener( 'dispose', onGeometryDispose ); + if ( texture.generateMipmaps && isImagePowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); - var buffergeometry; + textureProperties.__version = texture.version; - if ( geometry instanceof THREE.BufferGeometry ) { + if ( texture.onUpdate ) texture.onUpdate( texture ); - buffergeometry = geometry; + } - } else if ( geometry instanceof THREE.Geometry ) { + this.setTexture = function ( texture, slot ) { - if ( geometry._bufferGeometry === undefined ) { + var textureProperties = properties.get( texture ); - geometry._bufferGeometry = new THREE.BufferGeometry().setFromObject( object ); + if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - } + var image = texture.image; - buffergeometry = geometry._bufferGeometry; + if ( image === undefined ) { - } + console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is undefined', texture ); + return; - geometries[ geometry.id ] = buffergeometry; + } - info.memory.geometries ++; + if ( image.complete === false ) { - return buffergeometry; + console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete', texture ); + return; - } + } - function onGeometryDispose( event ) { + uploadTexture( textureProperties, texture, slot ); + return; - var geometry = event.target; - var buffergeometry = geometries[ geometry.id ]; + } - deleteAttributes( buffergeometry.attributes ); + state.activeTexture( _gl.TEXTURE0 + slot ); + state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); - geometry.removeEventListener( 'dispose', onGeometryDispose ); + }; - delete geometries[ geometry.id ]; + function clampToMaxSize ( image, maxSize ) { - var property = properties.get( geometry ); - if ( property.wireframe ) deleteAttribute( property.wireframe ); + if ( image.width > maxSize || image.height > maxSize ) { - info.memory.geometries --; + // Warning: Scaling through the canvas will only work with images that use + // premultiplied alpha. - } + var scale = maxSize / Math.max( image.width, image.height ); - function getAttributeBuffer( attribute ) { + var canvas = document.createElement( 'canvas' ); + canvas.width = Math.floor( image.width * scale ); + canvas.height = Math.floor( image.height * scale ); - if ( attribute instanceof THREE.InterleavedBufferAttribute ) { + var context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height ); - return properties.get( attribute.data ).__webglBuffer; + console.warn( 'THREE.WebGLRenderer: image is too big (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height, image ); + + return canvas; } - return properties.get( attribute ).__webglBuffer; + return image; } - function deleteAttribute( attribute ) { - - var buffer = getAttributeBuffer( attribute ); - - if ( buffer !== undefined ) { + function setCubeTexture ( texture, slot ) { - gl.deleteBuffer( buffer ); - removeAttributeBuffer( attribute ); + var textureProperties = properties.get( texture ); - } + if ( texture.image.length === 6 ) { - } + if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - function deleteAttributes( attributes ) { + if ( ! textureProperties.__image__webglTextureCube ) { - for ( var name in attributes ) { + texture.addEventListener( 'dispose', onTextureDispose ); - deleteAttribute( attributes[ name ] ); + textureProperties.__image__webglTextureCube = _gl.createTexture(); - } + _infoMemory.textures ++; - } + } - function removeAttributeBuffer( attribute ) { + state.activeTexture( _gl.TEXTURE0 + slot ); + state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube ); - if ( attribute instanceof THREE.InterleavedBufferAttribute ) { + _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); - properties.delete( attribute.data ); + var isCompressed = texture instanceof THREE.CompressedTexture; + var isDataTexture = texture.image[ 0 ] instanceof THREE.DataTexture; - } else { + var cubeImage = []; - properties.delete( attribute ); + for ( var i = 0; i < 6; i ++ ) { - } + if ( _this.autoScaleCubemaps && ! isCompressed && ! isDataTexture ) { - } + cubeImage[ i ] = clampToMaxSize( texture.image[ i ], capabilities.maxCubemapSize ); - this.get = get; + } else { -}; + cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; -// File:src/renderers/webgl/WebGLObjects.js + } -/** -* @author mrdoob / http://mrdoob.com/ -*/ + } -THREE.WebGLObjects = function ( gl, properties, info ) { + var image = cubeImage[ 0 ], + isImagePowerOfTwo = THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height ), + glFormat = paramThreeToGL( texture.format ), + glType = paramThreeToGL( texture.type ); - var geometries = new THREE.WebGLGeometries( gl, properties, info ); + setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isImagePowerOfTwo ); - // + for ( var i = 0; i < 6; i ++ ) { - function update( object ) { + if ( ! isCompressed ) { - // TODO: Avoid updating twice (when using shadowMap). Maybe add frame counter. + if ( isDataTexture ) { - var geometry = geometries.get( object ); + state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); - if ( object.geometry instanceof THREE.Geometry ) { + } else { - geometry.updateFromObject( object ); + state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] ); - } + } - var index = geometry.index; - var attributes = geometry.attributes; + } else { - if ( index !== null ) { + var mipmap, mipmaps = cubeImage[ i ].mipmaps; - updateAttribute( index, gl.ELEMENT_ARRAY_BUFFER ); + for ( var j = 0, jl = mipmaps.length; j < jl; j ++ ) { - } + mipmap = mipmaps[ j ]; - for ( var name in attributes ) { + if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) { - updateAttribute( attributes[ name ], gl.ARRAY_BUFFER ); + if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) { - } + state.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); - // morph targets + } else { - var morphAttributes = geometry.morphAttributes; + console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setCubeTexture()" ); - for ( var name in morphAttributes ) { + } - var array = morphAttributes[ name ]; + } else { - for ( var i = 0, l = array.length; i < l; i ++ ) { + state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - updateAttribute( array[ i ], gl.ARRAY_BUFFER ); + } - } + } - } + } - return geometry; + } - } + if ( texture.generateMipmaps && isImagePowerOfTwo ) { - function updateAttribute( attribute, bufferType ) { + _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); - var data = ( attribute instanceof THREE.InterleavedBufferAttribute ) ? attribute.data : attribute; + } - var attributeProperties = properties.get( data ); + textureProperties.__version = texture.version; - if ( attributeProperties.__webglBuffer === undefined ) { + if ( texture.onUpdate ) texture.onUpdate( texture ); - createBuffer( attributeProperties, data, bufferType ); + } else { - } else if ( attributeProperties.version !== data.version ) { + state.activeTexture( _gl.TEXTURE0 + slot ); + state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube ); - updateBuffer( attributeProperties, data, bufferType ); + } } } - function createBuffer( attributeProperties, data, bufferType ) { + function setCubeTextureDynamic ( texture, slot ) { - attributeProperties.__webglBuffer = gl.createBuffer(); - gl.bindBuffer( bufferType, attributeProperties.__webglBuffer ); + state.activeTexture( _gl.TEXTURE0 + slot ); + state.bindTexture( _gl.TEXTURE_CUBE_MAP, properties.get( texture ).__webglTexture ); - var usage = data.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW; + } - gl.bufferData( bufferType, data.array, usage ); + // Render targets - attributeProperties.version = data.version; + function setupFrameBuffer ( framebuffer, renderTarget, textureTarget ) { + + _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureTarget, properties.get( renderTarget ).__webglTexture, 0 ); } - function updateBuffer( attributeProperties, data, bufferType ) { + function setupRenderBuffer ( renderbuffer, renderTarget ) { - gl.bindBuffer( bufferType, attributeProperties.__webglBuffer ); + _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer ); - if ( data.dynamic === false || data.updateRange.count === - 1 ) { + if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { - // Not using update ranges + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); - gl.bufferSubData( bufferType, 0, data.array ); + /* For some reason this is not working. Defaulting to RGBA4. + } else if ( ! renderTarget.depthBuffer && renderTarget.stencilBuffer ) { - } else if ( data.updateRange.count === 0 ) { + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.STENCIL_INDEX8, renderTarget.width, renderTarget.height ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); + */ - console.error( 'THREE.WebGLObjects.updateBuffer: dynamic THREE.BufferAttribute marked as needsUpdate but updateRange.count is 0, ensure you are using set methods or updating manually.' ); + } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { - } else { + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); - gl.bufferSubData( bufferType, data.updateRange.offset * data.array.BYTES_PER_ELEMENT, - data.array.subarray( data.updateRange.offset, data.updateRange.offset + data.updateRange.count ) ); + } else { - data.updateRange.count = 0; // reset range + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height ); } - attributeProperties.version = data.version; - } - function getAttributeBuffer( attribute ) { - - if ( attribute instanceof THREE.InterleavedBufferAttribute ) { - - return properties.get( attribute.data ).__webglBuffer; - - } + this.setRenderTarget = function ( renderTarget ) { - return properties.get( attribute ).__webglBuffer; + var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube ); - } + if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) { - function getWireframeAttribute( geometry ) { + var renderTargetProperties = properties.get( renderTarget ); - var property = properties.get( geometry ); + if ( renderTarget.depthBuffer === undefined ) renderTarget.depthBuffer = true; + if ( renderTarget.stencilBuffer === undefined ) renderTarget.stencilBuffer = true; - if ( property.wireframe !== undefined ) { + renderTarget.addEventListener( 'dispose', onRenderTargetDispose ); - return property.wireframe; + renderTargetProperties.__webglTexture = _gl.createTexture(); - } + _infoMemory.textures ++; - var indices = []; + // Setup texture, create render and frame buffers - var index = geometry.index; - var attributes = geometry.attributes; - var position = attributes.position; + var isTargetPowerOfTwo = THREE.Math.isPowerOfTwo( renderTarget.width ) && THREE.Math.isPowerOfTwo( renderTarget.height ), + glFormat = paramThreeToGL( renderTarget.format ), + glType = paramThreeToGL( renderTarget.type ); - // console.time( 'wireframe' ); + if ( isCube ) { - if ( index !== null ) { + renderTargetProperties.__webglFramebuffer = []; + renderTargetProperties.__webglRenderbuffer = []; - var edges = {}; - var array = index.array; + state.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTargetProperties.__webglTexture ); - for ( var i = 0, l = array.length; i < l; i += 3 ) { + setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget, isTargetPowerOfTwo ); - var a = array[ i + 0 ]; - var b = array[ i + 1 ]; - var c = array[ i + 2 ]; + for ( var i = 0; i < 6; i ++ ) { - if ( checkEdge( edges, a, b ) ) indices.push( a, b ); - if ( checkEdge( edges, b, c ) ) indices.push( b, c ); - if ( checkEdge( edges, c, a ) ) indices.push( c, a ); + renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer(); + renderTargetProperties.__webglRenderbuffer[ i ] = _gl.createRenderbuffer(); - } + state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); - } else { + setupFrameBuffer( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i ); + setupRenderBuffer( renderTargetProperties.__webglRenderbuffer[ i ], renderTarget ); - var array = attributes.position.array; + } - for ( var i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) { + if ( renderTarget.generateMipmaps && isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); - var a = i + 0; - var b = i + 1; - var c = i + 2; + } else { - indices.push( a, b, b, c, c, a ); + renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); - } + if ( renderTarget.shareDepthFrom ) { - } + renderTargetProperties.__webglRenderbuffer = renderTarget.shareDepthFrom.__webglRenderbuffer; - // console.timeEnd( 'wireframe' ); + } else { - var TypeArray = position.count > 65535 ? Uint32Array : Uint16Array; - var attribute = new THREE.BufferAttribute( new TypeArray( indices ), 1 ); + renderTargetProperties.__webglRenderbuffer = _gl.createRenderbuffer(); - updateAttribute( attribute, gl.ELEMENT_ARRAY_BUFFER ); + } - property.wireframe = attribute; + state.bindTexture( _gl.TEXTURE_2D, renderTargetProperties.__webglTexture ); + setTextureParameters( _gl.TEXTURE_2D, renderTarget, isTargetPowerOfTwo ); - return attribute; + state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); - } + setupFrameBuffer( renderTargetProperties.__webglFramebuffer, renderTarget, _gl.TEXTURE_2D ); - function checkEdge( edges, a, b ) { + if ( renderTarget.shareDepthFrom ) { - if ( a > b ) { + if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { - var tmp = a; - a = b; - b = tmp; + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderTargetProperties.__webglRenderbuffer ); - } + } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { - var list = edges[ a ]; + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderTargetProperties.__webglRenderbuffer ); - if ( list === undefined ) { + } - edges[ a ] = [ b ]; - return true; + } else { - } else if ( list.indexOf( b ) === -1 ) { + setupRenderBuffer( renderTargetProperties.__webglRenderbuffer, renderTarget ); - list.push( b ); - return true; + } - } + if ( renderTarget.generateMipmaps && isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); - return false; + } - } + // Release everything - this.getAttributeBuffer = getAttributeBuffer; - this.getWireframeAttribute = getWireframeAttribute; + if ( isCube ) { - this.update = update; + state.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); -}; + } else { -// File:src/renderers/webgl/WebGLProgram.js + state.bindTexture( _gl.TEXTURE_2D, null ); -THREE.WebGLProgram = ( function () { + } - var programIdCount = 0; + _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); + _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); - function generateDefines( defines ) { + } - var chunks = []; + var framebuffer, width, height, vx, vy; - for ( var name in defines ) { + if ( renderTarget ) { - var value = defines[ name ]; + var renderTargetProperties = properties.get( renderTarget ); - if ( value === false ) continue; + if ( isCube ) { - chunks.push( '#define ' + name + ' ' + value ); + framebuffer = renderTargetProperties.__webglFramebuffer[ renderTarget.activeCubeFace ]; - } + } else { - return chunks.join( '\n' ); + framebuffer = renderTargetProperties.__webglFramebuffer; - } + } - function fetchUniformLocations( gl, program, identifiers ) { + width = renderTarget.width; + height = renderTarget.height; - var uniforms = {}; + vx = 0; + vy = 0; - var n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS ); + } else { - for ( var i = 0; i < n; i ++ ) { + framebuffer = null; - var info = gl.getActiveUniform( program, i ); - var name = info.name; - var location = gl.getUniformLocation( program, name ); + width = _viewportWidth; + height = _viewportHeight; - // console.log("THREE.WebGLProgram: ACTIVE UNIFORM:", name); + vx = _viewportX; + vy = _viewportY; - var suffixPos = name.lastIndexOf( '[0]' ); - if ( suffixPos !== - 1 && suffixPos === name.length - 3 ) { + } - uniforms[ name.substr( 0, suffixPos ) ] = location; + if ( framebuffer !== _currentFramebuffer ) { - } + _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + _gl.viewport( vx, vy, width, height ); - uniforms[ name ] = location; + _currentFramebuffer = framebuffer; } - return uniforms; - - } + _currentWidth = width; + _currentHeight = height; - function fetchAttributeLocations( gl, program, identifiers ) { + }; - var attributes = {}; + this.readRenderTargetPixels = function( renderTarget, x, y, width, height, buffer ) { - var n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES ); + if ( ! ( renderTarget instanceof THREE.WebGLRenderTarget ) ) { - for ( var i = 0; i < n; i ++ ) { + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); + return; - var info = gl.getActiveAttrib( program, i ); - var name = info.name; + } - // console.log("THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name, i ); + if ( properties.get( renderTarget ).__webglFramebuffer ) { - attributes[ name ] = gl.getAttribLocation( program, name ); + if ( renderTarget.format !== THREE.RGBAFormat ) { - } + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA format. readPixels can read only RGBA format.' ); + return; - return attributes; + } - } + var restore = false; - function filterEmptyLine( string ) { + if ( properties.get( renderTarget ).__webglFramebuffer !== _currentFramebuffer ) { - return string !== ''; + _gl.bindFramebuffer( _gl.FRAMEBUFFER, properties.get( renderTarget ).__webglFramebuffer ); - } + restore = true; - return function WebGLProgram( renderer, code, material, parameters ) { + } - var gl = renderer.context; + if ( _gl.checkFramebufferStatus( _gl.FRAMEBUFFER ) === _gl.FRAMEBUFFER_COMPLETE ) { - var defines = material.defines; + _gl.readPixels( x, y, width, height, _gl.RGBA, _gl.UNSIGNED_BYTE, buffer ); - var vertexShader = material.__webglShader.vertexShader; - var fragmentShader = material.__webglShader.fragmentShader; + } else { - var shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC'; + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' ); - if ( parameters.shadowMapType === THREE.PCFShadowMap ) { + } - shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF'; + if ( restore ) { - } else if ( parameters.shadowMapType === THREE.PCFSoftShadowMap ) { + _gl.bindFramebuffer( _gl.FRAMEBUFFER, _currentFramebuffer ); - shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT'; + } } - var envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; - var envMapModeDefine = 'ENVMAP_MODE_REFLECTION'; - var envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; + }; - if ( parameters.envMap ) { + function updateRenderTargetMipmap ( renderTarget ) { - switch ( material.envMap.mapping ) { + if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) { - case THREE.CubeReflectionMapping: - case THREE.CubeRefractionMapping: - envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; - break; + state.bindTexture( _gl.TEXTURE_CUBE_MAP, properties.get( renderTarget ).__webglTexture ); + _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); + state.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); - case THREE.EquirectangularReflectionMapping: - case THREE.EquirectangularRefractionMapping: - envMapTypeDefine = 'ENVMAP_TYPE_EQUIREC'; - break; + } else { - case THREE.SphericalReflectionMapping: - envMapTypeDefine = 'ENVMAP_TYPE_SPHERE'; - break; + state.bindTexture( _gl.TEXTURE_2D, properties.get( renderTarget ).__webglTexture ); + _gl.generateMipmap( _gl.TEXTURE_2D ); + state.bindTexture( _gl.TEXTURE_2D, null ); - } + } - switch ( material.envMap.mapping ) { + } - case THREE.CubeRefractionMapping: - case THREE.EquirectangularRefractionMapping: - envMapModeDefine = 'ENVMAP_MODE_REFRACTION'; - break; + // Fallback filters for non-power-of-2 textures - } + function filterFallback ( f ) { - switch ( material.combine ) { + if ( f === THREE.NearestFilter || f === THREE.NearestMipMapNearestFilter || f === THREE.NearestMipMapLinearFilter ) { - case THREE.MultiplyOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; - break; + return _gl.NEAREST; - case THREE.MixOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_MIX'; - break; + } - case THREE.AddOperation: - envMapBlendingDefine = 'ENVMAP_BLENDING_ADD'; - break; + return _gl.LINEAR; - } + } - } + // Map three.js constants to WebGL constants - var gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0; + function paramThreeToGL ( p ) { - // console.log( 'building new program ' ); + var extension; - // + if ( p === THREE.RepeatWrapping ) return _gl.REPEAT; + if ( p === THREE.ClampToEdgeWrapping ) return _gl.CLAMP_TO_EDGE; + if ( p === THREE.MirroredRepeatWrapping ) return _gl.MIRRORED_REPEAT; - var customDefines = generateDefines( defines ); + 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 ( p === THREE.LinearFilter ) return _gl.LINEAR; + if ( p === THREE.LinearMipMapNearestFilter ) return _gl.LINEAR_MIPMAP_NEAREST; + if ( p === THREE.LinearMipMapLinearFilter ) return _gl.LINEAR_MIPMAP_LINEAR; - var program = gl.createProgram(); + 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; - var prefixVertex, prefixFragment; + 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 ( material instanceof THREE.RawShaderMaterial ) { + extension = extensions.get( 'OES_texture_half_float' ); - prefixVertex = ''; - prefixFragment = ''; + if ( extension !== null ) { - } else { + if ( p === THREE.HalfFloatType ) return extension.HALF_FLOAT_OES; - prefixVertex = [ + } - 'precision ' + parameters.precision + ' float;', - 'precision ' + parameters.precision + ' int;', + 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; - '#define SHADER_NAME ' + material.__webglShader.name, + 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; - customDefines, + 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; - parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '', + 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; - renderer.gammaInput ? '#define GAMMA_INPUT' : '', - renderer.gammaOutput ? '#define GAMMA_OUTPUT' : '', - '#define GAMMA_FACTOR ' + gammaFactorDefine, + extension = extensions.get( 'WEBGL_compressed_texture_s3tc' ); - '#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 ( extension !== null ) { - '#define MAX_SHADOWS ' + parameters.maxShadows, + 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; - '#define MAX_BONES ' + parameters.maxBones, + } - parameters.map ? '#define USE_MAP' : '', - parameters.envMap ? '#define USE_ENVMAP' : '', - parameters.envMap ? '#define ' + envMapModeDefine : '', - parameters.lightMap ? '#define USE_LIGHTMAP' : '', - parameters.aoMap ? '#define USE_AOMAP' : '', - parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', - parameters.bumpMap ? '#define USE_BUMPMAP' : '', - parameters.normalMap ? '#define USE_NORMALMAP' : '', - parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '', - parameters.specularMap ? '#define USE_SPECULARMAP' : '', - parameters.alphaMap ? '#define USE_ALPHAMAP' : '', - parameters.vertexColors ? '#define USE_COLOR' : '', + extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' ); - parameters.flatShading ? '#define FLAT_SHADED' : '', + if ( extension !== null ) { - parameters.skinning ? '#define USE_SKINNING' : '', - parameters.useVertexTexture ? '#define BONE_TEXTURE' : '', + 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; - parameters.morphTargets ? '#define USE_MORPHTARGETS' : '', - parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '', - parameters.doubleSided ? '#define DOUBLE_SIDED' : '', - parameters.flipSided ? '#define FLIP_SIDED' : '', + } - parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', - parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', - parameters.shadowMapDebug ? '#define SHADOWMAP_DEBUG' : '', + extension = extensions.get( 'EXT_blend_minmax' ); - parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '', + if ( extension !== null ) { - parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', - parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '', + if ( p === THREE.MinEquation ) return extension.MIN_EXT; + if ( p === THREE.MaxEquation ) return extension.MAX_EXT; + } - 'uniform mat4 modelMatrix;', - 'uniform mat4 modelViewMatrix;', - 'uniform mat4 projectionMatrix;', - 'uniform mat4 viewMatrix;', - 'uniform mat3 normalMatrix;', - 'uniform vec3 cameraPosition;', + return 0; - 'attribute vec3 position;', - 'attribute vec3 normal;', - 'attribute vec2 uv;', + } - '#ifdef USE_COLOR', + // DEPRECATED - ' attribute vec3 color;', + this.supportsFloatTextures = function () { - '#endif', + console.warn( 'THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( \'OES_texture_float\' ).' ); + return extensions.get( 'OES_texture_float' ); - '#ifdef USE_MORPHTARGETS', + }; - ' attribute vec3 morphTarget0;', - ' attribute vec3 morphTarget1;', - ' attribute vec3 morphTarget2;', - ' attribute vec3 morphTarget3;', + this.supportsHalfFloatTextures = function () { - ' #ifdef USE_MORPHNORMALS', + console.warn( 'THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( \'OES_texture_half_float\' ).' ); + return extensions.get( 'OES_texture_half_float' ); - ' attribute vec3 morphNormal0;', - ' attribute vec3 morphNormal1;', - ' attribute vec3 morphNormal2;', - ' attribute vec3 morphNormal3;', + }; - ' #else', + this.supportsStandardDerivatives = function () { - ' attribute vec3 morphTarget4;', - ' attribute vec3 morphTarget5;', - ' attribute vec3 morphTarget6;', - ' attribute vec3 morphTarget7;', + console.warn( 'THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( \'OES_standard_derivatives\' ).' ); + return extensions.get( 'OES_standard_derivatives' ); - ' #endif', + }; - '#endif', + this.supportsCompressedTextureS3TC = function () { - '#ifdef USE_SKINNING', + console.warn( 'THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( \'WEBGL_compressed_texture_s3tc\' ).' ); + return extensions.get( 'WEBGL_compressed_texture_s3tc' ); - ' attribute vec4 skinIndex;', - ' attribute vec4 skinWeight;', + }; - '#endif', + this.supportsCompressedTexturePVRTC = function () { - '\n' + console.warn( 'THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( \'WEBGL_compressed_texture_pvrtc\' ).' ); + return extensions.get( 'WEBGL_compressed_texture_pvrtc' ); - ].filter( filterEmptyLine ).join( '\n' ); + }; - prefixFragment = [ + this.supportsBlendMinMax = function () { - parameters.bumpMap || parameters.normalMap || parameters.flatShading || material.derivatives ? '#extension GL_OES_standard_derivatives : enable' : '', - parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#extension GL_EXT_frag_depth : enable' : '', + console.warn( 'THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( \'EXT_blend_minmax\' ).' ); + return extensions.get( 'EXT_blend_minmax' ); - 'precision ' + parameters.precision + ' float;', - 'precision ' + parameters.precision + ' int;', + }; - '#define SHADER_NAME ' + material.__webglShader.name, + this.supportsVertexTextures = function () { - customDefines, + return capabilities.vertexTextures; - '#define MAX_DIR_LIGHTS ' + parameters.maxDirLights, - '#define MAX_POINT_LIGHTS ' + parameters.maxPointLights, - '#define MAX_SPOT_LIGHTS ' + parameters.maxSpotLights, - '#define MAX_HEMI_LIGHTS ' + parameters.maxHemiLights, + }; - '#define MAX_SHADOWS ' + parameters.maxShadows, + this.supportsInstancedArrays = function () { - parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest : '', + console.warn( 'THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( \'ANGLE_instanced_arrays\' ).' ); + return extensions.get( 'ANGLE_instanced_arrays' ); - renderer.gammaInput ? '#define GAMMA_INPUT' : '', - renderer.gammaOutput ? '#define GAMMA_OUTPUT' : '', - '#define GAMMA_FACTOR ' + gammaFactorDefine, + }; - ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', - ( parameters.useFog && parameters.fogExp ) ? '#define FOG_EXP2' : '', + // - parameters.map ? '#define USE_MAP' : '', - parameters.envMap ? '#define USE_ENVMAP' : '', - parameters.envMap ? '#define ' + envMapTypeDefine : '', - parameters.envMap ? '#define ' + envMapModeDefine : '', - parameters.envMap ? '#define ' + envMapBlendingDefine : '', - parameters.lightMap ? '#define USE_LIGHTMAP' : '', - parameters.aoMap ? '#define USE_AOMAP' : '', - parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', - 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' : '', + this.initMaterial = function () { - parameters.flatShading ? '#define FLAT_SHADED' : '', + console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' ); - parameters.metal ? '#define METAL' : '', - parameters.doubleSided ? '#define DOUBLE_SIDED' : '', - parameters.flipSided ? '#define FLIP_SIDED' : '', + }; - parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', - parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', - parameters.shadowMapDebug ? '#define SHADOWMAP_DEBUG' : '', + this.addPrePlugin = function () { - parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', - parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '', + console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' ); - 'uniform mat4 viewMatrix;', - 'uniform vec3 cameraPosition;', + }; - '\n' + this.addPostPlugin = function () { - ].filter( filterEmptyLine ).join( '\n' ); + console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' ); - } + }; - var vertexGlsl = prefixVertex + vertexShader; - var fragmentGlsl = prefixFragment + fragmentShader; + this.updateShadowMap = function () { - var glVertexShader = THREE.WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl ); - var glFragmentShader = THREE.WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl ); + console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' ); - gl.attachShader( program, glVertexShader ); - gl.attachShader( program, glFragmentShader ); + }; - // Force a particular attribute to index 0. + Object.defineProperties( this, { + shadowMapEnabled: { + get: function () { - if ( material.index0AttributeName !== undefined ) { + return shadowMap.enabled; - gl.bindAttribLocation( program, 0, material.index0AttributeName ); + }, + set: function ( value ) { - } else if ( parameters.morphTargets === true ) { + console.warn( 'THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled.' ); + shadowMap.enabled = value; - // programs with morphTargets displace position out of attribute 0 - gl.bindAttribLocation( program, 0, 'position' ); + } + }, + shadowMapType: { + get: function () { - } + return shadowMap.type; - gl.linkProgram( program ); + }, + set: function ( value ) { - var programLog = gl.getProgramInfoLog( program ); - var vertexLog = gl.getShaderInfoLog( glVertexShader ); - var fragmentLog = gl.getShaderInfoLog( glFragmentShader ); + console.warn( 'THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type.' ); + shadowMap.type = value; - var runnable = true; - var haveDiagnostics = true; + } + }, + shadowMapCullFace: { + get: function () { - if ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) { + return shadowMap.cullFace; - runnable = false; + }, + set: function ( value ) { - console.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), 'gl.VALIDATE_STATUS', gl.getProgramParameter( program, gl.VALIDATE_STATUS ), 'gl.getProgramInfoLog', programLog, vertexLog, fragmentLog ); + console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace is now .shadowMap.cullFace.' ); + shadowMap.cullFace = value; - } else if ( programLog !== '' ) { + } + }, + shadowMapDebug: { + get: function () { - console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', programLog ); + return shadowMap.debug; - } else if ( vertexLog === '' || fragmentLog === '' ) { + }, + set: function ( value ) { - haveDiagnostics = false; + console.warn( 'THREE.WebGLRenderer: .shadowMapDebug is now .shadowMap.debug.' ); + shadowMap.debug = value; + } } + } ); - if ( haveDiagnostics ) { +}; - this.diagnostics = { +// File:src/renderers/WebGLRenderTarget.js - runnable: runnable, - material: material, +/** + * @author szimek / https://github.com/szimek/ + * @author alteredq / http://alteredqualia.com/ + */ - programLog: programLog, +THREE.WebGLRenderTarget = function ( width, height, options ) { - vertexShader: { + this.uuid = THREE.Math.generateUUID(); - log: vertexLog, - prefix: prefixVertex + this.width = width; + this.height = height; - }, + options = options || {}; - fragmentShader: { + this.wrapS = options.wrapS !== undefined ? options.wrapS : THREE.ClampToEdgeWrapping; + this.wrapT = options.wrapT !== undefined ? options.wrapT : THREE.ClampToEdgeWrapping; - log: fragmentLog, - prefix: prefixFragment + this.magFilter = options.magFilter !== undefined ? options.magFilter : THREE.LinearFilter; + this.minFilter = options.minFilter !== undefined ? options.minFilter : THREE.LinearMipMapLinearFilter; - } + this.anisotropy = options.anisotropy !== undefined ? options.anisotropy : 1; - }; + 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; - // clean up + this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; + this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true; - gl.deleteShader( glVertexShader ); - gl.deleteShader( glFragmentShader ); + this.generateMipmaps = true; - // set up caching for uniform locations + this.shareDepthFrom = options.shareDepthFrom !== undefined ? options.shareDepthFrom : null; - var cachedUniforms; +}; - this.getUniforms = function() { +THREE.WebGLRenderTarget.prototype = { - if ( cachedUniforms === undefined ) { + constructor: THREE.WebGLRenderTarget, - cachedUniforms = fetchUniformLocations( gl, program ); + setSize: function ( width, height ) { - } + if ( this.width !== width || this.height !== height ) { - return cachedUniforms; + this.width = width; + this.height = height; - }; + this.dispose(); - // set up caching for attribute locations + } - var cachedAttributes; + }, - this.getAttributes = function() { + clone: function () { - if ( cachedAttributes === undefined ) { + return new this.constructor().copy( this ); - cachedAttributes = fetchAttributeLocations( gl, program ); + }, - } + copy: function ( source ) { - return cachedAttributes; + this.width = source.width; + this.height = source.height; - }; + this.wrapS = source.wrapS; + this.wrapT = source.wrapT; - // free resource + this.magFilter = source.magFilter; + this.minFilter = source.minFilter; - this.destroy = function() { + this.anisotropy = source.anisotropy; - gl.deleteProgram( program ); - this.program = undefined; + this.offset.copy( source.offset ); + this.repeat.copy( source.repeat ); - }; + this.format = source.format; + this.type = source.type; - // DEPRECATED + this.depthBuffer = source.depthBuffer; + this.stencilBuffer = source.stencilBuffer; - Object.defineProperties( this, { + this.generateMipmaps = source.generateMipmaps; - uniforms: { - get: function() { + this.shareDepthFrom = source.shareDepthFrom; - console.warn( 'THREE.WebGLProgram: .uniforms is now .getUniforms().' ); - return this.getUniforms(); + return this; - } - }, + }, - attributes: { - get: function() { + dispose: function () { - console.warn( 'THREE.WebGLProgram: .attributes is now .getAttributes().' ); - return this.getAttributes(); + this.dispatchEvent( { type: 'dispose' } ); - } - } + } - } ); +}; + +THREE.EventDispatcher.prototype.apply( THREE.WebGLRenderTarget.prototype ); + +// File:src/renderers/WebGLRenderTargetCube.js + +/** + * @author alteredq / http://alteredqualia.com + */ +THREE.WebGLRenderTargetCube = function ( width, height, options ) { - // + THREE.WebGLRenderTarget.call( this, width, height, options ); - this.id = programIdCount ++; - this.code = code; - this.usedTimes = 1; - this.program = program; - this.vertexShader = glVertexShader; - this.fragmentShader = glFragmentShader; + this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5 - return this; +}; - }; +THREE.WebGLRenderTargetCube.prototype = Object.create( THREE.WebGLRenderTarget.prototype ); +THREE.WebGLRenderTargetCube.prototype.constructor = THREE.WebGLRenderTargetCube; -} )(); +// File:src/renderers/webgl/WebGLBufferRenderer.js -// File:src/renderers/webgl/WebGLPrograms.js +/** +* @author mrdoob / http://mrdoob.com/ +*/ -THREE.WebGLPrograms = function ( renderer, capabilities ) { +THREE.WebGLBufferRenderer = function ( _gl, extensions, _infoRender ) { - var programs = []; + var mode; - var shaderIDs = { - MeshDepthMaterial: 'depth', - MeshNormalMaterial: 'normal', - MeshBasicMaterial: 'basic', - MeshLambertMaterial: 'lambert', - MeshPhongMaterial: 'phong', - LineBasicMaterial: 'basic', - LineDashedMaterial: 'dashed', - PointsMaterial: 'points' - }; + function setMode( value ) { - var parameterNames = [ - "precision", "supportsVertexTextures", "map", "envMap", "envMapMode", - "lightMap", "aoMap", "emissiveMap", "bumpMap", "normalMap", "specularMap", - "alphaMap", "combine", "vertexColors", "fog", "useFog", "fogExp", - "flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning", - "maxBones", "useVertexTexture", "morphTargets", "morphNormals", - "maxMorphTargets", "maxMorphNormals", "maxDirLights", "maxPointLights", - "maxSpotLights", "maxHemiLights", "maxShadows", "shadowMapEnabled", - "shadowMapType", "shadowMapDebug", "alphaTest", "metal", "doubleSided", - "flipSided" - ]; + mode = value; + } - function allocateBones ( object ) { + function render( start, count ) { - if ( capabilities.floatVertexTextures && object && object.skeleton && object.skeleton.useVertexTexture ) { + _gl.drawArrays( mode, start, count ); - return 1024; + _infoRender.calls ++; + _infoRender.vertices += count; + if ( mode === _gl.TRIANGLES ) _infoRender.faces += count / 3; - } 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) + function renderInstances( geometry ) { - var nVertexUniforms = capabilities.maxVertexUniforms; - var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); + var extension = extensions.get( 'ANGLE_instanced_arrays' ); - var maxBones = nVertexMatrices; + if ( extension === null ) { - if ( object !== undefined && object instanceof THREE.SkinnedMesh ) { + console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); + return; - maxBones = Math.min( object.skeleton.bones.length, maxBones ); + } - if ( maxBones < object.skeleton.bones.length ) { + var position = geometry.attributes.position; - console.warn( 'WebGLRenderer: too many bones - ' + object.skeleton.bones.length + ', this GPU supports just ' + maxBones + ' (try OpenGL instead of ANGLE)' ); + if ( position instanceof THREE.InterleavedBufferAttribute ) { - } + extension.drawArraysInstancedANGLE( mode, 0, position.data.count, geometry.maxInstancedCount ); - } + } else { - return maxBones; + extension.drawArraysInstancedANGLE( mode, 0, position.count, geometry.maxInstancedCount ); } } - function allocateLights( lights ) { + this.setMode = setMode; + this.render = render; + this.renderInstances = renderInstances; - var dirLights = 0; - var pointLights = 0; - var spotLights = 0; - var hemiLights = 0; +}; - for ( var l = 0, ll = lights.length; l < ll; l ++ ) { +// File:src/renderers/webgl/WebGLIndexedBufferRenderer.js - var light = lights[ l ]; +/** +* @author mrdoob / http://mrdoob.com/ +*/ - if ( light.onlyShadow || light.visible === false ) continue; +THREE.WebGLIndexedBufferRenderer = function ( _gl, extensions, _infoRender ) { - if ( light instanceof THREE.DirectionalLight ) dirLights ++; - if ( light instanceof THREE.PointLight ) pointLights ++; - if ( light instanceof THREE.SpotLight ) spotLights ++; - if ( light instanceof THREE.HemisphereLight ) hemiLights ++; + var mode; - } + function setMode( value ) { - return { 'directional': dirLights, 'point': pointLights, 'spot': spotLights, 'hemi': hemiLights }; + mode = value; } - function allocateShadows( lights ) { + var type, size; - var maxShadows = 0; + function setIndex( index ) { - for ( var l = 0, ll = lights.length; l < ll; l ++ ) { + if ( index.array instanceof Uint32Array && extensions.get( 'OES_element_index_uint' ) ) { - var light = lights[ l ]; + type = _gl.UNSIGNED_INT; + size = 4; - if ( ! light.castShadow ) continue; + } else { - if ( light instanceof THREE.SpotLight ) maxShadows ++; - if ( light instanceof THREE.DirectionalLight ) maxShadows ++; + type = _gl.UNSIGNED_SHORT; + size = 2; } - return maxShadows; - } - this.getParameters = function ( material, lights, fog, object ) { + function render( start, count ) { - var shaderID = shaderIDs[ material.type ]; - // heuristics to create shader parameters according to lights in the scene - // (not to blow over maxLights budget) + _gl.drawElements( mode, count, type, start * size ); - var maxLightCount = allocateLights( lights ); - var maxShadows = allocateShadows( lights ); - var maxBones = allocateBones( object ); - var precision = renderer.getPrecision(); + _infoRender.calls ++; + _infoRender.vertices += count; + if ( mode === _gl.TRIANGLES ) _infoRender.faces += count / 3; - if ( material.precision !== null ) { + } - precision = capabilities.getMaxPrecision( material.precision ); + function renderInstances( geometry ) { - if ( precision !== material.precision ) { + var extension = extensions.get( 'ANGLE_instanced_arrays' ); - console.warn( 'THREE.WebGLRenderer.initMaterial:', material.precision, 'not supported, using', precision, 'instead.' ); + if ( extension === null ) { - } + console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); + return; } - var parameters = { - - shaderID: shaderID, + var index = geometry.index; - precision: precision, - supportsVertexTextures: capabilities.vertexTextures, + extension.drawElementsInstancedANGLE( mode, index.array.length, type, 0, geometry.maxInstancedCount ); - map: !! material.map, - envMap: !! material.envMap, - envMapMode: material.envMap && material.envMap.mapping, - lightMap: !! material.lightMap, - aoMap: !! material.aoMap, - emissiveMap: !! material.emissiveMap, - bumpMap: !! material.bumpMap, - normalMap: !! material.normalMap, - displacementMap: !! material.displacementMap, - specularMap: !! material.specularMap, - alphaMap: !! material.alphaMap, + } - combine: material.combine, + this.setMode = setMode; + this.setIndex = setIndex; + this.render = render; + this.renderInstances = renderInstances; - vertexColors: material.vertexColors, +}; - fog: fog, - useFog: material.fog, - fogExp: fog instanceof THREE.FogExp2, +// File:src/renderers/webgl/WebGLExtensions.js - flatShading: material.shading === THREE.FlatShading, +/** +* @author mrdoob / http://mrdoob.com/ +*/ - sizeAttenuation: material.sizeAttenuation, - logarithmicDepthBuffer: capabilities.logarithmicDepthBuffer, +THREE.WebGLExtensions = function ( gl ) { - skinning: material.skinning, - maxBones: maxBones, - useVertexTexture: capabilities.floatVertexTextures && object && object.skeleton && object.skeleton.useVertexTexture, + var extensions = {}; - morphTargets: material.morphTargets, - morphNormals: material.morphNormals, - maxMorphTargets: renderer.maxMorphTargets, - maxMorphNormals: renderer.maxMorphNormals, + this.get = function ( name ) { - maxDirLights: maxLightCount.directional, - maxPointLights: maxLightCount.point, - maxSpotLights: maxLightCount.spot, - maxHemiLights: maxLightCount.hemi, + if ( extensions[ name ] !== undefined ) { - maxShadows: maxShadows, - shadowMapEnabled: renderer.shadowMap.enabled && object.receiveShadow && maxShadows > 0, - shadowMapType: renderer.shadowMap.type, - shadowMapDebug: renderer.shadowMap.debug, + return extensions[ name ]; - alphaTest: material.alphaTest, - metal: material.metal, - doubleSided: material.side === THREE.DoubleSide, - flipSided: material.side === THREE.BackSide + } - }; + var extension; - return parameters; + switch ( name ) { - }; + 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; - this.getProgramCode = function ( material, parameters ) { + 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; - var chunks = []; + case 'WEBGL_compressed_texture_pvrtc': + extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ); + break; - if ( parameters.shaderID ) { + default: + extension = gl.getExtension( name ); - chunks.push( parameters.shaderID ); + } - } else { + if ( extension === null ) { - chunks.push( material.fragmentShader ); - chunks.push( material.vertexShader ); + console.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' ); } - if ( material.defines !== undefined ) { - - for ( var name in material.defines ) { + extensions[ name ] = extension; - chunks.push( name ); - chunks.push( material.defines[ name ] ); + return extension; - } + }; - } +}; - for ( var i = 0; i < parameterNames.length; i ++ ) { +// File:src/renderers/webgl/WebGLCapabilities.js - var parameterName = parameterNames[ i ]; - chunks.push( parameterName ); - chunks.push( parameters[ parameterName ] ); +THREE.WebGLCapabilities = function ( gl, extensions, parameters ) { - } + function getMaxPrecision( precision ) { - return chunks.join(); + if ( precision === 'highp' ) { - }; + if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.HIGH_FLOAT ).precision > 0 && + gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).precision > 0 ) { - this.acquireProgram = function ( material, parameters, code ) { + return 'highp'; - var program; + } - // Check if code has been already compiled - for ( var p = 0, pl = programs.length; p < pl; p ++ ) { + precision = 'mediump'; - var programInfo = programs[ p ]; + } - if ( programInfo.code === code ) { + if ( precision === 'mediump' ) { - program = programInfo; - ++ program.usedTimes; + if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).precision > 0 && + gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).precision > 0 ) { - break; + return 'mediump'; } } - if ( program === undefined ) { + return 'lowp'; - program = new THREE.WebGLProgram( renderer, code, material, parameters ); - programs.push( program ); + } - } + this.getMaxPrecision = getMaxPrecision; - return program; + this.precision = parameters.precision !== undefined ? parameters.precision : 'highp', + this.logarithmicDepthBuffer = parameters.logarithmicDepthBuffer !== undefined ? parameters.logarithmicDepthBuffer : false; - }; + this.maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS ); + this.maxVertexTextures = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ); + this.maxTextureSize = gl.getParameter( gl.MAX_TEXTURE_SIZE ); + this.maxCubemapSize = gl.getParameter( gl.MAX_CUBE_MAP_TEXTURE_SIZE ); - this.releaseProgram = function( program ) { + this.maxAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); + this.maxVertexUniforms = gl.getParameter( gl.MAX_VERTEX_UNIFORM_VECTORS ); + this.maxVaryings = gl.getParameter( gl.MAX_VARYING_VECTORS ); + this.maxFragmentUniforms = gl.getParameter( gl.MAX_FRAGMENT_UNIFORM_VECTORS ); - if ( -- program.usedTimes === 0 ) { + this.vertexTextures = this.maxVertexTextures > 0; + this.floatFragmentTextures = !! extensions.get( 'OES_texture_float' ); + this.floatVertexTextures = this.vertexTextures && this.floatFragmentTextures; - // Remove from unordered set - var i = programs.indexOf( program ); - programs[ i ] = programs[ programs.length - 1 ]; - programs.pop(); + var _maxPrecision = getMaxPrecision( this.precision ); - // Free WebGL resources - program.destroy(); + if ( _maxPrecision !== this.precision ) { - } + console.warn( 'THREE.WebGLRenderer:', this.precision, 'not supported, using', _maxPrecision, 'instead.' ); + this.precision = _maxPrecision; - }; + } - // Exposed for resource monitoring & error feedback via renderer.info: - this.programs = programs; + if ( this.logarithmicDepthBuffer ) { + + this.logarithmicDepthBuffer = !! extensions.get( 'EXT_frag_depth' ); + + } }; -// File:src/renderers/webgl/WebGLProperties.js +// File:src/renderers/webgl/WebGLGeometries.js /** -* @author fordacious / fordacious.github.io +* @author mrdoob / http://mrdoob.com/ */ -THREE.WebGLProperties = function () { +THREE.WebGLGeometries = function ( gl, properties, info ) { - var properties = {}; + var geometries = {}; - this.get = function ( object ) { + function get( object ) { - var uuid = object.uuid; - var map = properties[ uuid ]; + var geometry = object.geometry; - if ( map === undefined ) { + if ( geometries[ geometry.id ] !== undefined ) { - map = {}; - properties[ uuid ] = map; + return geometries[ geometry.id ]; } - return map; + geometry.addEventListener( 'dispose', onGeometryDispose ); - }; + var buffergeometry; - this.delete = function ( object ) { + if ( geometry instanceof THREE.BufferGeometry ) { - delete properties[ object.uuid ]; + buffergeometry = geometry; - }; + } else if ( geometry instanceof THREE.Geometry ) { - this.clear = function () { + if ( geometry._bufferGeometry === undefined ) { - properties = {}; + geometry._bufferGeometry = new THREE.BufferGeometry().setFromObject( object ); - }; + } -}; + buffergeometry = geometry._bufferGeometry; -// File:src/renderers/webgl/WebGLShader.js + } -THREE.WebGLShader = ( function () { + geometries[ geometry.id ] = buffergeometry; - var addLineNumbers = function ( string ) { + info.memory.geometries ++; - var lines = string.split( '\n' ); + return buffergeometry; - for ( var i = 0; i < lines.length; i ++ ) { + } - lines[ i ] = ( i + 1 ) + ': ' + lines[ i ]; + function onGeometryDispose( event ) { - } + var geometry = event.target; + var buffergeometry = geometries[ geometry.id ]; - return lines.join( '\n' ); + deleteAttributes( buffergeometry.attributes ); - }; + geometry.removeEventListener( 'dispose', onGeometryDispose ); - return function WebGLShader( gl, type, string ) { + delete geometries[ geometry.id ]; - var shader = gl.createShader( type ); + var property = properties.get( geometry ); + if ( property.wireframe ) deleteAttribute( property.wireframe ); - gl.shaderSource( shader, string ); - gl.compileShader( shader ); + info.memory.geometries --; - if ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === false ) { + } - console.error( 'THREE.WebGLShader: Shader couldn\'t compile.' ); + function getAttributeBuffer( attribute ) { + + if ( attribute instanceof THREE.InterleavedBufferAttribute ) { + + return properties.get( attribute.data ).__webglBuffer; } - if ( gl.getShaderInfoLog( shader ) !== '' ) { + return properties.get( attribute ).__webglBuffer; - console.warn( 'THREE.WebGLShader: gl.getShaderInfoLog()', type === gl.VERTEX_SHADER ? 'vertex' : 'fragment', gl.getShaderInfoLog( shader ), addLineNumbers( string ) ); + } - } + function deleteAttribute( attribute ) { - // --enable-privileged-webgl-extension - // console.log( type, gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) ); + var buffer = getAttributeBuffer( attribute ); - return shader; + if ( buffer !== undefined ) { - }; + gl.deleteBuffer( buffer ); + removeAttributeBuffer( attribute ); -} )(); + } -// File:src/renderers/webgl/WebGLShadowMap.js + } -/** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - */ + function deleteAttributes( attributes ) { -THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) { + for ( var name in attributes ) { - var _gl = _renderer.context, - _state = _renderer.state, - _frustum = new THREE.Frustum(), - _projScreenMatrix = new THREE.Matrix4(), + deleteAttribute( attributes[ name ] ); - _min = new THREE.Vector3(), - _max = new THREE.Vector3(), + } - _matrixPosition = new THREE.Vector3(), + } - _renderList = []; + function removeAttributeBuffer( attribute ) { - // init + if ( attribute instanceof THREE.InterleavedBufferAttribute ) { - var depthShader = THREE.ShaderLib[ "depthRGBA" ]; - var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms ); + properties.delete( attribute.data ); - var _depthMaterial = new THREE.ShaderMaterial( { - uniforms: depthUniforms, - vertexShader: depthShader.vertexShader, - fragmentShader: depthShader.fragmentShader - } ); + } else { - var _depthMaterialMorph = new THREE.ShaderMaterial( { - uniforms: depthUniforms, - vertexShader: depthShader.vertexShader, - fragmentShader: depthShader.fragmentShader, - morphTargets: true - } ); + properties.delete( attribute ); - var _depthMaterialSkin = new THREE.ShaderMaterial( { - uniforms: depthUniforms, - vertexShader: depthShader.vertexShader, - fragmentShader: depthShader.fragmentShader, - skinning: true - } ); + } - var _depthMaterialMorphSkin = new THREE.ShaderMaterial( { - uniforms: depthUniforms, - vertexShader: depthShader.vertexShader, - fragmentShader: depthShader.fragmentShader, - morphTargets: true, - skinning: true - } ); + } - _depthMaterial._shadowPass = true; - _depthMaterialMorph._shadowPass = true; - _depthMaterialSkin._shadowPass = true; - _depthMaterialMorphSkin._shadowPass = true; + this.get = get; - // +}; - var scope = this; +// File:src/renderers/webgl/WebGLObjects.js - this.enabled = false; +/** +* @author mrdoob / http://mrdoob.com/ +*/ - this.autoUpdate = true; - this.needsUpdate = false; +THREE.WebGLObjects = function ( gl, properties, info ) { - this.type = THREE.PCFShadowMap; - this.cullFace = THREE.CullFaceFront; + var geometries = new THREE.WebGLGeometries( gl, properties, info ); - this.render = function ( scene, camera ) { + // - if ( scope.enabled === false ) return; - if ( scope.autoUpdate === false && scope.needsUpdate === false ) return; + function update( object ) { - // set GL state for depth map + // TODO: Avoid updating twice (when using shadowMap). Maybe add frame counter. - _gl.clearColor( 1, 1, 1, 1 ); - _state.disable( _gl.BLEND ); + var geometry = geometries.get( object ); - _state.enable( _gl.CULL_FACE ); - _gl.frontFace( _gl.CCW ); + if ( object.geometry instanceof THREE.Geometry ) { - if ( scope.cullFace === THREE.CullFaceFront ) { + geometry.updateFromObject( object ); - _gl.cullFace( _gl.FRONT ); + } - } else { + var index = geometry.index; + var attributes = geometry.attributes; - _gl.cullFace( _gl.BACK ); + if ( index !== null ) { + + updateAttribute( index, gl.ELEMENT_ARRAY_BUFFER ); } - _state.setDepthTest( true ); + for ( var name in attributes ) { + + updateAttribute( attributes[ name ], gl.ARRAY_BUFFER ); - // render depth map + } - for ( var i = 0, il = _lights.length; i < il; i ++ ) { + // morph targets - var light = _lights[ i ]; + var morphAttributes = geometry.morphAttributes; - if ( ! light.castShadow ) continue; + for ( var name in morphAttributes ) { - if ( ! light.shadowMap ) { + var array = morphAttributes[ name ]; - var shadowFilter = THREE.LinearFilter; + for ( var i = 0, l = array.length; i < l; i ++ ) { - if ( scope.type === THREE.PCFSoftShadowMap ) { + updateAttribute( array[ i ], gl.ARRAY_BUFFER ); - shadowFilter = THREE.NearestFilter; + } - } + } - var pars = { minFilter: shadowFilter, magFilter: shadowFilter, format: THREE.RGBAFormat }; + return geometry; - light.shadowMap = new THREE.WebGLRenderTarget( light.shadowMapWidth, light.shadowMapHeight, pars ); - light.shadowMapSize = new THREE.Vector2( light.shadowMapWidth, light.shadowMapHeight ); + } - light.shadowMatrix = new THREE.Matrix4(); + function updateAttribute( attribute, bufferType ) { - } + var data = ( attribute instanceof THREE.InterleavedBufferAttribute ) ? attribute.data : attribute; - if ( ! light.shadowCamera ) { + var attributeProperties = properties.get( data ); - if ( light instanceof THREE.SpotLight ) { + if ( attributeProperties.__webglBuffer === undefined ) { - light.shadowCamera = new THREE.PerspectiveCamera( light.shadowCameraFov, light.shadowMapWidth / light.shadowMapHeight, light.shadowCameraNear, light.shadowCameraFar ); + createBuffer( attributeProperties, data, bufferType ); - } else if ( light instanceof THREE.DirectionalLight ) { + } else if ( attributeProperties.version !== data.version ) { - light.shadowCamera = new THREE.OrthographicCamera( light.shadowCameraLeft, light.shadowCameraRight, light.shadowCameraTop, light.shadowCameraBottom, light.shadowCameraNear, light.shadowCameraFar ); + updateBuffer( attributeProperties, data, bufferType ); - } else { + } - console.error( "THREE.ShadowMapPlugin: Unsupported light type for shadow", light ); - continue; + } - } + function createBuffer( attributeProperties, data, bufferType ) { - scene.add( light.shadowCamera ); + attributeProperties.__webglBuffer = gl.createBuffer(); + gl.bindBuffer( bufferType, attributeProperties.__webglBuffer ); - if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); + var usage = data.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW; - } + gl.bufferData( bufferType, data.array, usage ); - if ( light.shadowCameraVisible && ! light.cameraHelper ) { + attributeProperties.version = data.version; - light.cameraHelper = new THREE.CameraHelper( light.shadowCamera ); - scene.add( light.cameraHelper ); + } - } + function updateBuffer( attributeProperties, data, bufferType ) { - var shadowMap = light.shadowMap; - var shadowMatrix = light.shadowMatrix; - var shadowCamera = light.shadowCamera; + gl.bindBuffer( bufferType, attributeProperties.__webglBuffer ); - // + if ( data.dynamic === false || data.updateRange.count === - 1 ) { - shadowCamera.position.setFromMatrixPosition( light.matrixWorld ); - _matrixPosition.setFromMatrixPosition( light.target.matrixWorld ); - shadowCamera.lookAt( _matrixPosition ); - shadowCamera.updateMatrixWorld(); + // Not using update ranges - shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld ); + gl.bufferSubData( bufferType, 0, data.array ); - // + } else if ( data.updateRange.count === 0 ) { - if ( light.cameraHelper ) light.cameraHelper.visible = light.shadowCameraVisible; - if ( light.shadowCameraVisible ) light.cameraHelper.update(); + console.error( 'THREE.WebGLObjects.updateBuffer: dynamic THREE.BufferAttribute marked as needsUpdate but updateRange.count is 0, ensure you are using set methods or updating manually.' ); - // compute shadow matrix + } else { - 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 - ); + gl.bufferSubData( bufferType, data.updateRange.offset * data.array.BYTES_PER_ELEMENT, + data.array.subarray( data.updateRange.offset, data.updateRange.offset + data.updateRange.count ) ); - shadowMatrix.multiply( shadowCamera.projectionMatrix ); - shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); + data.updateRange.count = 0; // reset range - // update camera matrices and frustum + } - _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); - _frustum.setFromMatrix( _projScreenMatrix ); + attributeProperties.version = data.version; - // render shadow map + } - _renderer.setRenderTarget( shadowMap ); - _renderer.clear(); + function getAttributeBuffer( attribute ) { - // set object matrices & frustum culling + if ( attribute instanceof THREE.InterleavedBufferAttribute ) { - _renderList.length = 0; + return properties.get( attribute.data ).__webglBuffer; - projectObject( scene, shadowCamera ); + } + return properties.get( attribute ).__webglBuffer; - // render regular objects + } - for ( var j = 0, jl = _renderList.length; j < jl; j ++ ) { + function getWireframeAttribute( geometry ) { - var object = _renderList[ j ]; - var geometry = _objects.update( object ); - var material = object.material; + var property = properties.get( geometry ); - if ( material instanceof THREE.MeshFaceMaterial ) { + if ( property.wireframe !== undefined ) { - var groups = geometry.groups; - var materials = material.materials; + return property.wireframe; - for ( var k = 0, kl = groups.length; k < kl; k ++ ) { + } - var group = groups[ k ]; - var groupMaterial = materials[ group.materialIndex ]; + var indices = []; - if ( groupMaterial.visible === true ) { + var index = geometry.index; + var attributes = geometry.attributes; + var position = attributes.position; - _renderer.renderBufferDirect( shadowCamera, _lights, null, geometry, getDepthMaterial( object, groupMaterial ), object, group ); + // console.time( 'wireframe' ); - } + if ( index !== null ) { - } + var edges = {}; + var array = index.array; - } else { + for ( var i = 0, l = array.length; i < l; i += 3 ) { - _renderer.renderBufferDirect( shadowCamera, _lights, null, geometry, getDepthMaterial( object, material ), object, null ); + var a = array[ i + 0 ]; + var b = array[ i + 1 ]; + var c = array[ i + 2 ]; - } + if ( checkEdge( edges, a, b ) ) indices.push( a, b ); + if ( checkEdge( edges, b, c ) ) indices.push( b, c ); + if ( checkEdge( edges, c, a ) ) indices.push( c, a ); } - } + } else { - // restore GL state + var array = attributes.position.array; - var clearColor = _renderer.getClearColor(), - clearAlpha = _renderer.getClearAlpha(); + for ( var i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) { - _renderer.setClearColor( clearColor, clearAlpha ); - _state.enable( _gl.BLEND ); + var a = i + 0; + var b = i + 1; + var c = i + 2; - if ( scope.cullFace === THREE.CullFaceFront ) { + indices.push( a, b, b, c, c, a ); - _gl.cullFace( _gl.BACK ); + } } - _renderer.resetGLState(); + // console.timeEnd( 'wireframe' ); - scope.needsUpdate = false; + var TypeArray = position.count > 65535 ? Uint32Array : Uint16Array; + var attribute = new THREE.BufferAttribute( new TypeArray( indices ), 1 ); - }; + updateAttribute( attribute, gl.ELEMENT_ARRAY_BUFFER ); - function getDepthMaterial( object, material ) { + property.wireframe = attribute; - var geometry = object.geometry; + return attribute; - var useMorphing = geometry.morphTargets !== undefined && geometry.morphTargets.length > 0 && material.morphTargets; - var useSkinning = object instanceof THREE.SkinnedMesh && material.skinning; + } - var depthMaterial; + function checkEdge( edges, a, b ) { - if ( object.customDepthMaterial ) { + if ( a > b ) { - depthMaterial = object.customDepthMaterial; + var tmp = a; + a = b; + b = tmp; - } else if ( useSkinning ) { + } - depthMaterial = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin; + var list = edges[ a ]; - } else if ( useMorphing ) { + if ( list === undefined ) { - depthMaterial = _depthMaterialMorph; + edges[ a ] = [ b ]; + return true; - } else { + } else if ( list.indexOf( b ) === -1 ) { - depthMaterial = _depthMaterial; + list.push( b ); + return true; } - depthMaterial.visible = material.visible; - depthMaterial.wireframe = material.wireframe; - depthMaterial.wireframeLinewidth = material.wireframeLinewidth; - - return depthMaterial; + return false; } - function projectObject( object, camera ) { - - if ( object.visible === false ) return; + this.getAttributeBuffer = getAttributeBuffer; + this.getWireframeAttribute = getWireframeAttribute; - if ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Points ) { + this.update = update; - if ( object.castShadow && ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) ) { +}; - var material = object.material; +// File:src/renderers/webgl/WebGLProgram.js - if ( material.visible === true ) { +THREE.WebGLProgram = ( function () { - object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); - _renderList.push( object ); + var programIdCount = 0; - } + function generateDefines( defines ) { - } + var chunks = []; - } + for ( var name in defines ) { - var children = object.children; + var value = defines[ name ]; - for ( var i = 0, l = children.length; i < l; i ++ ) { + if ( value === false ) continue; - projectObject( children[ i ], camera ); + chunks.push( '#define ' + name + ' ' + value ); } + return chunks.join( '\n' ); + } -}; + function fetchUniformLocations( gl, program, identifiers ) { -// File:src/renderers/webgl/WebGLState.js + var uniforms = {}; -/** -* @author mrdoob / http://mrdoob.com/ -*/ + var n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS ); -THREE.WebGLState = function ( gl, extensions, paramThreeToGL ) { + for ( var i = 0; i < n; i ++ ) { - var _this = this; + var info = gl.getActiveUniform( program, i ); + var name = info.name; + var location = gl.getUniformLocation( program, name ); - var newAttributes = new Uint8Array( 16 ); - var enabledAttributes = new Uint8Array( 16 ); + // console.log("THREE.WebGLProgram: ACTIVE UNIFORM:", name); - var capabilities = {}; + var suffixPos = name.lastIndexOf( '[0]' ); + if ( suffixPos !== - 1 && suffixPos === name.length - 3 ) { - var compressedTextureFormats = null; + uniforms[ name.substr( 0, suffixPos ) ] = location; - var currentBlending = null; - var currentBlendEquation = null; - var currentBlendSrc = null; - var currentBlendDst = null; - var currentBlendEquationAlpha = null; - var currentBlendSrcAlpha = null; - var currentBlendDstAlpha = null; + } - var currentDepthFunc = null; - var currentDepthWrite = null; + uniforms[ name ] = location; - var currentColorWrite = null; + } - var currentFlipSided = null; + return uniforms; - var currentLineWidth = null; + } - var currentPolygonOffsetFactor = null; - var currentPolygonOffsetUnits = null; + function fetchAttributeLocations( gl, program, identifiers ) { - var maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS ); + var attributes = {}; - var currentTextureSlot = undefined; - var currentBoundTextures = {}; + var n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES ); - this.init = function () { + for ( var i = 0; i < n; i ++ ) { - gl.clearColor( 0, 0, 0, 1 ); - gl.clearDepth( 1 ); - gl.clearStencil( 0 ); + var info = gl.getActiveAttrib( program, i ); + var name = info.name; - this.enable( gl.DEPTH_TEST ); - gl.depthFunc( gl.LEQUAL ); + // console.log("THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name, i ); - gl.frontFace( gl.CCW ); - gl.cullFace( gl.BACK ); - this.enable( gl.CULL_FACE ); + attributes[ name ] = gl.getAttribLocation( program, name ); - this.enable( gl.BLEND ); - gl.blendEquation( gl.FUNC_ADD ); - gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA ); + } - }; + return attributes; - this.initAttributes = function () { + } - for ( var i = 0, l = newAttributes.length; i < l; i ++ ) { + function filterEmptyLine( string ) { - newAttributes[ i ] = 0; + return string !== ''; - } + } - }; + return function WebGLProgram( renderer, code, material, parameters ) { - this.enableAttribute = function ( attribute ) { + var gl = renderer.context; - newAttributes[ attribute ] = 1; + var defines = material.defines; - if ( enabledAttributes[ attribute ] === 0 ) { + var vertexShader = material.__webglShader.vertexShader; + var fragmentShader = material.__webglShader.fragmentShader; - gl.enableVertexAttribArray( attribute ); - enabledAttributes[ attribute ] = 1; + var shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC'; + + if ( parameters.shadowMapType === THREE.PCFShadowMap ) { + + shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF'; + + } else if ( parameters.shadowMapType === THREE.PCFSoftShadowMap ) { + + shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT'; } - }; + var envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; + var envMapModeDefine = 'ENVMAP_MODE_REFLECTION'; + var envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; - this.disableUnusedAttributes = function () { + if ( parameters.envMap ) { - for ( var i = 0, l = enabledAttributes.length; i < l; i ++ ) { + switch ( material.envMap.mapping ) { - if ( enabledAttributes[ i ] !== newAttributes[ i ] ) { + case THREE.CubeReflectionMapping: + case THREE.CubeRefractionMapping: + envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; + break; - gl.disableVertexAttribArray( i ); - enabledAttributes[ i ] = 0; + case THREE.EquirectangularReflectionMapping: + case THREE.EquirectangularRefractionMapping: + envMapTypeDefine = 'ENVMAP_TYPE_EQUIREC'; + break; + + case THREE.SphericalReflectionMapping: + envMapTypeDefine = 'ENVMAP_TYPE_SPHERE'; + break; } - } + switch ( material.envMap.mapping ) { - }; + case THREE.CubeRefractionMapping: + case THREE.EquirectangularRefractionMapping: + envMapModeDefine = 'ENVMAP_MODE_REFRACTION'; + break; - this.enable = function ( id ) { + } - if ( capabilities[ id ] !== true ) { + switch ( material.combine ) { - gl.enable( id ); - capabilities[ id ] = true; + case THREE.MultiplyOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; + break; - } + case THREE.MixOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_MIX'; + break; - }; + case THREE.AddOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_ADD'; + break; - this.disable = function ( id ) { + } - if ( capabilities[ id ] !== false ) { + } - gl.disable( id ); - capabilities[ id ] = false; + var gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0; - } + // console.log( 'building new program ' ); - }; + // - this.getCompressedTextureFormats = function () { + var customDefines = generateDefines( defines ); - if ( compressedTextureFormats === null ) { + // - compressedTextureFormats = []; + var program = gl.createProgram(); - if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) || - extensions.get( 'WEBGL_compressed_texture_s3tc' ) ) { + var prefixVertex, prefixFragment; - var formats = gl.getParameter( gl.COMPRESSED_TEXTURE_FORMATS ); + if ( material instanceof THREE.RawShaderMaterial ) { - for ( var i = 0; i < formats.length; i ++ ) { + prefixVertex = ''; + prefixFragment = ''; - compressedTextureFormats.push( formats[ i ] ); + } else { - } + prefixVertex = [ - } + 'precision ' + parameters.precision + ' float;', + 'precision ' + parameters.precision + ' int;', - } + '#define SHADER_NAME ' + material.__webglShader.name, - return compressedTextureFormats; + customDefines, - }; + parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '', - this.setBlending = function ( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha ) { + renderer.gammaInput ? '#define GAMMA_INPUT' : '', + renderer.gammaOutput ? '#define GAMMA_OUTPUT' : '', + '#define GAMMA_FACTOR ' + gammaFactorDefine, - if ( blending !== currentBlending ) { + '#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 ( blending === THREE.NoBlending ) { + '#define MAX_SHADOWS ' + parameters.maxShadows, - this.disable( gl.BLEND ); + '#define MAX_BONES ' + parameters.maxBones, - } else if ( blending === THREE.AdditiveBlending ) { + parameters.map ? '#define USE_MAP' : '', + parameters.envMap ? '#define USE_ENVMAP' : '', + parameters.envMap ? '#define ' + envMapModeDefine : '', + parameters.lightMap ? '#define USE_LIGHTMAP' : '', + parameters.aoMap ? '#define USE_AOMAP' : '', + parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', + parameters.bumpMap ? '#define USE_BUMPMAP' : '', + parameters.normalMap ? '#define USE_NORMALMAP' : '', + parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '', + parameters.specularMap ? '#define USE_SPECULARMAP' : '', + parameters.alphaMap ? '#define USE_ALPHAMAP' : '', + parameters.vertexColors ? '#define USE_COLOR' : '', - this.enable( gl.BLEND ); - gl.blendEquation( gl.FUNC_ADD ); - gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); + parameters.flatShading ? '#define FLAT_SHADED' : '', - } else if ( blending === THREE.SubtractiveBlending ) { + parameters.skinning ? '#define USE_SKINNING' : '', + parameters.useVertexTexture ? '#define BONE_TEXTURE' : '', - // TODO: Find blendFuncSeparate() combination + parameters.morphTargets ? '#define USE_MORPHTARGETS' : '', + parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '', + parameters.doubleSided ? '#define DOUBLE_SIDED' : '', + parameters.flipSided ? '#define FLIP_SIDED' : '', - this.enable( gl.BLEND ); - gl.blendEquation( gl.FUNC_ADD ); - gl.blendFunc( gl.ZERO, gl.ONE_MINUS_SRC_COLOR ); + parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', + parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', + parameters.shadowMapDebug ? '#define SHADOWMAP_DEBUG' : '', - } else if ( blending === THREE.MultiplyBlending ) { + parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '', - // TODO: Find blendFuncSeparate() combination + parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', + parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '', - this.enable( gl.BLEND ); - gl.blendEquation( gl.FUNC_ADD ); - gl.blendFunc( gl.ZERO, gl.SRC_COLOR ); - } else if ( blending === THREE.CustomBlending ) { + 'uniform mat4 modelMatrix;', + 'uniform mat4 modelViewMatrix;', + 'uniform mat4 projectionMatrix;', + 'uniform mat4 viewMatrix;', + 'uniform mat3 normalMatrix;', + 'uniform vec3 cameraPosition;', - this.enable( gl.BLEND ); + 'attribute vec3 position;', + 'attribute vec3 normal;', + 'attribute vec2 uv;', - } else { + '#ifdef USE_COLOR', - this.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 ); + ' attribute vec3 color;', - } + '#endif', - currentBlending = blending; + '#ifdef USE_MORPHTARGETS', - } + ' attribute vec3 morphTarget0;', + ' attribute vec3 morphTarget1;', + ' attribute vec3 morphTarget2;', + ' attribute vec3 morphTarget3;', - if ( blending === THREE.CustomBlending ) { + ' #ifdef USE_MORPHNORMALS', - blendEquationAlpha = blendEquationAlpha || blendEquation; - blendSrcAlpha = blendSrcAlpha || blendSrc; - blendDstAlpha = blendDstAlpha || blendDst; + ' attribute vec3 morphNormal0;', + ' attribute vec3 morphNormal1;', + ' attribute vec3 morphNormal2;', + ' attribute vec3 morphNormal3;', - if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { + ' #else', - gl.blendEquationSeparate( paramThreeToGL( blendEquation ), paramThreeToGL( blendEquationAlpha ) ); + ' attribute vec3 morphTarget4;', + ' attribute vec3 morphTarget5;', + ' attribute vec3 morphTarget6;', + ' attribute vec3 morphTarget7;', - currentBlendEquation = blendEquation; - currentBlendEquationAlpha = blendEquationAlpha; + ' #endif', + + '#endif', - } + '#ifdef USE_SKINNING', - if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { + ' attribute vec4 skinIndex;', + ' attribute vec4 skinWeight;', - gl.blendFuncSeparate( paramThreeToGL( blendSrc ), paramThreeToGL( blendDst ), paramThreeToGL( blendSrcAlpha ), paramThreeToGL( blendDstAlpha ) ); + '#endif', - currentBlendSrc = blendSrc; - currentBlendDst = blendDst; - currentBlendSrcAlpha = blendSrcAlpha; - currentBlendDstAlpha = blendDstAlpha; + '\n' - } + ].filter( filterEmptyLine ).join( '\n' ); - } else { + prefixFragment = [ - currentBlendEquation = null; - currentBlendSrc = null; - currentBlendDst = null; - currentBlendEquationAlpha = null; - currentBlendSrcAlpha = null; - currentBlendDstAlpha = null; + parameters.bumpMap || parameters.normalMap || parameters.flatShading || material.derivatives ? '#extension GL_OES_standard_derivatives : enable' : '', + parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#extension GL_EXT_frag_depth : enable' : '', - } + 'precision ' + parameters.precision + ' float;', + 'precision ' + parameters.precision + ' int;', - }; + '#define SHADER_NAME ' + material.__webglShader.name, - this.setDepthFunc = function ( depthFunc ) { + customDefines, - if ( currentDepthFunc !== depthFunc ) { + '#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 ( depthFunc ) { + '#define MAX_SHADOWS ' + parameters.maxShadows, - switch ( depthFunc ) { + parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest : '', - case THREE.NeverDepth: + renderer.gammaInput ? '#define GAMMA_INPUT' : '', + renderer.gammaOutput ? '#define GAMMA_OUTPUT' : '', + '#define GAMMA_FACTOR ' + gammaFactorDefine, - gl.depthFunc( gl.NEVER ); - break; + ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '', + ( parameters.useFog && parameters.fogExp ) ? '#define FOG_EXP2' : '', - case THREE.AlwaysDepth: + parameters.map ? '#define USE_MAP' : '', + parameters.envMap ? '#define USE_ENVMAP' : '', + parameters.envMap ? '#define ' + envMapTypeDefine : '', + parameters.envMap ? '#define ' + envMapModeDefine : '', + parameters.envMap ? '#define ' + envMapBlendingDefine : '', + parameters.lightMap ? '#define USE_LIGHTMAP' : '', + parameters.aoMap ? '#define USE_AOMAP' : '', + parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '', + 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.depthFunc( gl.ALWAYS ); - break; + parameters.flatShading ? '#define FLAT_SHADED' : '', - case THREE.LessDepth: + parameters.metal ? '#define METAL' : '', + parameters.doubleSided ? '#define DOUBLE_SIDED' : '', + parameters.flipSided ? '#define FLIP_SIDED' : '', - gl.depthFunc( gl.LESS ); - break; + parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', + parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', + parameters.shadowMapDebug ? '#define SHADOWMAP_DEBUG' : '', - case THREE.LessEqualDepth: + parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', + parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '', - gl.depthFunc( gl.LEQUAL ); - break; + 'uniform mat4 viewMatrix;', + 'uniform vec3 cameraPosition;', - case THREE.EqualDepth: + '\n' - gl.depthFunc( gl.EQUAL ); - break; + ].filter( filterEmptyLine ).join( '\n' ); - case THREE.GreaterEqualDepth: + } - gl.depthFunc( gl.GEQUAL ); - break; + var vertexGlsl = prefixVertex + vertexShader; + var fragmentGlsl = prefixFragment + fragmentShader; - case THREE.GreaterDepth: + var glVertexShader = THREE.WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl ); + var glFragmentShader = THREE.WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl ); - gl.depthFunc( gl.GREATER ); - break; + gl.attachShader( program, glVertexShader ); + gl.attachShader( program, glFragmentShader ); - case THREE.NotEqualDepth: + // Force a particular attribute to index 0. - gl.depthFunc( gl.NOTEQUAL ); - break; + if ( material.index0AttributeName !== undefined ) { - default: + gl.bindAttribLocation( program, 0, material.index0AttributeName ); - gl.depthFunc( gl.LEQUAL ); + } else if ( parameters.morphTargets === true ) { - } + // programs with morphTargets displace position out of attribute 0 + gl.bindAttribLocation( program, 0, 'position' ); - } else { + } - gl.depthFunc( gl.LEQUAL ); + gl.linkProgram( program ); - } + var programLog = gl.getProgramInfoLog( program ); + var vertexLog = gl.getShaderInfoLog( glVertexShader ); + var fragmentLog = gl.getShaderInfoLog( glFragmentShader ); - currentDepthFunc = depthFunc; + var runnable = true; + var haveDiagnostics = true; - } + if ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) { - }; + runnable = false; - this.setDepthTest = function ( depthTest ) { + console.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), 'gl.VALIDATE_STATUS', gl.getProgramParameter( program, gl.VALIDATE_STATUS ), 'gl.getProgramInfoLog', programLog, vertexLog, fragmentLog ); - if ( depthTest ) { + } else if ( programLog !== '' ) { - this.enable( gl.DEPTH_TEST ); + console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', programLog ); - } else { + } else if ( vertexLog === '' || fragmentLog === '' ) { - this.disable( gl.DEPTH_TEST ); + haveDiagnostics = false; } - }; - - this.setDepthWrite = function ( depthWrite ) { + if ( haveDiagnostics ) { - if ( currentDepthWrite !== depthWrite ) { + this.diagnostics = { - gl.depthMask( depthWrite ); - currentDepthWrite = depthWrite; + runnable: runnable, + material: material, - } + programLog: programLog, - }; + vertexShader: { - this.setColorWrite = function ( colorWrite ) { + log: vertexLog, + prefix: prefixVertex - if ( currentColorWrite !== colorWrite ) { + }, - gl.colorMask( colorWrite, colorWrite, colorWrite, colorWrite ); - currentColorWrite = colorWrite; + fragmentShader: { - } + log: fragmentLog, + prefix: prefixFragment - }; + } - this.setFlipSided = function ( flipSided ) { + }; - if ( currentFlipSided !== flipSided ) { + } - if ( flipSided ) { + // clean up - gl.frontFace( gl.CW ); + gl.deleteShader( glVertexShader ); + gl.deleteShader( glFragmentShader ); - } else { + // set up caching for uniform locations - gl.frontFace( gl.CCW ); + var cachedUniforms; - } + this.getUniforms = function() { - currentFlipSided = flipSided; + if ( cachedUniforms === undefined ) { - } + cachedUniforms = fetchUniformLocations( gl, program ); - }; + } - this.setLineWidth = function ( width ) { + return cachedUniforms; - if ( width !== currentLineWidth ) { + }; - gl.lineWidth( width ); + // set up caching for attribute locations - currentLineWidth = width; + var cachedAttributes; - } + this.getAttributes = function() { - }; + if ( cachedAttributes === undefined ) { - this.setPolygonOffset = function ( polygonOffset, factor, units ) { + cachedAttributes = fetchAttributeLocations( gl, program ); - if ( polygonOffset ) { + } - this.enable( gl.POLYGON_OFFSET_FILL ); + return cachedAttributes; - } else { + }; - this.disable( gl.POLYGON_OFFSET_FILL ); + // free resource - } + this.destroy = function() { - if ( polygonOffset && ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) ) { + gl.deleteProgram( program ); + this.program = undefined; - gl.polygonOffset( factor, units ); + }; - currentPolygonOffsetFactor = factor; - currentPolygonOffsetUnits = units; + // DEPRECATED - } + Object.defineProperties( this, { - }; + uniforms: { + get: function() { - this.setScissorTest = function ( scissorTest ) { + console.warn( 'THREE.WebGLProgram: .uniforms is now .getUniforms().' ); + return this.getUniforms(); - if ( scissorTest ) { + } + }, - this.enable( gl.SCISSOR_TEST ); + attributes: { + get: function() { - } else { + console.warn( 'THREE.WebGLProgram: .attributes is now .getAttributes().' ); + return this.getAttributes(); - this.disable( gl.SCISSOR_TEST ); + } + } - } + } ); - }; - // texture + // - this.activeTexture = function ( webglSlot ) { + this.id = programIdCount ++; + this.code = code; + this.usedTimes = 1; + this.program = program; + this.vertexShader = glVertexShader; + this.fragmentShader = glFragmentShader; - if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1; + return this; - if ( currentTextureSlot !== webglSlot ) { + }; - gl.activeTexture( webglSlot ); - currentTextureSlot = webglSlot; +} )(); - } +// File:src/renderers/webgl/WebGLPrograms.js - } +THREE.WebGLPrograms = function ( renderer, capabilities ) { - this.bindTexture = function ( webglType, webglTexture ) { + var programs = []; - if ( currentTextureSlot === undefined ) { + var shaderIDs = { + MeshDepthMaterial: 'depth', + MeshNormalMaterial: 'normal', + MeshBasicMaterial: 'basic', + MeshLambertMaterial: 'lambert', + MeshPhongMaterial: 'phong', + LineBasicMaterial: 'basic', + LineDashedMaterial: 'dashed', + PointsMaterial: 'points' + }; - _this.activeTexture(); + var parameterNames = [ + "precision", "supportsVertexTextures", "map", "envMap", "envMapMode", + "lightMap", "aoMap", "emissiveMap", "bumpMap", "normalMap", "specularMap", + "alphaMap", "combine", "vertexColors", "fog", "useFog", "fogExp", + "flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning", + "maxBones", "useVertexTexture", "morphTargets", "morphNormals", + "maxMorphTargets", "maxMorphNormals", "maxDirLights", "maxPointLights", + "maxSpotLights", "maxHemiLights", "maxShadows", "shadowMapEnabled", + "shadowMapType", "shadowMapDebug", "alphaTest", "metal", "doubleSided", + "flipSided" + ]; - } - var boundTexture = currentBoundTextures[ currentTextureSlot ]; + function allocateBones ( object ) { - if ( boundTexture === undefined ) { + if ( capabilities.floatVertexTextures && object && object.skeleton && object.skeleton.useVertexTexture ) { - boundTexture = { type: undefined, texture: undefined }; - currentBoundTextures[ currentTextureSlot ] = boundTexture; + return 1024; - } + } else { - if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { + // 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) - gl.bindTexture( webglType, webglTexture ); + var nVertexUniforms = capabilities.maxVertexUniforms; + var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); - boundTexture.type = webglType; - boundTexture.texture = webglTexture; + var maxBones = nVertexMatrices; - } + if ( object !== undefined && object instanceof THREE.SkinnedMesh ) { - }; + maxBones = Math.min( object.skeleton.bones.length, maxBones ); - this.compressedTexImage2D = function () { + if ( maxBones < object.skeleton.bones.length ) { - try { + console.warn( 'WebGLRenderer: too many bones - ' + object.skeleton.bones.length + ', this GPU supports just ' + maxBones + ' (try OpenGL instead of ANGLE)' ); - gl.compressedTexImage2D.apply( gl, arguments ); + } - } catch ( error ) { + } - console.error( error ); + return maxBones; } - }; + } - this.texImage2D = function () { + function allocateLights( lights ) { - try { + var dirLights = 0; + var pointLights = 0; + var spotLights = 0; + var hemiLights = 0; - gl.texImage2D.apply( gl, arguments ); + for ( var l = 0, ll = lights.length; l < ll; l ++ ) { - } catch ( error ) { + var light = lights[ l ]; - console.error( error ); + if ( light.onlyShadow || light.visible === false ) continue; - } + if ( light instanceof THREE.DirectionalLight ) dirLights ++; + if ( light instanceof THREE.PointLight ) pointLights ++; + if ( light instanceof THREE.SpotLight ) spotLights ++; + if ( light instanceof THREE.HemisphereLight ) hemiLights ++; - }; + } - // + return { 'directional': dirLights, 'point': pointLights, 'spot': spotLights, 'hemi': hemiLights }; - this.reset = function () { + } - for ( var i = 0; i < enabledAttributes.length; i ++ ) { + function allocateShadows( lights ) { - if ( enabledAttributes[ i ] === 1 ) { + var maxShadows = 0; - gl.disableVertexAttribArray( i ); - enabledAttributes[ i ] = 0; + for ( var l = 0, ll = lights.length; l < ll; l ++ ) { - } + var light = lights[ l ]; - } + if ( ! light.castShadow ) continue; - capabilities = {}; + if ( light instanceof THREE.SpotLight ) maxShadows ++; + if ( light instanceof THREE.DirectionalLight ) maxShadows ++; - compressedTextureFormats = null; + } - currentBlending = null; + return maxShadows; - currentDepthWrite = null; - currentColorWrite = null; + } - currentFlipSided = null; + this.getParameters = function ( material, lights, fog, object ) { - }; + var shaderID = shaderIDs[ material.type ]; + // heuristics to create shader parameters according to lights in the scene + // (not to blow over maxLights budget) -}; + var maxLightCount = allocateLights( lights ); + var maxShadows = allocateShadows( lights ); + var maxBones = allocateBones( object ); + var precision = renderer.getPrecision(); -// File:src/renderers/webgl/plugins/LensFlarePlugin.js + if ( material.precision !== null ) { -/** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - */ + precision = capabilities.getMaxPrecision( material.precision ); -THREE.LensFlarePlugin = function ( renderer, flares ) { + if ( precision !== material.precision ) { - var gl = renderer.context; - var state = renderer.state; + console.warn( 'THREE.WebGLRenderer.initMaterial:', material.precision, 'not supported, using', precision, 'instead.' ); - var vertexBuffer, elementBuffer; - var program, attributes, uniforms; - var hasVertexTexture; + } - var tempTexture, occlusionTexture; + } - var init = function () { + var parameters = { - var vertices = new Float32Array( [ - - 1, - 1, 0, 0, - 1, - 1, 1, 0, - 1, 1, 1, 1, - - 1, 1, 0, 1 - ] ); + shaderID: shaderID, - var faces = new Uint16Array( [ - 0, 1, 2, - 0, 2, 3 - ] ); + precision: precision, + supportsVertexTextures: capabilities.vertexTextures, - // buffers + map: !! material.map, + envMap: !! material.envMap, + envMapMode: material.envMap && material.envMap.mapping, + lightMap: !! material.lightMap, + aoMap: !! material.aoMap, + emissiveMap: !! material.emissiveMap, + bumpMap: !! material.bumpMap, + normalMap: !! material.normalMap, + displacementMap: !! material.displacementMap, + specularMap: !! material.specularMap, + alphaMap: !! material.alphaMap, - vertexBuffer = gl.createBuffer(); - elementBuffer = gl.createBuffer(); + combine: material.combine, - gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); - gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW ); + vertexColors: material.vertexColors, - gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); - gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW ); + fog: fog, + useFog: material.fog, + fogExp: fog instanceof THREE.FogExp2, - // textures + flatShading: material.shading === THREE.FlatShading, - tempTexture = gl.createTexture(); - occlusionTexture = gl.createTexture(); + sizeAttenuation: material.sizeAttenuation, + logarithmicDepthBuffer: capabilities.logarithmicDepthBuffer, - state.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 ); + skinning: material.skinning, + maxBones: maxBones, + useVertexTexture: capabilities.floatVertexTextures && object && object.skeleton && object.skeleton.useVertexTexture, - state.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 ); + morphTargets: material.morphTargets, + morphNormals: material.morphNormals, + maxMorphTargets: renderer.maxMorphTargets, + maxMorphNormals: renderer.maxMorphNormals, - hasVertexTexture = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) > 0; + maxDirLights: maxLightCount.directional, + maxPointLights: maxLightCount.point, + maxSpotLights: maxLightCount.spot, + maxHemiLights: maxLightCount.hemi, - var shader; + maxShadows: maxShadows, + shadowMapEnabled: renderer.shadowMap.enabled && object.receiveShadow && maxShadows > 0, + shadowMapType: renderer.shadowMap.type, + shadowMapDebug: renderer.shadowMap.debug, - if ( hasVertexTexture ) { + alphaTest: material.alphaTest, + metal: material.metal, + doubleSided: material.side === THREE.DoubleSide, + flipSided: material.side === THREE.BackSide - shader = { + }; - vertexShader: [ + return parameters; - "uniform lowp int renderType;", + }; - "uniform vec3 screenPosition;", - "uniform vec2 scale;", - "uniform float rotation;", + this.getProgramCode = function ( material, parameters ) { - "uniform sampler2D occlusionMap;", + var chunks = []; - "attribute vec2 position;", - "attribute vec2 uv;", + if ( parameters.shaderID ) { - "varying vec2 vUV;", - "varying float vVisibility;", + chunks.push( parameters.shaderID ); - "void main() {", + } else { - "vUV = uv;", + chunks.push( material.fragmentShader ); + chunks.push( material.vertexShader ); - "vec2 pos = position;", + } - "if( renderType == 2 ) {", + if ( material.defines !== undefined ) { - "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 ) );", + for ( var name in material.defines ) { - "vVisibility = visibility.r / 9.0;", - "vVisibility *= 1.0 - visibility.g / 9.0;", - "vVisibility *= visibility.b / 9.0;", - "vVisibility *= 1.0 - visibility.a / 9.0;", + chunks.push( name ); + chunks.push( material.defines[ name ] ); - "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", - "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", + } - "}", + } - "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", + for ( var i = 0; i < parameterNames.length; i ++ ) { - "}" + var parameterName = parameterNames[ i ]; + chunks.push( parameterName ); + chunks.push( parameters[ parameterName ] ); - ].join( "\n" ), + } - fragmentShader: [ + return chunks.join(); - "uniform lowp int renderType;", + }; - "uniform sampler2D map;", - "uniform float opacity;", - "uniform vec3 color;", + this.acquireProgram = function ( material, parameters, code ) { - "varying vec2 vUV;", - "varying float vVisibility;", + var program; - "void main() {", + // Check if code has been already compiled + for ( var p = 0, pl = programs.length; p < pl; p ++ ) { - // pink square + var programInfo = programs[ p ]; - "if( renderType == 0 ) {", + if ( programInfo.code === code ) { - "gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );", + program = programInfo; + ++ program.usedTimes; - // restore + break; - "} else if( renderType == 1 ) {", + } - "gl_FragColor = texture2D( map, vUV );", + } - // flare + if ( program === undefined ) { - "} else {", + program = new THREE.WebGLProgram( renderer, code, material, parameters ); + programs.push( program ); - "vec4 texture = texture2D( map, vUV );", - "texture.a *= opacity * vVisibility;", - "gl_FragColor = texture;", - "gl_FragColor.rgb *= color;", + } - "}", + return program; - "}" + }; - ].join( "\n" ) + this.releaseProgram = function( program ) { - }; + if ( -- program.usedTimes === 0 ) { - } else { + // Remove from unordered set + var i = programs.indexOf( program ); + programs[ i ] = programs[ programs.length - 1 ]; + programs.pop(); - shader = { + // Free WebGL resources + program.destroy(); - vertexShader: [ + } - "uniform lowp int renderType;", + }; - "uniform vec3 screenPosition;", - "uniform vec2 scale;", - "uniform float rotation;", + // Exposed for resource monitoring & error feedback via renderer.info: + this.programs = programs; - "attribute vec2 position;", - "attribute vec2 uv;", +}; - "varying vec2 vUV;", +// File:src/renderers/webgl/WebGLProperties.js - "void main() {", +/** +* @author fordacious / fordacious.github.io +*/ - "vUV = uv;", +THREE.WebGLProperties = function () { - "vec2 pos = position;", + var properties = {}; - "if( renderType == 2 ) {", + this.get = function ( object ) { - "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", - "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", + var uuid = object.uuid; + var map = properties[ uuid ]; - "}", + if ( map === undefined ) { - "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", + map = {}; + properties[ uuid ] = map; - "}" + } - ].join( "\n" ), + return map; - fragmentShader: [ + }; - "precision mediump float;", + this.delete = function ( object ) { - "uniform lowp int renderType;", + delete properties[ object.uuid ]; - "uniform sampler2D map;", - "uniform sampler2D occlusionMap;", - "uniform float opacity;", - "uniform vec3 color;", + }; - "varying vec2 vUV;", + this.clear = function () { - "void main() {", + properties = {}; - // pink square + }; - "if( renderType == 0 ) {", +}; - "gl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );", +// File:src/renderers/webgl/WebGLShader.js - // restore +THREE.WebGLShader = ( function () { - "} else if( renderType == 1 ) {", + var addLineNumbers = function ( string ) { - "gl_FragColor = texture2D( map, vUV );", + var lines = string.split( '\n' ); - // flare + for ( var i = 0; i < lines.length; i ++ ) { - "} else {", + lines[ i ] = ( i + 1 ) + ': ' + lines[ i ]; - "float visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 ) ).a;", - "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a;", - "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a;", - "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) ).a;", - "visibility = ( 1.0 - visibility / 4.0 );", + } - "vec4 texture = texture2D( map, vUV );", - "texture.a *= opacity * visibility;", - "gl_FragColor = texture;", - "gl_FragColor.rgb *= color;", + return lines.join( '\n' ); - "}", + }; - "}" + return function WebGLShader( gl, type, string ) { - ].join( "\n" ) + var shader = gl.createShader( type ); - }; + gl.shaderSource( shader, string ); + gl.compileShader( shader ); + + if ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === false ) { + + console.error( 'THREE.WebGLShader: Shader couldn\'t compile.' ); } - program = createProgram( shader ); + if ( gl.getShaderInfoLog( shader ) !== '' ) { - attributes = { - vertex: gl.getAttribLocation ( program, "position" ), - uv: gl.getAttribLocation ( program, "uv" ) - }; + console.warn( 'THREE.WebGLShader: gl.getShaderInfoLog()', type === gl.VERTEX_SHADER ? 'vertex' : 'fragment', gl.getShaderInfoLog( shader ), addLineNumbers( string ) ); - 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" ) - }; + } + + // --enable-privileged-webgl-extension + // console.log( type, gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) ); + + return shader; }; - /* - * Render lens flares - * Method: renders 16x16 0xff00ff-colored points scattered over the light source area, - * reads these back and calculates occlusion. - */ +} )(); - this.render = function ( scene, camera, viewportWidth, viewportHeight ) { +// File:src/renderers/webgl/WebGLShadowMap.js - if ( flares.length === 0 ) return; +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ - var tempPosition = new THREE.Vector3(); +THREE.WebGLShadowMap = function ( _renderer, _lights, _objects ) { - var invAspect = viewportHeight / viewportWidth, - halfViewportWidth = viewportWidth * 0.5, - halfViewportHeight = viewportHeight * 0.5; + var _gl = _renderer.context, + _state = _renderer.state, + _frustum = new THREE.Frustum(), + _projScreenMatrix = new THREE.Matrix4(), - var size = 16 / viewportHeight, - scale = new THREE.Vector2( size * invAspect, size ); + _min = new THREE.Vector3(), + _max = new THREE.Vector3(), - var screenPosition = new THREE.Vector3( 1, 1, 0 ), - screenPositionPixels = new THREE.Vector2( 1, 1 ); + _matrixPosition = new THREE.Vector3(), - if ( program === undefined ) { + _renderList = []; - init(); + // init - } + var depthShader = THREE.ShaderLib[ "depthRGBA" ]; + var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms ); - gl.useProgram( program ); + var _depthMaterial = new THREE.ShaderMaterial( { + uniforms: depthUniforms, + vertexShader: depthShader.vertexShader, + fragmentShader: depthShader.fragmentShader + } ); - state.initAttributes(); - state.enableAttribute( attributes.vertex ); - state.enableAttribute( attributes.uv ); - state.disableUnusedAttributes(); + var _depthMaterialMorph = new THREE.ShaderMaterial( { + uniforms: depthUniforms, + vertexShader: depthShader.vertexShader, + fragmentShader: depthShader.fragmentShader, + morphTargets: true + } ); - // loop through all lens flares to update their occlusion and positions - // setup gl and common used attribs/uniforms + var _depthMaterialSkin = new THREE.ShaderMaterial( { + uniforms: depthUniforms, + vertexShader: depthShader.vertexShader, + fragmentShader: depthShader.fragmentShader, + skinning: true + } ); - gl.uniform1i( uniforms.occlusionMap, 0 ); - gl.uniform1i( uniforms.map, 1 ); + var _depthMaterialMorphSkin = new THREE.ShaderMaterial( { + uniforms: depthUniforms, + vertexShader: depthShader.vertexShader, + fragmentShader: depthShader.fragmentShader, + morphTargets: true, + skinning: true + } ); - 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 ); + _depthMaterial._shadowPass = true; + _depthMaterialMorph._shadowPass = true; + _depthMaterialSkin._shadowPass = true; + _depthMaterialMorphSkin._shadowPass = true; - gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); + // - state.disable( gl.CULL_FACE ); - gl.depthMask( false ); + var scope = this; - for ( var i = 0, l = flares.length; i < l; i ++ ) { + this.enabled = false; - size = 16 / viewportHeight; - scale.set( size * invAspect, size ); + this.autoUpdate = true; + this.needsUpdate = false; - // calc object screen position + this.type = THREE.PCFShadowMap; + this.cullFace = THREE.CullFaceFront; - var flare = flares[ i ]; + this.render = function ( scene ) { - tempPosition.set( flare.matrixWorld.elements[ 12 ], flare.matrixWorld.elements[ 13 ], flare.matrixWorld.elements[ 14 ] ); + if ( scope.enabled === false ) return; + if ( scope.autoUpdate === false && scope.needsUpdate === false ) return; - tempPosition.applyMatrix4( camera.matrixWorldInverse ); - tempPosition.applyProjection( camera.projectionMatrix ); + // set GL state for depth map - // setup arrays for gl programs + _gl.clearColor( 1, 1, 1, 1 ); + _state.disable( _gl.BLEND ); - screenPosition.copy( tempPosition ); + _state.enable( _gl.CULL_FACE ); + _gl.frontFace( _gl.CCW ); - screenPositionPixels.x = screenPosition.x * halfViewportWidth + halfViewportWidth; - screenPositionPixels.y = screenPosition.y * halfViewportHeight + halfViewportHeight; + if ( scope.cullFace === THREE.CullFaceFront ) { - // screen cull + _gl.cullFace( _gl.FRONT ); - if ( hasVertexTexture || ( - screenPositionPixels.x > 0 && - screenPositionPixels.x < viewportWidth && - screenPositionPixels.y > 0 && - screenPositionPixels.y < viewportHeight ) ) { + } else { - // save current RGB to temp texture + _gl.cullFace( _gl.BACK ); - state.activeTexture( gl.TEXTURE0 ); - state.bindTexture( gl.TEXTURE_2D, null ); - state.activeTexture( gl.TEXTURE1 ); - state.bindTexture( gl.TEXTURE_2D, tempTexture ); - gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGB, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 ); + } + _state.setDepthTest( true ); - // render pink quad + // render depth map - gl.uniform1i( uniforms.renderType, 0 ); - gl.uniform2f( uniforms.scale, scale.x, scale.y ); - gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); + for ( var i = 0, il = _lights.length; i < il; i ++ ) { - state.disable( gl.BLEND ); - state.enable( gl.DEPTH_TEST ); + var light = _lights[ i ]; - gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); + if ( ! light.castShadow ) continue; + if ( ! light.shadowMap ) { - // copy result to occlusionMap + var shadowFilter = THREE.LinearFilter; - state.activeTexture( gl.TEXTURE0 ); - state.bindTexture( gl.TEXTURE_2D, occlusionTexture ); - gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGBA, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 ); + if ( scope.type === THREE.PCFSoftShadowMap ) { + shadowFilter = THREE.NearestFilter; - // restore graphics + } - gl.uniform1i( uniforms.renderType, 1 ); - state.disable( gl.DEPTH_TEST ); + var pars = { minFilter: shadowFilter, magFilter: shadowFilter, format: THREE.RGBAFormat }; - state.activeTexture( gl.TEXTURE1 ); - state.bindTexture( gl.TEXTURE_2D, tempTexture ); - gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); + light.shadowMap = new THREE.WebGLRenderTarget( light.shadowMapWidth, light.shadowMapHeight, pars ); + light.shadowMapSize = new THREE.Vector2( light.shadowMapWidth, light.shadowMapHeight ); + light.shadowMatrix = new THREE.Matrix4(); - // update object positions + } - flare.positionScreen.copy( screenPosition ); + if ( ! light.shadowCamera ) { - if ( flare.customUpdateCallback ) { + if ( light instanceof THREE.SpotLight ) { - flare.customUpdateCallback( flare ); + light.shadowCamera = new THREE.PerspectiveCamera( light.shadowCameraFov, light.shadowMapWidth / light.shadowMapHeight, light.shadowCameraNear, light.shadowCameraFar ); - } else { + } else if ( light instanceof THREE.DirectionalLight ) { - flare.updateLensFlares(); + light.shadowCamera = new THREE.OrthographicCamera( light.shadowCameraLeft, light.shadowCameraRight, light.shadowCameraTop, light.shadowCameraBottom, light.shadowCameraNear, light.shadowCameraFar ); - } + } else { - // render flares + console.error( "THREE.ShadowMapPlugin: Unsupported light type for shadow", light ); + continue; - gl.uniform1i( uniforms.renderType, 2 ); - state.enable( gl.BLEND ); + } - for ( var j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) { + scene.add( light.shadowCamera ); - var sprite = flare.lensFlares[ j ]; + if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); - if ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) { + } - screenPosition.x = sprite.x; - screenPosition.y = sprite.y; - screenPosition.z = sprite.z; + if ( light.shadowCameraVisible && ! light.cameraHelper ) { - size = sprite.size * sprite.scale / viewportHeight; + light.cameraHelper = new THREE.CameraHelper( light.shadowCamera ); + scene.add( light.cameraHelper ); - 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 ); + var shadowMap = light.shadowMap; + var shadowMatrix = light.shadowMatrix; + var shadowCamera = light.shadowCamera; - gl.uniform1f( uniforms.opacity, sprite.opacity ); - gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b ); + // - state.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst ); - renderer.setTexture( sprite.texture, 1 ); + shadowCamera.position.setFromMatrixPosition( light.matrixWorld ); + _matrixPosition.setFromMatrixPosition( light.target.matrixWorld ); + shadowCamera.lookAt( _matrixPosition ); + shadowCamera.updateMatrixWorld(); - gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); + shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld ); - } + // - } + if ( light.cameraHelper ) light.cameraHelper.visible = light.shadowCameraVisible; + if ( light.shadowCameraVisible ) light.cameraHelper.update(); - } + // compute shadow matrix - } + shadowMatrix.set( + 0.5, 0.0, 0.0, 0.5, + 0.0, 0.5, 0.0, 0.5, + 0.0, 0.0, 0.5, 0.5, + 0.0, 0.0, 0.0, 1.0 + ); - // restore gl + shadowMatrix.multiply( shadowCamera.projectionMatrix ); + shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); - state.enable( gl.CULL_FACE ); - state.enable( gl.DEPTH_TEST ); - gl.depthMask( true ); + // update camera matrices and frustum - renderer.resetGLState(); + _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); + _frustum.setFromMatrix( _projScreenMatrix ); - }; + // render shadow map - function createProgram ( shader ) { + _renderer.setRenderTarget( shadowMap ); + _renderer.clear(); - var program = gl.createProgram(); + // set object matrices & frustum culling - var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER ); - var vertexShader = gl.createShader( gl.VERTEX_SHADER ); + _renderList.length = 0; - var prefix = "precision " + renderer.getPrecision() + " float;\n"; + projectObject( scene, shadowCamera ); - gl.shaderSource( fragmentShader, prefix + shader.fragmentShader ); - gl.shaderSource( vertexShader, prefix + shader.vertexShader ); - gl.compileShader( fragmentShader ); - gl.compileShader( vertexShader ); + // render regular objects - gl.attachShader( program, fragmentShader ); - gl.attachShader( program, vertexShader ); + for ( var j = 0, jl = _renderList.length; j < jl; j ++ ) { - gl.linkProgram( program ); + var object = _renderList[ j ]; + var geometry = _objects.update( object ); + var material = object.material; - return program; + if ( material instanceof THREE.MeshFaceMaterial ) { - } + var groups = geometry.groups; + var materials = material.materials; -}; + for ( var k = 0, kl = groups.length; k < kl; k ++ ) { -// File:src/renderers/webgl/plugins/SpritePlugin.js + var group = groups[ k ]; + var groupMaterial = materials[ group.materialIndex ]; -/** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - */ + if ( groupMaterial.visible === true ) { -THREE.SpritePlugin = function ( renderer, sprites ) { + _renderer.renderBufferDirect( shadowCamera, _lights, null, geometry, getDepthMaterial( object, groupMaterial ), object, group ); - var gl = renderer.context; - var state = renderer.state; + } - var vertexBuffer, elementBuffer; - var program, attributes, uniforms; + } - var texture; + } else { - // decompose matrixWorld + _renderer.renderBufferDirect( shadowCamera, _lights, null, geometry, getDepthMaterial( object, material ), object, null ); - var spritePosition = new THREE.Vector3(); - var spriteRotation = new THREE.Quaternion(); - var spriteScale = new THREE.Vector3(); + } - 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 - ] ); + // restore GL state - vertexBuffer = gl.createBuffer(); - elementBuffer = gl.createBuffer(); + var clearColor = _renderer.getClearColor(), + clearAlpha = _renderer.getClearAlpha(); - gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); - gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW ); + _renderer.setClearColor( clearColor, clearAlpha ); + _state.enable( _gl.BLEND ); - gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); - gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW ); + if ( scope.cullFace === THREE.CullFaceFront ) { - program = createProgram(); + _gl.cullFace( _gl.BACK ); - attributes = { - position: gl.getAttribLocation ( program, 'position' ), - uv: gl.getAttribLocation ( program, 'uv' ) - }; + } - uniforms = { - uvOffset: gl.getUniformLocation( program, 'uvOffset' ), - uvScale: gl.getUniformLocation( program, 'uvScale' ), + _renderer.resetGLState(); - rotation: gl.getUniformLocation( program, 'rotation' ), - scale: gl.getUniformLocation( program, 'scale' ), + scope.needsUpdate = false; - 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' ), + function getDepthMaterial( object, material ) { - 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' ), + var geometry = object.geometry; - alphaTest: gl.getUniformLocation( program, 'alphaTest' ) - }; + var useMorphing = geometry.morphTargets !== undefined && geometry.morphTargets.length > 0 && material.morphTargets; + var useSkinning = object instanceof THREE.SkinnedMesh && material.skinning; - var canvas = document.createElement( 'canvas' ); - canvas.width = 8; - canvas.height = 8; + var depthMaterial; - var context = canvas.getContext( '2d' ); - context.fillStyle = 'white'; - context.fillRect( 0, 0, 8, 8 ); + if ( object.customDepthMaterial ) { - texture = new THREE.Texture( canvas ); - texture.needsUpdate = true; + depthMaterial = object.customDepthMaterial; - }; + } else if ( useSkinning ) { - this.render = function ( scene, camera ) { + depthMaterial = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin; - if ( sprites.length === 0 ) return; + } else if ( useMorphing ) { - // setup gl + depthMaterial = _depthMaterialMorph; - if ( program === undefined ) { + } else { - init(); + depthMaterial = _depthMaterial; } - gl.useProgram( program ); + depthMaterial.visible = material.visible; + depthMaterial.wireframe = material.wireframe; + depthMaterial.wireframeLinewidth = material.wireframeLinewidth; - state.initAttributes(); - state.enableAttribute( attributes.position ); - state.enableAttribute( attributes.uv ); - state.disableUnusedAttributes(); + return depthMaterial; - state.disable( gl.CULL_FACE ); - state.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 ); + function projectObject( object, camera ) { - gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); + if ( object.visible === false ) return; - gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); + if ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Points ) { - state.activeTexture( gl.TEXTURE0 ); - gl.uniform1i( uniforms.map, 0 ); + if ( object.castShadow && ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) ) { - var oldFogType = 0; - var sceneFogType = 0; - var fog = scene.fog; + var material = object.material; - if ( fog ) { + if ( material.visible === true ) { - gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b ); + object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + _renderList.push( object ); - 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 ) { + var children = object.children; - gl.uniform1f( uniforms.fogDensity, fog.density ); + for ( var i = 0, l = children.length; i < l; i ++ ) { - gl.uniform1i( uniforms.fogType, 2 ); - oldFogType = 2; - sceneFogType = 2; + projectObject( children[ i ], camera ); - } + } - } else { + } - gl.uniform1i( uniforms.fogType, 0 ); - oldFogType = 0; - sceneFogType = 0; +}; - } +// File:src/renderers/webgl/WebGLState.js +/** +* @author mrdoob / http://mrdoob.com/ +*/ - // update positions and sort +THREE.WebGLState = function ( gl, extensions, paramThreeToGL ) { - for ( var i = 0, l = sprites.length; i < l; i ++ ) { + var _this = this; - var sprite = sprites[ i ]; + var newAttributes = new Uint8Array( 16 ); + var enabledAttributes = new Uint8Array( 16 ); - sprite.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld ); - sprite.z = - sprite.modelViewMatrix.elements[ 14 ]; + var capabilities = {}; - } + var compressedTextureFormats = null; - sprites.sort( painterSortStable ); + var currentBlending = null; + var currentBlendEquation = null; + var currentBlendSrc = null; + var currentBlendDst = null; + var currentBlendEquationAlpha = null; + var currentBlendSrcAlpha = null; + var currentBlendDstAlpha = null; - // render all sprites + var currentDepthFunc = null; + var currentDepthWrite = null; - var scale = []; + var currentColorWrite = null; - for ( var i = 0, l = sprites.length; i < l; i ++ ) { + var currentFlipSided = null; - var sprite = sprites[ i ]; - var material = sprite.material; + var currentLineWidth = null; - gl.uniform1f( uniforms.alphaTest, material.alphaTest ); - gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite.modelViewMatrix.elements ); + var currentPolygonOffsetFactor = null; + var currentPolygonOffsetUnits = null; - sprite.matrixWorld.decompose( spritePosition, spriteRotation, spriteScale ); + var maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS ); - scale[ 0 ] = spriteScale.x; - scale[ 1 ] = spriteScale.y; + var currentTextureSlot = undefined; + var currentBoundTextures = {}; - var fogType = 0; + this.init = function () { - if ( scene.fog && material.fog ) { + gl.clearColor( 0, 0, 0, 1 ); + gl.clearDepth( 1 ); + gl.clearStencil( 0 ); - fogType = sceneFogType; + this.enable( gl.DEPTH_TEST ); + gl.depthFunc( gl.LEQUAL ); - } + gl.frontFace( gl.CCW ); + gl.cullFace( gl.BACK ); + this.enable( gl.CULL_FACE ); - if ( oldFogType !== fogType ) { + this.enable( gl.BLEND ); + gl.blendEquation( gl.FUNC_ADD ); + gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA ); - gl.uniform1i( uniforms.fogType, fogType ); - oldFogType = fogType; + }; - } + this.initAttributes = function () { - if ( material.map !== null ) { + for ( var i = 0, l = newAttributes.length; i < l; i ++ ) { - gl.uniform2f( uniforms.uvOffset, material.map.offset.x, material.map.offset.y ); - gl.uniform2f( uniforms.uvScale, material.map.repeat.x, material.map.repeat.y ); + newAttributes[ i ] = 0; - } else { + } - gl.uniform2f( uniforms.uvOffset, 0, 0 ); - gl.uniform2f( uniforms.uvScale, 1, 1 ); + }; - } + this.enableAttribute = function ( attribute ) { - gl.uniform1f( uniforms.opacity, material.opacity ); - gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b ); + newAttributes[ attribute ] = 1; - gl.uniform1f( uniforms.rotation, material.rotation ); - gl.uniform2fv( uniforms.scale, scale ); + if ( enabledAttributes[ attribute ] === 0 ) { - state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); - state.setDepthTest( material.depthTest ); - state.setDepthWrite( material.depthWrite ); + gl.enableVertexAttribArray( attribute ); + enabledAttributes[ attribute ] = 1; - if ( material.map && material.map.image && material.map.image.width ) { + } - renderer.setTexture( material.map, 0 ); + }; - } else { + this.disableUnusedAttributes = function () { - renderer.setTexture( texture, 0 ); + for ( var i = 0, l = enabledAttributes.length; i < l; i ++ ) { - } + if ( enabledAttributes[ i ] !== newAttributes[ i ] ) { - gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); + gl.disableVertexAttribArray( i ); + enabledAttributes[ i ] = 0; + + } } - // restore gl + }; - state.enable( gl.CULL_FACE ); + this.enable = function ( id ) { - renderer.resetGLState(); + if ( capabilities[ id ] !== true ) { + + gl.enable( id ); + capabilities[ id ] = true; + + } }; - function createProgram () { + this.disable = function ( id ) { - var program = gl.createProgram(); + if ( capabilities[ id ] !== false ) { - var vertexShader = gl.createShader( gl.VERTEX_SHADER ); - var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER ); + gl.disable( id ); + capabilities[ id ] = false; - 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;', + this.getCompressedTextureFormats = function () { - 'attribute vec2 position;', - 'attribute vec2 uv;', + if ( compressedTextureFormats === null ) { - 'varying vec2 vUV;', + compressedTextureFormats = []; - 'void main() {', + if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) || + extensions.get( 'WEBGL_compressed_texture_s3tc' ) ) { - 'vUV = uvOffset + uv * uvScale;', + var formats = gl.getParameter( gl.COMPRESSED_TEXTURE_FORMATS ); - 'vec2 alignedPosition = position * scale;', + for ( var i = 0; i < formats.length; i ++ ) { - 'vec2 rotatedPosition;', - 'rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;', - 'rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;', + compressedTextureFormats.push( formats[ i ] ); - 'vec4 finalPosition;', + } - 'finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );', - 'finalPosition.xy += rotatedPosition;', - 'finalPosition = projectionMatrix * finalPosition;', + } - 'gl_Position = finalPosition;', + } - '}' + return compressedTextureFormats; - ].join( '\n' ) ); + }; - gl.shaderSource( fragmentShader, [ + this.setBlending = function ( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha ) { - 'precision ' + renderer.getPrecision() + ' float;', + if ( blending !== currentBlending ) { - 'uniform vec3 color;', - 'uniform sampler2D map;', - 'uniform float opacity;', + if ( blending === THREE.NoBlending ) { - 'uniform int fogType;', - 'uniform vec3 fogColor;', - 'uniform float fogDensity;', - 'uniform float fogNear;', - 'uniform float fogFar;', - 'uniform float alphaTest;', + this.disable( gl.BLEND ); - 'varying vec2 vUV;', + } else if ( blending === THREE.AdditiveBlending ) { - 'void main() {', + this.enable( gl.BLEND ); + gl.blendEquation( gl.FUNC_ADD ); + gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); - 'vec4 texture = texture2D( map, vUV );', + } else if ( blending === THREE.SubtractiveBlending ) { - 'if ( texture.a < alphaTest ) discard;', + // TODO: Find blendFuncSeparate() combination - 'gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );', + this.enable( gl.BLEND ); + gl.blendEquation( gl.FUNC_ADD ); + gl.blendFunc( gl.ZERO, gl.ONE_MINUS_SRC_COLOR ); - 'if ( fogType > 0 ) {', + } else if ( blending === THREE.MultiplyBlending ) { - 'float depth = gl_FragCoord.z / gl_FragCoord.w;', - 'float fogFactor = 0.0;', + // TODO: Find blendFuncSeparate() combination - 'if ( fogType == 1 ) {', + this.enable( gl.BLEND ); + gl.blendEquation( gl.FUNC_ADD ); + gl.blendFunc( gl.ZERO, gl.SRC_COLOR ); - 'fogFactor = smoothstep( fogNear, fogFar, depth );', + } else if ( blending === THREE.CustomBlending ) { - '} else {', + this.enable( gl.BLEND ); - 'const float LOG2 = 1.442695;', - 'fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );', - 'fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );', + } else { - '}', + this.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 ); - 'gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );', + } - '}', + currentBlending = blending; - '}' + } - ].join( '\n' ) ); + if ( blending === THREE.CustomBlending ) { - gl.compileShader( vertexShader ); - gl.compileShader( fragmentShader ); + blendEquationAlpha = blendEquationAlpha || blendEquation; + blendSrcAlpha = blendSrcAlpha || blendSrc; + blendDstAlpha = blendDstAlpha || blendDst; - gl.attachShader( program, vertexShader ); - gl.attachShader( program, fragmentShader ); + if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { - gl.linkProgram( program ); + gl.blendEquationSeparate( paramThreeToGL( blendEquation ), paramThreeToGL( blendEquationAlpha ) ); - return program; + currentBlendEquation = blendEquation; + currentBlendEquationAlpha = blendEquationAlpha; - } + } - function painterSortStable ( a, b ) { + if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { - if ( a.z !== b.z ) { + gl.blendFuncSeparate( paramThreeToGL( blendSrc ), paramThreeToGL( blendDst ), paramThreeToGL( blendSrcAlpha ), paramThreeToGL( blendDstAlpha ) ); - return b.z - a.z; + currentBlendSrc = blendSrc; + currentBlendDst = blendDst; + currentBlendSrcAlpha = blendSrcAlpha; + currentBlendDstAlpha = blendDstAlpha; + + } } else { - return b.id - a.id; + currentBlendEquation = null; + currentBlendSrc = null; + currentBlendDst = null; + currentBlendEquationAlpha = null; + currentBlendSrcAlpha = null; + currentBlendDstAlpha = null; } - } + }; -}; + this.setDepthFunc = function ( depthFunc ) { -// File:src/extras/GeometryUtils.js + if ( currentDepthFunc !== depthFunc ) { -/** - * @author mrdoob / http://mrdoob.com/ - */ + if ( depthFunc ) { -THREE.GeometryUtils = { + switch ( depthFunc ) { - merge: function ( geometry1, geometry2, materialIndexOffset ) { + case THREE.NeverDepth: - console.warn( 'THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.' ); + gl.depthFunc( gl.NEVER ); + break; - var matrix; + case THREE.AlwaysDepth: - if ( geometry2 instanceof THREE.Mesh ) { + gl.depthFunc( gl.ALWAYS ); + break; - geometry2.matrixAutoUpdate && geometry2.updateMatrix(); + case THREE.LessDepth: - matrix = geometry2.matrix; - geometry2 = geometry2.geometry; + gl.depthFunc( gl.LESS ); + break; - } + case THREE.LessEqualDepth: - geometry1.merge( geometry2, matrix, materialIndexOffset ); + gl.depthFunc( gl.LEQUAL ); + break; - }, + case THREE.EqualDepth: - center: function ( geometry ) { + gl.depthFunc( gl.EQUAL ); + break; - console.warn( 'THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.' ); - return geometry.center(); + case THREE.GreaterEqualDepth: - } + gl.depthFunc( gl.GEQUAL ); + break; -}; + case THREE.GreaterDepth: -// File:src/extras/ImageUtils.js + gl.depthFunc( gl.GREATER ); + break; -/** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - * @author Daosheng Mu / https://github.com/DaoshengMu/ - */ + case THREE.NotEqualDepth: -THREE.ImageUtils = { + gl.depthFunc( gl.NOTEQUAL ); + break; - crossOrigin: undefined, + default: - loadTexture: function ( url, mapping, onLoad, onError ) { + gl.depthFunc( gl.LEQUAL ); - var loader = new THREE.ImageLoader(); - loader.crossOrigin = this.crossOrigin; + } - var texture = new THREE.Texture( undefined, mapping ); + } else { - loader.load( url, function ( image ) { + gl.depthFunc( gl.LEQUAL ); - texture.image = image; - texture.needsUpdate = true; + } - if ( onLoad ) onLoad( texture ); + currentDepthFunc = depthFunc; - }, undefined, function ( event ) { + } - if ( onError ) onError( event ); + }; - } ); + this.setDepthTest = function ( depthTest ) { - texture.sourceFile = url; + if ( depthTest ) { - return texture; + this.enable( gl.DEPTH_TEST ); - }, + } else { - loadTextureCube: function ( array, mapping, onLoad, onError ) { + this.disable( gl.DEPTH_TEST ); - var images = []; + } - var loader = new THREE.ImageLoader(); - loader.crossOrigin = this.crossOrigin; + }; - var texture = new THREE.CubeTexture( images, mapping ); + this.setDepthWrite = function ( depthWrite ) { - var loaded = 0; + if ( currentDepthWrite !== depthWrite ) { - var loadTexture = function ( i ) { + gl.depthMask( depthWrite ); + currentDepthWrite = depthWrite; - loader.load( array[ i ], function ( image ) { + } - texture.images[ i ] = image; + }; - loaded += 1; + this.setColorWrite = function ( colorWrite ) { - if ( loaded === 6 ) { + if ( currentColorWrite !== colorWrite ) { - texture.needsUpdate = true; + gl.colorMask( colorWrite, colorWrite, colorWrite, colorWrite ); + currentColorWrite = colorWrite; - if ( onLoad ) onLoad( texture ); + } - } + }; - }, undefined, onError ); + this.setFlipSided = function ( flipSided ) { - }; + if ( currentFlipSided !== flipSided ) { - for ( var i = 0, il = array.length; i < il; ++ i ) { + if ( flipSided ) { - loadTexture( i ); + gl.frontFace( gl.CW ); - } + } else { - return texture; + gl.frontFace( gl.CCW ); - }, + } - loadCompressedTexture: function () { + currentFlipSided = flipSided; - console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' ) + } - }, + }; - loadCompressedTextureCube: function () { + this.setLineWidth = function ( width ) { - console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' ) + if ( width !== currentLineWidth ) { - }, + gl.lineWidth( width ); - getNormalMap: function ( image, depth ) { + currentLineWidth = width; - // Adapted from http://www.paulbrunt.co.uk/lab/heightnormal/ + } - var cross = function ( a, b ) { + }; - return [ a[ 1 ] * b[ 2 ] - a[ 2 ] * b[ 1 ], a[ 2 ] * b[ 0 ] - a[ 0 ] * b[ 2 ], a[ 0 ] * b[ 1 ] - a[ 1 ] * b[ 0 ] ]; + this.setPolygonOffset = function ( polygonOffset, factor, units ) { - }; + if ( polygonOffset ) { - var subtract = function ( a, b ) { + this.enable( gl.POLYGON_OFFSET_FILL ); - return [ a[ 0 ] - b[ 0 ], a[ 1 ] - b[ 1 ], a[ 2 ] - b[ 2 ] ]; + } else { + + this.disable( gl.POLYGON_OFFSET_FILL ); + + } + + if ( polygonOffset && ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) ) { - }; + gl.polygonOffset( factor, units ); - var normalize = function ( a ) { + currentPolygonOffsetFactor = factor; + currentPolygonOffsetUnits = units; - var l = Math.sqrt( a[ 0 ] * a[ 0 ] + a[ 1 ] * a[ 1 ] + a[ 2 ] * a[ 2 ] ); - return [ a[ 0 ] / l, a[ 1 ] / l, a[ 2 ] / l ]; + } - }; + }; - depth = depth | 1; + this.setScissorTest = function ( scissorTest ) { - var width = image.width; - var height = image.height; + if ( scissorTest ) { - var canvas = document.createElement( 'canvas' ); - canvas.width = width; - canvas.height = height; + this.enable( gl.SCISSOR_TEST ); - var context = canvas.getContext( '2d' ); - context.drawImage( image, 0, 0 ); + } else { - var data = context.getImageData( 0, 0, width, height ).data; - var imageData = context.createImageData( width, height ); - var output = imageData.data; + this.disable( gl.SCISSOR_TEST ); - for ( var x = 0; x < width; x ++ ) { + } - for ( var y = 0; y < height; y ++ ) { + }; - var ly = y - 1 < 0 ? 0 : y - 1; - var uy = y + 1 > height - 1 ? height - 1 : y + 1; - var lx = x - 1 < 0 ? 0 : x - 1; - var ux = x + 1 > width - 1 ? width - 1 : x + 1; + // texture - var points = []; - var origin = [ 0, 0, data[ ( y * width + x ) * 4 ] / 255 * depth ]; - points.push( [ - 1, 0, data[ ( y * width + lx ) * 4 ] / 255 * depth ] ); - points.push( [ - 1, - 1, data[ ( ly * width + lx ) * 4 ] / 255 * depth ] ); - points.push( [ 0, - 1, data[ ( ly * width + x ) * 4 ] / 255 * depth ] ); - points.push( [ 1, - 1, data[ ( ly * width + ux ) * 4 ] / 255 * depth ] ); - points.push( [ 1, 0, data[ ( y * width + ux ) * 4 ] / 255 * depth ] ); - points.push( [ 1, 1, data[ ( uy * width + ux ) * 4 ] / 255 * depth ] ); - points.push( [ 0, 1, data[ ( uy * width + x ) * 4 ] / 255 * depth ] ); - points.push( [ - 1, 1, data[ ( uy * width + lx ) * 4 ] / 255 * depth ] ); + this.activeTexture = function ( webglSlot ) { - var normals = []; - var num_points = points.length; + if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1; - for ( var i = 0; i < num_points; i ++ ) { + if ( currentTextureSlot !== webglSlot ) { - var v1 = points[ i ]; - var v2 = points[ ( i + 1 ) % num_points ]; - v1 = subtract( v1, origin ); - v2 = subtract( v2, origin ); - normals.push( normalize( cross( v1, v2 ) ) ); + gl.activeTexture( webglSlot ); + currentTextureSlot = webglSlot; - } + } - var normal = [ 0, 0, 0 ]; + } - for ( var i = 0; i < normals.length; i ++ ) { + this.bindTexture = function ( webglType, webglTexture ) { - normal[ 0 ] += normals[ i ][ 0 ]; - normal[ 1 ] += normals[ i ][ 1 ]; - normal[ 2 ] += normals[ i ][ 2 ]; + if ( currentTextureSlot === undefined ) { - } + _this.activeTexture(); - normal[ 0 ] /= normals.length; - normal[ 1 ] /= normals.length; - normal[ 2 ] /= normals.length; + } - var idx = ( y * width + x ) * 4; + var boundTexture = currentBoundTextures[ currentTextureSlot ]; - output[ idx ] = ( ( normal[ 0 ] + 1.0 ) / 2.0 * 255 ) | 0; - output[ idx + 1 ] = ( ( normal[ 1 ] + 1.0 ) / 2.0 * 255 ) | 0; - output[ idx + 2 ] = ( normal[ 2 ] * 255 ) | 0; - output[ idx + 3 ] = 255; + if ( boundTexture === undefined ) { - } + boundTexture = { type: undefined, texture: undefined }; + currentBoundTextures[ currentTextureSlot ] = boundTexture; } - context.putImageData( imageData, 0, 0 ); + if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) { - return canvas; + gl.bindTexture( webglType, webglTexture ); - }, + boundTexture.type = webglType; + boundTexture.texture = webglTexture; - generateDataTexture: function ( width, height, color ) { + } - var size = width * height; - var data = new Uint8Array( 3 * size ); + }; - var r = Math.floor( color.r * 255 ); - var g = Math.floor( color.g * 255 ); - var b = Math.floor( color.b * 255 ); + this.compressedTexImage2D = function () { - for ( var i = 0; i < size; i ++ ) { + try { - data[ i * 3 ] = r; - data[ i * 3 + 1 ] = g; - data[ i * 3 + 2 ] = b; + gl.compressedTexImage2D.apply( gl, arguments ); + + } catch ( error ) { + + console.error( error ); } - var texture = new THREE.DataTexture( data, width, height, THREE.RGBFormat ); - texture.needsUpdate = true; + }; - return texture; + this.texImage2D = function () { - } + try { -}; + gl.texImage2D.apply( gl, arguments ); -// File:src/extras/SceneUtils.js + } catch ( error ) { -/** - * @author alteredq / http://alteredqualia.com/ - */ + console.error( error ); -THREE.SceneUtils = { + } - createMultiMaterialObject: function ( geometry, materials ) { + }; - var group = new THREE.Group(); + // - for ( var i = 0, l = materials.length; i < l; i ++ ) { + this.reset = function () { - group.add( new THREE.Mesh( geometry, materials[ i ] ) ); + for ( var i = 0; i < enabledAttributes.length; i ++ ) { - } + if ( enabledAttributes[ i ] === 1 ) { - return group; + gl.disableVertexAttribArray( i ); + enabledAttributes[ i ] = 0; - }, + } - detach: function ( child, parent, scene ) { + } - child.applyMatrix( parent.matrixWorld ); - parent.remove( child ); - scene.add( child ); + capabilities = {}; - }, + compressedTextureFormats = null; - attach: function ( child, scene, parent ) { + currentBlending = null; - var matrixWorldInverse = new THREE.Matrix4(); - matrixWorldInverse.getInverse( parent.matrixWorld ); - child.applyMatrix( matrixWorldInverse ); + currentDepthWrite = null; + currentColorWrite = null; - scene.remove( child ); - parent.add( child ); + currentFlipSided = null; - } + }; }; -// File:src/extras/FontUtils.js +// File:src/renderers/webgl/plugins/LensFlarePlugin.js /** - * @author zz85 / http://www.lab4games.net/zz85/blog + * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ - * - * For Text operations in three.js (See TextGeometry) - * - * It uses techniques used in: - * - * Triangulation ported from AS3 - * Simple Polygon Triangulation - * http://actionsnippet.com/?p=1462 - * - * A Method to triangulate shapes with holes - * http://www.sakri.net/blog/2009/06/12/an-approach-to-triangulating-polygons-with-holes/ - * */ -THREE.FontUtils = { +THREE.LensFlarePlugin = function ( renderer, flares ) { - faces: {}, + var gl = renderer.context; + var state = renderer.state; - // Just for now. face[weight][style] + var vertexBuffer, elementBuffer; + var program, attributes, uniforms; + var hasVertexTexture; - face: 'helvetiker', - weight: 'normal', - style: 'normal', - size: 150, - divisions: 10, + var tempTexture, occlusionTexture; - getFace: function () { + var init = function () { - try { + var vertices = new Float32Array( [ + - 1, - 1, 0, 0, + 1, - 1, 1, 0, + 1, 1, 1, 1, + - 1, 1, 0, 1 + ] ); - return this.faces[ this.face.toLowerCase() ][ this.weight ][ this.style ]; + var faces = new Uint16Array( [ + 0, 1, 2, + 0, 2, 3 + ] ); - } catch ( e ) { + // buffers - throw "The font " + this.face + " with " + this.weight + " weight and " + this.style + " style is missing." + 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 ); - loadFace: function ( data ) { + // textures - var family = data.familyName.toLowerCase(); + tempTexture = gl.createTexture(); + occlusionTexture = gl.createTexture(); - var ThreeFont = this; + state.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 ); - ThreeFont.faces[ family ] = ThreeFont.faces[ family ] || {}; + state.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 ); - ThreeFont.faces[ family ][ data.cssFontWeight ] = ThreeFont.faces[ family ][ data.cssFontWeight ] || {}; - ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data; + hasVertexTexture = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) > 0; - ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data; + var shader; - return data; + if ( hasVertexTexture ) { - }, + shader = { - drawText: function ( text ) { + vertexShader: [ - // RenderText + "uniform lowp int renderType;", - var i, - face = this.getFace(), - scale = this.size / face.resolution, - offset = 0, - chars = String( text ).split( '' ), - length = chars.length; + "uniform vec3 screenPosition;", + "uniform vec2 scale;", + "uniform float rotation;", - var fontPaths = []; + "uniform sampler2D occlusionMap;", - for ( i = 0; i < length; i ++ ) { + "attribute vec2 position;", + "attribute vec2 uv;", - var path = new THREE.Path(); + "varying vec2 vUV;", + "varying float vVisibility;", - var ret = this.extractGlyphPoints( chars[ i ], face, scale, offset, path ); - offset += ret.offset; + "void main() {", - fontPaths.push( ret.path ); + "vUV = uv;", - } + "vec2 pos = position;", - // get the width + "if( renderType == 2 ) {", - var width = offset / 2; - // - // for ( p = 0; p < allPts.length; p++ ) { - // - // allPts[ p ].x -= width; - // - // } + "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 extract = this.extractPoints( allPts, characterPts ); - //extract.contour = allPts; + "vVisibility = visibility.r / 9.0;", + "vVisibility *= 1.0 - visibility.g / 9.0;", + "vVisibility *= visibility.b / 9.0;", + "vVisibility *= 1.0 - visibility.a / 9.0;", - //extract.paths = fontPaths; - //extract.offset = width; + "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", + "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", - return { paths: fontPaths, offset: width }; + "}", - }, + "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", + "}" + ].join( "\n" ), + fragmentShader: [ - extractGlyphPoints: function ( c, face, scale, offset, path ) { + "uniform lowp int renderType;", - var pts = []; + "uniform sampler2D map;", + "uniform float opacity;", + "uniform vec3 color;", - var i, i2, divisions, - outline, action, length, - scaleX, scaleY, - x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2, - laste, - glyph = face.glyphs[ c ] || face.glyphs[ '?' ]; + "varying vec2 vUV;", + "varying float vVisibility;", - if ( ! glyph ) return; + "void main() {", - if ( glyph.o ) { + // pink square - outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) ); - length = outline.length; + "if( renderType == 0 ) {", - scaleX = scale; - scaleY = scale; + "gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );", - for ( i = 0; i < length; ) { + // restore - action = outline[ i ++ ]; + "} else if( renderType == 1 ) {", - //console.log( action ); + "gl_FragColor = texture2D( map, vUV );", - switch ( action ) { + // flare - case 'm': + "} else {", - // Move To + "vec4 texture = texture2D( map, vUV );", + "texture.a *= opacity * vVisibility;", + "gl_FragColor = texture;", + "gl_FragColor.rgb *= color;", - x = outline[ i ++ ] * scaleX + offset; - y = outline[ i ++ ] * scaleY; + "}", - path.moveTo( x, y ); - break; + "}" - case 'l': + ].join( "\n" ) - // Line To + }; - x = outline[ i ++ ] * scaleX + offset; - y = outline[ i ++ ] * scaleY; - path.lineTo( x, y ); - break; + } else { - case 'q': + shader = { - // QuadraticCurveTo + vertexShader: [ - cpx = outline[ i ++ ] * scaleX + offset; - cpy = outline[ i ++ ] * scaleY; - cpx1 = outline[ i ++ ] * scaleX + offset; - cpy1 = outline[ i ++ ] * scaleY; + "uniform lowp int renderType;", - path.quadraticCurveTo( cpx1, cpy1, cpx, cpy ); + "uniform vec3 screenPosition;", + "uniform vec2 scale;", + "uniform float rotation;", - laste = pts[ pts.length - 1 ]; + "attribute vec2 position;", + "attribute vec2 uv;", - if ( laste ) { + "varying vec2 vUV;", - cpx0 = laste.x; - cpy0 = laste.y; + "void main() {", - for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) { + "vUV = uv;", - var t = i2 / divisions; - THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx ); - THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy ); + "vec2 pos = position;", - } + "if( renderType == 2 ) {", - } + "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", + "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", - break; + "}", - case 'b': + "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", - // Cubic Bezier Curve + "}" - cpx = outline[ i ++ ] * scaleX + offset; - cpy = outline[ i ++ ] * scaleY; - cpx1 = outline[ i ++ ] * scaleX + offset; - cpy1 = outline[ i ++ ] * scaleY; - cpx2 = outline[ i ++ ] * scaleX + offset; - cpy2 = outline[ i ++ ] * scaleY; + ].join( "\n" ), - path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy ); + fragmentShader: [ - laste = pts[ pts.length - 1 ]; + "precision mediump float;", - if ( laste ) { + "uniform lowp int renderType;", - cpx0 = laste.x; - cpy0 = laste.y; + "uniform sampler2D map;", + "uniform sampler2D occlusionMap;", + "uniform float opacity;", + "uniform vec3 color;", - for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) { + "varying vec2 vUV;", - var t = i2 / divisions; - THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx ); - THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy ); + "void main() {", - } + // pink square - } + "if( renderType == 0 ) {", - break; + "gl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );", - } + // restore - } + "} else if( renderType == 1 ) {", - } + "gl_FragColor = texture2D( map, vUV );", + // flare + "} else {", - return { offset: glyph.ha * scale, path: path }; + "float visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 ) ).a;", + "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a;", + "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a;", + "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) ).a;", + "visibility = ( 1.0 - visibility / 4.0 );", - } + "vec4 texture = texture2D( map, vUV );", + "texture.a *= opacity * visibility;", + "gl_FragColor = texture;", + "gl_FragColor.rgb *= color;", -}; + "}", + "}" -THREE.FontUtils.generateShapes = function ( text, parameters ) { + ].join( "\n" ) - // Parameters + }; - parameters = parameters || {}; + } - var size = parameters.size !== undefined ? parameters.size : 100; - var curveSegments = parameters.curveSegments !== undefined ? parameters.curveSegments : 4; + program = createProgram( shader ); - var font = parameters.font !== undefined ? parameters.font : 'helvetiker'; - var weight = parameters.weight !== undefined ? parameters.weight : 'normal'; - var style = parameters.style !== undefined ? parameters.style : 'normal'; + attributes = { + vertex: gl.getAttribLocation ( program, "position" ), + uv: gl.getAttribLocation ( program, "uv" ) + }; - THREE.FontUtils.size = size; - THREE.FontUtils.divisions = curveSegments; + 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" ) + }; - THREE.FontUtils.face = font; - THREE.FontUtils.weight = weight; - THREE.FontUtils.style = style; + }; - // Get a Font data json object + /* + * Render lens flares + * Method: renders 16x16 0xff00ff-colored points scattered over the light source area, + * reads these back and calculates occlusion. + */ - var data = THREE.FontUtils.drawText( text ); + this.render = function ( scene, camera, viewportWidth, viewportHeight ) { - var paths = data.paths; - var shapes = []; + if ( flares.length === 0 ) return; - for ( var p = 0, pl = paths.length; p < pl; p ++ ) { + var tempPosition = new THREE.Vector3(); - Array.prototype.push.apply( shapes, paths[ p ].toShapes() ); + var invAspect = viewportHeight / viewportWidth, + halfViewportWidth = viewportWidth * 0.5, + halfViewportHeight = viewportHeight * 0.5; + + var size = 16 / viewportHeight, + scale = new THREE.Vector2( size * invAspect, size ); + + var screenPosition = new THREE.Vector3( 1, 1, 0 ), + screenPositionPixels = new THREE.Vector2( 1, 1 ); + + if ( program === undefined ) { + + init(); - } + } - return shapes; + gl.useProgram( program ); -}; + state.initAttributes(); + state.enableAttribute( attributes.vertex ); + state.enableAttribute( attributes.uv ); + state.disableUnusedAttributes(); + // loop through all lens flares to update their occlusion and positions + // setup gl and common used attribs/uniforms -/** - * This code is a quick port of code written in C++ which was submitted to - * flipcode.com by John W. Ratcliff // July 22, 2000 - * See original code and more information here: - * http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml - * - * ported to actionscript by Zevan Rosser - * www.actionsnippet.com - * - * ported to javascript by Joshua Koo - * http://www.lab4games.net/zz85/blog - * - */ + gl.uniform1i( uniforms.occlusionMap, 0 ); + gl.uniform1i( uniforms.map, 1 ); + 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 ); -( function ( namespace ) { + gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); - var EPSILON = 0.0000000001; + state.disable( gl.CULL_FACE ); + gl.depthMask( false ); - // takes in an contour array and returns + for ( var i = 0, l = flares.length; i < l; i ++ ) { - var process = function ( contour, indices ) { + size = 16 / viewportHeight; + scale.set( size * invAspect, size ); - var n = contour.length; + // calc object screen position - if ( n < 3 ) return null; + var flare = flares[ i ]; - var result = [], - verts = [], - vertIndices = []; + tempPosition.set( flare.matrixWorld.elements[ 12 ], flare.matrixWorld.elements[ 13 ], flare.matrixWorld.elements[ 14 ] ); - /* we want a counter-clockwise polygon in verts */ + tempPosition.applyMatrix4( camera.matrixWorldInverse ); + tempPosition.applyProjection( camera.projectionMatrix ); - var u, v, w; + // setup arrays for gl programs - if ( area( contour ) > 0.0 ) { + screenPosition.copy( tempPosition ); - for ( v = 0; v < n; v ++ ) verts[ v ] = v; + screenPositionPixels.x = screenPosition.x * halfViewportWidth + halfViewportWidth; + screenPositionPixels.y = screenPosition.y * halfViewportHeight + halfViewportHeight; - } else { + // screen cull - for ( v = 0; v < n; v ++ ) verts[ v ] = ( n - 1 ) - v; + if ( hasVertexTexture || ( + screenPositionPixels.x > 0 && + screenPositionPixels.x < viewportWidth && + screenPositionPixels.y > 0 && + screenPositionPixels.y < viewportHeight ) ) { - } + // save current RGB to temp texture - var nv = n; + state.activeTexture( gl.TEXTURE0 ); + state.bindTexture( gl.TEXTURE_2D, null ); + state.activeTexture( gl.TEXTURE1 ); + state.bindTexture( gl.TEXTURE_2D, tempTexture ); + gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGB, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 ); - /* remove nv - 2 vertices, creating 1 triangle every time */ - var count = 2 * nv; /* error detection */ + // render pink quad - for ( v = nv - 1; nv > 2; ) { + gl.uniform1i( uniforms.renderType, 0 ); + gl.uniform2f( uniforms.scale, scale.x, scale.y ); + gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); - /* if we loop, it is probably a non-simple polygon */ + state.disable( gl.BLEND ); + state.enable( gl.DEPTH_TEST ); - if ( ( count -- ) <= 0 ) { + gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - //** Triangulate: ERROR - probable bad polygon! - //throw ( "Warning, unable to triangulate polygon!" ); - //return null; - // Sometimes warning is fine, especially polygons are triangulated in reverse. - console.warn( 'THREE.FontUtils: Warning, unable to triangulate polygon! in Triangulate.process()' ); + // copy result to occlusionMap - if ( indices ) return vertIndices; - return result; + state.activeTexture( gl.TEXTURE0 ); + state.bindTexture( gl.TEXTURE_2D, occlusionTexture ); + gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGBA, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 ); - } - /* three consecutive vertices in current polygon, */ + // restore graphics - u = v; if ( nv <= u ) u = 0; /* previous */ - v = u + 1; if ( nv <= v ) v = 0; /* new v */ - w = v + 1; if ( nv <= w ) w = 0; /* next */ + gl.uniform1i( uniforms.renderType, 1 ); + state.disable( gl.DEPTH_TEST ); - if ( snip( contour, u, v, w, nv, verts ) ) { + state.activeTexture( gl.TEXTURE1 ); + state.bindTexture( gl.TEXTURE_2D, tempTexture ); + gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - var a, b, c, s, t; - /* true names of the vertices */ + // update object positions - a = verts[ u ]; - b = verts[ v ]; - c = verts[ w ]; + flare.positionScreen.copy( screenPosition ); - /* output Triangle */ + if ( flare.customUpdateCallback ) { - result.push( [ contour[ a ], - contour[ b ], - contour[ c ] ] ); + flare.customUpdateCallback( flare ); + } else { - vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] ); + flare.updateLensFlares(); - /* remove v from the remaining polygon */ + } - for ( s = v, t = v + 1; t < nv; s ++, t ++ ) { + // render flares - verts[ s ] = verts[ t ]; + gl.uniform1i( uniforms.renderType, 2 ); + state.enable( gl.BLEND ); - } + for ( var j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) { - nv --; + var sprite = flare.lensFlares[ j ]; - /* reset error detection counter */ + if ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) { - count = 2 * nv; + screenPosition.x = sprite.x; + screenPosition.y = sprite.y; + screenPosition.z = sprite.z; - } + size = sprite.size * sprite.scale / viewportHeight; - } + scale.x = size * invAspect; + scale.y = size; - if ( indices ) return vertIndices; - return result; + gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); + gl.uniform2f( uniforms.scale, scale.x, scale.y ); + gl.uniform1f( uniforms.rotation, sprite.rotation ); - }; + gl.uniform1f( uniforms.opacity, sprite.opacity ); + gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b ); - // calculate area of the contour polygon + state.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst ); + renderer.setTexture( sprite.texture, 1 ); - var area = function ( contour ) { + gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - var n = contour.length; - var a = 0.0; + } - for ( var p = n - 1, q = 0; q < n; p = q ++ ) { + } - a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; + } } - return a * 0.5; + // restore gl - }; + state.enable( gl.CULL_FACE ); + state.enable( gl.DEPTH_TEST ); + gl.depthMask( true ); - var snip = function ( contour, u, v, w, n, verts ) { + renderer.resetGLState(); - var p; - var ax, ay, bx, by; - var cx, cy, px, py; + }; - ax = contour[ verts[ u ] ].x; - ay = contour[ verts[ u ] ].y; + function createProgram ( shader ) { - bx = contour[ verts[ v ] ].x; - by = contour[ verts[ v ] ].y; + var program = gl.createProgram(); - cx = contour[ verts[ w ] ].x; - cy = contour[ verts[ w ] ].y; + var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER ); + var vertexShader = gl.createShader( gl.VERTEX_SHADER ); - if ( EPSILON > ( ( ( bx - ax ) * ( cy - ay ) ) - ( ( by - ay ) * ( cx - ax ) ) ) ) return false; + var prefix = "precision " + renderer.getPrecision() + " float;\n"; - var aX, aY, bX, bY, cX, cY; - var apx, apy, bpx, bpy, cpx, cpy; - var cCROSSap, bCROSScp, aCROSSbp; + gl.shaderSource( fragmentShader, prefix + shader.fragmentShader ); + gl.shaderSource( vertexShader, prefix + shader.vertexShader ); - aX = cx - bx; aY = cy - by; - bX = ax - cx; bY = ay - cy; - cX = bx - ax; cY = by - ay; + gl.compileShader( fragmentShader ); + gl.compileShader( vertexShader ); - for ( p = 0; p < n; p ++ ) { + gl.attachShader( program, fragmentShader ); + gl.attachShader( program, vertexShader ); - px = contour[ verts[ p ] ].x; - py = contour[ verts[ p ] ].y; + gl.linkProgram( program ); - if ( ( ( px === ax ) && ( py === ay ) ) || - ( ( px === bx ) && ( py === by ) ) || - ( ( px === cx ) && ( py === cy ) ) ) continue; + return program; - apx = px - ax; apy = py - ay; - bpx = px - bx; bpy = py - by; - cpx = px - cx; cpy = py - cy; + } - // see if p is inside triangle abc +}; - aCROSSbp = aX * bpy - aY * bpx; - cCROSSap = cX * apy - cY * apx; - bCROSScp = bX * cpy - bY * cpx; +// File:src/renderers/webgl/plugins/SpritePlugin.js - if ( ( aCROSSbp >= - EPSILON ) && ( bCROSScp >= - EPSILON ) && ( cCROSSap >= - EPSILON ) ) return false; +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ - } +THREE.SpritePlugin = function ( renderer, sprites ) { - return true; + var gl = renderer.context; + var state = renderer.state; - }; + var vertexBuffer, elementBuffer; + var program, attributes, uniforms; + var texture; - namespace.Triangulate = process; - namespace.Triangulate.area = area; + // decompose matrixWorld - return namespace; + var spritePosition = new THREE.Vector3(); + var spriteRotation = new THREE.Quaternion(); + var spriteScale = new THREE.Vector3(); -} )( THREE.FontUtils ); + var init = function () { -// To use the typeface.js face files, hook up the API + 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 + ] ); -THREE.typeface_js = { faces: THREE.FontUtils.faces, loadFace: THREE.FontUtils.loadFace }; -if ( typeof self !== 'undefined' ) self._typeface_js = THREE.typeface_js; + var faces = new Uint16Array( [ + 0, 1, 2, + 0, 2, 3 + ] ); -// File:src/extras/audio/Audio.js + vertexBuffer = gl.createBuffer(); + elementBuffer = gl.createBuffer(); -/** - * @author mrdoob / http://mrdoob.com/ - */ + gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); + gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW ); -THREE.Audio = function ( listener ) { + gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); + gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW ); - THREE.Object3D.call( this ); + program = createProgram(); - this.type = 'Audio'; + attributes = { + position: gl.getAttribLocation ( program, 'position' ), + uv: gl.getAttribLocation ( program, 'uv' ) + }; - this.context = listener.context; - this.source = this.context.createBufferSource(); - this.source.onended = this.onEnded.bind( this ); + uniforms = { + uvOffset: gl.getUniformLocation( program, 'uvOffset' ), + uvScale: gl.getUniformLocation( program, 'uvScale' ), - this.gain = this.context.createGain(); - this.gain.connect( this.context.destination ); + rotation: gl.getUniformLocation( program, 'rotation' ), + scale: gl.getUniformLocation( program, 'scale' ), - this.panner = this.context.createPanner(); - this.panner.connect( this.gain ); + color: gl.getUniformLocation( program, 'color' ), + map: gl.getUniformLocation( program, 'map' ), + opacity: gl.getUniformLocation( program, 'opacity' ), - this.autoplay = false; + modelViewMatrix: gl.getUniformLocation( program, 'modelViewMatrix' ), + projectionMatrix: gl.getUniformLocation( program, 'projectionMatrix' ), - this.startTime = 0; - this.playbackRate = 1; - this.isPlaying = false; + 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' ) + }; -THREE.Audio.prototype = Object.create( THREE.Object3D.prototype ); -THREE.Audio.prototype.constructor = THREE.Audio; + var canvas = document.createElement( 'canvas' ); + canvas.width = 8; + canvas.height = 8; -THREE.Audio.prototype.load = function ( file ) { + var context = canvas.getContext( '2d' ); + context.fillStyle = 'white'; + context.fillRect( 0, 0, 8, 8 ); - var scope = this; + texture = new THREE.Texture( canvas ); + texture.needsUpdate = true; - var request = new XMLHttpRequest(); - request.open( 'GET', file, true ); - request.responseType = 'arraybuffer'; - request.onload = function ( e ) { + }; - scope.context.decodeAudioData( this.response, function ( buffer ) { + this.render = function ( scene, camera ) { - scope.source.buffer = buffer; + if ( sprites.length === 0 ) return; - if ( scope.autoplay ) scope.play(); + // setup gl - } ); + if ( program === undefined ) { - }; - request.send(); + init(); - return this; + } -}; + gl.useProgram( program ); -THREE.Audio.prototype.play = function () { + state.initAttributes(); + state.enableAttribute( attributes.position ); + state.enableAttribute( attributes.uv ); + state.disableUnusedAttributes(); - if ( this.isPlaying === true ) { + state.disable( gl.CULL_FACE ); + state.enable( gl.BLEND ); - console.warn( 'THREE.Audio: Audio is already playing.' ); - return; + 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 ); - var source = this.context.createBufferSource(); + gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); - source.buffer = this.source.buffer; - source.loop = this.source.loop; - source.onended = this.source.onended; - source.start( 0, this.startTime ); - source.playbackRate.value = this.playbackRate; + state.activeTexture( gl.TEXTURE0 ); + gl.uniform1i( uniforms.map, 0 ); - this.isPlaying = true; + var oldFogType = 0; + var sceneFogType = 0; + var fog = scene.fog; - this.source = source; + if ( fog ) { - this.connect(); + gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b ); -}; + if ( fog instanceof THREE.Fog ) { -THREE.Audio.prototype.pause = function () { + gl.uniform1f( uniforms.fogNear, fog.near ); + gl.uniform1f( uniforms.fogFar, fog.far ); - this.source.stop(); - this.startTime = this.context.currentTime; + gl.uniform1i( uniforms.fogType, 1 ); + oldFogType = 1; + sceneFogType = 1; -}; + } else if ( fog instanceof THREE.FogExp2 ) { -THREE.Audio.prototype.stop = function () { + gl.uniform1f( uniforms.fogDensity, fog.density ); - this.source.stop(); - this.startTime = 0; + gl.uniform1i( uniforms.fogType, 2 ); + oldFogType = 2; + sceneFogType = 2; -}; + } -THREE.Audio.prototype.connect = function () { + } else { - if ( this.filter !== undefined ) { + gl.uniform1i( uniforms.fogType, 0 ); + oldFogType = 0; + sceneFogType = 0; - this.source.connect( this.filter ); - this.filter.connect( this.panner ); + } - } else { - this.source.connect( this.panner ); + // update positions and sort - } + for ( var i = 0, l = sprites.length; i < l; i ++ ) { -}; + var sprite = sprites[ i ]; -THREE.Audio.prototype.disconnect = function () { + sprite.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld ); + sprite.z = - sprite.modelViewMatrix.elements[ 14 ]; - if ( this.filter !== undefined ) { + } - this.source.disconnect( this.filter ); - this.filter.disconnect( this.panner ); + sprites.sort( painterSortStable ); - } else { + // render all sprites - this.source.disconnect( this.panner ); + var scale = []; - } + for ( var i = 0, l = sprites.length; i < l; i ++ ) { -}; + var sprite = sprites[ i ]; + var material = sprite.material; -THREE.Audio.prototype.setFilter = function ( value ) { + gl.uniform1f( uniforms.alphaTest, material.alphaTest ); + gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite.modelViewMatrix.elements ); - if ( this.isPlaying === true ) { + sprite.matrixWorld.decompose( spritePosition, spriteRotation, spriteScale ); - this.disconnect(); - this.filter = value; - this.connect(); + scale[ 0 ] = spriteScale.x; + scale[ 1 ] = spriteScale.y; - } else { + var fogType = 0; - this.filter = value; + if ( scene.fog && material.fog ) { - } + fogType = sceneFogType; -}; + } -THREE.Audio.prototype.getFilter = function () { + if ( oldFogType !== fogType ) { - return this.filter; + gl.uniform1i( uniforms.fogType, fogType ); + oldFogType = fogType; -}; + } -THREE.Audio.prototype.setPlaybackRate = function ( value ) { + if ( material.map !== null ) { - this.playbackRate = value; + gl.uniform2f( uniforms.uvOffset, material.map.offset.x, material.map.offset.y ); + gl.uniform2f( uniforms.uvScale, material.map.repeat.x, material.map.repeat.y ); - if ( this.isPlaying === true ) { + } else { - this.source.playbackRate.value = this.playbackRate; + 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 ); -THREE.Audio.prototype.getPlaybackRate = function () { + gl.uniform1f( uniforms.rotation, material.rotation ); + gl.uniform2fv( uniforms.scale, scale ); - return this.playbackRate; + state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); + state.setDepthTest( material.depthTest ); + state.setDepthWrite( material.depthWrite ); -}; + if ( material.map && material.map.image && material.map.image.width ) { -THREE.Audio.prototype.onEnded = function() { + renderer.setTexture( material.map, 0 ); - this.isPlaying = false; + } else { -}; + renderer.setTexture( texture, 0 ); -THREE.Audio.prototype.setLoop = function ( value ) { + } - this.source.loop = value; + gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); -}; + } -THREE.Audio.prototype.getLoop = function () { + // restore gl - return this.source.loop; + state.enable( gl.CULL_FACE ); -}; + renderer.resetGLState(); -THREE.Audio.prototype.setRefDistance = function ( value ) { + }; - this.panner.refDistance = value; + function createProgram () { -}; + var program = gl.createProgram(); -THREE.Audio.prototype.getRefDistance = function () { + var vertexShader = gl.createShader( gl.VERTEX_SHADER ); + var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER ); - return this.panner.refDistance; + gl.shaderSource( vertexShader, [ -}; + 'precision ' + renderer.getPrecision() + ' float;', -THREE.Audio.prototype.setRolloffFactor = function ( value ) { + 'uniform mat4 modelViewMatrix;', + 'uniform mat4 projectionMatrix;', + 'uniform float rotation;', + 'uniform vec2 scale;', + 'uniform vec2 uvOffset;', + 'uniform vec2 uvScale;', + + 'attribute vec2 position;', + 'attribute vec2 uv;', - this.panner.rolloffFactor = value; + 'varying vec2 vUV;', -}; + 'void main() {', -THREE.Audio.prototype.getRolloffFactor = function () { + 'vUV = uvOffset + uv * uvScale;', - return this.panner.rolloffFactor; + '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;', -THREE.Audio.prototype.setVolume = function ( value ) { + 'vec4 finalPosition;', - this.gain.gain.value = value; + 'finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );', + 'finalPosition.xy += rotatedPosition;', + 'finalPosition = projectionMatrix * finalPosition;', -}; + 'gl_Position = finalPosition;', -THREE.Audio.prototype.getVolume = function () { + '}' - return this.gain.gain.value; + ].join( '\n' ) ); -}; + gl.shaderSource( fragmentShader, [ -THREE.Audio.prototype.updateMatrixWorld = ( function () { + 'precision ' + renderer.getPrecision() + ' float;', - var position = new THREE.Vector3(); + 'uniform vec3 color;', + 'uniform sampler2D map;', + 'uniform float opacity;', - return function updateMatrixWorld( force ) { + 'uniform int fogType;', + 'uniform vec3 fogColor;', + 'uniform float fogDensity;', + 'uniform float fogNear;', + 'uniform float fogFar;', + 'uniform float alphaTest;', - THREE.Object3D.prototype.updateMatrixWorld.call( this, force ); + 'varying vec2 vUV;', - position.setFromMatrixPosition( this.matrixWorld ); + 'void main() {', - this.panner.setPosition( position.x, position.y, position.z ); + 'vec4 texture = texture2D( map, vUV );', - }; + 'if ( texture.a < alphaTest ) discard;', -} )(); + 'gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );', -// File:src/extras/audio/AudioListener.js + 'if ( fogType > 0 ) {', -/** - * @author mrdoob / http://mrdoob.com/ - */ + 'float depth = gl_FragCoord.z / gl_FragCoord.w;', + 'float fogFactor = 0.0;', -THREE.AudioListener = function () { + 'if ( fogType == 1 ) {', - THREE.Object3D.call( this ); + 'fogFactor = smoothstep( fogNear, fogFar, depth );', - this.type = 'AudioListener'; + '} else {', - this.context = new ( window.AudioContext || window.webkitAudioContext )(); + 'const float LOG2 = 1.442695;', + 'fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );', + 'fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );', -}; + '}', -THREE.AudioListener.prototype = Object.create( THREE.Object3D.prototype ); -THREE.AudioListener.prototype.constructor = THREE.AudioListener; + 'gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );', -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(); + ].join( '\n' ) ); - return function updateMatrixWorld( force ) { + gl.compileShader( vertexShader ); + gl.compileShader( fragmentShader ); - THREE.Object3D.prototype.updateMatrixWorld.call( this, force ); + gl.attachShader( program, vertexShader ); + gl.attachShader( program, fragmentShader ); - var listener = this.context.listener; - var up = this.up; + gl.linkProgram( program ); - this.matrixWorld.decompose( position, quaternion, scale ); + return program; - orientation.set( 0, 0, - 1 ).applyQuaternion( quaternion ); + } - listener.setPosition( position.x, position.y, position.z ); - listener.setOrientation( orientation.x, orientation.y, orientation.z, up.x, up.y, up.z ); + function painterSortStable ( a, b ) { - }; + if ( a.z !== b.z ) { -} )(); + return b.z - a.z; -// File:src/extras/core/Curve.js + } else { -/** - * @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 - * - **/ + return b.id - a.id; -/************************************************************** - * Abstract Curve base class - **************************************************************/ + } -THREE.Curve = function () { + } }; -// Virtual base class method to overwrite and implement in subclasses -// - t [0 .. 1] +// File:src/extras/GeometryUtils.js -THREE.Curve.prototype.getPoint = function ( t ) { +/** + * @author mrdoob / http://mrdoob.com/ + */ - console.warn( "THREE.Curve: Warning, getPoint() not implemented!" ); - return null; +THREE.GeometryUtils = { -}; + merge: function ( geometry1, geometry2, materialIndexOffset ) { -// Get point at relative position in curve according to arc length -// - u [0 .. 1] + console.warn( 'THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.' ); -THREE.Curve.prototype.getPointAt = function ( u ) { + var matrix; - var t = this.getUtoTmapping( u ); - return this.getPoint( t ); + if ( geometry2 instanceof THREE.Mesh ) { -}; + geometry2.matrixAutoUpdate && geometry2.updateMatrix(); -// Get sequence of points using getPoint( t ) + matrix = geometry2.matrix; + geometry2 = geometry2.geometry; -THREE.Curve.prototype.getPoints = function ( divisions ) { + } - if ( ! divisions ) divisions = 5; + geometry1.merge( geometry2, matrix, materialIndexOffset ); - var d, pts = []; + }, - for ( d = 0; d <= divisions; d ++ ) { + center: function ( geometry ) { - pts.push( this.getPoint( d / divisions ) ); + console.warn( 'THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.' ); + return geometry.center(); } - return pts; - }; -// Get sequence of points using getPointAt( u ) +// File:src/extras/ImageUtils.js -THREE.Curve.prototype.getSpacedPoints = function ( divisions ) { +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author Daosheng Mu / https://github.com/DaoshengMu/ + */ - if ( ! divisions ) divisions = 5; +THREE.ImageUtils = { - var d, pts = []; + crossOrigin: undefined, - for ( d = 0; d <= divisions; d ++ ) { + loadTexture: function ( url, mapping, onLoad, onError ) { - pts.push( this.getPointAt( d / divisions ) ); + var loader = new THREE.ImageLoader(); + loader.crossOrigin = this.crossOrigin; - } + var texture = new THREE.Texture( undefined, mapping ); - return pts; + loader.load( url, function ( image ) { -}; + texture.image = image; + texture.needsUpdate = true; -// Get total curve arc length + if ( onLoad ) onLoad( texture ); -THREE.Curve.prototype.getLength = function () { + }, undefined, function ( event ) { - var lengths = this.getLengths(); - return lengths[ lengths.length - 1 ]; + if ( onError ) onError( event ); -}; + } ); -// Get list of cumulative segment lengths + texture.sourceFile = url; -THREE.Curve.prototype.getLengths = function ( divisions ) { + return texture; - if ( ! divisions ) divisions = ( this.__arcLengthDivisions ) ? ( this.__arcLengthDivisions ) : 200; + }, - if ( this.cacheArcLengths - && ( this.cacheArcLengths.length === divisions + 1 ) - && ! this.needsUpdate ) { + loadTextureCube: function ( array, mapping, onLoad, onError ) { - //console.log( "cached", this.cacheArcLengths ); - return this.cacheArcLengths; + var images = []; - } + var loader = new THREE.ImageLoader(); + loader.crossOrigin = this.crossOrigin; - this.needsUpdate = false; + var texture = new THREE.CubeTexture( images, mapping ); - var cache = []; - var current, last = this.getPoint( 0 ); - var p, sum = 0; + var loaded = 0; - cache.push( 0 ); + var loadTexture = function ( i ) { - for ( p = 1; p <= divisions; p ++ ) { + loader.load( array[ i ], function ( image ) { - current = this.getPoint ( p / divisions ); - sum += current.distanceTo( last ); - cache.push( sum ); - last = current; + texture.images[ i ] = image; - } + loaded += 1; - this.cacheArcLengths = cache; + if ( loaded === 6 ) { - return cache; // { sums: cache, sum:sum }; Sum is in the last element. + texture.needsUpdate = true; -}; + if ( onLoad ) onLoad( texture ); + } -THREE.Curve.prototype.updateArcLengths = function() { + }, undefined, onError ); - this.needsUpdate = true; - this.getLengths(); + }; -}; + for ( var i = 0, il = array.length; i < il; ++ i ) { -// Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant + loadTexture( i ); -THREE.Curve.prototype.getUtoTmapping = function ( u, distance ) { + } - var arcLengths = this.getLengths(); + return texture; - var i = 0, il = arcLengths.length; + }, - var targetArcLength; // The targeted u distance value to get + loadCompressedTexture: function () { - if ( distance ) { + console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' ) - targetArcLength = distance; + }, - } else { + loadCompressedTextureCube: function () { - targetArcLength = u * arcLengths[ il - 1 ]; + console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' ) - } + }, - //var time = Date.now(); + getNormalMap: function ( image, depth ) { - // binary search for the index with largest value smaller than target u distance + // Adapted from http://www.paulbrunt.co.uk/lab/heightnormal/ - var low = 0, high = il - 1, comparison; + var cross = function ( a, b ) { - while ( low <= high ) { + return [ a[ 1 ] * b[ 2 ] - a[ 2 ] * b[ 1 ], a[ 2 ] * b[ 0 ] - a[ 0 ] * b[ 2 ], a[ 0 ] * b[ 1 ] - a[ 1 ] * b[ 0 ] ]; - i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats + }; - comparison = arcLengths[ i ] - targetArcLength; + var subtract = function ( a, b ) { - if ( comparison < 0 ) { + return [ a[ 0 ] - b[ 0 ], a[ 1 ] - b[ 1 ], a[ 2 ] - b[ 2 ] ]; - low = i + 1; + }; - } else if ( comparison > 0 ) { + var normalize = function ( a ) { - high = i - 1; + var l = Math.sqrt( a[ 0 ] * a[ 0 ] + a[ 1 ] * a[ 1 ] + a[ 2 ] * a[ 2 ] ); + return [ a[ 0 ] / l, a[ 1 ] / l, a[ 2 ] / l ]; - } else { + }; - high = i; - break; + depth = depth | 1; - // DONE + var width = image.width; + var height = image.height; - } + var canvas = document.createElement( 'canvas' ); + canvas.width = width; + canvas.height = height; - } + var context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0 ); - i = high; + var data = context.getImageData( 0, 0, width, height ).data; + var imageData = context.createImageData( width, height ); + var output = imageData.data; - //console.log('b' , i, low, high, Date.now()- time); + for ( var x = 0; x < width; x ++ ) { - if ( arcLengths[ i ] === targetArcLength ) { + for ( var y = 0; y < height; y ++ ) { - var t = i / ( il - 1 ); - return t; + var ly = y - 1 < 0 ? 0 : y - 1; + var uy = y + 1 > height - 1 ? height - 1 : y + 1; + var lx = x - 1 < 0 ? 0 : x - 1; + var ux = x + 1 > width - 1 ? width - 1 : x + 1; - } + var points = []; + var origin = [ 0, 0, data[ ( y * width + x ) * 4 ] / 255 * depth ]; + points.push( [ - 1, 0, data[ ( y * width + lx ) * 4 ] / 255 * depth ] ); + points.push( [ - 1, - 1, data[ ( ly * width + lx ) * 4 ] / 255 * depth ] ); + points.push( [ 0, - 1, data[ ( ly * width + x ) * 4 ] / 255 * depth ] ); + points.push( [ 1, - 1, data[ ( ly * width + ux ) * 4 ] / 255 * depth ] ); + points.push( [ 1, 0, data[ ( y * width + ux ) * 4 ] / 255 * depth ] ); + points.push( [ 1, 1, data[ ( uy * width + ux ) * 4 ] / 255 * depth ] ); + points.push( [ 0, 1, data[ ( uy * width + x ) * 4 ] / 255 * depth ] ); + points.push( [ - 1, 1, data[ ( uy * width + lx ) * 4 ] / 255 * depth ] ); - // we could get finer grain at lengths, or use simple interpolation between two points + var normals = []; + var num_points = points.length; - var lengthBefore = arcLengths[ i ]; - var lengthAfter = arcLengths[ i + 1 ]; + for ( var i = 0; i < num_points; i ++ ) { - var segmentLength = lengthAfter - lengthBefore; + var v1 = points[ i ]; + var v2 = points[ ( i + 1 ) % num_points ]; + v1 = subtract( v1, origin ); + v2 = subtract( v2, origin ); + normals.push( normalize( cross( v1, v2 ) ) ); - // determine where we are between the 'before' and 'after' points + } - var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; + var normal = [ 0, 0, 0 ]; - // add that fractional amount to t + for ( var i = 0; i < normals.length; i ++ ) { - var t = ( i + segmentFraction ) / ( il - 1 ); + normal[ 0 ] += normals[ i ][ 0 ]; + normal[ 1 ] += normals[ i ][ 1 ]; + normal[ 2 ] += normals[ i ][ 2 ]; - return t; + } -}; + normal[ 0 ] /= normals.length; + normal[ 1 ] /= normals.length; + normal[ 2 ] /= normals.length; -// Returns a unit vector tangent at t -// In case any sub curve does not implement its tangent derivation, -// 2 points a small delta apart will be used to find its gradient -// which seems to give a reasonable approximation + var idx = ( y * width + x ) * 4; -THREE.Curve.prototype.getTangent = function( t ) { + output[ idx ] = ( ( normal[ 0 ] + 1.0 ) / 2.0 * 255 ) | 0; + output[ idx + 1 ] = ( ( normal[ 1 ] + 1.0 ) / 2.0 * 255 ) | 0; + output[ idx + 2 ] = ( normal[ 2 ] * 255 ) | 0; + output[ idx + 3 ] = 255; - var delta = 0.0001; - var t1 = t - delta; - var t2 = t + delta; + } - // Capping in case of danger + } - if ( t1 < 0 ) t1 = 0; - if ( t2 > 1 ) t2 = 1; + context.putImageData( imageData, 0, 0 ); - var pt1 = this.getPoint( t1 ); - var pt2 = this.getPoint( t2 ); + return canvas; - var vec = pt2.clone().sub( pt1 ); - return vec.normalize(); + }, -}; + generateDataTexture: function ( width, height, color ) { + var size = width * height; + var data = new Uint8Array( 3 * size ); -THREE.Curve.prototype.getTangentAt = function ( u ) { + var r = Math.floor( color.r * 255 ); + var g = Math.floor( color.g * 255 ); + var b = Math.floor( color.b * 255 ); - var t = this.getUtoTmapping( u ); - return this.getTangent( t ); + for ( var i = 0; i < size; i ++ ) { -}; + data[ i * 3 ] = r; + data[ i * 3 + 1 ] = g; + data[ i * 3 + 2 ] = b; + } + var texture = new THREE.DataTexture( data, width, height, THREE.RGBFormat ); + texture.needsUpdate = true; + return texture; + } -/************************************************************** - * Utils - **************************************************************/ +}; -THREE.Curve.Utils = { +// File:src/extras/SceneUtils.js - tangentQuadraticBezier: function ( t, p0, p1, p2 ) { +/** + * @author alteredq / http://alteredqualia.com/ + */ - return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 ); +THREE.SceneUtils = { - }, + createMultiMaterialObject: function ( geometry, materials ) { - // Puay Bing, thanks for helping with this derivative! + var group = new THREE.Group(); - tangentCubicBezier: function ( t, p0, p1, p2, p3 ) { + for ( var i = 0, l = materials.length; i < l; i ++ ) { - return - 3 * p0 * ( 1 - t ) * ( 1 - t ) + - 3 * p1 * ( 1 - t ) * ( 1 - t ) - 6 * t * p1 * ( 1 - t ) + - 6 * t * p2 * ( 1 - t ) - 3 * t * t * p2 + - 3 * t * t * p3; + group.add( new THREE.Mesh( geometry, materials[ i ] ) ); - }, + } - tangentSpline: function ( t, p0, p1, p2, p3 ) { + return group; - // To check if my formulas are correct + }, - var h00 = 6 * t * t - 6 * t; // derived from 2t^3 − 3t^2 + 1 - var h10 = 3 * t * t - 4 * t + 1; // t^3 − 2t^2 + t - var h01 = - 6 * t * t + 6 * t; // − 2t3 + 3t2 - var h11 = 3 * t * t - 2 * t; // t3 − t2 + detach: function ( child, parent, scene ) { - return h00 + h10 + h01 + h11; + child.applyMatrix( parent.matrixWorld ); + parent.remove( child ); + scene.add( child ); }, - // Catmull-Rom + attach: function ( child, scene, parent ) { - interpolate: function( p0, p1, p2, p3, t ) { + var matrixWorldInverse = new THREE.Matrix4(); + matrixWorldInverse.getInverse( parent.matrixWorld ); + child.applyMatrix( matrixWorldInverse ); - var v0 = ( p2 - p0 ) * 0.5; - var v1 = ( p3 - p1 ) * 0.5; - var t2 = t * t; - var t3 = t * t2; - return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; + scene.remove( child ); + parent.add( child ); } }; +// File:src/extras/FontUtils.js -// TODO: Transformation for Curves? - -/************************************************************** - * 3D Curves - **************************************************************/ +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * @author alteredq / http://alteredqualia.com/ + * + * For Text operations in three.js (See TextGeometry) + * + * It uses techniques used in: + * + * Triangulation ported from AS3 + * Simple Polygon Triangulation + * http://actionsnippet.com/?p=1462 + * + * A Method to triangulate shapes with holes + * http://www.sakri.net/blog/2009/06/12/an-approach-to-triangulating-polygons-with-holes/ + * + */ -// A Factory method for creating new curve subclasses +THREE.FontUtils = { -THREE.Curve.create = function ( constructor, getPointFunc ) { + faces: {}, - constructor.prototype = Object.create( THREE.Curve.prototype ); - constructor.prototype.constructor = constructor; - constructor.prototype.getPoint = getPointFunc; + // Just for now. face[weight][style] - return constructor; + face: 'helvetiker', + weight: 'normal', + style: 'normal', + size: 150, + divisions: 10, -}; + getFace: function () { -// File:src/extras/core/CurvePath.js + try { -/** - * @author zz85 / http://www.lab4games.net/zz85/blog - * - **/ + return this.faces[ this.face.toLowerCase() ][ this.weight ][ this.style ]; -/************************************************************** - * Curved Path - a curve path is simply a array of connected - * curves, but retains the api of a curve - **************************************************************/ + } catch ( e ) { -THREE.CurvePath = function () { + throw "The font " + this.face + " with " + this.weight + " weight and " + this.style + " style is missing." - this.curves = []; - this.bends = []; + } - this.autoClose = false; // Automatically closes the path + }, -}; + loadFace: function ( data ) { -THREE.CurvePath.prototype = Object.create( THREE.Curve.prototype ); -THREE.CurvePath.prototype.constructor = THREE.CurvePath; + var family = data.familyName.toLowerCase(); -THREE.CurvePath.prototype.add = function ( curve ) { + var ThreeFont = this; - this.curves.push( curve ); + ThreeFont.faces[ family ] = ThreeFont.faces[ family ] || {}; -}; + ThreeFont.faces[ family ][ data.cssFontWeight ] = ThreeFont.faces[ family ][ data.cssFontWeight ] || {}; + ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data; -THREE.CurvePath.prototype.checkConnection = function() { - // TODO - // If the ending of curve is not connected to the starting - // or the next curve, then, this is not a real path -}; + ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data; -THREE.CurvePath.prototype.closePath = function() { + return data; - // TODO Test - // and verify for vector3 (needs to implement equals) - // Add a line curve if start and end of lines are not connected - var startPoint = this.curves[ 0 ].getPoint( 0 ); - var endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 ); + }, - if ( ! startPoint.equals( endPoint ) ) { + drawText: function ( text ) { - this.curves.push( new THREE.LineCurve( endPoint, startPoint ) ); + // RenderText - } + var i, + face = this.getFace(), + scale = this.size / face.resolution, + offset = 0, + chars = String( text ).split( '' ), + length = chars.length; -}; + var fontPaths = []; -// To get accurate point with reference to -// entire path distance at time t, -// following has to be done: + for ( i = 0; i < length; i ++ ) { -// 1. Length of each sub path have to be known -// 2. Locate and identify type of curve -// 3. Get t for the curve -// 4. Return curve.getPointAt(t') + var path = new THREE.Path(); -THREE.CurvePath.prototype.getPoint = function( t ) { + var ret = this.extractGlyphPoints( chars[ i ], face, scale, offset, path ); + offset += ret.offset; - var d = t * this.getLength(); - var curveLengths = this.getCurveLengths(); - var i = 0, diff, curve; + fontPaths.push( ret.path ); - // To think about boundaries points. + } - while ( i < curveLengths.length ) { + // get the width - if ( curveLengths[ i ] >= d ) { + var width = offset / 2; + // + // for ( p = 0; p < allPts.length; p++ ) { + // + // allPts[ p ].x -= width; + // + // } - diff = curveLengths[ i ] - d; - curve = this.curves[ i ]; + //var extract = this.extractPoints( allPts, characterPts ); + //extract.contour = allPts; - var u = 1 - diff / curve.getLength(); + //extract.paths = fontPaths; + //extract.offset = width; - return curve.getPointAt( u ); + return { paths: fontPaths, offset: width }; - } + }, - i ++; - } - return null; - // loop where sum != 0, sum > d , sum+1 maxX ) maxX = p.x; - else if ( p.x < minX ) minX = p.x; + laste = pts[ pts.length - 1 ]; - if ( p.y > maxY ) maxY = p.y; - else if ( p.y < minY ) minY = p.y; + if ( laste ) { - if ( v3 ) { + cpx0 = laste.x; + cpy0 = laste.y; - if ( p.z > maxZ ) maxZ = p.z; - else if ( p.z < minZ ) minZ = p.z; + for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) { - } + var t = i2 / divisions; + THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx ); + THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy ); - sum.add( p ); + } - } + } - var ret = { + break; - minX: minX, - minY: minY, - maxX: maxX, - maxY: maxY + } - }; + } - if ( v3 ) { + } - ret.maxZ = maxZ; - ret.minZ = minZ; - } - return ret; + return { offset: glyph.ha * scale, path: path }; + + } }; -/************************************************************** - * Create Geometries Helpers - **************************************************************/ -/// Generate geometry from path points (for Line or Points objects) +THREE.FontUtils.generateShapes = function ( text, parameters ) { -THREE.CurvePath.prototype.createPointsGeometry = function( divisions ) { + // Parameters - var pts = this.getPoints( divisions, true ); - return this.createGeometry( pts ); + parameters = parameters || {}; -}; + var size = parameters.size !== undefined ? parameters.size : 100; + var curveSegments = parameters.curveSegments !== undefined ? parameters.curveSegments : 4; -// Generate geometry from equidistant sampling along the path + var font = parameters.font !== undefined ? parameters.font : 'helvetiker'; + var weight = parameters.weight !== undefined ? parameters.weight : 'normal'; + var style = parameters.style !== undefined ? parameters.style : 'normal'; -THREE.CurvePath.prototype.createSpacedPointsGeometry = function( divisions ) { + THREE.FontUtils.size = size; + THREE.FontUtils.divisions = curveSegments; - var pts = this.getSpacedPoints( divisions, true ); - return this.createGeometry( pts ); + THREE.FontUtils.face = font; + THREE.FontUtils.weight = weight; + THREE.FontUtils.style = style; -}; + // Get a Font data json object -THREE.CurvePath.prototype.createGeometry = function( points ) { + var data = THREE.FontUtils.drawText( text ); - var geometry = new THREE.Geometry(); + var paths = data.paths; + var shapes = []; - for ( var i = 0; i < points.length; i ++ ) { + for ( var p = 0, pl = paths.length; p < pl; p ++ ) { - geometry.vertices.push( new THREE.Vector3( points[ i ].x, points[ i ].y, points[ i ].z || 0 ) ); + Array.prototype.push.apply( shapes, paths[ p ].toShapes() ); } - return geometry; + return shapes; }; -/************************************************************** - * Bend / Wrap Helper Methods - **************************************************************/ - -// Wrap path / Bend modifiers? - -THREE.CurvePath.prototype.addWrapPath = function ( bendpath ) { - - this.bends.push( bendpath ); +/** + * This code is a quick port of code written in C++ which was submitted to + * flipcode.com by John W. Ratcliff // July 22, 2000 + * See original code and more information here: + * http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml + * + * ported to actionscript by Zevan Rosser + * www.actionsnippet.com + * + * ported to javascript by Joshua Koo + * http://www.lab4games.net/zz85/blog + * + */ -}; -THREE.CurvePath.prototype.getTransformedPoints = function( segments, bends ) { +( function ( namespace ) { - var oldPts = this.getPoints( segments ); // getPoints getSpacedPoints - var i, il; + var EPSILON = 0.0000000001; - if ( ! bends ) { + // takes in an contour array and returns - bends = this.bends; + var process = function ( contour, indices ) { - } + var n = contour.length; - for ( i = 0, il = bends.length; i < il; i ++ ) { + if ( n < 3 ) return null; - oldPts = this.getWrapPoints( oldPts, bends[ i ] ); + var result = [], + verts = [], + vertIndices = []; - } + /* we want a counter-clockwise polygon in verts */ - return oldPts; + var u, v, w; -}; + if ( area( contour ) > 0.0 ) { -THREE.CurvePath.prototype.getTransformedSpacedPoints = function( segments, bends ) { + for ( v = 0; v < n; v ++ ) verts[ v ] = v; - var oldPts = this.getSpacedPoints( segments ); + } else { - var i, il; + for ( v = 0; v < n; v ++ ) verts[ v ] = ( n - 1 ) - v; - if ( ! bends ) { + } - bends = this.bends; + var nv = n; - } + /* remove nv - 2 vertices, creating 1 triangle every time */ - for ( i = 0, il = bends.length; i < il; i ++ ) { + var count = 2 * nv; /* error detection */ - oldPts = this.getWrapPoints( oldPts, bends[ i ] ); + for ( v = nv - 1; nv > 2; ) { - } + /* if we loop, it is probably a non-simple polygon */ - return oldPts; + if ( ( count -- ) <= 0 ) { -}; + //** Triangulate: ERROR - probable bad polygon! -// This returns getPoints() bend/wrapped around the contour of a path. -// Read http://www.planetclegg.com/projects/WarpingTextToSplines.html + //throw ( "Warning, unable to triangulate polygon!" ); + //return null; + // Sometimes warning is fine, especially polygons are triangulated in reverse. + console.warn( 'THREE.FontUtils: Warning, unable to triangulate polygon! in Triangulate.process()' ); -THREE.CurvePath.prototype.getWrapPoints = function ( oldPts, path ) { + if ( indices ) return vertIndices; + return result; - var bounds = this.getBoundingBox(); + } - var i, il, p, oldX, oldY, xNorm; + /* three consecutive vertices in current polygon, */ - for ( i = 0, il = oldPts.length; i < il; i ++ ) { + u = v; if ( nv <= u ) u = 0; /* previous */ + v = u + 1; if ( nv <= v ) v = 0; /* new v */ + w = v + 1; if ( nv <= w ) w = 0; /* next */ - p = oldPts[ i ]; + if ( snip( contour, u, v, w, nv, verts ) ) { - oldX = p.x; - oldY = p.y; + var a, b, c, s, t; - xNorm = oldX / bounds.maxX; + /* true names of the vertices */ - // If using actual distance, for length > path, requires line extrusions - //xNorm = path.getUtoTmapping(xNorm, oldX); // 3 styles. 1) wrap stretched. 2) wrap stretch by arc length 3) warp by actual distance + a = verts[ u ]; + b = verts[ v ]; + c = verts[ w ]; - xNorm = path.getUtoTmapping( xNorm, oldX ); + /* output Triangle */ - // check for out of bounds? + result.push( [ contour[ a ], + contour[ b ], + contour[ c ] ] ); - var pathPt = path.getPoint( xNorm ); - var normal = path.getTangent( xNorm ); - normal.set( - normal.y, normal.x ).multiplyScalar( oldY ); - p.x = pathPt.x + normal.x; - p.y = pathPt.y + normal.y; + vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] ); - } + /* remove v from the remaining polygon */ - return oldPts; + for ( s = v, t = v + 1; t < nv; s ++, t ++ ) { -}; + verts[ s ] = verts[ t ]; -// File:src/extras/core/Path.js + } -/** - * @author zz85 / http://www.lab4games.net/zz85/blog - * Creates free form 2d path using series of points, lines or curves. - * - **/ + nv --; -THREE.Path = function ( points ) { + /* reset error detection counter */ - THREE.CurvePath.call( this ); + count = 2 * nv; - this.actions = []; + } - if ( points ) { + } - this.fromPoints( points ); + if ( indices ) return vertIndices; + return result; - } + }; -}; + // calculate area of the contour polygon -THREE.Path.prototype = Object.create( THREE.CurvePath.prototype ); -THREE.Path.prototype.constructor = THREE.Path; + var area = function ( contour ) { -THREE.PathActions = { + var n = contour.length; + var a = 0.0; - MOVE_TO: 'moveTo', - LINE_TO: 'lineTo', - QUADRATIC_CURVE_TO: 'quadraticCurveTo', // Bezier quadratic curve - BEZIER_CURVE_TO: 'bezierCurveTo', // Bezier cubic curve - CSPLINE_THRU: 'splineThru', // Catmull-Rom spline - ARC: 'arc', // Circle - ELLIPSE: 'ellipse' -}; + for ( var p = n - 1, q = 0; q < n; p = q ++ ) { -// TODO Clean up PATH API + a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; -// Create path using straight lines to connect all points -// - vectors: array of Vector2 + } -THREE.Path.prototype.fromPoints = function ( vectors ) { + return a * 0.5; - this.moveTo( vectors[ 0 ].x, vectors[ 0 ].y ); + }; - for ( var v = 1, vlen = vectors.length; v < vlen; v ++ ) { + var snip = function ( contour, u, v, w, n, verts ) { - this.lineTo( vectors[ v ].x, vectors[ v ].y ); + var p; + var ax, ay, bx, by; + var cx, cy, px, py; - } + ax = contour[ verts[ u ] ].x; + ay = contour[ verts[ u ] ].y; -}; + bx = contour[ verts[ v ] ].x; + by = contour[ verts[ v ] ].y; -// startPath() endPath()? + cx = contour[ verts[ w ] ].x; + cy = contour[ verts[ w ] ].y; -THREE.Path.prototype.moveTo = function ( x, y ) { + if ( EPSILON > ( ( ( bx - ax ) * ( cy - ay ) ) - ( ( by - ay ) * ( cx - ax ) ) ) ) return false; - var args = Array.prototype.slice.call( arguments ); - this.actions.push( { action: THREE.PathActions.MOVE_TO, args: args } ); + var aX, aY, bX, bY, cX, cY; + var apx, apy, bpx, bpy, cpx, cpy; + var cCROSSap, bCROSScp, aCROSSbp; -}; + aX = cx - bx; aY = cy - by; + bX = ax - cx; bY = ay - cy; + cX = bx - ax; cY = by - ay; -THREE.Path.prototype.lineTo = function ( x, y ) { + for ( p = 0; p < n; p ++ ) { - var args = Array.prototype.slice.call( arguments ); + px = contour[ verts[ p ] ].x; + py = contour[ verts[ p ] ].y; - var lastargs = this.actions[ this.actions.length - 1 ].args; + if ( ( ( px === ax ) && ( py === ay ) ) || + ( ( px === bx ) && ( py === by ) ) || + ( ( px === cx ) && ( py === cy ) ) ) continue; - var x0 = lastargs[ lastargs.length - 2 ]; - var y0 = lastargs[ lastargs.length - 1 ]; + apx = px - ax; apy = py - ay; + bpx = px - bx; bpy = py - by; + cpx = px - cx; cpy = py - cy; - var curve = new THREE.LineCurve( new THREE.Vector2( x0, y0 ), new THREE.Vector2( x, y ) ); - this.curves.push( curve ); + // see if p is inside triangle abc - this.actions.push( { action: THREE.PathActions.LINE_TO, args: args } ); + aCROSSbp = aX * bpy - aY * bpx; + cCROSSap = cX * apy - cY * apx; + bCROSScp = bX * cpy - bY * cpx; -}; + if ( ( aCROSSbp >= - EPSILON ) && ( bCROSScp >= - EPSILON ) && ( cCROSSap >= - EPSILON ) ) return false; -THREE.Path.prototype.quadraticCurveTo = function( aCPx, aCPy, aX, aY ) { + } - var args = Array.prototype.slice.call( arguments ); + return true; - var lastargs = this.actions[ this.actions.length - 1 ].args; + }; - var x0 = lastargs[ lastargs.length - 2 ]; - var y0 = lastargs[ lastargs.length - 1 ]; - var curve = new THREE.QuadraticBezierCurve( new THREE.Vector2( x0, y0 ), - new THREE.Vector2( aCPx, aCPy ), - new THREE.Vector2( aX, aY ) ); - this.curves.push( curve ); + namespace.Triangulate = process; + namespace.Triangulate.area = area; - this.actions.push( { action: THREE.PathActions.QUADRATIC_CURVE_TO, args: args } ); + return namespace; -}; +} )( THREE.FontUtils ); -THREE.Path.prototype.bezierCurveTo = function( aCP1x, aCP1y, - aCP2x, aCP2y, - aX, aY ) { +// To use the typeface.js face files, hook up the API - var args = Array.prototype.slice.call( arguments ); +THREE.typeface_js = { faces: THREE.FontUtils.faces, loadFace: THREE.FontUtils.loadFace }; +if ( typeof self !== 'undefined' ) self._typeface_js = THREE.typeface_js; - var lastargs = this.actions[ this.actions.length - 1 ].args; +// File:src/extras/audio/Audio.js - var x0 = lastargs[ lastargs.length - 2 ]; - var y0 = lastargs[ lastargs.length - 1 ]; +/** + * @author mrdoob / http://mrdoob.com/ + */ - var curve = new THREE.CubicBezierCurve( new THREE.Vector2( x0, y0 ), - new THREE.Vector2( aCP1x, aCP1y ), - new THREE.Vector2( aCP2x, aCP2y ), - new THREE.Vector2( aX, aY ) ); - this.curves.push( curve ); +THREE.Audio = function ( listener ) { - this.actions.push( { action: THREE.PathActions.BEZIER_CURVE_TO, args: args } ); + THREE.Object3D.call( this ); -}; + this.type = 'Audio'; -THREE.Path.prototype.splineThru = function( pts /*Array of Vector*/ ) { + this.context = listener.context; + this.source = this.context.createBufferSource(); + this.source.onended = this.onEnded.bind( this ); - var args = Array.prototype.slice.call( arguments ); - var lastargs = this.actions[ this.actions.length - 1 ].args; + this.gain = this.context.createGain(); + this.gain.connect( this.context.destination ); - var x0 = lastargs[ lastargs.length - 2 ]; - var y0 = lastargs[ lastargs.length - 1 ]; - //--- - var npts = [ new THREE.Vector2( x0, y0 ) ]; - Array.prototype.push.apply( npts, pts ); + this.panner = this.context.createPanner(); + this.panner.connect( this.gain ); - var curve = new THREE.SplineCurve( npts ); - this.curves.push( curve ); + this.autoplay = false; - this.actions.push( { action: THREE.PathActions.CSPLINE_THRU, args: args } ); + this.startTime = 0; + this.playbackRate = 1; + this.isPlaying = false; }; -// FUTURE: Change the API or follow canvas API? - -THREE.Path.prototype.arc = function ( aX, aY, aRadius, - aStartAngle, aEndAngle, aClockwise ) { +THREE.Audio.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Audio.prototype.constructor = THREE.Audio; - var lastargs = this.actions[ this.actions.length - 1 ].args; - var x0 = lastargs[ lastargs.length - 2 ]; - var y0 = lastargs[ lastargs.length - 1 ]; +THREE.Audio.prototype.load = function ( file ) { - this.absarc( aX + x0, aY + y0, aRadius, - aStartAngle, aEndAngle, aClockwise ); + var scope = this; - }; + var request = new XMLHttpRequest(); + request.open( 'GET', file, true ); + request.responseType = 'arraybuffer'; + request.onload = function ( e ) { - THREE.Path.prototype.absarc = function ( aX, aY, aRadius, - aStartAngle, aEndAngle, aClockwise ) { + scope.context.decodeAudioData( this.response, function ( buffer ) { - this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); + scope.source.buffer = buffer; - }; + if ( scope.autoplay ) scope.play(); -THREE.Path.prototype.ellipse = function ( aX, aY, xRadius, yRadius, - aStartAngle, aEndAngle, aClockwise, aRotation ) { + } ); - var lastargs = this.actions[ this.actions.length - 1 ].args; - var x0 = lastargs[ lastargs.length - 2 ]; - var y0 = lastargs[ lastargs.length - 1 ]; + }; + request.send(); - this.absellipse( aX + x0, aY + y0, xRadius, yRadius, - aStartAngle, aEndAngle, aClockwise, aRotation ); + return this; - }; +}; +THREE.Audio.prototype.play = function () { -THREE.Path.prototype.absellipse = function ( aX, aY, xRadius, yRadius, - aStartAngle, aEndAngle, aClockwise, aRotation ) { + if ( this.isPlaying === true ) { - var args = [ - aX, aY, - xRadius, yRadius, - aStartAngle, aEndAngle, - aClockwise, - aRotation || 0 // aRotation is optional. - ]; - var curve = new THREE.EllipseCurve( aX, aY, xRadius, yRadius, - aStartAngle, aEndAngle, aClockwise, aRotation ); - this.curves.push( curve ); + console.warn( 'THREE.Audio: Audio is already playing.' ); + return; - var lastPoint = curve.getPoint( 1 ); - args.push( lastPoint.x ); - args.push( lastPoint.y ); + } - this.actions.push( { action: THREE.PathActions.ELLIPSE, args: args } ); + var source = this.context.createBufferSource(); - }; + source.buffer = this.source.buffer; + source.loop = this.source.loop; + source.onended = this.source.onended; + source.start( 0, this.startTime ); + source.playbackRate.value = this.playbackRate; -THREE.Path.prototype.getSpacedPoints = function ( divisions, closedPath ) { + this.isPlaying = true; - if ( ! divisions ) divisions = 40; + this.source = source; - var points = []; + this.connect(); - for ( var i = 0; i < divisions; i ++ ) { +}; - points.push( this.getPoint( i / divisions ) ); +THREE.Audio.prototype.pause = function () { - //if( !this.getPoint( i / divisions ) ) throw "DIE"; + this.source.stop(); + this.startTime = this.context.currentTime; - } +}; - // if ( closedPath ) { - // - // points.push( points[ 0 ] ); - // - // } +THREE.Audio.prototype.stop = function () { - return points; + this.source.stop(); + this.startTime = 0; }; -/* Return an array of vectors based on contour of the path */ +THREE.Audio.prototype.connect = function () { -THREE.Path.prototype.getPoints = function( divisions, closedPath ) { + if ( this.filter !== undefined ) { - if ( this.useSpacedPoints ) { + this.source.connect( this.filter ); + this.filter.connect( this.panner ); - return this.getSpacedPoints( divisions, closedPath ); + } else { + + this.source.connect( this.panner ); } - divisions = divisions || 12; +}; - var points = []; +THREE.Audio.prototype.disconnect = function () { - var i, il, item, action, args; - var cpx, cpy, cpx2, cpy2, cpx1, cpy1, cpx0, cpy0, - laste, j, - t, tx, ty; + if ( this.filter !== undefined ) { - for ( i = 0, il = this.actions.length; i < il; i ++ ) { + this.source.disconnect( this.filter ); + this.filter.disconnect( this.panner ); - item = this.actions[ i ]; + } else { - action = item.action; - args = item.args; + this.source.disconnect( this.panner ); - switch ( action ) { + } - case THREE.PathActions.MOVE_TO: +}; - points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) ); +THREE.Audio.prototype.setFilter = function ( value ) { - break; + if ( this.isPlaying === true ) { - case THREE.PathActions.LINE_TO: + this.disconnect(); + this.filter = value; + this.connect(); - points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) ); + } else { - break; + this.filter = value; - case THREE.PathActions.QUADRATIC_CURVE_TO: + } - cpx = args[ 2 ]; - cpy = args[ 3 ]; +}; - cpx1 = args[ 0 ]; - cpy1 = args[ 1 ]; +THREE.Audio.prototype.getFilter = function () { - if ( points.length > 0 ) { + return this.filter; - laste = points[ points.length - 1 ]; +}; - cpx0 = laste.x; - cpy0 = laste.y; +THREE.Audio.prototype.setPlaybackRate = function ( value ) { - } else { + this.playbackRate = value; - laste = this.actions[ i - 1 ].args; + if ( this.isPlaying === true ) { - cpx0 = laste[ laste.length - 2 ]; - cpy0 = laste[ laste.length - 1 ]; + this.source.playbackRate.value = this.playbackRate; - } + } - for ( j = 1; j <= divisions; j ++ ) { +}; - t = j / divisions; +THREE.Audio.prototype.getPlaybackRate = function () { - tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx ); - ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy ); + return this.playbackRate; - points.push( new THREE.Vector2( tx, ty ) ); +}; - } +THREE.Audio.prototype.onEnded = function() { - break; + this.isPlaying = false; - case THREE.PathActions.BEZIER_CURVE_TO: +}; - cpx = args[ 4 ]; - cpy = args[ 5 ]; +THREE.Audio.prototype.setLoop = function ( value ) { - cpx1 = args[ 0 ]; - cpy1 = args[ 1 ]; + this.source.loop = value; - cpx2 = args[ 2 ]; - cpy2 = args[ 3 ]; +}; - if ( points.length > 0 ) { +THREE.Audio.prototype.getLoop = function () { - laste = points[ points.length - 1 ]; + return this.source.loop; - cpx0 = laste.x; - cpy0 = laste.y; +}; - } else { +THREE.Audio.prototype.setRefDistance = function ( value ) { - laste = this.actions[ i - 1 ].args; + this.panner.refDistance = value; - cpx0 = laste[ laste.length - 2 ]; - cpy0 = laste[ laste.length - 1 ]; +}; - } +THREE.Audio.prototype.getRefDistance = function () { + return this.panner.refDistance; - for ( j = 1; j <= divisions; j ++ ) { +}; - t = j / divisions; +THREE.Audio.prototype.setRolloffFactor = function ( value ) { - tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx ); - ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy ); + this.panner.rolloffFactor = value; - points.push( new THREE.Vector2( tx, ty ) ); +}; - } +THREE.Audio.prototype.getRolloffFactor = function () { - break; + return this.panner.rolloffFactor; - case THREE.PathActions.CSPLINE_THRU: +}; - laste = this.actions[ i - 1 ].args; +THREE.Audio.prototype.setVolume = function ( value ) { - var last = new THREE.Vector2( laste[ laste.length - 2 ], laste[ laste.length - 1 ] ); - var spts = [ last ]; + this.gain.gain.value = value; - var n = divisions * args[ 0 ].length; +}; - spts = spts.concat( args[ 0 ] ); +THREE.Audio.prototype.getVolume = function () { - var spline = new THREE.SplineCurve( spts ); + return this.gain.gain.value; - for ( j = 1; j <= n; j ++ ) { +}; - points.push( spline.getPointAt( j / n ) ); +THREE.Audio.prototype.updateMatrixWorld = ( function () { - } + var position = new THREE.Vector3(); - break; + return function updateMatrixWorld( force ) { - case THREE.PathActions.ARC: + THREE.Object3D.prototype.updateMatrixWorld.call( this, force ); - var aX = args[ 0 ], aY = args[ 1 ], - aRadius = args[ 2 ], - aStartAngle = args[ 3 ], aEndAngle = args[ 4 ], - aClockwise = !! args[ 5 ]; + position.setFromMatrixPosition( this.matrixWorld ); - var deltaAngle = aEndAngle - aStartAngle; - var angle; - var tdivisions = divisions * 2; + this.panner.setPosition( position.x, position.y, position.z ); - for ( j = 1; j <= tdivisions; j ++ ) { + }; - t = j / tdivisions; +} )(); - if ( ! aClockwise ) { +// File:src/extras/audio/AudioListener.js - t = 1 - t; +/** + * @author mrdoob / http://mrdoob.com/ + */ - } +THREE.AudioListener = function () { - angle = aStartAngle + t * deltaAngle; + THREE.Object3D.call( this ); - tx = aX + aRadius * Math.cos( angle ); - ty = aY + aRadius * Math.sin( angle ); + this.type = 'AudioListener'; - //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty); + this.context = new ( window.AudioContext || window.webkitAudioContext )(); - points.push( new THREE.Vector2( tx, ty ) ); +}; - } +THREE.AudioListener.prototype = Object.create( THREE.Object3D.prototype ); +THREE.AudioListener.prototype.constructor = THREE.AudioListener; - //console.log(points); +THREE.AudioListener.prototype.updateMatrixWorld = ( function () { - break; + var position = new THREE.Vector3(); + var quaternion = new THREE.Quaternion(); + var scale = new THREE.Vector3(); - case THREE.PathActions.ELLIPSE: + var orientation = new THREE.Vector3(); - var aX = args[ 0 ], aY = args[ 1 ], - xRadius = args[ 2 ], - yRadius = args[ 3 ], - aStartAngle = args[ 4 ], aEndAngle = args[ 5 ], - aClockwise = !! args[ 6 ], - aRotation = args[ 7 ]; + return function updateMatrixWorld( force ) { + THREE.Object3D.prototype.updateMatrixWorld.call( this, force ); - var deltaAngle = aEndAngle - aStartAngle; - var angle; - var tdivisions = divisions * 2; + var listener = this.context.listener; + var up = this.up; - var cos, sin; - if ( aRotation !== 0 ) { - - cos = Math.cos( aRotation ); - sin = Math.sin( aRotation ); + this.matrixWorld.decompose( position, quaternion, scale ); - } + orientation.set( 0, 0, - 1 ).applyQuaternion( quaternion ); - for ( j = 1; j <= tdivisions; j ++ ) { + listener.setPosition( position.x, position.y, position.z ); + listener.setOrientation( orientation.x, orientation.y, orientation.z, up.x, up.y, up.z ); - t = j / tdivisions; + }; - if ( ! aClockwise ) { +} )(); - t = 1 - t; +// 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 + * + **/ - angle = aStartAngle + t * deltaAngle; +/************************************************************** + * Abstract Curve base class + **************************************************************/ - tx = aX + xRadius * Math.cos( angle ); - ty = aY + yRadius * Math.sin( angle ); +THREE.Curve = function () { - if ( aRotation !== 0 ) { +}; - var x = tx, y = ty; +// Virtual base class method to overwrite and implement in subclasses +// - t [0 .. 1] - // Rotate the point about the center of the ellipse. - tx = ( x - aX ) * cos - ( y - aY ) * sin + aX; - ty = ( x - aX ) * sin + ( y - aY ) * cos + aY; +THREE.Curve.prototype.getPoint = function ( t ) { - } + console.warn( "THREE.Curve: Warning, getPoint() not implemented!" ); + return null; - //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty); +}; - points.push( new THREE.Vector2( tx, ty ) ); +// Get point at relative position in curve according to arc length +// - u [0 .. 1] - } +THREE.Curve.prototype.getPointAt = function ( u ) { - //console.log(points); + var t = this.getUtoTmapping( u ); + return this.getPoint( t ); - break; +}; - } // end switch +// Get sequence of points using getPoint( t ) - } +THREE.Curve.prototype.getPoints = function ( divisions ) { + if ( ! divisions ) divisions = 5; + var d, pts = []; - // Normalize to remove the closing point by default. - var lastPoint = points[ points.length - 1 ]; - var EPSILON = 0.0000000001; - if ( Math.abs( lastPoint.x - points[ 0 ].x ) < EPSILON && - Math.abs( lastPoint.y - points[ 0 ].y ) < EPSILON ) - points.splice( points.length - 1, 1 ); - if ( closedPath ) { + for ( d = 0; d <= divisions; d ++ ) { - points.push( points[ 0 ] ); + pts.push( this.getPoint( d / divisions ) ); } - return points; + return pts; }; -// -// Breaks path into shapes -// -// Assumptions (if parameter isCCW==true the opposite holds): -// - solid shapes are defined clockwise (CW) -// - holes are defined counterclockwise (CCW) -// -// If parameter noHoles==true: -// - all subPaths are regarded as solid shapes -// - definition order CW/CCW has no relevance -// +// Get sequence of points using getPointAt( u ) -THREE.Path.prototype.toShapes = function( isCCW, noHoles ) { +THREE.Curve.prototype.getSpacedPoints = function ( divisions ) { - function extractSubpaths( inActions ) { + if ( ! divisions ) divisions = 5; - var i, il, item, action, args; + var d, pts = []; - var subPaths = [], lastPath = new THREE.Path(); + for ( d = 0; d <= divisions; d ++ ) { - for ( i = 0, il = inActions.length; i < il; i ++ ) { + pts.push( this.getPointAt( d / divisions ) ); - item = inActions[ i ]; + } - args = item.args; - action = item.action; + return pts; - if ( action === THREE.PathActions.MOVE_TO ) { +}; - if ( lastPath.actions.length !== 0 ) { +// Get total curve arc length - subPaths.push( lastPath ); - lastPath = new THREE.Path(); +THREE.Curve.prototype.getLength = function () { - } + var lengths = this.getLengths(); + return lengths[ lengths.length - 1 ]; - } +}; - lastPath[ action ].apply( lastPath, args ); +// Get list of cumulative segment lengths - } +THREE.Curve.prototype.getLengths = function ( divisions ) { - if ( lastPath.actions.length !== 0 ) { + if ( ! divisions ) divisions = ( this.__arcLengthDivisions ) ? ( this.__arcLengthDivisions ) : 200; - subPaths.push( lastPath ); + if ( this.cacheArcLengths + && ( this.cacheArcLengths.length === divisions + 1 ) + && ! this.needsUpdate ) { - } + //console.log( "cached", this.cacheArcLengths ); + return this.cacheArcLengths; - // console.log(subPaths); + } - return subPaths; + this.needsUpdate = false; + + var cache = []; + var current, last = this.getPoint( 0 ); + var p, sum = 0; + + cache.push( 0 ); + + for ( p = 1; p <= divisions; p ++ ) { + + current = this.getPoint ( p / divisions ); + sum += current.distanceTo( last ); + cache.push( sum ); + last = current; } - function toShapesNoHoles( inSubpaths ) { + this.cacheArcLengths = cache; - var shapes = []; + return cache; // { sums: cache, sum:sum }; Sum is in the last element. - for ( var i = 0, il = inSubpaths.length; i < il; i ++ ) { +}; - var tmpPath = inSubpaths[ i ]; - var tmpShape = new THREE.Shape(); - tmpShape.actions = tmpPath.actions; - tmpShape.curves = tmpPath.curves; +THREE.Curve.prototype.updateArcLengths = function() { - shapes.push( tmpShape ); + this.needsUpdate = true; + this.getLengths(); - } +}; - //console.log("shape", shapes); +// Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant - return shapes; +THREE.Curve.prototype.getUtoTmapping = function ( u, distance ) { - } + var arcLengths = this.getLengths(); - function isPointInsidePolygon( inPt, inPolygon ) { + var i = 0, il = arcLengths.length; - var EPSILON = 0.0000000001; + var targetArcLength; // The targeted u distance value to get + + if ( distance ) { + + targetArcLength = distance; - var polyLen = inPolygon.length; + } else { - // inPt on polygon contour => immediate success or - // toggling of inside/outside at every single! intersection point of an edge - // with the horizontal line through inPt, left of inPt - // not counting lowerY endpoints of edges and whole edges on that line - var inside = false; - for ( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) { + targetArcLength = u * arcLengths[ il - 1 ]; - var edgeLowPt = inPolygon[ p ]; - var edgeHighPt = inPolygon[ q ]; + } - var edgeDx = edgeHighPt.x - edgeLowPt.x; - var edgeDy = edgeHighPt.y - edgeLowPt.y; + //var time = Date.now(); - if ( Math.abs( edgeDy ) > EPSILON ) { + // binary search for the index with largest value smaller than target u distance - // not parallel - if ( edgeDy < 0 ) { + var low = 0, high = il - 1, comparison; - edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx; - edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy; + while ( low <= high ) { - } - if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue; + i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats - if ( inPt.y === edgeLowPt.y ) { + comparison = arcLengths[ i ] - targetArcLength; - if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ? - // continue; // no intersection or edgeLowPt => doesn't count !!! + if ( comparison < 0 ) { - } else { + low = i + 1; - var perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y ); - if ( perpEdge === 0 ) return true; // inPt is on contour ? - if ( perpEdge < 0 ) continue; - inside = ! inside; // true intersection left of inPt + } else if ( comparison > 0 ) { - } + high = i - 1; - } else { + } else { - // parallel or collinear - if ( inPt.y !== edgeLowPt.y ) continue; // parallel - // edge lies on the same horizontal line as inPt - if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) || - ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour ! - // continue; + high = i; + break; - } + // DONE } - return inside; - } + i = high; - var subPaths = extractSubpaths( this.actions ); - if ( subPaths.length === 0 ) return []; - - if ( noHoles === true ) return toShapesNoHoles( subPaths ); + //console.log('b' , i, low, high, Date.now()- time); + if ( arcLengths[ i ] === targetArcLength ) { - var solid, tmpPath, tmpShape, shapes = []; + var t = i / ( il - 1 ); + return t; - if ( subPaths.length === 1 ) { + } - tmpPath = subPaths[ 0 ]; - tmpShape = new THREE.Shape(); - tmpShape.actions = tmpPath.actions; - tmpShape.curves = tmpPath.curves; - shapes.push( tmpShape ); - return shapes; + // we could get finer grain at lengths, or use simple interpolation between two points - } + var lengthBefore = arcLengths[ i ]; + var lengthAfter = arcLengths[ i + 1 ]; - var holesFirst = ! THREE.Shape.Utils.isClockWise( subPaths[ 0 ].getPoints() ); - holesFirst = isCCW ? ! holesFirst : holesFirst; + var segmentLength = lengthAfter - lengthBefore; - // console.log("Holes first", holesFirst); + // determine where we are between the 'before' and 'after' points - var betterShapeHoles = []; - var newShapes = []; - var newShapeHoles = []; - var mainIdx = 0; - var tmpPoints; + var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; - newShapes[ mainIdx ] = undefined; - newShapeHoles[ mainIdx ] = []; + // add that fractional amount to t - var i, il; + var t = ( i + segmentFraction ) / ( il - 1 ); - for ( i = 0, il = subPaths.length; i < il; i ++ ) { + return t; - tmpPath = subPaths[ i ]; - tmpPoints = tmpPath.getPoints(); - solid = THREE.Shape.Utils.isClockWise( tmpPoints ); - solid = isCCW ? ! solid : solid; +}; - if ( solid ) { +// Returns a unit vector tangent at t +// In case any sub curve does not implement its tangent derivation, +// 2 points a small delta apart will be used to find its gradient +// which seems to give a reasonable approximation - if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++; +THREE.Curve.prototype.getTangent = function( t ) { - newShapes[ mainIdx ] = { s: new THREE.Shape(), p: tmpPoints }; - newShapes[ mainIdx ].s.actions = tmpPath.actions; - newShapes[ mainIdx ].s.curves = tmpPath.curves; + var delta = 0.0001; + var t1 = t - delta; + var t2 = t + delta; - if ( holesFirst ) mainIdx ++; - newShapeHoles[ mainIdx ] = []; + // Capping in case of danger - //console.log('cw', i); + if ( t1 < 0 ) t1 = 0; + if ( t2 > 1 ) t2 = 1; - } else { + var pt1 = this.getPoint( t1 ); + var pt2 = this.getPoint( t2 ); - newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } ); + var vec = pt2.clone().sub( pt1 ); + return vec.normalize(); - //console.log('ccw', i); +}; - } - } +THREE.Curve.prototype.getTangentAt = function ( u ) { - // only Holes? -> probably all Shapes with wrong orientation - if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths ); + var t = this.getUtoTmapping( u ); + return this.getTangent( t ); +}; - if ( newShapes.length > 1 ) { - var ambiguous = false; - var toChange = []; - for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { - betterShapeHoles[ sIdx ] = []; - } - for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { +/************************************************************** + * Utils + **************************************************************/ - var sho = newShapeHoles[ sIdx ]; - for ( var hIdx = 0; hIdx < sho.length; hIdx ++ ) { +THREE.Curve.Utils = { - var ho = sho[ hIdx ]; - var hole_unassigned = true; - for ( var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) { + tangentQuadraticBezier: function ( t, p0, p1, p2 ) { - if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) { + return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 ); - if ( sIdx !== s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } ); - if ( hole_unassigned ) { + }, - hole_unassigned = false; - betterShapeHoles[ s2Idx ].push( ho ); + // Puay Bing, thanks for helping with this derivative! - } else { + tangentCubicBezier: function ( t, p0, p1, p2, p3 ) { - ambiguous = true; + return - 3 * p0 * ( 1 - t ) * ( 1 - t ) + + 3 * p1 * ( 1 - t ) * ( 1 - t ) - 6 * t * p1 * ( 1 - t ) + + 6 * t * p2 * ( 1 - t ) - 3 * t * t * p2 + + 3 * t * t * p3; - } + }, - } + tangentSpline: function ( t, p0, p1, p2, p3 ) { - } - if ( hole_unassigned ) { + // To check if my formulas are correct - betterShapeHoles[ sIdx ].push( ho ); + var h00 = 6 * t * t - 6 * t; // derived from 2t^3 − 3t^2 + 1 + var h10 = 3 * t * t - 4 * t + 1; // t^3 − 2t^2 + t + var h01 = - 6 * t * t + 6 * t; // − 2t3 + 3t2 + var h11 = 3 * t * t - 2 * t; // t3 − t2 - } + return h00 + h10 + h01 + h11; - } + }, - } - // console.log("ambiguous: ", ambiguous); - if ( toChange.length > 0 ) { + // Catmull-Rom - // console.log("to change: ", toChange); - if ( ! ambiguous ) newShapeHoles = betterShapeHoles; + interpolate: function( p0, p1, p2, p3, t ) { - } + var v0 = ( p2 - p0 ) * 0.5; + var v1 = ( p3 - p1 ) * 0.5; + var t2 = t * t; + var t3 = t * t2; + return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; } - var tmpHoles, j, jl; - for ( i = 0, il = newShapes.length; i < il; i ++ ) { +}; - tmpShape = newShapes[ i ].s; - shapes.push( tmpShape ); - tmpHoles = newShapeHoles[ i ]; - for ( j = 0, jl = tmpHoles.length; j < jl; j ++ ) { - tmpShape.holes.push( tmpHoles[ j ].h ); +// TODO: Transformation for Curves? - } +/************************************************************** + * 3D Curves + **************************************************************/ - } +// A Factory method for creating new curve subclasses - //console.log("shape", shapes); +THREE.Curve.create = function ( constructor, getPointFunc ) { - return shapes; + constructor.prototype = Object.create( THREE.Curve.prototype ); + constructor.prototype.constructor = constructor; + constructor.prototype.getPoint = getPointFunc; + + return constructor; }; -// File:src/extras/core/Shape.js +// File:src/extras/core/CurvePath.js /** * @author zz85 / http://www.lab4games.net/zz85/blog - * Defines a 2d shape plane using paths. + * **/ -// STEP 1 Create a path. -// STEP 2 Turn path into shape. -// STEP 3 ExtrudeGeometry takes in Shape/Shapes -// STEP 3a - Extract points from each shape, turn to vertices -// STEP 3b - Triangulate each shape, add faces. +/************************************************************** + * Curved Path - a curve path is simply a array of connected + * curves, but retains the api of a curve + **************************************************************/ -THREE.Shape = function () { +THREE.CurvePath = function () { - THREE.Path.apply( this, arguments ); - this.holes = []; + this.curves = []; + this.bends = []; + + this.autoClose = false; // Automatically closes the path }; -THREE.Shape.prototype = Object.create( THREE.Path.prototype ); -THREE.Shape.prototype.constructor = THREE.Shape; +THREE.CurvePath.prototype = Object.create( THREE.Curve.prototype ); +THREE.CurvePath.prototype.constructor = THREE.CurvePath; -// Convenience method to return ExtrudeGeometry +THREE.CurvePath.prototype.add = function ( curve ) { -THREE.Shape.prototype.extrude = function ( options ) { + this.curves.push( curve ); - var extruded = new THREE.ExtrudeGeometry( this, options ); - return extruded; +}; +THREE.CurvePath.prototype.checkConnection = function() { + // TODO + // If the ending of curve is not connected to the starting + // or the next curve, then, this is not a real path }; -// Convenience method to return ShapeGeometry +THREE.CurvePath.prototype.closePath = function() { -THREE.Shape.prototype.makeGeometry = function ( options ) { + // TODO Test + // and verify for vector3 (needs to implement equals) + // Add a line curve if start and end of lines are not connected + var startPoint = this.curves[ 0 ].getPoint( 0 ); + var endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 ); - var geometry = new THREE.ShapeGeometry( this, options ); - return geometry; + if ( ! startPoint.equals( endPoint ) ) { -}; + this.curves.push( new THREE.LineCurve( endPoint, startPoint ) ); -// Get points of holes + } -THREE.Shape.prototype.getPointsHoles = function ( divisions ) { +}; - var i, il = this.holes.length, holesPts = []; +// To get accurate point with reference to +// entire path distance at time t, +// following has to be done: - for ( i = 0; i < il; i ++ ) { +// 1. Length of each sub path have to be known +// 2. Locate and identify type of curve +// 3. Get t for the curve +// 4. Return curve.getPointAt(t') - holesPts[ i ] = this.holes[ i ].getTransformedPoints( divisions, this.bends ); +THREE.CurvePath.prototype.getPoint = function( t ) { - } + var d = t * this.getLength(); + var curveLengths = this.getCurveLengths(); + var i = 0, diff, curve; - return holesPts; + // To think about boundaries points. -}; + while ( i < curveLengths.length ) { -// Get points of holes (spaced by regular distance) + if ( curveLengths[ i ] >= d ) { -THREE.Shape.prototype.getSpacedPointsHoles = function ( divisions ) { + diff = curveLengths[ i ] - d; + curve = this.curves[ i ]; - var i, il = this.holes.length, holesPts = []; + var u = 1 - diff / curve.getLength(); - for ( i = 0; i < il; i ++ ) { + return curve.getPointAt( u ); - holesPts[ i ] = this.holes[ i ].getTransformedSpacedPoints( divisions, this.bends ); + } + + i ++; } - return holesPts; + return null; -}; + // loop where sum != 0, sum > d , sum+1 maxX ) maxX = p.x; + else if ( p.x < minX ) minX = p.x; - return ( ( inSegPt1.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt2.x ) ); + if ( p.y > maxY ) maxY = p.y; + else if ( p.y < minY ) minY = p.y; - } else { + if ( v3 ) { - return ( ( inSegPt2.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt1.x ) ); + if ( p.z > maxZ ) maxZ = p.z; + else if ( p.z < minZ ) minZ = p.z; - } + } - } else { + sum.add( p ); - if ( inSegPt1.y < inSegPt2.y ) { + } - return ( ( inSegPt1.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt2.y ) ); + var ret = { - } else { + minX: minX, + minY: minY, + maxX: maxX, + maxY: maxY - return ( ( inSegPt2.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt1.y ) ); + }; - } + if ( v3 ) { - } + ret.maxZ = maxZ; + ret.minZ = minZ; - } + } - function intersect_segments_2D( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1, inSeg2Pt2, inExcludeAdjacentSegs ) { + return ret; - var EPSILON = 0.0000000001; +}; - var seg1dx = inSeg1Pt2.x - inSeg1Pt1.x, seg1dy = inSeg1Pt2.y - inSeg1Pt1.y; - var seg2dx = inSeg2Pt2.x - inSeg2Pt1.x, seg2dy = inSeg2Pt2.y - inSeg2Pt1.y; +/************************************************************** + * Create Geometries Helpers + **************************************************************/ - var seg1seg2dx = inSeg1Pt1.x - inSeg2Pt1.x; - var seg1seg2dy = inSeg1Pt1.y - inSeg2Pt1.y; +/// Generate geometry from path points (for Line or Points objects) - var limit = seg1dy * seg2dx - seg1dx * seg2dy; - var perpSeg1 = seg1dy * seg1seg2dx - seg1dx * seg1seg2dy; +THREE.CurvePath.prototype.createPointsGeometry = function( divisions ) { - if ( Math.abs( limit ) > EPSILON ) { + var pts = this.getPoints( divisions, true ); + return this.createGeometry( pts ); - // not parallel +}; - var perpSeg2; - if ( limit > 0 ) { +// Generate geometry from equidistant sampling along the path - if ( ( perpSeg1 < 0 ) || ( perpSeg1 > limit ) ) return []; - perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy; - if ( ( perpSeg2 < 0 ) || ( perpSeg2 > limit ) ) return []; +THREE.CurvePath.prototype.createSpacedPointsGeometry = function( divisions ) { - } else { + var pts = this.getSpacedPoints( divisions, true ); + return this.createGeometry( pts ); - if ( ( perpSeg1 > 0 ) || ( perpSeg1 < limit ) ) return []; - perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy; - if ( ( perpSeg2 > 0 ) || ( perpSeg2 < limit ) ) return []; +}; - } +THREE.CurvePath.prototype.createGeometry = function( points ) { - // i.e. to reduce rounding errors - // intersection at endpoint of segment#1? - if ( perpSeg2 === 0 ) { + var geometry = new THREE.Geometry(); - if ( ( inExcludeAdjacentSegs ) && - ( ( perpSeg1 === 0 ) || ( perpSeg1 === limit ) ) ) return []; - return [ inSeg1Pt1 ]; + for ( var i = 0; i < points.length; i ++ ) { - } - if ( perpSeg2 === limit ) { + geometry.vertices.push( new THREE.Vector3( points[ i ].x, points[ i ].y, points[ i ].z || 0 ) ); - if ( ( inExcludeAdjacentSegs ) && - ( ( perpSeg1 === 0 ) || ( perpSeg1 === limit ) ) ) return []; - return [ inSeg1Pt2 ]; + } - } - // intersection at endpoint of segment#2? - if ( perpSeg1 === 0 ) return [ inSeg2Pt1 ]; - if ( perpSeg1 === limit ) return [ inSeg2Pt2 ]; + return geometry; - // return real intersection point - var factorSeg1 = perpSeg2 / limit; - return [ { x: inSeg1Pt1.x + factorSeg1 * seg1dx, - y: inSeg1Pt1.y + factorSeg1 * seg1dy } ]; +}; - } else { - // parallel or collinear - if ( ( perpSeg1 !== 0 ) || - ( seg2dy * seg1seg2dx !== seg2dx * seg1seg2dy ) ) return []; +/************************************************************** + * Bend / Wrap Helper Methods + **************************************************************/ - // they are collinear or degenerate - var seg1Pt = ( ( seg1dx === 0 ) && ( seg1dy === 0 ) ); // segment1 is just a point? - var seg2Pt = ( ( seg2dx === 0 ) && ( seg2dy === 0 ) ); // segment2 is just a point? - // both segments are points - if ( seg1Pt && seg2Pt ) { +// Wrap path / Bend modifiers? + +THREE.CurvePath.prototype.addWrapPath = function ( bendpath ) { + + this.bends.push( bendpath ); + +}; + +THREE.CurvePath.prototype.getTransformedPoints = function( segments, bends ) { + + var oldPts = this.getPoints( segments ); // getPoints getSpacedPoints + var i, il; + + if ( ! bends ) { + + bends = this.bends; + + } - if ( ( inSeg1Pt1.x !== inSeg2Pt1.x ) || - ( inSeg1Pt1.y !== inSeg2Pt1.y ) ) return []; // they are distinct points - return [ inSeg1Pt1 ]; // they are the same point + for ( i = 0, il = bends.length; i < il; i ++ ) { - } - // segment#1 is a single point - if ( seg1Pt ) { + oldPts = this.getWrapPoints( oldPts, bends[ i ] ); - if ( ! point_in_segment_2D_colin( inSeg2Pt1, inSeg2Pt2, inSeg1Pt1 ) ) return []; // but not in segment#2 - return [ inSeg1Pt1 ]; + } - } - // segment#2 is a single point - if ( seg2Pt ) { + return oldPts; - if ( ! point_in_segment_2D_colin( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1 ) ) return []; // but not in segment#1 - return [ inSeg2Pt1 ]; +}; - } +THREE.CurvePath.prototype.getTransformedSpacedPoints = function( segments, bends ) { - // they are collinear segments, which might overlap - var seg1min, seg1max, seg1minVal, seg1maxVal; - var seg2min, seg2max, seg2minVal, seg2maxVal; - if ( seg1dx !== 0 ) { + var oldPts = this.getSpacedPoints( segments ); - // the segments are NOT on a vertical line - if ( inSeg1Pt1.x < inSeg1Pt2.x ) { + var i, il; - seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.x; - seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.x; + if ( ! bends ) { - } else { + bends = this.bends; - seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.x; - seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.x; + } - } - if ( inSeg2Pt1.x < inSeg2Pt2.x ) { + for ( i = 0, il = bends.length; i < il; i ++ ) { - seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.x; - seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.x; + oldPts = this.getWrapPoints( oldPts, bends[ i ] ); - } else { + } - seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.x; - seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.x; + return oldPts; - } +}; - } else { +// This returns getPoints() bend/wrapped around the contour of a path. +// Read http://www.planetclegg.com/projects/WarpingTextToSplines.html - // the segments are on a vertical line - if ( inSeg1Pt1.y < inSeg1Pt2.y ) { +THREE.CurvePath.prototype.getWrapPoints = function ( oldPts, path ) { - seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.y; - seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.y; + var bounds = this.getBoundingBox(); - } else { + var i, il, p, oldX, oldY, xNorm; - seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.y; - seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.y; + for ( i = 0, il = oldPts.length; i < il; i ++ ) { - } - if ( inSeg2Pt1.y < inSeg2Pt2.y ) { + p = oldPts[ i ]; - seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.y; - seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.y; + oldX = p.x; + oldY = p.y; - } else { + xNorm = oldX / bounds.maxX; - seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.y; - seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.y; + // If using actual distance, for length > path, requires line extrusions + //xNorm = path.getUtoTmapping(xNorm, oldX); // 3 styles. 1) wrap stretched. 2) wrap stretch by arc length 3) warp by actual distance - } + xNorm = path.getUtoTmapping( xNorm, oldX ); - } - if ( seg1minVal <= seg2minVal ) { + // check for out of bounds? - if ( seg1maxVal < seg2minVal ) return []; - if ( seg1maxVal === seg2minVal ) { + var pathPt = path.getPoint( xNorm ); + var normal = path.getTangent( xNorm ); + normal.set( - normal.y, normal.x ).multiplyScalar( oldY ); - if ( inExcludeAdjacentSegs ) return []; - return [ seg2min ]; + p.x = pathPt.x + normal.x; + p.y = pathPt.y + normal.y; - } - if ( seg1maxVal <= seg2maxVal ) return [ seg2min, seg1max ]; - return [ seg2min, seg2max ]; + } - } else { + return oldPts; - if ( seg1minVal > seg2maxVal ) return []; - if ( seg1minVal === seg2maxVal ) { +}; - if ( inExcludeAdjacentSegs ) return []; - return [ seg1min ]; +// File:src/extras/core/Path.js - } - if ( seg1maxVal <= seg2maxVal ) return [ seg1min, seg1max ]; - return [ seg1min, seg2max ]; +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * Creates free form 2d path using series of points, lines or curves. + * + **/ - } +THREE.Path = function ( points ) { - } + THREE.CurvePath.call( this ); - } + this.actions = []; - function isPointInsideAngle( inVertex, inLegFromPt, inLegToPt, inOtherPt ) { + if ( points ) { - // The order of legs is important + this.fromPoints( points ); - var EPSILON = 0.0000000001; + } - // translation of all points, so that Vertex is at (0,0) - var legFromPtX = inLegFromPt.x - inVertex.x, legFromPtY = inLegFromPt.y - inVertex.y; - var legToPtX = inLegToPt.x - inVertex.x, legToPtY = inLegToPt.y - inVertex.y; - var otherPtX = inOtherPt.x - inVertex.x, otherPtY = inOtherPt.y - inVertex.y; +}; - // main angle >0: < 180 deg.; 0: 180 deg.; <0: > 180 deg. - var from2toAngle = legFromPtX * legToPtY - legFromPtY * legToPtX; - var from2otherAngle = legFromPtX * otherPtY - legFromPtY * otherPtX; +THREE.Path.prototype = Object.create( THREE.CurvePath.prototype ); +THREE.Path.prototype.constructor = THREE.Path; - if ( Math.abs( from2toAngle ) > EPSILON ) { +THREE.PathActions = { - // angle != 180 deg. + MOVE_TO: 'moveTo', + LINE_TO: 'lineTo', + QUADRATIC_CURVE_TO: 'quadraticCurveTo', // Bezier quadratic curve + BEZIER_CURVE_TO: 'bezierCurveTo', // Bezier cubic curve + CSPLINE_THRU: 'splineThru', // Catmull-Rom spline + ARC: 'arc', // Circle + ELLIPSE: 'ellipse' +}; - var other2toAngle = otherPtX * legToPtY - otherPtY * legToPtX; - // console.log( "from2to: " + from2toAngle + ", from2other: " + from2otherAngle + ", other2to: " + other2toAngle ); +// TODO Clean up PATH API - if ( from2toAngle > 0 ) { +// Create path using straight lines to connect all points +// - vectors: array of Vector2 - // main angle < 180 deg. - return ( ( from2otherAngle >= 0 ) && ( other2toAngle >= 0 ) ); +THREE.Path.prototype.fromPoints = function ( vectors ) { - } else { + this.moveTo( vectors[ 0 ].x, vectors[ 0 ].y ); - // main angle > 180 deg. - return ( ( from2otherAngle >= 0 ) || ( other2toAngle >= 0 ) ); + for ( var v = 1, vlen = vectors.length; v < vlen; v ++ ) { - } + this.lineTo( vectors[ v ].x, vectors[ v ].y ); - } else { + } - // angle == 180 deg. - // console.log( "from2to: 180 deg., from2other: " + from2otherAngle ); - return ( from2otherAngle > 0 ); +}; - } +// startPath() endPath()? - } +THREE.Path.prototype.moveTo = function ( x, y ) { + var args = Array.prototype.slice.call( arguments ); + this.actions.push( { action: THREE.PathActions.MOVE_TO, args: args } ); - function removeHoles( contour, holes ) { +}; - var shape = contour.concat(); // work on this shape - var hole; +THREE.Path.prototype.lineTo = function ( x, y ) { - function isCutLineInsideAngles( inShapeIdx, inHoleIdx ) { + var args = Array.prototype.slice.call( arguments ); - // Check if hole point lies within angle around shape point - var lastShapeIdx = shape.length - 1; + var lastargs = this.actions[ this.actions.length - 1 ].args; - var prevShapeIdx = inShapeIdx - 1; - if ( prevShapeIdx < 0 ) prevShapeIdx = lastShapeIdx; + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; - var nextShapeIdx = inShapeIdx + 1; - if ( nextShapeIdx > lastShapeIdx ) nextShapeIdx = 0; + var curve = new THREE.LineCurve( new THREE.Vector2( x0, y0 ), new THREE.Vector2( x, y ) ); + this.curves.push( curve ); - var insideAngle = isPointInsideAngle( shape[ inShapeIdx ], shape[ prevShapeIdx ], shape[ nextShapeIdx ], hole[ inHoleIdx ] ); - if ( ! insideAngle ) { + this.actions.push( { action: THREE.PathActions.LINE_TO, args: args } ); - // console.log( "Vertex (Shape): " + inShapeIdx + ", Point: " + hole[inHoleIdx].x + "/" + hole[inHoleIdx].y ); - return false; +}; - } +THREE.Path.prototype.quadraticCurveTo = function( aCPx, aCPy, aX, aY ) { - // Check if shape point lies within angle around hole point - var lastHoleIdx = hole.length - 1; + var args = Array.prototype.slice.call( arguments ); - var prevHoleIdx = inHoleIdx - 1; - if ( prevHoleIdx < 0 ) prevHoleIdx = lastHoleIdx; + var lastargs = this.actions[ this.actions.length - 1 ].args; - var nextHoleIdx = inHoleIdx + 1; - if ( nextHoleIdx > lastHoleIdx ) nextHoleIdx = 0; + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; - insideAngle = isPointInsideAngle( hole[ inHoleIdx ], hole[ prevHoleIdx ], hole[ nextHoleIdx ], shape[ inShapeIdx ] ); - if ( ! insideAngle ) { + var curve = new THREE.QuadraticBezierCurve( new THREE.Vector2( x0, y0 ), + new THREE.Vector2( aCPx, aCPy ), + new THREE.Vector2( aX, aY ) ); + this.curves.push( curve ); - // console.log( "Vertex (Hole): " + inHoleIdx + ", Point: " + shape[inShapeIdx].x + "/" + shape[inShapeIdx].y ); - return false; + this.actions.push( { action: THREE.PathActions.QUADRATIC_CURVE_TO, args: args } ); - } +}; - return true; +THREE.Path.prototype.bezierCurveTo = function( aCP1x, aCP1y, + aCP2x, aCP2y, + aX, aY ) { - } + var args = Array.prototype.slice.call( arguments ); - function intersectsShapeEdge( inShapePt, inHolePt ) { + var lastargs = this.actions[ this.actions.length - 1 ].args; - // checks for intersections with shape edges - var sIdx, nextIdx, intersection; - for ( sIdx = 0; sIdx < shape.length; sIdx ++ ) { + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; - nextIdx = sIdx + 1; nextIdx %= shape.length; - intersection = intersect_segments_2D( inShapePt, inHolePt, shape[ sIdx ], shape[ nextIdx ], true ); - if ( intersection.length > 0 ) return true; + var curve = new THREE.CubicBezierCurve( new THREE.Vector2( x0, y0 ), + new THREE.Vector2( aCP1x, aCP1y ), + new THREE.Vector2( aCP2x, aCP2y ), + new THREE.Vector2( aX, aY ) ); + this.curves.push( curve ); - } + this.actions.push( { action: THREE.PathActions.BEZIER_CURVE_TO, args: args } ); - return false; +}; - } +THREE.Path.prototype.splineThru = function( pts /*Array of Vector*/ ) { - var indepHoles = []; + var args = Array.prototype.slice.call( arguments ); + var lastargs = this.actions[ this.actions.length - 1 ].args; - function intersectsHoleEdge( inShapePt, inHolePt ) { + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; + //--- + var npts = [ new THREE.Vector2( x0, y0 ) ]; + Array.prototype.push.apply( npts, pts ); - // checks for intersections with hole edges - var ihIdx, chkHole, - hIdx, nextIdx, intersection; - for ( ihIdx = 0; ihIdx < indepHoles.length; ihIdx ++ ) { + var curve = new THREE.SplineCurve( npts ); + this.curves.push( curve ); - chkHole = holes[ indepHoles[ ihIdx ]]; - for ( hIdx = 0; hIdx < chkHole.length; hIdx ++ ) { + this.actions.push( { action: THREE.PathActions.CSPLINE_THRU, args: args } ); - nextIdx = hIdx + 1; nextIdx %= chkHole.length; - intersection = intersect_segments_2D( inShapePt, inHolePt, chkHole[ hIdx ], chkHole[ nextIdx ], true ); - if ( intersection.length > 0 ) return true; +}; - } +// FUTURE: Change the API or follow canvas API? - } - return false; +THREE.Path.prototype.arc = function ( aX, aY, aRadius, + aStartAngle, aEndAngle, aClockwise ) { - } + var lastargs = this.actions[ this.actions.length - 1 ].args; + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; - var holeIndex, shapeIndex, - shapePt, holePt, - holeIdx, cutKey, failedCuts = [], - tmpShape1, tmpShape2, - tmpHole1, tmpHole2; + this.absarc( aX + x0, aY + y0, aRadius, + aStartAngle, aEndAngle, aClockwise ); - for ( var h = 0, hl = holes.length; h < hl; h ++ ) { + }; - indepHoles.push( h ); + THREE.Path.prototype.absarc = function ( aX, aY, aRadius, + aStartAngle, aEndAngle, aClockwise ) { - } + this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); - var minShapeIndex = 0; - var counter = indepHoles.length * 2; - while ( indepHoles.length > 0 ) { + }; - counter --; - if ( counter < 0 ) { +THREE.Path.prototype.ellipse = function ( aX, aY, xRadius, yRadius, + aStartAngle, aEndAngle, aClockwise, aRotation ) { - console.log( "Infinite Loop! Holes left:" + indepHoles.length + ", Probably Hole outside Shape!" ); - break; + var lastargs = this.actions[ this.actions.length - 1 ].args; + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; - } + this.absellipse( aX + x0, aY + y0, xRadius, yRadius, + aStartAngle, aEndAngle, aClockwise, aRotation ); - // search for shape-vertex and hole-vertex, - // which can be connected without intersections - for ( shapeIndex = minShapeIndex; shapeIndex < shape.length; shapeIndex ++ ) { + }; - shapePt = shape[ shapeIndex ]; - holeIndex = - 1; - // search for hole which can be reached without intersections - for ( var h = 0; h < indepHoles.length; h ++ ) { +THREE.Path.prototype.absellipse = function ( aX, aY, xRadius, yRadius, + aStartAngle, aEndAngle, aClockwise, aRotation ) { - holeIdx = indepHoles[ h ]; + var args = [ + aX, aY, + xRadius, yRadius, + aStartAngle, aEndAngle, + aClockwise, + aRotation || 0 // aRotation is optional. + ]; + var curve = new THREE.EllipseCurve( aX, aY, xRadius, yRadius, + aStartAngle, aEndAngle, aClockwise, aRotation ); + this.curves.push( curve ); - // prevent multiple checks - cutKey = shapePt.x + ":" + shapePt.y + ":" + holeIdx; - if ( failedCuts[ cutKey ] !== undefined ) continue; + var lastPoint = curve.getPoint( 1 ); + args.push( lastPoint.x ); + args.push( lastPoint.y ); - hole = holes[ holeIdx ]; - for ( var h2 = 0; h2 < hole.length; h2 ++ ) { + this.actions.push( { action: THREE.PathActions.ELLIPSE, args: args } ); - holePt = hole[ h2 ]; - if ( ! isCutLineInsideAngles( shapeIndex, h2 ) ) continue; - if ( intersectsShapeEdge( shapePt, holePt ) ) continue; - if ( intersectsHoleEdge( shapePt, holePt ) ) continue; + }; - holeIndex = h2; - indepHoles.splice( h, 1 ); +THREE.Path.prototype.getSpacedPoints = function ( divisions, closedPath ) { - tmpShape1 = shape.slice( 0, shapeIndex + 1 ); - tmpShape2 = shape.slice( shapeIndex ); - tmpHole1 = hole.slice( holeIndex ); - tmpHole2 = hole.slice( 0, holeIndex + 1 ); + if ( ! divisions ) divisions = 40; - shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 ); + var points = []; - minShapeIndex = shapeIndex; + for ( var i = 0; i < divisions; i ++ ) { - // Debug only, to show the selected cuts - // glob_CutLines.push( [ shapePt, holePt ] ); + points.push( this.getPoint( i / divisions ) ); - break; + //if( !this.getPoint( i / divisions ) ) throw "DIE"; - } - if ( holeIndex >= 0 ) break; // hole-vertex found + } - failedCuts[ cutKey ] = true; // remember failure + // if ( closedPath ) { + // + // points.push( points[ 0 ] ); + // + // } - } - if ( holeIndex >= 0 ) break; // hole-vertex found + return points; - } +}; - } +/* Return an array of vectors based on contour of the path */ - return shape; /* shape with no holes */ +THREE.Path.prototype.getPoints = function( divisions, closedPath ) { - } + if ( this.useSpacedPoints ) { + return this.getSpacedPoints( divisions, closedPath ); - var i, il, f, face, - key, index, - allPointsMap = {}; + } - // To maintain reference to old shape, one must match coordinates, or offset the indices from original arrays. It's probably easier to do the first. + divisions = divisions || 12; - var allpoints = contour.concat(); + var points = []; - for ( var h = 0, hl = holes.length; h < hl; h ++ ) { + var i, il, item, action, args; + var cpx, cpy, cpx2, cpy2, cpx1, cpy1, cpx0, cpy0, + laste, j, + t, tx, ty; - Array.prototype.push.apply( allpoints, holes[ h ] ); + for ( i = 0, il = this.actions.length; i < il; i ++ ) { - } + item = this.actions[ i ]; - //console.log( "allpoints",allpoints, allpoints.length ); + action = item.action; + args = item.args; - // prepare all points map + switch ( action ) { - for ( i = 0, il = allpoints.length; i < il; i ++ ) { + case THREE.PathActions.MOVE_TO: - key = allpoints[ i ].x + ":" + allpoints[ i ].y; + points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) ); - if ( allPointsMap[ key ] !== undefined ) { + break; - console.warn( "THREE.Shape: Duplicate point", key ); + case THREE.PathActions.LINE_TO: - } + points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) ); - allPointsMap[ key ] = i; + break; - } + case THREE.PathActions.QUADRATIC_CURVE_TO: - // remove holes by cutting paths to holes and adding them to the shape - var shapeWithoutHoles = removeHoles( contour, holes ); + cpx = args[ 2 ]; + cpy = args[ 3 ]; - var triangles = THREE.FontUtils.Triangulate( shapeWithoutHoles, false ); // True returns indices for points of spooled shape - //console.log( "triangles",triangles, triangles.length ); + cpx1 = args[ 0 ]; + cpy1 = args[ 1 ]; - // check all face vertices against all points map + if ( points.length > 0 ) { - for ( i = 0, il = triangles.length; i < il; i ++ ) { + laste = points[ points.length - 1 ]; - face = triangles[ i ]; + cpx0 = laste.x; + cpy0 = laste.y; - for ( f = 0; f < 3; f ++ ) { + } else { - key = face[ f ].x + ":" + face[ f ].y; + laste = this.actions[ i - 1 ].args; - index = allPointsMap[ key ]; + cpx0 = laste[ laste.length - 2 ]; + cpy0 = laste[ laste.length - 1 ]; - if ( index !== undefined ) { + } - face[ f ] = index; + for ( j = 1; j <= divisions; j ++ ) { - } + t = j / divisions; + + tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx ); + ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy ); + + points.push( new THREE.Vector2( tx, ty ) ); } - } + break; - return triangles.concat(); + case THREE.PathActions.BEZIER_CURVE_TO: - }, + cpx = args[ 4 ]; + cpy = args[ 5 ]; - isClockWise: function ( pts ) { + cpx1 = args[ 0 ]; + cpy1 = args[ 1 ]; - return THREE.FontUtils.Triangulate.area( pts ) < 0; + cpx2 = args[ 2 ]; + cpy2 = args[ 3 ]; - }, + if ( points.length > 0 ) { - // Bezier Curves formulas obtained from - // http://en.wikipedia.org/wiki/B%C3%A9zier_curve + laste = points[ points.length - 1 ]; - // Quad Bezier Functions + cpx0 = laste.x; + cpy0 = laste.y; - b2p0: function ( t, p ) { + } else { - var k = 1 - t; - return k * k * p; + laste = this.actions[ i - 1 ].args; - }, + cpx0 = laste[ laste.length - 2 ]; + cpy0 = laste[ laste.length - 1 ]; - b2p1: function ( t, p ) { + } - return 2 * ( 1 - t ) * t * p; - }, + for ( j = 1; j <= divisions; j ++ ) { - b2p2: function ( t, p ) { + t = j / divisions; - return t * t * p; + tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx ); + ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy ); - }, + points.push( new THREE.Vector2( tx, ty ) ); - b2: function ( t, p0, p1, p2 ) { + } - return this.b2p0( t, p0 ) + this.b2p1( t, p1 ) + this.b2p2( t, p2 ); + break; - }, + case THREE.PathActions.CSPLINE_THRU: - // Cubic Bezier Functions + laste = this.actions[ i - 1 ].args; - b3p0: function ( t, p ) { + var last = new THREE.Vector2( laste[ laste.length - 2 ], laste[ laste.length - 1 ] ); + var spts = [ last ]; - var k = 1 - t; - return k * k * k * p; + var n = divisions * args[ 0 ].length; - }, + spts = spts.concat( args[ 0 ] ); - b3p1: function ( t, p ) { + var spline = new THREE.SplineCurve( spts ); - var k = 1 - t; - return 3 * k * k * t * p; + for ( j = 1; j <= n; j ++ ) { - }, + points.push( spline.getPointAt( j / n ) ); - b3p2: function ( t, p ) { + } - var k = 1 - t; - return 3 * k * t * t * p; + break; - }, + case THREE.PathActions.ARC: - b3p3: function ( t, p ) { + var aX = args[ 0 ], aY = args[ 1 ], + aRadius = args[ 2 ], + aStartAngle = args[ 3 ], aEndAngle = args[ 4 ], + aClockwise = !! args[ 5 ]; - return t * t * t * p; + var deltaAngle = aEndAngle - aStartAngle; + var angle; + var tdivisions = divisions * 2; - }, + for ( j = 1; j <= tdivisions; j ++ ) { - b3: function ( t, p0, p1, p2, p3 ) { + t = j / tdivisions; - return this.b3p0( t, p0 ) + this.b3p1( t, p1 ) + this.b3p2( t, p2 ) + this.b3p3( t, p3 ); + if ( ! aClockwise ) { - } + t = 1 - t; -}; + } -// File:src/extras/curves/LineCurve.js + angle = aStartAngle + t * deltaAngle; -/************************************************************** - * Line - **************************************************************/ + tx = aX + aRadius * Math.cos( angle ); + ty = aY + aRadius * Math.sin( angle ); -THREE.LineCurve = function ( v1, v2 ) { + //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty); - this.v1 = v1; - this.v2 = v2; + points.push( new THREE.Vector2( tx, ty ) ); -}; + } -THREE.LineCurve.prototype = Object.create( THREE.Curve.prototype ); -THREE.LineCurve.prototype.constructor = THREE.LineCurve; + //console.log(points); -THREE.LineCurve.prototype.getPoint = function ( t ) { + break; - var point = this.v2.clone().sub( this.v1 ); - point.multiplyScalar( t ).add( this.v1 ); + case THREE.PathActions.ELLIPSE: - return point; + var aX = args[ 0 ], aY = args[ 1 ], + xRadius = args[ 2 ], + yRadius = args[ 3 ], + aStartAngle = args[ 4 ], aEndAngle = args[ 5 ], + aClockwise = !! args[ 6 ], + aRotation = args[ 7 ]; -}; -// Line curve is linear, so we can overwrite default getPointAt + var deltaAngle = aEndAngle - aStartAngle; + var angle; + var tdivisions = divisions * 2; -THREE.LineCurve.prototype.getPointAt = function ( u ) { + var cos, sin; + if ( aRotation !== 0 ) { + + cos = Math.cos( aRotation ); + sin = Math.sin( aRotation ); - return this.getPoint( u ); + } -}; + for ( j = 1; j <= tdivisions; j ++ ) { -THREE.LineCurve.prototype.getTangent = function( t ) { + t = j / tdivisions; - var tangent = this.v2.clone().sub( this.v1 ); + if ( ! aClockwise ) { - return tangent.normalize(); + t = 1 - t; -}; + } -// File:src/extras/curves/QuadraticBezierCurve.js + angle = aStartAngle + t * deltaAngle; -/************************************************************** - * Quadratic Bezier curve - **************************************************************/ + tx = aX + xRadius * Math.cos( angle ); + ty = aY + yRadius * Math.sin( angle ); + if ( aRotation !== 0 ) { -THREE.QuadraticBezierCurve = function ( v0, v1, v2 ) { + var x = tx, y = ty; - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; + // Rotate the point about the center of the ellipse. + tx = ( x - aX ) * cos - ( y - aY ) * sin + aX; + ty = ( x - aX ) * sin + ( y - aY ) * cos + aY; -}; + } -THREE.QuadraticBezierCurve.prototype = Object.create( THREE.Curve.prototype ); -THREE.QuadraticBezierCurve.prototype.constructor = THREE.QuadraticBezierCurve; + //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty); + points.push( new THREE.Vector2( tx, ty ) ); -THREE.QuadraticBezierCurve.prototype.getPoint = function ( t ) { + } - var vector = new THREE.Vector2(); + //console.log(points); - 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 ); + break; - return vector; + } // end switch -}; + } -THREE.QuadraticBezierCurve.prototype.getTangent = function( t ) { - var vector = new THREE.Vector2(); + // Normalize to remove the closing point by default. + var lastPoint = points[ points.length - 1 ]; + var EPSILON = 0.0000000001; + if ( Math.abs( lastPoint.x - points[ 0 ].x ) < EPSILON && + Math.abs( lastPoint.y - points[ 0 ].y ) < EPSILON ) + points.splice( points.length - 1, 1 ); + if ( closedPath ) { - 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 ); + points.push( points[ 0 ] ); - // returns unit vector + } - return vector.normalize(); + return points; }; -// File:src/extras/curves/CubicBezierCurve.js +// +// Breaks path into shapes +// +// Assumptions (if parameter isCCW==true the opposite holds): +// - solid shapes are defined clockwise (CW) +// - holes are defined counterclockwise (CCW) +// +// If parameter noHoles==true: +// - all subPaths are regarded as solid shapes +// - definition order CW/CCW has no relevance +// -/************************************************************** - * Cubic Bezier curve - **************************************************************/ +THREE.Path.prototype.toShapes = function( isCCW, noHoles ) { -THREE.CubicBezierCurve = function ( v0, v1, v2, v3 ) { + function extractSubpaths( inActions ) { - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; - this.v3 = v3; + var i, il, item, action, args; -}; + var subPaths = [], lastPath = new THREE.Path(); -THREE.CubicBezierCurve.prototype = Object.create( THREE.Curve.prototype ); -THREE.CubicBezierCurve.prototype.constructor = THREE.CubicBezierCurve; + for ( i = 0, il = inActions.length; i < il; i ++ ) { -THREE.CubicBezierCurve.prototype.getPoint = function ( t ) { + item = inActions[ i ]; - var tx, ty; + args = item.args; + action = item.action; - tx = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ); - ty = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ); + if ( action === THREE.PathActions.MOVE_TO ) { - return new THREE.Vector2( tx, ty ); + if ( lastPath.actions.length !== 0 ) { -}; + subPaths.push( lastPath ); + lastPath = new THREE.Path(); -THREE.CubicBezierCurve.prototype.getTangent = function( t ) { + } - var tx, ty; + } - tx = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ); - ty = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ); + lastPath[ action ].apply( lastPath, args ); - var tangent = new THREE.Vector2( tx, ty ); - tangent.normalize(); + } - return tangent; + if ( lastPath.actions.length !== 0 ) { -}; + subPaths.push( lastPath ); -// File:src/extras/curves/SplineCurve.js + } -/************************************************************** - * Spline curve - **************************************************************/ + // console.log(subPaths); -THREE.SplineCurve = function ( points /* array of Vector2 */ ) { + return subPaths; - this.points = ( points == undefined ) ? [] : points; + } -}; + function toShapesNoHoles( inSubpaths ) { -THREE.SplineCurve.prototype = Object.create( THREE.Curve.prototype ); -THREE.SplineCurve.prototype.constructor = THREE.SplineCurve; + var shapes = []; -THREE.SplineCurve.prototype.getPoint = function ( t ) { + for ( var i = 0, il = inSubpaths.length; i < il; i ++ ) { - var points = this.points; - var point = ( points.length - 1 ) * t; + var tmpPath = inSubpaths[ i ]; - var intPoint = Math.floor( point ); - var weight = point - intPoint; + var tmpShape = new THREE.Shape(); + tmpShape.actions = tmpPath.actions; + tmpShape.curves = tmpPath.curves; - 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 ]; + shapes.push( tmpShape ); - var vector = new THREE.Vector2(); + } - 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 ); + //console.log("shape", shapes); - return vector; + return shapes; -}; + } -// File:src/extras/curves/EllipseCurve.js + function isPointInsidePolygon( inPt, inPolygon ) { -/************************************************************** - * Ellipse curve - **************************************************************/ + var EPSILON = 0.0000000001; -THREE.EllipseCurve = function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { + var polyLen = inPolygon.length; - this.aX = aX; - this.aY = aY; + // inPt on polygon contour => immediate success or + // toggling of inside/outside at every single! intersection point of an edge + // with the horizontal line through inPt, left of inPt + // not counting lowerY endpoints of edges and whole edges on that line + var inside = false; + for ( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) { - this.xRadius = xRadius; - this.yRadius = yRadius; + var edgeLowPt = inPolygon[ p ]; + var edgeHighPt = inPolygon[ q ]; - this.aStartAngle = aStartAngle; - this.aEndAngle = aEndAngle; + var edgeDx = edgeHighPt.x - edgeLowPt.x; + var edgeDy = edgeHighPt.y - edgeLowPt.y; - this.aClockwise = aClockwise; - - this.aRotation = aRotation || 0; + if ( Math.abs( edgeDy ) > EPSILON ) { -}; + // not parallel + if ( edgeDy < 0 ) { -THREE.EllipseCurve.prototype = Object.create( THREE.Curve.prototype ); -THREE.EllipseCurve.prototype.constructor = THREE.EllipseCurve; + edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx; + edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy; -THREE.EllipseCurve.prototype.getPoint = function ( t ) { + } + if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue; - var deltaAngle = this.aEndAngle - this.aStartAngle; + if ( inPt.y === edgeLowPt.y ) { - if ( deltaAngle < 0 ) deltaAngle += Math.PI * 2; - if ( deltaAngle > Math.PI * 2 ) deltaAngle -= Math.PI * 2; + if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ? + // continue; // no intersection or edgeLowPt => doesn't count !!! - var angle; + } else { - if ( this.aClockwise === true ) { + var perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y ); + if ( perpEdge === 0 ) return true; // inPt is on contour ? + if ( perpEdge < 0 ) continue; + inside = ! inside; // true intersection left of inPt - angle = this.aEndAngle + ( 1 - t ) * ( Math.PI * 2 - deltaAngle ); + } - } else { + } else { - angle = this.aStartAngle + t * deltaAngle; + // parallel or collinear + if ( inPt.y !== edgeLowPt.y ) continue; // parallel + // edge lies on the same horizontal line as inPt + if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) || + ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour ! + // continue; + + } + + } + + return inside; } - - var x = this.aX + this.xRadius * Math.cos( angle ); - var y = this.aY + this.yRadius * Math.sin( angle ); - if ( this.aRotation !== 0 ) { - var cos = Math.cos( this.aRotation ); - var sin = Math.sin( this.aRotation ); + var subPaths = extractSubpaths( this.actions ); + if ( subPaths.length === 0 ) return []; - var tx = x, ty = y; + if ( noHoles === true ) return toShapesNoHoles( subPaths ); - // Rotate the point about the center of the ellipse. - x = ( tx - this.aX ) * cos - ( ty - this.aY ) * sin + this.aX; - y = ( tx - this.aX ) * sin + ( ty - this.aY ) * cos + this.aY; - } + var solid, tmpPath, tmpShape, shapes = []; - return new THREE.Vector2( x, y ); + if ( subPaths.length === 1 ) { -}; + tmpPath = subPaths[ 0 ]; + tmpShape = new THREE.Shape(); + tmpShape.actions = tmpPath.actions; + tmpShape.curves = tmpPath.curves; + shapes.push( tmpShape ); + return shapes; -// File:src/extras/curves/ArcCurve.js + } -/************************************************************** - * Arc curve - **************************************************************/ + var holesFirst = ! THREE.Shape.Utils.isClockWise( subPaths[ 0 ].getPoints() ); + holesFirst = isCCW ? ! holesFirst : holesFirst; -THREE.ArcCurve = function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { + // console.log("Holes first", holesFirst); - THREE.EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); + var betterShapeHoles = []; + var newShapes = []; + var newShapeHoles = []; + var mainIdx = 0; + var tmpPoints; -}; + newShapes[ mainIdx ] = undefined; + newShapeHoles[ mainIdx ] = []; -THREE.ArcCurve.prototype = Object.create( THREE.EllipseCurve.prototype ); -THREE.ArcCurve.prototype.constructor = THREE.ArcCurve; + var i, il; -// File:src/extras/curves/LineCurve3.js + for ( i = 0, il = subPaths.length; i < il; i ++ ) { -/************************************************************** - * Line3D - **************************************************************/ + tmpPath = subPaths[ i ]; + tmpPoints = tmpPath.getPoints(); + solid = THREE.Shape.Utils.isClockWise( tmpPoints ); + solid = isCCW ? ! solid : solid; -THREE.LineCurve3 = THREE.Curve.create( + if ( solid ) { - function ( v1, v2 ) { + if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++; - this.v1 = v1; - this.v2 = v2; + newShapes[ mainIdx ] = { s: new THREE.Shape(), p: tmpPoints }; + newShapes[ mainIdx ].s.actions = tmpPath.actions; + newShapes[ mainIdx ].s.curves = tmpPath.curves; - }, + if ( holesFirst ) mainIdx ++; + newShapeHoles[ mainIdx ] = []; - function ( t ) { + //console.log('cw', i); - var vector = new THREE.Vector3(); + } else { - vector.subVectors( this.v2, this.v1 ); // diff - vector.multiplyScalar( t ); - vector.add( this.v1 ); + newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } ); - return vector; + //console.log('ccw', i); + + } } -); + // only Holes? -> probably all Shapes with wrong orientation + if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths ); -// File:src/extras/curves/QuadraticBezierCurve3.js -/************************************************************** - * Quadratic Bezier 3D curve - **************************************************************/ + if ( newShapes.length > 1 ) { -THREE.QuadraticBezierCurve3 = THREE.Curve.create( + var ambiguous = false; + var toChange = []; - function ( v0, v1, v2 ) { + for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; + betterShapeHoles[ sIdx ] = []; - }, + } + for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) { - function ( t ) { + var sho = newShapeHoles[ sIdx ]; + for ( var hIdx = 0; hIdx < sho.length; hIdx ++ ) { - var vector = new THREE.Vector3(); + var ho = sho[ hIdx ]; + var hole_unassigned = true; + for ( var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) { - 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 ); + if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) { - return vector; + if ( sIdx !== s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } ); + if ( hole_unassigned ) { - } + hole_unassigned = false; + betterShapeHoles[ s2Idx ].push( ho ); -); + } else { -// File:src/extras/curves/CubicBezierCurve3.js + ambiguous = true; -/************************************************************** - * Cubic Bezier 3D curve - **************************************************************/ + } -THREE.CubicBezierCurve3 = THREE.Curve.create( + } - function ( v0, v1, v2, v3 ) { + } + if ( hole_unassigned ) { - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; - this.v3 = v3; + betterShapeHoles[ sIdx ].push( ho ); - }, + } - function ( t ) { + } - var vector = new THREE.Vector3(); + } + // console.log("ambiguous: ", ambiguous); + if ( toChange.length > 0 ) { - 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 ); + // console.log("to change: ", toChange); + if ( ! ambiguous ) newShapeHoles = betterShapeHoles; - return vector; + } } -); + var tmpHoles, j, jl; + for ( i = 0, il = newShapes.length; i < il; i ++ ) { -// File:src/extras/curves/SplineCurve3.js + tmpShape = newShapes[ i ].s; + shapes.push( tmpShape ); + tmpHoles = newShapeHoles[ i ]; + for ( j = 0, jl = tmpHoles.length; j < jl; j ++ ) { -/************************************************************** - * Spline 3D curve - **************************************************************/ + tmpShape.holes.push( tmpHoles[ j ].h ); + } -THREE.SplineCurve3 = THREE.Curve.create( + } - function ( points /* array of Vector3 */ ) { + //console.log("shape", shapes); - console.warn( 'THREE.SplineCurve3 will be deprecated. Please use THREE.CatmullRomCurve3' ); - this.points = ( points == undefined ) ? [] : points; + return shapes; - }, +}; - function ( t ) { +// File:src/extras/core/Shape.js - var points = this.points; - var point = ( points.length - 1 ) * t; +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * Defines a 2d shape plane using paths. + **/ - var intPoint = Math.floor( point ); - var weight = point - intPoint; +// STEP 1 Create a path. +// STEP 2 Turn path into shape. +// STEP 3 ExtrudeGeometry takes in Shape/Shapes +// STEP 3a - Extract points from each shape, turn to vertices +// STEP 3b - Triangulate each shape, add faces. - var point0 = points[ intPoint == 0 ? intPoint : intPoint - 1 ]; - var point1 = points[ intPoint ]; - var point2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ]; - var point3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ]; +THREE.Shape = function () { - var vector = new THREE.Vector3(); + THREE.Path.apply( this, arguments ); + this.holes = []; - vector.x = THREE.Curve.Utils.interpolate( point0.x, point1.x, point2.x, point3.x, weight ); - vector.y = THREE.Curve.Utils.interpolate( point0.y, point1.y, point2.y, point3.y, weight ); - vector.z = THREE.Curve.Utils.interpolate( point0.z, point1.z, point2.z, point3.z, weight ); +}; - return vector; +THREE.Shape.prototype = Object.create( THREE.Path.prototype ); +THREE.Shape.prototype.constructor = THREE.Shape; - } +// Convenience method to return ExtrudeGeometry -); +THREE.Shape.prototype.extrude = function ( options ) { -// File:src/extras/curves/CatmullRomCurve3.js + var extruded = new THREE.ExtrudeGeometry( this, options ); + return extruded; -/** - * @author zz85 https://github.com/zz85 - * - * Centripetal CatmullRom Curve - which is useful for avoiding - * cusps and self-intersections in non-uniform catmull rom curves. - * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf - * - * curve.type accepts centripetal(default), chordal and catmullrom - * curve.tension is used for catmullrom which defaults to 0.5 - */ +}; + +// Convenience method to return ShapeGeometry + +THREE.Shape.prototype.makeGeometry = function ( options ) { -THREE.CatmullRomCurve3 = ( function() { + var geometry = new THREE.ShapeGeometry( this, options ); + return geometry; - var - tmp = new THREE.Vector3(), - px = new CubicPoly(), - py = new CubicPoly(), - pz = new CubicPoly(); +}; - /* - Based on an optimized c++ solution in - - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ - - http://ideone.com/NoEbVM +// Get points of holes - This CubicPoly class could be used for reusing some variables and calculations, - but for three.js curve use, it could be possible inlined and flatten into a single function call - which can be placed in CurveUtils. - */ +THREE.Shape.prototype.getPointsHoles = function ( divisions ) { - function CubicPoly() { + var i, il = this.holes.length, holesPts = []; + + for ( i = 0; i < il; i ++ ) { + + holesPts[ i ] = this.holes[ i ].getTransformedPoints( divisions, this.bends ); } - /* - * Compute coefficients for a cubic polynomial - * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 - * such that - * p(0) = x0, p(1) = x1 - * and - * p'(0) = t0, p'(1) = t1. - */ - CubicPoly.prototype.init = function( x0, x1, t0, t1 ) { + return holesPts; - this.c0 = x0; - this.c1 = t0; - this.c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; - this.c3 = 2 * x0 - 2 * x1 + t0 + t1; +}; - }; +// Get points of holes (spaced by regular distance) - CubicPoly.prototype.initNonuniformCatmullRom = function( x0, x1, x2, x3, dt0, dt1, dt2 ) { +THREE.Shape.prototype.getSpacedPointsHoles = function ( divisions ) { - // compute tangents when parameterized in [t1,t2] - var t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; - var t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; + var i, il = this.holes.length, holesPts = []; - // rescale tangents for parametrization in [0,1] - t1 *= dt1; - t2 *= dt1; + for ( i = 0; i < il; i ++ ) { - // initCubicPoly - this.init( x1, x2, t1, t2 ); + holesPts[ i ] = this.holes[ i ].getTransformedSpacedPoints( divisions, this.bends ); - }; + } - // standard Catmull-Rom spline: interpolate between x1 and x2 with previous/following points x1/x4 - CubicPoly.prototype.initCatmullRom = function( x0, x1, x2, x3, tension ) { + return holesPts; - this.init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); +}; - }; - CubicPoly.prototype.calc = function( t ) { +// Get points of shape and holes (keypoints based on segments parameter) - var t2 = t * t; - var t3 = t2 * t; - return this.c0 + this.c1 * t + this.c2 * t2 + this.c3 * t3; +THREE.Shape.prototype.extractAllPoints = function ( divisions ) { - }; + return { - // Subclass Three.js curve - return THREE.Curve.create( + shape: this.getTransformedPoints( divisions ), + holes: this.getPointsHoles( divisions ) - function ( p /* array of Vector3 */ ) { + }; - this.points = p || []; +}; - }, +THREE.Shape.prototype.extractPoints = function ( divisions ) { - function ( t ) { + if ( this.useSpacedPoints ) { - var points = this.points, - point, intPoint, weight, l; + return this.extractAllSpacedPoints( divisions ); - l = points.length; + } - if ( l < 2 ) console.log( 'duh, you need at least 2 points' ); + return this.extractAllPoints( divisions ); - point = ( l - 1 ) * t; - intPoint = Math.floor( point ); - weight = point - intPoint; +}; - if ( weight === 0 && intPoint === l - 1 ) { +// +// THREE.Shape.prototype.extractAllPointsWithBend = function ( divisions, bend ) { +// +// return { +// +// shape: this.transform( bend, divisions ), +// holes: this.getPointsHoles( divisions, bend ) +// +// }; +// +// }; - intPoint = l - 2; - weight = 1; +// Get points of shape and holes (spaced by regular distance) - } +THREE.Shape.prototype.extractAllSpacedPoints = function ( divisions ) { - var p0, p1, p2, p3; + return { - if ( intPoint === 0 ) { + shape: this.getTransformedSpacedPoints( divisions ), + holes: this.getSpacedPointsHoles( divisions ) - // extrapolate first point - tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); - p0 = tmp; + }; - } else { +}; - p0 = points[ intPoint - 1 ]; +/************************************************************** + * Utils + **************************************************************/ - } +THREE.Shape.Utils = { - p1 = points[ intPoint ]; - p2 = points[ intPoint + 1 ]; + triangulateShape: function ( contour, holes ) { - if ( intPoint + 2 < l ) { + function point_in_segment_2D_colin( inSegPt1, inSegPt2, inOtherPt ) { - p3 = points[ intPoint + 2 ] + // inOtherPt needs to be collinear to the inSegment + if ( inSegPt1.x !== inSegPt2.x ) { - } else { + if ( inSegPt1.x < inSegPt2.x ) { - // extrapolate last point - tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 2 ] ); - p3 = tmp; + return ( ( inSegPt1.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt2.x ) ); - } + } else { - if ( this.type === undefined || this.type === 'centripetal' || this.type === 'chordal' ) { + return ( ( inSegPt2.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt1.x ) ); - // init Centripetal / Chordal Catmull-Rom - var pow = this.type === 'chordal' ? 0.5 : 0.25; - var dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); - var dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); - var dt2 = Math.pow( p2.distanceToSquared( p3 ), pow ); + } - // safety check for repeated points - if ( dt1 < 1e-4 ) dt1 = 1.0; - if ( dt0 < 1e-4 ) dt0 = dt1; - if ( dt2 < 1e-4 ) dt2 = dt1; + } else { - px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 ); - py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 ); - pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 ); + if ( inSegPt1.y < inSegPt2.y ) { - } else if ( this.type === 'catmullrom' ) { + return ( ( inSegPt1.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt2.y ) ); - var tension = this.tension !== undefined ? this.tension : 0.5; - px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, tension ); - py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, tension ); - pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, tension ); + } else { - } + return ( ( inSegPt2.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt1.y ) ); - var v = new THREE.Vector3( - px.calc( weight ), - py.calc( weight ), - pz.calc( weight ) - ); + } - return v; + } } - ); + function intersect_segments_2D( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1, inSeg2Pt2, inExcludeAdjacentSegs ) { -} )(); + var EPSILON = 0.0000000001; -// File:src/extras/curves/ClosedSplineCurve3.js + var seg1dx = inSeg1Pt2.x - inSeg1Pt1.x, seg1dy = inSeg1Pt2.y - inSeg1Pt1.y; + var seg2dx = inSeg2Pt2.x - inSeg2Pt1.x, seg2dy = inSeg2Pt2.y - inSeg2Pt1.y; -/************************************************************** - * Closed Spline 3D curve - **************************************************************/ + var seg1seg2dx = inSeg1Pt1.x - inSeg2Pt1.x; + var seg1seg2dy = inSeg1Pt1.y - inSeg2Pt1.y; + var limit = seg1dy * seg2dx - seg1dx * seg2dy; + var perpSeg1 = seg1dy * seg1seg2dx - seg1dx * seg1seg2dy; -THREE.ClosedSplineCurve3 = THREE.Curve.create( + if ( Math.abs( limit ) > EPSILON ) { - function ( points /* array of Vector3 */ ) { + // not parallel - this.points = ( points == undefined ) ? [] : points; + var perpSeg2; + if ( limit > 0 ) { - }, + if ( ( perpSeg1 < 0 ) || ( perpSeg1 > limit ) ) return []; + perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy; + if ( ( perpSeg2 < 0 ) || ( perpSeg2 > limit ) ) return []; - function ( t ) { + } else { - var points = this.points; - var point = ( points.length - 0 ) * t; // This needs to be from 0-length +1 + if ( ( perpSeg1 > 0 ) || ( perpSeg1 < limit ) ) return []; + perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy; + if ( ( perpSeg2 > 0 ) || ( perpSeg2 < limit ) ) return []; - var intPoint = Math.floor( point ); - var weight = point - intPoint; + } - intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length; + // i.e. to reduce rounding errors + // intersection at endpoint of segment#1? + if ( perpSeg2 === 0 ) { - 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 ]; + if ( ( inExcludeAdjacentSegs ) && + ( ( perpSeg1 === 0 ) || ( perpSeg1 === limit ) ) ) return []; + return [ inSeg1Pt1 ]; - var vector = new THREE.Vector3(); + } + if ( perpSeg2 === limit ) { - 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 ); + if ( ( inExcludeAdjacentSegs ) && + ( ( perpSeg1 === 0 ) || ( perpSeg1 === limit ) ) ) return []; + return [ inSeg1Pt2 ]; - return vector; + } + // intersection at endpoint of segment#2? + if ( perpSeg1 === 0 ) return [ inSeg2Pt1 ]; + if ( perpSeg1 === limit ) return [ inSeg2Pt2 ]; - } + // return real intersection point + var factorSeg1 = perpSeg2 / limit; + return [ { x: inSeg1Pt1.x + factorSeg1 * seg1dx, + y: inSeg1Pt1.y + factorSeg1 * seg1dy } ]; -); + } else { -// File:src/extras/animation/AnimationHandler.js + // parallel or collinear + if ( ( perpSeg1 !== 0 ) || + ( seg2dy * seg1seg2dx !== seg2dx * seg1seg2dy ) ) return []; -/** - * @author mikael emtinger / http://gomo.se/ - */ + // they are collinear or degenerate + var seg1Pt = ( ( seg1dx === 0 ) && ( seg1dy === 0 ) ); // segment1 is just a point? + var seg2Pt = ( ( seg2dx === 0 ) && ( seg2dy === 0 ) ); // segment2 is just a point? + // both segments are points + if ( seg1Pt && seg2Pt ) { -THREE.AnimationHandler = { + if ( ( inSeg1Pt1.x !== inSeg2Pt1.x ) || + ( inSeg1Pt1.y !== inSeg2Pt1.y ) ) return []; // they are distinct points + return [ inSeg1Pt1 ]; // they are the same point - LINEAR: 0, - CATMULLROM: 1, - CATMULLROM_FORWARD: 2, + } + // segment#1 is a single point + if ( seg1Pt ) { - // + if ( ! point_in_segment_2D_colin( inSeg2Pt1, inSeg2Pt2, inSeg1Pt1 ) ) return []; // but not in segment#2 + return [ inSeg1Pt1 ]; - add: function () { + } + // segment#2 is a single point + if ( seg2Pt ) { - console.warn( 'THREE.AnimationHandler.add() has been deprecated.' ); + if ( ! point_in_segment_2D_colin( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1 ) ) return []; // but not in segment#1 + return [ inSeg2Pt1 ]; - }, - get: function () { + } - console.warn( 'THREE.AnimationHandler.get() has been deprecated.' ); + // they are collinear segments, which might overlap + var seg1min, seg1max, seg1minVal, seg1maxVal; + var seg2min, seg2max, seg2minVal, seg2maxVal; + if ( seg1dx !== 0 ) { - }, - remove: function () { + // the segments are NOT on a vertical line + if ( inSeg1Pt1.x < inSeg1Pt2.x ) { - console.warn( 'THREE.AnimationHandler.remove() has been deprecated.' ); + seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.x; + seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.x; - }, + } else { - // + seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.x; + seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.x; - animations: [], + } + if ( inSeg2Pt1.x < inSeg2Pt2.x ) { - init: function ( data ) { + seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.x; + seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.x; - if ( data.initialized === true ) return data; + } else { - // loop through all keys + seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.x; + seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.x; - for ( var h = 0; h < data.hierarchy.length; h ++ ) { + } - for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { + } else { - // remove minus times + // the segments are on a vertical line + if ( inSeg1Pt1.y < inSeg1Pt2.y ) { - if ( data.hierarchy[ h ].keys[ k ].time < 0 ) { + seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.y; + seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.y; - data.hierarchy[ h ].keys[ k ].time = 0; + } else { - } + seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.y; + seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.y; - // create quaternions + } + if ( inSeg2Pt1.y < inSeg2Pt2.y ) { - if ( data.hierarchy[ h ].keys[ k ].rot !== undefined && - ! ( data.hierarchy[ h ].keys[ k ].rot instanceof THREE.Quaternion ) ) { + seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.y; + seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.y; - var quat = data.hierarchy[ h ].keys[ k ].rot; - data.hierarchy[ h ].keys[ k ].rot = new THREE.Quaternion().fromArray( quat ); + } else { - } + seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.y; + seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.y; - } + } - // prepare morph target keys + } + if ( seg1minVal <= seg2minVal ) { - if ( data.hierarchy[ h ].keys.length && data.hierarchy[ h ].keys[ 0 ].morphTargets !== undefined ) { + if ( seg1maxVal < seg2minVal ) return []; + if ( seg1maxVal === seg2minVal ) { - // get all used + if ( inExcludeAdjacentSegs ) return []; + return [ seg2min ]; - var usedMorphTargets = {}; + } + if ( seg1maxVal <= seg2maxVal ) return [ seg2min, seg1max ]; + return [ seg2min, seg2max ]; - for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { + } else { - for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) { + if ( seg1minVal > seg2maxVal ) return []; + if ( seg1minVal === seg2maxVal ) { - var morphTargetName = data.hierarchy[ h ].keys[ k ].morphTargets[ m ]; - usedMorphTargets[ morphTargetName ] = - 1; + if ( inExcludeAdjacentSegs ) return []; + return [ seg1min ]; } + if ( seg1maxVal <= seg2maxVal ) return [ seg1min, seg1max ]; + return [ seg1min, seg2max ]; } - data.hierarchy[ h ].usedMorphTargets = usedMorphTargets; + } + } - // set all used on all frames + function isPointInsideAngle( inVertex, inLegFromPt, inLegToPt, inOtherPt ) { - for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { + // The order of legs is important - var influences = {}; + var EPSILON = 0.0000000001; - for ( var morphTargetName in usedMorphTargets ) { + // translation of all points, so that Vertex is at (0,0) + var legFromPtX = inLegFromPt.x - inVertex.x, legFromPtY = inLegFromPt.y - inVertex.y; + var legToPtX = inLegToPt.x - inVertex.x, legToPtY = inLegToPt.y - inVertex.y; + var otherPtX = inOtherPt.x - inVertex.x, otherPtY = inOtherPt.y - inVertex.y; - for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) { + // main angle >0: < 180 deg.; 0: 180 deg.; <0: > 180 deg. + var from2toAngle = legFromPtX * legToPtY - legFromPtY * legToPtX; + var from2otherAngle = legFromPtX * otherPtY - legFromPtY * otherPtX; - if ( data.hierarchy[ h ].keys[ k ].morphTargets[ m ] === morphTargetName ) { + if ( Math.abs( from2toAngle ) > EPSILON ) { - influences[ morphTargetName ] = data.hierarchy[ h ].keys[ k ].morphTargetsInfluences[ m ]; - break; + // angle != 180 deg. - } + var other2toAngle = otherPtX * legToPtY - otherPtY * legToPtX; + // console.log( "from2to: " + from2toAngle + ", from2other: " + from2otherAngle + ", other2to: " + other2toAngle ); - } + if ( from2toAngle > 0 ) { - if ( m === data.hierarchy[ h ].keys[ k ].morphTargets.length ) { + // main angle < 180 deg. + return ( ( from2otherAngle >= 0 ) && ( other2toAngle >= 0 ) ); - influences[ morphTargetName ] = 0; + } else { - } + // main angle > 180 deg. + return ( ( from2otherAngle >= 0 ) || ( other2toAngle >= 0 ) ); - } + } - data.hierarchy[ h ].keys[ k ].morphTargetsInfluences = influences; + } else { - } + // angle == 180 deg. + // console.log( "from2to: 180 deg., from2other: " + from2otherAngle ); + return ( from2otherAngle > 0 ); } + } + + + function removeHoles( contour, holes ) { + + var shape = contour.concat(); // work on this shape + var hole; + + function isCutLineInsideAngles( inShapeIdx, inHoleIdx ) { - // remove all keys that are on the same time + // Check if hole point lies within angle around shape point + var lastShapeIdx = shape.length - 1; + + var prevShapeIdx = inShapeIdx - 1; + if ( prevShapeIdx < 0 ) prevShapeIdx = lastShapeIdx; - for ( var k = 1; k < data.hierarchy[ h ].keys.length; k ++ ) { + var nextShapeIdx = inShapeIdx + 1; + if ( nextShapeIdx > lastShapeIdx ) nextShapeIdx = 0; - if ( data.hierarchy[ h ].keys[ k ].time === data.hierarchy[ h ].keys[ k - 1 ].time ) { + var insideAngle = isPointInsideAngle( shape[ inShapeIdx ], shape[ prevShapeIdx ], shape[ nextShapeIdx ], hole[ inHoleIdx ] ); + if ( ! insideAngle ) { - data.hierarchy[ h ].keys.splice( k, 1 ); - k --; + // console.log( "Vertex (Shape): " + inShapeIdx + ", Point: " + hole[inHoleIdx].x + "/" + hole[inHoleIdx].y ); + return false; } - } + // Check if shape point lies within angle around hole point + var lastHoleIdx = hole.length - 1; + var prevHoleIdx = inHoleIdx - 1; + if ( prevHoleIdx < 0 ) prevHoleIdx = lastHoleIdx; - // set index + var nextHoleIdx = inHoleIdx + 1; + if ( nextHoleIdx > lastHoleIdx ) nextHoleIdx = 0; - for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { + insideAngle = isPointInsideAngle( hole[ inHoleIdx ], hole[ prevHoleIdx ], hole[ nextHoleIdx ], shape[ inShapeIdx ] ); + if ( ! insideAngle ) { - data.hierarchy[ h ].keys[ k ].index = k; + // console.log( "Vertex (Hole): " + inHoleIdx + ", Point: " + shape[inShapeIdx].x + "/" + shape[inShapeIdx].y ); + return false; - } + } - } + return true; - data.initialized = true; + } - return data; + function intersectsShapeEdge( inShapePt, inHolePt ) { - }, + // checks for intersections with shape edges + var sIdx, nextIdx, intersection; + for ( sIdx = 0; sIdx < shape.length; sIdx ++ ) { + + nextIdx = sIdx + 1; nextIdx %= shape.length; + intersection = intersect_segments_2D( inShapePt, inHolePt, shape[ sIdx ], shape[ nextIdx ], true ); + if ( intersection.length > 0 ) return true; - parse: function ( root ) { + } - var parseRecurseHierarchy = function ( root, hierarchy ) { + return false; - hierarchy.push( root ); + } - for ( var c = 0; c < root.children.length; c ++ ) - parseRecurseHierarchy( root.children[ c ], hierarchy ); + var indepHoles = []; - }; + function intersectsHoleEdge( inShapePt, inHolePt ) { - // setup hierarchy + // checks for intersections with hole edges + var ihIdx, chkHole, + hIdx, nextIdx, intersection; + for ( ihIdx = 0; ihIdx < indepHoles.length; ihIdx ++ ) { - var hierarchy = []; + chkHole = holes[ indepHoles[ ihIdx ]]; + for ( hIdx = 0; hIdx < chkHole.length; hIdx ++ ) { - if ( root instanceof THREE.SkinnedMesh ) { + nextIdx = hIdx + 1; nextIdx %= chkHole.length; + intersection = intersect_segments_2D( inShapePt, inHolePt, chkHole[ hIdx ], chkHole[ nextIdx ], true ); + if ( intersection.length > 0 ) return true; - for ( var b = 0; b < root.skeleton.bones.length; b ++ ) { + } - hierarchy.push( root.skeleton.bones[ b ] ); + } + return false; } - } else { - - parseRecurseHierarchy( root, hierarchy ); + var holeIndex, shapeIndex, + shapePt, holePt, + holeIdx, cutKey, failedCuts = [], + tmpShape1, tmpShape2, + tmpHole1, tmpHole2; - } + for ( var h = 0, hl = holes.length; h < hl; h ++ ) { - return hierarchy; + indepHoles.push( h ); - }, + } - play: function ( animation ) { + var minShapeIndex = 0; + var counter = indepHoles.length * 2; + while ( indepHoles.length > 0 ) { - if ( this.animations.indexOf( animation ) === - 1 ) { + counter --; + if ( counter < 0 ) { - this.animations.push( animation ); + console.log( "Infinite Loop! Holes left:" + indepHoles.length + ", Probably Hole outside Shape!" ); + break; - } + } - }, + // search for shape-vertex and hole-vertex, + // which can be connected without intersections + for ( shapeIndex = minShapeIndex; shapeIndex < shape.length; shapeIndex ++ ) { - stop: function ( animation ) { + shapePt = shape[ shapeIndex ]; + holeIndex = - 1; - var index = this.animations.indexOf( animation ); + // search for hole which can be reached without intersections + for ( var h = 0; h < indepHoles.length; h ++ ) { - if ( index !== - 1 ) { + holeIdx = indepHoles[ h ]; - this.animations.splice( index, 1 ); + // prevent multiple checks + cutKey = shapePt.x + ":" + shapePt.y + ":" + holeIdx; + if ( failedCuts[ cutKey ] !== undefined ) continue; - } + hole = holes[ holeIdx ]; + for ( var h2 = 0; h2 < hole.length; h2 ++ ) { - }, + holePt = hole[ h2 ]; + if ( ! isCutLineInsideAngles( shapeIndex, h2 ) ) continue; + if ( intersectsShapeEdge( shapePt, holePt ) ) continue; + if ( intersectsHoleEdge( shapePt, holePt ) ) continue; - update: function ( deltaTimeMS ) { + holeIndex = h2; + indepHoles.splice( h, 1 ); - for ( var i = 0; i < this.animations.length; i ++ ) { + tmpShape1 = shape.slice( 0, shapeIndex + 1 ); + tmpShape2 = shape.slice( shapeIndex ); + tmpHole1 = hole.slice( holeIndex ); + tmpHole2 = hole.slice( 0, holeIndex + 1 ); - this.animations[ i ].resetBlendWeights(); + shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 ); - } + minShapeIndex = shapeIndex; - for ( var i = 0; i < this.animations.length; i ++ ) { + // Debug only, to show the selected cuts + // glob_CutLines.push( [ shapePt, holePt ] ); - this.animations[ i ].update( deltaTimeMS ); + break; - } + } + if ( holeIndex >= 0 ) break; // hole-vertex found - } + failedCuts[ cutKey ] = true; // remember failure -}; + } + if ( holeIndex >= 0 ) break; // hole-vertex found -// File:src/extras/animation/Animation.js + } -/** - * @author mikael emtinger / http://gomo.se/ - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ + } -THREE.Animation = function ( root, data ) { + return shape; /* shape with no holes */ - this.root = root; - this.data = THREE.AnimationHandler.init( data ); - this.hierarchy = THREE.AnimationHandler.parse( root ); + } - this.currentTime = 0; - this.timeScale = 1; - this.isPlaying = false; - this.loop = true; - this.weight = 0; + var i, il, f, face, + key, index, + allPointsMap = {}; - this.interpolationType = THREE.AnimationHandler.LINEAR; + // To maintain reference to old shape, one must match coordinates, or offset the indices from original arrays. It's probably easier to do the first. -}; + var allpoints = contour.concat(); -THREE.Animation.prototype = { + for ( var h = 0, hl = holes.length; h < hl; h ++ ) { - constructor: THREE.Animation, + Array.prototype.push.apply( allpoints, holes[ h ] ); - keyTypes: [ "pos", "rot", "scl" ], + } - play: function ( startTime, weight ) { + //console.log( "allpoints",allpoints, allpoints.length ); - this.currentTime = startTime !== undefined ? startTime : 0; - this.weight = weight !== undefined ? weight : 1; + // prepare all points map - this.isPlaying = true; + for ( i = 0, il = allpoints.length; i < il; i ++ ) { - this.reset(); + key = allpoints[ i ].x + ":" + allpoints[ i ].y; - THREE.AnimationHandler.play( this ); + if ( allPointsMap[ key ] !== undefined ) { - }, + console.warn( "THREE.Shape: Duplicate point", key ); - stop: function() { + } - this.isPlaying = false; + allPointsMap[ key ] = i; - THREE.AnimationHandler.stop( this ); + } - }, + // remove holes by cutting paths to holes and adding them to the shape + var shapeWithoutHoles = removeHoles( contour, holes ); - reset: function () { + var triangles = THREE.FontUtils.Triangulate( shapeWithoutHoles, false ); // True returns indices for points of spooled shape + //console.log( "triangles",triangles, triangles.length ); - for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { + // check all face vertices against all points map - var object = this.hierarchy[ h ]; + for ( i = 0, il = triangles.length; i < il; i ++ ) { - if ( object.animationCache === undefined ) { + face = triangles[ i ]; - object.animationCache = { - animations: {}, - blending: { - positionWeight: 0.0, - quaternionWeight: 0.0, - scaleWeight: 0.0 - } - }; + for ( f = 0; f < 3; f ++ ) { - } + key = face[ f ].x + ":" + face[ f ].y; - var name = this.data.name; - var animations = object.animationCache.animations; - var animationCache = animations[ name ]; + index = allPointsMap[ key ]; - if ( animationCache === undefined ) { + if ( index !== undefined ) { - animationCache = { - prevKey: { pos: 0, rot: 0, scl: 0 }, - nextKey: { pos: 0, rot: 0, scl: 0 }, - originalMatrix: object.matrix - }; + face[ f ] = index; - animations[ name ] = animationCache; + } } - // Get keys to match our current time + } - for ( var t = 0; t < 3; t ++ ) { + return triangles.concat(); - var type = this.keyTypes[ t ]; + }, - var prevKey = this.data.hierarchy[ h ].keys[ 0 ]; - var nextKey = this.getNextKeyWith( type, h, 1 ); + isClockWise: function ( pts ) { - while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) { + return THREE.FontUtils.Triangulate.area( pts ) < 0; - prevKey = nextKey; - nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 ); + }, - } + // Bezier Curves formulas obtained from + // http://en.wikipedia.org/wiki/B%C3%A9zier_curve - animationCache.prevKey[ type ] = prevKey; - animationCache.nextKey[ type ] = nextKey; + // Quad Bezier Functions - } + b2p0: function ( t, p ) { - } + var k = 1 - t; + return k * k * p; }, - resetBlendWeights: function () { + b2p1: function ( t, p ) { - for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { + return 2 * ( 1 - t ) * t * p; - var object = this.hierarchy[ h ]; - var animationCache = object.animationCache; + }, - if ( animationCache !== undefined ) { + b2p2: function ( t, p ) { - var blending = animationCache.blending; + return t * t * p; - blending.positionWeight = 0.0; - blending.quaternionWeight = 0.0; - blending.scaleWeight = 0.0; + }, - } + b2: function ( t, p0, p1, p2 ) { - } + return this.b2p0( t, p0 ) + this.b2p1( t, p1 ) + this.b2p2( t, p2 ); }, - update: ( function() { - - var points = []; - var target = new THREE.Vector3(); - var newVector = new THREE.Vector3(); - var newQuat = new THREE.Quaternion(); + // Cubic Bezier Functions - // Catmull-Rom spline + b3p0: function ( t, p ) { - var interpolateCatmullRom = function ( points, scale ) { + var k = 1 - t; + return k * k * k * p; - var c = [], v3 = [], - point, intPoint, weight, w2, w3, - pa, pb, pc, pd; + }, - point = ( points.length - 1 ) * scale; - intPoint = Math.floor( point ); - weight = point - intPoint; + b3p1: function ( t, p ) { - c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1; - c[ 1 ] = intPoint; - c[ 2 ] = intPoint > points.length - 2 ? intPoint : intPoint + 1; - c[ 3 ] = intPoint > points.length - 3 ? intPoint : intPoint + 2; + var k = 1 - t; + return 3 * k * k * t * p; - pa = points[ c[ 0 ] ]; - pb = points[ c[ 1 ] ]; - pc = points[ c[ 2 ] ]; - pd = points[ c[ 3 ] ]; + }, - w2 = weight * weight; - w3 = weight * w2; + b3p2: function ( t, p ) { - v3[ 0 ] = interpolate( pa[ 0 ], pb[ 0 ], pc[ 0 ], pd[ 0 ], weight, w2, w3 ); - v3[ 1 ] = interpolate( pa[ 1 ], pb[ 1 ], pc[ 1 ], pd[ 1 ], weight, w2, w3 ); - v3[ 2 ] = interpolate( pa[ 2 ], pb[ 2 ], pc[ 2 ], pd[ 2 ], weight, w2, w3 ); + var k = 1 - t; + return 3 * k * t * t * p; - return v3; + }, - }; + b3p3: function ( t, p ) { - var interpolate = function ( p0, p1, p2, p3, t, t2, t3 ) { + return t * t * t * p; - var v0 = ( p2 - p0 ) * 0.5, - v1 = ( p3 - p1 ) * 0.5; + }, - return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1; + b3: function ( t, p0, p1, p2, p3 ) { - }; + return this.b3p0( t, p0 ) + this.b3p1( t, p1 ) + this.b3p2( t, p2 ) + this.b3p3( t, p3 ); - return function ( delta ) { + } - if ( this.isPlaying === false ) return; +}; - this.currentTime += delta * this.timeScale; +// File:src/extras/curves/LineCurve.js - if ( this.weight === 0 ) - return; +/************************************************************** + * Line + **************************************************************/ - // +THREE.LineCurve = function ( v1, v2 ) { - var duration = this.data.length; + this.v1 = v1; + this.v2 = v2; - if ( this.currentTime > duration || this.currentTime < 0 ) { +}; - if ( this.loop ) { +THREE.LineCurve.prototype = Object.create( THREE.Curve.prototype ); +THREE.LineCurve.prototype.constructor = THREE.LineCurve; - this.currentTime %= duration; +THREE.LineCurve.prototype.getPoint = function ( t ) { - if ( this.currentTime < 0 ) - this.currentTime += duration; + var point = this.v2.clone().sub( this.v1 ); + point.multiplyScalar( t ).add( this.v1 ); - this.reset(); + return point; - } else { +}; - this.stop(); +// Line curve is linear, so we can overwrite default getPointAt - } +THREE.LineCurve.prototype.getPointAt = function ( u ) { - } + return this.getPoint( u ); - for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { +}; - var object = this.hierarchy[ h ]; - var animationCache = object.animationCache.animations[ this.data.name ]; - var blending = object.animationCache.blending; +THREE.LineCurve.prototype.getTangent = function( t ) { - // loop through pos/rot/scl + var tangent = this.v2.clone().sub( this.v1 ); - for ( var t = 0; t < 3; t ++ ) { + return tangent.normalize(); - // get keys +}; - var type = this.keyTypes[ t ]; - var prevKey = animationCache.prevKey[ type ]; - var nextKey = animationCache.nextKey[ type ]; +// File:src/extras/curves/QuadraticBezierCurve.js - if ( ( this.timeScale > 0 && nextKey.time <= this.currentTime ) || - ( this.timeScale < 0 && prevKey.time >= this.currentTime ) ) { +/************************************************************** + * Quadratic Bezier curve + **************************************************************/ - prevKey = this.data.hierarchy[ h ].keys[ 0 ]; - nextKey = this.getNextKeyWith( type, h, 1 ); - while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) { +THREE.QuadraticBezierCurve = function ( v0, v1, v2 ) { - prevKey = nextKey; - nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 ); + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; - } +}; - animationCache.prevKey[ type ] = prevKey; - animationCache.nextKey[ type ] = nextKey; +THREE.QuadraticBezierCurve.prototype = Object.create( THREE.Curve.prototype ); +THREE.QuadraticBezierCurve.prototype.constructor = THREE.QuadraticBezierCurve; - } - var scale = ( this.currentTime - prevKey.time ) / ( nextKey.time - prevKey.time ); +THREE.QuadraticBezierCurve.prototype.getPoint = function ( t ) { - var prevXYZ = prevKey[ type ]; - var nextXYZ = nextKey[ type ]; + var vector = new THREE.Vector2(); - if ( scale < 0 ) scale = 0; - if ( scale > 1 ) scale = 1; + 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 ); - // interpolate + return vector; - if ( type === "pos" ) { +}; - if ( this.interpolationType === THREE.AnimationHandler.LINEAR ) { - newVector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale; - newVector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale; - newVector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale; +THREE.QuadraticBezierCurve.prototype.getTangent = function( t ) { - // blend - var proportionalWeight = this.weight / ( this.weight + blending.positionWeight ); - object.position.lerp( newVector, proportionalWeight ); - blending.positionWeight += this.weight; + var vector = new THREE.Vector2(); - } else if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || - this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + 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 ); - points[ 0 ] = this.getPrevKeyWith( "pos", h, prevKey.index - 1 )[ "pos" ]; - points[ 1 ] = prevXYZ; - points[ 2 ] = nextXYZ; - points[ 3 ] = this.getNextKeyWith( "pos", h, nextKey.index + 1 )[ "pos" ]; + // returns unit vector - scale = scale * 0.33 + 0.33; + return vector.normalize(); - var currentPoint = interpolateCatmullRom( points, scale ); - var proportionalWeight = this.weight / ( this.weight + blending.positionWeight ); - blending.positionWeight += this.weight; +}; - // blend +// File:src/extras/curves/CubicBezierCurve.js - var vector = object.position; +/************************************************************** + * Cubic Bezier curve + **************************************************************/ - vector.x = vector.x + ( currentPoint[ 0 ] - vector.x ) * proportionalWeight; - vector.y = vector.y + ( currentPoint[ 1 ] - vector.y ) * proportionalWeight; - vector.z = vector.z + ( currentPoint[ 2 ] - vector.z ) * proportionalWeight; +THREE.CubicBezierCurve = function ( v0, v1, v2, v3 ) { - if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; - var forwardPoint = interpolateCatmullRom( points, scale * 1.01 ); +}; - target.set( forwardPoint[ 0 ], forwardPoint[ 1 ], forwardPoint[ 2 ] ); - target.sub( vector ); - target.y = 0; - target.normalize(); +THREE.CubicBezierCurve.prototype = Object.create( THREE.Curve.prototype ); +THREE.CubicBezierCurve.prototype.constructor = THREE.CubicBezierCurve; - var angle = Math.atan2( target.x, target.z ); - object.rotation.set( 0, angle, 0 ); +THREE.CubicBezierCurve.prototype.getPoint = function ( t ) { - } + var tx, ty; - } + 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 ); - } else if ( type === "rot" ) { + return new THREE.Vector2( tx, ty ); - THREE.Quaternion.slerp( prevXYZ, nextXYZ, newQuat, scale ); +}; - // Avoid paying the cost of an additional slerp if we don't have to - if ( blending.quaternionWeight === 0 ) { +THREE.CubicBezierCurve.prototype.getTangent = function( t ) { - object.quaternion.copy( newQuat ); - blending.quaternionWeight = this.weight; + var tx, ty; - } else { + tx = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ); + ty = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ); - var proportionalWeight = this.weight / ( this.weight + blending.quaternionWeight ); - THREE.Quaternion.slerp( object.quaternion, newQuat, object.quaternion, proportionalWeight ); - blending.quaternionWeight += this.weight; + var tangent = new THREE.Vector2( tx, ty ); + tangent.normalize(); - } + return tangent; - } else if ( type === "scl" ) { +}; - newVector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale; - newVector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale; - newVector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale; +// File:src/extras/curves/SplineCurve.js - var proportionalWeight = this.weight / ( this.weight + blending.scaleWeight ); - object.scale.lerp( newVector, proportionalWeight ); - blending.scaleWeight += this.weight; +/************************************************************** + * Spline curve + **************************************************************/ - } +THREE.SplineCurve = function ( points /* array of Vector2 */ ) { - } + this.points = ( points == undefined ) ? [] : points; - } +}; - return true; +THREE.SplineCurve.prototype = Object.create( THREE.Curve.prototype ); +THREE.SplineCurve.prototype.constructor = THREE.SplineCurve; - }; +THREE.SplineCurve.prototype.getPoint = function ( t ) { - } )(), + var points = this.points; + var point = ( points.length - 1 ) * t; - getNextKeyWith: function ( type, h, key ) { + var intPoint = Math.floor( point ); + var weight = point - intPoint; - var keys = this.data.hierarchy[ h ].keys; + 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 ]; - if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || - this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + var vector = new THREE.Vector2(); - key = key < keys.length - 1 ? key : keys.length - 1; + 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 ); - } else { + return vector; - key = key % keys.length; +}; - } +// File:src/extras/curves/EllipseCurve.js - for ( ; key < keys.length; key ++ ) { +/************************************************************** + * Ellipse curve + **************************************************************/ - if ( keys[ key ][ type ] !== undefined ) { +THREE.EllipseCurve = function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { - return keys[ key ]; + this.aX = aX; + this.aY = aY; - } + this.xRadius = xRadius; + this.yRadius = yRadius; - } + this.aStartAngle = aStartAngle; + this.aEndAngle = aEndAngle; - return this.data.hierarchy[ h ].keys[ 0 ]; + this.aClockwise = aClockwise; + + this.aRotation = aRotation || 0; - }, +}; - getPrevKeyWith: function ( type, h, key ) { +THREE.EllipseCurve.prototype = Object.create( THREE.Curve.prototype ); +THREE.EllipseCurve.prototype.constructor = THREE.EllipseCurve; - var keys = this.data.hierarchy[ h ].keys; +THREE.EllipseCurve.prototype.getPoint = function ( t ) { - if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || - this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + var deltaAngle = this.aEndAngle - this.aStartAngle; - key = key > 0 ? key : 0; + if ( deltaAngle < 0 ) deltaAngle += Math.PI * 2; + if ( deltaAngle > Math.PI * 2 ) deltaAngle -= Math.PI * 2; - } else { + var angle; - key = key >= 0 ? key : key + keys.length; + if ( this.aClockwise === true ) { - } + angle = this.aEndAngle + ( 1 - t ) * ( Math.PI * 2 - deltaAngle ); + } else { - for ( ; key >= 0; key -- ) { + angle = this.aStartAngle + t * deltaAngle; - if ( keys[ key ][ type ] !== undefined ) { + } + + var x = this.aX + this.xRadius * Math.cos( angle ); + var y = this.aY + this.yRadius * Math.sin( angle ); - return keys[ key ]; + if ( this.aRotation !== 0 ) { - } + var cos = Math.cos( this.aRotation ); + var sin = Math.sin( this.aRotation ); - } + var tx = x, ty = y; - return this.data.hierarchy[ h ].keys[ keys.length - 1 ]; + // Rotate the point about the center of the ellipse. + x = ( tx - this.aX ) * cos - ( ty - this.aY ) * sin + this.aX; + y = ( tx - this.aX ) * sin + ( ty - this.aY ) * cos + this.aY; } + return new THREE.Vector2( x, y ); + }; -// File:src/extras/animation/KeyFrameAnimation.js +// File:src/extras/curves/ArcCurve.js -/** - * @author mikael emtinger / http://gomo.se/ - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * @author khang duong - * @author erik kitson - */ +/************************************************************** + * Arc curve + **************************************************************/ -THREE.KeyFrameAnimation = function ( data ) { +THREE.ArcCurve = function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { - this.root = data.node; - this.data = THREE.AnimationHandler.init( data ); - this.hierarchy = THREE.AnimationHandler.parse( this.root ); - this.currentTime = 0; - this.timeScale = 0.001; - this.isPlaying = false; - this.isPaused = true; - this.loop = true; + THREE.EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); - // initialize to first keyframes +}; - for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { +THREE.ArcCurve.prototype = Object.create( THREE.EllipseCurve.prototype ); +THREE.ArcCurve.prototype.constructor = THREE.ArcCurve; - var keys = this.data.hierarchy[ h ].keys, - sids = this.data.hierarchy[ h ].sids, - obj = this.hierarchy[ h ]; +// File:src/extras/curves/LineCurve3.js - if ( keys.length && sids ) { +/************************************************************** + * Line3D + **************************************************************/ - for ( var s = 0; s < sids.length; s ++ ) { +THREE.LineCurve3 = THREE.Curve.create( - var sid = sids[ s ], - next = this.getNextKeyWith( sid, h, 0 ); + function ( v1, v2 ) { - if ( next ) { + this.v1 = v1; + this.v2 = v2; - next.apply( sid ); + }, - } + function ( t ) { - } + var vector = new THREE.Vector3(); - obj.matrixAutoUpdate = false; - this.data.hierarchy[ h ].node.updateMatrix(); - obj.matrixWorldNeedsUpdate = true; + vector.subVectors( this.v2, this.v1 ); // diff + vector.multiplyScalar( t ); + vector.add( this.v1 ); - } + return vector; } -}; - -THREE.KeyFrameAnimation.prototype = { - - constructor: THREE.KeyFrameAnimation, +); - play: function ( startTime ) { +// File:src/extras/curves/QuadraticBezierCurve3.js - this.currentTime = startTime !== undefined ? startTime : 0; +/************************************************************** + * Quadratic Bezier 3D curve + **************************************************************/ - if ( this.isPlaying === false ) { +THREE.QuadraticBezierCurve3 = THREE.Curve.create( - this.isPlaying = true; + function ( v0, v1, v2 ) { - // reset key cache + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; - var h, hl = this.hierarchy.length, - object, - node; + }, - for ( h = 0; h < hl; h ++ ) { + function ( t ) { - object = this.hierarchy[ h ]; - node = this.data.hierarchy[ h ]; + var vector = new THREE.Vector3(); - if ( node.animationCache === undefined ) { + 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 ); - node.animationCache = {}; - node.animationCache.prevKey = null; - node.animationCache.nextKey = null; - node.animationCache.originalMatrix = object.matrix; + return vector; - } + } - var keys = this.data.hierarchy[ h ].keys; +); - if ( keys.length ) { +// File:src/extras/curves/CubicBezierCurve3.js - node.animationCache.prevKey = keys[ 0 ]; - node.animationCache.nextKey = keys[ 1 ]; +/************************************************************** + * Cubic Bezier 3D curve + **************************************************************/ - this.startTime = Math.min( keys[ 0 ].time, this.startTime ); - this.endTime = Math.max( keys[ keys.length - 1 ].time, this.endTime ); +THREE.CubicBezierCurve3 = THREE.Curve.create( - } + function ( v0, v1, v2, v3 ) { - } + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; - this.update( 0 ); + }, - } + function ( t ) { - this.isPaused = false; + var vector = new THREE.Vector3(); - THREE.AnimationHandler.play( this ); + vector.x = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ); + vector.y = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ); + vector.z = THREE.Shape.Utils.b3( t, this.v0.z, this.v1.z, this.v2.z, this.v3.z ); - }, + return vector; - stop: function () { + } - this.isPlaying = false; - this.isPaused = false; +); - THREE.AnimationHandler.stop( this ); +// File:src/extras/curves/SplineCurve3.js - // reset JIT matrix and remove cache +/************************************************************** + * Spline 3D curve + **************************************************************/ - for ( var h = 0; h < this.data.hierarchy.length; h ++ ) { - var obj = this.hierarchy[ h ]; - var node = this.data.hierarchy[ h ]; +THREE.SplineCurve3 = THREE.Curve.create( - if ( node.animationCache !== undefined ) { + function ( points /* array of Vector3 */ ) { - var original = node.animationCache.originalMatrix; + console.warn( 'THREE.SplineCurve3 will be deprecated. Please use THREE.CatmullRomCurve3' ); + this.points = ( points == undefined ) ? [] : points; - original.copy( obj.matrix ); - obj.matrix = original; + }, - delete node.animationCache; + function ( t ) { - } + var points = this.points; + var point = ( points.length - 1 ) * t; - } + var intPoint = Math.floor( point ); + var 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 ]; - update: function ( delta ) { + var vector = new THREE.Vector3(); - if ( this.isPlaying === false ) return; + vector.x = THREE.Curve.Utils.interpolate( point0.x, point1.x, point2.x, point3.x, weight ); + vector.y = THREE.Curve.Utils.interpolate( point0.y, point1.y, point2.y, point3.y, weight ); + vector.z = THREE.Curve.Utils.interpolate( point0.z, point1.z, point2.z, point3.z, weight ); - this.currentTime += delta * this.timeScale; + return vector; - // + } - var duration = this.data.length; +); - if ( this.loop === true && this.currentTime > duration ) { +// File:src/extras/curves/CatmullRomCurve3.js - this.currentTime %= duration; +/** + * @author zz85 https://github.com/zz85 + * + * Centripetal CatmullRom Curve - which is useful for avoiding + * cusps and self-intersections in non-uniform catmull rom curves. + * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf + * + * curve.type accepts centripetal(default), chordal and catmullrom + * curve.tension is used for catmullrom which defaults to 0.5 + */ - } +THREE.CatmullRomCurve3 = ( function() { - this.currentTime = Math.min( this.currentTime, duration ); + var + tmp = new THREE.Vector3(), + px = new CubicPoly(), + py = new CubicPoly(), + pz = new CubicPoly(); - for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { + /* + Based on an optimized c++ solution in + - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/ + - http://ideone.com/NoEbVM - var object = this.hierarchy[ h ]; - var node = this.data.hierarchy[ h ]; + This CubicPoly class could be used for reusing some variables and calculations, + but for three.js curve use, it could be possible inlined and flatten into a single function call + which can be placed in CurveUtils. + */ - var keys = node.keys, - animationCache = node.animationCache; + function CubicPoly() { + } - if ( keys.length ) { + /* + * Compute coefficients for a cubic polynomial + * p(s) = c0 + c1*s + c2*s^2 + c3*s^3 + * such that + * p(0) = x0, p(1) = x1 + * and + * p'(0) = t0, p'(1) = t1. + */ + CubicPoly.prototype.init = function( x0, x1, t0, t1 ) { - var prevKey = animationCache.prevKey; - var nextKey = animationCache.nextKey; + this.c0 = x0; + this.c1 = t0; + this.c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; + this.c3 = 2 * x0 - 2 * x1 + t0 + t1; - if ( nextKey.time <= this.currentTime ) { + }; - while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) { + CubicPoly.prototype.initNonuniformCatmullRom = function( x0, x1, x2, x3, dt0, dt1, dt2 ) { - prevKey = nextKey; - nextKey = keys[ prevKey.index + 1 ]; + // compute tangents when parameterized in [t1,t2] + var t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1; + var t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2; - } + // rescale tangents for parametrization in [0,1] + t1 *= dt1; + t2 *= dt1; - animationCache.prevKey = prevKey; - animationCache.nextKey = nextKey; + // initCubicPoly + this.init( x1, x2, t1, t2 ); - } + }; - if ( nextKey.time >= this.currentTime ) { + // standard Catmull-Rom spline: interpolate between x1 and x2 with previous/following points x1/x4 + CubicPoly.prototype.initCatmullRom = function( x0, x1, x2, x3, tension ) { - prevKey.interpolate( nextKey, this.currentTime ); + this.init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); - } else { + }; - prevKey.interpolate( nextKey, nextKey.time ); + CubicPoly.prototype.calc = function( t ) { - } + var t2 = t * t; + var t3 = t2 * t; + return this.c0 + this.c1 * t + this.c2 * t2 + this.c3 * t3; - this.data.hierarchy[ h ].node.updateMatrix(); - object.matrixWorldNeedsUpdate = true; + }; - } + // Subclass Three.js curve + return THREE.Curve.create( - } + function ( p /* array of Vector3 */ ) { - }, + this.points = p || []; - getNextKeyWith: function ( sid, h, key ) { + }, - var keys = this.data.hierarchy[ h ].keys; - key = key % keys.length; + function ( t ) { - for ( ; key < keys.length; key ++ ) { + var points = this.points, + point, intPoint, weight, l; - if ( keys[ key ].hasTarget( sid ) ) { + l = points.length; - return keys[ key ]; + if ( l < 2 ) console.log( 'duh, you need at least 2 points' ); - } + point = ( l - 1 ) * t; + intPoint = Math.floor( point ); + weight = point - intPoint; - } + if ( weight === 0 && intPoint === l - 1 ) { - return keys[ 0 ]; + intPoint = l - 2; + weight = 1; - }, + } - getPrevKeyWith: function ( sid, h, key ) { + var p0, p1, p2, p3; - var keys = this.data.hierarchy[ h ].keys; - key = key >= 0 ? key : key + keys.length; + if ( intPoint === 0 ) { - for ( ; key >= 0; key -- ) { + // extrapolate first point + tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); + p0 = tmp; - if ( keys[ key ].hasTarget( sid ) ) { + } else { - return keys[ key ]; + p0 = points[ intPoint - 1 ]; } - } + p1 = points[ intPoint ]; + p2 = points[ intPoint + 1 ]; - return keys[ keys.length - 1 ]; + if ( intPoint + 2 < l ) { - } + p3 = points[ intPoint + 2 ] -}; + } else { -// File:src/extras/animation/MorphAnimation.js + // extrapolate last point + tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 2 ] ); + p3 = tmp; -/** - * @author mrdoob / http://mrdoob.com - * @author willy-vvu / http://willy-vvu.github.io - */ + } -THREE.MorphAnimation = function ( mesh ) { + if ( this.type === undefined || this.type === 'centripetal' || this.type === 'chordal' ) { - this.mesh = mesh; - this.frames = mesh.morphTargetInfluences.length; - this.currentTime = 0; - this.duration = 1000; - this.loop = true; - this.lastFrame = 0; - this.currentFrame = 0; + // init Centripetal / Chordal Catmull-Rom + var pow = this.type === 'chordal' ? 0.5 : 0.25; + var dt0 = Math.pow( p0.distanceToSquared( p1 ), pow ); + var dt1 = Math.pow( p1.distanceToSquared( p2 ), pow ); + var dt2 = Math.pow( p2.distanceToSquared( p3 ), pow ); - this.isPlaying = false; + // safety check for repeated points + if ( dt1 < 1e-4 ) dt1 = 1.0; + if ( dt0 < 1e-4 ) dt0 = dt1; + if ( dt2 < 1e-4 ) dt2 = dt1; -}; + px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 ); + py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 ); + pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 ); -THREE.MorphAnimation.prototype = { + } else if ( this.type === 'catmullrom' ) { - constructor: THREE.MorphAnimation, + var tension = this.tension !== undefined ? this.tension : 0.5; + px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, tension ); + py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, tension ); + pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, tension ); - play: function () { + } - this.isPlaying = true; + var v = new THREE.Vector3( + px.calc( weight ), + py.calc( weight ), + pz.calc( weight ) + ); - }, + return v; - pause: function () { + } - this.isPlaying = false; + ); - }, +} )(); - update: function ( delta ) { +// File:src/extras/curves/ClosedSplineCurve3.js - if ( this.isPlaying === false ) return; +/************************************************************** + * Closed Spline 3D curve + **************************************************************/ - this.currentTime += delta; - if ( this.loop === true && this.currentTime > this.duration ) { +THREE.ClosedSplineCurve3 = THREE.Curve.create( - this.currentTime %= this.duration; + function ( points /* array of Vector3 */ ) { - } + this.points = ( points == undefined ) ? [] : points; - this.currentTime = Math.min( this.currentTime, this.duration ); + }, - var frameTime = this.duration / this.frames; - var frame = Math.floor( this.currentTime / frameTime ); + function ( t ) { - var influences = this.mesh.morphTargetInfluences; + var points = this.points; + var point = ( points.length - 0 ) * t; // This needs to be from 0-length +1 - if ( frame !== this.currentFrame ) { + var intPoint = Math.floor( point ); + var weight = point - intPoint; - influences[ this.lastFrame ] = 0; - influences[ this.currentFrame ] = 1; - influences[ frame ] = 0; + intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length; - this.lastFrame = this.currentFrame; - this.currentFrame = frame; + var point0 = points[ ( intPoint - 1 ) % points.length ]; + var point1 = points[ ( intPoint ) % points.length ]; + var point2 = points[ ( intPoint + 1 ) % points.length ]; + var point3 = points[ ( intPoint + 2 ) % points.length ]; - } + var vector = new THREE.Vector3(); - var mix = ( this.currentTime % frameTime ) / frameTime; + 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 ); - influences[ frame ] = mix; - influences[ this.lastFrame ] = 1 - mix; + return vector; } -}; +); // File:src/extras/geometries/BoxGeometry.js @@ -32513,7 +33286,7 @@ THREE.ShapeGeometry.prototype.addShape = function ( shape, options ) { /** * @author astrodud / http://astrodud.isgreat.org/ * @author zz85 / https://github.com/zz85 - * @author bhouston / http://exocortex.com + * @author bhouston / http://clara.io */ // points - to create a closed torus, one must use a set of points @@ -34297,7 +35070,7 @@ THREE.WireframeGeometry = function ( geometry ) { if ( drawcalls.length === 0 ) { - geometry.addDrawCall( 0, indices.length ); + geometry.addGroup( 0, indices.length ); } @@ -34437,7 +35210,7 @@ THREE.AxisHelper.prototype.constructor = THREE.AxisHelper; /** * @author WestLangley / http://github.com/WestLangley * @author zz85 / http://github.com/zz85 - * @author bhouston / http://exocortex.com + * @author bhouston / http://clara.io * * Creates an arrow for visualizing directions * diff --git a/build/three.min.js b/build/three.min.js index 110d60ec8b9a24474f5fa79fa1926b0ae29e8e76..78d7ae69507e4f60b1be00a4f48d8cce8791b904 100644 --- a/build/three.min.js +++ b/build/three.min.js @@ -6,7 +6,7 @@ THREE.CullFaceFront=2;THREE.CullFaceFrontBack=3;THREE.FrontFaceDirectionCW=0;THR THREE.SubtractEquation=101;THREE.ReverseSubtractEquation=102;THREE.MinEquation=103;THREE.MaxEquation=104;THREE.ZeroFactor=200;THREE.OneFactor=201;THREE.SrcColorFactor=202;THREE.OneMinusSrcColorFactor=203;THREE.SrcAlphaFactor=204;THREE.OneMinusSrcAlphaFactor=205;THREE.DstAlphaFactor=206;THREE.OneMinusDstAlphaFactor=207;THREE.DstColorFactor=208;THREE.OneMinusDstColorFactor=209;THREE.SrcAlphaSaturateFactor=210;THREE.NeverDepth=0;THREE.AlwaysDepth=1;THREE.LessDepth=2;THREE.LessEqualDepth=3; THREE.EqualDepth=4;THREE.GreaterEqualDepth=5;THREE.GreaterDepth=6;THREE.NotEqualDepth=7;THREE.MultiplyOperation=0;THREE.MixOperation=1;THREE.AddOperation=2;THREE.UVMapping=300;THREE.CubeReflectionMapping=301;THREE.CubeRefractionMapping=302;THREE.EquirectangularReflectionMapping=303;THREE.EquirectangularRefractionMapping=304;THREE.SphericalReflectionMapping=305;THREE.RepeatWrapping=1E3;THREE.ClampToEdgeWrapping=1001;THREE.MirroredRepeatWrapping=1002;THREE.NearestFilter=1003; THREE.NearestMipMapNearestFilter=1004;THREE.NearestMipMapLinearFilter=1005;THREE.LinearFilter=1006;THREE.LinearMipMapNearestFilter=1007;THREE.LinearMipMapLinearFilter=1008;THREE.UnsignedByteType=1009;THREE.ByteType=1010;THREE.ShortType=1011;THREE.UnsignedShortType=1012;THREE.IntType=1013;THREE.UnsignedIntType=1014;THREE.FloatType=1015;THREE.HalfFloatType=1025;THREE.UnsignedShort4444Type=1016;THREE.UnsignedShort5551Type=1017;THREE.UnsignedShort565Type=1018;THREE.AlphaFormat=1019;THREE.RGBFormat=1020; -THREE.RGBAFormat=1021;THREE.LuminanceFormat=1022;THREE.LuminanceAlphaFormat=1023;THREE.RGBEFormat=THREE.RGBAFormat;THREE.RGB_S3TC_DXT1_Format=2001;THREE.RGBA_S3TC_DXT1_Format=2002;THREE.RGBA_S3TC_DXT3_Format=2003;THREE.RGBA_S3TC_DXT5_Format=2004;THREE.RGB_PVRTC_4BPPV1_Format=2100;THREE.RGB_PVRTC_2BPPV1_Format=2101;THREE.RGBA_PVRTC_4BPPV1_Format=2102;THREE.RGBA_PVRTC_2BPPV1_Format=2103; +THREE.RGBAFormat=1021;THREE.LuminanceFormat=1022;THREE.LuminanceAlphaFormat=1023;THREE.RGBEFormat=THREE.RGBAFormat;THREE.RGB_S3TC_DXT1_Format=2001;THREE.RGBA_S3TC_DXT1_Format=2002;THREE.RGBA_S3TC_DXT3_Format=2003;THREE.RGBA_S3TC_DXT5_Format=2004;THREE.RGB_PVRTC_4BPPV1_Format=2100;THREE.RGB_PVRTC_2BPPV1_Format=2101;THREE.RGBA_PVRTC_4BPPV1_Format=2102;THREE.RGBA_PVRTC_2BPPV1_Format=2103;THREE.LoopOnce=2200;THREE.LoopRepeat=2201;THREE.LoopPingPong=2202; THREE.Projector=function(){console.error("THREE.Projector has been moved to /examples/js/renderers/Projector.js.");this.projectVector=function(a,b){console.warn("THREE.Projector: .projectVector() is now vector.project().");a.project(b)};this.unprojectVector=function(a,b){console.warn("THREE.Projector: .unprojectVector() is now vector.unproject().");a.unproject(b)};this.pickingRay=function(a,b){console.error("THREE.Projector: .pickingRay() is now raycaster.setFromCamera().")}}; THREE.CanvasRenderer=function(){console.error("THREE.CanvasRenderer has been moved to /examples/js/renderers/CanvasRenderer.js");this.domElement=document.createElement("canvas");this.clear=function(){};this.render=function(){};this.setClearColor=function(){};this.setSize=function(){}};THREE.Color=function(a){return 3===arguments.length?this.setRGB(arguments[0],arguments[1],arguments[2]):this.set(a)}; THREE.Color.prototype={constructor:THREE.Color,r:1,g:1,b:1,set:function(a){a instanceof THREE.Color?this.copy(a):"number"===typeof a?this.setHex(a):"string"===typeof a&&this.setStyle(a);return this},setHex:function(a){a=Math.floor(a);this.r=(a>>16&255)/255;this.g=(a>>8&255)/255;this.b=(a&255)/255;return this},setRGB:function(a,b,c){this.r=a;this.g=b;this.b=c;return this},setHSL:function(){function a(a,c,d){0>d&&(d+=1);1d?c:d<2/3?a+6*(c-a)*(2/3-d):a}return function(b, @@ -99,22 +99,22 @@ a&&(a=new THREE.Vector3);void 0===c&&(c=0);void 0===d&&(d=b.length/b.itemSize);f b){var c=a.elements,d=this.elements;d[0]=c[10]*c[5]-c[6]*c[9];d[1]=-c[10]*c[1]+c[2]*c[9];d[2]=c[6]*c[1]-c[2]*c[5];d[3]=-c[10]*c[4]+c[6]*c[8];d[4]=c[10]*c[0]-c[2]*c[8];d[5]=-c[6]*c[0]+c[2]*c[4];d[6]=c[9]*c[4]-c[5]*c[8];d[7]=-c[9]*c[0]+c[1]*c[8];d[8]=c[5]*c[0]-c[1]*c[4];c=c[0]*d[0]+c[1]*d[3]+c[2]*d[6];if(0===c){if(b)throw Error("Matrix3.getInverse(): can't invert matrix, determinant is 0");console.warn("Matrix3.getInverse(): can't invert matrix, determinant is 0");this.identity();return this}this.multiplyScalar(1/ c);return this},transpose:function(){var a,b=this.elements;a=b[1];b[1]=b[3];b[3]=a;a=b[2];b[2]=b[6];b[6]=a;a=b[5];b[5]=b[7];b[7]=a;return this},flattenToArrayOffset:function(a,b){var c=this.elements;a[b]=c[0];a[b+1]=c[1];a[b+2]=c[2];a[b+3]=c[3];a[b+4]=c[4];a[b+5]=c[5];a[b+6]=c[6];a[b+7]=c[7];a[b+8]=c[8];return a},getNormalMatrix:function(a){this.getInverse(a).transpose();return this},transposeIntoArray:function(a){var b=this.elements;a[0]=b[0];a[1]=b[3];a[2]=b[6];a[3]=b[1];a[4]=b[4];a[5]=b[7];a[6]= b[2];a[7]=b[5];a[8]=b[8];return this},fromArray:function(a){this.elements.set(a);return this},toArray:function(){var a=this.elements;return[a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8]]}};THREE.Matrix4=function(){this.elements=new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]);0=this.radius},containsPoint:function(a){return a.distanceToSquared(this.center)<=this.radius*this.radius},distanceToPoint:function(a){return a.distanceTo(this.center)-this.radius},intersectsSphere:function(a){var b=this.radius+a.radius;return a.center.distanceToSquared(this.center)<=b*b},clampPoint:function(a,b){var c=this.center.distanceToSquared(a),d=b||new THREE.Vector3;d.copy(a);c>this.radius*this.radius&&(d.sub(this.center).normalize(),d.multiplyScalar(this.radius).add(this.center)); return d},getBoundingBox:function(a){a=a||new THREE.Box3;a.set(this.center,this.center);a.expandByScalar(this.radius);return a},applyMatrix4:function(a){this.center.applyMatrix4(a);this.radius*=a.getMaxScaleOnAxis();return this},translate:function(a){this.center.add(a);return this},equals:function(a){return a.center.equals(this.center)&&a.radius===this.radius}}; THREE.Frustum=function(a,b,c,d,e,g){this.planes=[void 0!==a?a:new THREE.Plane,void 0!==b?b:new THREE.Plane,void 0!==c?c:new THREE.Plane,void 0!==d?d:new THREE.Plane,void 0!==e?e:new THREE.Plane,void 0!==g?g:new THREE.Plane]}; -THREE.Frustum.prototype={constructor:THREE.Frustum,set:function(a,b,c,d,e,g){var f=this.planes;f[0].copy(a);f[1].copy(b);f[2].copy(c);f[3].copy(d);f[4].copy(e);f[5].copy(g);return this},clone:function(){return(new this.constructor).copy(this)},copy:function(a){for(var b=this.planes,c=0;6>c;c++)b[c].copy(a.planes[c]);return this},setFromMatrix:function(a){var b=this.planes,c=a.elements;a=c[0];var d=c[1],e=c[2],g=c[3],f=c[4],h=c[5],k=c[6],l=c[7],n=c[8],p=c[9],m=c[10],q=c[11],t=c[12],r=c[13],u=c[14], -c=c[15];b[0].setComponents(g-a,l-f,q-n,c-t).normalize();b[1].setComponents(g+a,l+f,q+n,c+t).normalize();b[2].setComponents(g+d,l+h,q+p,c+r).normalize();b[3].setComponents(g-d,l-h,q-p,c-r).normalize();b[4].setComponents(g-e,l-k,q-m,c-u).normalize();b[5].setComponents(g+e,l+k,q+m,c+u).normalize();return this},intersectsObject:function(){var a=new THREE.Sphere;return function(b){var c=b.geometry;null===c.boundingSphere&&c.computeBoundingSphere();a.copy(c.boundingSphere);a.applyMatrix4(b.matrixWorld); +THREE.Frustum.prototype={constructor:THREE.Frustum,set:function(a,b,c,d,e,g){var f=this.planes;f[0].copy(a);f[1].copy(b);f[2].copy(c);f[3].copy(d);f[4].copy(e);f[5].copy(g);return this},clone:function(){return(new this.constructor).copy(this)},copy:function(a){for(var b=this.planes,c=0;6>c;c++)b[c].copy(a.planes[c]);return this},setFromMatrix:function(a){var b=this.planes,c=a.elements;a=c[0];var d=c[1],e=c[2],g=c[3],f=c[4],h=c[5],k=c[6],l=c[7],n=c[8],p=c[9],m=c[10],q=c[11],s=c[12],t=c[13],v=c[14], +c=c[15];b[0].setComponents(g-a,l-f,q-n,c-s).normalize();b[1].setComponents(g+a,l+f,q+n,c+s).normalize();b[2].setComponents(g+d,l+h,q+p,c+t).normalize();b[3].setComponents(g-d,l-h,q-p,c-t).normalize();b[4].setComponents(g-e,l-k,q-m,c-v).normalize();b[5].setComponents(g+e,l+k,q+m,c+v).normalize();return this},intersectsObject:function(){var a=new THREE.Sphere;return function(b){var c=b.geometry;null===c.boundingSphere&&c.computeBoundingSphere();a.copy(c.boundingSphere);a.applyMatrix4(b.matrixWorld); return this.intersectsSphere(a)}}(),intersectsSphere:function(a){var b=this.planes,c=a.center;a=-a.radius;for(var d=0;6>d;d++)if(b[d].distanceToPoint(c)e;e++){var g=d[e];a.x=0f&&0>g)return!1}return!0}}(),containsPoint:function(a){for(var b=this.planes,c=0;6>c;c++)if(0>b[c].distanceToPoint(a))return!1;return!0}};THREE.Plane=function(a,b){this.normal=void 0!==a?a:new THREE.Vector3(1,0,0);this.constant=void 0!==b?b:0}; THREE.Plane.prototype={constructor:THREE.Plane,set:function(a,b){this.normal.copy(a);this.constant=b;return this},setComponents:function(a,b,c,d){this.normal.set(a,b,c);this.constant=d;return this},setFromNormalAndCoplanarPoint:function(a,b){this.normal.copy(a);this.constant=-b.dot(this.normal);return this},setFromCoplanarPoints:function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(c,d,e){d=a.subVectors(e,d).cross(b.subVectors(c,d)).normalize();this.setFromNormalAndCoplanarPoint(d, @@ -149,7 +149,7 @@ THREE.Math={generateUUID:function(){var a="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZa 180;return function(b){return b*a}}(),radToDeg:function(){var a=180/Math.PI;return function(b){return b*a}}(),isPowerOfTwo:function(a){return 0===(a&a-1)&&0!==a},nextPowerOfTwo:function(a){a--;a|=a>>1;a|=a>>2;a|=a>>4;a|=a>>8;a|=a>>16;a++;return a}}; THREE.Spline=function(a){function b(a,b,c,d,e,g,f){a=.5*(c-a);d=.5*(d-b);return(2*(b-c)+a+d)*f+(-3*(b-c)-2*a-d)*g+a*e+b}this.points=a;var c=[],d={x:0,y:0,z:0},e,g,f,h,k,l,n,p,m;this.initFromArray=function(a){this.points=[];for(var b=0;bthis.points.length-2?this.points.length-1:g+1;c[3]=g>this.points.length-3?this.points.length-1:g+ 2;l=this.points[c[0]];n=this.points[c[1]];p=this.points[c[2]];m=this.points[c[3]];h=f*f;k=f*h;d.x=b(l.x,n.x,p.x,m.x,f,h,k);d.y=b(l.y,n.y,p.y,m.y,f,h,k);d.z=b(l.z,n.z,p.z,m.z,f,h,k);return d};this.getControlPointsArray=function(){var a,b,c=this.points.length,d=[];for(a=0;a=b.x+b.y}}(); @@ -204,7 +204,7 @@ THREE.Geometry.prototype={constructor:THREE.Geometry,applyMatrix:function(a){for this.verticesNeedUpdate=!0},rotateX:function(){var a;return function(b){void 0===a&&(a=new THREE.Matrix4);a.makeRotationX(b);this.applyMatrix(a);return this}}(),rotateY:function(){var a;return function(b){void 0===a&&(a=new THREE.Matrix4);a.makeRotationY(b);this.applyMatrix(a);return this}}(),rotateZ:function(){var a;return function(b){void 0===a&&(a=new THREE.Matrix4);a.makeRotationZ(b);this.applyMatrix(a);return this}}(),translate:function(){var a;return function(b,c,d){void 0===a&&(a=new THREE.Matrix4); a.makeTranslation(b,c,d);this.applyMatrix(a);return this}}(),scale:function(){var a;return function(b,c,d){void 0===a&&(a=new THREE.Matrix4);a.makeScale(b,c,d);this.applyMatrix(a);return this}}(),lookAt:function(){var a;return function(b){void 0===a&&(a=new THREE.Object3D);a.lookAt(b);a.updateMatrix();this.applyMatrix(a.matrix)}}(),fromBufferGeometry:function(a){var b=this,c=null!==a.index?a.index.array:void 0,d=a.attributes,e=d.position.array,g=void 0!==d.normal?d.normal.array:void 0,f=void 0!== d.color?d.color.array:void 0,h=void 0!==d.uv?d.uv.array:void 0,k=void 0!==d.uv2?d.uv2.array:void 0;void 0!==k&&(this.faceVertexUvs[1]=[]);for(var l=[],n=[],p=[],m=d=0;dd;d++)if(e[d]===e[(d+1)%3]){a.push(g);break}for(g=a.length-1;0<=g;g--)for(e=a[g],this.faces.splice(e, 1),c=0,f=this.faceVertexUvs.length;cthis.duration)for(a=0;a=e.referenceCount&&(e.unbind(),delete this.propertyBindingMap[d])}return this},findActionByName:function(a){for(var b=0;b=c.weight)&&c.enabled)for(var g=0;gc?a:b},lerp_boolean_immediate:function(a,b,c){return a},lerp_string:function(a,b,c){return.5>c?a:b},lerp_string_immediate:function(a,b,c){return a},getLerpFunc:function(a,b){if(void 0===a||null===a)throw Error("examplarValue is null");switch(typeof a){case "object":if(a.lerp)return THREE.AnimationUtils.lerp_object;if(a.slerp)return THREE.AnimationUtils.slerp_object;break;case "number":return THREE.AnimationUtils.lerp_number;case "boolean":return b?THREE.AnimationUtils.lerp_boolean:THREE.AnimationUtils.lerp_boolean_immediate; +case "string":return b?THREE.AnimationUtils.lerp_string:THREE.AnimationUtils.lerp_string_immediate}}};THREE.KeyframeTrack=function(a,b){if(void 0===a)throw Error("track name is undefined");if(void 0===b||0===b.length)throw Error("no keys in track named "+a);this.name=a;this.keys=b;this.lastIndex=0;this.validate();this.optimize()}; +THREE.KeyframeTrack.prototype={constructor:THREE.KeyframeTrack,getAt:function(a){for(;this.lastIndex=this.keys[this.lastIndex].time;)this.lastIndex++;for(;0=this.keys.length)return this.setResult(this.keys[this.keys.length-1].value),this.result;if(0===this.lastIndex)return this.setResult(this.keys[0].value),this.result;var b=this.keys[this.lastIndex-1];this.setResult(b.value);if(b.constantToNext)return this.result; +var c=this.keys[this.lastIndex];return this.result=this.lerpValues(this.result,c.value,(a-b.time)/(c.time-b.time))},shift:function(a){if(0!==a)for(var b=0;b=b)e++;else break;0c.time){console.error(" key.time is less than previous key time, out of order keys", +this,b,c,a);return}a=c}return this}},optimize:function(){var a=[],b=this.keys[0];a.push(b);THREE.AnimationUtils.getEqualsFunc(b.value);for(var c=1;cthis.cumulativeWeight){var a= +1-this.cumulativeWeight;this.cumulativeValue=this.lerpValue(this.cumulativeValue,this.originalValue,a/(this.cumulativeWeight+a))}this.setValue(this.cumulativeValue)&&this.triggerDirty&&this.triggerDirty();this.cumulativeValue=null;this.cumulativeWeight=0}}}; +THREE.PropertyBinding.parseTrackName=function(a){var b=/^(([\w]+\/)*)([\w-\d]+)?(\.([\w]+)(\[([\w\d\[\]\_. ]+)\])?)?(\.([\w.]+)(\[([\w\d\[\]\_. ]+)\])?)$/,c=b.exec(a);if(!c)throw Error("cannot parse trackName at all: "+a);c.index===b.lastIndex&&b.lastIndex++;b={directoryName:c[1],nodeName:c[3],objectName:c[5],objectIndex:c[7],propertyName:c[9],propertyIndex:c[11]};if(null===b.propertyName||0===b.propertyName.length)throw Error("can not parse propertyName from trackName: "+a);return b}; +THREE.PropertyBinding.findNode=function(a,b){if(!b||""===b||"root"===b||"."===b||-1===b||b===a.name||b===a.uuid)return a;if(a.skeleton){var c=function(a){for(var c=0;cc?a:b};THREE.StringKeyframeTrack.prototype.compareValues=function(a,b){return a===b};THREE.StringKeyframeTrack.prototype.clone=function(){for(var a=[],b=0;bc?a:b};THREE.BooleanKeyframeTrack.prototype.compareValues=function(a,b){return a===b};THREE.BooleanKeyframeTrack.prototype.clone=function(){for(var a=[],b=0;bb.opacity&&(k.transparent=!0);void 0!==b.depthTest&&(k.depthTest=b.depthTest);void 0!==b.depthWrite&&(k.depthWrite=b.depthWrite);void 0!==b.visible&&(k.visible=b.visible);void 0!==b.flipSided&&(k.side=THREE.BackSide);void 0!==b.doubleSided&&(k.side=THREE.DoubleSide);void 0!==b.wireframe&&(k.wireframe=b.wireframe);void 0!==b.vertexColors&&("face"===b.vertexColors?k.vertexColors=THREE.FaceColors:b.vertexColors&&(k.vertexColors=THREE.VertexColors)); b.colorDiffuse?k.color=f(b.colorDiffuse):b.DbgColor&&(k.color=b.DbgColor);b.colorEmissive&&(k.emissive=f(b.colorEmissive));"MeshPhongMaterial"===h&&(b.colorSpecular&&(k.specular=f(b.colorSpecular)),b.specularCoef&&(k.shininess=b.specularCoef));void 0!==b.transparency&&(console.warn("THREE.Loader: transparency has been renamed to opacity"),b.opacity=b.transparency);void 0!==b.opacity&&(k.opacity=b.opacity);c&&(b.mapDiffuse&&g(k,"map",b.mapDiffuse,b.mapDiffuseRepeat,b.mapDiffuseOffset,b.mapDiffuseWrap, b.mapDiffuseAnisotropy),b.mapLight&&g(k,"lightMap",b.mapLight,b.mapLightRepeat,b.mapLightOffset,b.mapLightWrap,b.mapLightAnisotropy),b.mapAO&&g(k,"aoMap",b.mapAO,b.mapAORepeat,b.mapAOOffset,b.mapAOWrap,b.mapAOAnisotropy),b.mapBump&&g(k,"bumpMap",b.mapBump,b.mapBumpRepeat,b.mapBumpOffset,b.mapBumpWrap,b.mapBumpAnisotropy),b.mapNormal&&g(k,"normalMap",b.mapNormal,b.mapNormalRepeat,b.mapNormalOffset,b.mapNormalWrap,b.mapNormalAnisotropy),b.mapSpecular&&g(k,"specularMap",b.mapSpecular,b.mapSpecularRepeat, @@ -289,13 +335,14 @@ THREE.ImageLoader.prototype={constructor:THREE.ImageLoader,load:function(a,b,c,d e.manager.itemStart(a);f.src=a;return f},setCrossOrigin:function(a){this.crossOrigin=a}};THREE.JSONLoader=function(a){"boolean"===typeof a&&(console.warn("THREE.JSONLoader: showStatus parameter has been removed from constructor."),a=void 0);this.manager=void 0!==a?a:THREE.DefaultLoadingManager;this.withCredentials=!1}; THREE.JSONLoader.prototype={constructor:THREE.JSONLoader,get statusDomElement(){void 0===this._statusDomElement&&(this._statusDomElement=document.createElement("div"));console.warn("THREE.JSONLoader: .statusDomElement has been removed.");return this._statusDomElement},load:function(a,b,c,d){var e=this,g=this.texturePath&&"string"===typeof this.texturePath?this.texturePath:THREE.Loader.prototype.extractUrlBase(a);c=new THREE.XHRLoader(this.manager);c.setCrossOrigin(this.crossOrigin);c.setWithCredentials(this.withCredentials); c.load(a,function(c){c=JSON.parse(c);var d=c.metadata;if(void 0!==d){if("object"===d.type){console.error("THREE.JSONLoader: "+a+" should be loaded with THREE.ObjectLoader instead.");return}if("scene"===d.type){console.error("THREE.JSONLoader: "+a+" should be loaded with THREE.SceneLoader instead.");return}}c=e.parse(c,g);b(c.geometry,c.materials)})},setCrossOrigin:function(a){this.crossOrigin=a},setTexturePath:function(a){this.texturePath=a},parse:function(a,b){var c=new THREE.Geometry,d=void 0!== -a.scale?1/a.scale:1;(function(b){var d,f,h,k,l,n,p,m,q,t,r,u,w,v=a.faces;n=a.vertices;var A=a.normals,x=a.colors,H=0;if(void 0!==a.uvs){for(d=0;df;f++)m=v[k++],w=u[2*m],m=u[2*m+1],w=new THREE.Vector2(w,m),2!==f&&c.faceVertexUvs[d][h].push(w),0!==f&&c.faceVertexUvs[d][h+1].push(w);p&&(p=3*v[k++],q.normal.set(A[p++],A[p++],A[p]),r.normal.copy(q.normal));if(t)for(d=0;4>d;d++)p=3*v[k++],t=new THREE.Vector3(A[p++],A[p++],A[p]),2!==d&&q.vertexNormals.push(t),0!==d&&r.vertexNormals.push(t); -n&&(n=v[k++],n=x[n],q.color.setHex(n),r.color.setHex(n));if(b)for(d=0;4>d;d++)n=v[k++],n=x[n],2!==d&&q.vertexColors.push(new THREE.Color(n)),0!==d&&r.vertexColors.push(new THREE.Color(n));c.faces.push(q);c.faces.push(r)}else{q=new THREE.Face3;q.a=v[k++];q.b=v[k++];q.c=v[k++];h&&(h=v[k++],q.materialIndex=h);h=c.faces.length;if(d)for(d=0;df;f++)m=v[k++],w=u[2*m],m=u[2*m+1],w=new THREE.Vector2(w,m),c.faceVertexUvs[d][h].push(w);p&&(p=3*v[k++],q.normal.set(A[p++], -A[p++],A[p]));if(t)for(d=0;3>d;d++)p=3*v[k++],t=new THREE.Vector3(A[p++],A[p++],A[p]),q.vertexNormals.push(t);n&&(n=v[k++],q.color.setHex(x[n]));if(b)for(d=0;3>d;d++)n=v[k++],q.vertexColors.push(new THREE.Color(x[n]));c.faces.push(q)}})(d);(function(){var b=void 0!==a.influencesPerVertex?a.influencesPerVertex:2;if(a.skinWeights)for(var d=0,f=a.skinWeights.length;df;f++)m=u[k++],w=v[2*m],m=v[2*m+1],w=new THREE.Vector2(w,m),2!==f&&c.faceVertexUvs[d][h].push(w),0!==f&&c.faceVertexUvs[d][h+1].push(w);p&&(p=3*u[k++],q.normal.set(y[p++],y[p++],y[p]),t.normal.copy(q.normal));if(s)for(d=0;4>d;d++)p=3*u[k++],s=new THREE.Vector3(y[p++],y[p++],y[p]),2!==d&&q.vertexNormals.push(s),0!==d&&t.vertexNormals.push(s); +n&&(n=u[k++],n=x[n],q.color.setHex(n),t.color.setHex(n));if(b)for(d=0;4>d;d++)n=u[k++],n=x[n],2!==d&&q.vertexColors.push(new THREE.Color(n)),0!==d&&t.vertexColors.push(new THREE.Color(n));c.faces.push(q);c.faces.push(t)}else{q=new THREE.Face3;q.a=u[k++];q.b=u[k++];q.c=u[k++];h&&(h=u[k++],q.materialIndex=h);h=c.faces.length;if(d)for(d=0;df;f++)m=u[k++],w=v[2*m],m=v[2*m+1],w=new THREE.Vector2(w,m),c.faceVertexUvs[d][h].push(w);p&&(p=3*u[k++],q.normal.set(y[p++], +y[p++],y[p]));if(s)for(d=0;3>d;d++)p=3*u[k++],s=new THREE.Vector3(y[p++],y[p++],y[p]),q.vertexNormals.push(s);n&&(n=u[k++],q.color.setHex(x[n]));if(b)for(d=0;3>d;d++)n=u[k++],q.vertexColors.push(new THREE.Color(x[n]));c.faces.push(q)}})(d);(function(){var b=void 0!==a.influencesPerVertex?a.influencesPerVertex:2;if(a.skinWeights)for(var d=0,f=a.skinWeights.length;dg||(n.applyMatrix4(this.matrixWorld),r=d.ray.origin.distanceTo(n),rd.far||e.push({distance:r,point:l.clone().applyMatrix4(this.matrixWorld),index:m,face:null,faceIndex:null,object:this}))}else for(q=q.position.array,m=0,t=q.length/3-1;mg||(n.applyMatrix4(this.matrixWorld),r=d.ray.origin.distanceTo(n),rd.far||e.push({distance:r,point:l.clone().applyMatrix4(this.matrixWorld),index:m,face:null,faceIndex:null,object:this}))}else if(f instanceof THREE.Geometry)for(h=f.vertices,k=h.length,m=0;mg||(n.applyMatrix4(this.matrixWorld),r=d.ray.origin.distanceTo(n),rd.far||e.push({distance:r,point:l.clone().applyMatrix4(this.matrixWorld), +THREE.BufferGeometry){var m=f.index,q=f.attributes;if(null!==m)for(var f=m.array,q=q.position.array,m=0,s=f.length-1;mg||(n.applyMatrix4(this.matrixWorld),t=d.ray.origin.distanceTo(n),td.far||e.push({distance:t,point:l.clone().applyMatrix4(this.matrixWorld),index:m,face:null,faceIndex:null,object:this}))}else for(q=q.position.array,m=0,s=q.length/3-1;mg||(n.applyMatrix4(this.matrixWorld),t=d.ray.origin.distanceTo(n),td.far||e.push({distance:t,point:l.clone().applyMatrix4(this.matrixWorld),index:m,face:null,faceIndex:null,object:this}))}else if(f instanceof THREE.Geometry)for(h=f.vertices,k=h.length,m=0;mg||(n.applyMatrix4(this.matrixWorld),t=d.ray.origin.distanceTo(n),td.far||e.push({distance:t,point:l.clone().applyMatrix4(this.matrixWorld), index:m,face:null,faceIndex:null,object:this}))}}}();THREE.Line.prototype.clone=function(){return(new this.constructor(this.geometry,this.material)).copy(this)}; THREE.Line.prototype.toJSON=function(a){var b=THREE.Object3D.prototype.toJSON.call(this,a);void 0===a.geometries[this.geometry.uuid]&&(a.geometries[this.geometry.uuid]=this.geometry.toJSON());void 0===a.materials[this.material.uuid]&&(a.materials[this.material.uuid]=this.material.toJSON());b.object.geometry=this.geometry.uuid;b.object.material=this.material.uuid;return b};THREE.LineStrip=0;THREE.LinePieces=1;THREE.LineSegments=function(a,b){THREE.Line.call(this,a,b);this.type="LineSegments"}; THREE.LineSegments.prototype=Object.create(THREE.Line.prototype);THREE.LineSegments.prototype.constructor=THREE.LineSegments;THREE.Mesh=function(a,b){THREE.Object3D.call(this);this.type="Mesh";this.geometry=void 0!==a?a:new THREE.Geometry;this.material=void 0!==b?b:new THREE.MeshBasicMaterial({color:16777215*Math.random()});this.updateMorphTargets()};THREE.Mesh.prototype=Object.create(THREE.Object3D.prototype);THREE.Mesh.prototype.constructor=THREE.Mesh; THREE.Mesh.prototype.updateMorphTargets=function(){if(void 0!==this.geometry.morphTargets&&0q.far)){var L;void 0!==v.uv&&(L=v.uv.array,n.fromArray(L,2*x),p.fromArray(L,2*H),m.fromArray(L,2*F),L=a(t,e,g,f,n,p,m));w.push({distance:E,point:r.clone(),uv:L,face:new THREE.Face3(x, -H,F,THREE.Triangle.normal(e,g,f)),faceIndex:Math.floor(D/3),object:this})}}else for(B=v.position.array,D=0,N=B.length;Dq.far||(void 0!==v.uv&&(L=v.uv.array,n.fromArray(L,D),p.fromArray(L,D+2),m.fromArray(L, -D+4),L=a(t,e,g,f,n,p,m)),x=D/3,H=x+1,F=x+2,w.push({distance:E,point:r.clone(),uv:L,face:new THREE.Face3(x,H,F,THREE.Triangle.normal(e,g,f)),index:x,object:this}))}else if(v instanceof THREE.Geometry)for(var z=A instanceof THREE.MeshFaceMaterial,B=!0===z?A.materials:null,D=v.vertices,N=v.faces,M=0,C=N.length;Mq.far|| -(0q.far)){var C;void 0!==u.uv&&(C=u.uv.array,n.fromArray(C,2*x),p.fromArray(C,2*H),m.fromArray(C,2*E),C=a(s,e,g,f,n,p,m));w.push({distance:D,point:t.clone(),uv:C,face:new THREE.Face3(x, +H,E,THREE.Triangle.normal(e,g,f)),faceIndex:Math.floor(I/3),object:this})}}else for(A=u.position.array,I=0,L=A.length;Iq.far||(void 0!==u.uv&&(C=u.uv.array,n.fromArray(C,I),p.fromArray(C,I+2),m.fromArray(C, +I+4),C=a(s,e,g,f,n,p,m)),x=I/3,H=x+1,E=x+2,w.push({distance:D,point:t.clone(),uv:C,face:new THREE.Face3(x,H,E,THREE.Triangle.normal(e,g,f)),index:x,object:this}))}else if(u instanceof THREE.Geometry)for(var z=y instanceof THREE.MeshFaceMaterial,A=!0===z?y.materials:null,I=u.vertices,L=u.faces,N=0,K=L.length;Nq.far|| +(0h.end&&(h.end=e);b||(b=f)}}a.firstAnimation=b}; -THREE.MorphAnimMesh.prototype.setAnimationLabel=function(a,b,c){this.geometry.animations||(this.geometry.animations={});this.geometry.animations[a]={start:b,end:c}};THREE.MorphAnimMesh.prototype.playAnimation=function(a,b){var c=this.geometry.animations[a];c?(this.setFrameRange(c.start,c.end),this.duration=(c.end-c.start)/b*1E3,this.time=0):console.warn("THREE.MorphAnimMesh: animation["+a+"] undefined in .playAnimation()")}; -THREE.MorphAnimMesh.prototype.updateAnimation=function(a){var b=this.duration/this.length;this.time+=this.direction*a;if(this.mirroredLoop){if(this.time>this.duration||0>this.time)this.direction*=-1,this.time>this.duration&&(this.time=this.duration,this.directionBackwards=!0),0>this.time&&(this.time=0,this.directionBackwards=!1)}else this.time%=this.duration,0>this.time&&(this.time+=this.duration);var c=this.startKeyframe+THREE.Math.clamp(Math.floor(this.time/b),0,this.length-1);a=this.morphTargetInfluences; -c!==this.currentKeyframe&&(a[this.lastKeyframe]=0,a[this.currentKeyframe]=1,a[c]=0,this.lastKeyframe=this.currentKeyframe,this.currentKeyframe=c);b=this.time%b/b;this.directionBackwards&&(b=1-b);a[this.currentKeyframe]=b;a[this.lastKeyframe]=1-b};THREE.MorphAnimMesh.prototype.interpolateTargets=function(a,b,c){for(var d=this.morphTargetInfluences,e=0,g=d.length;ec;c++)s.deleteFramebuffer(b.__webglFramebuffer[c]),s.deleteRenderbuffer(b.__webglRenderbuffer[c]);else s.deleteFramebuffer(b.__webglFramebuffer),s.deleteRenderbuffer(b.__webglRenderbuffer);Z.delete(a)}Ga.textures--}function h(a){a=a.target;a.removeEventListener("dispose",h);k(a);Z.delete(a)}function k(a){var b= -Z.get(a).program;a.program=void 0;void 0!==b&&Pa.releaseProgram(b)}function l(a,b){return b[0]-a[0]}function n(a,b){return a.object.renderOrder!==b.object.renderOrder?a.object.renderOrder-b.object.renderOrder:a.material.id!==b.material.id?a.material.id-b.material.id:a.z!==b.z?a.z-b.z:a.id-b.id}function p(a,b){return a.object.renderOrder!==b.object.renderOrder?a.object.renderOrder-b.object.renderOrder:a.z!==b.z?b.z-a.z:a.id-b.id}function m(a,b,c,d,e){var f;c.transparent?(d=U,f=++la):(d=ba,f=++ma); -f=d[f];void 0!==f?(f.id=a.id,f.object=a,f.geometry=b,f.material=c,f.z=Y.z,f.group=e):(f={id:a.id,object:a,geometry:b,material:c,z:Y.z,group:e},d.push(f))}function q(a){if(!1!==a.visible){if(a instanceof THREE.Light)fa.push(a);else if(a instanceof THREE.Sprite)ka.push(a);else if(a instanceof THREE.LensFlare)qa.push(a);else if(a instanceof THREE.ImmediateRenderObject)!0===ia.sortObjects&&(Y.setFromMatrixPosition(a.matrixWorld),Y.applyProjection(Ha)),m(a,null,a.material,Y.z,null);else if(a instanceof -THREE.Mesh||a instanceof THREE.Line||a instanceof THREE.Points)if(a instanceof THREE.SkinnedMesh&&a.skeleton.update(),!1===a.frustumCulled||!0===Za.intersectsObject(a)){var b=a.material;if(!0===b.visible){!0===ia.sortObjects&&(Y.setFromMatrixPosition(a.matrixWorld),Y.applyProjection(Ha));var c=ra.update(a);if(b instanceof THREE.MeshFaceMaterial)for(var d=c.groups,e=b.materials,b=0,f=d.length;bda;da++)Ca[da]=!ia.autoScaleCubemaps||xb||mb?mb?aa.image[da].image:aa.image[da]:H(aa.image[da],na.maxCubemapSize);var yb=Ca[0],zb=THREE.Math.isPowerOfTwo(yb.width)&&THREE.Math.isPowerOfTwo(yb.height),ya=D(aa.format),nb=D(aa.type);x(s.TEXTURE_CUBE_MAP, -aa,zb);for(da=0;6>da;da++)if(xb)for(var Da,Ab=Ca[da].mipmaps,Ua=0,qb=Ab.length;Ua=na.maxTextures&&console.warn("WebGLRenderer: trying to use "+a+" texture units while this GPU supports only "+na.maxTextures);$a+=1;return a}function A(a,b,c,d){a[b+0]=c.r*d;a[b+1]=c.g*d;a[b+2]=c.b*d}function x(a,b,c){c?(s.texParameteri(a,s.TEXTURE_WRAP_S,D(b.wrapS)),s.texParameteri(a, -s.TEXTURE_WRAP_T,D(b.wrapT)),s.texParameteri(a,s.TEXTURE_MAG_FILTER,D(b.magFilter)),s.texParameteri(a,s.TEXTURE_MIN_FILTER,D(b.minFilter))):(s.texParameteri(a,s.TEXTURE_WRAP_S,s.CLAMP_TO_EDGE),s.texParameteri(a,s.TEXTURE_WRAP_T,s.CLAMP_TO_EDGE),b.wrapS===THREE.ClampToEdgeWrapping&&b.wrapT===THREE.ClampToEdgeWrapping||console.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping. ( "+b.sourceFile+" )"),s.texParameteri(a,s.TEXTURE_MAG_FILTER, -B(b.magFilter)),s.texParameteri(a,s.TEXTURE_MIN_FILTER,B(b.minFilter)),b.minFilter!==THREE.NearestFilter&&b.minFilter!==THREE.LinearFilter&&console.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter. ( "+b.sourceFile+" )"));!(c=X.get("EXT_texture_filter_anisotropic"))||b.type===THREE.FloatType&&null===X.get("OES_texture_float_linear")||b.type===THREE.HalfFloatType&&null===X.get("OES_texture_half_float_linear")||!(1< -b.anisotropy||Z.get(b).__currentAnisotropy)||(s.texParameterf(a,c.TEXTURE_MAX_ANISOTROPY_EXT,Math.min(b.anisotropy,ia.getMaxAnisotropy())),Z.get(b).__currentAnisotropy=b.anisotropy)}function H(a,b){if(a.width>b||a.height>b){var c=b/Math.max(a.width,a.height),d=document.createElement("canvas");d.width=Math.floor(a.width*c);d.height=Math.floor(a.height*c);d.getContext("2d").drawImage(a,0,0,a.width,a.height,0,0,d.width,d.height);console.warn("THREE.WebGLRenderer: image is too big ("+a.width+"x"+a.height+ -"). Resized to "+d.width+"x"+d.height,a);return d}return a}function F(a,b,c){s.bindFramebuffer(s.FRAMEBUFFER,a);s.framebufferTexture2D(s.FRAMEBUFFER,s.COLOR_ATTACHMENT0,c,Z.get(b).__webglTexture,0)}function z(a,b){s.bindRenderbuffer(s.RENDERBUFFER,a);b.depthBuffer&&!b.stencilBuffer?(s.renderbufferStorage(s.RENDERBUFFER,s.DEPTH_COMPONENT16,b.width,b.height),s.framebufferRenderbuffer(s.FRAMEBUFFER,s.DEPTH_ATTACHMENT,s.RENDERBUFFER,a)):b.depthBuffer&&b.stencilBuffer?(s.renderbufferStorage(s.RENDERBUFFER, -s.DEPTH_STENCIL,b.width,b.height),s.framebufferRenderbuffer(s.FRAMEBUFFER,s.DEPTH_STENCIL_ATTACHMENT,s.RENDERBUFFER,a)):s.renderbufferStorage(s.RENDERBUFFER,s.RGBA4,b.width,b.height)}function B(a){return a===THREE.NearestFilter||a===THREE.NearestMipMapNearestFilter||a===THREE.NearestMipMapLinearFilter?s.NEAREST:s.LINEAR}function D(a){var b;if(a===THREE.RepeatWrapping)return s.REPEAT;if(a===THREE.ClampToEdgeWrapping)return s.CLAMP_TO_EDGE;if(a===THREE.MirroredRepeatWrapping)return s.MIRRORED_REPEAT; -if(a===THREE.NearestFilter)return s.NEAREST;if(a===THREE.NearestMipMapNearestFilter)return s.NEAREST_MIPMAP_NEAREST;if(a===THREE.NearestMipMapLinearFilter)return s.NEAREST_MIPMAP_LINEAR;if(a===THREE.LinearFilter)return s.LINEAR;if(a===THREE.LinearMipMapNearestFilter)return s.LINEAR_MIPMAP_NEAREST;if(a===THREE.LinearMipMapLinearFilter)return s.LINEAR_MIPMAP_LINEAR;if(a===THREE.UnsignedByteType)return s.UNSIGNED_BYTE;if(a===THREE.UnsignedShort4444Type)return s.UNSIGNED_SHORT_4_4_4_4;if(a===THREE.UnsignedShort5551Type)return s.UNSIGNED_SHORT_5_5_5_1; -if(a===THREE.UnsignedShort565Type)return s.UNSIGNED_SHORT_5_6_5;if(a===THREE.ByteType)return s.BYTE;if(a===THREE.ShortType)return s.SHORT;if(a===THREE.UnsignedShortType)return s.UNSIGNED_SHORT;if(a===THREE.IntType)return s.INT;if(a===THREE.UnsignedIntType)return s.UNSIGNED_INT;if(a===THREE.FloatType)return s.FLOAT;b=X.get("OES_texture_half_float");if(null!==b&&a===THREE.HalfFloatType)return b.HALF_FLOAT_OES;if(a===THREE.AlphaFormat)return s.ALPHA;if(a===THREE.RGBFormat)return s.RGB;if(a===THREE.RGBAFormat)return s.RGBA; -if(a===THREE.LuminanceFormat)return s.LUMINANCE;if(a===THREE.LuminanceAlphaFormat)return s.LUMINANCE_ALPHA;if(a===THREE.AddEquation)return s.FUNC_ADD;if(a===THREE.SubtractEquation)return s.FUNC_SUBTRACT;if(a===THREE.ReverseSubtractEquation)return s.FUNC_REVERSE_SUBTRACT;if(a===THREE.ZeroFactor)return s.ZERO;if(a===THREE.OneFactor)return s.ONE;if(a===THREE.SrcColorFactor)return s.SRC_COLOR;if(a===THREE.OneMinusSrcColorFactor)return s.ONE_MINUS_SRC_COLOR;if(a===THREE.SrcAlphaFactor)return s.SRC_ALPHA; -if(a===THREE.OneMinusSrcAlphaFactor)return s.ONE_MINUS_SRC_ALPHA;if(a===THREE.DstAlphaFactor)return s.DST_ALPHA;if(a===THREE.OneMinusDstAlphaFactor)return s.ONE_MINUS_DST_ALPHA;if(a===THREE.DstColorFactor)return s.DST_COLOR;if(a===THREE.OneMinusDstColorFactor)return s.ONE_MINUS_DST_COLOR;if(a===THREE.SrcAlphaSaturateFactor)return s.SRC_ALPHA_SATURATE;b=X.get("WEBGL_compressed_texture_s3tc");if(null!==b){if(a===THREE.RGB_S3TC_DXT1_Format)return b.COMPRESSED_RGB_S3TC_DXT1_EXT;if(a===THREE.RGBA_S3TC_DXT1_Format)return b.COMPRESSED_RGBA_S3TC_DXT1_EXT; +THREE.WebGLRenderer=function(a){function b(a,b,c,d){!0===V&&(a*=d,b*=d,c*=d);r.clearColor(a,b,c,d)}function c(){O.init();r.viewport(Ea,ua,Fa,Ga);b(R.r,R.g,R.b,B)}function d(){Pa=eb=null;pa="";Qa=-1;$a=!0;O.reset()}function e(a){a.preventDefault();d();c();Z.clear()}function g(a){a=a.target;a.removeEventListener("dispose",g);a:{var b=Z.get(a);if(a.image&&b.__image__webglTextureCube)r.deleteTexture(b.__image__webglTextureCube);else{if(void 0===b.__webglInit)break a;r.deleteTexture(b.__webglTexture)}Z.delete(a)}Ha.textures--} +function f(a){a=a.target;a.removeEventListener("dispose",f);var b=Z.get(a);if(a&&void 0!==b.__webglTexture){r.deleteTexture(b.__webglTexture);if(a instanceof THREE.WebGLRenderTargetCube)for(var c=0;6>c;c++)r.deleteFramebuffer(b.__webglFramebuffer[c]),r.deleteRenderbuffer(b.__webglRenderbuffer[c]);else r.deleteFramebuffer(b.__webglFramebuffer),r.deleteRenderbuffer(b.__webglRenderbuffer);Z.delete(a)}Ha.textures--}function h(a){a=a.target;a.removeEventListener("dispose",h);k(a);Z.delete(a)}function k(a){var b= +Z.get(a).program;a.program=void 0;void 0!==b&&Ra.releaseProgram(b)}function l(a,b){return b[0]-a[0]}function n(a,b){return a.object.renderOrder!==b.object.renderOrder?a.object.renderOrder-b.object.renderOrder:a.material.id!==b.material.id?a.material.id-b.material.id:a.z!==b.z?a.z-b.z:a.id-b.id}function p(a,b){return a.object.renderOrder!==b.object.renderOrder?a.object.renderOrder-b.object.renderOrder:a.z!==b.z?b.z-a.z:a.id-b.id}function m(a,b,c,d,e){var f;c.transparent?(d=qa,f=++ya):(d=ba,f=++la); +f=d[f];void 0!==f?(f.id=a.id,f.object=a,f.geometry=b,f.material=c,f.z=Y.z,f.group=e):(f={id:a.id,object:a,geometry:b,material:c,z:Y.z,group:e},d.push(f))}function q(a){if(!1!==a.visible){if(a instanceof THREE.Light)fa.push(a);else if(a instanceof THREE.Sprite)Ia.push(a);else if(a instanceof THREE.LensFlare)za.push(a);else if(a instanceof THREE.ImmediateRenderObject)!0===ha.sortObjects&&(Y.setFromMatrixPosition(a.matrixWorld),Y.applyProjection(Ja)),m(a,null,a.material,Y.z,null);else if(a instanceof +THREE.Mesh||a instanceof THREE.Line||a instanceof THREE.Points)if(a instanceof THREE.SkinnedMesh&&a.skeleton.update(),!1===a.frustumCulled||!0===Sa.intersectsObject(a)){var b=a.material;if(!0===b.visible){!0===ha.sortObjects&&(Y.setFromMatrixPosition(a.matrixWorld),Y.applyProjection(Ja));var c=ra.update(a);if(b instanceof THREE.MeshFaceMaterial)for(var d=c.groups,e=b.materials,b=0,f=d.length;bda;da++)Ca[da]=!ha.autoScaleCubemaps||xb||nb?nb?aa.image[da].image:aa.image[da]:H(aa.image[da],ma.maxCubemapSize);var yb=Ca[0],zb=THREE.Math.isPowerOfTwo(yb.width)&&THREE.Math.isPowerOfTwo(yb.height),xa=I(aa.format),ob=I(aa.type);x(r.TEXTURE_CUBE_MAP, +aa,zb);for(da=0;6>da;da++)if(xb)for(var Da,Ab=Ca[da].mipmaps,Xa=0,rb=Ab.length;Xa=ma.maxTextures&&console.warn("WebGLRenderer: trying to use "+a+" texture units while this GPU supports only "+ma.maxTextures);ab+=1;return a}function y(a,b,c,d){a[b+0]=c.r*d;a[b+1]=c.g*d;a[b+2]=c.b*d}function x(a,b,c){c?(r.texParameteri(a,r.TEXTURE_WRAP_S,I(b.wrapS)),r.texParameteri(a, +r.TEXTURE_WRAP_T,I(b.wrapT)),r.texParameteri(a,r.TEXTURE_MAG_FILTER,I(b.magFilter)),r.texParameteri(a,r.TEXTURE_MIN_FILTER,I(b.minFilter))):(r.texParameteri(a,r.TEXTURE_WRAP_S,r.CLAMP_TO_EDGE),r.texParameteri(a,r.TEXTURE_WRAP_T,r.CLAMP_TO_EDGE),b.wrapS===THREE.ClampToEdgeWrapping&&b.wrapT===THREE.ClampToEdgeWrapping||console.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping. ( "+b.sourceFile+" )"),r.texParameteri(a,r.TEXTURE_MAG_FILTER, +A(b.magFilter)),r.texParameteri(a,r.TEXTURE_MIN_FILTER,A(b.minFilter)),b.minFilter!==THREE.NearestFilter&&b.minFilter!==THREE.LinearFilter&&console.warn("THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter. ( "+b.sourceFile+" )"));!(c=X.get("EXT_texture_filter_anisotropic"))||b.type===THREE.FloatType&&null===X.get("OES_texture_float_linear")||b.type===THREE.HalfFloatType&&null===X.get("OES_texture_half_float_linear")||!(1< +b.anisotropy||Z.get(b).__currentAnisotropy)||(r.texParameterf(a,c.TEXTURE_MAX_ANISOTROPY_EXT,Math.min(b.anisotropy,ha.getMaxAnisotropy())),Z.get(b).__currentAnisotropy=b.anisotropy)}function H(a,b){if(a.width>b||a.height>b){var c=b/Math.max(a.width,a.height),d=document.createElement("canvas");d.width=Math.floor(a.width*c);d.height=Math.floor(a.height*c);d.getContext("2d").drawImage(a,0,0,a.width,a.height,0,0,d.width,d.height);console.warn("THREE.WebGLRenderer: image is too big ("+a.width+"x"+a.height+ +"). Resized to "+d.width+"x"+d.height,a);return d}return a}function E(a,b,c){r.bindFramebuffer(r.FRAMEBUFFER,a);r.framebufferTexture2D(r.FRAMEBUFFER,r.COLOR_ATTACHMENT0,c,Z.get(b).__webglTexture,0)}function z(a,b){r.bindRenderbuffer(r.RENDERBUFFER,a);b.depthBuffer&&!b.stencilBuffer?(r.renderbufferStorage(r.RENDERBUFFER,r.DEPTH_COMPONENT16,b.width,b.height),r.framebufferRenderbuffer(r.FRAMEBUFFER,r.DEPTH_ATTACHMENT,r.RENDERBUFFER,a)):b.depthBuffer&&b.stencilBuffer?(r.renderbufferStorage(r.RENDERBUFFER, +r.DEPTH_STENCIL,b.width,b.height),r.framebufferRenderbuffer(r.FRAMEBUFFER,r.DEPTH_STENCIL_ATTACHMENT,r.RENDERBUFFER,a)):r.renderbufferStorage(r.RENDERBUFFER,r.RGBA4,b.width,b.height)}function A(a){return a===THREE.NearestFilter||a===THREE.NearestMipMapNearestFilter||a===THREE.NearestMipMapLinearFilter?r.NEAREST:r.LINEAR}function I(a){var b;if(a===THREE.RepeatWrapping)return r.REPEAT;if(a===THREE.ClampToEdgeWrapping)return r.CLAMP_TO_EDGE;if(a===THREE.MirroredRepeatWrapping)return r.MIRRORED_REPEAT; +if(a===THREE.NearestFilter)return r.NEAREST;if(a===THREE.NearestMipMapNearestFilter)return r.NEAREST_MIPMAP_NEAREST;if(a===THREE.NearestMipMapLinearFilter)return r.NEAREST_MIPMAP_LINEAR;if(a===THREE.LinearFilter)return r.LINEAR;if(a===THREE.LinearMipMapNearestFilter)return r.LINEAR_MIPMAP_NEAREST;if(a===THREE.LinearMipMapLinearFilter)return r.LINEAR_MIPMAP_LINEAR;if(a===THREE.UnsignedByteType)return r.UNSIGNED_BYTE;if(a===THREE.UnsignedShort4444Type)return r.UNSIGNED_SHORT_4_4_4_4;if(a===THREE.UnsignedShort5551Type)return r.UNSIGNED_SHORT_5_5_5_1; +if(a===THREE.UnsignedShort565Type)return r.UNSIGNED_SHORT_5_6_5;if(a===THREE.ByteType)return r.BYTE;if(a===THREE.ShortType)return r.SHORT;if(a===THREE.UnsignedShortType)return r.UNSIGNED_SHORT;if(a===THREE.IntType)return r.INT;if(a===THREE.UnsignedIntType)return r.UNSIGNED_INT;if(a===THREE.FloatType)return r.FLOAT;b=X.get("OES_texture_half_float");if(null!==b&&a===THREE.HalfFloatType)return b.HALF_FLOAT_OES;if(a===THREE.AlphaFormat)return r.ALPHA;if(a===THREE.RGBFormat)return r.RGB;if(a===THREE.RGBAFormat)return r.RGBA; +if(a===THREE.LuminanceFormat)return r.LUMINANCE;if(a===THREE.LuminanceAlphaFormat)return r.LUMINANCE_ALPHA;if(a===THREE.AddEquation)return r.FUNC_ADD;if(a===THREE.SubtractEquation)return r.FUNC_SUBTRACT;if(a===THREE.ReverseSubtractEquation)return r.FUNC_REVERSE_SUBTRACT;if(a===THREE.ZeroFactor)return r.ZERO;if(a===THREE.OneFactor)return r.ONE;if(a===THREE.SrcColorFactor)return r.SRC_COLOR;if(a===THREE.OneMinusSrcColorFactor)return r.ONE_MINUS_SRC_COLOR;if(a===THREE.SrcAlphaFactor)return r.SRC_ALPHA; +if(a===THREE.OneMinusSrcAlphaFactor)return r.ONE_MINUS_SRC_ALPHA;if(a===THREE.DstAlphaFactor)return r.DST_ALPHA;if(a===THREE.OneMinusDstAlphaFactor)return r.ONE_MINUS_DST_ALPHA;if(a===THREE.DstColorFactor)return r.DST_COLOR;if(a===THREE.OneMinusDstColorFactor)return r.ONE_MINUS_DST_COLOR;if(a===THREE.SrcAlphaSaturateFactor)return r.SRC_ALPHA_SATURATE;b=X.get("WEBGL_compressed_texture_s3tc");if(null!==b){if(a===THREE.RGB_S3TC_DXT1_Format)return b.COMPRESSED_RGB_S3TC_DXT1_EXT;if(a===THREE.RGBA_S3TC_DXT1_Format)return b.COMPRESSED_RGBA_S3TC_DXT1_EXT; if(a===THREE.RGBA_S3TC_DXT3_Format)return b.COMPRESSED_RGBA_S3TC_DXT3_EXT;if(a===THREE.RGBA_S3TC_DXT5_Format)return b.COMPRESSED_RGBA_S3TC_DXT5_EXT}b=X.get("WEBGL_compressed_texture_pvrtc");if(null!==b){if(a===THREE.RGB_PVRTC_4BPPV1_Format)return b.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;if(a===THREE.RGB_PVRTC_2BPPV1_Format)return b.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;if(a===THREE.RGBA_PVRTC_4BPPV1_Format)return b.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;if(a===THREE.RGBA_PVRTC_2BPPV1_Format)return b.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG}b= -X.get("EXT_blend_minmax");if(null!==b){if(a===THREE.MinEquation)return b.MIN_EXT;if(a===THREE.MaxEquation)return b.MAX_EXT}return 0}console.log("THREE.WebGLRenderer",THREE.REVISION);a=a||{};var N=void 0!==a.canvas?a.canvas:document.createElement("canvas"),E=void 0!==a.context?a.context:null,L=N.width,M=N.height,C=1,G=void 0!==a.alpha?a.alpha:!1,K=void 0!==a.depth?a.depth:!0,y=void 0!==a.stencil?a.stencil:!0,J=void 0!==a.antialias?a.antialias:!1,V=void 0!==a.premultipliedAlpha?a.premultipliedAlpha: -!0,Q=void 0!==a.preserveDrawingBuffer?a.preserveDrawingBuffer:!1,R=new THREE.Color(0),I=0,fa=[],ba=[],ma=-1,U=[],la=-1,Va=new Float32Array(8),ka=[],qa=[];this.domElement=N;this.context=null;this.sortObjects=this.autoClearStencil=this.autoClearDepth=this.autoClearColor=this.autoClear=!0;this.gammaFactor=2;this.gammaOutput=this.gammaInput=!1;this.maxMorphTargets=8;this.maxMorphNormals=4;this.autoScaleCubemaps=!0;var ia=this,db=null,za=null,Oa=-1,pa="",Na=null,$a=0,ua=0,Ea=0,va=N.width,Fa=N.height,Wa= -0,Xa=0,Za=new THREE.Frustum,Ha=new THREE.Matrix4,Y=new THREE.Vector3,ca=new THREE.Vector3,Ya=!0,sb={ambient:[0,0,0],directional:{length:0,colors:[],positions:[]},point:{length:0,colors:[],positions:[],distances:[],decays:[]},spot:{length:0,colors:[],positions:[],distances:[],directions:[],anglesCos:[],exponents:[],decays:[]},hemi:{length:0,skyColors:[],groundColors:[],positions:[]}},Ga={geometries:0,textures:0},ta={calls:0,vertices:0,faces:0,points:0};this.info={render:ta,memory:Ga,programs:null}; -var s;try{G={alpha:G,depth:K,stencil:y,antialias:J,premultipliedAlpha:V,preserveDrawingBuffer:Q};s=E||N.getContext("webgl",G)||N.getContext("experimental-webgl",G);if(null===s){if(null!==N.getContext("webgl"))throw"Error creating WebGL context with your selected attributes.";throw"Error creating WebGL context.";}N.addEventListener("webglcontextlost",e,!1)}catch(ob){console.error("THREE.WebGLRenderer: "+ob)}var X=new THREE.WebGLExtensions(s);X.get("OES_texture_float");X.get("OES_texture_float_linear"); -X.get("OES_texture_half_float");X.get("OES_texture_half_float_linear");X.get("OES_standard_derivatives");X.get("ANGLE_instanced_arrays");X.get("OES_element_index_uint")&&(THREE.BufferGeometry.MaxIndex=4294967296);var na=new THREE.WebGLCapabilities(s,X,a),O=new THREE.WebGLState(s,X,D),Z=new THREE.WebGLProperties,ra=new THREE.WebGLObjects(s,Z,this.info),Pa=new THREE.WebGLPrograms(this,na);this.info.programs=Pa.programs;var pb=new THREE.WebGLBufferRenderer(s,X,ta),qb=new THREE.WebGLIndexedBufferRenderer(s, -X,ta);c();this.context=s;this.capabilities=na;this.extensions=X;this.state=O;var ga=new THREE.WebGLShadowMap(this,fa,ra);this.shadowMap=ga;var rb=new THREE.SpritePlugin(this,ka),Bb=new THREE.LensFlarePlugin(this,qa);this.getContext=function(){return s};this.getContextAttributes=function(){return s.getContextAttributes()};this.forceContextLoss=function(){X.get("WEBGL_lose_context").loseContext()};this.getMaxAnisotropy=function(){var a;return function(){if(void 0!==a)return a;var b=X.get("EXT_texture_filter_anisotropic"); -return a=null!==b?s.getParameter(b.MAX_TEXTURE_MAX_ANISOTROPY_EXT):0}}();this.getPrecision=function(){return na.precision};this.getPixelRatio=function(){return C};this.setPixelRatio=function(a){void 0!==a&&(C=a)};this.getSize=function(){return{width:L,height:M}};this.setSize=function(a,b,c){L=a;M=b;N.width=a*C;N.height=b*C;!1!==c&&(N.style.width=a+"px",N.style.height=b+"px");this.setViewport(0,0,a,b)};this.setViewport=function(a,b,c,d){ua=a*C;Ea=b*C;va=c*C;Fa=d*C;s.viewport(ua,Ea,va,Fa)};this.setScissor= -function(a,b,c,d){s.scissor(a*C,b*C,c*C,d*C)};this.enableScissorTest=function(a){O.setScissorTest(a)};this.getClearColor=function(){return R};this.setClearColor=function(a,c){R.set(a);I=void 0!==c?c:1;b(R.r,R.g,R.b,I)};this.getClearAlpha=function(){return I};this.setClearAlpha=function(a){I=a;b(R.r,R.g,R.b,I)};this.clear=function(a,b,c){var d=0;if(void 0===a||a)d|=s.COLOR_BUFFER_BIT;if(void 0===b||b)d|=s.DEPTH_BUFFER_BIT;if(void 0===c||c)d|=s.STENCIL_BUFFER_BIT;s.clear(d)};this.clearColor=function(){s.clear(s.COLOR_BUFFER_BIT)}; -this.clearDepth=function(){s.clear(s.DEPTH_BUFFER_BIT)};this.clearStencil=function(){s.clear(s.STENCIL_BUFFER_BIT)};this.clearTarget=function(a,b,c,d){this.setRenderTarget(a);this.clear(b,c,d)};this.resetGLState=d;this.dispose=function(){N.removeEventListener("webglcontextlost",e,!1)};this.renderBufferImmediate=function(a,b,c){O.initAttributes();var d=Z.get(a);a.hasPositions&&!d.position&&(d.position=s.createBuffer());a.hasNormals&&!d.normal&&(d.normal=s.createBuffer());a.hasUvs&&!d.uv&&(d.uv=s.createBuffer()); -a.hasColors&&!d.color&&(d.color=s.createBuffer());b=b.getAttributes();a.hasPositions&&(s.bindBuffer(s.ARRAY_BUFFER,d.position),s.bufferData(s.ARRAY_BUFFER,a.positionArray,s.DYNAMIC_DRAW),O.enableAttribute(b.position),s.vertexAttribPointer(b.position,3,s.FLOAT,!1,0,0));if(a.hasNormals){s.bindBuffer(s.ARRAY_BUFFER,d.normal);if("MeshPhongMaterial"!==c.type&&c.shading===THREE.FlatShading)for(var e=0,f=3*a.count;eh;h++)c.__webglFramebuffer[h]=s.createFramebuffer(),c.__webglRenderbuffer[h]= -s.createRenderbuffer(),O.texImage2D(s.TEXTURE_CUBE_MAP_POSITIVE_X+h,0,e,a.width,a.height,0,e,g,null),F(c.__webglFramebuffer[h],a,s.TEXTURE_CUBE_MAP_POSITIVE_X+h),z(c.__webglRenderbuffer[h],a);a.generateMipmaps&&d&&s.generateMipmap(s.TEXTURE_CUBE_MAP)}else c.__webglFramebuffer=s.createFramebuffer(),c.__webglRenderbuffer=a.shareDepthFrom?a.shareDepthFrom.__webglRenderbuffer:s.createRenderbuffer(),O.bindTexture(s.TEXTURE_2D,c.__webglTexture),x(s.TEXTURE_2D,a,d),O.texImage2D(s.TEXTURE_2D,0,e,a.width, -a.height,0,e,g,null),F(c.__webglFramebuffer,a,s.TEXTURE_2D),a.shareDepthFrom?a.depthBuffer&&!a.stencilBuffer?s.framebufferRenderbuffer(s.FRAMEBUFFER,s.DEPTH_ATTACHMENT,s.RENDERBUFFER,c.__webglRenderbuffer):a.depthBuffer&&a.stencilBuffer&&s.framebufferRenderbuffer(s.FRAMEBUFFER,s.DEPTH_STENCIL_ATTACHMENT,s.RENDERBUFFER,c.__webglRenderbuffer):z(c.__webglRenderbuffer,a),a.generateMipmaps&&d&&s.generateMipmap(s.TEXTURE_2D);b?O.bindTexture(s.TEXTURE_CUBE_MAP,null):O.bindTexture(s.TEXTURE_2D,null);s.bindRenderbuffer(s.RENDERBUFFER, -null);s.bindFramebuffer(s.FRAMEBUFFER,null)}a?(c=Z.get(a),b=b?c.__webglFramebuffer[a.activeCubeFace]:c.__webglFramebuffer,c=a.width,a=a.height,e=d=0):(b=null,c=va,a=Fa,d=ua,e=Ea);b!==za&&(s.bindFramebuffer(s.FRAMEBUFFER,b),s.viewport(d,e,c,a),za=b);Wa=c;Xa=a};this.readRenderTargetPixels=function(a,b,c,d,e,f){if(!(a instanceof THREE.WebGLRenderTarget))console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.");else if(Z.get(a).__webglFramebuffer)if(a.format!== -THREE.RGBAFormat)console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA format. readPixels can read only RGBA format.");else{var g=!1;Z.get(a).__webglFramebuffer!==za&&(s.bindFramebuffer(s.FRAMEBUFFER,Z.get(a).__webglFramebuffer),g=!0);s.checkFramebufferStatus(s.FRAMEBUFFER)===s.FRAMEBUFFER_COMPLETE?s.readPixels(b,c,d,e,s.RGBA,s.UNSIGNED_BYTE,f):console.error("THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete."); -g&&s.bindFramebuffer(s.FRAMEBUFFER,za)}};this.supportsFloatTextures=function(){console.warn("THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( 'OES_texture_float' ).");return X.get("OES_texture_float")};this.supportsHalfFloatTextures=function(){console.warn("THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( 'OES_texture_half_float' ).");return X.get("OES_texture_half_float")};this.supportsStandardDerivatives=function(){console.warn("THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( 'OES_standard_derivatives' )."); -return X.get("OES_standard_derivatives")};this.supportsCompressedTextureS3TC=function(){console.warn("THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( 'WEBGL_compressed_texture_s3tc' ).");return X.get("WEBGL_compressed_texture_s3tc")};this.supportsCompressedTexturePVRTC=function(){console.warn("THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( 'WEBGL_compressed_texture_pvrtc' ).");return X.get("WEBGL_compressed_texture_pvrtc")};this.supportsBlendMinMax= -function(){console.warn("THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( 'EXT_blend_minmax' ).");return X.get("EXT_blend_minmax")};this.supportsVertexTextures=function(){return na.vertexTextures};this.supportsInstancedArrays=function(){console.warn("THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( 'ANGLE_instanced_arrays' ).");return X.get("ANGLE_instanced_arrays")};this.initMaterial=function(){console.warn("THREE.WebGLRenderer: .initMaterial() has been removed.")}; -this.addPrePlugin=function(){console.warn("THREE.WebGLRenderer: .addPrePlugin() has been removed.")};this.addPostPlugin=function(){console.warn("THREE.WebGLRenderer: .addPostPlugin() has been removed.")};this.updateShadowMap=function(){console.warn("THREE.WebGLRenderer: .updateShadowMap() has been removed.")};Object.defineProperties(this,{shadowMapEnabled:{get:function(){return ga.enabled},set:function(a){console.warn("THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled.");ga.enabled= -a}},shadowMapType:{get:function(){return ga.type},set:function(a){console.warn("THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type.");ga.type=a}},shadowMapCullFace:{get:function(){return ga.cullFace},set:function(a){console.warn("THREE.WebGLRenderer: .shadowMapCullFace is now .shadowMap.cullFace.");ga.cullFace=a}},shadowMapDebug:{get:function(){return ga.debug},set:function(a){console.warn("THREE.WebGLRenderer: .shadowMapDebug is now .shadowMap.debug.");ga.debug=a}}})}; +X.get("EXT_blend_minmax");if(null!==b){if(a===THREE.MinEquation)return b.MIN_EXT;if(a===THREE.MaxEquation)return b.MAX_EXT}return 0}console.log("THREE.WebGLRenderer",THREE.REVISION);a=a||{};var L=void 0!==a.canvas?a.canvas:document.createElement("canvas"),D=void 0!==a.context?a.context:null,C=L.width,N=L.height,K=1,G=void 0!==a.alpha?a.alpha:!1,M=void 0!==a.depth?a.depth:!0,F=void 0!==a.stencil?a.stencil:!0,Q=void 0!==a.antialias?a.antialias:!1,V=void 0!==a.premultipliedAlpha?a.premultipliedAlpha: +!0,S=void 0!==a.preserveDrawingBuffer?a.preserveDrawingBuffer:!1,R=new THREE.Color(0),B=0,fa=[],ba=[],la=-1,qa=[],ya=-1,ta=new Float32Array(8),Ia=[],za=[];this.domElement=L;this.context=null;this.sortObjects=this.autoClearStencil=this.autoClearDepth=this.autoClearColor=this.autoClear=!0;this.gammaFactor=2;this.gammaOutput=this.gammaInput=!1;this.maxMorphTargets=8;this.maxMorphNormals=4;this.autoScaleCubemaps=!0;var ha=this,eb=null,oa=null,Qa=-1,pa="",Pa=null,ab=0,Ea=0,ua=0,Fa=L.width,Ga=L.height, +Ya=0,Za=0,Sa=new THREE.Frustum,Ja=new THREE.Matrix4,Y=new THREE.Vector3,ca=new THREE.Vector3,$a=!0,tb={ambient:[0,0,0],directional:{length:0,colors:[],positions:[]},point:{length:0,colors:[],positions:[],distances:[],decays:[]},spot:{length:0,colors:[],positions:[],distances:[],directions:[],anglesCos:[],exponents:[],decays:[]},hemi:{length:0,skyColors:[],groundColors:[],positions:[]}},Ha={geometries:0,textures:0},ka={calls:0,vertices:0,faces:0,points:0};this.info={render:ka,memory:Ha,programs:null}; +var r;try{G={alpha:G,depth:M,stencil:F,antialias:Q,premultipliedAlpha:V,preserveDrawingBuffer:S};r=D||L.getContext("webgl",G)||L.getContext("experimental-webgl",G);if(null===r){if(null!==L.getContext("webgl"))throw"Error creating WebGL context with your selected attributes.";throw"Error creating WebGL context.";}L.addEventListener("webglcontextlost",e,!1)}catch(pb){console.error("THREE.WebGLRenderer: "+pb)}var X=new THREE.WebGLExtensions(r);X.get("OES_texture_float");X.get("OES_texture_float_linear"); +X.get("OES_texture_half_float");X.get("OES_texture_half_float_linear");X.get("OES_standard_derivatives");X.get("ANGLE_instanced_arrays");X.get("OES_element_index_uint")&&(THREE.BufferGeometry.MaxIndex=4294967296);var ma=new THREE.WebGLCapabilities(r,X,a),O=new THREE.WebGLState(r,X,I),Z=new THREE.WebGLProperties,ra=new THREE.WebGLObjects(r,Z,this.info),Ra=new THREE.WebGLPrograms(this,ma);this.info.programs=Ra.programs;var qb=new THREE.WebGLBufferRenderer(r,X,ka),rb=new THREE.WebGLIndexedBufferRenderer(r, +X,ka);c();this.context=r;this.capabilities=ma;this.extensions=X;this.state=O;var ja=new THREE.WebGLShadowMap(this,fa,ra);this.shadowMap=ja;var sb=new THREE.SpritePlugin(this,Ia),Bb=new THREE.LensFlarePlugin(this,za);this.getContext=function(){return r};this.getContextAttributes=function(){return r.getContextAttributes()};this.forceContextLoss=function(){X.get("WEBGL_lose_context").loseContext()};this.getMaxAnisotropy=function(){var a;return function(){if(void 0!==a)return a;var b=X.get("EXT_texture_filter_anisotropic"); +return a=null!==b?r.getParameter(b.MAX_TEXTURE_MAX_ANISOTROPY_EXT):0}}();this.getPrecision=function(){return ma.precision};this.getPixelRatio=function(){return K};this.setPixelRatio=function(a){void 0!==a&&(K=a)};this.getSize=function(){return{width:C,height:N}};this.setSize=function(a,b,c){C=a;N=b;L.width=a*K;L.height=b*K;!1!==c&&(L.style.width=a+"px",L.style.height=b+"px");this.setViewport(0,0,a,b)};this.setViewport=function(a,b,c,d){Ea=a*K;ua=b*K;Fa=c*K;Ga=d*K;r.viewport(Ea,ua,Fa,Ga)};this.setScissor= +function(a,b,c,d){r.scissor(a*K,b*K,c*K,d*K)};this.enableScissorTest=function(a){O.setScissorTest(a)};this.getClearColor=function(){return R};this.setClearColor=function(a,c){R.set(a);B=void 0!==c?c:1;b(R.r,R.g,R.b,B)};this.getClearAlpha=function(){return B};this.setClearAlpha=function(a){B=a;b(R.r,R.g,R.b,B)};this.clear=function(a,b,c){var d=0;if(void 0===a||a)d|=r.COLOR_BUFFER_BIT;if(void 0===b||b)d|=r.DEPTH_BUFFER_BIT;if(void 0===c||c)d|=r.STENCIL_BUFFER_BIT;r.clear(d)};this.clearColor=function(){r.clear(r.COLOR_BUFFER_BIT)}; +this.clearDepth=function(){r.clear(r.DEPTH_BUFFER_BIT)};this.clearStencil=function(){r.clear(r.STENCIL_BUFFER_BIT)};this.clearTarget=function(a,b,c,d){this.setRenderTarget(a);this.clear(b,c,d)};this.resetGLState=d;this.dispose=function(){L.removeEventListener("webglcontextlost",e,!1)};this.renderBufferImmediate=function(a,b,c){O.initAttributes();var d=Z.get(a);a.hasPositions&&!d.position&&(d.position=r.createBuffer());a.hasNormals&&!d.normal&&(d.normal=r.createBuffer());a.hasUvs&&!d.uv&&(d.uv=r.createBuffer()); +a.hasColors&&!d.color&&(d.color=r.createBuffer());b=b.getAttributes();a.hasPositions&&(r.bindBuffer(r.ARRAY_BUFFER,d.position),r.bufferData(r.ARRAY_BUFFER,a.positionArray,r.DYNAMIC_DRAW),O.enableAttribute(b.position),r.vertexAttribPointer(b.position,3,r.FLOAT,!1,0,0));if(a.hasNormals){r.bindBuffer(r.ARRAY_BUFFER,d.normal);if("MeshPhongMaterial"!==c.type&&c.shading===THREE.FlatShading)for(var e=0,f=3*a.count;eh;h++)c.__webglFramebuffer[h]=r.createFramebuffer(),c.__webglRenderbuffer[h]=r.createRenderbuffer(),O.texImage2D(r.TEXTURE_CUBE_MAP_POSITIVE_X+h,0,e,a.width, +a.height,0,e,g,null),E(c.__webglFramebuffer[h],a,r.TEXTURE_CUBE_MAP_POSITIVE_X+h),z(c.__webglRenderbuffer[h],a);a.generateMipmaps&&d&&r.generateMipmap(r.TEXTURE_CUBE_MAP)}else c.__webglFramebuffer=r.createFramebuffer(),c.__webglRenderbuffer=a.shareDepthFrom?a.shareDepthFrom.__webglRenderbuffer:r.createRenderbuffer(),O.bindTexture(r.TEXTURE_2D,c.__webglTexture),x(r.TEXTURE_2D,a,d),O.texImage2D(r.TEXTURE_2D,0,e,a.width,a.height,0,e,g,null),E(c.__webglFramebuffer,a,r.TEXTURE_2D),a.shareDepthFrom?a.depthBuffer&& +!a.stencilBuffer?r.framebufferRenderbuffer(r.FRAMEBUFFER,r.DEPTH_ATTACHMENT,r.RENDERBUFFER,c.__webglRenderbuffer):a.depthBuffer&&a.stencilBuffer&&r.framebufferRenderbuffer(r.FRAMEBUFFER,r.DEPTH_STENCIL_ATTACHMENT,r.RENDERBUFFER,c.__webglRenderbuffer):z(c.__webglRenderbuffer,a),a.generateMipmaps&&d&&r.generateMipmap(r.TEXTURE_2D);b?O.bindTexture(r.TEXTURE_CUBE_MAP,null):O.bindTexture(r.TEXTURE_2D,null);r.bindRenderbuffer(r.RENDERBUFFER,null);r.bindFramebuffer(r.FRAMEBUFFER,null)}a?(c=Z.get(a),b=b? +c.__webglFramebuffer[a.activeCubeFace]:c.__webglFramebuffer,c=a.width,a=a.height,e=d=0):(b=null,c=Fa,a=Ga,d=Ea,e=ua);b!==oa&&(r.bindFramebuffer(r.FRAMEBUFFER,b),r.viewport(d,e,c,a),oa=b);Ya=c;Za=a};this.readRenderTargetPixels=function(a,b,c,d,e,f){if(!(a instanceof THREE.WebGLRenderTarget))console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.");else if(Z.get(a).__webglFramebuffer)if(a.format!==THREE.RGBAFormat)console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA format. readPixels can read only RGBA format."); +else{var g=!1;Z.get(a).__webglFramebuffer!==oa&&(r.bindFramebuffer(r.FRAMEBUFFER,Z.get(a).__webglFramebuffer),g=!0);r.checkFramebufferStatus(r.FRAMEBUFFER)===r.FRAMEBUFFER_COMPLETE?r.readPixels(b,c,d,e,r.RGBA,r.UNSIGNED_BYTE,f):console.error("THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.");g&&r.bindFramebuffer(r.FRAMEBUFFER,oa)}};this.supportsFloatTextures=function(){console.warn("THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( 'OES_texture_float' )."); +return X.get("OES_texture_float")};this.supportsHalfFloatTextures=function(){console.warn("THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( 'OES_texture_half_float' ).");return X.get("OES_texture_half_float")};this.supportsStandardDerivatives=function(){console.warn("THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( 'OES_standard_derivatives' ).");return X.get("OES_standard_derivatives")};this.supportsCompressedTextureS3TC=function(){console.warn("THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( 'WEBGL_compressed_texture_s3tc' )."); +return X.get("WEBGL_compressed_texture_s3tc")};this.supportsCompressedTexturePVRTC=function(){console.warn("THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( 'WEBGL_compressed_texture_pvrtc' ).");return X.get("WEBGL_compressed_texture_pvrtc")};this.supportsBlendMinMax=function(){console.warn("THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( 'EXT_blend_minmax' ).");return X.get("EXT_blend_minmax")};this.supportsVertexTextures=function(){return ma.vertexTextures}; +this.supportsInstancedArrays=function(){console.warn("THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( 'ANGLE_instanced_arrays' ).");return X.get("ANGLE_instanced_arrays")};this.initMaterial=function(){console.warn("THREE.WebGLRenderer: .initMaterial() has been removed.")};this.addPrePlugin=function(){console.warn("THREE.WebGLRenderer: .addPrePlugin() has been removed.")};this.addPostPlugin=function(){console.warn("THREE.WebGLRenderer: .addPostPlugin() has been removed.")}; +this.updateShadowMap=function(){console.warn("THREE.WebGLRenderer: .updateShadowMap() has been removed.")};Object.defineProperties(this,{shadowMapEnabled:{get:function(){return ja.enabled},set:function(a){console.warn("THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled.");ja.enabled=a}},shadowMapType:{get:function(){return ja.type},set:function(a){console.warn("THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type.");ja.type=a}},shadowMapCullFace:{get:function(){return ja.cullFace}, +set:function(a){console.warn("THREE.WebGLRenderer: .shadowMapCullFace is now .shadowMap.cullFace.");ja.cullFace=a}},shadowMapDebug:{get:function(){return ja.debug},set:function(a){console.warn("THREE.WebGLRenderer: .shadowMapDebug is now .shadowMap.debug.");ja.debug=a}}})}; THREE.WebGLRenderTarget=function(a,b,c){this.uuid=THREE.Math.generateUUID();this.width=a;this.height=b;c=c||{};this.wrapS=void 0!==c.wrapS?c.wrapS:THREE.ClampToEdgeWrapping;this.wrapT=void 0!==c.wrapT?c.wrapT:THREE.ClampToEdgeWrapping;this.magFilter=void 0!==c.magFilter?c.magFilter:THREE.LinearFilter;this.minFilter=void 0!==c.minFilter?c.minFilter:THREE.LinearMipMapLinearFilter;this.anisotropy=void 0!==c.anisotropy?c.anisotropy:1;this.offset=new THREE.Vector2(0,0);this.repeat=new THREE.Vector2(1, 1);this.format=void 0!==c.format?c.format:THREE.RGBAFormat;this.type=void 0!==c.type?c.type:THREE.UnsignedByteType;this.depthBuffer=void 0!==c.depthBuffer?c.depthBuffer:!0;this.stencilBuffer=void 0!==c.stencilBuffer?c.stencilBuffer:!0;this.generateMipmaps=!0;this.shareDepthFrom=void 0!==c.shareDepthFrom?c.shareDepthFrom:null}; THREE.WebGLRenderTarget.prototype={constructor:THREE.WebGLRenderTarget,setSize:function(a,b){if(this.width!==a||this.height!==b)this.width=a,this.height=b,this.dispose()},clone:function(){return(new this.constructor).copy(this)},copy:function(a){this.width=a.width;this.height=a.height;this.wrapS=a.wrapS;this.wrapT=a.wrapT;this.magFilter=a.magFilter;this.minFilter=a.minFilter;this.anisotropy=a.anisotropy;this.offset.copy(a.offset);this.repeat.copy(a.repeat);this.format=a.format;this.type=a.type;this.depthBuffer= @@ -587,79 +628,79 @@ THREE.WebGLGeometries=function(a,b,c){function d(a){a=a.target;var h=g[a.id].att g[b.id])return g[b.id];b.addEventListener("dispose",d);var e;b instanceof THREE.BufferGeometry?e=b:b instanceof THREE.Geometry&&(void 0===b._bufferGeometry&&(b._bufferGeometry=(new THREE.BufferGeometry).setFromObject(a)),e=b._bufferGeometry);g[b.id]=e;c.memory.geometries++;return e}}; THREE.WebGLObjects=function(a,b,c){function d(c,d){var e=c instanceof THREE.InterleavedBufferAttribute?c.data:c,g=b.get(e);void 0===g.__webglBuffer?(g.__webglBuffer=a.createBuffer(),a.bindBuffer(d,g.__webglBuffer),a.bufferData(d,e.array,e.dynamic?a.DYNAMIC_DRAW:a.STATIC_DRAW),g.version=e.version):g.version!==e.version&&(a.bindBuffer(d,g.__webglBuffer),!1===e.dynamic||-1===e.updateRange.count?a.bufferSubData(d,0,e.array):0===e.updateRange.count?console.error("THREE.WebGLObjects.updateBuffer: dynamic THREE.BufferAttribute marked as needsUpdate but updateRange.count is 0, ensure you are using set methods or updating manually."): (a.bufferSubData(d,e.updateRange.offset*e.array.BYTES_PER_ELEMENT,e.array.subarray(e.updateRange.offset,e.updateRange.offset+e.updateRange.count)),e.updateRange.count=0),g.version=e.version)}function e(a,b,c){if(b>c){var d=b;b=c;c=d}d=a[b];return void 0===d?(a[b]=[c],!0):-1===d.indexOf(c)?(d.push(c),!0):!1}var g=new THREE.WebGLGeometries(a,b,c);this.getAttributeBuffer=function(a){return a instanceof THREE.InterleavedBufferAttribute?b.get(a.data).__webglBuffer:b.get(a).__webglBuffer};this.getWireframeAttribute= -function(c){var g=b.get(c);if(void 0!==g.wireframe)return g.wireframe;var k=[],l=c.index,n=c.attributes;c=n.position;if(null!==l)for(var n={},l=l.array,p=0,m=l.length;p 0 ) {\nfloat depth = gl_FragCoord.z / gl_FragCoord.w;\nfloat fogFactor = 0.0;\nif ( fogType == 1 ) {\nfogFactor = smoothstep( fogNear, fogFar, depth );\n} else {\nconst float LOG2 = 1.442695;\nfogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );\nfogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );\n}\ngl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );\n}\n}"].join("\n")); -x.compileShader(K);x.compileShader(y);x.attachShader(G,K);x.attachShader(G,y);x.linkProgram(G);B=G;w=x.getAttribLocation(B,"position");v=x.getAttribLocation(B,"uv");c=x.getUniformLocation(B,"uvOffset");d=x.getUniformLocation(B,"uvScale");e=x.getUniformLocation(B,"rotation");g=x.getUniformLocation(B,"scale");f=x.getUniformLocation(B,"color");h=x.getUniformLocation(B,"map");k=x.getUniformLocation(B,"opacity");l=x.getUniformLocation(B,"modelViewMatrix");n=x.getUniformLocation(B,"projectionMatrix");p= -x.getUniformLocation(B,"fogType");m=x.getUniformLocation(B,"fogDensity");q=x.getUniformLocation(B,"fogNear");t=x.getUniformLocation(B,"fogFar");r=x.getUniformLocation(B,"fogColor");u=x.getUniformLocation(B,"alphaTest");G=document.createElement("canvas");G.width=8;G.height=8;K=G.getContext("2d");K.fillStyle="white";K.fillRect(0,0,8,8);D=new THREE.Texture(G);D.needsUpdate=!0}x.useProgram(B);H.initAttributes();H.enableAttribute(w);H.enableAttribute(v);H.disableUnusedAttributes();H.disable(x.CULL_FACE); -H.enable(x.BLEND);x.bindBuffer(x.ARRAY_BUFFER,F);x.vertexAttribPointer(w,2,x.FLOAT,!1,16,0);x.vertexAttribPointer(v,2,x.FLOAT,!1,16,8);x.bindBuffer(x.ELEMENT_ARRAY_BUFFER,z);x.uniformMatrix4fv(n,!1,C.projectionMatrix.elements);H.activeTexture(x.TEXTURE0);x.uniform1i(h,0);K=G=0;(y=M.fog)?(x.uniform3f(r,y.color.r,y.color.g,y.color.b),y instanceof THREE.Fog?(x.uniform1f(q,y.near),x.uniform1f(t,y.far),x.uniform1i(p,1),K=G=1):y instanceof THREE.FogExp2&&(x.uniform1f(m,y.density),x.uniform1i(p,2),K=G=2)): -(x.uniform1i(p,0),K=G=0);for(var y=0,J=b.length;y 0 ) {\nfloat depth = gl_FragCoord.z / gl_FragCoord.w;\nfloat fogFactor = 0.0;\nif ( fogType == 1 ) {\nfogFactor = smoothstep( fogNear, fogFar, depth );\n} else {\nconst float LOG2 = 1.442695;\nfogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );\nfogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );\n}\ngl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );\n}\n}"].join("\n")); +x.compileShader(M);x.compileShader(F);x.attachShader(G,M);x.attachShader(G,F);x.linkProgram(G);A=G;w=x.getAttribLocation(A,"position");u=x.getAttribLocation(A,"uv");c=x.getUniformLocation(A,"uvOffset");d=x.getUniformLocation(A,"uvScale");e=x.getUniformLocation(A,"rotation");g=x.getUniformLocation(A,"scale");f=x.getUniformLocation(A,"color");h=x.getUniformLocation(A,"map");k=x.getUniformLocation(A,"opacity");l=x.getUniformLocation(A,"modelViewMatrix");n=x.getUniformLocation(A,"projectionMatrix");p= +x.getUniformLocation(A,"fogType");m=x.getUniformLocation(A,"fogDensity");q=x.getUniformLocation(A,"fogNear");s=x.getUniformLocation(A,"fogFar");t=x.getUniformLocation(A,"fogColor");v=x.getUniformLocation(A,"alphaTest");G=document.createElement("canvas");G.width=8;G.height=8;M=G.getContext("2d");M.fillStyle="white";M.fillRect(0,0,8,8);I=new THREE.Texture(G);I.needsUpdate=!0}x.useProgram(A);H.initAttributes();H.enableAttribute(w);H.enableAttribute(u);H.disableUnusedAttributes();H.disable(x.CULL_FACE); +H.enable(x.BLEND);x.bindBuffer(x.ARRAY_BUFFER,E);x.vertexAttribPointer(w,2,x.FLOAT,!1,16,0);x.vertexAttribPointer(u,2,x.FLOAT,!1,16,8);x.bindBuffer(x.ELEMENT_ARRAY_BUFFER,z);x.uniformMatrix4fv(n,!1,K.projectionMatrix.elements);H.activeTexture(x.TEXTURE0);x.uniform1i(h,0);M=G=0;(F=N.fog)?(x.uniform3f(t,F.color.r,F.color.g,F.color.b),F instanceof THREE.Fog?(x.uniform1f(q,F.near),x.uniform1f(s,F.far),x.uniform1i(p,1),M=G=1):F instanceof THREE.FogExp2&&(x.uniform1f(m,F.density),x.uniform1i(p,2),M=G=2)): +(x.uniform1i(p,0),M=G=0);for(var F=0,Q=b.length;Fp-1?0:p-1,q=p+1>e-1?e-1:p+1,t=0>n-1?0:n-1,r=n+1>d-1?d-1:n+1,u=[],w=[0,0,h[4*(p*d+n)]/255*b];u.push([-1,0,h[4*(p*d+t)]/255*b]);u.push([-1,-1,h[4*(m*d+t)]/255*b]);u.push([0,-1,h[4*(m*d+n)]/255*b]);u.push([1,-1,h[4*(m*d+r)]/255*b]);u.push([1,0,h[4*(p*d+r)]/255*b]);u.push([1,1,h[4*(q*d+r)]/255*b]);u.push([0,1,h[4*(q*d+n)]/255* -b]);u.push([-1,1,h[4*(q*d+t)]/255*b]);m=[];t=u.length;for(q=0;qp-1?0:p-1,q=p+1>e-1?e-1:p+1,s=0>n-1?0:n-1,t=n+1>d-1?d-1:n+1,v=[],w=[0,0,h[4*(p*d+n)]/255*b];v.push([-1,0,h[4*(p*d+s)]/255*b]);v.push([-1,-1,h[4*(m*d+s)]/255*b]);v.push([0,-1,h[4*(m*d+n)]/255*b]);v.push([1,-1,h[4*(m*d+t)]/255*b]);v.push([1,0,h[4*(p*d+t)]/255*b]);v.push([1,1,h[4*(q*d+t)]/255*b]);v.push([0,1,h[4*(q*d+n)]/255* +b]);v.push([-1,1,h[4*(q*d+s)]/255*b]);m=[];s=v.length;for(q=0;qe)return null;var g=[],f=[],h=[],k,l,n;if(0=p--){console.warn("THREE.FontUtils: Warning, unable to triangulate polygon! in Triangulate.process()");break}k=l;e<=k&&(k=0);l=k+1;e<=l&&(l=0);n=l+1;e<=n&&(n=0);var m;a:{var q=m=void 0,t=void 0,r=void 0, -u=void 0,w=void 0,v=void 0,A=void 0,x=void 0,q=a[f[k]].x,t=a[f[k]].y,r=a[f[l]].x,u=a[f[l]].y,w=a[f[n]].x,v=a[f[n]].y;if(1E-10>(r-q)*(v-t)-(u-t)*(w-q))m=!1;else{var H=void 0,F=void 0,z=void 0,B=void 0,D=void 0,N=void 0,E=void 0,L=void 0,M=void 0,C=void 0,M=L=E=x=A=void 0,H=w-r,F=v-u,z=q-w,B=t-v,D=r-q,N=u-t;for(m=0;me)return null;var g=[],f=[],h=[],k,l,n;if(0=p--){console.warn("THREE.FontUtils: Warning, unable to triangulate polygon! in Triangulate.process()");break}k=l;e<=k&&(k=0);l=k+1;e<=l&&(l=0);n=l+1;e<=n&&(n=0);var m;a:{var q=m=void 0,s=void 0,t=void 0, +v=void 0,w=void 0,u=void 0,y=void 0,x=void 0,q=a[f[k]].x,s=a[f[k]].y,t=a[f[l]].x,v=a[f[l]].y,w=a[f[n]].x,u=a[f[n]].y;if(1E-10>(t-q)*(u-s)-(v-s)*(w-q))m=!1;else{var H=void 0,E=void 0,z=void 0,A=void 0,I=void 0,L=void 0,D=void 0,C=void 0,N=void 0,K=void 0,N=C=D=x=y=void 0,H=w-t,E=u-v,z=q-w,A=s-u,I=t-q,L=v-s;for(m=0;mMath.abs(d.x-c[0].x)&&1E-10>Math.abs(d.y-c[0].y)&&c.splice(c.length-1,1);b&&c.push(c[0]);return c}; -THREE.Path.prototype.toShapes=function(a,b){function c(a){for(var b=[],c=0,d=a.length;cm&&(g=b[f],k=-k,h=b[e],m=-m),!(a.yh.y))if(a.y===g.y){if(a.x===g.x)return!0}else{e=m*(a.x-g.x)-k*(a.y-g.y);if(0===e)return!0;0>e||(d=!d)}}else if(a.y===g.y&&(h.x<=a.x&&a.x<=g.x||g.x<=a.x&& +THREE.Path.prototype.getPoints=function(a,b){if(this.useSpacedPoints)return this.getSpacedPoints(a,b);a=a||12;var c=[],d,e,g,f,h,k,l,n,p,m,q,s,t;d=0;for(e=this.actions.length;dMath.abs(d.x-c[0].x)&&1E-10>Math.abs(d.y-c[0].y)&&c.splice(c.length-1,1);b&&c.push(c[0]);return c}; +THREE.Path.prototype.toShapes=function(a,b){function c(a){for(var b=[],c=0,d=a.length;cl&&(g=b[f],k=-k,h=b[e],l=-l),!(a.yh.y))if(a.y===g.y){if(a.x===g.x)return!0}else{e=l*(a.x-g.x)-k*(a.y-g.y);if(0===e)return!0;0>e||(d=!d)}}else if(a.y===g.y&&(h.x<=a.x&&a.x<=g.x||g.x<=a.x&& a.x<=h.x))return!0}return d}var e=function(a){var b,c,d,e,f=[],g=new THREE.Path;b=0;for(c=a.length;bB||B>z)return[];k=l*n-k*p;if(0>k||k>z)return[]}else{if(0d?[]: +THREE.Shape.Utils={triangulateShape:function(a,b){function c(a,b,c){return a.x!==b.x?a.xA||A>z)return[];k=l*n-k*p;if(0>k||k>z)return[]}else{if(0d?[]: k===d?f?[]:[g]:a<=d?[g,h]:[g,l]}function e(a,b,c,d){var e=b.x-a.x,f=b.y-a.y;b=c.x-a.x;c=c.y-a.y;var g=d.x-a.x;d=d.y-a.y;a=e*c-f*b;e=e*d-f*g;return 1E-10f&&(f=d);var g=a+1;g>d&&(g= -0);d=e(h[a],h[f],h[g],k[b]);if(!d)return!1;d=k.length-1;f=b-1;0>f&&(f=d);g=b+1;g>d&&(g=0);return(d=e(k[b],k[f],k[g],h[a]))?!0:!1}function f(a,b){var c,e;for(c=0;cC){console.log("Infinite Loop! Holes left:"+l.length+", Probably Hole outside Shape!");break}for(p=N;pf&&(f=d);g=b+1;g>d&&(g=0);return(d=e(k[b],k[f],k[g],h[a]))?!0:!1}function f(a,b){var c,e;for(c=0;cK){console.log("Infinite Loop! Holes left:"+l.length+", Probably Hole outside Shape!");break}for(p=L;ph;h++)l=k[h].x+":"+k[h].y,l=n[l],void 0!==l&&(k[h]=l);return p.concat()},isClockWise:function(a){return 0>THREE.FontUtils.Triangulate.area(a)},b2p0:function(a,b){var c=1-a;return c*c*b},b2p1:function(a,b){return 2*(1-a)*a*b},b2p2:function(a,b){return a*a*b},b2:function(a,b,c,d){return this.b2p0(a,b)+this.b2p1(a,c)+this.b2p2(a,d)},b3p0:function(a,b){var c=1-a;return c*c*c*b},b3p1:function(a,b){var c=1-a;return 3*c*c*a*b},b3p2:function(a,b){return 3*(1- a)*a*a*b},b3p3:function(a,b){return a*a*a*b},b3:function(a,b,c,d,e){return this.b3p0(a,b)+this.b3p1(a,c)+this.b3p2(a,d)+this.b3p3(a,e)}};THREE.LineCurve=function(a,b){this.v1=a;this.v2=b};THREE.LineCurve.prototype=Object.create(THREE.Curve.prototype);THREE.LineCurve.prototype.constructor=THREE.LineCurve;THREE.LineCurve.prototype.getPoint=function(a){var b=this.v2.clone().sub(this.v1);b.multiplyScalar(a).add(this.v1);return b};THREE.LineCurve.prototype.getPointAt=function(a){return this.getPoint(a)}; THREE.LineCurve.prototype.getTangent=function(a){return this.v2.clone().sub(this.v1).normalize()};THREE.QuadraticBezierCurve=function(a,b,c){this.v0=a;this.v1=b;this.v2=c};THREE.QuadraticBezierCurve.prototype=Object.create(THREE.Curve.prototype);THREE.QuadraticBezierCurve.prototype.constructor=THREE.QuadraticBezierCurve; @@ -722,32 +763,9 @@ THREE.CatmullRomCurve3=function(){function a(){}var b=new THREE.Vector3,c=new a, b*a};return THREE.Curve.create(function(a){this.points=a||[]},function(a){var f=this.points,h,k;k=f.length;2>k&&console.log("duh, you need at least 2 points");a*=k-1;h=Math.floor(a);a-=h;0===a&&h===k-1&&(h=k-2,a=1);var l,n,p;0===h?(b.subVectors(f[0],f[1]).add(f[0]),l=b):l=f[h-1];n=f[h];p=f[h+1];h+2h&&(h=1);1E-4>k&&(k=h);1E-4>m&&(m=h);c.initNonuniformCatmullRom(l.x,n.x,p.x,f.x,k,h,m);d.initNonuniformCatmullRom(l.y,n.y,p.y,f.y,k,h,m);e.initNonuniformCatmullRom(l.z,n.z,p.z,f.z,k,h,m)}else"catmullrom"===this.type&&(k=void 0!==this.tension?this.tension:.5,c.initCatmullRom(l.x,n.x,p.x,f.x,k),d.initCatmullRom(l.y,n.y,p.y,f.y,k),e.initCatmullRom(l.z,n.z,p.z,f.z,k));return new THREE.Vector3(c.calc(a),d.calc(a),e.calc(a))})}(); THREE.ClosedSplineCurve3=THREE.Curve.create(function(a){this.points=void 0==a?[]:a},function(a){var b=this.points;a*=b.length-0;var c=Math.floor(a);a-=c;var c=c+(0a.hierarchy[b].keys[c].time&&(a.hierarchy[b].keys[c].time= -0),void 0!==a.hierarchy[b].keys[c].rot&&!(a.hierarchy[b].keys[c].rot instanceof THREE.Quaternion)){var d=a.hierarchy[b].keys[c].rot;a.hierarchy[b].keys[c].rot=(new THREE.Quaternion).fromArray(d)}if(a.hierarchy[b].keys.length&&void 0!==a.hierarchy[b].keys[0].morphTargets){d={};for(c=0;cc;c++){for(var d=this.keyTypes[c],e=this.data.hierarchy[a].keys[0],f=this.getNextKeyWith(d,a,1);f.timee.index;)e=f,f=this.getNextKeyWith(d,a,f.index+1);g.prevKey[d]=e;g.nextKey[d]=f}}},resetBlendWeights:function(){for(var a=0,b=this.hierarchy.length;aa.length-2?p:p+1;c[3]=p>a.length-3?p:p+2;p=a[c[0]];q=a[c[1]];t=a[c[2]];r=a[c[3]];c=e*e;m=e*c;d[0]=g(p[0],q[0],t[0],r[0],e,c,m);d[1]=g(p[1],q[1],t[1],r[1],e,c,m);d[2]=g(p[2],q[2],t[2],r[2],e,c,m);return d},g=function(a,b,c,d, -e,g,m){a=.5*(c-a);d=.5*(d-b);return(2*(b-c)+a+d)*m+(-3*(b-c)-2*a-d)*g+a*e+b};return function(f){if(!1!==this.isPlaying&&(this.currentTime+=f*this.timeScale,0!==this.weight)){f=this.data.length;if(this.currentTime>f||0>this.currentTime)this.loop?(this.currentTime%=f,0>this.currentTime&&(this.currentTime+=f),this.reset()):this.stop();f=0;for(var g=this.hierarchy.length;fp;p++){var m=this.keyTypes[p], -q=l.prevKey[m],t=l.nextKey[m];if(0this.timeScale&&q.time>=this.currentTime){q=this.data.hierarchy[f].keys[0];for(t=this.getNextKeyWith(m,f,1);t.timeq.index;)q=t,t=this.getNextKeyWith(m,f,t.index+1);l.prevKey[m]=q;l.nextKey[m]=t}var r=(this.currentTime-q.time)/(t.time-q.time),u=q[m],w=t[m];0>r&&(r=0);1a&&(this.currentTime%=a);this.currentTime=Math.min(this.currentTime,a);a=0;for(var b=this.hierarchy.length;ag.index;)g=f,f=e[g.index+1];d.prevKey=g;d.nextKey=f}f.time>=this.currentTime?g.interpolate(f,this.currentTime): -g.interpolate(f,f.time);this.data.hierarchy[a].node.updateMatrix();c.matrixWorldNeedsUpdate=!0}}}},getNextKeyWith:function(a,b,c){b=this.data.hierarchy[b].keys;for(c%=b.length;cthis.duration&&(this.currentTime%=this.duration);this.currentTime=Math.min(this.currentTime,this.duration);var b=this.duration/this.frames;a=Math.floor(this.currentTime/b);var c=this.mesh.morphTargetInfluences;a!==this.currentFrame&&(c[this.lastFrame]=0,c[this.currentFrame]= -1,c[a]=0,this.lastFrame=this.currentFrame,this.currentFrame=a);b=this.currentTime%b/b;c[a]=b;c[this.lastFrame]=1-b}}}; -THREE.BoxGeometry=function(a,b,c,d,e,g){function f(a,b,c,d,e,f,g,r){var u,w=h.widthSegments,v=h.heightSegments,A=e/2,x=f/2,H=h.vertices.length;if("x"===a&&"y"===b||"y"===a&&"x"===b)u="z";else if("x"===a&&"z"===b||"z"===a&&"x"===b)u="y",v=h.depthSegments;else if("z"===a&&"y"===b||"y"===a&&"z"===b)u="x",w=h.depthSegments;var F=w+1,z=v+1,B=e/w,D=f/v,N=new THREE.Vector3;N[u]=0m;m++){d[0]=p[f[m]];d[1]=p[f[(m+1)%3]];d.sort(g);var q=d.toString();void 0===e[q]?e[q]={vert1:d[0],vert2:d[1],face1:l, face2:void 0}:e[q].face2=l}d=[];for(q in e)if(g=e[q],void 0===g.face2||h[g.face1].normal.dot(h[g.face2].normal)<=c)f=k[g.vert1],d.push(f.x),d.push(f.y),d.push(f.z),f=k[g.vert2],d.push(f.x),d.push(f.y),d.push(f.z);this.addAttribute("position",new THREE.BufferAttribute(new Float32Array(d),3))};THREE.EdgesGeometry.prototype=Object.create(THREE.BufferGeometry.prototype);THREE.EdgesGeometry.prototype.constructor=THREE.EdgesGeometry; THREE.ExtrudeGeometry=function(a,b){"undefined"!==typeof a&&(THREE.Geometry.call(this),this.type="ExtrudeGeometry",a=Array.isArray(a)?a:[a],this.addShapeList(a,b),this.computeFaceNormals())};THREE.ExtrudeGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.ExtrudeGeometry.prototype.constructor=THREE.ExtrudeGeometry;THREE.ExtrudeGeometry.prototype.addShapeList=function(a,b){for(var c=a.length,d=0;d=d)return new THREE.Vector2(c,a);d=Math.sqrt(d/2)}else a=!1,1E-10d?-1E-10>f&&(a=!0):Math.sign(e)===Math.sign(g)&&(a=!0),a?(c=-e,a=d,d=Math.sqrt(h)):(c=d,a=e,d=Math.sqrt(h/2));return new THREE.Vector2(c/d,a/d)}function e(a,b){var c,d;for(I=a.length;0<=--I;){c=I;d=I-1;0>d&&(d=a.length-1);for(var e=0,f=q+2*n,e=0;ed?-1E-10>f&&(a=!0):Math.sign(e)===Math.sign(g)&&(a=!0),a?(c=-e,a=d,d=Math.sqrt(h)):(c=d,a=e,d=Math.sqrt(h/2));return new THREE.Vector2(c/d,a/d)}function e(a,b){var c,d;for(B=a.length;0<=--B;){c=B;d=B-1;0>d&&(d=a.length-1);for(var e=0,f=q+2*n,e=0;eMath.abs(b.y-c.y)?[new THREE.Vector2(b.x,1-b.z),new THREE.Vector2(c.x,1-c.z),new THREE.Vector2(d.x,1-d.z),new THREE.Vector2(e.x,1-e.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),new THREE.Vector2(e.y,1-e.z)]}};THREE.ShapeGeometry=function(a,b){THREE.Geometry.call(this);this.type="ShapeGeometry";!1===Array.isArray(a)&&(a=[a]);this.addShapeList(a,b);this.computeFaceNormals()};THREE.ShapeGeometry.prototype=Object.create(THREE.Geometry.prototype);THREE.ShapeGeometry.prototype.constructor=THREE.ShapeGeometry;THREE.ShapeGeometry.prototype.addShapeList=function(a,b){for(var c=0,d=a.length;cc&&1===a.x&&(a=new THREE.Vector2(a.x-1,a.y));0===b.x&&0===b.z&&(a=new THREE.Vector2(c/ -2/Math.PI+.5,a.y));return a.clone()}THREE.Geometry.call(this);this.type="PolyhedronGeometry";this.parameters={vertices:a,indices:b,radius:c,detail:d};c=c||1;d=d||0;for(var k=this,l=0,n=a.length;lc&&1===a.x&&(a=new THREE.Vector2(a.x-1,a.y));0===b.x&&0===b.z&&(a=new THREE.Vector2(c/ +2/Math.PI+.5,a.y));return a.clone()}THREE.Geometry.call(this);this.type="PolyhedronGeometry";this.parameters={vertices:a,indices:b,radius:c,detail:d};c=c||1;d=d||0;for(var k=this,l=0,n=a.length;lq&&(.2>d&&(b[0].x+=1),.2>a&&(b[1].x+=1),.2>p&&(b[2].x+=1));l=0;for(n=this.vertices.length;lp;p++){b[0]=n[e[p]];b[1]=n[e[(p+1)%3]];b.sort(d);var m=b.toString();void 0===c[m]&&(k[2*h]=b[0],k[2*h+1]=b[1],c[m]=!0,h++)}b=new Float32Array(6*h);a=0;for(l=h;ap;p++)c=g[k[2*a+p]],h=6*a+3*p,b[h+0]=c.x,b[h+1]=c.y, -b[h+2]=c.z;this.addAttribute("position",new THREE.BufferAttribute(b,3))}else if(a instanceof THREE.BufferGeometry){if(null!==a.index){l=a.index.array;g=a.attributes.position;e=a.drawcalls;h=0;0===e.length&&a.addDrawCall(0,l.length);k=new Uint32Array(2*l.length);f=0;for(n=e.length;fp;p++)b[0]=l[a+p],b[1]=l[a+(p+1)%3],b.sort(d),m=b.toString(),void 0===c[m]&&(k[2*h]=b[0],k[2*h+1]=b[1],c[m]=!0,h++)}b=new Float32Array(6*h);a=0;for(l= +b[h+2]=c.z;this.addAttribute("position",new THREE.BufferAttribute(b,3))}else if(a instanceof THREE.BufferGeometry){if(null!==a.index){l=a.index.array;g=a.attributes.position;e=a.drawcalls;h=0;0===e.length&&a.addGroup(0,l.length);k=new Uint32Array(2*l.length);f=0;for(n=e.length;fp;p++)b[0]=l[a+p],b[1]=l[a+(p+1)%3],b.sort(d),m=b.toString(),void 0===c[m]&&(k[2*h]=b[0],k[2*h+1]=b[1],c[m]=!0,h++)}b=new Float32Array(6*h);a=0;for(l= h;ap;p++)h=6*a+3*p,c=k[2*a+p],b[h+0]=g.getX(c),b[h+1]=g.getY(c),b[h+2]=g.getZ(c)}else for(g=a.attributes.position.array,h=g.length/3,k=h/3,b=new Float32Array(6*h),a=0,l=k;ap;p++)h=18*a+6*p,k=9*a+3*p,b[h+0]=g[k],b[h+1]=g[k+1],b[h+2]=g[k+2],c=9*a+(p+1)%3*3,b[h+3]=g[c],b[h+4]=g[c+1],b[h+5]=g[c+2];this.addAttribute("position",new THREE.BufferAttribute(b,3))}};THREE.WireframeGeometry.prototype=Object.create(THREE.BufferGeometry.prototype); THREE.WireframeGeometry.prototype.constructor=THREE.WireframeGeometry;THREE.AxisHelper=function(a){a=a||1;var b=new Float32Array([0,0,0,a,0,0,0,0,0,0,a,0,0,0,0,0,0,a]),c=new Float32Array([1,0,0,1,.6,0,0,1,0,.6,1,0,0,0,1,0,.6,1]);a=new THREE.BufferGeometry;a.addAttribute("position",new THREE.BufferAttribute(b,3));a.addAttribute("color",new THREE.BufferAttribute(c,3));b=new THREE.LineBasicMaterial({vertexColors:THREE.VertexColors});THREE.LineSegments.call(this,a,b)};THREE.AxisHelper.prototype=Object.create(THREE.LineSegments.prototype); THREE.AxisHelper.prototype.constructor=THREE.AxisHelper; @@ -857,8 +875,8 @@ THREE.SpotLightHelper=function(a){THREE.Object3D.call(this);this.light=a;this.li THREE.SpotLightHelper.prototype.dispose=function(){this.cone.geometry.dispose();this.cone.material.dispose()};THREE.SpotLightHelper.prototype.update=function(){var a=new THREE.Vector3,b=new THREE.Vector3;return function(){var c=this.light.distance?this.light.distance:1E4,d=c*Math.tan(this.light.angle);this.cone.scale.set(d,d,c);a.setFromMatrixPosition(this.light.matrixWorld);b.setFromMatrixPosition(this.light.target.matrixWorld);this.cone.lookAt(b.sub(a));this.cone.material.color.copy(this.light.color).multiplyScalar(this.light.intensity)}}(); THREE.VertexNormalsHelper=function(a,b,c,d){this.object=a;this.size=void 0!==b?b:1;a=void 0!==c?c:16711680;d=void 0!==d?d:1;b=0;c=this.object.geometry;c instanceof THREE.Geometry?b=3*c.faces.length:c instanceof THREE.BufferGeometry&&(b=c.attributes.normal.count);c=new THREE.BufferGeometry;b=new THREE.Float32Attribute(6*b,3);c.addAttribute("position",b);THREE.LineSegments.call(this,c,new THREE.LineBasicMaterial({color:a,linewidth:d}));this.matrixAutoUpdate=!1;this.update()}; THREE.VertexNormalsHelper.prototype=Object.create(THREE.LineSegments.prototype);THREE.VertexNormalsHelper.prototype.constructor=THREE.VertexNormalsHelper; -THREE.VertexNormalsHelper.prototype.update=function(){var a=new THREE.Vector3,b=new THREE.Vector3,c=new THREE.Matrix3;return function(){var d=["a","b","c"];this.object.updateMatrixWorld(!0);c.getNormalMatrix(this.object.matrixWorld);var e=this.object.matrixWorld,g=this.geometry.attributes.position,f=this.object.geometry;if(f instanceof THREE.Geometry)for(var h=f.vertices,k=f.faces,l=f=0,n=k.length;l