diff --git a/Chapter2/2.4.1.html b/Chapter2/2.4.1.html index c9d7328..7282e91 100644 --- a/Chapter2/2.4.1.html +++ b/Chapter2/2.4.1.html @@ -13,7 +13,7 @@ // camera // canvas size is 400x300 - var camera = new THREE.PerspectiveCamera(45, 400 / 300, 1, 10); + var camera = new THREE.PerspectiveCamera(60, 400 / 300, 1, 10); camera.position.set(0, 0, 5); scene.add(camera); diff --git a/Chapter3/3.1.1.html b/Chapter3/3.1.1.html index b3e9223..71d5d4e 100644 --- a/Chapter3/3.1.1.html +++ b/Chapter3/3.1.1.html @@ -26,15 +26,15 @@ }); drawCube(scene, material); - //drawPlane(scene, material); - //drawSphere(scene, material); - //drawCircle(scene, material); - //drawCylinder(scene, material); - //drawTetra(scene, material); - //drawOcta(scene, material); - //drawIcosa(scene, material); - //drawTorus(scene, material); - //drawTorusKnot(scene, material); + // drawPlane(scene, material); + // drawSphere(scene, material); + // drawCircle(scene, material); + // drawCylinder(scene, material); + // drawTetra(scene, material); + // drawOcta(scene, material); + // drawIcosa(scene, material); + // drawTorus(scene, material); + // drawTorusKnot(scene, material); // render renderer.render(scene, camera); @@ -51,12 +51,17 @@ } function drawSphere(scene, material) { - var sphere = new THREE.Mesh(new THREE.SphereGeometry(3, 8, 6, - Math.PI / 6, Math.PI / 3), material); - //var sphere = new THREE.Mesh(new THREE.SphereGeometry(3, 8, 6, - // 0, Math.PI * 2, Math.PI / 6, Math.PI / 2), material); - //var sphere = new THREE.Mesh(new THREE.SphereGeometry(3, 8, 6, - // Math.PI / 2, Math.PI, Math.PI / 6, Math.PI / 2), material); + var sphere = new THREE.Mesh(new THREE.SphereGeometry(3, 8, 6), material); + // var sphere = new THREE.Mesh(new THREE.SphereGeometry(3, 8, 6, + // 0, Math.PI * 2, Math.PI / 6, Math.PI / 2), material); + // var sphere = new THREE.Mesh(new THREE.SphereGeometry(3, 8, 6, + // Math.PI / 2, Math.PI, Math.PI / 6, Math.PI / 2), material); + // var sphere = new THREE.Mesh(new THREE.SphereGeometry(3, 8, 6, + // Math.PI / 6, Math.PI / 3), material); + // var sphere = new THREE.Mesh(new THREE.SphereGeometry(3, 8, 6, + // 0, Math.PI * 2, Math.PI / 6, Math.PI / 3), material); + // var sphere = new THREE.Mesh(new THREE.SphereGeometry(3, 8, 6, + // Math.PI / 2, Math.PI, Math.PI / 6, Math.PI / 2), material); scene.add(sphere); } @@ -67,8 +72,8 @@ function drawCylinder(scene, material) { var cylinder = new THREE.Mesh(new THREE.CylinderGeometry(2, 2, 4, 18, 3), material); - //var cylinder = new THREE.Mesh(new THREE.CylinderGeometry(2, 3, 4, 18, 3), material); - //var cylinder = new THREE.Mesh(new THREE.CylinderGeometry(2, 3, 4, 18, 3, true), material); + // var cylinder = new THREE.Mesh(new THREE.CylinderGeometry(2, 3, 4, 18, 3), material); + // var cylinder = new THREE.Mesh(new THREE.CylinderGeometry(2, 3, 4, 18, 3, true), material); scene.add(cylinder); } @@ -88,7 +93,9 @@ } function drawTorus(scene, material) { - var torus = new THREE.Mesh(new THREE.TorusGeometry(3, 1, 4, 8, Math.PI / 3 * 2), material); + var torus = new THREE.Mesh(new THREE.TorusGeometry(3, 1, 4, 8), material); + // var torus = new THREE.Mesh(new THREE.TorusGeometry(3, 1, 12, 18), material); + // var torus = new THREE.Mesh(new THREE.TorusGeometry(3, 1, 4, 8, Math.PI / 3 * 2), material); scene.add(torus); } @@ -106,7 +113,7 @@ // x-axis var xGeo = new THREE.Geometry(); xGeo.vertices.push(new THREE.Vector3(0, 0, 0)); - xGeo.vertices.push(new THREE.Vector3(1, 0, 0)); + xGeo.vertices.push(new THREE.Vector3(3, 0, 0)); var xMat = new THREE.LineBasicMaterial({ color: 0xff0000 }); @@ -116,7 +123,7 @@ // y-axis var yGeo = new THREE.Geometry(); yGeo.vertices.push(new THREE.Vector3(0, 0, 0)); - yGeo.vertices.push(new THREE.Vector3(0, 1, 0)); + yGeo.vertices.push(new THREE.Vector3(0, 3, 0)); var yMat = new THREE.LineBasicMaterial({ color: 0x00ff00 }); @@ -126,7 +133,7 @@ // z-axis var zGeo = new THREE.Geometry(); zGeo.vertices.push(new THREE.Vector3(0, 0, 0)); - zGeo.vertices.push(new THREE.Vector3(0, 0, 1)); + zGeo.vertices.push(new THREE.Vector3(0, 0, 3)); var zMat = new THREE.LineBasicMaterial({ color: 0x00ccff }); diff --git a/Chapter3/3.2.1.html b/Chapter3/3.2.1.html index 0ccd4fd..2225a7e 100644 --- a/Chapter3/3.2.1.html +++ b/Chapter3/3.2.1.html @@ -2,8 +2,7 @@ - - + - + + + + + + + + + + diff --git a/index.html b/index.html index 6665c30..5ba2d0f 100644 --- a/index.html +++ b/index.html @@ -5,7 +5,7 @@ var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-38205696-1']); _gaq.push(['_trackPageview']); - + (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; @@ -19,7 +19,7 @@ -
+
@@ -31,21 +31,21 @@

《Three.js 入门指南》书例代码

目录

第 1 章 概述

-

阅读原文

+

阅读原文

例 1.1.1

运行代码 | 查看代码

例 1.2.1

运行代码 | 查看代码

第 2 章 照相机

-

阅读原文

+

阅读原文

例 2.3.1

运行代码 | 查看代码

例 2.4.1

运行代码 | 查看代码

第 3 章 几何形状

-

阅读原文

+

阅读原文

例 3.1.1

运行代码 | 查看代码

例 3.2.1

@@ -54,7 +54,7 @@

例 3.3.1

运行代码 | 查看代码

第 4 章 材质

-

阅读原文

+

阅读原文

例 4.1.1

运行代码 | 查看代码

例 4.2.1

@@ -71,7 +71,7 @@

例 4.5.3

运行代码 | 查看代码

第 5 章 网格

-

阅读原文

+

阅读原文

例 5.1.1

运行代码 | 查看代码

例 5.2.1

@@ -80,7 +80,7 @@

例 5.2.2

运行代码 | 查看代码

第 6 章 动画

-

阅读原文

+

阅读原文

例 6.1.1

运行代码 | 查看代码

例 6.1.2

@@ -93,7 +93,7 @@

例 6.3.1

运行代码 | 查看代码

第 7 章 外部模型

-

阅读原文

+

阅读原文

例 7.2.1

运行代码 | 查看代码

例 7.3.1

@@ -102,7 +102,7 @@

例 7.3.2

运行代码 | 查看代码

第 8 章 光与影

-

阅读原文

+

阅读原文

例 8.1.1

运行代码 | 查看代码

例 8.1.2

@@ -119,7 +119,7 @@

例 8.5.1

运行代码 | 查看代码

第 9 章 着色器

-

阅读原文

+

阅读原文

例 9.3.1

运行代码 | 查看代码

例 9.3.2

@@ -158,7 +158,7 @@

关于作者

- + (0,0,1,1,0,0) + optimize: function() { - var inverse = new THREE.Matrix4(); + var times = this.times, + values = this.values, + stride = this.getValueSize(), - inverse.getInverse( this.bones[ b ].skinMatrix ); + writeIndex = 1; - this.boneInverses.push( inverse ); + for( var i = 1, n = times.length - 1; i <= n; ++ i ) { - } + var keep = false; - } + var time = times[ i ]; + var timeNext = times[ i + 1 ]; - // flatten bone matrices to array + // remove adjacent keyframes scheduled at the same time - for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) { - // compute the offset between the current and the original transform; + // remove unnecessary keyframes same as their neighbors + var offset = i * stride, + offsetP = offset - stride, + offsetN = offset + stride; - // TODO: we could get rid of this multiplication step if the skinMatrix - // was already representing the offset; however, this requires some - // major changes to the animation system + for ( var j = 0; j !== stride; ++ j ) { - offsetMatrix.multiplyMatrices( this.bones[ b ].skinMatrix, this.boneInverses[ b ] ); - offsetMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 ); + var value = values[ offset + j ]; - } + if ( value !== values[ offsetP + j ] || + value !== values[ offsetN + j ] ) { - if ( this.useVertexTexture ) { + keep = true; + break; - this.boneTexture.needsUpdate = true; + } - } + } - }; + } -}(); + // in-place compaction -THREE.SkinnedMesh.prototype.pose = function () { + if ( keep ) { - this.updateMatrixWorld( true ); + if ( i !== writeIndex ) { - this.normalizeSkinWeights(); + times[ writeIndex ] = times[ i ]; -}; + var readOffset = i * stride, + writeOffset = writeIndex * stride; -THREE.SkinnedMesh.prototype.normalizeSkinWeights = function () { + for ( var j = 0; j !== stride; ++ j ) { - if ( this.geometry instanceof THREE.Geometry ) { + values[ writeOffset + j ] = values[ readOffset + j ]; - for ( var i = 0; i < this.geometry.skinIndices.length; i ++ ) { + } - var sw = this.geometry.skinWeights[ i ]; - var scale = 1.0 / sw.lengthManhattan(); + } - if ( scale !== Infinity ) { + ++ writeIndex; - sw.multiplyScalar( scale ); + } - } else { + } - sw.set( 1 ); // this will be normalized by the shader anyway + if ( writeIndex !== times.length ) { - } + this.times = THREE.AnimationUtils.arraySlice( times, 0, writeIndex ); + this.values = THREE.AnimationUtils.arraySlice( values, 0, writeIndex * stride ); } - } else { - - // skinning weights assumed to be normalized for THREE.BufferGeometry + return this; } }; -THREE.SkinnedMesh.prototype.clone = function ( object ) { - - if ( object === undefined ) { - - object = new THREE.SkinnedMesh( this.geometry, this.material, this.useVertexTexture ); - - } +// Static methods: - THREE.Mesh.prototype.clone.call( this, object ); +Object.assign( THREE.KeyframeTrack, { - return object; + // Serialization (in static context, because of constructor invocation + // and automatic invocation of .toJSON): -}; + parse: function( json ) { -/** - * @author alteredq / http://alteredqualia.com/ - */ + if( json.type === undefined ) { -THREE.MorphAnimMesh = function ( geometry, material ) { + throw new Error( "track type undefined, can not parse" ); - THREE.Mesh.call( this, geometry, material ); + } - // API + var trackType = THREE.KeyframeTrack._getTrackTypeForValueTypeName( json.type ); - this.duration = 1000; // milliseconds - this.mirroredLoop = false; - this.time = 0; + if ( json.times === undefined ) { - // internals + var times = [], values = []; - this.lastKeyframe = 0; - this.currentKeyframe = 0; + THREE.AnimationUtils.flattenJSON( json.keys, times, values, 'value' ); - this.direction = 1; - this.directionBackwards = false; + json.times = times; + json.values = values; - this.setFrameRange( 0, this.geometry.morphTargets.length - 1 ); + } -}; + // derived classes can define a static parse method + if ( trackType.parse !== undefined ) { -THREE.MorphAnimMesh.prototype = Object.create( THREE.Mesh.prototype ); + return trackType.parse( json ); -THREE.MorphAnimMesh.prototype.setFrameRange = function ( start, end ) { + } else { - this.startKeyframe = start; - this.endKeyframe = end; + // by default, we asssume a constructor compatible with the base + return new trackType( + json.name, json.times, json.values, json.interpolation ); - this.length = this.endKeyframe - this.startKeyframe + 1; + } -}; + }, -THREE.MorphAnimMesh.prototype.setDirectionForward = function () { + toJSON: function( track ) { - this.direction = 1; - this.directionBackwards = false; + var trackType = track.constructor; -}; + var json; -THREE.MorphAnimMesh.prototype.setDirectionBackward = function () { + // derived classes can define a static toJSON method + if ( trackType.toJSON !== undefined ) { - this.direction = -1; - this.directionBackwards = true; + json = trackType.toJSON( track ); -}; + } else { -THREE.MorphAnimMesh.prototype.parseAnimations = function () { + // by default, we assume the data can be serialized as-is + json = { - var geometry = this.geometry; + 'name': track.name, + 'times': THREE.AnimationUtils.convertArray( track.times, Array ), + 'values': THREE.AnimationUtils.convertArray( track.values, Array ) - if ( ! geometry.animations ) geometry.animations = {}; + }; - var firstAnimation, animations = geometry.animations; + var interpolation = track.getInterpolation(); - var pattern = /([a-z]+)(\d+)/; + if ( interpolation !== track.DefaultInterpolation ) { - for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) { + json.interpolation = interpolation; - var morph = geometry.morphTargets[ i ]; - var parts = morph.name.match( pattern ); + } - if ( parts && parts.length > 1 ) { + } - var label = parts[ 1 ]; - var num = parts[ 2 ]; + json.type = track.ValueTypeName; // mandatory - if ( ! animations[ label ] ) animations[ label ] = { start: Infinity, end: -Infinity }; + return json; - var animation = animations[ label ]; + }, - if ( i < animation.start ) animation.start = i; - if ( i > animation.end ) animation.end = i; + _getTrackTypeForValueTypeName: function( typeName ) { - if ( ! firstAnimation ) firstAnimation = label; + switch( typeName.toLowerCase() ) { - } + case "scalar": + case "double": + case "float": + case "number": + case "integer": - } + return THREE.NumberKeyframeTrack; - geometry.firstAnimation = firstAnimation; + case "vector": + case "vector2": + case "vector3": + case "vector4": -}; + return THREE.VectorKeyframeTrack; -THREE.MorphAnimMesh.prototype.setAnimationLabel = function ( label, start, end ) { + case "color": - if ( ! this.geometry.animations ) this.geometry.animations = {}; + return THREE.ColorKeyframeTrack; - this.geometry.animations[ label ] = { start: start, end: end }; + case "quaternion": -}; + return THREE.QuaternionKeyframeTrack; -THREE.MorphAnimMesh.prototype.playAnimation = function ( label, fps ) { + case "bool": + case "boolean": - var animation = this.geometry.animations[ label ]; + return THREE.BooleanKeyframeTrack; - if ( animation ) { + case "string": - this.setFrameRange( animation.start, animation.end ); - this.duration = 1000 * ( ( animation.end - animation.start ) / fps ); - this.time = 0; + return THREE.StringKeyframeTrack; - } else { + } - console.warn( "animation[" + label + "] undefined" ); + throw new Error( "Unsupported typeName: " + typeName ); } -}; +} ); -THREE.MorphAnimMesh.prototype.updateAnimation = function ( delta ) { +// File:src/animation/PropertyBinding.js - var frameTime = this.duration / this.length; +/** + * + * A reference to a real property in the scene graph. + * + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ - this.time += this.direction * delta; +THREE.PropertyBinding = function ( rootNode, path, parsedPath ) { - if ( this.mirroredLoop ) { + this.path = path; + this.parsedPath = parsedPath || + THREE.PropertyBinding.parseTrackName( path ); - if ( this.time > this.duration || this.time < 0 ) { + this.node = THREE.PropertyBinding.findNode( + rootNode, this.parsedPath.nodeName ) || rootNode; - this.direction *= -1; + this.rootNode = rootNode; - if ( this.time > this.duration ) { +}; - this.time = this.duration; - this.directionBackwards = true; +THREE.PropertyBinding.prototype = { - } + constructor: THREE.PropertyBinding, - if ( this.time < 0 ) { + getValue: function getValue_unbound( targetArray, offset ) { - this.time = 0; - this.directionBackwards = false; + this.bind(); + this.getValue( targetArray, offset ); - } + // Note: This class uses a State pattern on a per-method basis: + // 'bind' sets 'this.getValue' / 'setValue' and shadows the + // prototype version of these methods with one that represents + // the bound state. When the property is not found, the methods + // become no-ops. - } + }, - } else { + setValue: function getValue_unbound( sourceArray, offset ) { - this.time = this.time % this.duration; + this.bind(); + this.setValue( sourceArray, offset ); - if ( this.time < 0 ) this.time += this.duration; + }, - } + // create getter / setter pair for a property in the scene graph + bind: function() { - var keyframe = this.startKeyframe + THREE.Math.clamp( Math.floor( this.time / frameTime ), 0, this.length - 1 ); + var targetObject = this.node, + parsedPath = this.parsedPath, - if ( keyframe !== this.currentKeyframe ) { + objectName = parsedPath.objectName, + propertyName = parsedPath.propertyName, + propertyIndex = parsedPath.propertyIndex; - this.morphTargetInfluences[ this.lastKeyframe ] = 0; - this.morphTargetInfluences[ this.currentKeyframe ] = 1; + if ( ! targetObject ) { - this.morphTargetInfluences[ keyframe ] = 0; + targetObject = THREE.PropertyBinding.findNode( + this.rootNode, parsedPath.nodeName ) || this.rootNode; - this.lastKeyframe = this.currentKeyframe; - this.currentKeyframe = keyframe; + this.node = targetObject; - } + } - var mix = ( this.time % frameTime ) / frameTime; + // set fail state so we can just 'return' on error + this.getValue = this._getValue_unavailable; + this.setValue = this._setValue_unavailable; - if ( this.directionBackwards ) { + // ensure there is a value node + if ( ! targetObject ) { - mix = 1 - mix; + console.error( " trying to update node for track: " + this.path + " but it wasn't found." ); + return; - } + } - this.morphTargetInfluences[ this.currentKeyframe ] = mix; - this.morphTargetInfluences[ this.lastKeyframe ] = 1 - mix; + if ( objectName ) { -}; + var objectIndex = parsedPath.objectIndex; -THREE.MorphAnimMesh.prototype.clone = function ( object ) { + // special cases were we need to reach deeper into the hierarchy to get the face materials.... + switch ( objectName ) { - if ( object === undefined ) object = new THREE.MorphAnimMesh( this.geometry, this.material ); + case 'materials': - object.duration = this.duration; - object.mirroredLoop = this.mirroredLoop; - object.time = this.time; + if ( ! targetObject.material ) { - object.lastKeyframe = this.lastKeyframe; - object.currentKeyframe = this.currentKeyframe; + console.error( ' can not bind to material as node does not have a material', this ); + return; - object.direction = this.direction; - object.directionBackwards = this.directionBackwards; + } - THREE.Mesh.prototype.clone.call( this, object ); + if ( ! targetObject.material.materials ) { - return object; + console.error( ' can not bind to material.materials as node.material does not have a materials array', this ); + return; -}; + } -/** - * @author alteredq / http://alteredqualia.com/ - */ + targetObject = targetObject.material.materials; -THREE.Ribbon = function ( geometry, material ) { + break; - THREE.Object3D.call( this ); + case 'bones': - this.geometry = geometry; - this.material = material; + if ( ! targetObject.skeleton ) { -}; + console.error( ' can not bind to bones as node does not have a skeleton', this ); + return; -THREE.Ribbon.prototype = Object.create( THREE.Object3D.prototype ); + } -THREE.Ribbon.prototype.clone = function ( object ) { + // potential future optimization: skip this if propertyIndex is already an integer + // and convert the integer string to a true integer. - if ( object === undefined ) object = new THREE.Ribbon( this.geometry, this.material ); + targetObject = targetObject.skeleton.bones; - THREE.Object3D.prototype.clone.call( this, object ); + // support resolving morphTarget names into indices. + for ( var i = 0; i < targetObject.length; i ++ ) { - return object; + if ( targetObject[ i ].name === objectIndex ) { -}; + objectIndex = i; + break; -/** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - */ + } -THREE.LOD = function () { + } - THREE.Object3D.call( this ); + break; - this.objects = []; + default: -}; + if ( targetObject[ objectName ] === undefined ) { + console.error( ' can not bind to objectName of node, undefined', this ); + return; -THREE.LOD.prototype = Object.create( THREE.Object3D.prototype ); + } -THREE.LOD.prototype.addLevel = function ( object, distance ) { + targetObject = targetObject[ objectName ]; - if ( distance === undefined ) distance = 0; + } - distance = Math.abs( distance ); - for ( var l = 0; l < this.objects.length; l ++ ) { + if ( objectIndex !== undefined ) { - if ( distance < this.objects[ l ].distance ) { + if ( targetObject[ objectIndex ] === undefined ) { - break; + console.error( " trying to bind to objectIndex of objectName, but is undefined:", this, targetObject ); + return; - } + } - } + targetObject = targetObject[ objectIndex ]; - this.objects.splice( l, 0, { distance: distance, object: object } ); - this.add( object ); + } -}; + } -THREE.LOD.prototype.getObjectForDistance = function ( distance ) { + // resolve property + var nodeProperty = targetObject[ propertyName ]; - for ( var i = 1, l = this.objects.length; i < l; i ++ ) { + if ( nodeProperty === undefined ) { - if ( distance < this.objects[ i ].distance ) { + var nodeName = parsedPath.nodeName; - break; + console.error( " trying to update property for track: " + nodeName + + '.' + propertyName + " but it wasn't found.", targetObject ); + return; } - } - - return this.objects[ i - 1 ].object; + // determine versioning scheme + var versioning = this.Versioning.None; -}; + if ( targetObject.needsUpdate !== undefined ) { // material -THREE.LOD.prototype.update = function () { + versioning = this.Versioning.NeedsUpdate; + this.targetObject = targetObject; - var v1 = new THREE.Vector3(); - var v2 = new THREE.Vector3(); + } else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform - return function ( camera ) { + versioning = this.Versioning.MatrixWorldNeedsUpdate; + this.targetObject = targetObject; - if ( this.objects.length > 1 ) { + } - v1.getPositionFromMatrix( camera.matrixWorld ); - v2.getPositionFromMatrix( this.matrixWorld ); + // determine how the property gets bound + var bindingType = this.BindingType.Direct; - var distance = v1.distanceTo( v2 ); + if ( propertyIndex !== undefined ) { + // access a sub element of the property array (only primitives are supported right now) - this.objects[ 0 ].object.visible = true; + if ( propertyName === "morphTargetInfluences" ) { + // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. - for ( var i = 1, l = this.objects.length; i < l; i ++ ) { + // support resolving morphTarget names into indices. + if ( ! targetObject.geometry ) { - if ( distance >= this.objects[ i ].distance ) { + console.error( ' can not bind to morphTargetInfluences becasuse node does not have a geometry', this ); + return; - this.objects[ i - 1 ].object.visible = false; - this.objects[ i ].object.visible = true; + } - } else { + if ( ! targetObject.geometry.morphTargets ) { - break; + console.error( ' can not bind to morphTargetInfluences becasuse node does not have a geometry.morphTargets', this ); + return; } - } + for ( var i = 0; i < this.node.geometry.morphTargets.length; i ++ ) { + + if ( targetObject.geometry.morphTargets[ i ].name === propertyIndex ) { - for( ; i < l; i ++ ) { + propertyIndex = i; + break; + + } - this.objects[ i ].object.visible = false; + } } - } + bindingType = this.BindingType.ArrayElement; - }; + this.resolvedProperty = nodeProperty; + this.propertyIndex = propertyIndex; -}(); + } else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) { + // must use copy for Object3D.Euler/Quaternion -THREE.LOD.prototype.clone = function () { + bindingType = this.BindingType.HasFromToArray; - // TODO + this.resolvedProperty = nodeProperty; -}; + } else if ( nodeProperty.length !== undefined ) { -/** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - */ + bindingType = this.BindingType.EntireArray; -THREE.Sprite = function ( material ) { + this.resolvedProperty = nodeProperty; - THREE.Object3D.call( this ); + } else { - this.material = ( material !== undefined ) ? material : new THREE.SpriteMaterial(); + this.propertyName = propertyName; - this.rotation3d = this.rotation; - this.rotation = 0; + } -}; + // select getter / setter + this.getValue = this.GetterByBindingType[ bindingType ]; + this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ]; -THREE.Sprite.prototype = Object.create( THREE.Object3D.prototype ); + }, -/* - * Custom update matrix - */ + unbind: function() { -THREE.Sprite.prototype.updateMatrix = function () { + this.node = null; - this.rotation3d.set( 0, 0, this.rotation, this.rotation3d.order ); - this.quaternion.setFromEuler( this.rotation3d ); - this.matrix.compose( this.position, this.quaternion, this.scale ); + // back to the prototype version of getValue / setValue + // note: avoiding to mutate the shape of 'this' via 'delete' + this.getValue = this._getValue_unbound; + this.setValue = this._setValue_unbound; - this.matrixWorldNeedsUpdate = true; + } }; -THREE.Sprite.prototype.clone = function ( object ) { +Object.assign( THREE.PropertyBinding.prototype, { // prototype, continued - if ( object === undefined ) object = new THREE.Sprite( this.material ); + // these are used to "bind" a nonexistent property + _getValue_unavailable: function() {}, + _setValue_unavailable: function() {}, - THREE.Object3D.prototype.clone.call( this, object ); + // initial state of these methods that calls 'bind' + _getValue_unbound: THREE.PropertyBinding.prototype.getValue, + _setValue_unbound: THREE.PropertyBinding.prototype.setValue, - return object; + BindingType: { + Direct: 0, + EntireArray: 1, + ArrayElement: 2, + HasFromToArray: 3 + }, -}; + Versioning: { + None: 0, + NeedsUpdate: 1, + MatrixWorldNeedsUpdate: 2 + }, + GetterByBindingType: [ -/** - * @author mrdoob / http://mrdoob.com/ - */ + function getValue_direct( buffer, offset ) { -THREE.Scene = function () { + buffer[ offset ] = this.node[ this.propertyName ]; - THREE.Object3D.call( this ); + }, - this.fog = null; - this.overrideMaterial = null; + function getValue_array( buffer, offset ) { - this.autoUpdate = true; // checked by the renderer - this.matrixAutoUpdate = false; + var source = this.resolvedProperty; - this.__objects = []; - this.__lights = []; + for ( var i = 0, n = source.length; i !== n; ++ i ) { - this.__objectsAdded = []; - this.__objectsRemoved = []; + buffer[ offset ++ ] = source[ i ]; -}; + } -THREE.Scene.prototype = Object.create( THREE.Object3D.prototype ); + }, -THREE.Scene.prototype.__addObject = function ( object ) { + function getValue_arrayElement( buffer, offset ) { - if ( object instanceof THREE.Light ) { + buffer[ offset ] = this.resolvedProperty[ this.propertyIndex ]; - if ( this.__lights.indexOf( object ) === - 1 ) { + }, + + function getValue_toArray( buffer, offset ) { - this.__lights.push( object ); + this.resolvedProperty.toArray( buffer, offset ); } - if ( object.target && object.target.parent === undefined ) { + ], - this.add( object.target ); + SetterByBindingTypeAndVersioning: [ - } + [ + // Direct + + function setValue_direct( buffer, offset ) { - } else if ( !( object instanceof THREE.Camera || object instanceof THREE.Bone ) ) { + this.node[ this.propertyName ] = buffer[ offset ]; - if ( this.__objects.indexOf( object ) === - 1 ) { + }, - this.__objects.push( object ); - this.__objectsAdded.push( object ); + function setValue_direct_setNeedsUpdate( buffer, offset ) { - // check if previously removed + this.node[ this.propertyName ] = buffer[ offset ]; + this.targetObject.needsUpdate = true; - var i = this.__objectsRemoved.indexOf( object ); + }, - if ( i !== -1 ) { + function setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) { - this.__objectsRemoved.splice( i, 1 ); + this.node[ this.propertyName ] = buffer[ offset ]; + this.targetObject.matrixWorldNeedsUpdate = true; } - } + ], [ - } + // EntireArray - for ( var c = 0; c < object.children.length; c ++ ) { + function setValue_array( buffer, offset ) { - this.__addObject( object.children[ c ] ); + var dest = this.resolvedProperty; - } + for ( var i = 0, n = dest.length; i !== n; ++ i ) { -}; + dest[ i ] = buffer[ offset ++ ]; -THREE.Scene.prototype.__removeObject = function ( object ) { + } - if ( object instanceof THREE.Light ) { + }, - var i = this.__lights.indexOf( object ); + function setValue_array_setNeedsUpdate( buffer, offset ) { - if ( i !== -1 ) { + var dest = this.resolvedProperty; - this.__lights.splice( i, 1 ); + for ( var i = 0, n = dest.length; i !== n; ++ i ) { - } + dest[ i ] = buffer[ offset ++ ]; + + } - } else if ( !( object instanceof THREE.Camera ) ) { + this.targetObject.needsUpdate = true; - var i = this.__objects.indexOf( object ); + }, - if( i !== -1 ) { + function setValue_array_setMatrixWorldNeedsUpdate( buffer, offset ) { - this.__objects.splice( i, 1 ); - this.__objectsRemoved.push( object ); + var dest = this.resolvedProperty; - // check if previously added + for ( var i = 0, n = dest.length; i !== n; ++ i ) { - var ai = this.__objectsAdded.indexOf( object ); + dest[ i ] = buffer[ offset ++ ]; - if ( ai !== -1 ) { + } - this.__objectsAdded.splice( ai, 1 ); + this.targetObject.matrixWorldNeedsUpdate = true; } - } - - } + ], [ - for ( var c = 0; c < object.children.length; c ++ ) { + // ArrayElement - this.__removeObject( object.children[ c ] ); + function setValue_arrayElement( buffer, offset ) { - } + this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; -}; + }, -THREE.Scene.prototype.clone = function ( object ) { + function setValue_arrayElement_setNeedsUpdate( buffer, offset ) { - if ( object === undefined ) object = new THREE.Scene(); + this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; + this.targetObject.needsUpdate = true; - THREE.Object3D.prototype.clone.call(this, object); + }, - if ( this.fog !== null ) object.fog = this.fog.clone(); - if ( this.overrideMaterial !== null ) object.overrideMaterial = this.overrideMaterial.clone(); + function setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) { - object.autoUpdate = this.autoUpdate; - object.matrixAutoUpdate = this.matrixAutoUpdate; + this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; + this.targetObject.matrixWorldNeedsUpdate = true; - return object; + } -}; + ], [ -/** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ + // HasToFromArray -THREE.Fog = function ( hex, near, far ) { + function setValue_fromArray( buffer, offset ) { - this.name = ''; + this.resolvedProperty.fromArray( buffer, offset ); - this.color = new THREE.Color( hex ); + }, - this.near = ( near !== undefined ) ? near : 1; - this.far = ( far !== undefined ) ? far : 1000; + function setValue_fromArray_setNeedsUpdate( buffer, offset ) { -}; + this.resolvedProperty.fromArray( buffer, offset ); + this.targetObject.needsUpdate = true; -THREE.Fog.prototype.clone = function () { + }, - return new THREE.Fog( this.color.getHex(), this.near, this.far ); + function setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) { -}; + this.resolvedProperty.fromArray( buffer, offset ); + this.targetObject.matrixWorldNeedsUpdate = true; -/** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ + } -THREE.FogExp2 = function ( hex, density ) { + ] - this.name = ''; + ] - this.color = new THREE.Color( hex ); - this.density = ( density !== undefined ) ? density : 0.00025; +} ); -}; +THREE.PropertyBinding.Composite = + function( targetGroup, path, optionalParsedPath ) { -THREE.FogExp2.prototype.clone = function () { + var parsedPath = optionalParsedPath || + THREE.PropertyBinding.parseTrackName( path ); - return new THREE.FogExp2( this.color.getHex(), this.density ); + this._targetGroup = targetGroup; + this._bindings = targetGroup.subscribe_( path, parsedPath ); }; -/** - * @author mrdoob / http://mrdoob.com/ - */ +THREE.PropertyBinding.Composite.prototype = { -THREE.CanvasRenderer = function ( parameters ) { + constructor: THREE.PropertyBinding.Composite, - console.log( 'THREE.CanvasRenderer', THREE.REVISION ); + getValue: function( array, offset ) { - var smoothstep = THREE.Math.smoothstep; + this.bind(); // bind all binding - parameters = parameters || {}; + var firstValidIndex = this._targetGroup.nCachedObjects_, + binding = this._bindings[ firstValidIndex ]; - var _this = this, - _renderData, _elements, _lights, - _projector = new THREE.Projector(), + // and only call .getValue on the first + if ( binding !== undefined ) binding.getValue( array, offset ); - _canvas = parameters.canvas !== undefined - ? parameters.canvas - : document.createElement( 'canvas' ), + }, - _canvasWidth, _canvasHeight, _canvasWidthHalf, _canvasHeightHalf, - _context = _canvas.getContext( '2d' ), + setValue: function( array, offset ) { - _clearColor = new THREE.Color( 0x000000 ), - _clearAlpha = 0, + var bindings = this._bindings; - _contextGlobalAlpha = 1, - _contextGlobalCompositeOperation = 0, - _contextStrokeStyle = null, - _contextFillStyle = null, - _contextLineWidth = null, - _contextLineCap = null, - _contextLineJoin = null, - _contextDashSize = null, - _contextGapSize = 0, + for ( var i = this._targetGroup.nCachedObjects_, + n = bindings.length; i !== n; ++ i ) { - _camera, + bindings[ i ].setValue( array, offset ); - _v1, _v2, _v3, _v4, - _v5 = new THREE.RenderableVertex(), - _v6 = new THREE.RenderableVertex(), + } - _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, - _v4x, _v4y, _v5x, _v5y, _v6x, _v6y, + }, - _color = new THREE.Color(), - _color1 = new THREE.Color(), - _color2 = new THREE.Color(), - _color3 = new THREE.Color(), - _color4 = new THREE.Color(), + bind: function() { - _diffuseColor = new THREE.Color(), - _emissiveColor = new THREE.Color(), + var bindings = this._bindings; - _lightColor = new THREE.Color(), + for ( var i = this._targetGroup.nCachedObjects_, + n = bindings.length; i !== n; ++ i ) { - _patterns = {}, _imagedatas = {}, + bindings[ i ].bind(); - _near, _far, + } - _image, _uvs, - _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, + }, - _clipBox = new THREE.Box2(), - _clearBox = new THREE.Box2(), - _elemBox = new THREE.Box2(), + unbind: function() { - _ambientLight = new THREE.Color(), - _directionalLights = new THREE.Color(), - _pointLights = new THREE.Color(), + var bindings = this._bindings; - _vector3 = new THREE.Vector3(), // Needed for PointLight + for ( var i = this._targetGroup.nCachedObjects_, + n = bindings.length; i !== n; ++ i ) { - _pixelMap, _pixelMapContext, _pixelMapImage, _pixelMapData, - _gradientMap, _gradientMapContext, _gradientMapQuality = 16; + bindings[ i ].unbind(); - _pixelMap = document.createElement( 'canvas' ); - _pixelMap.width = _pixelMap.height = 2; + } - _pixelMapContext = _pixelMap.getContext( '2d' ); - _pixelMapContext.fillStyle = 'rgba(0,0,0,1)'; - _pixelMapContext.fillRect( 0, 0, 2, 2 ); + } - _pixelMapImage = _pixelMapContext.getImageData( 0, 0, 2, 2 ); - _pixelMapData = _pixelMapImage.data; +}; - _gradientMap = document.createElement( 'canvas' ); - _gradientMap.width = _gradientMap.height = _gradientMapQuality; +THREE.PropertyBinding.create = function( root, path, parsedPath ) { - _gradientMapContext = _gradientMap.getContext( '2d' ); - _gradientMapContext.translate( - _gradientMapQuality / 2, - _gradientMapQuality / 2 ); - _gradientMapContext.scale( _gradientMapQuality, _gradientMapQuality ); + if ( ! ( root instanceof THREE.AnimationObjectGroup ) ) { - _gradientMapQuality --; // Fix UVs + return new THREE.PropertyBinding( root, path, parsedPath ); - // dash+gap fallbacks for Firefox and everything else + } else { - if ( _context.setLineDash === undefined ) { + return new THREE.PropertyBinding.Composite( root, path, parsedPath ); - if ( _context.mozDash !== undefined ) { + } - _context.setLineDash = function ( values ) { +}; - _context.mozDash = values[ 0 ] !== null ? values : null; +THREE.PropertyBinding.parseTrackName = function( trackName ) { - } + // 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 - } else { + var re = /^(([\w]+\/)*)([\w-\d]+)?(\.([\w]+)(\[([\w\d\[\]\_.:\- ]+)\])?)?(\.([\w.]+)(\[([\w\d\[\]\_. ]+)\])?)$/; + var matches = re.exec( trackName ); - _context.setLineDash = function () {} + if ( ! matches ) { - } + throw new Error( "cannot parse trackName at all: " + trackName ); } - this.domElement = _canvas; + if ( matches.index === re.lastIndex ) { - this.devicePixelRatio = parameters.devicePixelRatio !== undefined - ? parameters.devicePixelRatio - : window.devicePixelRatio !== undefined - ? window.devicePixelRatio - : 1; + re.lastIndex++; - this.autoClear = true; - this.sortObjects = true; - this.sortElements = true; - - this.info = { + } - render: { + var results = { + // directoryName: matches[ 1 ], // (tschw) currently unused + 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. + }; - vertices: 0, - faces: 0 + if ( results.propertyName === null || results.propertyName.length === 0 ) { - } + throw new Error( "can not parse propertyName from trackName: " + trackName ); } - // WebGLRenderer compatibility - - this.supportsVertexTextures = function () {}; - this.setFaceCulling = function () {}; + return results; - this.setSize = function ( width, height, updateStyle ) { +}; - _canvasWidth = width * this.devicePixelRatio; - _canvasHeight = height * this.devicePixelRatio; +THREE.PropertyBinding.findNode = function( root, nodeName ) { - _canvasWidthHalf = Math.floor( _canvasWidth / 2 ); - _canvasHeightHalf = Math.floor( _canvasHeight / 2 ); + if ( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid ) { - _canvas.width = _canvasWidth; - _canvas.height = _canvasHeight; + return root; - if ( this.devicePixelRatio !== 1 && updateStyle !== false ) { + } - _canvas.style.width = width + 'px'; - _canvas.style.height = height + 'px'; + // search into skeleton bones. + if ( root.skeleton ) { - } + var searchSkeleton = function( skeleton ) { - _clipBox.set( - new THREE.Vector2( - _canvasWidthHalf, - _canvasHeightHalf ), - new THREE.Vector2( _canvasWidthHalf, _canvasHeightHalf ) - ); + for( var i = 0; i < skeleton.bones.length; i ++ ) { - _clearBox.set( - new THREE.Vector2( - _canvasWidthHalf, - _canvasHeightHalf ), - new THREE.Vector2( _canvasWidthHalf, _canvasHeightHalf ) - ); + var bone = skeleton.bones[ i ]; - _contextGlobalAlpha = 1; - _contextGlobalCompositeOperation = 0; - _contextStrokeStyle = null; - _contextFillStyle = null; - _contextLineWidth = null; - _contextLineCap = null; - _contextLineJoin = null; + if ( bone.name === nodeName ) { - }; + return bone; - this.setClearColor = function ( color, alpha ) { + } + } - _clearColor.set( color ); - _clearAlpha = alpha !== undefined ? alpha : 1; + return null; - _clearBox.set( - new THREE.Vector2( - _canvasWidthHalf, - _canvasHeightHalf ), - new THREE.Vector2( _canvasWidthHalf, _canvasHeightHalf ) - ); + }; - }; + var bone = searchSkeleton( root.skeleton ); - this.setClearColorHex = function ( hex, alpha ) { + if ( bone ) { - console.warn( 'DEPRECATED: .setClearColorHex() is being removed. Use .setClearColor() instead.' ); - this.setClearColor( hex, alpha ); + return bone; - }; + } + } - this.getMaxAnisotropy = function () { + // search into node subtree. + if ( root.children ) { - return 0; + var searchNodeSubtree = function( children ) { - }; + for( var i = 0; i < children.length; i ++ ) { - this.clear = function () { + var childNode = children[ i ]; - _context.setTransform( 1, 0, 0, - 1, _canvasWidthHalf, _canvasHeightHalf ); + if ( childNode.name === nodeName || childNode.uuid === nodeName ) { - if ( _clearBox.empty() === false ) { + return childNode; - _clearBox.intersect( _clipBox ); - _clearBox.expandByScalar( 2 ); + } - if ( _clearAlpha < 1 ) { + var result = searchNodeSubtree( childNode.children ); - _context.clearRect( - _clearBox.min.x | 0, - _clearBox.min.y | 0, - ( _clearBox.max.x - _clearBox.min.x ) | 0, - ( _clearBox.max.y - _clearBox.min.y ) | 0 - ); + if ( result ) return result; } - if ( _clearAlpha > 0 ) { - - setBlending( THREE.NormalBlending ); - setOpacity( 1 ); + return null; - setFillStyle( 'rgba(' + Math.floor( _clearColor.r * 255 ) + ',' + Math.floor( _clearColor.g * 255 ) + ',' + Math.floor( _clearColor.b * 255 ) + ',' + _clearAlpha + ')' ); + }; - _context.fillRect( - _clearBox.min.x | 0, - _clearBox.min.y | 0, - ( _clearBox.max.x - _clearBox.min.x ) | 0, - ( _clearBox.max.y - _clearBox.min.y ) | 0 - ); + var subTreeNode = searchNodeSubtree( root.children ); - } + if ( subTreeNode ) { - _clearBox.makeEmpty(); + return subTreeNode; } + } - }; + return null; - this.render = function ( scene, camera ) { +}; - if ( camera instanceof THREE.Camera === false ) { +// File:src/animation/PropertyMixer.js - console.error( 'THREE.CanvasRenderer.render: camera is not an instance of THREE.Camera.' ); - return; +/** + * + * Buffered scene graph property that allows weighted accumulation. + * + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ - } +THREE.PropertyMixer = function ( binding, typeName, valueSize ) { - if ( this.autoClear === true ) this.clear(); + this.binding = binding; + this.valueSize = valueSize; - _context.setTransform( 1, 0, 0, - 1, _canvasWidthHalf, _canvasHeightHalf ); + var bufferType = Float64Array, + mixFunction; - _this.info.render.vertices = 0; - _this.info.render.faces = 0; + switch ( typeName ) { - _renderData = _projector.projectScene( scene, camera, this.sortObjects, this.sortElements ); - _elements = _renderData.elements; - _lights = _renderData.lights; - _camera = camera; + case 'quaternion': mixFunction = this._slerp; break; - /* DEBUG - setFillStyle( 'rgba( 0, 255, 255, 0.5 )' ); - _context.fillRect( _clipBox.min.x, _clipBox.min.y, _clipBox.max.x - _clipBox.min.x, _clipBox.max.y - _clipBox.min.y ); - */ + case 'string': + case 'bool': - calculateLights(); + bufferType = Array, mixFunction = this._select; break; - for ( var e = 0, el = _elements.length; e < el; e++ ) { + default: mixFunction = this._lerp; + + } - var element = _elements[ e ]; + this.buffer = new bufferType( valueSize * 4 ); + // layout: [ incoming | accu0 | accu1 | orig ] + // + // interpolators can use .buffer as their .result + // the data then goes to 'incoming' + // + // 'accu0' and 'accu1' are used frame-interleaved for + // the cumulative result and are compared to detect + // changes + // + // 'orig' stores the original state of the property - var material = element.material; + this._mixBufferRegion = mixFunction; - if ( material === undefined || material.visible === false ) continue; + this.cumulativeWeight = 0; - _elemBox.makeEmpty(); + this.useCount = 0; + this.referenceCount = 0; - if ( element instanceof THREE.RenderableParticle ) { +}; - _v1 = element; - _v1.x *= _canvasWidthHalf; _v1.y *= _canvasHeightHalf; +THREE.PropertyMixer.prototype = { - renderParticle( _v1, element, material ); + constructor: THREE.PropertyMixer, - } else if ( element instanceof THREE.RenderableLine ) { + // accumulate data in the 'incoming' region into 'accu' + accumulate: function( accuIndex, weight ) { - _v1 = element.v1; _v2 = element.v2; + // note: happily accumulating nothing when weight = 0, the caller knows + // the weight and shouldn't have made the call in the first place - _v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf; - _v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf; + var buffer = this.buffer, + stride = this.valueSize, + offset = accuIndex * stride + stride, - _elemBox.setFromPoints( [ - _v1.positionScreen, - _v2.positionScreen - ] ); + currentWeight = this.cumulativeWeight; - if ( _clipBox.isIntersectionBox( _elemBox ) === true ) { + if ( currentWeight === 0 ) { - renderLine( _v1, _v2, element, material ); + // accuN := incoming * weight - } + for ( var i = 0; i !== stride; ++ i ) { - } else if ( element instanceof THREE.RenderableFace3 ) { + buffer[ offset + i ] = buffer[ i ]; - _v1 = element.v1; _v2 = element.v2; _v3 = element.v3; + } - if ( _v1.positionScreen.z < -1 || _v1.positionScreen.z > 1 ) continue; - if ( _v2.positionScreen.z < -1 || _v2.positionScreen.z > 1 ) continue; - if ( _v3.positionScreen.z < -1 || _v3.positionScreen.z > 1 ) continue; + currentWeight = weight; - _v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf; - _v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf; - _v3.positionScreen.x *= _canvasWidthHalf; _v3.positionScreen.y *= _canvasHeightHalf; + } else { - if ( material.overdraw > 0 ) { + // accuN := accuN + incoming * weight - expand( _v1.positionScreen, _v2.positionScreen, material.overdraw ); - expand( _v2.positionScreen, _v3.positionScreen, material.overdraw ); - expand( _v3.positionScreen, _v1.positionScreen, material.overdraw ); + currentWeight += weight; + var mix = weight / currentWeight; + this._mixBufferRegion( buffer, offset, 0, mix, stride ); - } + } - _elemBox.setFromPoints( [ - _v1.positionScreen, - _v2.positionScreen, - _v3.positionScreen - ] ); + this.cumulativeWeight = currentWeight; - if ( _clipBox.isIntersectionBox( _elemBox ) === true ) { + }, - renderFace3( _v1, _v2, _v3, 0, 1, 2, element, material ); + // apply the state of 'accu' to the binding when accus differ + apply: function( accuIndex ) { - } + var stride = this.valueSize, + buffer = this.buffer, + offset = accuIndex * stride + stride, - } + weight = this.cumulativeWeight, - /* DEBUG - setLineWidth( 1 ); - setStrokeStyle( 'rgba( 0, 255, 0, 0.5 )' ); - _context.strokeRect( _elemBox.min.x, _elemBox.min.y, _elemBox.max.x - _elemBox.min.x, _elemBox.max.y - _elemBox.min.y ); - */ + binding = this.binding; - _clearBox.union( _elemBox ); + this.cumulativeWeight = 0; - } + if ( weight < 1 ) { - /* DEBUG - setLineWidth( 1 ); - setStrokeStyle( 'rgba( 255, 0, 0, 0.5 )' ); - _context.strokeRect( _clearBox.min.x, _clearBox.min.y, _clearBox.max.x - _clearBox.min.x, _clearBox.max.y - _clearBox.min.y ); - */ + // accuN := accuN + original * ( 1 - cumulativeWeight ) - _context.setTransform( 1, 0, 0, 1, 0, 0 ); + var originalValueOffset = stride * 3; - }; + this._mixBufferRegion( + buffer, offset, originalValueOffset, 1 - weight, stride ); - // + } - function calculateLights() { + for ( var i = stride, e = stride + stride; i !== e; ++ i ) { - _ambientLight.setRGB( 0, 0, 0 ); - _directionalLights.setRGB( 0, 0, 0 ); - _pointLights.setRGB( 0, 0, 0 ); + if ( buffer[ i ] !== buffer[ i + stride ] ) { - for ( var l = 0, ll = _lights.length; l < ll; l ++ ) { + // value has changed -> update scene graph - var light = _lights[ l ]; - var lightColor = light.color; + binding.setValue( buffer, offset ); + break; - if ( light instanceof THREE.AmbientLight ) { + } - _ambientLight.add( lightColor ); + } - } else if ( light instanceof THREE.DirectionalLight ) { + }, - // for particles + // remember the state of the bound property and copy it to both accus + saveOriginalState: function() { - _directionalLights.add( lightColor ); + var binding = this.binding; - } else if ( light instanceof THREE.PointLight ) { + var buffer = this.buffer, + stride = this.valueSize, - // for particles + originalValueOffset = stride * 3; - _pointLights.add( lightColor ); + binding.getValue( buffer, originalValueOffset ); - } + // accu[0..1] := orig -- initially detect changes against the original + for ( var i = stride, e = originalValueOffset; i !== e; ++ i ) { + + buffer[ i ] = buffer[ originalValueOffset + ( i % stride ) ]; } - } + this.cumulativeWeight = 0; + + }, - function calculateLight( position, normal, color ) { + // apply the state previously taken via 'saveOriginalState' to the binding + restoreOriginalState: function() { - for ( var l = 0, ll = _lights.length; l < ll; l ++ ) { + var originalValueOffset = this.valueSize * 3; + this.binding.setValue( this.buffer, originalValueOffset ); - var light = _lights[ l ]; + }, - _lightColor.copy( light.color ); - if ( light instanceof THREE.DirectionalLight ) { + // mix functions - var lightPosition = _vector3.getPositionFromMatrix( light.matrixWorld ).normalize(); + _select: function( buffer, dstOffset, srcOffset, t, stride ) { - var amount = normal.dot( lightPosition ); + if ( t >= 0.5 ) { - if ( amount <= 0 ) continue; + for ( var i = 0; i !== stride; ++ i ) { - amount *= light.intensity; + buffer[ dstOffset + i ] = buffer[ srcOffset + i ]; - color.add( _lightColor.multiplyScalar( amount ) ); + } - } else if ( light instanceof THREE.PointLight ) { + } - var lightPosition = _vector3.getPositionFromMatrix( light.matrixWorld ); + }, - var amount = normal.dot( _vector3.subVectors( lightPosition, position ).normalize() ); + _slerp: function( buffer, dstOffset, srcOffset, t, stride ) { - if ( amount <= 0 ) continue; + THREE.Quaternion.slerpFlat( buffer, dstOffset, + buffer, dstOffset, buffer, srcOffset, t ); - amount *= light.distance == 0 ? 1 : 1 - Math.min( position.distanceTo( lightPosition ) / light.distance, 1 ); + }, - if ( amount == 0 ) continue; + _lerp: function( buffer, dstOffset, srcOffset, t, stride ) { - amount *= light.intensity; + var s = 1 - t; - color.add( _lightColor.multiplyScalar( amount ) ); + for ( var i = 0; i !== stride; ++ i ) { - } + var j = dstOffset + i; + + buffer[ j ] = buffer[ j ] * s + buffer[ srcOffset + i ] * t; } } - function renderParticle( v1, element, material ) { +}; - setOpacity( material.opacity ); - setBlending( material.blending ); +// File:src/animation/tracks/BooleanKeyframeTrack.js - var width, height, scaleX, scaleY, - bitmap, bitmapWidth, bitmapHeight; +/** + * + * A Track of Boolean keyframe values. + * + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ - if ( material instanceof THREE.ParticleBasicMaterial ) { +THREE.BooleanKeyframeTrack = function ( name, times, values ) { - if ( material.map === null ) { + THREE.KeyframeTrack.call( this, name, times, values ); - scaleX = element.object.scale.x; - scaleY = element.object.scale.y; +}; - // TODO: Be able to disable this +THREE.BooleanKeyframeTrack.prototype = + Object.assign( Object.create( THREE.KeyframeTrack.prototype ), { - scaleX *= element.scale.x * _canvasWidthHalf; - scaleY *= element.scale.y * _canvasHeightHalf; + constructor: THREE.BooleanKeyframeTrack, - _elemBox.min.set( v1.x - scaleX, v1.y - scaleY ); - _elemBox.max.set( v1.x + scaleX, v1.y + scaleY ); + ValueTypeName: 'bool', + ValueBufferType: Array, - if ( _clipBox.isIntersectionBox( _elemBox ) === false ) { + DefaultInterpolation: THREE.InterpolateDiscrete, - _elemBox.makeEmpty(); - return; + InterpolantFactoryMethodLinear: undefined, + InterpolantFactoryMethodSmooth: undefined - } + // Note: Actually this track could have a optimized / compressed + // representation of a single value and a custom interpolant that + // computes "firstValue ^ isOdd( index )". - setFillStyle( material.color.getStyle() ); +} ); - _context.save(); - _context.translate( v1.x, v1.y ); - _context.rotate( - element.rotation ); - _context.scale( scaleX, scaleY ); - _context.fillRect( -1, -1, 2, 2 ); - _context.restore(); +// File:src/animation/tracks/ColorKeyframeTrack.js - } else { +/** + * + * A Track of keyframe values that represent color. + * + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ - bitmap = material.map.image; - bitmapWidth = bitmap.width >> 1; - bitmapHeight = bitmap.height >> 1; +THREE.ColorKeyframeTrack = function ( name, times, values, interpolation ) { - scaleX = element.scale.x * _canvasWidthHalf; - scaleY = element.scale.y * _canvasHeightHalf; + THREE.KeyframeTrack.call( this, name, times, values, interpolation ); - width = scaleX * bitmapWidth; - height = scaleY * bitmapHeight; +}; - // TODO: Rotations break this... +THREE.ColorKeyframeTrack.prototype = + Object.assign( Object.create( THREE.KeyframeTrack.prototype ), { - _elemBox.min.set( v1.x - width, v1.y - height ); - _elemBox.max.set( v1.x + width, v1.y + height ); + constructor: THREE.ColorKeyframeTrack, - if ( _clipBox.isIntersectionBox( _elemBox ) === false ) { + ValueTypeName: 'color' - _elemBox.makeEmpty(); - return; + // ValueBufferType is inherited - } + // DefaultInterpolation is inherited - _context.save(); - _context.translate( v1.x, v1.y ); - _context.rotate( - element.rotation ); - _context.scale( scaleX, - scaleY ); - _context.translate( - bitmapWidth, - bitmapHeight ); - _context.drawImage( bitmap, 0, 0 ); - _context.restore(); + // Note: Very basic implementation and nothing special yet. + // However, this is the place for color space parameterization. - } +} ); - /* DEBUG - setStrokeStyle( 'rgb(255,255,0)' ); - _context.beginPath(); - _context.moveTo( v1.x - 10, v1.y ); - _context.lineTo( v1.x + 10, v1.y ); - _context.moveTo( v1.x, v1.y - 10 ); - _context.lineTo( v1.x, v1.y + 10 ); - _context.stroke(); - */ +// File:src/animation/tracks/NumberKeyframeTrack.js - } else if ( material instanceof THREE.ParticleCanvasMaterial ) { +/** + * + * A Track of numeric keyframe values. + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ - width = element.scale.x * _canvasWidthHalf; - height = element.scale.y * _canvasHeightHalf; +THREE.NumberKeyframeTrack = function ( name, times, values, interpolation ) { - _elemBox.min.set( v1.x - width, v1.y - height ); - _elemBox.max.set( v1.x + width, v1.y + height ); + THREE.KeyframeTrack.call( this, name, times, values, interpolation ); - if ( _clipBox.isIntersectionBox( _elemBox ) === false ) { +}; - _elemBox.makeEmpty(); - return; +THREE.NumberKeyframeTrack.prototype = + Object.assign( Object.create( THREE.KeyframeTrack.prototype ), { - } + constructor: THREE.NumberKeyframeTrack, - setStrokeStyle( material.color.getStyle() ); - setFillStyle( material.color.getStyle() ); + ValueTypeName: 'number', - _context.save(); - _context.translate( v1.x, v1.y ); - _context.rotate( - element.rotation ); - _context.scale( width, height ); + // ValueBufferType is inherited - material.program( _context ); + // DefaultInterpolation is inherited - _context.restore(); +} ); - } +// File:src/animation/tracks/QuaternionKeyframeTrack.js - } +/** + * + * A Track of quaternion keyframe values. + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ - function renderLine( v1, v2, element, material ) { +THREE.QuaternionKeyframeTrack = function ( name, times, values, interpolation ) { - setOpacity( material.opacity ); - setBlending( material.blending ); + THREE.KeyframeTrack.call( this, name, times, values, interpolation ); - _context.beginPath(); - _context.moveTo( v1.positionScreen.x, v1.positionScreen.y ); - _context.lineTo( v2.positionScreen.x, v2.positionScreen.y ); +}; - if ( material instanceof THREE.LineBasicMaterial ) { +THREE.QuaternionKeyframeTrack.prototype = + Object.assign( Object.create( THREE.KeyframeTrack.prototype ), { - setLineWidth( material.linewidth ); - setLineCap( material.linecap ); - setLineJoin( material.linejoin ); + constructor: THREE.QuaternionKeyframeTrack, - if ( material.vertexColors !== THREE.VertexColors ) { + ValueTypeName: 'quaternion', - setStrokeStyle( material.color.getStyle() ); + // ValueBufferType is inherited - } else { + DefaultInterpolation: THREE.InterpolateLinear, - var colorStyle1 = element.vertexColors[0].getStyle(); - var colorStyle2 = element.vertexColors[1].getStyle(); + InterpolantFactoryMethodLinear: function( result ) { - if ( colorStyle1 === colorStyle2 ) { + return new THREE.QuaternionLinearInterpolant( + this.times, this.values, this.getValueSize(), result ); - setStrokeStyle( colorStyle1 ); + }, - } else { + InterpolantFactoryMethodSmooth: undefined // not yet implemented - try { +} ); - var grad = _context.createLinearGradient( - v1.positionScreen.x, - v1.positionScreen.y, - v2.positionScreen.x, - v2.positionScreen.y - ); - grad.addColorStop( 0, colorStyle1 ); - grad.addColorStop( 1, colorStyle2 ); +// File:src/animation/tracks/StringKeyframeTrack.js - } catch ( exception ) { +/** + * + * A Track that interpolates Strings + * + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ - grad = colorStyle1; +THREE.StringKeyframeTrack = function ( name, times, values, interpolation ) { - } + THREE.KeyframeTrack.call( this, name, times, values, interpolation ); - setStrokeStyle( grad ); +}; - } +THREE.StringKeyframeTrack.prototype = + Object.assign( Object.create( THREE.KeyframeTrack.prototype ), { - } + constructor: THREE.StringKeyframeTrack, - _context.stroke(); - _elemBox.expandByScalar( material.linewidth * 2 ); + ValueTypeName: 'string', + ValueBufferType: Array, - } else if ( material instanceof THREE.LineDashedMaterial ) { + DefaultInterpolation: THREE.InterpolateDiscrete, - setLineWidth( material.linewidth ); - setLineCap( material.linecap ); - setLineJoin( material.linejoin ); - setStrokeStyle( material.color.getStyle() ); - setDashAndGap( material.dashSize, material.gapSize ); + InterpolantFactoryMethodLinear: undefined, - _context.stroke(); + InterpolantFactoryMethodSmooth: undefined - _elemBox.expandByScalar( material.linewidth * 2 ); +} ); - setDashAndGap( null, null ); +// File:src/animation/tracks/VectorKeyframeTrack.js - } +/** + * + * A Track of vectored keyframe values. + * + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ - } +THREE.VectorKeyframeTrack = function ( name, times, values, interpolation ) { - function renderFace3( v1, v2, v3, uv1, uv2, uv3, element, material ) { + THREE.KeyframeTrack.call( this, name, times, values, interpolation ); - _this.info.render.vertices += 3; - _this.info.render.faces ++; +}; - setOpacity( material.opacity ); - setBlending( material.blending ); +THREE.VectorKeyframeTrack.prototype = + Object.assign( Object.create( THREE.KeyframeTrack.prototype ), { - _v1x = v1.positionScreen.x; _v1y = v1.positionScreen.y; - _v2x = v2.positionScreen.x; _v2y = v2.positionScreen.y; - _v3x = v3.positionScreen.x; _v3y = v3.positionScreen.y; + constructor: THREE.VectorKeyframeTrack, - drawTriangle( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y ); + ValueTypeName: 'vector' - if ( ( material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) && material.map === null ) { + // ValueBufferType is inherited - _diffuseColor.copy( material.color ); - _emissiveColor.copy( material.emissive ); + // DefaultInterpolation is inherited - if ( material.vertexColors === THREE.FaceColors ) { +} ); - _diffuseColor.multiply( element.color ); +// File:src/audio/Audio.js - } +/** + * @author mrdoob / http://mrdoob.com/ + * @author Reece Aaron Lecrivain / http://reecenotes.com/ + */ - if ( material.wireframe === false && material.shading == THREE.SmoothShading && element.vertexNormalsLength == 3 ) { +THREE.Audio = function ( listener ) { - _color1.copy( _ambientLight ); - _color2.copy( _ambientLight ); - _color3.copy( _ambientLight ); + THREE.Object3D.call( this ); - calculateLight( element.v1.positionWorld, element.vertexNormalsModel[ 0 ], _color1 ); - calculateLight( element.v2.positionWorld, element.vertexNormalsModel[ 1 ], _color2 ); - calculateLight( element.v3.positionWorld, element.vertexNormalsModel[ 2 ], _color3 ); + this.type = 'Audio'; - _color1.multiply( _diffuseColor ).add( _emissiveColor ); - _color2.multiply( _diffuseColor ).add( _emissiveColor ); - _color3.multiply( _diffuseColor ).add( _emissiveColor ); - _color4.addColors( _color2, _color3 ).multiplyScalar( 0.5 ); + this.context = listener.context; + this.source = this.context.createBufferSource(); + this.source.onended = this.onEnded.bind( this ); - _image = getGradientTexture( _color1, _color2, _color3, _color4 ); + this.gain = this.context.createGain(); + this.gain.connect( listener.getInput() ); - clipImage( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, 0, 0, 1, 0, 0, 1, _image ); + this.autoplay = false; - } else { + this.startTime = 0; + this.playbackRate = 1; + this.isPlaying = false; + this.hasPlaybackControl = true; + this.sourceType = 'empty'; - _color.copy( _ambientLight ); + this.filters = []; - calculateLight( element.centroidModel, element.normalModel, _color ); +}; - _color.multiply( _diffuseColor ).add( _emissiveColor ); +THREE.Audio.prototype = Object.assign( Object.create( THREE.Object3D.prototype ), { - material.wireframe === true - ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) - : fillPath( _color ); + constructor: THREE.Audio, - } + getOutput: function () { - } else if ( material instanceof THREE.MeshBasicMaterial || material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) { + return this.gain; - if ( material.map !== null ) { + }, - if ( material.map.mapping instanceof THREE.UVMapping ) { + setNodeSource: function ( audioNode ) { - _uvs = element.uvs[ 0 ]; - patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uvs[ uv1 ].x, _uvs[ uv1 ].y, _uvs[ uv2 ].x, _uvs[ uv2 ].y, _uvs[ uv3 ].x, _uvs[ uv3 ].y, material.map ); + this.hasPlaybackControl = false; + this.sourceType = 'audioNode'; + this.source = audioNode; + this.connect(); - } + return this; + + }, + setBuffer: function ( audioBuffer ) { - } else if ( material.envMap !== null ) { + this.source.buffer = audioBuffer; + this.sourceType = 'buffer'; - if ( material.envMap.mapping instanceof THREE.SphericalReflectionMapping ) { + if ( this.autoplay ) this.play(); - _vector3.copy( element.vertexNormalsModelView[ uv1 ] ); - _uv1x = 0.5 * _vector3.x + 0.5; - _uv1y = 0.5 * _vector3.y + 0.5; + return this; - _vector3.copy( element.vertexNormalsModelView[ uv2 ] ); - _uv2x = 0.5 * _vector3.x + 0.5; - _uv2y = 0.5 * _vector3.y + 0.5; + }, - _vector3.copy( element.vertexNormalsModelView[ uv3 ] ); - _uv3x = 0.5 * _vector3.x + 0.5; - _uv3y = 0.5 * _vector3.y + 0.5; + play: function () { - patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, material.envMap ); + if ( this.isPlaying === true ) { - }/* else if ( material.envMap.mapping == THREE.SphericalRefractionMapping ) { + console.warn( 'THREE.Audio: Audio is already playing.' ); + return; + } + if ( this.hasPlaybackControl === false ) { - }*/ + console.warn( 'THREE.Audio: this Audio has no playback control.' ); + return; + } - } else { + var source = this.context.createBufferSource(); - _color.copy( material.color ); + 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; - if ( material.vertexColors === THREE.FaceColors ) { + this.isPlaying = true; - _color.multiply( element.color ); + this.source = source; - } + return this.connect(); - material.wireframe === true - ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) - : fillPath( _color ); + }, - } + pause: function () { - } else if ( material instanceof THREE.MeshDepthMaterial ) { + if ( this.hasPlaybackControl === false ) { - _near = _camera.near; - _far = _camera.far; + console.warn( 'THREE.Audio: this Audio has no playback control.' ); + return; - _color1.r = _color1.g = _color1.b = 1 - smoothstep( v1.positionScreen.z * v1.positionScreen.w, _near, _far ); - _color2.r = _color2.g = _color2.b = 1 - smoothstep( v2.positionScreen.z * v2.positionScreen.w, _near, _far ); - _color3.r = _color3.g = _color3.b = 1 - smoothstep( v3.positionScreen.z * v3.positionScreen.w, _near, _far ); - _color4.addColors( _color2, _color3 ).multiplyScalar( 0.5 ); + } - _image = getGradientTexture( _color1, _color2, _color3, _color4 ); + this.source.stop(); + this.startTime = this.context.currentTime; + this.isPlaying = false; - clipImage( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, 0, 0, 1, 0, 0, 1, _image ); + return this; - } else if ( material instanceof THREE.MeshNormalMaterial ) { + }, - var normal; + stop: function () { - if ( material.shading == THREE.FlatShading ) { + if ( this.hasPlaybackControl === false ) { - normal = element.normalModelView; + console.warn( 'THREE.Audio: this Audio has no playback control.' ); + return; - _color.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 ); + } - material.wireframe === true - ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) - : fillPath( _color ); + this.source.stop(); + this.startTime = 0; + this.isPlaying = false; - } else if ( material.shading == THREE.SmoothShading ) { + return this; - normal = element.vertexNormalsModelView[ uv1 ]; - _color1.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 ); + }, - normal = element.vertexNormalsModelView[ uv2 ]; - _color2.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 ); + connect: function () { - normal = element.vertexNormalsModelView[ uv3 ]; - _color3.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 ); + if ( this.filters.length > 0 ) { - _color4.addColors( _color2, _color3 ).multiplyScalar( 0.5 ); + this.source.connect( this.filters[ 0 ] ); - _image = getGradientTexture( _color1, _color2, _color3, _color4 ); + for ( var i = 1, l = this.filters.length; i < l; i ++ ) { - clipImage( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, 0, 0, 1, 0, 0, 1, _image ); + this.filters[ i - 1 ].connect( this.filters[ i ] ); } - } + this.filters[ this.filters.length - 1 ].connect( this.getOutput() ); - } + } else { - // + this.source.connect( this.getOutput() ); - function drawTriangle( x0, y0, x1, y1, x2, y2 ) { + } - _context.beginPath(); - _context.moveTo( x0, y0 ); - _context.lineTo( x1, y1 ); - _context.lineTo( x2, y2 ); - _context.closePath(); + return this; - } + }, - function strokePath( color, linewidth, linecap, linejoin ) { + disconnect: function () { - setLineWidth( linewidth ); - setLineCap( linecap ); - setLineJoin( linejoin ); - setStrokeStyle( color.getStyle() ); + if ( this.filters.length > 0 ) { - _context.stroke(); + this.source.disconnect( this.filters[ 0 ] ); - _elemBox.expandByScalar( linewidth * 2 ); + for ( var i = 1, l = this.filters.length; i < l; i ++ ) { - } + this.filters[ i - 1 ].disconnect( this.filters[ i ] ); - function fillPath( color ) { + } - setFillStyle( color.getStyle() ); - _context.fill(); + this.filters[ this.filters.length - 1 ].disconnect( this.getOutput() ); - } + } else { - function patternPath( x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2, texture ) { + this.source.disconnect( this.getOutput() ); - if ( texture instanceof THREE.DataTexture || texture.image === undefined || texture.image.width == 0 ) return; + } - if ( texture.needsUpdate === true ) { + return this; - var repeatX = texture.wrapS == THREE.RepeatWrapping; - var repeatY = texture.wrapT == THREE.RepeatWrapping; + }, - _patterns[ texture.id ] = _context.createPattern( - texture.image, repeatX === true && repeatY === true - ? 'repeat' - : repeatX === true && repeatY === false - ? 'repeat-x' - : repeatX === false && repeatY === true - ? 'repeat-y' - : 'no-repeat' - ); + getFilters: function () { - texture.needsUpdate = false; + return this.filters; - } + }, - _patterns[ texture.id ] === undefined - ? setFillStyle( 'rgba(0,0,0,1)' ) - : setFillStyle( _patterns[ texture.id ] ); + setFilters: function ( value ) { - // http://extremelysatisfactorytotalitarianism.com/blog/?p=2120 + if ( ! value ) value = []; - var a, b, c, d, e, f, det, idet, - offsetX = texture.offset.x / texture.repeat.x, - offsetY = texture.offset.y / texture.repeat.y, - width = texture.image.width * texture.repeat.x, - height = texture.image.height * texture.repeat.y; + if ( this.isPlaying === true ) { - u0 = ( u0 + offsetX ) * width; - v0 = ( 1.0 - v0 + offsetY ) * height; + this.disconnect(); + this.filters = value; + this.connect(); - u1 = ( u1 + offsetX ) * width; - v1 = ( 1.0 - v1 + offsetY ) * height; + } else { - u2 = ( u2 + offsetX ) * width; - v2 = ( 1.0 - v2 + offsetY ) * height; + this.filters = value; - x1 -= x0; y1 -= y0; - x2 -= x0; y2 -= y0; + } - u1 -= u0; v1 -= v0; - u2 -= u0; v2 -= v0; + return this; - det = u1 * v2 - u2 * v1; + }, - if ( det === 0 ) { + getFilter: function () { - if ( _imagedatas[ texture.id ] === undefined ) { + return this.getFilters()[ 0 ]; - var canvas = document.createElement( 'canvas' ) - canvas.width = texture.image.width; - canvas.height = texture.image.height; + }, - var context = canvas.getContext( '2d' ); - context.drawImage( texture.image, 0, 0 ); + setFilter: function ( filter ) { - _imagedatas[ texture.id ] = context.getImageData( 0, 0, texture.image.width, texture.image.height ).data; + return this.setFilters( filter ? [ filter ] : [] ); - } + }, - var data = _imagedatas[ texture.id ]; - var index = ( Math.floor( u0 ) + Math.floor( v0 ) * texture.image.width ) * 4; + setPlaybackRate: function ( value ) { - _color.setRGB( data[ index ] / 255, data[ index + 1 ] / 255, data[ index + 2 ] / 255 ); - fillPath( _color ); + if ( this.hasPlaybackControl === false ) { + console.warn( 'THREE.Audio: this Audio has no playback control.' ); return; } - idet = 1 / det; - - a = ( v2 * x1 - v1 * x2 ) * idet; - b = ( v2 * y1 - v1 * y2 ) * idet; - c = ( u1 * x2 - u2 * x1 ) * idet; - d = ( u1 * y2 - u2 * y1 ) * idet; + this.playbackRate = value; - e = x0 - a * u0 - c * v0; - f = y0 - b * u0 - d * v0; + if ( this.isPlaying === true ) { - _context.save(); - _context.transform( a, b, c, d, e, f ); - _context.fill(); - _context.restore(); + this.source.playbackRate.value = this.playbackRate; - } - - function clipImage( x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2, image ) { + } - // http://extremelysatisfactorytotalitarianism.com/blog/?p=2120 + return this; - var a, b, c, d, e, f, det, idet, - width = image.width - 1, - height = image.height - 1; + }, - u0 *= width; v0 *= height; - u1 *= width; v1 *= height; - u2 *= width; v2 *= height; + getPlaybackRate: function () { - x1 -= x0; y1 -= y0; - x2 -= x0; y2 -= y0; + return this.playbackRate; - u1 -= u0; v1 -= v0; - u2 -= u0; v2 -= v0; + }, - det = u1 * v2 - u2 * v1; + onEnded: function () { - idet = 1 / det; + this.isPlaying = false; - a = ( v2 * x1 - v1 * x2 ) * idet; - b = ( v2 * y1 - v1 * y2 ) * idet; - c = ( u1 * x2 - u2 * x1 ) * idet; - d = ( u1 * y2 - u2 * y1 ) * idet; + }, - e = x0 - a * u0 - c * v0; - f = y0 - b * u0 - d * v0; + getLoop: function () { - _context.save(); - _context.transform( a, b, c, d, e, f ); - _context.clip(); - _context.drawImage( image, 0, 0 ); - _context.restore(); + if ( this.hasPlaybackControl === false ) { - } + console.warn( 'THREE.Audio: this Audio has no playback control.' ); + return false; - function getGradientTexture( color1, color2, color3, color4 ) { + } - // http://mrdoob.com/blog/post/710 + return this.source.loop; - _pixelMapData[ 0 ] = ( color1.r * 255 ) | 0; - _pixelMapData[ 1 ] = ( color1.g * 255 ) | 0; - _pixelMapData[ 2 ] = ( color1.b * 255 ) | 0; + }, - _pixelMapData[ 4 ] = ( color2.r * 255 ) | 0; - _pixelMapData[ 5 ] = ( color2.g * 255 ) | 0; - _pixelMapData[ 6 ] = ( color2.b * 255 ) | 0; + setLoop: function ( value ) { - _pixelMapData[ 8 ] = ( color3.r * 255 ) | 0; - _pixelMapData[ 9 ] = ( color3.g * 255 ) | 0; - _pixelMapData[ 10 ] = ( color3.b * 255 ) | 0; + if ( this.hasPlaybackControl === false ) { - _pixelMapData[ 12 ] = ( color4.r * 255 ) | 0; - _pixelMapData[ 13 ] = ( color4.g * 255 ) | 0; - _pixelMapData[ 14 ] = ( color4.b * 255 ) | 0; + console.warn( 'THREE.Audio: this Audio has no playback control.' ); + return; - _pixelMapContext.putImageData( _pixelMapImage, 0, 0 ); - _gradientMapContext.drawImage( _pixelMap, 0, 0 ); + } - return _gradientMap; + this.source.loop = value; - } + }, - // Hide anti-alias gaps + getVolume: function () { - function expand( v1, v2, pixels ) { + return this.gain.gain.value; - var x = v2.x - v1.x, y = v2.y - v1.y, - det = x * x + y * y, idet; + }, - if ( det === 0 ) return; - idet = pixels / Math.sqrt( det ); + setVolume: function ( value ) { - x *= idet; y *= idet; + this.gain.gain.value = value; - v2.x += x; v2.y += y; - v1.x -= x; v1.y -= y; + return this; } - // Context cached methods. +} ); + +// File:src/audio/AudioAnalyser.js - function setOpacity( value ) { +/** + * @author mrdoob / http://mrdoob.com/ + */ - if ( _contextGlobalAlpha !== value ) { +THREE.AudioAnalyser = function ( audio, fftSize ) { - _context.globalAlpha = value; - _contextGlobalAlpha = value; + this.analyser = audio.context.createAnalyser(); + this.analyser.fftSize = fftSize !== undefined ? fftSize : 2048; - } + this.data = new Uint8Array( this.analyser.frequencyBinCount ); - } + audio.getOutput().connect( this.analyser ); - function setBlending( value ) { +}; - if ( _contextGlobalCompositeOperation !== value ) { +Object.assign( THREE.AudioAnalyser.prototype, { - if ( value === THREE.NormalBlending ) { + getFrequencyData: function () { - _context.globalCompositeOperation = 'source-over'; + this.analyser.getByteFrequencyData( this.data ); - } else if ( value === THREE.AdditiveBlending ) { + return this.data; - _context.globalCompositeOperation = 'lighter'; + }, - } else if ( value === THREE.SubtractiveBlending ) { + getAverageFrequency: function () { - _context.globalCompositeOperation = 'darker'; + var value = 0, data = this.getFrequencyData(); - } + for ( var i = 0; i < data.length; i ++ ) { - _contextGlobalCompositeOperation = value; + value += data[ i ]; } + return value / data.length; + } - function setLineWidth( value ) { +} ); - if ( _contextLineWidth !== value ) { +// File:src/audio/AudioContext.js - _context.lineWidth = value; - _contextLineWidth = value; +/** + * @author mrdoob / http://mrdoob.com/ + */ - } +Object.defineProperty( THREE, 'AudioContext', { - } + get: ( function () { - function setLineCap( value ) { + var context; - // "butt", "round", "square" + return function get() { - if ( _contextLineCap !== value ) { + if ( context === undefined ) { - _context.lineCap = value; - _contextLineCap = value; + context = new ( window.AudioContext || window.webkitAudioContext )(); - } + } - } + return context; - function setLineJoin( value ) { + }; - // "round", "bevel", "miter" + } )() - if ( _contextLineJoin !== value ) { +} ); - _context.lineJoin = value; - _contextLineJoin = value; +// File:src/audio/PositionalAudio.js - } - - } - - function setStrokeStyle( value ) { +/** + * @author mrdoob / http://mrdoob.com/ + */ - if ( _contextStrokeStyle !== value ) { +THREE.PositionalAudio = function ( listener ) { - _context.strokeStyle = value; - _contextStrokeStyle = value; + THREE.Audio.call( this, listener ); - } + this.panner = this.context.createPanner(); + this.panner.connect( this.gain ); - } +}; - function setFillStyle( value ) { +THREE.PositionalAudio.prototype = Object.assign( Object.create( THREE.Audio.prototype ), { - if ( _contextFillStyle !== value ) { + constructor: THREE.PositionalAudio, - _context.fillStyle = value; - _contextFillStyle = value; + getOutput: function () { - } + return this.panner; - } + }, - function setDashAndGap( dashSizeValue, gapSizeValue ) { + getRefDistance: function () { - if ( _contextDashSize !== dashSizeValue || _contextGapSize !== gapSizeValue ) { + return this.panner.refDistance; - _context.setLineDash( [ dashSizeValue, gapSizeValue ] ); - _contextDashSize = dashSizeValue; - _contextGapSize = gapSizeValue; + }, - } + setRefDistance: function ( value ) { - } + this.panner.refDistance = value; -}; + }, -/** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - * @author mikael emtinger / http://gomo.se/ - */ + getRolloffFactor: function () { -THREE.ShaderChunk = { + return this.panner.rolloffFactor; - // FOG + }, - fog_pars_fragment: [ + setRolloffFactor: function ( value ) { - "#ifdef USE_FOG", + this.panner.rolloffFactor = value; - "uniform vec3 fogColor;", + }, - "#ifdef FOG_EXP2", + getDistanceModel: function () { - "uniform float fogDensity;", + return this.panner.distanceModel; - "#else", + }, - "uniform float fogNear;", - "uniform float fogFar;", + setDistanceModel: function ( value ) { - "#endif", + this.panner.distanceModel = value; - "#endif" + }, - ].join("\n"), + getMaxDistance: function () { - fog_fragment: [ + return this.panner.maxDistance; - "#ifdef USE_FOG", + }, - "float depth = gl_FragCoord.z / gl_FragCoord.w;", + setMaxDistance: function ( value ) { - "#ifdef FOG_EXP2", + this.panner.maxDistance = value; - "const float LOG2 = 1.442695;", - "float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );", - "fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );", + }, - "#else", + updateMatrixWorld: ( function () { - "float fogFactor = smoothstep( fogNear, fogFar, depth );", + var position = new THREE.Vector3(); - "#endif", + return function updateMatrixWorld( force ) { - "gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );", + THREE.Object3D.prototype.updateMatrixWorld.call( this, force ); - "#endif" + position.setFromMatrixPosition( this.matrixWorld ); - ].join("\n"), + this.panner.setPosition( position.x, position.y, position.z ); - // ENVIRONMENT MAP + }; - envmap_pars_fragment: [ + } )() - "#ifdef USE_ENVMAP", - "uniform float reflectivity;", - "uniform samplerCube envMap;", - "uniform float flipEnvMap;", - "uniform int combine;", +} ); - "#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )", +// File:src/audio/AudioListener.js - "uniform bool useRefract;", - "uniform float refractionRatio;", +/** + * @author mrdoob / http://mrdoob.com/ + */ - "#else", +THREE.AudioListener = function () { - "varying vec3 vReflect;", + THREE.Object3D.call( this ); - "#endif", + this.type = 'AudioListener'; - "#endif" + this.context = THREE.AudioContext; - ].join("\n"), + this.gain = this.context.createGain(); + this.gain.connect( this.context.destination ); - envmap_fragment: [ + this.filter = null; - "#ifdef USE_ENVMAP", +}; - "vec3 reflectVec;", +THREE.AudioListener.prototype = Object.assign( Object.create( THREE.Object3D.prototype ), { - "#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )", + constructor: THREE.AudioListener, - "vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );", + getInput: function () { - "if ( useRefract ) {", + return this.gain; - "reflectVec = refract( cameraToVertex, normal, refractionRatio );", + }, - "} else { ", + removeFilter: function ( ) { - "reflectVec = reflect( cameraToVertex, normal );", + if ( this.filter !== null ) { - "}", + this.gain.disconnect( this.filter ); + this.filter.disconnect( this.context.destination ); + this.gain.connect( this.context.destination ); + this.filter = null; - "#else", + } - "reflectVec = vReflect;", + }, - "#endif", + getFilter: function () { - "#ifdef DOUBLE_SIDED", + return this.filter; - "float flipNormal = ( -1.0 + 2.0 * float( gl_FrontFacing ) );", - "vec4 cubeColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );", + }, - "#else", + setFilter: function ( value ) { - "vec4 cubeColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );", + if ( this.filter !== null ) { - "#endif", + this.gain.disconnect( this.filter ); + this.filter.disconnect( this.context.destination ); - "#ifdef GAMMA_INPUT", + } else { - "cubeColor.xyz *= cubeColor.xyz;", + this.gain.disconnect( this.context.destination ); - "#endif", + } - "if ( combine == 1 ) {", + this.filter = value; + this.gain.connect( this.filter ); + this.filter.connect( this.context.destination ); - "gl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularStrength * reflectivity );", + }, - "} else if ( combine == 2 ) {", + getMasterVolume: function () { - "gl_FragColor.xyz += cubeColor.xyz * specularStrength * reflectivity;", + return this.gain.gain.value; - "} else {", + }, - "gl_FragColor.xyz = mix( gl_FragColor.xyz, gl_FragColor.xyz * cubeColor.xyz, specularStrength * reflectivity );", + setMasterVolume: function ( value ) { - "}", + this.gain.gain.value = value; - "#endif" + }, - ].join("\n"), + updateMatrixWorld: ( function () { - envmap_pars_vertex: [ + var position = new THREE.Vector3(); + var quaternion = new THREE.Quaternion(); + var scale = new THREE.Vector3(); - "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP )", + var orientation = new THREE.Vector3(); - "varying vec3 vReflect;", + return function updateMatrixWorld( force ) { - "uniform float refractionRatio;", - "uniform bool useRefract;", + THREE.Object3D.prototype.updateMatrixWorld.call( this, force ); - "#endif" + var listener = this.context.listener; + var up = this.up; - ].join("\n"), + this.matrixWorld.decompose( position, quaternion, scale ); - worldpos_vertex : [ + orientation.set( 0, 0, - 1 ).applyQuaternion( quaternion ); - "#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )", + listener.setPosition( position.x, position.y, position.z ); + listener.setOrientation( orientation.x, orientation.y, orientation.z, up.x, up.y, up.z ); - "#ifdef USE_SKINNING", + }; - "vec4 worldPosition = modelMatrix * skinned;", + } )() - "#endif", +} ); - "#if defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )", +// File:src/cameras/Camera.js - "vec4 worldPosition = modelMatrix * vec4( morphed, 1.0 );", +/** + * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + * @author WestLangley / http://github.com/WestLangley +*/ - "#endif", +THREE.Camera = function () { - "#if ! defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )", + THREE.Object3D.call( this ); - "vec4 worldPosition = modelMatrix * vec4( position, 1.0 );", + this.type = 'Camera'; - "#endif", + this.matrixWorldInverse = new THREE.Matrix4(); + this.projectionMatrix = new THREE.Matrix4(); - "#endif" +}; - ].join("\n"), +THREE.Camera.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Camera.prototype.constructor = THREE.Camera; - envmap_vertex : [ +THREE.Camera.prototype.getWorldDirection = function () { - "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP )", + var quaternion = new THREE.Quaternion(); - "vec3 worldNormal = mat3( modelMatrix[ 0 ].xyz, modelMatrix[ 1 ].xyz, modelMatrix[ 2 ].xyz ) * objectNormal;", - "worldNormal = normalize( worldNormal );", + return function getWorldDirection( optionalTarget ) { - "vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );", + var result = optionalTarget || new THREE.Vector3(); - "if ( useRefract ) {", + this.getWorldQuaternion( quaternion ); - "vReflect = refract( cameraToVertex, worldNormal, refractionRatio );", + return result.set( 0, 0, - 1 ).applyQuaternion( quaternion ); - "} else {", + }; - "vReflect = reflect( cameraToVertex, worldNormal );", +}(); - "}", +THREE.Camera.prototype.lookAt = function () { - "#endif" + // This routine does not support cameras with rotated and/or translated parent(s) - ].join("\n"), + var m1 = new THREE.Matrix4(); - // COLOR MAP (particles) + return function lookAt( vector ) { - map_particle_pars_fragment: [ + m1.lookAt( this.position, vector, this.up ); - "#ifdef USE_MAP", + this.quaternion.setFromRotationMatrix( m1 ); - "uniform sampler2D map;", + }; - "#endif" +}(); - ].join("\n"), +THREE.Camera.prototype.clone = function () { + return new this.constructor().copy( this ); - map_particle_fragment: [ +}; - "#ifdef USE_MAP", +THREE.Camera.prototype.copy = function ( source ) { - "gl_FragColor = gl_FragColor * texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) );", + THREE.Object3D.prototype.copy.call( this, source ); - "#endif" + this.matrixWorldInverse.copy( source.matrixWorldInverse ); + this.projectionMatrix.copy( source.projectionMatrix ); - ].join("\n"), + return this; - // COLOR MAP (triangles) +}; - map_pars_vertex: [ +// File:src/cameras/CubeCamera.js - "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP )", +/** + * Camera for rendering cube maps + * - renders scene into axis-aligned cube + * + * @author alteredq / http://alteredqualia.com/ + */ - "varying vec2 vUv;", - "uniform vec4 offsetRepeat;", +THREE.CubeCamera = function ( near, far, cubeResolution ) { - "#endif" + THREE.Object3D.call( this ); - ].join("\n"), + this.type = 'CubeCamera'; - map_pars_fragment: [ + var fov = 90, aspect = 1; - "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP )", + 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 ); - "varying vec2 vUv;", + 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 ); - "#endif", + 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 ); - "#ifdef USE_MAP", + 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 ); - "uniform sampler2D map;", + 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 ); - "#endif" + 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 ); - ].join("\n"), + var options = { format: THREE.RGBFormat, magFilter: THREE.LinearFilter, minFilter: THREE.LinearFilter }; - map_vertex: [ + this.renderTarget = new THREE.WebGLRenderTargetCube( cubeResolution, cubeResolution, options ); - "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP )", + this.updateCubeMap = function ( renderer, scene ) { - "vUv = uv * offsetRepeat.zw + offsetRepeat.xy;", + if ( this.parent === null ) this.updateMatrixWorld(); - "#endif" + var renderTarget = this.renderTarget; + var generateMipmaps = renderTarget.texture.generateMipmaps; - ].join("\n"), + renderTarget.texture.generateMipmaps = false; - map_fragment: [ + renderTarget.activeCubeFace = 0; + renderer.render( scene, cameraPX, renderTarget ); - "#ifdef USE_MAP", + renderTarget.activeCubeFace = 1; + renderer.render( scene, cameraNX, renderTarget ); - "vec4 texelColor = texture2D( map, vUv );", + renderTarget.activeCubeFace = 2; + renderer.render( scene, cameraPY, renderTarget ); - "#ifdef GAMMA_INPUT", + renderTarget.activeCubeFace = 3; + renderer.render( scene, cameraNY, renderTarget ); - "texelColor.xyz *= texelColor.xyz;", + renderTarget.activeCubeFace = 4; + renderer.render( scene, cameraPZ, renderTarget ); - "#endif", + renderTarget.texture.generateMipmaps = generateMipmaps; - "gl_FragColor = gl_FragColor * texelColor;", + renderTarget.activeCubeFace = 5; + renderer.render( scene, cameraNZ, renderTarget ); - "#endif" + renderer.setRenderTarget( null ); - ].join("\n"), + }; - // LIGHT MAP +}; - lightmap_pars_fragment: [ +THREE.CubeCamera.prototype = Object.create( THREE.Object3D.prototype ); +THREE.CubeCamera.prototype.constructor = THREE.CubeCamera; - "#ifdef USE_LIGHTMAP", +// File:src/cameras/OrthographicCamera.js - "varying vec2 vUv2;", - "uniform sampler2D lightMap;", +/** + * @author alteredq / http://alteredqualia.com/ + * @author arose / http://github.com/arose + */ - "#endif" +THREE.OrthographicCamera = function ( left, right, top, bottom, near, far ) { - ].join("\n"), + THREE.Camera.call( this ); - lightmap_pars_vertex: [ + this.type = 'OrthographicCamera'; - "#ifdef USE_LIGHTMAP", + this.zoom = 1; + this.view = null; - "varying vec2 vUv2;", + this.left = left; + this.right = right; + this.top = top; + this.bottom = bottom; - "#endif" + this.near = ( near !== undefined ) ? near : 0.1; + this.far = ( far !== undefined ) ? far : 2000; - ].join("\n"), + this.updateProjectionMatrix(); - lightmap_fragment: [ +}; - "#ifdef USE_LIGHTMAP", +THREE.OrthographicCamera.prototype = Object.assign( Object.create( THREE.Camera.prototype ), { - "gl_FragColor = gl_FragColor * texture2D( lightMap, vUv2 );", + constructor: THREE.OrthographicCamera, - "#endif" + copy: function ( source ) { - ].join("\n"), + THREE.Camera.prototype.copy.call( this, source ); - lightmap_vertex: [ + this.left = source.left; + this.right = source.right; + this.top = source.top; + this.bottom = source.bottom; + this.near = source.near; + this.far = source.far; - "#ifdef USE_LIGHTMAP", + this.zoom = source.zoom; + this.view = source.view === null ? null : Object.assign( {}, source.view ); - "vUv2 = uv2;", + return this; - "#endif" + }, - ].join("\n"), + setViewOffset: function( fullWidth, fullHeight, x, y, width, height ) { - // BUMP MAP + this.view = { + fullWidth: fullWidth, + fullHeight: fullHeight, + offsetX: x, + offsetY: y, + width: width, + height: height + }; - bumpmap_pars_fragment: [ + this.updateProjectionMatrix(); - "#ifdef USE_BUMPMAP", + }, - "uniform sampler2D bumpMap;", - "uniform float bumpScale;", + clearViewOffset: function() { - // Derivative maps - bump mapping unparametrized surfaces by Morten Mikkelsen - // http://mmikkelsen3d.blogspot.sk/2011/07/derivative-maps.html + this.view = null; + this.updateProjectionMatrix(); - // Evaluate the derivative of the height w.r.t. screen-space using forward differencing (listing 2) + }, - "vec2 dHdxy_fwd() {", + updateProjectionMatrix: function () { - "vec2 dSTdx = dFdx( vUv );", - "vec2 dSTdy = dFdy( vUv );", + 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; - "float Hll = bumpScale * texture2D( bumpMap, vUv ).x;", - "float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;", - "float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;", + var left = cx - dx; + var right = cx + dx; + var top = cy + dy; + var bottom = cy - dy; - "return vec2( dBx, dBy );", + if ( this.view !== null ) { - "}", + var zoomW = this.zoom / ( this.view.width / this.view.fullWidth ); + var zoomH = this.zoom / ( this.view.height / this.view.fullHeight ); + var scaleW = ( this.right - this.left ) / this.view.width; + var scaleH = ( this.top - this.bottom ) / this.view.height; - "vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {", + left += scaleW * ( this.view.offsetX / zoomW ); + right = left + scaleW * ( this.view.width / zoomW ); + top -= scaleH * ( this.view.offsetY / zoomH ); + bottom = top - scaleH * ( this.view.height / zoomH ); - "vec3 vSigmaX = dFdx( surf_pos );", - "vec3 vSigmaY = dFdy( surf_pos );", - "vec3 vN = surf_norm;", // normalized + } - "vec3 R1 = cross( vSigmaY, vN );", - "vec3 R2 = cross( vN, vSigmaX );", + this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far ); - "float fDet = dot( vSigmaX, R1 );", + }, - "vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );", - "return normalize( abs( fDet ) * surf_norm - vGrad );", + toJSON: function ( meta ) { - "}", + var data = THREE.Object3D.prototype.toJSON.call( this, meta ); - "#endif" + 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; - ].join("\n"), + if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); - // NORMAL MAP + return data; - normalmap_pars_fragment: [ + } - "#ifdef USE_NORMALMAP", +} ); - "uniform sampler2D normalMap;", - "uniform vec2 normalScale;", +// File:src/cameras/PerspectiveCamera.js - // Per-Pixel Tangent Space Normal Mapping - // http://hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping.html +/** + * @author mrdoob / http://mrdoob.com/ + * @author greggman / http://games.greggman.com/ + * @author zz85 / http://www.lab4games.net/zz85/blog + * @author tschw + */ - "vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {", +THREE.PerspectiveCamera = function ( fov, aspect, near, far ) { - "vec3 q0 = dFdx( eye_pos.xyz );", - "vec3 q1 = dFdy( eye_pos.xyz );", - "vec2 st0 = dFdx( vUv.st );", - "vec2 st1 = dFdy( vUv.st );", + THREE.Camera.call( this ); - "vec3 S = normalize( q0 * st1.t - q1 * st0.t );", - "vec3 T = normalize( -q0 * st1.s + q1 * st0.s );", - "vec3 N = normalize( surf_norm );", + this.type = 'PerspectiveCamera'; - "vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;", - "mapN.xy = normalScale * mapN.xy;", - "mat3 tsn = mat3( S, T, N );", - "return normalize( tsn * mapN );", + this.fov = fov !== undefined ? fov : 50; + this.zoom = 1; - "}", + this.near = near !== undefined ? near : 0.1; + this.far = far !== undefined ? far : 2000; + this.focus = 10; - "#endif" + this.aspect = aspect !== undefined ? aspect : 1; + this.view = null; - ].join("\n"), + this.filmGauge = 35; // width of the film (default in millimeters) + this.filmOffset = 0; // horizontal film offset (same unit as gauge) - // SPECULAR MAP + this.updateProjectionMatrix(); - specularmap_pars_fragment: [ +}; - "#ifdef USE_SPECULARMAP", +THREE.PerspectiveCamera.prototype = Object.assign( Object.create( THREE.Camera.prototype ), { - "uniform sampler2D specularMap;", + constructor: THREE.PerspectiveCamera, - "#endif" + copy: function ( source ) { - ].join("\n"), + THREE.Camera.prototype.copy.call( this, source ); - specularmap_fragment: [ + this.fov = source.fov; + this.zoom = source.zoom; - "float specularStrength;", + this.near = source.near; + this.far = source.far; + this.focus = source.focus; - "#ifdef USE_SPECULARMAP", + this.aspect = source.aspect; + this.view = source.view === null ? null : Object.assign( {}, source.view ); - "vec4 texelSpecular = texture2D( specularMap, vUv );", - "specularStrength = texelSpecular.r;", + this.filmGauge = source.filmGauge; + this.filmOffset = source.filmOffset; - "#else", + return this; - "specularStrength = 1.0;", + }, - "#endif" + /** + * Sets the FOV by focal length in respect to the current .filmGauge. + * + * The default film gauge is 35, so that the focal length can be specified for + * a 35mm (full frame) camera. + * + * Values for focal length and film gauge must have the same unit. + */ + setFocalLength: function ( focalLength ) { - ].join("\n"), + // see http://www.bobatkins.com/photography/technical/field_of_view.html + var vExtentSlope = 0.5 * this.getFilmHeight() / focalLength; - // LIGHTS LAMBERT + this.fov = THREE.Math.RAD2DEG * 2 * Math.atan( vExtentSlope ); + this.updateProjectionMatrix(); - lights_lambert_pars_vertex: [ + }, - "uniform vec3 ambient;", - "uniform vec3 diffuse;", - "uniform vec3 emissive;", + /** + * Calculates the focal length from the current .fov and .filmGauge. + */ + getFocalLength: function () { - "uniform vec3 ambientLightColor;", + var vExtentSlope = Math.tan( THREE.Math.DEG2RAD * 0.5 * this.fov ); - "#if MAX_DIR_LIGHTS > 0", + return 0.5 * this.getFilmHeight() / vExtentSlope; - "uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];", - "uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];", + }, - "#endif", + getEffectiveFOV: function () { - "#if MAX_HEMI_LIGHTS > 0", + return THREE.Math.RAD2DEG * 2 * Math.atan( + Math.tan( THREE.Math.DEG2RAD * 0.5 * this.fov ) / this.zoom ); - "uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];", - "uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];", - "uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];", + }, - "#endif", + getFilmWidth: function () { - "#if MAX_POINT_LIGHTS > 0", + // film not completely covered in portrait format (aspect < 1) + return this.filmGauge * Math.min( this.aspect, 1 ); - "uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];", - "uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];", - "uniform float pointLightDistance[ MAX_POINT_LIGHTS ];", + }, - "#endif", + getFilmHeight: function () { - "#if MAX_SPOT_LIGHTS > 0", + // film not completely covered in landscape format (aspect > 1) + return this.filmGauge / Math.max( this.aspect, 1 ); - "uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];", - "uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];", - "uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];", - "uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];", - "uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];", - "uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];", + }, - "#endif", + /** + * 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. + */ + setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) { - "#ifdef WRAP_AROUND", + this.aspect = fullWidth / fullHeight; - "uniform vec3 wrapRGB;", + this.view = { + fullWidth: fullWidth, + fullHeight: fullHeight, + offsetX: x, + offsetY: y, + width: width, + height: height + }; - "#endif" + this.updateProjectionMatrix(); - ].join("\n"), + }, - lights_lambert_vertex: [ + clearViewOffset: function() { - "vLightFront = vec3( 0.0 );", + this.view = null; + this.updateProjectionMatrix(); - "#ifdef DOUBLE_SIDED", + }, - "vLightBack = vec3( 0.0 );", + updateProjectionMatrix: function () { - "#endif", + var near = this.near, + top = near * Math.tan( + THREE.Math.DEG2RAD * 0.5 * this.fov ) / this.zoom, + height = 2 * top, + width = this.aspect * height, + left = - 0.5 * width, + view = this.view; - "transformedNormal = normalize( transformedNormal );", + if ( view !== null ) { - "#if MAX_DIR_LIGHTS > 0", + var fullWidth = view.fullWidth, + fullHeight = view.fullHeight; - "for( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {", + left += view.offsetX * width / fullWidth; + top -= view.offsetY * height / fullHeight; + width *= view.width / fullWidth; + height *= view.height / fullHeight; - "vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );", - "vec3 dirVector = normalize( lDirection.xyz );", + } - "float dotProduct = dot( transformedNormal, dirVector );", - "vec3 directionalLightWeighting = vec3( max( dotProduct, 0.0 ) );", + var skew = this.filmOffset; + if ( skew !== 0 ) left += near * skew / this.getFilmWidth(); - "#ifdef DOUBLE_SIDED", + this.projectionMatrix.makeFrustum( + left, left + width, top - height, top, near, this.far ); - "vec3 directionalLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );", + }, - "#ifdef WRAP_AROUND", + toJSON: function ( meta ) { - "vec3 directionalLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );", + var data = THREE.Object3D.prototype.toJSON.call( this, meta ); - "#endif", + data.object.fov = this.fov; + data.object.zoom = this.zoom; - "#endif", + data.object.near = this.near; + data.object.far = this.far; + data.object.focus = this.focus; - "#ifdef WRAP_AROUND", + data.object.aspect = this.aspect; - "vec3 directionalLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );", - "directionalLightWeighting = mix( directionalLightWeighting, directionalLightWeightingHalf, wrapRGB );", + if ( this.view !== null ) data.object.view = Object.assign( {}, this.view ); - "#ifdef DOUBLE_SIDED", + data.object.filmGauge = this.filmGauge; + data.object.filmOffset = this.filmOffset; - "directionalLightWeightingBack = mix( directionalLightWeightingBack, directionalLightWeightingHalfBack, wrapRGB );", + return data; - "#endif", + } - "#endif", +} ); - "vLightFront += directionalLightColor[ i ] * directionalLightWeighting;", +// File:src/cameras/StereoCamera.js - "#ifdef DOUBLE_SIDED", +/** + * @author mrdoob / http://mrdoob.com/ + */ - "vLightBack += directionalLightColor[ i ] * directionalLightWeightingBack;", +THREE.StereoCamera = function () { - "#endif", + this.type = 'StereoCamera'; - "}", + this.aspect = 1; - "#endif", + this.cameraL = new THREE.PerspectiveCamera(); + this.cameraL.layers.enable( 1 ); + this.cameraL.matrixAutoUpdate = false; - "#if MAX_POINT_LIGHTS > 0", + this.cameraR = new THREE.PerspectiveCamera(); + this.cameraR.layers.enable( 2 ); + this.cameraR.matrixAutoUpdate = false; - "for( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {", +}; - "vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );", - "vec3 lVector = lPosition.xyz - mvPosition.xyz;", +Object.assign( THREE.StereoCamera.prototype, { - "float lDistance = 1.0;", - "if ( pointLightDistance[ i ] > 0.0 )", - "lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );", + update: ( function () { - "lVector = normalize( lVector );", - "float dotProduct = dot( transformedNormal, lVector );", + var focus, fov, aspect, near, far; - "vec3 pointLightWeighting = vec3( max( dotProduct, 0.0 ) );", + var eyeRight = new THREE.Matrix4(); + var eyeLeft = new THREE.Matrix4(); - "#ifdef DOUBLE_SIDED", + return function update( camera ) { - "vec3 pointLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );", + var needsUpdate = focus !== camera.focus || fov !== camera.fov || + aspect !== camera.aspect * this.aspect || near !== camera.near || + far !== camera.far; - "#ifdef WRAP_AROUND", + if ( needsUpdate ) { - "vec3 pointLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );", + focus = camera.focus; + fov = camera.fov; + aspect = camera.aspect * this.aspect; + near = camera.near; + far = camera.far; - "#endif", + // Off-axis stereoscopic effect based on + // http://paulbourke.net/stereographics/stereorender/ - "#endif", + var projectionMatrix = camera.projectionMatrix.clone(); + var eyeSep = 0.064 / 2; + var eyeSepOnProjection = eyeSep * near / focus; + var ymax = near * Math.tan( THREE.Math.DEG2RAD * fov * 0.5 ); + var xmin, xmax; - "#ifdef WRAP_AROUND", + // translate xOffset - "vec3 pointLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );", - "pointLightWeighting = mix( pointLightWeighting, pointLightWeightingHalf, wrapRGB );", + eyeLeft.elements[ 12 ] = - eyeSep; + eyeRight.elements[ 12 ] = eyeSep; - "#ifdef DOUBLE_SIDED", + // for left eye - "pointLightWeightingBack = mix( pointLightWeightingBack, pointLightWeightingHalfBack, wrapRGB );", + xmin = - ymax * aspect + eyeSepOnProjection; + xmax = ymax * aspect + eyeSepOnProjection; - "#endif", + projectionMatrix.elements[ 0 ] = 2 * near / ( xmax - xmin ); + projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); - "#endif", + this.cameraL.projectionMatrix.copy( projectionMatrix ); - "vLightFront += pointLightColor[ i ] * pointLightWeighting * lDistance;", + // for right eye - "#ifdef DOUBLE_SIDED", + xmin = - ymax * aspect - eyeSepOnProjection; + xmax = ymax * aspect - eyeSepOnProjection; - "vLightBack += pointLightColor[ i ] * pointLightWeightingBack * lDistance;", + projectionMatrix.elements[ 0 ] = 2 * near / ( xmax - xmin ); + projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); - "#endif", + this.cameraR.projectionMatrix.copy( projectionMatrix ); - "}", + } - "#endif", + this.cameraL.matrixWorld.copy( camera.matrixWorld ).multiply( eyeLeft ); + this.cameraR.matrixWorld.copy( camera.matrixWorld ).multiply( eyeRight ); - "#if MAX_SPOT_LIGHTS > 0", + }; - "for( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {", + } )() - "vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );", - "vec3 lVector = lPosition.xyz - mvPosition.xyz;", +} ); - "float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - worldPosition.xyz ) );", +// File:src/lights/Light.js - "if ( spotEffect > spotLightAngleCos[ i ] ) {", +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ - "spotEffect = max( pow( spotEffect, spotLightExponent[ i ] ), 0.0 );", +THREE.Light = function ( color, intensity ) { - "float lDistance = 1.0;", - "if ( spotLightDistance[ i ] > 0.0 )", - "lDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );", + THREE.Object3D.call( this ); - "lVector = normalize( lVector );", + this.type = 'Light'; - "float dotProduct = dot( transformedNormal, lVector );", - "vec3 spotLightWeighting = vec3( max( dotProduct, 0.0 ) );", + this.color = new THREE.Color( color ); + this.intensity = intensity !== undefined ? intensity : 1; - "#ifdef DOUBLE_SIDED", + this.receiveShadow = undefined; - "vec3 spotLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );", +}; - "#ifdef WRAP_AROUND", +THREE.Light.prototype = Object.assign( Object.create( THREE.Object3D.prototype ), { - "vec3 spotLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );", + constructor: THREE.Light, - "#endif", + copy: function ( source ) { - "#endif", + THREE.Object3D.prototype.copy.call( this, source ); - "#ifdef WRAP_AROUND", + this.color.copy( source.color ); + this.intensity = source.intensity; - "vec3 spotLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );", - "spotLightWeighting = mix( spotLightWeighting, spotLightWeightingHalf, wrapRGB );", + return this; - "#ifdef DOUBLE_SIDED", + }, - "spotLightWeightingBack = mix( spotLightWeightingBack, spotLightWeightingHalfBack, wrapRGB );", + toJSON: function ( meta ) { - "#endif", + var data = THREE.Object3D.prototype.toJSON.call( this, meta ); - "#endif", + data.object.color = this.color.getHex(); + data.object.intensity = this.intensity; - "vLightFront += spotLightColor[ i ] * spotLightWeighting * lDistance * spotEffect;", + if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex(); - "#ifdef DOUBLE_SIDED", + if ( this.distance !== undefined ) data.object.distance = this.distance; + if ( this.angle !== undefined ) data.object.angle = this.angle; + if ( this.decay !== undefined ) data.object.decay = this.decay; + if ( this.penumbra !== undefined ) data.object.penumbra = this.penumbra; - "vLightBack += spotLightColor[ i ] * spotLightWeightingBack * lDistance * spotEffect;", + return data; - "#endif", + } - "}", +} ); - "}", +// File:src/lights/LightShadow.js - "#endif", +/** + * @author mrdoob / http://mrdoob.com/ + */ - "#if MAX_HEMI_LIGHTS > 0", +THREE.LightShadow = function ( camera ) { - "for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {", + this.camera = camera; - "vec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );", - "vec3 lVector = normalize( lDirection.xyz );", + this.bias = 0; + this.radius = 1; - "float dotProduct = dot( transformedNormal, lVector );", + this.mapSize = new THREE.Vector2( 512, 512 ); - "float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;", - "float hemiDiffuseWeightBack = -0.5 * dotProduct + 0.5;", + this.map = null; + this.matrix = new THREE.Matrix4(); - "vLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );", +}; - "#ifdef DOUBLE_SIDED", +Object.assign( THREE.LightShadow.prototype, { - "vLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );", + copy: function ( source ) { - "#endif", + this.camera = source.camera.clone(); - "}", + this.bias = source.bias; + this.radius = source.radius; - "#endif", + this.mapSize.copy( source.mapSize ); - "vLightFront = vLightFront * diffuse + ambient * ambientLightColor + emissive;", + return this; - "#ifdef DOUBLE_SIDED", + }, - "vLightBack = vLightBack * diffuse + ambient * ambientLightColor + emissive;", + clone: function () { - "#endif" + return new this.constructor().copy( this ); - ].join("\n"), + } - // LIGHTS PHONG +} ); - lights_phong_pars_vertex: [ +// File:src/lights/AmbientLight.js - "#ifndef PHONG_PER_PIXEL", +/** + * @author mrdoob / http://mrdoob.com/ + */ - "#if MAX_POINT_LIGHTS > 0", +THREE.AmbientLight = function ( color, intensity ) { - "uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];", - "uniform float pointLightDistance[ MAX_POINT_LIGHTS ];", + THREE.Light.call( this, color, intensity ); - "varying vec4 vPointLight[ MAX_POINT_LIGHTS ];", + this.type = 'AmbientLight'; - "#endif", + this.castShadow = undefined; - "#if MAX_SPOT_LIGHTS > 0", +}; - "uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];", - "uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];", +THREE.AmbientLight.prototype = Object.assign( Object.create( THREE.Light.prototype ), { - "varying vec4 vSpotLight[ MAX_SPOT_LIGHTS ];", + constructor: THREE.AmbientLight - "#endif", +} ); - "#endif", +// File:src/lights/DirectionalLight.js - "#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP )", +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ - "varying vec3 vWorldPosition;", +THREE.DirectionalLight = function ( color, intensity ) { - "#endif" + THREE.Light.call( this, color, intensity ); - ].join("\n"), + this.type = 'DirectionalLight'; + this.position.copy( THREE.Object3D.DefaultUp ); + this.updateMatrix(); - lights_phong_vertex: [ + this.target = new THREE.Object3D(); - "#ifndef PHONG_PER_PIXEL", + this.shadow = new THREE.DirectionalLightShadow(); - "#if MAX_POINT_LIGHTS > 0", +}; - "for( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {", +THREE.DirectionalLight.prototype = Object.assign( Object.create( THREE.Light.prototype ), { - "vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );", - "vec3 lVector = lPosition.xyz - mvPosition.xyz;", + constructor: THREE.DirectionalLight, - "float lDistance = 1.0;", - "if ( pointLightDistance[ i ] > 0.0 )", - "lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );", + copy: function ( source ) { - "vPointLight[ i ] = vec4( lVector, lDistance );", + THREE.Light.prototype.copy.call( this, source ); - "}", + this.target = source.target.clone(); - "#endif", + this.shadow = source.shadow.clone(); - "#if MAX_SPOT_LIGHTS > 0", + return this; - "for( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {", + } - "vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );", - "vec3 lVector = lPosition.xyz - mvPosition.xyz;", +} ); - "float lDistance = 1.0;", - "if ( spotLightDistance[ i ] > 0.0 )", - "lDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );", +// File:src/lights/DirectionalLightShadow.js - "vSpotLight[ i ] = vec4( lVector, lDistance );", +/** + * @author mrdoob / http://mrdoob.com/ + */ - "}", +THREE.DirectionalLightShadow = function ( light ) { - "#endif", + THREE.LightShadow.call( this, new THREE.OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) ); - "#endif", +}; - "#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP )", +THREE.DirectionalLightShadow.prototype = Object.assign( Object.create( THREE.LightShadow.prototype ), { - "vWorldPosition = worldPosition.xyz;", + constructor: THREE.DirectionalLightShadow - "#endif" +} ); - ].join("\n"), +// File:src/lights/HemisphereLight.js - lights_phong_pars_fragment: [ +/** + * @author alteredq / http://alteredqualia.com/ + */ - "uniform vec3 ambientLightColor;", +THREE.HemisphereLight = function ( skyColor, groundColor, intensity ) { - "#if MAX_DIR_LIGHTS > 0", + THREE.Light.call( this, skyColor, intensity ); - "uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];", - "uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];", + this.type = 'HemisphereLight'; - "#endif", + this.castShadow = undefined; - "#if MAX_HEMI_LIGHTS > 0", + this.position.copy( THREE.Object3D.DefaultUp ); + this.updateMatrix(); - "uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];", - "uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];", - "uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];", + this.groundColor = new THREE.Color( groundColor ); - "#endif", +}; - "#if MAX_POINT_LIGHTS > 0", +THREE.HemisphereLight.prototype = Object.assign( Object.create( THREE.Light.prototype ), { - "uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];", + constructor: THREE.HemisphereLight, - "#ifdef PHONG_PER_PIXEL", + copy: function ( source ) { - "uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];", - "uniform float pointLightDistance[ MAX_POINT_LIGHTS ];", + THREE.Light.prototype.copy.call( this, source ); - "#else", + this.groundColor.copy( source.groundColor ); - "varying vec4 vPointLight[ MAX_POINT_LIGHTS ];", + return this; - "#endif", + } - "#endif", +} ); - "#if MAX_SPOT_LIGHTS > 0", +// File:src/lights/PointLight.js - "uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];", - "uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];", - "uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];", - "uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];", - "uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];", +/** + * @author mrdoob / http://mrdoob.com/ + */ - "#ifdef PHONG_PER_PIXEL", - "uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];", +THREE.PointLight = function ( color, intensity, distance, decay ) { - "#else", + THREE.Light.call( this, color, intensity ); - "varying vec4 vSpotLight[ MAX_SPOT_LIGHTS ];", + this.type = 'PointLight'; - "#endif", + Object.defineProperty( this, 'power', { + get: function () { + // intensity = power per solid angle. + // ref: equation (15) from http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf + return this.intensity * 4 * Math.PI; - "#endif", + }, + set: function ( power ) { + // intensity = power per solid angle. + // ref: equation (15) from http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf + this.intensity = power / ( 4 * Math.PI ); + } + } ); - "#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP )", + this.distance = ( distance !== undefined ) ? distance : 0; + this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. - "varying vec3 vWorldPosition;", + this.shadow = new THREE.LightShadow( new THREE.PerspectiveCamera( 90, 1, 0.5, 500 ) ); - "#endif", +}; - "#ifdef WRAP_AROUND", +THREE.PointLight.prototype = Object.assign( Object.create( THREE.Light.prototype ), { - "uniform vec3 wrapRGB;", + constructor: THREE.PointLight, - "#endif", + copy: function ( source ) { - "varying vec3 vViewPosition;", - "varying vec3 vNormal;" + THREE.Light.prototype.copy.call( this, source ); - ].join("\n"), + this.distance = source.distance; + this.decay = source.decay; - lights_phong_fragment: [ + this.shadow = source.shadow.clone(); - "vec3 normal = normalize( vNormal );", - "vec3 viewPosition = normalize( vViewPosition );", + return this; - "#ifdef DOUBLE_SIDED", + } - "normal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );", +} ); - "#endif", +// File:src/lights/SpotLight.js - "#ifdef USE_NORMALMAP", +/** + * @author alteredq / http://alteredqualia.com/ + */ - "normal = perturbNormal2Arb( -vViewPosition, normal );", +THREE.SpotLight = function ( color, intensity, distance, angle, penumbra, decay ) { - "#elif defined( USE_BUMPMAP )", + THREE.Light.call( this, color, intensity ); - "normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );", + this.type = 'SpotLight'; - "#endif", + this.position.copy( THREE.Object3D.DefaultUp ); + this.updateMatrix(); - "#if MAX_POINT_LIGHTS > 0", + this.target = new THREE.Object3D(); - "vec3 pointDiffuse = vec3( 0.0 );", - "vec3 pointSpecular = vec3( 0.0 );", + Object.defineProperty( this, 'power', { + get: function () { + // intensity = power per solid angle. + // ref: equation (17) from http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf + return this.intensity * Math.PI; + }, + set: function ( power ) { + // intensity = power per solid angle. + // ref: equation (17) from http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf + this.intensity = power / Math.PI; + } + } ); - "for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {", + this.distance = ( distance !== undefined ) ? distance : 0; + this.angle = ( angle !== undefined ) ? angle : Math.PI / 3; + this.penumbra = ( penumbra !== undefined ) ? penumbra : 0; + this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. - "#ifdef PHONG_PER_PIXEL", + this.shadow = new THREE.SpotLightShadow(); - "vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );", - "vec3 lVector = lPosition.xyz + vViewPosition.xyz;", +}; - "float lDistance = 1.0;", - "if ( pointLightDistance[ i ] > 0.0 )", - "lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );", +THREE.SpotLight.prototype = Object.assign( Object.create( THREE.Light.prototype ), { - "lVector = normalize( lVector );", + constructor: THREE.SpotLight, - "#else", + copy: function ( source ) { - "vec3 lVector = normalize( vPointLight[ i ].xyz );", - "float lDistance = vPointLight[ i ].w;", + THREE.Light.prototype.copy.call( this, source ); - "#endif", + this.distance = source.distance; + this.angle = source.angle; + this.penumbra = source.penumbra; + this.decay = source.decay; - // diffuse + this.target = source.target.clone(); - "float dotProduct = dot( normal, lVector );", + this.shadow = source.shadow.clone(); - "#ifdef WRAP_AROUND", + return this; - "float pointDiffuseWeightFull = max( dotProduct, 0.0 );", - "float pointDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );", + } - "vec3 pointDiffuseWeight = mix( vec3 ( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );", +} ); - "#else", +// File:src/lights/SpotLightShadow.js - "float pointDiffuseWeight = max( dotProduct, 0.0 );", +/** + * @author mrdoob / http://mrdoob.com/ + */ - "#endif", +THREE.SpotLightShadow = function () { - "pointDiffuse += diffuse * pointLightColor[ i ] * pointDiffuseWeight * lDistance;", + THREE.LightShadow.call( this, new THREE.PerspectiveCamera( 50, 1, 0.5, 500 ) ); - // specular +}; - "vec3 pointHalfVector = normalize( lVector + viewPosition );", - "float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );", - "float pointSpecularWeight = specularStrength * max( pow( pointDotNormalHalf, shininess ), 0.0 );", +THREE.SpotLightShadow.prototype = Object.assign( Object.create( THREE.LightShadow.prototype ), { - "#ifdef PHYSICALLY_BASED_SHADING", + constructor: THREE.SpotLightShadow, - // 2.0 => 2.0001 is hack to work around ANGLE bug + update: function ( light ) { - "float specularNormalization = ( shininess + 2.0001 ) / 8.0;", + var fov = THREE.Math.RAD2DEG * 2 * light.angle; + var aspect = this.mapSize.width / this.mapSize.height; + var far = light.distance || 500; - "vec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVector, pointHalfVector ), 5.0 );", - "pointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance * specularNormalization;", + var camera = this.camera; - "#else", + if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) { - "pointSpecular += specular * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance;", + camera.fov = fov; + camera.aspect = aspect; + camera.far = far; + camera.updateProjectionMatrix(); - "#endif", + } - "}", + } - "#endif", +} ); - "#if MAX_SPOT_LIGHTS > 0", +// File:src/loaders/AudioLoader.js - "vec3 spotDiffuse = vec3( 0.0 );", - "vec3 spotSpecular = vec3( 0.0 );", +/** + * @author Reece Aaron Lecrivain / http://reecenotes.com/ + */ - "for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {", +THREE.AudioLoader = function ( manager ) { - "#ifdef PHONG_PER_PIXEL", + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; - "vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );", - "vec3 lVector = lPosition.xyz + vViewPosition.xyz;", +}; - "float lDistance = 1.0;", - "if ( spotLightDistance[ i ] > 0.0 )", - "lDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );", +Object.assign( THREE.AudioLoader.prototype, { - "lVector = normalize( lVector );", + load: function ( url, onLoad, onProgress, onError ) { - "#else", + var loader = new THREE.XHRLoader( this.manager ); + loader.setResponseType( 'arraybuffer' ); + loader.load( url, function ( buffer ) { - "vec3 lVector = normalize( vSpotLight[ i ].xyz );", - "float lDistance = vSpotLight[ i ].w;", + var context = THREE.AudioContext; - "#endif", + context.decodeAudioData( buffer, function ( audioBuffer ) { - "float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );", + onLoad( audioBuffer ); - "if ( spotEffect > spotLightAngleCos[ i ] ) {", + } ); - "spotEffect = max( pow( spotEffect, spotLightExponent[ i ] ), 0.0 );", + }, onProgress, onError ); - // diffuse + } - "float dotProduct = dot( normal, lVector );", +} ); - "#ifdef WRAP_AROUND", +// File:src/loaders/Cache.js - "float spotDiffuseWeightFull = max( dotProduct, 0.0 );", - "float spotDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );", +/** + * @author mrdoob / http://mrdoob.com/ + */ - "vec3 spotDiffuseWeight = mix( vec3 ( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );", +THREE.Cache = { - "#else", + enabled: false, - "float spotDiffuseWeight = max( dotProduct, 0.0 );", + files: {}, - "#endif", + add: function ( key, file ) { - "spotDiffuse += diffuse * spotLightColor[ i ] * spotDiffuseWeight * lDistance * spotEffect;", + if ( this.enabled === false ) return; - // specular + // console.log( 'THREE.Cache', 'Adding key:', key ); - "vec3 spotHalfVector = normalize( lVector + viewPosition );", - "float spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );", - "float spotSpecularWeight = specularStrength * max( pow( spotDotNormalHalf, shininess ), 0.0 );", + this.files[ key ] = file; - "#ifdef PHYSICALLY_BASED_SHADING", + }, - // 2.0 => 2.0001 is hack to work around ANGLE bug + get: function ( key ) { - "float specularNormalization = ( shininess + 2.0001 ) / 8.0;", + if ( this.enabled === false ) return; - "vec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVector, spotHalfVector ), 5.0 );", - "spotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * lDistance * specularNormalization * spotEffect;", + // console.log( 'THREE.Cache', 'Checking key:', key ); - "#else", + return this.files[ key ]; - "spotSpecular += specular * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * lDistance * spotEffect;", + }, - "#endif", + remove: function ( key ) { - "}", + delete this.files[ key ]; - "}", + }, - "#endif", + clear: function () { - "#if MAX_DIR_LIGHTS > 0", + this.files = {}; - "vec3 dirDiffuse = vec3( 0.0 );", - "vec3 dirSpecular = vec3( 0.0 );" , + } - "for( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {", +}; - "vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );", - "vec3 dirVector = normalize( lDirection.xyz );", +// File:src/loaders/Loader.js - // diffuse +/** + * @author alteredq / http://alteredqualia.com/ + */ - "float dotProduct = dot( normal, dirVector );", +THREE.Loader = function () { - "#ifdef WRAP_AROUND", + this.onLoadStart = function () {}; + this.onLoadProgress = function () {}; + this.onLoadComplete = function () {}; - "float dirDiffuseWeightFull = max( dotProduct, 0.0 );", - "float dirDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );", +}; - "vec3 dirDiffuseWeight = mix( vec3( dirDiffuseWeightFull ), vec3( dirDiffuseWeightHalf ), wrapRGB );", +THREE.Loader.prototype = { - "#else", + constructor: THREE.Loader, - "float dirDiffuseWeight = max( dotProduct, 0.0 );", + crossOrigin: undefined, - "#endif", + extractUrlBase: function ( url ) { - "dirDiffuse += diffuse * directionalLightColor[ i ] * dirDiffuseWeight;", + var parts = url.split( '/' ); - // specular + if ( parts.length === 1 ) return './'; - "vec3 dirHalfVector = normalize( dirVector + viewPosition );", - "float dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );", - "float dirSpecularWeight = specularStrength * max( pow( dirDotNormalHalf, shininess ), 0.0 );", + parts.pop(); - "#ifdef PHYSICALLY_BASED_SHADING", + return parts.join( '/' ) + '/'; - /* - // fresnel term from skin shader - "const float F0 = 0.128;", + }, - "float base = 1.0 - dot( viewPosition, dirHalfVector );", - "float exponential = pow( base, 5.0 );", + initMaterials: function ( materials, texturePath, crossOrigin ) { - "float fresnel = exponential + F0 * ( 1.0 - exponential );", - */ + var array = []; - /* - // fresnel term from fresnel shader - "const float mFresnelBias = 0.08;", - "const float mFresnelScale = 0.3;", - "const float mFresnelPower = 5.0;", + for ( var i = 0; i < materials.length; ++ i ) { - "float fresnel = mFresnelBias + mFresnelScale * pow( 1.0 + dot( normalize( -viewPosition ), normal ), mFresnelPower );", - */ + array[ i ] = this.createMaterial( materials[ i ], texturePath, crossOrigin ); - // 2.0 => 2.0001 is hack to work around ANGLE bug + } - "float specularNormalization = ( shininess + 2.0001 ) / 8.0;", + return array; - //"dirSpecular += specular * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization * fresnel;", + }, - "vec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( dirVector, dirHalfVector ), 5.0 );", - "dirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;", + createMaterial: ( function () { - "#else", + var color, textureLoader, materialLoader; - "dirSpecular += specular * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight;", + return function createMaterial( m, texturePath, crossOrigin ) { - "#endif", + if ( color === undefined ) color = new THREE.Color(); + if ( textureLoader === undefined ) textureLoader = new THREE.TextureLoader(); + if ( materialLoader === undefined ) materialLoader = new THREE.MaterialLoader(); - "}", + // convert from old material format - "#endif", + var textures = {}; - "#if MAX_HEMI_LIGHTS > 0", + function loadTexture( path, repeat, offset, wrap, anisotropy ) { - "vec3 hemiDiffuse = vec3( 0.0 );", - "vec3 hemiSpecular = vec3( 0.0 );" , + var fullPath = texturePath + path; + var loader = THREE.Loader.Handlers.get( fullPath ); - "for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {", + var texture; - "vec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );", - "vec3 lVector = normalize( lDirection.xyz );", + if ( loader !== null ) { - // diffuse + texture = loader.load( fullPath ); - "float dotProduct = dot( normal, lVector );", - "float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;", + } else { - "vec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );", + textureLoader.setCrossOrigin( crossOrigin ); + texture = textureLoader.load( fullPath ); - "hemiDiffuse += diffuse * hemiColor;", + } - // specular (sky light) + if ( repeat !== undefined ) { - "vec3 hemiHalfVectorSky = normalize( lVector + viewPosition );", - "float hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;", - "float hemiSpecularWeightSky = specularStrength * max( pow( hemiDotNormalHalfSky, shininess ), 0.0 );", + texture.repeat.fromArray( repeat ); - // specular (ground light) + if ( repeat[ 0 ] !== 1 ) texture.wrapS = THREE.RepeatWrapping; + if ( repeat[ 1 ] !== 1 ) texture.wrapT = THREE.RepeatWrapping; - "vec3 lVectorGround = -lVector;", + } - "vec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );", - "float hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;", - "float hemiSpecularWeightGround = specularStrength * max( pow( hemiDotNormalHalfGround, shininess ), 0.0 );", + if ( offset !== undefined ) { - "#ifdef PHYSICALLY_BASED_SHADING", + texture.offset.fromArray( offset ); - "float dotProductGround = dot( normal, lVectorGround );", + } - // 2.0 => 2.0001 is hack to work around ANGLE bug + if ( wrap !== undefined ) { - "float specularNormalization = ( shininess + 2.0001 ) / 8.0;", + if ( wrap[ 0 ] === 'repeat' ) texture.wrapS = THREE.RepeatWrapping; + if ( wrap[ 0 ] === 'mirror' ) texture.wrapS = THREE.MirroredRepeatWrapping; - "vec3 schlickSky = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVector, hemiHalfVectorSky ), 5.0 );", - "vec3 schlickGround = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 5.0 );", - "hemiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );", + if ( wrap[ 1 ] === 'repeat' ) texture.wrapT = THREE.RepeatWrapping; + if ( wrap[ 1 ] === 'mirror' ) texture.wrapT = THREE.MirroredRepeatWrapping; - "#else", + } - "hemiSpecular += specular * hemiColor * ( hemiSpecularWeightSky + hemiSpecularWeightGround ) * hemiDiffuseWeight;", + if ( anisotropy !== undefined ) { - "#endif", + texture.anisotropy = anisotropy; - "}", + } - "#endif", + var uuid = THREE.Math.generateUUID(); - "vec3 totalDiffuse = vec3( 0.0 );", - "vec3 totalSpecular = vec3( 0.0 );", + textures[ uuid ] = texture; - "#if MAX_DIR_LIGHTS > 0", + return uuid; - "totalDiffuse += dirDiffuse;", - "totalSpecular += dirSpecular;", + } - "#endif", + // - "#if MAX_HEMI_LIGHTS > 0", + var json = { + uuid: THREE.Math.generateUUID(), + type: 'MeshLambertMaterial' + }; - "totalDiffuse += hemiDiffuse;", - "totalSpecular += hemiSpecular;", + for ( var name in m ) { - "#endif", + var value = m[ name ]; - "#if MAX_POINT_LIGHTS > 0", + switch ( name ) { + case 'DbgColor': + case 'DbgIndex': + case 'opticalDensity': + case 'illumination': + break; + case 'DbgName': + json.name = value; + break; + case 'blending': + json.blending = THREE[ value ]; + break; + case 'colorAmbient': + case 'mapAmbient': + console.warn( 'THREE.Loader.createMaterial:', name, 'is no longer supported.' ); + break; + case 'colorDiffuse': + json.color = color.fromArray( value ).getHex(); + break; + case 'colorSpecular': + json.specular = color.fromArray( value ).getHex(); + break; + case 'colorEmissive': + json.emissive = color.fromArray( value ).getHex(); + break; + case 'specularCoef': + json.shininess = value; + break; + case 'shading': + if ( value.toLowerCase() === 'basic' ) json.type = 'MeshBasicMaterial'; + if ( value.toLowerCase() === 'phong' ) json.type = 'MeshPhongMaterial'; + if ( value.toLowerCase() === 'standard' ) json.type = 'MeshStandardMaterial'; + break; + case 'mapDiffuse': + json.map = loadTexture( value, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy ); + break; + case 'mapDiffuseRepeat': + case 'mapDiffuseOffset': + case 'mapDiffuseWrap': + case 'mapDiffuseAnisotropy': + break; + case 'mapEmissive': + json.emissiveMap = loadTexture( value, m.mapEmissiveRepeat, m.mapEmissiveOffset, m.mapEmissiveWrap, m.mapEmissiveAnisotropy ); + break; + case 'mapEmissiveRepeat': + case 'mapEmissiveOffset': + case 'mapEmissiveWrap': + case 'mapEmissiveAnisotropy': + break; + case 'mapLight': + json.lightMap = loadTexture( value, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy ); + break; + case 'mapLightRepeat': + case 'mapLightOffset': + case 'mapLightWrap': + case 'mapLightAnisotropy': + break; + case 'mapAO': + json.aoMap = loadTexture( value, m.mapAORepeat, m.mapAOOffset, m.mapAOWrap, m.mapAOAnisotropy ); + break; + case 'mapAORepeat': + case 'mapAOOffset': + case 'mapAOWrap': + case 'mapAOAnisotropy': + break; + case 'mapBump': + json.bumpMap = loadTexture( value, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy ); + break; + case 'mapBumpScale': + json.bumpScale = value; + break; + case 'mapBumpRepeat': + case 'mapBumpOffset': + case 'mapBumpWrap': + case 'mapBumpAnisotropy': + break; + case 'mapNormal': + json.normalMap = loadTexture( value, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy ); + break; + case 'mapNormalFactor': + json.normalScale = [ value, value ]; + break; + case 'mapNormalRepeat': + case 'mapNormalOffset': + case 'mapNormalWrap': + case 'mapNormalAnisotropy': + break; + case 'mapSpecular': + json.specularMap = loadTexture( value, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy ); + break; + case 'mapSpecularRepeat': + case 'mapSpecularOffset': + case 'mapSpecularWrap': + case 'mapSpecularAnisotropy': + break; + case 'mapMetalness': + json.metalnessMap = loadTexture( value, m.mapMetalnessRepeat, m.mapMetalnessOffset, m.mapMetalnessWrap, m.mapMetalnessAnisotropy ); + break; + case 'mapMetalnessRepeat': + case 'mapMetalnessOffset': + case 'mapMetalnessWrap': + case 'mapMetalnessAnisotropy': + break; + case 'mapRoughness': + json.roughnessMap = loadTexture( value, m.mapRoughnessRepeat, m.mapRoughnessOffset, m.mapRoughnessWrap, m.mapRoughnessAnisotropy ); + break; + case 'mapRoughnessRepeat': + case 'mapRoughnessOffset': + case 'mapRoughnessWrap': + case 'mapRoughnessAnisotropy': + break; + case 'mapAlpha': + json.alphaMap = loadTexture( value, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy ); + break; + case 'mapAlphaRepeat': + case 'mapAlphaOffset': + case 'mapAlphaWrap': + case 'mapAlphaAnisotropy': + break; + case 'flipSided': + json.side = THREE.BackSide; + break; + case 'doubleSided': + json.side = THREE.DoubleSide; + break; + case 'transparency': + console.warn( 'THREE.Loader.createMaterial: transparency has been renamed to opacity' ); + json.opacity = value; + break; + case 'depthTest': + case 'depthWrite': + case 'colorWrite': + case 'opacity': + case 'reflectivity': + case 'transparent': + case 'visible': + case 'wireframe': + json[ name ] = value; + break; + case 'vertexColors': + if ( value === true ) json.vertexColors = THREE.VertexColors; + if ( value === 'face' ) json.vertexColors = THREE.FaceColors; + break; + default: + console.error( 'THREE.Loader.createMaterial: Unsupported', name, value ); + break; + } - "totalDiffuse += pointDiffuse;", - "totalSpecular += pointSpecular;", + } - "#endif", + if ( json.type === 'MeshBasicMaterial' ) delete json.emissive; + if ( json.type !== 'MeshPhongMaterial' ) delete json.specular; - "#if MAX_SPOT_LIGHTS > 0", + if ( json.opacity < 1 ) json.transparent = true; - "totalDiffuse += spotDiffuse;", - "totalSpecular += spotSpecular;", + materialLoader.setTextures( textures ); - "#endif", + return materialLoader.parse( json ); - "#ifdef METAL", + }; - "gl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient + totalSpecular );", + } )() - "#else", +}; - "gl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient ) + totalSpecular;", +THREE.Loader.Handlers = { - "#endif" + handlers: [], - ].join("\n"), + add: function ( regex, loader ) { - // VERTEX COLORS + this.handlers.push( regex, loader ); - color_pars_fragment: [ + }, - "#ifdef USE_COLOR", + get: function ( file ) { - "varying vec3 vColor;", + var handlers = this.handlers; - "#endif" + for ( var i = 0, l = handlers.length; i < l; i += 2 ) { - ].join("\n"), + var regex = handlers[ i ]; + var loader = handlers[ i + 1 ]; + if ( regex.test( file ) ) { - color_fragment: [ + return loader; - "#ifdef USE_COLOR", + } - "gl_FragColor = gl_FragColor * vec4( vColor, opacity );", + } - "#endif" + return null; - ].join("\n"), + } - color_pars_vertex: [ +}; - "#ifdef USE_COLOR", +// File:src/loaders/XHRLoader.js - "varying vec3 vColor;", +/** + * @author mrdoob / http://mrdoob.com/ + */ - "#endif" +THREE.XHRLoader = function ( manager ) { - ].join("\n"), + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; +}; - color_vertex: [ +Object.assign( THREE.XHRLoader.prototype, { - "#ifdef USE_COLOR", + load: function ( url, onLoad, onProgress, onError ) { - "#ifdef GAMMA_INPUT", + if ( this.path !== undefined ) url = this.path + url; - "vColor = color * color;", + var scope = this; - "#else", + var cached = THREE.Cache.get( url ); - "vColor = color;", + if ( cached !== undefined ) { - "#endif", + scope.manager.itemStart( url ); - "#endif" + setTimeout( function () { - ].join("\n"), + if ( onLoad ) onLoad( cached ); - // SKINNING + scope.manager.itemEnd( url ); - skinning_pars_vertex: [ + }, 0 ); - "#ifdef USE_SKINNING", + return cached; - "#ifdef BONE_TEXTURE", + } - "uniform sampler2D boneTexture;", + var request = new XMLHttpRequest(); + request.overrideMimeType( 'text/plain' ); + request.open( 'GET', url, true ); - "mat4 getBoneMatrix( const in float i ) {", + request.addEventListener( 'load', function ( event ) { - "float j = i * 4.0;", - "float x = mod( j, N_BONE_PIXEL_X );", - "float y = floor( j / N_BONE_PIXEL_X );", + var response = event.target.response; - "const float dx = 1.0 / N_BONE_PIXEL_X;", - "const float dy = 1.0 / N_BONE_PIXEL_Y;", + THREE.Cache.add( url, response ); - "y = dy * ( y + 0.5 );", + if ( this.status === 200 ) { - "vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );", - "vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );", - "vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );", - "vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );", + if ( onLoad ) onLoad( response ); - "mat4 bone = mat4( v1, v2, v3, v4 );", + scope.manager.itemEnd( url ); - "return bone;", + } else if ( this.status === 0 ) { - "}", + // Some browsers return HTTP Status 0 when using non-http protocol + // e.g. 'file://' or 'data://'. Handle as success. - "#else", + console.warn( 'THREE.XHRLoader: HTTP Status 0 received.' ); - "uniform mat4 boneGlobalMatrices[ MAX_BONES ];", + if ( onLoad ) onLoad( response ); - "mat4 getBoneMatrix( const in float i ) {", + scope.manager.itemEnd( url ); - "mat4 bone = boneGlobalMatrices[ int(i) ];", - "return bone;", + } else { - "}", + if ( onError ) onError( event ); - "#endif", + scope.manager.itemError( url ); - "#endif" + } - ].join("\n"), + }, false ); - skinbase_vertex: [ + if ( onProgress !== undefined ) { - "#ifdef USE_SKINNING", + request.addEventListener( 'progress', function ( event ) { - "mat4 boneMatX = getBoneMatrix( skinIndex.x );", - "mat4 boneMatY = getBoneMatrix( skinIndex.y );", + onProgress( event ); - "#endif" + }, false ); - ].join("\n"), + } - skinning_vertex: [ + request.addEventListener( 'error', function ( event ) { - "#ifdef USE_SKINNING", + if ( onError ) onError( event ); - "#ifdef USE_MORPHTARGETS", + scope.manager.itemError( url ); - "vec4 skinVertex = vec4( morphed, 1.0 );", + }, false ); - "#else", + if ( this.responseType !== undefined ) request.responseType = this.responseType; + if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials; - "vec4 skinVertex = vec4( position, 1.0 );", + request.send( null ); - "#endif", + scope.manager.itemStart( url ); - "vec4 skinned = boneMatX * skinVertex * skinWeight.x;", - "skinned += boneMatY * skinVertex * skinWeight.y;", + return request; - "#endif" + }, - ].join("\n"), + setPath: function ( value ) { - // MORPHING + this.path = value; + return this; - morphtarget_pars_vertex: [ + }, - "#ifdef USE_MORPHTARGETS", + setResponseType: function ( value ) { - "#ifndef USE_MORPHNORMALS", + this.responseType = value; + return this; - "uniform float morphTargetInfluences[ 8 ];", + }, - "#else", + setWithCredentials: function ( value ) { - "uniform float morphTargetInfluences[ 4 ];", + this.withCredentials = value; + return this; - "#endif", + } - "#endif" +} ); - ].join("\n"), +// File:src/loaders/FontLoader.js - morphtarget_vertex: [ +/** + * @author mrdoob / http://mrdoob.com/ + */ - "#ifdef USE_MORPHTARGETS", +THREE.FontLoader = function ( manager ) { - "vec3 morphed = vec3( 0.0 );", - "morphed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];", - "morphed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];", - "morphed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];", - "morphed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];", + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; - "#ifndef USE_MORPHNORMALS", +}; - "morphed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];", - "morphed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];", - "morphed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];", - "morphed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];", +Object.assign( THREE.FontLoader.prototype, { - "#endif", + load: function ( url, onLoad, onProgress, onError ) { - "morphed += position;", + var scope = this; - "#endif" + var loader = new THREE.XHRLoader( this.manager ); + loader.load( url, function ( text ) { - ].join("\n"), + var json; - default_vertex : [ + try { - "vec4 mvPosition;", + json = JSON.parse( text ); - "#ifdef USE_SKINNING", + } catch ( e ) { - "mvPosition = modelViewMatrix * skinned;", + console.warn( 'THREE.FontLoader: typeface.js support is being deprecated. Use typeface.json instead.' ); + json = JSON.parse( text.substring( 65, text.length - 2 ) ); - "#endif", + } - "#if !defined( USE_SKINNING ) && defined( USE_MORPHTARGETS )", + var font = scope.parse( json ); - "mvPosition = modelViewMatrix * vec4( morphed, 1.0 );", + if ( onLoad ) onLoad( font ); - "#endif", + }, onProgress, onError ); - "#if !defined( USE_SKINNING ) && ! defined( USE_MORPHTARGETS )", + }, - "mvPosition = modelViewMatrix * vec4( position, 1.0 );", + parse: function ( json ) { - "#endif", + return new THREE.Font( json ); - "gl_Position = projectionMatrix * mvPosition;" + } - ].join("\n"), +} ); - morphnormal_vertex: [ +// File:src/loaders/ImageLoader.js - "#ifdef USE_MORPHNORMALS", +/** + * @author mrdoob / http://mrdoob.com/ + */ - "vec3 morphedNormal = vec3( 0.0 );", +THREE.ImageLoader = function ( manager ) { - "morphedNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];", - "morphedNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];", - "morphedNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];", - "morphedNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];", + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; - "morphedNormal += normal;", +}; - "#endif" +Object.assign( THREE.ImageLoader.prototype, { - ].join("\n"), + load: function ( url, onLoad, onProgress, onError ) { - skinnormal_vertex: [ + var scope = this; - "#ifdef USE_SKINNING", + var image = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'img' ); + image.onload = function () { - "mat4 skinMatrix = skinWeight.x * boneMatX;", - "skinMatrix += skinWeight.y * boneMatY;", + URL.revokeObjectURL( image.src ); - "#ifdef USE_MORPHNORMALS", + if ( onLoad ) onLoad( image ); - "vec4 skinnedNormal = skinMatrix * vec4( morphedNormal, 0.0 );", + scope.manager.itemEnd( url ); - "#else", + }; - "vec4 skinnedNormal = skinMatrix * vec4( normal, 0.0 );", + if ( url.indexOf( 'data:' ) === 0 ) { - "#endif", + image.src = url; - "#endif" + } else { - ].join("\n"), + var loader = new THREE.XHRLoader(); + loader.setPath( this.path ); + loader.setResponseType( 'blob' ); + loader.load( url, function ( blob ) { - defaultnormal_vertex: [ + image.src = URL.createObjectURL( blob ); - "vec3 objectNormal;", + }, onProgress, onError ); - "#ifdef USE_SKINNING", + } - "objectNormal = skinnedNormal.xyz;", + scope.manager.itemStart( url ); - "#endif", + return image; - "#if !defined( USE_SKINNING ) && defined( USE_MORPHNORMALS )", + }, - "objectNormal = morphedNormal;", + setCrossOrigin: function ( value ) { - "#endif", + this.crossOrigin = value; + return this; - "#if !defined( USE_SKINNING ) && ! defined( USE_MORPHNORMALS )", + }, - "objectNormal = normal;", + setPath: function ( value ) { - "#endif", + this.path = value; + return this; - "#ifdef FLIP_SIDED", + } - "objectNormal = -objectNormal;", +} ); - "#endif", +// File:src/loaders/JSONLoader.js - "vec3 transformedNormal = normalMatrix * objectNormal;" +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ - ].join("\n"), +THREE.JSONLoader = function ( manager ) { - // SHADOW MAP + if ( typeof manager === 'boolean' ) { - // based on SpiderGL shadow map and Fabien Sanglard's GLSL shadow mapping examples - // http://spidergl.org/example.php?id=6 - // http://fabiensanglard.net/shadowmapping + console.warn( 'THREE.JSONLoader: showStatus parameter has been removed from constructor.' ); + manager = undefined; - shadowmap_pars_fragment: [ + } - "#ifdef USE_SHADOWMAP", + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; - "uniform sampler2D shadowMap[ MAX_SHADOWS ];", - "uniform vec2 shadowMapSize[ MAX_SHADOWS ];", + this.withCredentials = false; - "uniform float shadowDarkness[ MAX_SHADOWS ];", - "uniform float shadowBias[ MAX_SHADOWS ];", +}; - "varying vec4 vShadowCoord[ MAX_SHADOWS ];", +Object.assign( THREE.JSONLoader.prototype, { - "float unpackDepth( const in vec4 rgba_depth ) {", + load: function( url, onLoad, onProgress, onError ) { - "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 );", - "float depth = dot( rgba_depth, bit_shift );", - "return depth;", + var scope = this; - "}", + var texturePath = this.texturePath && ( typeof this.texturePath === "string" ) ? this.texturePath : THREE.Loader.prototype.extractUrlBase( url ); - "#endif" + var loader = new THREE.XHRLoader( this.manager ); + loader.setWithCredentials( this.withCredentials ); + loader.load( url, function ( text ) { - ].join("\n"), + var json = JSON.parse( text ); + var metadata = json.metadata; - shadowmap_fragment: [ + if ( metadata !== undefined ) { - "#ifdef USE_SHADOWMAP", + var type = metadata.type; - "#ifdef SHADOWMAP_DEBUG", + if ( type !== undefined ) { - "vec3 frustumColors[3];", - "frustumColors[0] = vec3( 1.0, 0.5, 0.0 );", - "frustumColors[1] = vec3( 0.0, 1.0, 0.8 );", - "frustumColors[2] = vec3( 0.0, 0.5, 1.0 );", + if ( type.toLowerCase() === 'object' ) { - "#endif", + console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.ObjectLoader instead.' ); + return; - "#ifdef SHADOWMAP_CASCADE", + } - "int inFrustumCount = 0;", + if ( type.toLowerCase() === 'scene' ) { - "#endif", + console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.SceneLoader instead.' ); + return; - "float fDepth;", - "vec3 shadowColor = vec3( 1.0 );", + } - "for( int i = 0; i < MAX_SHADOWS; i ++ ) {", + } - "vec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;", + } - // "if ( something && something )" breaks ATI OpenGL shader compiler - // "if ( all( something, something ) )" using this instead + var object = scope.parse( json, texturePath ); + onLoad( object.geometry, object.materials ); - "bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );", - "bool inFrustum = all( inFrustumVec );", + }, onProgress, onError ); - // don't shadow pixels outside of light frustum - // use just first frustum (for cascades) - // don't shadow pixels behind far plane of light frustum + }, - "#ifdef SHADOWMAP_CASCADE", + setTexturePath: function ( value ) { - "inFrustumCount += int( inFrustum );", - "bvec3 frustumTestVec = bvec3( inFrustum, inFrustumCount == 1, shadowCoord.z <= 1.0 );", + this.texturePath = value; - "#else", + }, - "bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );", + parse: function ( json, texturePath ) { - "#endif", + var geometry = new THREE.Geometry(), + scale = ( json.scale !== undefined ) ? 1.0 / json.scale : 1.0; - "bool frustumTest = all( frustumTestVec );", + parseModel( scale ); - "if ( frustumTest ) {", + parseSkin(); + parseMorphing( scale ); + parseAnimations(); - "shadowCoord.z += shadowBias[ i ];", + geometry.computeFaceNormals(); + geometry.computeBoundingSphere(); - "#if defined( SHADOWMAP_TYPE_PCF )", + function parseModel( scale ) { - // Percentage-close filtering - // (9 pixel kernel) - // http://fabiensanglard.net/shadowmappingPCF/ + function isBitSet( value, position ) { - "float shadow = 0.0;", + return value & ( 1 << position ); - /* - // nested loops breaks shader compiler / validator on some ATI cards when using OpenGL - // must enroll loop manually + } - "for ( float y = -1.25; y <= 1.25; y += 1.25 )", - "for ( float x = -1.25; x <= 1.25; x += 1.25 ) {", + var i, j, fi, - "vec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy );", + offset, zLength, - // doesn't seem to produce any noticeable visual difference compared to simple "texture2D" lookup - //"vec4 rgbaDepth = texture2DProj( shadowMap[ i ], vec4( vShadowCoord[ i ].w * ( vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy ), 0.05, vShadowCoord[ i ].w ) );", + colorIndex, normalIndex, uvIndex, materialIndex, - "float fDepth = unpackDepth( rgbaDepth );", + type, + isQuad, + hasMaterial, + hasFaceVertexUv, + hasFaceNormal, hasFaceVertexNormal, + hasFaceColor, hasFaceVertexColor, - "if ( fDepth < shadowCoord.z )", - "shadow += 1.0;", + vertex, face, faceA, faceB, hex, normal, - "}", + uvLayer, uv, u, v, - "shadow /= 9.0;", + faces = json.faces, + vertices = json.vertices, + normals = json.normals, + colors = json.colors, - */ + nUvLayers = 0; - "const float shadowDelta = 1.0 / 9.0;", + if ( json.uvs !== undefined ) { - "float xPixelOffset = 1.0 / shadowMapSize[ i ].x;", - "float yPixelOffset = 1.0 / shadowMapSize[ i ].y;", + // disregard empty arrays - "float dx0 = -1.25 * xPixelOffset;", - "float dy0 = -1.25 * yPixelOffset;", - "float dx1 = 1.25 * xPixelOffset;", - "float dy1 = 1.25 * yPixelOffset;", + for ( i = 0; i < json.uvs.length; i ++ ) { - "fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );", - "if ( fDepth < shadowCoord.z ) shadow += shadowDelta;", + if ( json.uvs[ i ].length ) nUvLayers ++; - "fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );", - "if ( fDepth < shadowCoord.z ) shadow += shadowDelta;", + } - "fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );", - "if ( fDepth < shadowCoord.z ) shadow += shadowDelta;", + for ( i = 0; i < nUvLayers; i ++ ) { - "fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );", - "if ( fDepth < shadowCoord.z ) shadow += shadowDelta;", + geometry.faceVertexUvs[ i ] = []; - "fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );", - "if ( fDepth < shadowCoord.z ) shadow += shadowDelta;", + } - "fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );", - "if ( fDepth < shadowCoord.z ) shadow += shadowDelta;", + } - "fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );", - "if ( fDepth < shadowCoord.z ) shadow += shadowDelta;", + offset = 0; + zLength = vertices.length; - "fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );", - "if ( fDepth < shadowCoord.z ) shadow += shadowDelta;", + while ( offset < zLength ) { - "fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );", - "if ( fDepth < shadowCoord.z ) shadow += shadowDelta;", + vertex = new THREE.Vector3(); - "shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );", + vertex.x = vertices[ offset ++ ] * scale; + vertex.y = vertices[ offset ++ ] * scale; + vertex.z = vertices[ offset ++ ] * scale; - "#elif defined( SHADOWMAP_TYPE_PCF_SOFT )", + geometry.vertices.push( vertex ); - // Percentage-close filtering - // (9 pixel kernel) - // http://fabiensanglard.net/shadowmappingPCF/ + } - "float shadow = 0.0;", + offset = 0; + zLength = faces.length; - "float xPixelOffset = 1.0 / shadowMapSize[ i ].x;", - "float yPixelOffset = 1.0 / shadowMapSize[ i ].y;", + while ( offset < zLength ) { - "float dx0 = -1.0 * xPixelOffset;", - "float dy0 = -1.0 * yPixelOffset;", - "float dx1 = 1.0 * xPixelOffset;", - "float dy1 = 1.0 * yPixelOffset;", + type = faces[ offset ++ ]; - "mat3 shadowKernel;", - "mat3 depthKernel;", - "depthKernel[0][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );", - "depthKernel[0][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );", - "depthKernel[0][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );", - "depthKernel[1][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );", - "depthKernel[1][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );", - "depthKernel[1][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );", - "depthKernel[2][0] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );", - "depthKernel[2][1] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );", - "depthKernel[2][2] = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );", + 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 ); - "vec3 shadowZ = vec3( shadowCoord.z );", - "shadowKernel[0] = vec3(lessThan(depthKernel[0], shadowZ ));", - "shadowKernel[0] *= vec3(0.25);", - - "shadowKernel[1] = vec3(lessThan(depthKernel[1], shadowZ ));", - "shadowKernel[1] *= vec3(0.25);", + // console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor); - "shadowKernel[2] = vec3(lessThan(depthKernel[2], shadowZ ));", - "shadowKernel[2] *= vec3(0.25);", + if ( isQuad ) { - "vec2 fractionalCoord = 1.0 - fract( shadowCoord.xy * shadowMapSize[i].xy );", + faceA = new THREE.Face3(); + faceA.a = faces[ offset ]; + faceA.b = faces[ offset + 1 ]; + faceA.c = faces[ offset + 3 ]; - "shadowKernel[0] = mix( shadowKernel[1], shadowKernel[0], fractionalCoord.x );", - "shadowKernel[1] = mix( shadowKernel[2], shadowKernel[1], fractionalCoord.x );", + faceB = new THREE.Face3(); + faceB.a = faces[ offset + 1 ]; + faceB.b = faces[ offset + 2 ]; + faceB.c = faces[ offset + 3 ]; - "vec4 shadowValues;", - "shadowValues.x = mix( shadowKernel[0][1], shadowKernel[0][0], fractionalCoord.y );", - "shadowValues.y = mix( shadowKernel[0][2], shadowKernel[0][1], fractionalCoord.y );", - "shadowValues.z = mix( shadowKernel[1][1], shadowKernel[1][0], fractionalCoord.y );", - "shadowValues.w = mix( shadowKernel[1][2], shadowKernel[1][1], fractionalCoord.y );", + offset += 4; - "shadow = dot( shadowValues, vec4( 1.0 ) );", + if ( hasMaterial ) { - "shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );", + materialIndex = faces[ offset ++ ]; + faceA.materialIndex = materialIndex; + faceB.materialIndex = materialIndex; - "#else", + } - "vec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );", - "float fDepth = unpackDepth( rgbaDepth );", + // to get face <=> uv index correspondence - "if ( fDepth < shadowCoord.z )", + fi = geometry.faces.length; - // spot with multiple shadows is darker + if ( hasFaceVertexUv ) { - "shadowColor = shadowColor * vec3( 1.0 - shadowDarkness[ i ] );", + for ( i = 0; i < nUvLayers; i ++ ) { - // spot with multiple shadows has the same color as single shadow spot + uvLayer = json.uvs[ i ]; - //"shadowColor = min( shadowColor, vec3( shadowDarkness[ i ] ) );", + geometry.faceVertexUvs[ i ][ fi ] = []; + geometry.faceVertexUvs[ i ][ fi + 1 ] = []; - "#endif", + for ( j = 0; j < 4; j ++ ) { - "}", + uvIndex = faces[ offset ++ ]; + u = uvLayer[ uvIndex * 2 ]; + v = uvLayer[ uvIndex * 2 + 1 ]; - "#ifdef SHADOWMAP_DEBUG", + uv = new THREE.Vector2( u, v ); - "#ifdef SHADOWMAP_CASCADE", + if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv ); + if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv ); - "if ( inFrustum && inFrustumCount == 1 ) gl_FragColor.xyz *= frustumColors[ i ];", + } - "#else", + } - "if ( inFrustum ) gl_FragColor.xyz *= frustumColors[ i ];", + } - "#endif", + if ( hasFaceNormal ) { - "#endif", + normalIndex = faces[ offset ++ ] * 3; - "}", + faceA.normal.set( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); - "#ifdef GAMMA_OUTPUT", + faceB.normal.copy( faceA.normal ); - "shadowColor *= shadowColor;", + } - "#endif", + if ( hasFaceVertexNormal ) { - "gl_FragColor.xyz = gl_FragColor.xyz * shadowColor;", + for ( i = 0; i < 4; i ++ ) { - "#endif" + normalIndex = faces[ offset ++ ] * 3; - ].join("\n"), + normal = new THREE.Vector3( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); - shadowmap_pars_vertex: [ - "#ifdef USE_SHADOWMAP", + if ( i !== 2 ) faceA.vertexNormals.push( normal ); + if ( i !== 0 ) faceB.vertexNormals.push( normal ); - "varying vec4 vShadowCoord[ MAX_SHADOWS ];", - "uniform mat4 shadowMatrix[ MAX_SHADOWS ];", + } - "#endif" + } - ].join("\n"), - shadowmap_vertex: [ + if ( hasFaceColor ) { - "#ifdef USE_SHADOWMAP", + colorIndex = faces[ offset ++ ]; + hex = colors[ colorIndex ]; - "for( int i = 0; i < MAX_SHADOWS; i ++ ) {", + faceA.color.setHex( hex ); + faceB.color.setHex( hex ); - "vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;", + } - "}", - "#endif" + if ( hasFaceVertexColor ) { - ].join("\n"), + for ( i = 0; i < 4; i ++ ) { - // ALPHATEST + colorIndex = faces[ offset ++ ]; + hex = colors[ colorIndex ]; - alphatest_fragment: [ + if ( i !== 2 ) faceA.vertexColors.push( new THREE.Color( hex ) ); + if ( i !== 0 ) faceB.vertexColors.push( new THREE.Color( hex ) ); - "#ifdef ALPHATEST", + } - "if ( gl_FragColor.a < ALPHATEST ) discard;", + } - "#endif" + geometry.faces.push( faceA ); + geometry.faces.push( faceB ); - ].join("\n"), + } else { - // LINEAR SPACE + face = new THREE.Face3(); + face.a = faces[ offset ++ ]; + face.b = faces[ offset ++ ]; + face.c = faces[ offset ++ ]; - linear_to_gamma_fragment: [ + if ( hasMaterial ) { - "#ifdef GAMMA_OUTPUT", + materialIndex = faces[ offset ++ ]; + face.materialIndex = materialIndex; - "gl_FragColor.xyz = sqrt( gl_FragColor.xyz );", + } - "#endif" + // to get face <=> uv index correspondence - ].join("\n") + fi = geometry.faces.length; + if ( hasFaceVertexUv ) { -}; + for ( i = 0; i < nUvLayers; i ++ ) { -THREE.UniformsUtils = { + uvLayer = json.uvs[ i ]; - merge: function ( uniforms ) { + geometry.faceVertexUvs[ i ][ fi ] = []; - var u, p, tmp, merged = {}; + for ( j = 0; j < 3; j ++ ) { - for ( u = 0; u < uniforms.length; u ++ ) { + uvIndex = faces[ offset ++ ]; - tmp = this.clone( uniforms[ u ] ); + u = uvLayer[ uvIndex * 2 ]; + v = uvLayer[ uvIndex * 2 + 1 ]; - for ( p in tmp ) { + uv = new THREE.Vector2( u, v ); - merged[ p ] = tmp[ p ]; + geometry.faceVertexUvs[ i ][ fi ].push( uv ); - } + } - } + } - return merged; + } - }, + if ( hasFaceNormal ) { - clone: function ( uniforms_src ) { + normalIndex = faces[ offset ++ ] * 3; - var u, p, parameter, parameter_src, uniforms_dst = {}; + face.normal.set( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); - for ( u in uniforms_src ) { + } - uniforms_dst[ u ] = {}; + if ( hasFaceVertexNormal ) { - for ( p in uniforms_src[ u ] ) { + for ( i = 0; i < 3; i ++ ) { - parameter_src = uniforms_src[ u ][ p ]; + normalIndex = faces[ offset ++ ] * 3; - if ( parameter_src instanceof THREE.Color || - parameter_src instanceof THREE.Vector2 || - parameter_src instanceof THREE.Vector3 || - parameter_src instanceof THREE.Vector4 || - parameter_src instanceof THREE.Matrix4 || - parameter_src instanceof THREE.Texture ) { + normal = new THREE.Vector3( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); - uniforms_dst[ u ][ p ] = parameter_src.clone(); + face.vertexNormals.push( normal ); - } else if ( parameter_src instanceof Array ) { + } - uniforms_dst[ u ][ p ] = parameter_src.slice(); + } - } else { - uniforms_dst[ u ][ p ] = parameter_src; + if ( hasFaceColor ) { - } + colorIndex = faces[ offset ++ ]; + face.color.setHex( colors[ colorIndex ] ); - } + } - } - return uniforms_dst; + if ( hasFaceVertexColor ) { - } + for ( i = 0; i < 3; i ++ ) { -}; + colorIndex = faces[ offset ++ ]; + face.vertexColors.push( new THREE.Color( colors[ colorIndex ] ) ); -THREE.UniformsLib = { + } - common: { + } - "diffuse" : { type: "c", value: new THREE.Color( 0xeeeeee ) }, - "opacity" : { type: "f", value: 1.0 }, + geometry.faces.push( face ); - "map" : { type: "t", value: null }, - "offsetRepeat" : { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) }, + } - "lightMap" : { type: "t", value: null }, - "specularMap" : { type: "t", value: null }, + } - "envMap" : { type: "t", value: null }, - "flipEnvMap" : { type: "f", value: -1 }, - "useRefract" : { type: "i", value: 0 }, - "reflectivity" : { type: "f", value: 1.0 }, - "refractionRatio" : { type: "f", value: 0.98 }, - "combine" : { type: "i", value: 0 }, + } - "morphTargetInfluences" : { type: "f", value: 0 } + function parseSkin() { - }, + var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2; - bump: { + if ( json.skinWeights ) { - "bumpMap" : { type: "t", value: null }, - "bumpScale" : { type: "f", value: 1 } + for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) { - }, + var x = json.skinWeights[ i ]; + var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0; + var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0; + var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0; - normalmap: { + geometry.skinWeights.push( new THREE.Vector4( x, y, z, w ) ); - "normalMap" : { type: "t", value: null }, - "normalScale" : { type: "v2", value: new THREE.Vector2( 1, 1 ) } - }, - - 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 ) } + } - }, + if ( json.skinIndices ) { - lights: { + for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) { - "ambientLightColor" : { type: "fv", value: [] }, + var a = json.skinIndices[ i ]; + var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0; + var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0; + var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0; - "directionalLightDirection" : { type: "fv", value: [] }, - "directionalLightColor" : { type: "fv", value: [] }, + geometry.skinIndices.push( new THREE.Vector4( a, b, c, d ) ); - "hemisphereLightDirection" : { type: "fv", value: [] }, - "hemisphereLightSkyColor" : { type: "fv", value: [] }, - "hemisphereLightGroundColor" : { type: "fv", value: [] }, + } - "pointLightColor" : { type: "fv", value: [] }, - "pointLightPosition" : { type: "fv", value: [] }, - "pointLightDistance" : { type: "fv1", value: [] }, + } - "spotLightColor" : { type: "fv", value: [] }, - "spotLightPosition" : { type: "fv", value: [] }, - "spotLightDirection" : { type: "fv", value: [] }, - "spotLightDistance" : { type: "fv1", value: [] }, - "spotLightAngleCos" : { type: "fv1", value: [] }, - "spotLightExponent" : { type: "fv1", value: [] } + geometry.bones = json.bones; - }, + if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) { - particle: { + console.warn( 'When skinning, number of vertices (' + geometry.vertices.length + '), skinIndices (' + + geometry.skinIndices.length + '), and skinWeights (' + geometry.skinWeights.length + ') should match.' ); - "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 }, + } - "fogDensity" : { type: "f", value: 0.00025 }, - "fogNear" : { type: "f", value: 1 }, - "fogFar" : { type: "f", value: 2000 }, - "fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) } + } - }, + function parseMorphing( scale ) { - shadowmap: { + if ( json.morphTargets !== undefined ) { - "shadowMap": { type: "tv", value: [] }, - "shadowMapSize": { type: "v2v", value: [] }, + for ( var i = 0, l = json.morphTargets.length; i < l; i ++ ) { - "shadowBias" : { type: "fv1", value: [] }, - "shadowDarkness": { type: "fv1", value: [] }, + geometry.morphTargets[ i ] = {}; + geometry.morphTargets[ i ].name = json.morphTargets[ i ].name; + geometry.morphTargets[ i ].vertices = []; - "shadowMatrix" : { type: "m4v", value: [] } + var dstVertices = geometry.morphTargets[ i ].vertices; + var srcVertices = json.morphTargets[ i ].vertices; - } + for ( var 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.ShaderLib = { + dstVertices.push( vertex ); - 'basic': { + } - uniforms: THREE.UniformsUtils.merge( [ + } - THREE.UniformsLib[ "common" ], - THREE.UniformsLib[ "fog" ], - THREE.UniformsLib[ "shadowmap" ] + } - ] ), + if ( json.morphColors !== undefined && json.morphColors.length > 0 ) { - vertexShader: [ + console.warn( 'THREE.JSONLoader: "morphColors" no longer supported. Using them as face colors.' ); - THREE.ShaderChunk[ "map_pars_vertex" ], - THREE.ShaderChunk[ "lightmap_pars_vertex" ], - THREE.ShaderChunk[ "envmap_pars_vertex" ], - THREE.ShaderChunk[ "color_pars_vertex" ], - THREE.ShaderChunk[ "morphtarget_pars_vertex" ], - THREE.ShaderChunk[ "skinning_pars_vertex" ], - THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + var faces = geometry.faces; + var morphColors = json.morphColors[ 0 ].colors; - "void main() {", + for ( var i = 0, l = faces.length; i < l; i ++ ) { - THREE.ShaderChunk[ "map_vertex" ], - THREE.ShaderChunk[ "lightmap_vertex" ], - THREE.ShaderChunk[ "color_vertex" ], - THREE.ShaderChunk[ "skinbase_vertex" ], + faces[ i ].color.fromArray( morphColors, i * 3 ); - "#ifdef USE_ENVMAP", + } - THREE.ShaderChunk[ "morphnormal_vertex" ], - THREE.ShaderChunk[ "skinnormal_vertex" ], - THREE.ShaderChunk[ "defaultnormal_vertex" ], + } - "#endif", + } - THREE.ShaderChunk[ "morphtarget_vertex" ], - THREE.ShaderChunk[ "skinning_vertex" ], - THREE.ShaderChunk[ "default_vertex" ], + function parseAnimations() { - THREE.ShaderChunk[ "worldpos_vertex" ], - THREE.ShaderChunk[ "envmap_vertex" ], - THREE.ShaderChunk[ "shadowmap_vertex" ], + var outputAnimations = []; - "}" + // parse old style Bone/Hierarchy animations + var animations = []; - ].join("\n"), + if ( json.animation !== undefined ) { - fragmentShader: [ + animations.push( json.animation ); - "uniform vec3 diffuse;", - "uniform float opacity;", + } - THREE.ShaderChunk[ "color_pars_fragment" ], - THREE.ShaderChunk[ "map_pars_fragment" ], - THREE.ShaderChunk[ "lightmap_pars_fragment" ], - THREE.ShaderChunk[ "envmap_pars_fragment" ], - THREE.ShaderChunk[ "fog_pars_fragment" ], - THREE.ShaderChunk[ "shadowmap_pars_fragment" ], - THREE.ShaderChunk[ "specularmap_pars_fragment" ], + if ( json.animations !== undefined ) { - "void main() {", + if ( json.animations.length ) { - "gl_FragColor = vec4( diffuse, opacity );", + animations = animations.concat( json.animations ); - THREE.ShaderChunk[ "map_fragment" ], - THREE.ShaderChunk[ "alphatest_fragment" ], - THREE.ShaderChunk[ "specularmap_fragment" ], - THREE.ShaderChunk[ "lightmap_fragment" ], - THREE.ShaderChunk[ "color_fragment" ], - THREE.ShaderChunk[ "envmap_fragment" ], - THREE.ShaderChunk[ "shadowmap_fragment" ], + } else { - THREE.ShaderChunk[ "linear_to_gamma_fragment" ], + animations.push( json.animations ); - THREE.ShaderChunk[ "fog_fragment" ], + } - "}" + } - ].join("\n") + for ( var i = 0; i < animations.length; i ++ ) { - }, + var clip = THREE.AnimationClip.parseAnimation( animations[ i ], geometry.bones ); + if ( clip ) outputAnimations.push( clip ); - 'lambert': { + } - uniforms: THREE.UniformsUtils.merge( [ + // parse implicit morph animations + if ( geometry.morphTargets ) { - THREE.UniformsLib[ "common" ], - THREE.UniformsLib[ "fog" ], - THREE.UniformsLib[ "lights" ], - THREE.UniformsLib[ "shadowmap" ], + // 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 ); - { - "ambient" : { type: "c", value: new THREE.Color( 0xffffff ) }, - "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) }, - "wrapRGB" : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) } } - ] ), + if ( outputAnimations.length > 0 ) geometry.animations = outputAnimations; - vertexShader: [ + } - "#define LAMBERT", + if ( json.materials === undefined || json.materials.length === 0 ) { - "varying vec3 vLightFront;", + return { geometry: geometry }; - "#ifdef DOUBLE_SIDED", + } else { - "varying vec3 vLightBack;", + var materials = THREE.Loader.prototype.initMaterials( json.materials, texturePath, this.crossOrigin ); - "#endif", + return { geometry: geometry, materials: materials }; - THREE.ShaderChunk[ "map_pars_vertex" ], - THREE.ShaderChunk[ "lightmap_pars_vertex" ], - THREE.ShaderChunk[ "envmap_pars_vertex" ], - THREE.ShaderChunk[ "lights_lambert_pars_vertex" ], - THREE.ShaderChunk[ "color_pars_vertex" ], - THREE.ShaderChunk[ "morphtarget_pars_vertex" ], - THREE.ShaderChunk[ "skinning_pars_vertex" ], - THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + } - "void main() {", + } - THREE.ShaderChunk[ "map_vertex" ], - THREE.ShaderChunk[ "lightmap_vertex" ], - THREE.ShaderChunk[ "color_vertex" ], +} ); - THREE.ShaderChunk[ "morphnormal_vertex" ], - THREE.ShaderChunk[ "skinbase_vertex" ], - THREE.ShaderChunk[ "skinnormal_vertex" ], - THREE.ShaderChunk[ "defaultnormal_vertex" ], +// File:src/loaders/LoadingManager.js - THREE.ShaderChunk[ "morphtarget_vertex" ], - THREE.ShaderChunk[ "skinning_vertex" ], - THREE.ShaderChunk[ "default_vertex" ], +/** + * @author mrdoob / http://mrdoob.com/ + */ - THREE.ShaderChunk[ "worldpos_vertex" ], - THREE.ShaderChunk[ "envmap_vertex" ], - THREE.ShaderChunk[ "lights_lambert_vertex" ], - THREE.ShaderChunk[ "shadowmap_vertex" ], +THREE.LoadingManager = function ( onLoad, onProgress, onError ) { - "}" + var scope = this; - ].join("\n"), + var isLoading = false, itemsLoaded = 0, itemsTotal = 0; - fragmentShader: [ + this.onStart = undefined; + this.onLoad = onLoad; + this.onProgress = onProgress; + this.onError = onError; - "uniform float opacity;", + this.itemStart = function ( url ) { - "varying vec3 vLightFront;", + itemsTotal ++; - "#ifdef DOUBLE_SIDED", + if ( isLoading === false ) { - "varying vec3 vLightBack;", + if ( scope.onStart !== undefined ) { - "#endif", + scope.onStart( url, itemsLoaded, itemsTotal ); - THREE.ShaderChunk[ "color_pars_fragment" ], - THREE.ShaderChunk[ "map_pars_fragment" ], - THREE.ShaderChunk[ "lightmap_pars_fragment" ], - THREE.ShaderChunk[ "envmap_pars_fragment" ], - THREE.ShaderChunk[ "fog_pars_fragment" ], - THREE.ShaderChunk[ "shadowmap_pars_fragment" ], - THREE.ShaderChunk[ "specularmap_pars_fragment" ], + } - "void main() {", + } - "gl_FragColor = vec4( vec3 ( 1.0 ), opacity );", + isLoading = true; - THREE.ShaderChunk[ "map_fragment" ], - THREE.ShaderChunk[ "alphatest_fragment" ], - THREE.ShaderChunk[ "specularmap_fragment" ], + }; - "#ifdef DOUBLE_SIDED", + this.itemEnd = function ( url ) { - //"float isFront = float( gl_FrontFacing );", - //"gl_FragColor.xyz *= isFront * vLightFront + ( 1.0 - isFront ) * vLightBack;", + itemsLoaded ++; - "if ( gl_FrontFacing )", - "gl_FragColor.xyz *= vLightFront;", - "else", - "gl_FragColor.xyz *= vLightBack;", + if ( scope.onProgress !== undefined ) { - "#else", + scope.onProgress( url, itemsLoaded, itemsTotal ); - "gl_FragColor.xyz *= vLightFront;", + } - "#endif", + if ( itemsLoaded === itemsTotal ) { - THREE.ShaderChunk[ "lightmap_fragment" ], - THREE.ShaderChunk[ "color_fragment" ], - THREE.ShaderChunk[ "envmap_fragment" ], - THREE.ShaderChunk[ "shadowmap_fragment" ], + isLoading = false; - THREE.ShaderChunk[ "linear_to_gamma_fragment" ], + if ( scope.onLoad !== undefined ) { - THREE.ShaderChunk[ "fog_fragment" ], + scope.onLoad(); - "}" + } - ].join("\n") + } - }, + }; - 'phong': { + this.itemError = function ( url ) { - uniforms: THREE.UniformsUtils.merge( [ + if ( scope.onError !== undefined ) { - THREE.UniformsLib[ "common" ], - THREE.UniformsLib[ "bump" ], - THREE.UniformsLib[ "normalmap" ], - THREE.UniformsLib[ "fog" ], - THREE.UniformsLib[ "lights" ], - THREE.UniformsLib[ "shadowmap" ], + scope.onError( url ); - { - "ambient" : { type: "c", value: new THREE.Color( 0xffffff ) }, - "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) }, - "specular" : { type: "c", value: new THREE.Color( 0x111111 ) }, - "shininess": { type: "f", value: 30 }, - "wrapRGB" : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) } - } + } - ] ), + }; - vertexShader: [ +}; - "#define PHONG", +THREE.DefaultLoadingManager = new THREE.LoadingManager(); - "varying vec3 vViewPosition;", - "varying vec3 vNormal;", +// File:src/loaders/BufferGeometryLoader.js - THREE.ShaderChunk[ "map_pars_vertex" ], - THREE.ShaderChunk[ "lightmap_pars_vertex" ], - THREE.ShaderChunk[ "envmap_pars_vertex" ], - THREE.ShaderChunk[ "lights_phong_pars_vertex" ], - THREE.ShaderChunk[ "color_pars_vertex" ], - THREE.ShaderChunk[ "morphtarget_pars_vertex" ], - THREE.ShaderChunk[ "skinning_pars_vertex" ], - THREE.ShaderChunk[ "shadowmap_pars_vertex" ], +/** + * @author mrdoob / http://mrdoob.com/ + */ - "void main() {", +THREE.BufferGeometryLoader = function ( manager ) { - THREE.ShaderChunk[ "map_vertex" ], - THREE.ShaderChunk[ "lightmap_vertex" ], - THREE.ShaderChunk[ "color_vertex" ], + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; - THREE.ShaderChunk[ "morphnormal_vertex" ], - THREE.ShaderChunk[ "skinbase_vertex" ], - THREE.ShaderChunk[ "skinnormal_vertex" ], - THREE.ShaderChunk[ "defaultnormal_vertex" ], +}; - "vNormal = normalize( transformedNormal );", +Object.assign( THREE.BufferGeometryLoader.prototype, { - THREE.ShaderChunk[ "morphtarget_vertex" ], - THREE.ShaderChunk[ "skinning_vertex" ], - THREE.ShaderChunk[ "default_vertex" ], + load: function ( url, onLoad, onProgress, onError ) { - "vViewPosition = -mvPosition.xyz;", + var scope = this; - THREE.ShaderChunk[ "worldpos_vertex" ], - THREE.ShaderChunk[ "envmap_vertex" ], - THREE.ShaderChunk[ "lights_phong_vertex" ], - THREE.ShaderChunk[ "shadowmap_vertex" ], + var loader = new THREE.XHRLoader( scope.manager ); + loader.load( url, function ( text ) { - "}" + onLoad( scope.parse( JSON.parse( text ) ) ); - ].join("\n"), + }, onProgress, onError ); - fragmentShader: [ + }, - "uniform vec3 diffuse;", - "uniform float opacity;", + parse: function ( json ) { - "uniform vec3 ambient;", - "uniform vec3 emissive;", - "uniform vec3 specular;", - "uniform float shininess;", + var geometry = new THREE.BufferGeometry(); - THREE.ShaderChunk[ "color_pars_fragment" ], - THREE.ShaderChunk[ "map_pars_fragment" ], - THREE.ShaderChunk[ "lightmap_pars_fragment" ], - THREE.ShaderChunk[ "envmap_pars_fragment" ], - THREE.ShaderChunk[ "fog_pars_fragment" ], - THREE.ShaderChunk[ "lights_phong_pars_fragment" ], - THREE.ShaderChunk[ "shadowmap_pars_fragment" ], - THREE.ShaderChunk[ "bumpmap_pars_fragment" ], - THREE.ShaderChunk[ "normalmap_pars_fragment" ], - THREE.ShaderChunk[ "specularmap_pars_fragment" ], + var index = json.data.index; + + var TYPED_ARRAYS = { + 'Int8Array': Int8Array, + 'Uint8Array': Uint8Array, + 'Uint8ClampedArray': Uint8ClampedArray, + 'Int16Array': Int16Array, + 'Uint16Array': Uint16Array, + 'Int32Array': Int32Array, + 'Uint32Array': Uint32Array, + 'Float32Array': Float32Array, + 'Float64Array': Float64Array + }; - "void main() {", + if ( index !== undefined ) { - "gl_FragColor = vec4( vec3 ( 1.0 ), opacity );", + var typedArray = new TYPED_ARRAYS[ index.type ]( index.array ); + geometry.setIndex( new THREE.BufferAttribute( typedArray, 1 ) ); - THREE.ShaderChunk[ "map_fragment" ], - THREE.ShaderChunk[ "alphatest_fragment" ], - THREE.ShaderChunk[ "specularmap_fragment" ], + } - THREE.ShaderChunk[ "lights_phong_fragment" ], + var attributes = json.data.attributes; - THREE.ShaderChunk[ "lightmap_fragment" ], - THREE.ShaderChunk[ "color_fragment" ], - THREE.ShaderChunk[ "envmap_fragment" ], - THREE.ShaderChunk[ "shadowmap_fragment" ], + for ( var key in attributes ) { - THREE.ShaderChunk[ "linear_to_gamma_fragment" ], + var attribute = attributes[ key ]; + var typedArray = new TYPED_ARRAYS[ attribute.type ]( attribute.array ); - THREE.ShaderChunk[ "fog_fragment" ], + geometry.addAttribute( key, new THREE.BufferAttribute( typedArray, attribute.itemSize, attribute.normalized ) ); - "}" + } - ].join("\n") + var groups = json.data.groups || json.data.drawcalls || json.data.offsets; - }, + if ( groups !== undefined ) { - 'particle_basic': { + for ( var i = 0, n = groups.length; i !== n; ++ i ) { - uniforms: THREE.UniformsUtils.merge( [ + var group = groups[ i ]; - THREE.UniformsLib[ "particle" ], - THREE.UniformsLib[ "shadowmap" ] + geometry.addGroup( group.start, group.count, group.materialIndex ); - ] ), + } - vertexShader: [ + } - "uniform float size;", - "uniform float scale;", + var boundingSphere = json.data.boundingSphere; - THREE.ShaderChunk[ "color_pars_vertex" ], - THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + if ( boundingSphere !== undefined ) { - "void main() {", + var center = new THREE.Vector3(); - THREE.ShaderChunk[ "color_vertex" ], + if ( boundingSphere.center !== undefined ) { - "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + center.fromArray( boundingSphere.center ); - "#ifdef USE_SIZEATTENUATION", - "gl_PointSize = size * ( scale / length( mvPosition.xyz ) );", - "#else", - "gl_PointSize = size;", - "#endif", + } - "gl_Position = projectionMatrix * mvPosition;", + geometry.boundingSphere = new THREE.Sphere( center, boundingSphere.radius ); - THREE.ShaderChunk[ "worldpos_vertex" ], - THREE.ShaderChunk[ "shadowmap_vertex" ], + } - "}" + return geometry; - ].join("\n"), + } - fragmentShader: [ +} ); - "uniform vec3 psColor;", - "uniform float opacity;", +// File:src/loaders/MaterialLoader.js - THREE.ShaderChunk[ "color_pars_fragment" ], - THREE.ShaderChunk[ "map_particle_pars_fragment" ], - THREE.ShaderChunk[ "fog_pars_fragment" ], - THREE.ShaderChunk[ "shadowmap_pars_fragment" ], +/** + * @author mrdoob / http://mrdoob.com/ + */ - "void main() {", +THREE.MaterialLoader = function ( manager ) { - "gl_FragColor = vec4( psColor, opacity );", + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + this.textures = {}; - THREE.ShaderChunk[ "map_particle_fragment" ], - THREE.ShaderChunk[ "alphatest_fragment" ], - THREE.ShaderChunk[ "color_fragment" ], - THREE.ShaderChunk[ "shadowmap_fragment" ], - THREE.ShaderChunk[ "fog_fragment" ], +}; - "}" +Object.assign( THREE.MaterialLoader.prototype, { - ].join("\n") + load: function ( url, onLoad, onProgress, onError ) { - }, + var scope = this; - 'dashed': { + var loader = new THREE.XHRLoader( scope.manager ); + loader.load( url, function ( text ) { - uniforms: THREE.UniformsUtils.merge( [ + onLoad( scope.parse( JSON.parse( text ) ) ); - THREE.UniformsLib[ "common" ], - THREE.UniformsLib[ "fog" ], + }, onProgress, onError ); - { - "scale": { type: "f", value: 1 }, - "dashSize": { type: "f", value: 1 }, - "totalSize": { type: "f", value: 2 } - } + }, - ] ), + setTextures: function ( value ) { - vertexShader: [ + this.textures = value; - "uniform float scale;", - "attribute float lineDistance;", + }, - "varying float vLineDistance;", + getTexture: function ( name ) { - THREE.ShaderChunk[ "color_pars_vertex" ], + var textures = this.textures; - "void main() {", + if ( textures[ name ] === undefined ) { - THREE.ShaderChunk[ "color_vertex" ], + console.warn( 'THREE.MaterialLoader: Undefined texture', name ); - "vLineDistance = scale * lineDistance;", + } - "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", - "gl_Position = projectionMatrix * mvPosition;", + return textures[ name ]; - "}" + }, - ].join("\n"), + parse: function ( json ) { - fragmentShader: [ + var material = new THREE[ json.type ]; - "uniform vec3 diffuse;", - "uniform float opacity;", + if ( json.uuid !== undefined ) material.uuid = json.uuid; + if ( json.name !== undefined ) material.name = json.name; + if ( json.color !== undefined ) material.color.setHex( json.color ); + if ( json.roughness !== undefined ) material.roughness = json.roughness; + if ( json.metalness !== undefined ) material.metalness = json.metalness; + 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.colorWrite !== undefined ) material.colorWrite = json.colorWrite; + if ( json.wireframe !== undefined ) material.wireframe = json.wireframe; + if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth; - "uniform float dashSize;", - "uniform float totalSize;", + // for PointsMaterial + if ( json.size !== undefined ) material.size = json.size; + if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation; - "varying float vLineDistance;", + // maps - THREE.ShaderChunk[ "color_pars_fragment" ], - THREE.ShaderChunk[ "fog_pars_fragment" ], + if ( json.map !== undefined ) material.map = this.getTexture( json.map ); - "void main() {", + if ( json.alphaMap !== undefined ) { - "if ( mod( vLineDistance, totalSize ) > dashSize ) {", + material.alphaMap = this.getTexture( json.alphaMap ); + material.transparent = true; - "discard;", + } - "}", + if ( json.bumpMap !== undefined ) material.bumpMap = this.getTexture( json.bumpMap ); + if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale; - "gl_FragColor = vec4( diffuse, opacity );", + if ( json.normalMap !== undefined ) material.normalMap = this.getTexture( json.normalMap ); + if ( json.normalScale !== undefined ) { - THREE.ShaderChunk[ "color_fragment" ], - THREE.ShaderChunk[ "fog_fragment" ], + var normalScale = json.normalScale; - "}" + if ( Array.isArray( normalScale ) === false ) { - ].join("\n") + // Blender exporter used to export a scalar. See #7459 - }, + normalScale = [ normalScale, normalScale ]; - 'depth': { + } - uniforms: { + material.normalScale = new THREE.Vector2().fromArray( normalScale ); - "mNear": { type: "f", value: 1.0 }, - "mFar" : { type: "f", value: 2000.0 }, - "opacity" : { type: "f", value: 1.0 } + } - }, + 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; - vertexShader: [ + if ( json.roughnessMap !== undefined ) material.roughnessMap = this.getTexture( json.roughnessMap ); + if ( json.metalnessMap !== undefined ) material.metalnessMap = this.getTexture( json.metalnessMap ); - "void main() {", + if ( json.emissiveMap !== undefined ) material.emissiveMap = this.getTexture( json.emissiveMap ); + if ( json.emissiveIntensity !== undefined ) material.emissiveIntensity = json.emissiveIntensity; - "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + if ( json.specularMap !== undefined ) material.specularMap = this.getTexture( json.specularMap ); - "}" + if ( json.envMap !== undefined ) { - ].join("\n"), + material.envMap = this.getTexture( json.envMap ); + material.combine = THREE.MultiplyOperation; - fragmentShader: [ + } - "uniform float mNear;", - "uniform float mFar;", - "uniform float opacity;", + if ( json.reflectivity !== undefined ) material.reflectivity = json.reflectivity; - "void main() {", + if ( json.lightMap !== undefined ) material.lightMap = this.getTexture( json.lightMap ); + if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity; - "float depth = gl_FragCoord.z / gl_FragCoord.w;", - "float color = 1.0 - smoothstep( mNear, mFar, depth );", - "gl_FragColor = vec4( vec3( color ), opacity );", + if ( json.aoMap !== undefined ) material.aoMap = this.getTexture( json.aoMap ); + if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity; - "}" + // MultiMaterial - ].join("\n") + if ( json.materials !== undefined ) { - }, + for ( var i = 0, l = json.materials.length; i < l; i ++ ) { - 'normal': { + material.materials.push( this.parse( json.materials[ i ] ) ); - uniforms: { + } - "opacity" : { type: "f", value: 1.0 } + } - }, + return material; - vertexShader: [ + } - "varying vec3 vNormal;", +} ); - THREE.ShaderChunk[ "morphtarget_pars_vertex" ], +// File:src/loaders/ObjectLoader.js - "void main() {", +/** + * @author mrdoob / http://mrdoob.com/ + */ - "vNormal = normalize( normalMatrix * normal );", +THREE.ObjectLoader = function ( manager ) { - THREE.ShaderChunk[ "morphtarget_vertex" ], - THREE.ShaderChunk[ "default_vertex" ], + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; + this.texturePath = ''; - "}" +}; - ].join("\n"), +Object.assign( THREE.ObjectLoader.prototype, { - fragmentShader: [ + load: function ( url, onLoad, onProgress, onError ) { - "uniform float opacity;", - "varying vec3 vNormal;", + if ( this.texturePath === '' ) { - "void main() {", + this.texturePath = url.substring( 0, url.lastIndexOf( '/' ) + 1 ); - "gl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );", + } - "}" + var scope = this; - ].join("\n") + var loader = new THREE.XHRLoader( scope.manager ); + loader.load( url, function ( text ) { - }, + scope.parse( JSON.parse( text ), onLoad ); - /* ------------------------------------------------------------------------- - // Normal map shader - // - Blinn-Phong - // - normal + diffuse + specular + AO + displacement + reflection + shadow maps - // - point and directional lights (use with "lights: true" material option) - ------------------------------------------------------------------------- */ + }, onProgress, onError ); - 'normalmap' : { + }, - uniforms: THREE.UniformsUtils.merge( [ + setTexturePath: function ( value ) { - THREE.UniformsLib[ "fog" ], - THREE.UniformsLib[ "lights" ], - THREE.UniformsLib[ "shadowmap" ], + this.texturePath = value; - { + }, - "enableAO" : { type: "i", value: 0 }, - "enableDiffuse" : { type: "i", value: 0 }, - "enableSpecular" : { type: "i", value: 0 }, - "enableReflection": { type: "i", value: 0 }, - "enableDisplacement": { type: "i", value: 0 }, + setCrossOrigin: function ( value ) { - "tDisplacement": { type: "t", value: null }, // must go first as this is vertex texture - "tDiffuse" : { type: "t", value: null }, - "tCube" : { type: "t", value: null }, - "tNormal" : { type: "t", value: null }, - "tSpecular" : { type: "t", value: null }, - "tAO" : { type: "t", value: null }, + this.crossOrigin = value; - "uNormalScale": { type: "v2", value: new THREE.Vector2( 1, 1 ) }, + }, - "uDisplacementBias": { type: "f", value: 0.0 }, - "uDisplacementScale": { type: "f", value: 1.0 }, + parse: function ( json, onLoad ) { - "uDiffuseColor": { type: "c", value: new THREE.Color( 0xffffff ) }, - "uSpecularColor": { type: "c", value: new THREE.Color( 0x111111 ) }, - "uAmbientColor": { type: "c", value: new THREE.Color( 0xffffff ) }, - "uShininess": { type: "f", value: 30 }, - "uOpacity": { type: "f", value: 1 }, + var geometries = this.parseGeometries( json.geometries ); - "useRefract": { type: "i", value: 0 }, - "uRefractionRatio": { type: "f", value: 0.98 }, - "uReflectivity": { type: "f", value: 0.5 }, + var images = this.parseImages( json.images, function () { - "uOffset" : { type: "v2", value: new THREE.Vector2( 0, 0 ) }, - "uRepeat" : { type: "v2", value: new THREE.Vector2( 1, 1 ) }, + if ( onLoad !== undefined ) onLoad( object ); - "wrapRGB" : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) } + } ); - } + var textures = this.parseTextures( json.textures, images ); + var materials = this.parseMaterials( json.materials, textures ); - ] ), + var object = this.parseObject( json.object, geometries, materials ); - fragmentShader: [ + if ( json.animations ) { - "uniform vec3 uAmbientColor;", - "uniform vec3 uDiffuseColor;", - "uniform vec3 uSpecularColor;", - "uniform float uShininess;", - "uniform float uOpacity;", + object.animations = this.parseAnimations( json.animations ); - "uniform bool enableDiffuse;", - "uniform bool enableSpecular;", - "uniform bool enableAO;", - "uniform bool enableReflection;", + } - "uniform sampler2D tDiffuse;", - "uniform sampler2D tNormal;", - "uniform sampler2D tSpecular;", - "uniform sampler2D tAO;", + if ( json.images === undefined || json.images.length === 0 ) { - "uniform samplerCube tCube;", + if ( onLoad !== undefined ) onLoad( object ); - "uniform vec2 uNormalScale;", + } - "uniform bool useRefract;", - "uniform float uRefractionRatio;", - "uniform float uReflectivity;", + return object; - "varying vec3 vTangent;", - "varying vec3 vBinormal;", - "varying vec3 vNormal;", - "varying vec2 vUv;", + }, - "uniform vec3 ambientLightColor;", + parseGeometries: function ( json ) { - "#if MAX_DIR_LIGHTS > 0", + var geometries = {}; - "uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];", - "uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];", + if ( json !== undefined ) { - "#endif", + var geometryLoader = new THREE.JSONLoader(); + var bufferGeometryLoader = new THREE.BufferGeometryLoader(); - "#if MAX_HEMI_LIGHTS > 0", + for ( var i = 0, l = json.length; i < l; i ++ ) { - "uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];", - "uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];", - "uniform vec3 hemisphereLightDirection[ MAX_HEMI_LIGHTS ];", + var geometry; + var data = json[ i ]; - "#endif", + switch ( data.type ) { - "#if MAX_POINT_LIGHTS > 0", + case 'PlaneGeometry': + case 'PlaneBufferGeometry': - "uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];", - "uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];", - "uniform float pointLightDistance[ MAX_POINT_LIGHTS ];", + geometry = new THREE[ data.type ]( + data.width, + data.height, + data.widthSegments, + data.heightSegments + ); - "#endif", + break; - "#if MAX_SPOT_LIGHTS > 0", + case 'BoxGeometry': + case 'BoxBufferGeometry': + case 'CubeGeometry': // backwards compatible - "uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];", - "uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];", - "uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];", - "uniform float spotLightAngleCos[ MAX_SPOT_LIGHTS ];", - "uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];", - "uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];", + geometry = new THREE[ data.type ]( + data.width, + data.height, + data.depth, + data.widthSegments, + data.heightSegments, + data.depthSegments + ); - "#endif", + break; - "#ifdef WRAP_AROUND", + case 'CircleGeometry': + case 'CircleBufferGeometry': - "uniform vec3 wrapRGB;", + geometry = new THREE[ data.type ]( + data.radius, + data.segments, + data.thetaStart, + data.thetaLength + ); - "#endif", + break; - "varying vec3 vWorldPosition;", - "varying vec3 vViewPosition;", + case 'CylinderGeometry': + case 'CylinderBufferGeometry': - THREE.ShaderChunk[ "shadowmap_pars_fragment" ], - THREE.ShaderChunk[ "fog_pars_fragment" ], + geometry = new THREE[ data.type ]( + data.radiusTop, + data.radiusBottom, + data.height, + data.radialSegments, + data.heightSegments, + data.openEnded, + data.thetaStart, + data.thetaLength + ); - "void main() {", + break; - "gl_FragColor = vec4( vec3( 1.0 ), uOpacity );", + case 'ConeGeometry': + case 'ConeBufferGeometry': - "vec3 specularTex = vec3( 1.0 );", + geometry = new THREE [ data.type ]( + data.radius, + data.height, + data.radialSegments, + data.heightSegments, + data.openEnded, + data.thetaStart, + data.thetaLength + ); - "vec3 normalTex = texture2D( tNormal, vUv ).xyz * 2.0 - 1.0;", - "normalTex.xy *= uNormalScale;", - "normalTex = normalize( normalTex );", + break; - "if( enableDiffuse ) {", + case 'SphereGeometry': + case 'SphereBufferGeometry': - "#ifdef GAMMA_INPUT", + geometry = new THREE[ data.type ]( + data.radius, + data.widthSegments, + data.heightSegments, + data.phiStart, + data.phiLength, + data.thetaStart, + data.thetaLength + ); - "vec4 texelColor = texture2D( tDiffuse, vUv );", - "texelColor.xyz *= texelColor.xyz;", + break; - "gl_FragColor = gl_FragColor * texelColor;", + case 'DodecahedronGeometry': + case 'IcosahedronGeometry': + case 'OctahedronGeometry': + case 'TetrahedronGeometry': - "#else", + geometry = new THREE[ data.type ]( + data.radius, + data.detail + ); - "gl_FragColor = gl_FragColor * texture2D( tDiffuse, vUv );", + break; - "#endif", + case 'RingGeometry': + case 'RingBufferGeometry': - "}", + geometry = new THREE[ data.type ]( + data.innerRadius, + data.outerRadius, + data.thetaSegments, + data.phiSegments, + data.thetaStart, + data.thetaLength + ); - "if( enableAO ) {", + break; - "#ifdef GAMMA_INPUT", + case 'TorusGeometry': + case 'TorusBufferGeometry': - "vec4 aoColor = texture2D( tAO, vUv );", - "aoColor.xyz *= aoColor.xyz;", + geometry = new THREE[ data.type ]( + data.radius, + data.tube, + data.radialSegments, + data.tubularSegments, + data.arc + ); - "gl_FragColor.xyz = gl_FragColor.xyz * aoColor.xyz;", + break; - "#else", + case 'TorusKnotGeometry': + case 'TorusKnotBufferGeometry': - "gl_FragColor.xyz = gl_FragColor.xyz * texture2D( tAO, vUv ).xyz;", - - "#endif", - - "}", - - "if( enableSpecular )", - "specularTex = texture2D( tSpecular, vUv ).xyz;", - - "mat3 tsb = mat3( normalize( vTangent ), normalize( vBinormal ), normalize( vNormal ) );", - "vec3 finalNormal = tsb * normalTex;", + geometry = new THREE[ data.type ]( + data.radius, + data.tube, + data.tubularSegments, + data.radialSegments, + data.p, + data.q + ); - "#ifdef FLIP_SIDED", + break; - "finalNormal = -finalNormal;", + case 'LatheGeometry': + case 'LatheBufferGeometry': - "#endif", + geometry = new THREE[ data.type ]( + data.points, + data.segments, + data.phiStart, + data.phiLength + ); - "vec3 normal = normalize( finalNormal );", - "vec3 viewPosition = normalize( vViewPosition );", + break; - // point lights + case 'BufferGeometry': - "#if MAX_POINT_LIGHTS > 0", + geometry = bufferGeometryLoader.parse( data ); - "vec3 pointDiffuse = vec3( 0.0 );", - "vec3 pointSpecular = vec3( 0.0 );", + break; - "for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {", + case 'Geometry': - "vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );", - "vec3 pointVector = lPosition.xyz + vViewPosition.xyz;", + geometry = geometryLoader.parse( data.data, this.texturePath ).geometry; - "float pointDistance = 1.0;", - "if ( pointLightDistance[ i ] > 0.0 )", - "pointDistance = 1.0 - min( ( length( pointVector ) / pointLightDistance[ i ] ), 1.0 );", + break; - "pointVector = normalize( pointVector );", + default: - // diffuse + console.warn( 'THREE.ObjectLoader: Unsupported geometry type "' + data.type + '"' ); - "#ifdef WRAP_AROUND", + continue; - "float pointDiffuseWeightFull = max( dot( normal, pointVector ), 0.0 );", - "float pointDiffuseWeightHalf = max( 0.5 * dot( normal, pointVector ) + 0.5, 0.0 );", + } - "vec3 pointDiffuseWeight = mix( vec3 ( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );", + geometry.uuid = data.uuid; - "#else", + if ( data.name !== undefined ) geometry.name = data.name; - "float pointDiffuseWeight = max( dot( normal, pointVector ), 0.0 );", + geometries[ data.uuid ] = geometry; - "#endif", + } - "pointDiffuse += pointDistance * pointLightColor[ i ] * uDiffuseColor * pointDiffuseWeight;", + } - // specular + return geometries; - "vec3 pointHalfVector = normalize( pointVector + viewPosition );", - "float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );", - "float pointSpecularWeight = specularTex.r * max( pow( pointDotNormalHalf, uShininess ), 0.0 );", + }, - "#ifdef PHYSICALLY_BASED_SHADING", + parseMaterials: function ( json, textures ) { - // 2.0 => 2.0001 is hack to work around ANGLE bug + var materials = {}; - "float specularNormalization = ( uShininess + 2.0001 ) / 8.0;", + if ( json !== undefined ) { - "vec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( pointVector, pointHalfVector ), 5.0 );", - "pointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * pointDistance * specularNormalization;", + var loader = new THREE.MaterialLoader(); + loader.setTextures( textures ); - "#else", + for ( var i = 0, l = json.length; i < l; i ++ ) { - "pointSpecular += pointDistance * pointLightColor[ i ] * uSpecularColor * pointSpecularWeight * pointDiffuseWeight;", + var material = loader.parse( json[ i ] ); + materials[ material.uuid ] = material; - "#endif", + } - "}", + } - "#endif", + return materials; - // spot lights + }, - "#if MAX_SPOT_LIGHTS > 0", + parseAnimations: function ( json ) { - "vec3 spotDiffuse = vec3( 0.0 );", - "vec3 spotSpecular = vec3( 0.0 );", + var animations = []; - "for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {", + for ( var i = 0; i < json.length; i ++ ) { - "vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );", - "vec3 spotVector = lPosition.xyz + vViewPosition.xyz;", + var clip = THREE.AnimationClip.parse( json[ i ] ); - "float spotDistance = 1.0;", - "if ( spotLightDistance[ i ] > 0.0 )", - "spotDistance = 1.0 - min( ( length( spotVector ) / spotLightDistance[ i ] ), 1.0 );", + animations.push( clip ); - "spotVector = normalize( spotVector );", + } - "float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );", + return animations; - "if ( spotEffect > spotLightAngleCos[ i ] ) {", + }, - "spotEffect = max( pow( spotEffect, spotLightExponent[ i ] ), 0.0 );", + parseImages: function ( json, onLoad ) { - // diffuse + var scope = this; + var images = {}; - "#ifdef WRAP_AROUND", + function loadImage( url ) { - "float spotDiffuseWeightFull = max( dot( normal, spotVector ), 0.0 );", - "float spotDiffuseWeightHalf = max( 0.5 * dot( normal, spotVector ) + 0.5, 0.0 );", + scope.manager.itemStart( url ); - "vec3 spotDiffuseWeight = mix( vec3 ( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );", + return loader.load( url, function () { - "#else", + scope.manager.itemEnd( url ); - "float spotDiffuseWeight = max( dot( normal, spotVector ), 0.0 );", + } ); - "#endif", + } - "spotDiffuse += spotDistance * spotLightColor[ i ] * uDiffuseColor * spotDiffuseWeight * spotEffect;", + if ( json !== undefined && json.length > 0 ) { - // specular + var manager = new THREE.LoadingManager( onLoad ); - "vec3 spotHalfVector = normalize( spotVector + viewPosition );", - "float spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );", - "float spotSpecularWeight = specularTex.r * max( pow( spotDotNormalHalf, uShininess ), 0.0 );", + var loader = new THREE.ImageLoader( manager ); + loader.setCrossOrigin( this.crossOrigin ); - "#ifdef PHYSICALLY_BASED_SHADING", + for ( var i = 0, l = json.length; i < l; i ++ ) { - // 2.0 => 2.0001 is hack to work around ANGLE bug + var image = json[ i ]; + var path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( image.url ) ? image.url : scope.texturePath + image.url; - "float specularNormalization = ( uShininess + 2.0001 ) / 8.0;", + images[ image.uuid ] = loadImage( path ); - "vec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( spotVector, spotHalfVector ), 5.0 );", - "spotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * spotDistance * specularNormalization * spotEffect;", + } - "#else", + } - "spotSpecular += spotDistance * spotLightColor[ i ] * uSpecularColor * spotSpecularWeight * spotDiffuseWeight * spotEffect;", + return images; - "#endif", + }, - "}", + parseTextures: function ( json, images ) { - "}", + function parseConstant( value ) { - "#endif", + if ( typeof( value ) === 'number' ) return value; - // directional lights + console.warn( 'THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value ); - "#if MAX_DIR_LIGHTS > 0", + return THREE[ value ]; - "vec3 dirDiffuse = vec3( 0.0 );", - "vec3 dirSpecular = vec3( 0.0 );", + } - "for( int i = 0; i < MAX_DIR_LIGHTS; i++ ) {", + var textures = {}; - "vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );", - "vec3 dirVector = normalize( lDirection.xyz );", + if ( json !== undefined ) { - // diffuse + for ( var i = 0, l = json.length; i < l; i ++ ) { - "#ifdef WRAP_AROUND", + var data = json[ i ]; - "float directionalLightWeightingFull = max( dot( normal, dirVector ), 0.0 );", - "float directionalLightWeightingHalf = max( 0.5 * dot( normal, dirVector ) + 0.5, 0.0 );", + if ( data.image === undefined ) { - "vec3 dirDiffuseWeight = mix( vec3( directionalLightWeightingFull ), vec3( directionalLightWeightingHalf ), wrapRGB );", + console.warn( 'THREE.ObjectLoader: No "image" specified for', data.uuid ); - "#else", + } - "float dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );", + if ( images[ data.image ] === undefined ) { - "#endif", + console.warn( 'THREE.ObjectLoader: Undefined image', data.image ); - "dirDiffuse += directionalLightColor[ i ] * uDiffuseColor * dirDiffuseWeight;", + } - // specular + var texture = new THREE.Texture( images[ data.image ] ); + texture.needsUpdate = true; - "vec3 dirHalfVector = normalize( dirVector + viewPosition );", - "float dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );", - "float dirSpecularWeight = specularTex.r * max( pow( dirDotNormalHalf, uShininess ), 0.0 );", + texture.uuid = data.uuid; - "#ifdef PHYSICALLY_BASED_SHADING", + if ( data.name !== undefined ) texture.name = data.name; - // 2.0 => 2.0001 is hack to work around ANGLE bug + if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping ); - "float specularNormalization = ( uShininess + 2.0001 ) / 8.0;", + if ( data.offset !== undefined ) texture.offset.fromArray( data.offset ); + if ( data.repeat !== undefined ) texture.repeat.fromArray( data.repeat ); + if ( data.wrap !== undefined ) { - "vec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( dirVector, dirHalfVector ), 5.0 );", - "dirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;", + texture.wrapS = parseConstant( data.wrap[ 0 ] ); + texture.wrapT = parseConstant( data.wrap[ 1 ] ); - "#else", + } - "dirSpecular += directionalLightColor[ i ] * uSpecularColor * dirSpecularWeight * dirDiffuseWeight;", + 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; - "#endif", + if ( data.flipY !== undefined ) texture.flipY = data.flipY; - "}", + textures[ data.uuid ] = texture; - "#endif", + } - // hemisphere lights + } - "#if MAX_HEMI_LIGHTS > 0", + return textures; - "vec3 hemiDiffuse = vec3( 0.0 );", - "vec3 hemiSpecular = vec3( 0.0 );" , + }, - "for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {", + parseObject: function () { - "vec4 lDirection = viewMatrix * vec4( hemisphereLightDirection[ i ], 0.0 );", - "vec3 lVector = normalize( lDirection.xyz );", + var matrix = new THREE.Matrix4(); - // diffuse + return function parseObject( data, geometries, materials ) { - "float dotProduct = dot( normal, lVector );", - "float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;", + var object; - "vec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );", + function getGeometry( name ) { - "hemiDiffuse += uDiffuseColor * hemiColor;", + if ( geometries[ name ] === undefined ) { - // specular (sky light) + console.warn( 'THREE.ObjectLoader: Undefined geometry', name ); + } - "vec3 hemiHalfVectorSky = normalize( lVector + viewPosition );", - "float hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;", - "float hemiSpecularWeightSky = specularTex.r * max( pow( hemiDotNormalHalfSky, uShininess ), 0.0 );", + return geometries[ name ]; - // specular (ground light) + } - "vec3 lVectorGround = -lVector;", + function getMaterial( name ) { - "vec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );", - "float hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;", - "float hemiSpecularWeightGround = specularTex.r * max( pow( hemiDotNormalHalfGround, uShininess ), 0.0 );", + if ( name === undefined ) return undefined; - "#ifdef PHYSICALLY_BASED_SHADING", + if ( materials[ name ] === undefined ) { - "float dotProductGround = dot( normal, lVectorGround );", + console.warn( 'THREE.ObjectLoader: Undefined material', name ); - // 2.0 => 2.0001 is hack to work around ANGLE bug + } - "float specularNormalization = ( uShininess + 2.0001 ) / 8.0;", + return materials[ name ]; - "vec3 schlickSky = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( lVector, hemiHalfVectorSky ), 5.0 );", - "vec3 schlickGround = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 5.0 );", - "hemiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );", + } - "#else", + switch ( data.type ) { - "hemiSpecular += uSpecularColor * hemiColor * ( hemiSpecularWeightSky + hemiSpecularWeightGround ) * hemiDiffuseWeight;", + case 'Scene': - "#endif", + object = new THREE.Scene(); - "}", + break; - "#endif", + case 'PerspectiveCamera': - // all lights contribution summation + object = new THREE.PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); - "vec3 totalDiffuse = vec3( 0.0 );", - "vec3 totalSpecular = vec3( 0.0 );", + if ( data.focus !== undefined ) object.focus = data.focus; + if ( data.zoom !== undefined ) object.zoom = data.zoom; + if ( data.filmGauge !== undefined ) object.filmGauge = data.filmGauge; + if ( data.filmOffset !== undefined ) object.filmOffset = data.filmOffset; + if ( data.view !== undefined ) object.view = Object.assign( {}, data.view ); - "#if MAX_DIR_LIGHTS > 0", + break; - "totalDiffuse += dirDiffuse;", - "totalSpecular += dirSpecular;", + case 'OrthographicCamera': - "#endif", + object = new THREE.OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); - "#if MAX_HEMI_LIGHTS > 0", + break; - "totalDiffuse += hemiDiffuse;", - "totalSpecular += hemiSpecular;", + case 'AmbientLight': - "#endif", + object = new THREE.AmbientLight( data.color, data.intensity ); - "#if MAX_POINT_LIGHTS > 0", + break; - "totalDiffuse += pointDiffuse;", - "totalSpecular += pointSpecular;", + case 'DirectionalLight': - "#endif", + object = new THREE.DirectionalLight( data.color, data.intensity ); - "#if MAX_SPOT_LIGHTS > 0", + break; - "totalDiffuse += spotDiffuse;", - "totalSpecular += spotSpecular;", + case 'PointLight': - "#endif", + object = new THREE.PointLight( data.color, data.intensity, data.distance, data.decay ); - "#ifdef METAL", + break; - "gl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * uAmbientColor + totalSpecular );", + case 'SpotLight': - "#else", + object = new THREE.SpotLight( data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay ); - "gl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * uAmbientColor ) + totalSpecular;", + break; - "#endif", + case 'HemisphereLight': - "if ( enableReflection ) {", + object = new THREE.HemisphereLight( data.color, data.groundColor, data.intensity ); - "vec3 vReflect;", - "vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );", + break; - "if ( useRefract ) {", + case 'Mesh': - "vReflect = refract( cameraToVertex, normal, uRefractionRatio );", + var geometry = getGeometry( data.geometry ); + var material = getMaterial( data.material ); - "} else {", + if ( geometry.bones && geometry.bones.length > 0 ) { - "vReflect = reflect( cameraToVertex, normal );", + object = new THREE.SkinnedMesh( geometry, material ); - "}", + } else { - "vec4 cubeColor = textureCube( tCube, vec3( -vReflect.x, vReflect.yz ) );", + object = new THREE.Mesh( geometry, material ); - "#ifdef GAMMA_INPUT", + } - "cubeColor.xyz *= cubeColor.xyz;", + break; - "#endif", + case 'LOD': - "gl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularTex.r * uReflectivity );", + object = new THREE.LOD(); - "}", + break; - THREE.ShaderChunk[ "shadowmap_fragment" ], - THREE.ShaderChunk[ "linear_to_gamma_fragment" ], - THREE.ShaderChunk[ "fog_fragment" ], + case 'Line': - "}" + object = new THREE.Line( getGeometry( data.geometry ), getMaterial( data.material ), data.mode ); - ].join("\n"), + break; - vertexShader: [ + case 'PointCloud': + case 'Points': - "attribute vec4 tangent;", + object = new THREE.Points( getGeometry( data.geometry ), getMaterial( data.material ) ); - "uniform vec2 uOffset;", - "uniform vec2 uRepeat;", + break; - "uniform bool enableDisplacement;", + case 'Sprite': - "#ifdef VERTEX_TEXTURES", + object = new THREE.Sprite( getMaterial( data.material ) ); - "uniform sampler2D tDisplacement;", - "uniform float uDisplacementScale;", - "uniform float uDisplacementBias;", + break; - "#endif", + case 'Group': - "varying vec3 vTangent;", - "varying vec3 vBinormal;", - "varying vec3 vNormal;", - "varying vec2 vUv;", + object = new THREE.Group(); - "varying vec3 vWorldPosition;", - "varying vec3 vViewPosition;", + break; - THREE.ShaderChunk[ "skinning_pars_vertex" ], - THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + default: - "void main() {", + object = new THREE.Object3D(); - THREE.ShaderChunk[ "skinbase_vertex" ], - THREE.ShaderChunk[ "skinnormal_vertex" ], + } - // normal, tangent and binormal vectors + object.uuid = data.uuid; - "#ifdef USE_SKINNING", + if ( data.name !== undefined ) object.name = data.name; + if ( data.matrix !== undefined ) { - "vNormal = normalize( normalMatrix * skinnedNormal.xyz );", + matrix.fromArray( data.matrix ); + matrix.decompose( object.position, object.quaternion, object.scale ); - "vec4 skinnedTangent = skinMatrix * vec4( tangent.xyz, 0.0 );", - "vTangent = normalize( normalMatrix * skinnedTangent.xyz );", + } else { - "#else", + 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 ); - "vNormal = normalize( normalMatrix * normal );", - "vTangent = normalize( normalMatrix * tangent.xyz );", + } - "#endif", + if ( data.castShadow !== undefined ) object.castShadow = data.castShadow; + if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow; - "vBinormal = normalize( cross( vNormal, vTangent ) * tangent.w );", + if ( data.visible !== undefined ) object.visible = data.visible; + if ( data.userData !== undefined ) object.userData = data.userData; - "vUv = uv * uRepeat + uOffset;", + if ( data.children !== undefined ) { - // displacement mapping + for ( var child in data.children ) { - "vec3 displacedPosition;", + object.add( this.parseObject( data.children[ child ], geometries, materials ) ); - "#ifdef VERTEX_TEXTURES", + } - "if ( enableDisplacement ) {", + } - "vec3 dv = texture2D( tDisplacement, uv ).xyz;", - "float df = uDisplacementScale * dv.x + uDisplacementBias;", - "displacedPosition = position + normalize( normal ) * df;", + if ( data.type === 'LOD' ) { - "} else {", + var levels = data.levels; - "#ifdef USE_SKINNING", + for ( var l = 0; l < levels.length; l ++ ) { - "vec4 skinVertex = vec4( position, 1.0 );", + var level = levels[ l ]; + var child = object.getObjectByProperty( 'uuid', level.object ); - "vec4 skinned = boneMatX * skinVertex * skinWeight.x;", - "skinned += boneMatY * skinVertex * skinWeight.y;", + if ( child !== undefined ) { - "displacedPosition = skinned.xyz;", + object.addLevel( child, level.distance ); - "#else", + } - "displacedPosition = position;", + } - "#endif", + } - "}", + return object; - "#else", + }; - "#ifdef USE_SKINNING", + }() - "vec4 skinVertex = vec4( position, 1.0 );", +} ); - "vec4 skinned = boneMatX * skinVertex * skinWeight.x;", - "skinned += boneMatY * skinVertex * skinWeight.y;", +// File:src/loaders/TextureLoader.js - "displacedPosition = skinned.xyz;", +/** + * @author mrdoob / http://mrdoob.com/ + */ - "#else", +THREE.TextureLoader = function ( manager ) { - "displacedPosition = position;", + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; - "#endif", +}; - "#endif", +Object.assign( THREE.TextureLoader.prototype, { - // + load: function ( url, onLoad, onProgress, onError ) { - "vec4 mvPosition = modelViewMatrix * vec4( displacedPosition, 1.0 );", - "vec4 worldPosition = modelMatrix * vec4( displacedPosition, 1.0 );", + var texture = new THREE.Texture(); - "gl_Position = projectionMatrix * mvPosition;", + var loader = new THREE.ImageLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.setPath( this.path ); + loader.load( url, function ( image ) { - // + // JPEGs can't have an alpha channel, so memory can be saved by storing them as RGB. + var isJPEG = url.search( /\.(jpg|jpeg)$/ ) > 0 || url.search( /^data\:image\/jpeg/ ) === 0; - "vWorldPosition = worldPosition.xyz;", - "vViewPosition = -mvPosition.xyz;", + texture.format = isJPEG ? THREE.RGBFormat : THREE.RGBAFormat; + texture.image = image; + texture.needsUpdate = true; - // shadows + if ( onLoad !== undefined ) { - "#ifdef USE_SHADOWMAP", + onLoad( texture ); - "for( int i = 0; i < MAX_SHADOWS; i ++ ) {", + } - "vShadowCoord[ i ] = shadowMatrix[ i ] * worldPosition;", + }, onProgress, onError ); - "}", + return texture; - "#endif", + }, - "}" + setCrossOrigin: function ( value ) { - ].join("\n") + this.crossOrigin = value; + return this; }, - /* ------------------------------------------------------------------------- - // Cube map shader - ------------------------------------------------------------------------- */ - - 'cube': { - - uniforms: { "tCube": { type: "t", value: null }, - "tFlip": { type: "f", value: -1 } }, + setPath: function ( value ) { - vertexShader: [ + this.path = value; + return this; - "varying vec3 vWorldPosition;", + } - "void main() {", +} ); - "vec4 worldPosition = modelMatrix * vec4( position, 1.0 );", - "vWorldPosition = worldPosition.xyz;", +// File:src/loaders/CubeTextureLoader.js - "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", +/** + * @author mrdoob / http://mrdoob.com/ + */ - "}" +THREE.CubeTextureLoader = function ( manager ) { - ].join("\n"), + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; - fragmentShader: [ +}; - "uniform samplerCube tCube;", - "uniform float tFlip;", +Object.assign( THREE.CubeTextureLoader.prototype, { - "varying vec3 vWorldPosition;", + load: function ( urls, onLoad, onProgress, onError ) { - "void main() {", + var texture = new THREE.CubeTexture(); - "gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );", + var loader = new THREE.ImageLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.setPath( this.path ); - "}" + var loaded = 0; - ].join("\n") + function loadTexture( i ) { - }, + loader.load( urls[ i ], function ( image ) { - // 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 here: - // http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ + texture.images[ i ] = image; - 'depthRGBA': { + loaded ++; - uniforms: {}, + if ( loaded === 6 ) { - vertexShader: [ + texture.needsUpdate = true; - THREE.ShaderChunk[ "morphtarget_pars_vertex" ], - THREE.ShaderChunk[ "skinning_pars_vertex" ], + if ( onLoad ) onLoad( texture ); - "void main() {", + } - THREE.ShaderChunk[ "skinbase_vertex" ], - THREE.ShaderChunk[ "morphtarget_vertex" ], - THREE.ShaderChunk[ "skinning_vertex" ], - THREE.ShaderChunk[ "default_vertex" ], + }, undefined, onError ); - "}" + } - ].join("\n"), + for ( var i = 0; i < urls.length; ++ i ) { - fragmentShader: [ + loadTexture( i ); - "vec4 pack_depth( const in float depth ) {", + } - "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 = fract( depth * bit_shift );", - "res -= res.xxyz * bit_mask;", - "return res;", + return texture; - "}", + }, - "void main() {", + setCrossOrigin: function ( value ) { - "gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );", + this.crossOrigin = value; + return this; - //"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 );", + }, - "}" + setPath: function ( value ) { - ].join("\n") + this.path = value; + return this; } -}; +} ); + +// File:src/loaders/BinaryTextureLoader.js /** - * @author supereggbert / http://www.paulbrunt.co.uk/ - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * @author szimek / https://github.com/szimek/ + * @author Nikos M. / https://github.com/foo123/ + * + * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) */ -THREE.WebGLRenderer = function ( parameters ) { +THREE.DataTextureLoader = THREE.BinaryTextureLoader = function ( manager ) { - console.log( 'THREE.WebGLRenderer', THREE.REVISION ); + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; - parameters = parameters || {}; + // override in sub classes + this._parser = null; - var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElement( 'canvas' ), +}; - _precision = parameters.precision !== undefined ? parameters.precision : 'highp', +Object.assign( THREE.BinaryTextureLoader.prototype, { - _alpha = parameters.alpha !== undefined ? parameters.alpha : true, - _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, - _antialias = parameters.antialias !== undefined ? parameters.antialias : false, - _stencil = parameters.stencil !== undefined ? parameters.stencil : true, - _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, + load: function ( url, onLoad, onProgress, onError ) { - _clearColor = new THREE.Color( 0x000000 ), - _clearAlpha = 0; + var scope = this; - if ( parameters.clearColor !== undefined ) { + var texture = new THREE.DataTexture(); - console.warn( 'DEPRECATED: clearColor in WebGLRenderer constructor parameters is being removed. Use .setClearColor() instead.' ); - _clearColor.setHex( parameters.clearColor ); + var loader = new THREE.XHRLoader( this.manager ); + loader.setResponseType( 'arraybuffer' ); - } + loader.load( url, function ( buffer ) { - if ( parameters.clearAlpha !== undefined ) { + var texData = scope._parser( buffer ); - console.warn( 'DEPRECATED: clearAlpha in WebGLRenderer constructor parameters is being removed. Use .setClearColor() instead.' ); - _clearAlpha = parameters.clearAlpha; + if ( ! texData ) return; - } + if ( undefined !== texData.image ) { - // public properties + texture.image = texData.image; - this.domElement = _canvas; - this.context = null; - this.devicePixelRatio = parameters.devicePixelRatio !== undefined - ? parameters.devicePixelRatio - : window.devicePixelRatio !== undefined - ? window.devicePixelRatio - : 1; + } else if ( undefined !== texData.data ) { - // clearing + texture.image.width = texData.width; + texture.image.height = texData.height; + texture.image.data = texData.data; - this.autoClear = true; - this.autoClearColor = true; - this.autoClearDepth = true; - this.autoClearStencil = true; + } - // scene graph + texture.wrapS = undefined !== texData.wrapS ? texData.wrapS : THREE.ClampToEdgeWrapping; + texture.wrapT = undefined !== texData.wrapT ? texData.wrapT : THREE.ClampToEdgeWrapping; - this.sortObjects = true; - this.autoUpdateObjects = true; + texture.magFilter = undefined !== texData.magFilter ? texData.magFilter : THREE.LinearFilter; + texture.minFilter = undefined !== texData.minFilter ? texData.minFilter : THREE.LinearMipMapLinearFilter; - // physically based shading + texture.anisotropy = undefined !== texData.anisotropy ? texData.anisotropy : 1; - this.gammaInput = false; - this.gammaOutput = false; - this.physicallyBasedShading = false; + if ( undefined !== texData.format ) { - // shadow map + texture.format = texData.format; - this.shadowMapEnabled = false; - this.shadowMapAutoUpdate = true; - this.shadowMapType = THREE.PCFShadowMap; - this.shadowMapCullFace = THREE.CullFaceFront; - this.shadowMapDebug = false; - this.shadowMapCascade = false; + } + if ( undefined !== texData.type ) { - // morphs + texture.type = texData.type; - this.maxMorphTargets = 8; - this.maxMorphNormals = 4; + } - // flags + if ( undefined !== texData.mipmaps ) { - this.autoScaleCubemaps = true; + texture.mipmaps = texData.mipmaps; - // custom render plugins + } - this.renderPluginsPre = []; - this.renderPluginsPost = []; + if ( 1 === texData.mipmapCount ) { - // info + texture.minFilter = THREE.LinearFilter; - this.info = { + } - memory: { + texture.needsUpdate = true; - programs: 0, - geometries: 0, - textures: 0 + if ( onLoad ) onLoad( texture, texData ); - }, + }, onProgress, onError ); - render: { - calls: 0, - vertices: 0, - faces: 0, - points: 0 + return texture; - } + } - }; +} ); - // internal properties +// File:src/loaders/CompressedTextureLoader.js - var _this = this, +/** + * @author mrdoob / http://mrdoob.com/ + * + * Abstract Base class to block based textures loader (dds, pvr, ...) + */ - _programs = [], - _programs_counter = 0, +THREE.CompressedTextureLoader = function ( manager ) { - // internal state cache + this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; - _currentProgram = null, - _currentFramebuffer = null, - _currentMaterialId = -1, - _currentGeometryGroupHash = null, - _currentCamera = null, - _geometryGroupCounter = 0, + // override in sub classes + this._parser = null; - _usedTextureUnits = 0, +}; - // GL state cache +Object.assign( THREE.CompressedTextureLoader.prototype, { - _oldDoubleSided = -1, - _oldFlipSided = -1, + load: function ( url, onLoad, onProgress, onError ) { - _oldBlending = -1, + var scope = this; - _oldBlendEquation = -1, - _oldBlendSrc = -1, - _oldBlendDst = -1, + var images = []; - _oldDepthTest = -1, - _oldDepthWrite = -1, + var texture = new THREE.CompressedTexture(); + texture.image = images; - _oldPolygonOffset = null, - _oldPolygonOffsetFactor = null, - _oldPolygonOffsetUnits = null, + var loader = new THREE.XHRLoader( this.manager ); + loader.setPath( this.path ); + loader.setResponseType( 'arraybuffer' ); - _oldLineWidth = null, + function loadTexture( i ) { - _viewportX = 0, - _viewportY = 0, - _viewportWidth = 0, - _viewportHeight = 0, - _currentWidth = 0, - _currentHeight = 0, + loader.load( url[ i ], function ( buffer ) { - _enabledAttributes = {}, + var texDatas = scope._parser( buffer, true ); - // frustum + images[ i ] = { + width: texDatas.width, + height: texDatas.height, + format: texDatas.format, + mipmaps: texDatas.mipmaps + }; - _frustum = new THREE.Frustum(), + loaded += 1; - // camera matrices cache + if ( loaded === 6 ) { - _projScreenMatrix = new THREE.Matrix4(), - _projScreenMatrixPS = new THREE.Matrix4(), + if ( texDatas.mipmapCount === 1 ) + texture.minFilter = THREE.LinearFilter; - _vector3 = new THREE.Vector3(), - - // light arrays cache - - _direction = new THREE.Vector3(), - - _lightsNeedUpdate = true, - - _lights = { - - ambient: [ 0, 0, 0 ], - directional: { length: 0, colors: new Array(), positions: new Array() }, - point: { length: 0, colors: new Array(), positions: new Array(), distances: new Array() }, - spot: { length: 0, colors: new Array(), positions: new Array(), distances: new Array(), directions: new Array(), anglesCos: new Array(), exponents: new Array() }, - hemi: { length: 0, skyColors: new Array(), groundColors: new Array(), positions: new Array() } - - }; - - // initialize - - var _gl; - - var _glExtensionTextureFloat; - var _glExtensionTextureFloatLinear; - var _glExtensionStandardDerivatives; - var _glExtensionTextureFilterAnisotropic; - var _glExtensionCompressedTextureS3TC; - - initGL(); - - setDefaultGLState(); - - this.context = _gl; - - // GPU capabilities - - var _maxTextures = _gl.getParameter( _gl.MAX_TEXTURE_IMAGE_UNITS ); - var _maxVertexTextures = _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ); - var _maxTextureSize = _gl.getParameter( _gl.MAX_TEXTURE_SIZE ); - var _maxCubemapSize = _gl.getParameter( _gl.MAX_CUBE_MAP_TEXTURE_SIZE ); - - var _maxAnisotropy = _glExtensionTextureFilterAnisotropic ? _gl.getParameter( _glExtensionTextureFilterAnisotropic.MAX_TEXTURE_MAX_ANISOTROPY_EXT ) : 0; - - var _supportsVertexTextures = ( _maxVertexTextures > 0 ); - var _supportsBoneTextures = _supportsVertexTextures && _glExtensionTextureFloat; - - var _compressedTextureFormats = _glExtensionCompressedTextureS3TC ? _gl.getParameter( _gl.COMPRESSED_TEXTURE_FORMATS ) : []; - - // + texture.format = texDatas.format; + texture.needsUpdate = true; - var _vertexShaderPrecisionHighpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.HIGH_FLOAT ); - var _vertexShaderPrecisionMediumpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.MEDIUM_FLOAT ); - var _vertexShaderPrecisionLowpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.LOW_FLOAT ); + if ( onLoad ) onLoad( texture ); - var _fragmentShaderPrecisionHighpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.HIGH_FLOAT ); - var _fragmentShaderPrecisionMediumpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.MEDIUM_FLOAT ); - var _fragmentShaderPrecisionLowpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.LOW_FLOAT ); + } - var _vertexShaderPrecisionHighpInt = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.HIGH_INT ); - var _vertexShaderPrecisionMediumpInt = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.MEDIUM_INT ); - var _vertexShaderPrecisionLowpInt = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.LOW_INT ); + }, onProgress, onError ); - var _fragmentShaderPrecisionHighpInt = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.HIGH_INT ); - var _fragmentShaderPrecisionMediumpInt = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.MEDIUM_INT ); - var _fragmentShaderPrecisionLowpInt = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.LOW_INT ); + } - // clamp precision to maximum available + if ( Array.isArray( url ) ) { - var highpAvailable = _vertexShaderPrecisionHighpFloat.precision > 0 && _fragmentShaderPrecisionHighpFloat.precision > 0; - var mediumpAvailable = _vertexShaderPrecisionMediumpFloat.precision > 0 && _fragmentShaderPrecisionMediumpFloat.precision > 0; + var loaded = 0; - if ( _precision === "highp" && ! highpAvailable ) { + for ( var i = 0, il = url.length; i < il; ++ i ) { - if ( mediumpAvailable ) { + loadTexture( i ); - _precision = "mediump"; - console.warn( "WebGLRenderer: highp not supported, using mediump" ); + } } else { - _precision = "lowp"; - console.warn( "WebGLRenderer: highp and mediump not supported, using lowp" ); - - } - - } + // compressed cubemap texture stored in a single DDS file - if ( _precision === "mediump" && ! mediumpAvailable ) { + loader.load( url, function ( buffer ) { - _precision = "lowp"; - console.warn( "WebGLRenderer: mediump not supported, using lowp" ); + var texDatas = scope._parser( buffer, true ); - } + if ( texDatas.isCubemap ) { - // API + var faces = texDatas.mipmaps.length / texDatas.mipmapCount; - this.getContext = function () { + for ( var f = 0; f < faces; f ++ ) { - return _gl; + images[ f ] = { mipmaps : [] }; - }; + for ( var i = 0; i < texDatas.mipmapCount; i ++ ) { - this.supportsVertexTextures = function () { + 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; - return _supportsVertexTextures; + } - }; + } - this.supportsFloatTextures = function () { + } else { - return _glExtensionTextureFloat; + texture.image.width = texDatas.width; + texture.image.height = texDatas.height; + texture.mipmaps = texDatas.mipmaps; - }; + } - this.supportsStandardDerivatives = function () { + if ( texDatas.mipmapCount === 1 ) { - return _glExtensionStandardDerivatives; + texture.minFilter = THREE.LinearFilter; - }; + } - this.supportsCompressedTextureS3TC = function () { + texture.format = texDatas.format; + texture.needsUpdate = true; - return _glExtensionCompressedTextureS3TC; + if ( onLoad ) onLoad( texture ); - }; + }, onProgress, onError ); - this.getMaxAnisotropy = function () { + } - return _maxAnisotropy; + return texture; - }; + }, - this.getPrecision = function () { + setPath: function ( value ) { - return _precision; + this.path = value; + return this; - }; + } - this.setSize = function ( width, height, updateStyle ) { +} ); - _canvas.width = width * this.devicePixelRatio; - _canvas.height = height * this.devicePixelRatio; +// File:src/materials/Material.js - if ( this.devicePixelRatio !== 1 && updateStyle !== false ) { +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ - _canvas.style.width = width + 'px'; - _canvas.style.height = height + 'px'; +THREE.Material = function () { - } + Object.defineProperty( this, 'id', { value: THREE.MaterialIdCount ++ } ); - this.setViewport( 0, 0, _canvas.width, _canvas.height ); + this.uuid = THREE.Math.generateUUID(); - }; + this.name = ''; + this.type = 'Material'; - this.setViewport = function ( x, y, width, height ) { + this.fog = true; + this.lights = true; - _viewportX = x !== undefined ? x : 0; - _viewportY = y !== undefined ? y : 0; + this.blending = THREE.NormalBlending; + this.side = THREE.FrontSide; + this.shading = THREE.SmoothShading; // THREE.FlatShading, THREE.SmoothShading + this.vertexColors = THREE.NoColors; // THREE.NoColors, THREE.VertexColors, THREE.FaceColors - _viewportWidth = width !== undefined ? width : _canvas.width; - _viewportHeight = height !== undefined ? height : _canvas.height; + this.opacity = 1; + this.transparent = false; - _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight ); + this.blendSrc = THREE.SrcAlphaFactor; + this.blendDst = THREE.OneMinusSrcAlphaFactor; + this.blendEquation = THREE.AddEquation; + this.blendSrcAlpha = null; + this.blendDstAlpha = null; + this.blendEquationAlpha = null; - }; + this.depthFunc = THREE.LessEqualDepth; + this.depthTest = true; + this.depthWrite = true; - this.setScissor = function ( x, y, width, height ) { + this.clippingPlanes = null; + this.clipShadows = false; - _gl.scissor( x, y, width, height ); + this.colorWrite = true; - }; + this.precision = null; // override the renderer's default precision for this material - this.enableScissorTest = function ( enable ) { + this.polygonOffset = false; + this.polygonOffsetFactor = 0; + this.polygonOffsetUnits = 0; - enable ? _gl.enable( _gl.SCISSOR_TEST ) : _gl.disable( _gl.SCISSOR_TEST ); + this.alphaTest = 0; + this.premultipliedAlpha = false; - }; + this.overdraw = 0; // Overdrawn pixels (typically between 0 and 1) for fixing antialiasing gaps in CanvasRenderer - // Clearing + this.visible = true; - this.setClearColor = function ( color, alpha ) { + this._needsUpdate = true; - _clearColor.set( color ); - _clearAlpha = alpha !== undefined ? alpha : 1; +}; - _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); +THREE.Material.prototype = { - }; + constructor: THREE.Material, - this.setClearColorHex = function ( hex, alpha ) { + get needsUpdate() { - console.warn( 'DEPRECATED: .setClearColorHex() is being removed. Use .setClearColor() instead.' ); - this.setClearColor( hex, alpha ); + return this._needsUpdate; - }; + }, - this.getClearColor = function () { + set needsUpdate( value ) { - return _clearColor; + if ( value === true ) this.update(); + this._needsUpdate = value; - }; + }, - this.getClearAlpha = function () { + setValues: function ( values ) { - return _clearAlpha; + if ( values === undefined ) return; - }; + for ( var key in values ) { - this.clear = function ( color, depth, stencil ) { + var newValue = values[ key ]; - var bits = 0; + if ( newValue === undefined ) { - 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; + console.warn( "THREE.Material: '" + key + "' parameter is undefined." ); + continue; - _gl.clear( bits ); + } - }; + var currentValue = this[ key ]; - this.clearTarget = function ( renderTarget, color, depth, stencil ) { + if ( currentValue === undefined ) { - this.setRenderTarget( renderTarget ); - this.clear( color, depth, stencil ); + console.warn( "THREE." + this.type + ": '" + key + "' is not a property of this material." ); + continue; - }; + } - // Plugins + if ( currentValue instanceof THREE.Color ) { - this.addPostPlugin = function ( plugin ) { + currentValue.set( newValue ); - plugin.init( this ); - this.renderPluginsPost.push( plugin ); + } else if ( currentValue instanceof THREE.Vector3 && newValue instanceof THREE.Vector3 ) { - }; + currentValue.copy( newValue ); - this.addPrePlugin = function ( plugin ) { + } else if ( key === 'overdraw' ) { - plugin.init( this ); - this.renderPluginsPre.push( plugin ); + // ensure overdraw is backwards-compatible with legacy boolean type + this[ key ] = Number( newValue ); - }; + } else { - // Rendering + this[ key ] = newValue; - this.updateShadowMap = function ( scene, camera ) { + } - _currentProgram = null; - _oldBlending = -1; - _oldDepthTest = -1; - _oldDepthWrite = -1; - _currentGeometryGroupHash = -1; - _currentMaterialId = -1; - _lightsNeedUpdate = true; - _oldDoubleSided = -1; - _oldFlipSided = -1; + } - this.shadowMapPlugin.update( scene, camera ); + }, - }; + toJSON: function ( meta ) { - // Internal functions + var isRoot = meta === undefined; - // Buffer allocation + if ( isRoot ) { - function createParticleBuffers ( geometry ) { + meta = { + textures: {}, + images: {} + }; - geometry.__webglVertexBuffer = _gl.createBuffer(); - geometry.__webglColorBuffer = _gl.createBuffer(); + } - _this.info.memory.geometries ++; + var data = { + metadata: { + version: 4.4, + type: 'Material', + generator: 'Material.toJSON' + } + }; - }; + // standard Material serialization + data.uuid = this.uuid; + data.type = this.type; - function createLineBuffers ( geometry ) { + if ( this.name !== '' ) data.name = this.name; - geometry.__webglVertexBuffer = _gl.createBuffer(); - geometry.__webglColorBuffer = _gl.createBuffer(); - geometry.__webglLineDistanceBuffer = _gl.createBuffer(); + if ( this.color instanceof THREE.Color ) data.color = this.color.getHex(); - _this.info.memory.geometries ++; + if ( this.roughness !== undefined ) data.roughness = this.roughness; + if ( this.metalness !== undefined ) data.metalness = this.metalness; - }; + 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; - function createRibbonBuffers ( geometry ) { + 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 ) { - geometry.__webglVertexBuffer = _gl.createBuffer(); - geometry.__webglColorBuffer = _gl.createBuffer(); - geometry.__webglNormalBuffer = _gl.createBuffer(); + data.bumpMap = this.bumpMap.toJSON( meta ).uuid; + data.bumpScale = this.bumpScale; - _this.info.memory.geometries ++; + } + if ( this.normalMap instanceof THREE.Texture ) { - }; + data.normalMap = this.normalMap.toJSON( meta ).uuid; + data.normalScale = this.normalScale.toArray(); - function createMeshBuffers ( geometryGroup ) { + } + if ( this.displacementMap instanceof THREE.Texture ) { - geometryGroup.__webglVertexBuffer = _gl.createBuffer(); - geometryGroup.__webglNormalBuffer = _gl.createBuffer(); - geometryGroup.__webglTangentBuffer = _gl.createBuffer(); - geometryGroup.__webglColorBuffer = _gl.createBuffer(); - geometryGroup.__webglUVBuffer = _gl.createBuffer(); - geometryGroup.__webglUV2Buffer = _gl.createBuffer(); + data.displacementMap = this.displacementMap.toJSON( meta ).uuid; + data.displacementScale = this.displacementScale; + data.displacementBias = this.displacementBias; - geometryGroup.__webglSkinIndicesBuffer = _gl.createBuffer(); - geometryGroup.__webglSkinWeightsBuffer = _gl.createBuffer(); + } + if ( this.roughnessMap instanceof THREE.Texture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid; + if ( this.metalnessMap instanceof THREE.Texture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid; - geometryGroup.__webglFaceBuffer = _gl.createBuffer(); - geometryGroup.__webglLineBuffer = _gl.createBuffer(); + if ( this.emissiveMap instanceof THREE.Texture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid; + if ( this.specularMap instanceof THREE.Texture ) data.specularMap = this.specularMap.toJSON( meta ).uuid; - var m, ml; + if ( this.envMap instanceof THREE.Texture ) { - if ( geometryGroup.numMorphTargets ) { + data.envMap = this.envMap.toJSON( meta ).uuid; + data.reflectivity = this.reflectivity; // Scale behind envMap - geometryGroup.__webglMorphTargetsBuffers = []; + } - for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) { + if ( this.size !== undefined ) data.size = this.size; + if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation; - geometryGroup.__webglMorphTargetsBuffers.push( _gl.createBuffer() ); + if ( this.blending !== THREE.NormalBlending ) data.blending = this.blending; + if ( this.shading !== THREE.SmoothShading ) data.shading = this.shading; + if ( this.side !== THREE.FrontSide ) data.side = this.side; + if ( this.vertexColors !== THREE.NoColors ) data.vertexColors = this.vertexColors; - } + 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.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha; + if ( this.wireframe === true ) data.wireframe = this.wireframe; + if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth; - } + // TODO: Copied from Object3D.toJSON - if ( geometryGroup.numMorphNormals ) { + function extractFromCache ( cache ) { - geometryGroup.__webglMorphNormalsBuffers = []; + var values = []; - for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) { + for ( var key in cache ) { - geometryGroup.__webglMorphNormalsBuffers.push( _gl.createBuffer() ); + var data = cache[ key ]; + delete data.metadata; + values.push( data ); } + return values; + } - _this.info.memory.geometries ++; + if ( isRoot ) { - }; + var textures = extractFromCache( meta.textures ); + var images = extractFromCache( meta.images ); - // Events + if ( textures.length > 0 ) data.textures = textures; + if ( images.length > 0 ) data.images = images; - var onGeometryDispose = function ( event ) { + } - var geometry = event.target; + return data; - geometry.removeEventListener( 'dispose', onGeometryDispose ); + }, - deallocateGeometry( geometry ); + clone: function () { - }; + return new this.constructor().copy( this ); - var onTextureDispose = function ( event ) { + }, - var texture = event.target; + copy: function ( source ) { - texture.removeEventListener( 'dispose', onTextureDispose ); + this.name = source.name; - deallocateTexture( texture ); + this.fog = source.fog; + this.lights = source.lights; - _this.info.memory.textures --; + this.blending = source.blending; + this.side = source.side; + this.shading = source.shading; + this.vertexColors = source.vertexColors; + this.opacity = source.opacity; + this.transparent = source.transparent; - }; + this.blendSrc = source.blendSrc; + this.blendDst = source.blendDst; + this.blendEquation = source.blendEquation; + this.blendSrcAlpha = source.blendSrcAlpha; + this.blendDstAlpha = source.blendDstAlpha; + this.blendEquationAlpha = source.blendEquationAlpha; - var onRenderTargetDispose = function ( event ) { + this.depthFunc = source.depthFunc; + this.depthTest = source.depthTest; + this.depthWrite = source.depthWrite; - var renderTarget = event.target; + this.colorWrite = source.colorWrite; - renderTarget.removeEventListener( 'dispose', onRenderTargetDispose ); + this.precision = source.precision; - deallocateRenderTarget( renderTarget ); + this.polygonOffset = source.polygonOffset; + this.polygonOffsetFactor = source.polygonOffsetFactor; + this.polygonOffsetUnits = source.polygonOffsetUnits; - _this.info.memory.textures --; + this.alphaTest = source.alphaTest; - }; + this.premultipliedAlpha = source.premultipliedAlpha; - var onMaterialDispose = function ( event ) { + this.overdraw = source.overdraw; - var material = event.target; + this.visible = source.visible; + this.clipShadows = source.clipShadows; - material.removeEventListener( 'dispose', onMaterialDispose ); + var srcPlanes = source.clippingPlanes, + dstPlanes = null; - deallocateMaterial( material ); + if ( srcPlanes !== null ) { - }; + var n = srcPlanes.length; + dstPlanes = new Array( n ); - // Buffer deallocation + for ( var i = 0; i !== n; ++ i ) + dstPlanes[ i ] = srcPlanes[ i ].clone(); - var deleteBuffers = function ( geometry ) { + } - if ( geometry.__webglVertexBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglVertexBuffer ); - if ( geometry.__webglNormalBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglNormalBuffer ); - if ( geometry.__webglTangentBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglTangentBuffer ); - if ( geometry.__webglColorBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglColorBuffer ); - if ( geometry.__webglUVBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglUVBuffer ); - if ( geometry.__webglUV2Buffer !== undefined ) _gl.deleteBuffer( geometry.__webglUV2Buffer ); + this.clippingPlanes = dstPlanes; - if ( geometry.__webglSkinIndicesBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglSkinIndicesBuffer ); - if ( geometry.__webglSkinWeightsBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglSkinWeightsBuffer ); + return this; - if ( geometry.__webglFaceBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglFaceBuffer ); - if ( geometry.__webglLineBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglLineBuffer ); + }, - if ( geometry.__webglLineDistanceBuffer !== undefined ) _gl.deleteBuffer( geometry.__webglLineDistanceBuffer ); - // custom attributes + update: function () { - if ( geometry.__webglCustomAttributesList !== undefined ) { + this.dispatchEvent( { type: 'update' } ); - for ( var id in geometry.__webglCustomAttributesList ) { + }, - _gl.deleteBuffer( geometry.__webglCustomAttributesList[ id ].buffer ); + dispose: function () { - } + this.dispatchEvent( { type: 'dispose' } ); - } + } - _this.info.memory.geometries --; +}; - }; +Object.assign( THREE.Material.prototype, THREE.EventDispatcher.prototype ); - var deallocateGeometry = function ( geometry ) { +THREE.MaterialIdCount = 0; - geometry.__webglInit = undefined; +// File:src/materials/LineBasicMaterial.js - if ( geometry instanceof THREE.BufferGeometry ) { +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * + * linewidth: , + * linecap: "round", + * linejoin: "round" + * } + */ - var attributes = geometry.attributes; +THREE.LineBasicMaterial = function ( parameters ) { - for ( var key in attributes ) { + THREE.Material.call( this ); - if ( attributes[ key ].buffer !== undefined ) { + this.type = 'LineBasicMaterial'; - _gl.deleteBuffer( attributes[ key ].buffer ); - - } + this.color = new THREE.Color( 0xffffff ); - } + this.linewidth = 1; + this.linecap = 'round'; + this.linejoin = 'round'; - _this.info.memory.geometries --; + this.lights = false; - } else { + this.setValues( parameters ); - if ( geometry.geometryGroups !== undefined ) { +}; - for ( var g in geometry.geometryGroups ) { +THREE.LineBasicMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.LineBasicMaterial.prototype.constructor = THREE.LineBasicMaterial; - var geometryGroup = geometry.geometryGroups[ g ]; +THREE.LineBasicMaterial.prototype.copy = function ( source ) { - if ( geometryGroup.numMorphTargets !== undefined ) { + THREE.Material.prototype.copy.call( this, source ); - for ( var m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) { + this.color.copy( source.color ); - _gl.deleteBuffer( geometryGroup.__webglMorphTargetsBuffers[ m ] ); + this.linewidth = source.linewidth; + this.linecap = source.linecap; + this.linejoin = source.linejoin; - } + return this; - } +}; - if ( geometryGroup.numMorphNormals !== undefined ) { +// File:src/materials/LineDashedMaterial.js - for ( var m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) { +/** + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * + * linewidth: , + * + * scale: , + * dashSize: , + * gapSize: + * } + */ - _gl.deleteBuffer( geometryGroup.__webglMorphNormalsBuffers[ m ] ); +THREE.LineDashedMaterial = function ( parameters ) { - } + THREE.Material.call( this ); - } + this.type = 'LineDashedMaterial'; - deleteBuffers( geometryGroup ); + this.color = new THREE.Color( 0xffffff ); - } + this.linewidth = 1; - } else { + this.scale = 1; + this.dashSize = 3; + this.gapSize = 1; - deleteBuffers( geometry ); + this.lights = false; - } + this.setValues( parameters ); - } +}; - }; +THREE.LineDashedMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.LineDashedMaterial.prototype.constructor = THREE.LineDashedMaterial; - var deallocateTexture = function ( texture ) { +THREE.LineDashedMaterial.prototype.copy = function ( source ) { - if ( texture.image && texture.image.__webglTextureCube ) { + THREE.Material.prototype.copy.call( this, source ); - // cube texture + this.color.copy( source.color ); - _gl.deleteTexture( texture.image.__webglTextureCube ); + this.linewidth = source.linewidth; - } else { + this.scale = source.scale; + this.dashSize = source.dashSize; + this.gapSize = source.gapSize; - // 2D texture + return this; - if ( ! texture.__webglInit ) return; +}; - texture.__webglInit = false; - _gl.deleteTexture( texture.__webglTexture ); +// File:src/materials/MeshBasicMaterial.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, + * depthTest: , + * depthWrite: , + * + * wireframe: , + * wireframeLinewidth: , + * + * skinning: , + * morphTargets: + * } + */ - }; +THREE.MeshBasicMaterial = function ( parameters ) { - var deallocateRenderTarget = function ( renderTarget ) { + THREE.Material.call( this ); - if ( !renderTarget || ! renderTarget.__webglTexture ) return; + this.type = 'MeshBasicMaterial'; - _gl.deleteTexture( renderTarget.__webglTexture ); + this.color = new THREE.Color( 0xffffff ); // emissive - if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) { + this.map = null; - for ( var i = 0; i < 6; i ++ ) { + this.aoMap = null; + this.aoMapIntensity = 1.0; - _gl.deleteFramebuffer( renderTarget.__webglFramebuffer[ i ] ); - _gl.deleteRenderbuffer( renderTarget.__webglRenderbuffer[ i ] ); + this.specularMap = null; - } + this.alphaMap = null; - } else { + this.envMap = null; + this.combine = THREE.MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; - _gl.deleteFramebuffer( renderTarget.__webglFramebuffer ); - _gl.deleteRenderbuffer( renderTarget.__webglRenderbuffer ); + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; - } + this.skinning = false; + this.morphTargets = false; - }; + this.lights = false; - var deallocateMaterial = function ( material ) { + this.setValues( parameters ); - var program = material.program; +}; - if ( program === undefined ) return; +THREE.MeshBasicMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.MeshBasicMaterial.prototype.constructor = THREE.MeshBasicMaterial; - material.program = undefined; +THREE.MeshBasicMaterial.prototype.copy = function ( source ) { - // only deallocate GL program if this was the last use of shared program - // assumed there is only single copy of any program in the _programs list - // (that's how it's constructed) + THREE.Material.prototype.copy.call( this, source ); - var i, il, programInfo; - var deleteProgram = false; + this.color.copy( source.color ); - for ( i = 0, il = _programs.length; i < il; i ++ ) { + this.map = source.map; - programInfo = _programs[ i ]; + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; - if ( programInfo.program === program ) { + this.specularMap = source.specularMap; - programInfo.usedTimes --; + this.alphaMap = source.alphaMap; - if ( programInfo.usedTimes === 0 ) { + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; - deleteProgram = true; + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; - } + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; - break; + return this; - } +}; - } +// File:src/materials/MeshDepthMaterial.js - if ( deleteProgram === true ) { +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author bhouston / https://clara.io + * @author WestLangley / http://github.com/WestLangley + * + * parameters = { + * + * opacity: , + * + * map: new THREE.Texture( ), + * + * alphaMap: new THREE.Texture( ), + * + * displacementMap: new THREE.Texture( ), + * displacementScale: , + * displacementBias: , + * + * wireframe: , + * wireframeLinewidth: + * } + */ - // avoid using array.splice, this is costlier than creating new array from scratch +THREE.MeshDepthMaterial = function ( parameters ) { - var newPrograms = []; + THREE.Material.call( this ); - for ( i = 0, il = _programs.length; i < il; i ++ ) { + this.type = 'MeshDepthMaterial'; - programInfo = _programs[ i ]; + this.depthPacking = THREE.BasicDepthPacking; - if ( programInfo.program !== program ) { + this.skinning = false; + this.morphTargets = false; - newPrograms.push( programInfo ); + this.map = null; - } + this.alphaMap = null; - } + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - _programs = newPrograms; + this.wireframe = false; + this.wireframeLinewidth = 1; - _gl.deleteProgram( program ); + this.fog = false; + this.lights = false; - _this.info.memory.programs --; + this.setValues( parameters ); - } +}; - }; +THREE.MeshDepthMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.MeshDepthMaterial.prototype.constructor = THREE.MeshDepthMaterial; - // Buffer initialization +THREE.MeshDepthMaterial.prototype.copy = function ( source ) { - function initCustomAttributes ( geometry, object ) { + THREE.Material.prototype.copy.call( this, source ); - var nvertices = geometry.vertices.length; + this.depthPacking = source.depthPacking; - var material = object.material; + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; - if ( material.attributes ) { + this.map = source.map; - if ( geometry.__webglCustomAttributesList === undefined ) { + this.alphaMap = source.alphaMap; - geometry.__webglCustomAttributesList = []; + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; - } + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; - for ( var a in material.attributes ) { + return this; - var attribute = material.attributes[ a ]; +}; - if ( !attribute.__webglInitialized || attribute.createUniqueBuffers ) { +// File:src/materials/MeshLambertMaterial.js - attribute.__webglInitialized = true; +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * + * map: new THREE.Texture( ), + * + * lightMap: new THREE.Texture( ), + * lightMapIntensity: + * + * aoMap: new THREE.Texture( ), + * aoMapIntensity: + * + * emissive: , + * emissiveIntensity: + * emissiveMap: 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: , + * + * wireframe: , + * wireframeLinewidth: , + * + * skinning: , + * morphTargets: , + * morphNormals: + * } + */ - var size = 1; // "f" and "i" +THREE.MeshLambertMaterial = function ( parameters ) { - if ( attribute.type === "v2" ) size = 2; - else if ( attribute.type === "v3" ) size = 3; - else if ( attribute.type === "v4" ) size = 4; - else if ( attribute.type === "c" ) size = 3; + THREE.Material.call( this ); - attribute.size = size; + this.type = 'MeshLambertMaterial'; - attribute.array = new Float32Array( nvertices * size ); + this.color = new THREE.Color( 0xffffff ); // diffuse - attribute.buffer = _gl.createBuffer(); - attribute.buffer.belongsToAttribute = a; + this.map = null; - attribute.needsUpdate = true; + this.lightMap = null; + this.lightMapIntensity = 1.0; - } + this.aoMap = null; + this.aoMapIntensity = 1.0; - geometry.__webglCustomAttributesList.push( attribute ); + this.emissive = new THREE.Color( 0x000000 ); + this.emissiveIntensity = 1.0; + this.emissiveMap = null; - } + this.specularMap = null; - } + this.alphaMap = null; - }; + this.envMap = null; + this.combine = THREE.MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; - function initParticleBuffers ( geometry, object ) { + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; - var nvertices = geometry.vertices.length; + this.skinning = false; + this.morphTargets = false; + this.morphNormals = false; - geometry.__vertexArray = new Float32Array( nvertices * 3 ); - geometry.__colorArray = new Float32Array( nvertices * 3 ); + this.setValues( parameters ); - geometry.__sortArray = []; +}; - geometry.__webglParticleCount = nvertices; +THREE.MeshLambertMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.MeshLambertMaterial.prototype.constructor = THREE.MeshLambertMaterial; - initCustomAttributes ( geometry, object ); +THREE.MeshLambertMaterial.prototype.copy = function ( source ) { - }; + THREE.Material.prototype.copy.call( this, source ); - function initLineBuffers ( geometry, object ) { + this.color.copy( source.color ); - var nvertices = geometry.vertices.length; + this.map = source.map; - geometry.__vertexArray = new Float32Array( nvertices * 3 ); - geometry.__colorArray = new Float32Array( nvertices * 3 ); - geometry.__lineDistanceArray = new Float32Array( nvertices * 1 ); + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; - geometry.__webglLineCount = nvertices; + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; - initCustomAttributes ( geometry, object ); + this.emissive.copy( source.emissive ); + this.emissiveMap = source.emissiveMap; + this.emissiveIntensity = source.emissiveIntensity; - }; + this.specularMap = source.specularMap; - function initRibbonBuffers ( geometry, object ) { + this.alphaMap = source.alphaMap; - var nvertices = geometry.vertices.length; + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; - geometry.__vertexArray = new Float32Array( nvertices * 3 ); - geometry.__colorArray = new Float32Array( nvertices * 3 ); - geometry.__normalArray = new Float32Array( nvertices * 3 ); + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; - geometry.__webglVertexCount = nvertices; + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; + this.morphNormals = source.morphNormals; - initCustomAttributes ( geometry, object ); + return this; - }; +}; - function initMeshBuffers ( geometryGroup, object ) { +// File:src/materials/MeshNormalMaterial.js - var geometry = object.geometry, - faces3 = geometryGroup.faces3, +/** + * @author mrdoob / http://mrdoob.com/ + * + * parameters = { + * opacity: , + * + * wireframe: , + * wireframeLinewidth: + * } + */ - nvertices = faces3.length * 3, - ntris = faces3.length * 1, - nlines = faces3.length * 3, +THREE.MeshNormalMaterial = function ( parameters ) { - material = getBufferMaterial( object, geometryGroup ), + THREE.Material.call( this, parameters ); - uvType = bufferGuessUVType( material ), - normalType = bufferGuessNormalType( material ), - vertexColorType = bufferGuessVertexColorType( material ); + this.type = 'MeshNormalMaterial'; - // console.log( "uvType", uvType, "normalType", normalType, "vertexColorType", vertexColorType, object, geometryGroup, material ); + this.wireframe = false; + this.wireframeLinewidth = 1; - geometryGroup.__vertexArray = new Float32Array( nvertices * 3 ); + this.fog = false; + this.lights = false; + this.morphTargets = false; - if ( normalType ) { + this.setValues( parameters ); - geometryGroup.__normalArray = new Float32Array( nvertices * 3 ); +}; - } +THREE.MeshNormalMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.MeshNormalMaterial.prototype.constructor = THREE.MeshNormalMaterial; - if ( geometry.hasTangents ) { +THREE.MeshNormalMaterial.prototype.copy = function ( source ) { - geometryGroup.__tangentArray = new Float32Array( nvertices * 4 ); + THREE.Material.prototype.copy.call( this, source ); - } + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; - if ( vertexColorType ) { + return this; - geometryGroup.__colorArray = new Float32Array( nvertices * 3 ); +}; - } +// File:src/materials/MeshPhongMaterial.js - if ( uvType ) { +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * specular: , + * shininess: , + * opacity: , + * + * map: new THREE.Texture( ), + * + * lightMap: new THREE.Texture( ), + * lightMapIntensity: + * + * aoMap: new THREE.Texture( ), + * aoMapIntensity: + * + * emissive: , + * emissiveIntensity: + * 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: , + * + * wireframe: , + * wireframeLinewidth: , + * + * skinning: , + * morphTargets: , + * morphNormals: + * } + */ - if ( geometry.faceVertexUvs.length > 0 ) { +THREE.MeshPhongMaterial = function ( parameters ) { - geometryGroup.__uvArray = new Float32Array( nvertices * 2 ); + THREE.Material.call( this ); - } + this.type = 'MeshPhongMaterial'; - if ( geometry.faceVertexUvs.length > 1 ) { + this.color = new THREE.Color( 0xffffff ); // diffuse + this.specular = new THREE.Color( 0x111111 ); + this.shininess = 30; - geometryGroup.__uv2Array = new Float32Array( nvertices * 2 ); + this.map = null; - } + this.lightMap = null; + this.lightMapIntensity = 1.0; - } + this.aoMap = null; + this.aoMapIntensity = 1.0; - if ( object.geometry.skinWeights.length && object.geometry.skinIndices.length ) { + this.emissive = new THREE.Color( 0x000000 ); + this.emissiveIntensity = 1.0; + this.emissiveMap = null; - geometryGroup.__skinIndexArray = new Float32Array( nvertices * 4 ); - geometryGroup.__skinWeightArray = new Float32Array( nvertices * 4 ); + this.bumpMap = null; + this.bumpScale = 1; - } + this.normalMap = null; + this.normalScale = new THREE.Vector2( 1, 1 ); - geometryGroup.__faceArray = new Uint16Array( ntris * 3 ); - geometryGroup.__lineArray = new Uint16Array( nlines * 2 ); + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - var m, ml; + this.specularMap = null; - if ( geometryGroup.numMorphTargets ) { + this.alphaMap = null; - geometryGroup.__morphTargetsArrays = []; + this.envMap = null; + this.combine = THREE.MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; - for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) { + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; - geometryGroup.__morphTargetsArrays.push( new Float32Array( nvertices * 3 ) ); + this.skinning = false; + this.morphTargets = false; + this.morphNormals = false; - } + this.setValues( parameters ); - } +}; - if ( geometryGroup.numMorphNormals ) { +THREE.MeshPhongMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.MeshPhongMaterial.prototype.constructor = THREE.MeshPhongMaterial; - geometryGroup.__morphNormalsArrays = []; +THREE.MeshPhongMaterial.prototype.copy = function ( source ) { - for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) { + THREE.Material.prototype.copy.call( this, source ); - geometryGroup.__morphNormalsArrays.push( new Float32Array( nvertices * 3 ) ); + this.color.copy( source.color ); + this.specular.copy( source.specular ); + this.shininess = source.shininess; - } + this.map = source.map; - } + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; - geometryGroup.__webglFaceCount = ntris * 3; - geometryGroup.__webglLineCount = nlines * 2; + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; + this.emissive.copy( source.emissive ); + this.emissiveMap = source.emissiveMap; + this.emissiveIntensity = source.emissiveIntensity; - // custom attributes + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; - if ( material.attributes ) { + this.normalMap = source.normalMap; + this.normalScale.copy( source.normalScale ); - if ( geometryGroup.__webglCustomAttributesList === undefined ) { + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; - geometryGroup.__webglCustomAttributesList = []; + this.specularMap = source.specularMap; - } + this.alphaMap = source.alphaMap; - for ( var a in material.attributes ) { + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; - // Do a shallow copy of the attribute object so different geometryGroup chunks use different - // attribute buffers which are correctly indexed in the setMeshBuffers function + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; - var originalAttribute = material.attributes[ a ]; + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; + this.morphNormals = source.morphNormals; - var attribute = {}; + return this; - for ( var property in originalAttribute ) { - - attribute[ property ] = originalAttribute[ property ]; +}; - } +// File:src/materials/MeshStandardMaterial.js - if ( !attribute.__webglInitialized || attribute.createUniqueBuffers ) { +/** + * @author WestLangley / http://github.com/WestLangley + * + * parameters = { + * color: , + * roughness: , + * metalness: , + * opacity: , + * + * map: new THREE.Texture( ), + * + * lightMap: new THREE.Texture( ), + * lightMapIntensity: + * + * aoMap: new THREE.Texture( ), + * aoMapIntensity: + * + * emissive: , + * emissiveIntensity: + * emissiveMap: new THREE.Texture( ), + * + * bumpMap: new THREE.Texture( ), + * bumpScale: , + * + * normalMap: new THREE.Texture( ), + * normalScale: , + * + * displacementMap: new THREE.Texture( ), + * displacementScale: , + * displacementBias: , + * + * roughnessMap: new THREE.Texture( ), + * + * metalnessMap: new THREE.Texture( ), + * + * alphaMap: new THREE.Texture( ), + * + * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), + * envMapIntensity: + * + * refractionRatio: , + * + * wireframe: , + * wireframeLinewidth: , + * + * skinning: , + * morphTargets: , + * morphNormals: + * } + */ - attribute.__webglInitialized = true; +THREE.MeshStandardMaterial = function ( parameters ) { - var size = 1; // "f" and "i" + THREE.Material.call( this ); - if( attribute.type === "v2" ) size = 2; - else if( attribute.type === "v3" ) size = 3; - else if( attribute.type === "v4" ) size = 4; - else if( attribute.type === "c" ) size = 3; + this.defines = { 'STANDARD': '' }; - attribute.size = size; + this.type = 'MeshStandardMaterial'; - attribute.array = new Float32Array( nvertices * size ); + this.color = new THREE.Color( 0xffffff ); // diffuse + this.roughness = 0.5; + this.metalness = 0.5; - attribute.buffer = _gl.createBuffer(); - attribute.buffer.belongsToAttribute = a; + this.map = null; - originalAttribute.needsUpdate = true; - attribute.__original = originalAttribute; + this.lightMap = null; + this.lightMapIntensity = 1.0; - } + this.aoMap = null; + this.aoMapIntensity = 1.0; - geometryGroup.__webglCustomAttributesList.push( attribute ); + this.emissive = new THREE.Color( 0x000000 ); + this.emissiveIntensity = 1.0; + this.emissiveMap = null; - } + this.bumpMap = null; + this.bumpScale = 1; - } + this.normalMap = null; + this.normalScale = new THREE.Vector2( 1, 1 ); - geometryGroup.__inittedArrays = true; + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - }; + this.roughnessMap = null; - function getBufferMaterial( object, geometryGroup ) { + this.metalnessMap = null; - return object.material instanceof THREE.MeshFaceMaterial - ? object.material.materials[ geometryGroup.materialIndex ] - : object.material; + this.alphaMap = null; - }; + this.envMap = null; + this.envMapIntensity = 1.0; - function materialNeedsSmoothNormals ( material ) { + this.refractionRatio = 0.98; - return material && material.shading !== undefined && material.shading === THREE.SmoothShading; + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; - }; + this.skinning = false; + this.morphTargets = false; + this.morphNormals = false; - function bufferGuessNormalType ( material ) { + this.setValues( parameters ); - // only MeshBasicMaterial and MeshDepthMaterial don't need normals +}; - if ( ( material instanceof THREE.MeshBasicMaterial && !material.envMap ) || material instanceof THREE.MeshDepthMaterial ) { +THREE.MeshStandardMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.MeshStandardMaterial.prototype.constructor = THREE.MeshStandardMaterial; - return false; +THREE.MeshStandardMaterial.prototype.copy = function ( source ) { - } + THREE.Material.prototype.copy.call( this, source ); - if ( materialNeedsSmoothNormals( material ) ) { + this.defines = { 'STANDARD': '' }; - return THREE.SmoothShading; + this.color.copy( source.color ); + this.roughness = source.roughness; + this.metalness = source.metalness; - } else { + this.map = source.map; - return THREE.FlatShading; + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; - } + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; - }; + this.emissive.copy( source.emissive ); + this.emissiveMap = source.emissiveMap; + this.emissiveIntensity = source.emissiveIntensity; - function bufferGuessVertexColorType( material ) { + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; - if ( material.vertexColors ) { + this.normalMap = source.normalMap; + this.normalScale.copy( source.normalScale ); - return material.vertexColors; + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; - } + this.roughnessMap = source.roughnessMap; - return false; + this.metalnessMap = source.metalnessMap; - }; + this.alphaMap = source.alphaMap; - function bufferGuessUVType( material ) { + this.envMap = source.envMap; + this.envMapIntensity = source.envMapIntensity; - // material must use some texture to require uvs + this.refractionRatio = source.refractionRatio; - if ( material.map || - material.lightMap || - material.bumpMap || - material.normalMap || - material.specularMap || - material instanceof THREE.ShaderMaterial ) { + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; - return true; + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; + this.morphNormals = source.morphNormals; - } + return this; - return false; +}; - }; +// File:src/materials/MeshPhysicalMaterial.js - // +/** + * @author WestLangley / http://github.com/WestLangley + * + * parameters = { + * reflectivity: + * } + */ - function initDirectBuffers( geometry ) { +THREE.MeshPhysicalMaterial = function ( parameters ) { - var a, attribute, type; + THREE.MeshStandardMaterial.call( this ); - for ( a in geometry.attributes ) { + this.defines = { 'PHYSICAL': '' }; - if ( a === "index" ) { + this.type = 'MeshPhysicalMaterial'; - type = _gl.ELEMENT_ARRAY_BUFFER; + this.reflectivity = 0.5; // maps to F0 = 0.04 - } else { + this.clearCoat = 0.0; + this.clearCoatRoughness = 0.0; - type = _gl.ARRAY_BUFFER; + this.setValues( parameters ); - } +}; - attribute = geometry.attributes[ a ]; +THREE.MeshPhysicalMaterial.prototype = Object.create( THREE.MeshStandardMaterial.prototype ); +THREE.MeshPhysicalMaterial.prototype.constructor = THREE.MeshPhysicalMaterial; - if ( attribute.numItems === undefined ) { +THREE.MeshPhysicalMaterial.prototype.copy = function ( source ) { - attribute.numItems = attribute.array.length; + THREE.MeshStandardMaterial.prototype.copy.call( this, source ); - } + this.defines = { 'PHYSICAL': '' }; - attribute.buffer = _gl.createBuffer(); + this.reflectivity = source.reflectivity; - _gl.bindBuffer( type, attribute.buffer ); - _gl.bufferData( type, attribute.array, _gl.STATIC_DRAW ); + this.clearCoat = source.clearCoat; + this.clearCoatRoughness = source.clearCoatRoughness; - } + return this; - }; +}; - // Buffer setting +// File:src/materials/MultiMaterial.js - function setParticleBuffers ( geometry, hint, object ) { +/** + * @author mrdoob / http://mrdoob.com/ + */ - var v, c, vertex, offset, index, color, +THREE.MultiMaterial = function ( materials ) { - vertices = geometry.vertices, - vl = vertices.length, + this.uuid = THREE.Math.generateUUID(); - colors = geometry.colors, - cl = colors.length, + this.type = 'MultiMaterial'; - vertexArray = geometry.__vertexArray, - colorArray = geometry.__colorArray, + this.materials = materials instanceof Array ? materials : []; - sortArray = geometry.__sortArray, + this.visible = true; - dirtyVertices = geometry.verticesNeedUpdate, - dirtyElements = geometry.elementsNeedUpdate, - dirtyColors = geometry.colorsNeedUpdate, +}; - customAttributes = geometry.__webglCustomAttributesList, - i, il, - a, ca, cal, value, - customAttribute; +THREE.MultiMaterial.prototype = { - if ( object.sortParticles ) { + constructor: THREE.MultiMaterial, - _projScreenMatrixPS.copy( _projScreenMatrix ); - _projScreenMatrixPS.multiply( object.matrixWorld ); + toJSON: function ( meta ) { - for ( v = 0; v < vl; v ++ ) { + var output = { + metadata: { + version: 4.2, + type: 'material', + generator: 'MaterialExporter' + }, + uuid: this.uuid, + type: this.type, + materials: [] + }; - vertex = vertices[ v ]; + var materials = this.materials; - _vector3.copy( vertex ); - _vector3.applyProjection( _projScreenMatrixPS ); + for ( var i = 0, l = materials.length; i < l; i ++ ) { - sortArray[ v ] = [ _vector3.z, v ]; + var material = materials[ i ].toJSON( meta ); + delete material.metadata; - } + output.materials.push( material ); - sortArray.sort( numericalSort ); + } - for ( v = 0; v < vl; v ++ ) { + output.visible = this.visible; - vertex = vertices[ sortArray[v][1] ]; + return output; - offset = v * 3; + }, - vertexArray[ offset ] = vertex.x; - vertexArray[ offset + 1 ] = vertex.y; - vertexArray[ offset + 2 ] = vertex.z; + clone: function () { - } + var material = new this.constructor(); - for ( c = 0; c < cl; c ++ ) { + for ( var i = 0; i < this.materials.length; i ++ ) { - offset = c * 3; + material.materials.push( this.materials[ i ].clone() ); - color = colors[ sortArray[c][1] ]; + } - colorArray[ offset ] = color.r; - colorArray[ offset + 1 ] = color.g; - colorArray[ offset + 2 ] = color.b; + material.visible = this.visible; - } + return material; - if ( customAttributes ) { + } - for ( i = 0, il = customAttributes.length; i < il; i ++ ) { +}; - customAttribute = customAttributes[ i ]; +// File:src/materials/PointsMaterial.js - if ( ! ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) ) continue; +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * map: new THREE.Texture( ), + * + * size: , + * sizeAttenuation: + * } + */ - offset = 0; +THREE.PointsMaterial = function ( parameters ) { - cal = customAttribute.value.length; + THREE.Material.call( this ); - if ( customAttribute.size === 1 ) { + this.type = 'PointsMaterial'; - for ( ca = 0; ca < cal; ca ++ ) { + this.color = new THREE.Color( 0xffffff ); - index = sortArray[ ca ][ 1 ]; + this.map = null; - customAttribute.array[ ca ] = customAttribute.value[ index ]; + this.size = 1; + this.sizeAttenuation = true; - } + this.lights = false; - } else if ( customAttribute.size === 2 ) { + this.setValues( parameters ); - for ( ca = 0; ca < cal; ca ++ ) { +}; - index = sortArray[ ca ][ 1 ]; +THREE.PointsMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.PointsMaterial.prototype.constructor = THREE.PointsMaterial; - value = customAttribute.value[ index ]; +THREE.PointsMaterial.prototype.copy = function ( source ) { - customAttribute.array[ offset ] = value.x; - customAttribute.array[ offset + 1 ] = value.y; + THREE.Material.prototype.copy.call( this, source ); - offset += 2; + this.color.copy( source.color ); - } + this.map = source.map; - } else if ( customAttribute.size === 3 ) { + this.size = source.size; + this.sizeAttenuation = source.sizeAttenuation; - if ( customAttribute.type === "c" ) { + return this; - for ( ca = 0; ca < cal; ca ++ ) { +}; - index = sortArray[ ca ][ 1 ]; +// File:src/materials/ShaderMaterial.js - value = customAttribute.value[ index ]; +/** + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * defines: { "label" : "value" }, + * uniforms: { "parameter1": { value: 1.0 }, "parameter2": { value2: 2 } }, + * + * fragmentShader: , + * vertexShader: , + * + * wireframe: , + * wireframeLinewidth: , + * + * lights: , + * + * skinning: , + * morphTargets: , + * morphNormals: + * } + */ - customAttribute.array[ offset ] = value.r; - customAttribute.array[ offset + 1 ] = value.g; - customAttribute.array[ offset + 2 ] = value.b; +THREE.ShaderMaterial = function ( parameters ) { - offset += 3; + THREE.Material.call( this ); - } + this.type = 'ShaderMaterial'; - } else { + this.defines = {}; + this.uniforms = {}; - for ( ca = 0; ca < cal; ca ++ ) { + 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}'; - index = sortArray[ ca ][ 1 ]; + this.linewidth = 1; - value = customAttribute.value[ index ]; + this.wireframe = false; + this.wireframeLinewidth = 1; - customAttribute.array[ offset ] = value.x; - customAttribute.array[ offset + 1 ] = value.y; - customAttribute.array[ offset + 2 ] = value.z; + this.fog = false; // set to use scene fog + this.lights = false; // set to use scene lights + this.clipping = false; // set to use user-defined clipping planes - offset += 3; + this.skinning = false; // set to use skinning attribute streams + this.morphTargets = false; // set to use morph targets + this.morphNormals = false; // set to use morph normals - } + this.extensions = { + derivatives: false, // set to use derivatives + fragDepth: false, // set to use fragment depth values + drawBuffers: false, // set to use draw buffers + shaderTextureLOD: false // set to use shader texture LOD + }; - } + // 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 ] + }; - } else if ( customAttribute.size === 4 ) { + this.index0AttributeName = undefined; - for ( ca = 0; ca < cal; ca ++ ) { + if ( parameters !== undefined ) { - index = sortArray[ ca ][ 1 ]; + if ( parameters.attributes !== undefined ) { - value = customAttribute.value[ index ]; + console.error( 'THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead.' ); - customAttribute.array[ offset ] = value.x; - customAttribute.array[ offset + 1 ] = value.y; - customAttribute.array[ offset + 2 ] = value.z; - customAttribute.array[ offset + 3 ] = value.w; + } - offset += 4; + this.setValues( parameters ); - } + } - } +}; - } +THREE.ShaderMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.ShaderMaterial.prototype.constructor = THREE.ShaderMaterial; - } +THREE.ShaderMaterial.prototype.copy = function ( source ) { - } else { + THREE.Material.prototype.copy.call( this, source ); - if ( dirtyVertices ) { + this.fragmentShader = source.fragmentShader; + this.vertexShader = source.vertexShader; - for ( v = 0; v < vl; v ++ ) { + this.uniforms = THREE.UniformsUtils.clone( source.uniforms ); - vertex = vertices[ v ]; + this.defines = source.defines; - offset = v * 3; + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; - vertexArray[ offset ] = vertex.x; - vertexArray[ offset + 1 ] = vertex.y; - vertexArray[ offset + 2 ] = vertex.z; + this.lights = source.lights; + this.clipping = source.clipping; - } + this.skinning = source.skinning; - } + this.morphTargets = source.morphTargets; + this.morphNormals = source.morphNormals; - if ( dirtyColors ) { + this.extensions = source.extensions; - for ( c = 0; c < cl; c ++ ) { + return this; - color = colors[ c ]; +}; - offset = c * 3; +THREE.ShaderMaterial.prototype.toJSON = function ( meta ) { - colorArray[ offset ] = color.r; - colorArray[ offset + 1 ] = color.g; - colorArray[ offset + 2 ] = color.b; + var data = THREE.Material.prototype.toJSON.call( this, meta ); - } + data.uniforms = this.uniforms; + data.vertexShader = this.vertexShader; + data.fragmentShader = this.fragmentShader; - } + return data; - if ( customAttributes ) { +}; - for ( i = 0, il = customAttributes.length; i < il; i ++ ) { +// File:src/materials/RawShaderMaterial.js - customAttribute = customAttributes[ i ]; +/** + * @author mrdoob / http://mrdoob.com/ + */ - if ( customAttribute.needsUpdate && - ( customAttribute.boundTo === undefined || - customAttribute.boundTo === "vertices") ) { +THREE.RawShaderMaterial = function ( parameters ) { - cal = customAttribute.value.length; + THREE.ShaderMaterial.call( this, parameters ); - offset = 0; + this.type = 'RawShaderMaterial'; - if ( customAttribute.size === 1 ) { +}; - for ( ca = 0; ca < cal; ca ++ ) { +THREE.RawShaderMaterial.prototype = Object.create( THREE.ShaderMaterial.prototype ); +THREE.RawShaderMaterial.prototype.constructor = THREE.RawShaderMaterial; - customAttribute.array[ ca ] = customAttribute.value[ ca ]; +// File:src/materials/SpriteMaterial.js - } +/** + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * map: new THREE.Texture( ), + * + * uvOffset: new THREE.Vector2(), + * uvScale: new THREE.Vector2() + * } + */ - } else if ( customAttribute.size === 2 ) { +THREE.SpriteMaterial = function ( parameters ) { - for ( ca = 0; ca < cal; ca ++ ) { + THREE.Material.call( this ); - value = customAttribute.value[ ca ]; + this.type = 'SpriteMaterial'; - customAttribute.array[ offset ] = value.x; - customAttribute.array[ offset + 1 ] = value.y; + this.color = new THREE.Color( 0xffffff ); + this.map = null; - offset += 2; + this.rotation = 0; - } + this.fog = false; + this.lights = false; - } else if ( customAttribute.size === 3 ) { + this.setValues( parameters ); - if ( customAttribute.type === "c" ) { +}; - for ( ca = 0; ca < cal; ca ++ ) { +THREE.SpriteMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.SpriteMaterial.prototype.constructor = THREE.SpriteMaterial; - value = customAttribute.value[ ca ]; +THREE.SpriteMaterial.prototype.copy = function ( source ) { - customAttribute.array[ offset ] = value.r; - customAttribute.array[ offset + 1 ] = value.g; - customAttribute.array[ offset + 2 ] = value.b; + THREE.Material.prototype.copy.call( this, source ); - offset += 3; + this.color.copy( source.color ); + this.map = source.map; - } + this.rotation = source.rotation; - } else { + return this; - for ( ca = 0; ca < cal; ca ++ ) { +}; - value = customAttribute.value[ ca ]; +// File:src/materials/ShadowMaterial.js - customAttribute.array[ offset ] = value.x; - customAttribute.array[ offset + 1 ] = value.y; - customAttribute.array[ offset + 2 ] = value.z; +/** + * @author mrdoob / http://mrdoob.com/ + */ - offset += 3; +THREE.ShadowMaterial = function () { - } + THREE.ShaderMaterial.call( this, { + uniforms: THREE.UniformsUtils.merge( [ + THREE.UniformsLib[ "lights" ], + { + opacity: { value: 1.0 } + } + ] ), + vertexShader: THREE.ShaderChunk[ 'shadow_vert' ], + fragmentShader: THREE.ShaderChunk[ 'shadow_frag' ] + } ); - } + this.lights = true; + this.transparent = true; - } else if ( customAttribute.size === 4 ) { + Object.defineProperties( this, { + opacity: { + enumerable: true, + get: function () { + return this.uniforms.opacity.value; + }, + set: function ( value ) { + this.uniforms.opacity.value = value; + } + } + } ); - for ( ca = 0; ca < cal; ca ++ ) { +}; - value = customAttribute.value[ ca ]; +THREE.ShadowMaterial.prototype = Object.create( THREE.ShaderMaterial.prototype ); +THREE.ShadowMaterial.prototype.constructor = THREE.ShadowMaterial; - customAttribute.array[ offset ] = value.x; - customAttribute.array[ offset + 1 ] = value.y; - customAttribute.array[ offset + 2 ] = value.z; - customAttribute.array[ offset + 3 ] = value.w; +// File:src/textures/Texture.js - offset += 4; +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author szimek / https://github.com/szimek/ + */ - } +THREE.Texture = function ( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) { - } + Object.defineProperty( this, 'id', { value: THREE.TextureIdCount ++ } ); - } + this.uuid = THREE.Math.generateUUID(); - } + this.name = ''; + this.sourceFile = ''; - } + this.image = image !== undefined ? image : THREE.Texture.DEFAULT_IMAGE; + this.mipmaps = []; - } + this.mapping = mapping !== undefined ? mapping : THREE.Texture.DEFAULT_MAPPING; - if ( dirtyVertices || object.sortParticles ) { + this.wrapS = wrapS !== undefined ? wrapS : THREE.ClampToEdgeWrapping; + this.wrapT = wrapT !== undefined ? wrapT : THREE.ClampToEdgeWrapping; - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); + this.magFilter = magFilter !== undefined ? magFilter : THREE.LinearFilter; + this.minFilter = minFilter !== undefined ? minFilter : THREE.LinearMipMapLinearFilter; - } + this.anisotropy = anisotropy !== undefined ? anisotropy : 1; - if ( dirtyColors || object.sortParticles ) { + this.format = format !== undefined ? format : THREE.RGBAFormat; + this.type = type !== undefined ? type : THREE.UnsignedByteType; - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); + this.offset = new THREE.Vector2( 0, 0 ); + this.repeat = new THREE.Vector2( 1, 1 ); - } + 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) - if ( customAttributes ) { - for ( i = 0, il = customAttributes.length; i < il; i ++ ) { + // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap. + // + // Also changing the encoding after already used by a Material will not automatically make the Material + // update. You need to explicitly call Material.needsUpdate to trigger it to recompile. + this.encoding = encoding !== undefined ? encoding : THREE.LinearEncoding; - customAttribute = customAttributes[ i ]; + this.version = 0; + this.onUpdate = null; - if ( customAttribute.needsUpdate || object.sortParticles ) { +}; - _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); +THREE.Texture.DEFAULT_IMAGE = undefined; +THREE.Texture.DEFAULT_MAPPING = THREE.UVMapping; - } +THREE.Texture.prototype = { - } + constructor: THREE.Texture, - } + set needsUpdate( value ) { + if ( value === true ) this.version ++; - }; + }, - function setLineBuffers ( geometry, hint ) { + clone: function () { - var v, c, d, vertex, offset, color, + return new this.constructor().copy( this ); - vertices = geometry.vertices, - colors = geometry.colors, - lineDistances = geometry.lineDistances, + }, - vl = vertices.length, - cl = colors.length, - dl = lineDistances.length, + copy: function ( source ) { - vertexArray = geometry.__vertexArray, - colorArray = geometry.__colorArray, - lineDistanceArray = geometry.__lineDistanceArray, + this.image = source.image; + this.mipmaps = source.mipmaps.slice( 0 ); - dirtyVertices = geometry.verticesNeedUpdate, - dirtyColors = geometry.colorsNeedUpdate, - dirtyLineDistances = geometry.lineDistancesNeedUpdate, + this.mapping = source.mapping; - customAttributes = geometry.__webglCustomAttributesList, + this.wrapS = source.wrapS; + this.wrapT = source.wrapT; - i, il, - a, ca, cal, value, - customAttribute; + this.magFilter = source.magFilter; + this.minFilter = source.minFilter; - if ( dirtyVertices ) { + this.anisotropy = source.anisotropy; - for ( v = 0; v < vl; v ++ ) { + this.format = source.format; + this.type = source.type; - vertex = vertices[ v ]; + this.offset.copy( source.offset ); + this.repeat.copy( source.repeat ); - offset = v * 3; + this.generateMipmaps = source.generateMipmaps; + this.premultiplyAlpha = source.premultiplyAlpha; + this.flipY = source.flipY; + this.unpackAlignment = source.unpackAlignment; + this.encoding = source.encoding; - vertexArray[ offset ] = vertex.x; - vertexArray[ offset + 1 ] = vertex.y; - vertexArray[ offset + 2 ] = vertex.z; + return this; - } + }, + + toJSON: function ( meta ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); + if ( meta.textures[ this.uuid ] !== undefined ) { + + return meta.textures[ this.uuid ]; } - if ( dirtyColors ) { + function getDataURL( image ) { + + var canvas; - for ( c = 0; c < cl; c ++ ) { + if ( image.toDataURL !== undefined ) { - color = colors[ c ]; + canvas = image; - offset = c * 3; + } else { - colorArray[ offset ] = color.r; - colorArray[ offset + 1 ] = color.g; - colorArray[ offset + 2 ] = color.b; + canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); + canvas.width = image.width; + canvas.height = image.height; - } + canvas.getContext( '2d' ).drawImage( image, 0, 0, image.width, image.height ); - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); + } - } + if ( canvas.width > 2048 || canvas.height > 2048 ) { - if ( dirtyLineDistances ) { + return canvas.toDataURL( 'image/jpeg', 0.6 ); - for ( d = 0; d < dl; d ++ ) { + } else { - lineDistanceArray[ d ] = lineDistances[ d ]; + return canvas.toDataURL( 'image/png' ); } - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglLineDistanceBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, lineDistanceArray, hint ); - } - if ( customAttributes ) { + var output = { + metadata: { + version: 4.4, + type: 'Texture', + generator: 'Texture.toJSON' + }, - for ( i = 0, il = customAttributes.length; i < il; i ++ ) { + uuid: this.uuid, + name: this.name, - customAttribute = customAttributes[ i ]; + mapping: this.mapping, - if ( customAttribute.needsUpdate && - ( customAttribute.boundTo === undefined || - customAttribute.boundTo === "vertices" ) ) { + repeat: [ this.repeat.x, this.repeat.y ], + offset: [ this.offset.x, this.offset.y ], + wrap: [ this.wrapS, this.wrapT ], - offset = 0; + minFilter: this.minFilter, + magFilter: this.magFilter, + anisotropy: this.anisotropy, - cal = customAttribute.value.length; - - if ( customAttribute.size === 1 ) { + flipY: this.flipY + }; - for ( ca = 0; ca < cal; ca ++ ) { + if ( this.image !== undefined ) { - customAttribute.array[ ca ] = customAttribute.value[ ca ]; + // TODO: Move to THREE.Image - } + var image = this.image; - } else if ( customAttribute.size === 2 ) { + if ( image.uuid === undefined ) { - for ( ca = 0; ca < cal; ca ++ ) { + image.uuid = THREE.Math.generateUUID(); // UGH - value = customAttribute.value[ ca ]; + } - customAttribute.array[ offset ] = value.x; - customAttribute.array[ offset + 1 ] = value.y; + if ( meta.images[ image.uuid ] === undefined ) { - offset += 2; + meta.images[ image.uuid ] = { + uuid: image.uuid, + url: getDataURL( image ) + }; - } + } - } else if ( customAttribute.size === 3 ) { + output.image = image.uuid; - if ( customAttribute.type === "c" ) { + } - for ( ca = 0; ca < cal; ca ++ ) { + meta.textures[ this.uuid ] = output; - value = customAttribute.value[ ca ]; + return output; - customAttribute.array[ offset ] = value.r; - customAttribute.array[ offset + 1 ] = value.g; - customAttribute.array[ offset + 2 ] = value.b; + }, - offset += 3; + dispose: function () { - } + this.dispatchEvent( { type: 'dispose' } ); - } else { + }, - for ( ca = 0; ca < cal; ca ++ ) { + transformUv: function ( uv ) { - value = customAttribute.value[ ca ]; + if ( this.mapping !== THREE.UVMapping ) return; - customAttribute.array[ offset ] = value.x; - customAttribute.array[ offset + 1 ] = value.y; - customAttribute.array[ offset + 2 ] = value.z; + uv.multiply( this.repeat ); + uv.add( this.offset ); - offset += 3; + if ( uv.x < 0 || uv.x > 1 ) { - } + switch ( this.wrapS ) { - } + case THREE.RepeatWrapping: - } else if ( customAttribute.size === 4 ) { + uv.x = uv.x - Math.floor( uv.x ); + break; - for ( ca = 0; ca < cal; ca ++ ) { + case THREE.ClampToEdgeWrapping: - value = customAttribute.value[ ca ]; + uv.x = uv.x < 0 ? 0 : 1; + break; - customAttribute.array[ offset ] = value.x; - customAttribute.array[ offset + 1 ] = value.y; - customAttribute.array[ offset + 2 ] = value.z; - customAttribute.array[ offset + 3 ] = value.w; + case THREE.MirroredRepeatWrapping: - offset += 4; + if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { - } + uv.x = Math.ceil( uv.x ) - uv.x; - } + } else { - _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); + uv.x = uv.x - Math.floor( uv.x ); - } + } + break; } } - }; + if ( uv.y < 0 || uv.y > 1 ) { - function setRibbonBuffers ( geometry, hint ) { + switch ( this.wrapT ) { - var v, c, n, vertex, offset, color, normal, + case THREE.RepeatWrapping: - i, il, ca, cal, customAttribute, value, + uv.y = uv.y - Math.floor( uv.y ); + break; - vertices = geometry.vertices, - colors = geometry.colors, - normals = geometry.normals, + case THREE.ClampToEdgeWrapping: - vl = vertices.length, - cl = colors.length, - nl = normals.length, + uv.y = uv.y < 0 ? 0 : 1; + break; - vertexArray = geometry.__vertexArray, - colorArray = geometry.__colorArray, - normalArray = geometry.__normalArray, + case THREE.MirroredRepeatWrapping: - dirtyVertices = geometry.verticesNeedUpdate, - dirtyColors = geometry.colorsNeedUpdate, - dirtyNormals = geometry.normalsNeedUpdate, + if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { - customAttributes = geometry.__webglCustomAttributesList; + uv.y = Math.ceil( uv.y ) - uv.y; - if ( dirtyVertices ) { + } else { - for ( v = 0; v < vl; v ++ ) { + uv.y = uv.y - Math.floor( uv.y ); - vertex = vertices[ v ]; + } + break; - offset = v * 3; + } - vertexArray[ offset ] = vertex.x; - vertexArray[ offset + 1 ] = vertex.y; - vertexArray[ offset + 2 ] = vertex.z; + } - } + if ( this.flipY ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); + uv.y = 1 - uv.y; } - if ( dirtyColors ) { + } + +}; + +Object.assign( THREE.Texture.prototype, THREE.EventDispatcher.prototype ); - for ( c = 0; c < cl; c ++ ) { +THREE.TextureIdCount = 0; - color = colors[ c ]; +// File:src/textures/DepthTexture.js - offset = c * 3; +/** + * @author Matt DesLauriers / @mattdesl + */ - colorArray[ offset ] = color.r; - colorArray[ offset + 1 ] = color.g; - colorArray[ offset + 2 ] = color.b; +THREE.DepthTexture = function ( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) { - } + THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, THREE.DepthFormat, type, anisotropy ); - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); + this.image = { width: width, height: height }; - } + this.type = type !== undefined ? type : THREE.UnsignedShortType; - if ( dirtyNormals ) { + this.magFilter = magFilter !== undefined ? magFilter : THREE.NearestFilter; + this.minFilter = minFilter !== undefined ? minFilter : THREE.NearestFilter; - for ( n = 0; n < nl; n ++ ) { + this.flipY = false; + this.generateMipmaps = false; - normal = normals[ n ]; +}; - offset = n * 3; +THREE.DepthTexture.prototype = Object.create( THREE.Texture.prototype ); +THREE.DepthTexture.prototype.constructor = THREE.DepthTexture; - normalArray[ offset ] = normal.x; - normalArray[ offset + 1 ] = normal.y; - normalArray[ offset + 2 ] = normal.z; +// File:src/textures/CanvasTexture.js - } +/** + * @author mrdoob / http://mrdoob.com/ + */ - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglNormalBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, normalArray, hint ); +THREE.CanvasTexture = function ( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { - } + THREE.Texture.call( this, canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - if ( customAttributes ) { + this.needsUpdate = true; - for ( i = 0, il = customAttributes.length; i < il; i ++ ) { +}; - customAttribute = customAttributes[ i ]; +THREE.CanvasTexture.prototype = Object.create( THREE.Texture.prototype ); +THREE.CanvasTexture.prototype.constructor = THREE.CanvasTexture; - if ( customAttribute.needsUpdate && - ( customAttribute.boundTo === undefined || - customAttribute.boundTo === "vertices" ) ) { +// File:src/textures/CubeTexture.js - offset = 0; +/** + * @author mrdoob / http://mrdoob.com/ + */ - cal = customAttribute.value.length; +THREE.CubeTexture = function ( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) { - if ( customAttribute.size === 1 ) { + images = images !== undefined ? images : []; + mapping = mapping !== undefined ? mapping : THREE.CubeReflectionMapping; - for ( ca = 0; ca < cal; ca ++ ) { + THREE.Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); - customAttribute.array[ ca ] = customAttribute.value[ ca ]; + this.flipY = false; - } +}; - } else if ( customAttribute.size === 2 ) { +THREE.CubeTexture.prototype = Object.create( THREE.Texture.prototype ); +THREE.CubeTexture.prototype.constructor = THREE.CubeTexture; - for ( ca = 0; ca < cal; ca ++ ) { +Object.defineProperty( THREE.CubeTexture.prototype, 'images', { - value = customAttribute.value[ ca ]; + get: function () { - customAttribute.array[ offset ] = value.x; - customAttribute.array[ offset + 1 ] = value.y; + return this.image; - offset += 2; + }, - } + set: function ( value ) { - } else if ( customAttribute.size === 3 ) { + this.image = value; - if ( customAttribute.type === "c" ) { + } - for ( ca = 0; ca < cal; ca ++ ) { +} ); - value = customAttribute.value[ ca ]; +// File:src/textures/CompressedTexture.js - customAttribute.array[ offset ] = value.r; - customAttribute.array[ offset + 1 ] = value.g; - customAttribute.array[ offset + 2 ] = value.b; +/** + * @author alteredq / http://alteredqualia.com/ + */ - offset += 3; +THREE.CompressedTexture = function ( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { - } + THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); - } else { + this.image = { width: width, height: height }; + this.mipmaps = mipmaps; - for ( ca = 0; ca < cal; ca ++ ) { + // no flipping for cube textures + // (also flipping doesn't work for compressed textures ) - value = customAttribute.value[ ca ]; + this.flipY = false; - customAttribute.array[ offset ] = value.x; - customAttribute.array[ offset + 1 ] = value.y; - customAttribute.array[ offset + 2 ] = value.z; + // can't generate mipmaps for compressed textures + // mips must be embedded in DDS files - offset += 3; + this.generateMipmaps = false; - } +}; - } +THREE.CompressedTexture.prototype = Object.create( THREE.Texture.prototype ); +THREE.CompressedTexture.prototype.constructor = THREE.CompressedTexture; - } else if ( customAttribute.size === 4 ) { +// File:src/textures/DataTexture.js - for ( ca = 0; ca < cal; ca ++ ) { +/** + * @author alteredq / http://alteredqualia.com/ + */ - value = customAttribute.value[ ca ]; +THREE.DataTexture = function ( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) { - customAttribute.array[ offset ] = value.x; - customAttribute.array[ offset + 1 ] = value.y; - customAttribute.array[ offset + 2 ] = value.z; - customAttribute.array[ offset + 3 ] = value.w; + THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ); - offset += 4; + this.image = { data: data, width: width, height: height }; - } + this.magFilter = magFilter !== undefined ? magFilter : THREE.NearestFilter; + this.minFilter = minFilter !== undefined ? minFilter : THREE.NearestFilter; - } + this.flipY = false; + this.generateMipmaps = false; - _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); +}; - } +THREE.DataTexture.prototype = Object.create( THREE.Texture.prototype ); +THREE.DataTexture.prototype.constructor = THREE.DataTexture; - } +// File:src/textures/VideoTexture.js - } +/** + * @author mrdoob / http://mrdoob.com/ + */ - }; +THREE.VideoTexture = function ( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { - function setMeshBuffers( geometryGroup, object, hint, dispose, material ) { + THREE.Texture.call( this, video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); - if ( ! geometryGroup.__inittedArrays ) { + this.generateMipmaps = false; - return; + var scope = this; - } + function update() { - var normalType = bufferGuessNormalType( material ), - vertexColorType = bufferGuessVertexColorType( material ), - uvType = bufferGuessUVType( material ), + requestAnimationFrame( update ); - needsSmoothNormals = ( normalType === THREE.SmoothShading ); + if ( video.readyState >= video.HAVE_CURRENT_DATA ) { - var f, fl, fi, face, - vertexNormals, faceNormal, normal, - vertexColors, faceColor, - vertexTangents, - uv, uv2, v1, v2, v3, v4, t1, t2, t3, t4, n1, n2, n3, n4, - c1, c2, c3, c4, - sw1, sw2, sw3, sw4, - si1, si2, si3, si4, - sa1, sa2, sa3, sa4, - sb1, sb2, sb3, sb4, - m, ml, i, il, - vn, uvi, uv2i, - vk, vkl, vka, - nka, chf, faceVertexNormals, - a, + scope.needsUpdate = true; - vertexIndex = 0, + } - offset = 0, - offset_uv = 0, - offset_uv2 = 0, - offset_face = 0, - offset_normal = 0, - offset_tangent = 0, - offset_line = 0, - offset_color = 0, - offset_skin = 0, - offset_morphTarget = 0, - offset_custom = 0, - offset_customSrc = 0, + } - value, + update(); - vertexArray = geometryGroup.__vertexArray, - uvArray = geometryGroup.__uvArray, - uv2Array = geometryGroup.__uv2Array, - normalArray = geometryGroup.__normalArray, - tangentArray = geometryGroup.__tangentArray, - colorArray = geometryGroup.__colorArray, +}; - skinIndexArray = geometryGroup.__skinIndexArray, - skinWeightArray = geometryGroup.__skinWeightArray, +THREE.VideoTexture.prototype = Object.create( THREE.Texture.prototype ); +THREE.VideoTexture.prototype.constructor = THREE.VideoTexture; - morphTargetsArrays = geometryGroup.__morphTargetsArrays, - morphNormalsArrays = geometryGroup.__morphNormalsArrays, +// File:src/objects/Group.js - customAttributes = geometryGroup.__webglCustomAttributesList, - customAttribute, +/** + * @author mrdoob / http://mrdoob.com/ + */ - faceArray = geometryGroup.__faceArray, - lineArray = geometryGroup.__lineArray, +THREE.Group = function () { - geometry = object.geometry, // this is shared for all chunks + THREE.Object3D.call( this ); - dirtyVertices = geometry.verticesNeedUpdate, - dirtyElements = geometry.elementsNeedUpdate, - dirtyUvs = geometry.uvsNeedUpdate, - dirtyNormals = geometry.normalsNeedUpdate, - dirtyTangents = geometry.tangentsNeedUpdate, - dirtyColors = geometry.colorsNeedUpdate, - dirtyMorphTargets = geometry.morphTargetsNeedUpdate, + this.type = 'Group'; - vertices = geometry.vertices, - chunk_faces3 = geometryGroup.faces3, - obj_faces = geometry.faces, +}; - obj_uvs = geometry.faceVertexUvs[ 0 ], - obj_uvs2 = geometry.faceVertexUvs[ 1 ], +THREE.Group.prototype = Object.assign( Object.create( THREE.Object3D.prototype ), { - obj_colors = geometry.colors, + constructor: THREE.Group - obj_skinIndices = geometry.skinIndices, - obj_skinWeights = geometry.skinWeights, +} ); - morphTargets = geometry.morphTargets, - morphNormals = geometry.morphNormals; +// File:src/objects/Points.js - if ( dirtyVertices ) { +/** + * @author alteredq / http://alteredqualia.com/ + */ - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { +THREE.Points = function ( geometry, material ) { - face = obj_faces[ chunk_faces3[ f ] ]; + THREE.Object3D.call( this ); - v1 = vertices[ face.a ]; - v2 = vertices[ face.b ]; - v3 = vertices[ face.c ]; + this.type = 'Points'; - vertexArray[ offset ] = v1.x; - vertexArray[ offset + 1 ] = v1.y; - vertexArray[ offset + 2 ] = v1.z; + this.geometry = geometry !== undefined ? geometry : new THREE.BufferGeometry(); + this.material = material !== undefined ? material : new THREE.PointsMaterial( { color: Math.random() * 0xffffff } ); - vertexArray[ offset + 3 ] = v2.x; - vertexArray[ offset + 4 ] = v2.y; - vertexArray[ offset + 5 ] = v2.z; +}; - vertexArray[ offset + 6 ] = v3.x; - vertexArray[ offset + 7 ] = v3.y; - vertexArray[ offset + 8 ] = v3.z; +THREE.Points.prototype = Object.assign( Object.create( THREE.Object3D.prototype ), { - offset += 9; + constructor: THREE.Points, - } + raycast: ( function () { - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); + var inverseMatrix = new THREE.Matrix4(); + var ray = new THREE.Ray(); + var sphere = new THREE.Sphere(); - } + return function raycast( raycaster, intersects ) { - if ( dirtyMorphTargets ) { + var object = this; + var geometry = this.geometry; + var matrixWorld = this.matrixWorld; + var threshold = raycaster.params.Points.threshold; - for ( vk = 0, vkl = morphTargets.length; vk < vkl; vk ++ ) { + // Checking boundingSphere distance to ray - offset_morphTarget = 0; + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + sphere.copy( geometry.boundingSphere ); + sphere.applyMatrix4( matrixWorld ); - chf = chunk_faces3[ f ]; - face = obj_faces[ chf ]; + if ( raycaster.ray.intersectsSphere( sphere ) === false ) return; - // morph positions + // - v1 = morphTargets[ vk ].vertices[ face.a ]; - v2 = morphTargets[ vk ].vertices[ face.b ]; - v3 = morphTargets[ vk ].vertices[ face.c ]; + inverseMatrix.getInverse( matrixWorld ); + ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); - vka = morphTargetsArrays[ vk ]; + var localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); + var localThresholdSq = localThreshold * localThreshold; + var position = new THREE.Vector3(); - vka[ offset_morphTarget ] = v1.x; - vka[ offset_morphTarget + 1 ] = v1.y; - vka[ offset_morphTarget + 2 ] = v1.z; + function testPoint( point, index ) { - vka[ offset_morphTarget + 3 ] = v2.x; - vka[ offset_morphTarget + 4 ] = v2.y; - vka[ offset_morphTarget + 5 ] = v2.z; + var rayPointDistanceSq = ray.distanceSqToPoint( point ); - vka[ offset_morphTarget + 6 ] = v3.x; - vka[ offset_morphTarget + 7 ] = v3.y; - vka[ offset_morphTarget + 8 ] = v3.z; + if ( rayPointDistanceSq < localThresholdSq ) { - // morph normals + var intersectPoint = ray.closestPointToPoint( point ); + intersectPoint.applyMatrix4( matrixWorld ); - if ( material.morphNormals ) { + var distance = raycaster.ray.origin.distanceTo( intersectPoint ); - if ( needsSmoothNormals ) { + if ( distance < raycaster.near || distance > raycaster.far ) return; - faceVertexNormals = morphNormals[ vk ].vertexNormals[ chf ]; + intersects.push( { - n1 = faceVertexNormals.a; - n2 = faceVertexNormals.b; - n3 = faceVertexNormals.c; + distance: distance, + distanceToRay: Math.sqrt( rayPointDistanceSq ), + point: intersectPoint.clone(), + index: index, + face: null, + object: object - } else { + } ); - n1 = morphNormals[ vk ].faceNormals[ chf ]; - n2 = n1; - n3 = n1; + } - } + } - nka = morphNormalsArrays[ vk ]; + if ( geometry instanceof THREE.BufferGeometry ) { - nka[ offset_morphTarget ] = n1.x; - nka[ offset_morphTarget + 1 ] = n1.y; - nka[ offset_morphTarget + 2 ] = n1.z; + var index = geometry.index; + var attributes = geometry.attributes; + var positions = attributes.position.array; - nka[ offset_morphTarget + 3 ] = n2.x; - nka[ offset_morphTarget + 4 ] = n2.y; - nka[ offset_morphTarget + 5 ] = n2.z; + if ( index !== null ) { - nka[ offset_morphTarget + 6 ] = n3.x; - nka[ offset_morphTarget + 7 ] = n3.y; - nka[ offset_morphTarget + 8 ] = n3.z; + var indices = index.array; - } + for ( var i = 0, il = indices.length; i < il; i ++ ) { - // + var a = indices[ i ]; - offset_morphTarget += 9; + position.fromArray( positions, a * 3 ); - } + testPoint( position, a ); - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ vk ] ); - _gl.bufferData( _gl.ARRAY_BUFFER, morphTargetsArrays[ vk ], hint ); + } - if ( material.morphNormals ) { + } else { - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ vk ] ); - _gl.bufferData( _gl.ARRAY_BUFFER, morphNormalsArrays[ vk ], hint ); + for ( var i = 0, l = positions.length / 3; i < l; i ++ ) { - } + position.fromArray( positions, i * 3 ); - } + testPoint( position, i ); - } + } - if ( obj_skinWeights.length ) { + } - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + } else { - face = obj_faces[ chunk_faces3[ f ] ]; + var vertices = geometry.vertices; - // weights + for ( var i = 0, l = vertices.length; i < l; i ++ ) { - sw1 = obj_skinWeights[ face.a ]; - sw2 = obj_skinWeights[ face.b ]; - sw3 = obj_skinWeights[ face.c ]; + testPoint( vertices[ i ], i ); - skinWeightArray[ offset_skin ] = sw1.x; - skinWeightArray[ offset_skin + 1 ] = sw1.y; - skinWeightArray[ offset_skin + 2 ] = sw1.z; - skinWeightArray[ offset_skin + 3 ] = sw1.w; + } - skinWeightArray[ offset_skin + 4 ] = sw2.x; - skinWeightArray[ offset_skin + 5 ] = sw2.y; - skinWeightArray[ offset_skin + 6 ] = sw2.z; - skinWeightArray[ offset_skin + 7 ] = sw2.w; + } - skinWeightArray[ offset_skin + 8 ] = sw3.x; - skinWeightArray[ offset_skin + 9 ] = sw3.y; - skinWeightArray[ offset_skin + 10 ] = sw3.z; - skinWeightArray[ offset_skin + 11 ] = sw3.w; + }; - // indices + }() ), - si1 = obj_skinIndices[ face.a ]; - si2 = obj_skinIndices[ face.b ]; - si3 = obj_skinIndices[ face.c ]; + clone: function () { - skinIndexArray[ offset_skin ] = si1.x; - skinIndexArray[ offset_skin + 1 ] = si1.y; - skinIndexArray[ offset_skin + 2 ] = si1.z; - skinIndexArray[ offset_skin + 3 ] = si1.w; + return new this.constructor( this.geometry, this.material ).copy( this ); - skinIndexArray[ offset_skin + 4 ] = si2.x; - skinIndexArray[ offset_skin + 5 ] = si2.y; - skinIndexArray[ offset_skin + 6 ] = si2.z; - skinIndexArray[ offset_skin + 7 ] = si2.w; + } - skinIndexArray[ offset_skin + 8 ] = si3.x; - skinIndexArray[ offset_skin + 9 ] = si3.y; - skinIndexArray[ offset_skin + 10 ] = si3.z; - skinIndexArray[ offset_skin + 11 ] = si3.w; +} ); - offset_skin += 12; +// File:src/objects/Line.js - } +/** + * @author mrdoob / http://mrdoob.com/ + */ - if ( offset_skin > 0 ) { +THREE.Line = function ( geometry, material, mode ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, skinIndexArray, hint ); + if ( mode === 1 ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, skinWeightArray, hint ); + console.warn( 'THREE.Line: parameter THREE.LinePieces no longer supported. Created THREE.LineSegments instead.' ); + return new THREE.LineSegments( geometry, material ); - } + } - } + THREE.Object3D.call( this ); - if ( dirtyColors && vertexColorType ) { + this.type = 'Line'; - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + this.geometry = geometry !== undefined ? geometry : new THREE.BufferGeometry(); + this.material = material !== undefined ? material : new THREE.LineBasicMaterial( { color: Math.random() * 0xffffff } ); - face = obj_faces[ chunk_faces3[ f ] ]; +}; - vertexColors = face.vertexColors; - faceColor = face.color; +THREE.Line.prototype = Object.assign( Object.create( THREE.Object3D.prototype ), { - if ( vertexColors.length === 3 && vertexColorType === THREE.VertexColors ) { + constructor: THREE.Line, - c1 = vertexColors[ 0 ]; - c2 = vertexColors[ 1 ]; - c3 = vertexColors[ 2 ]; + raycast: ( function () { - } else { + var inverseMatrix = new THREE.Matrix4(); + var ray = new THREE.Ray(); + var sphere = new THREE.Sphere(); - c1 = faceColor; - c2 = faceColor; - c3 = faceColor; + return function raycast( raycaster, intersects ) { - } + var precision = raycaster.linePrecision; + var precisionSq = precision * precision; - colorArray[ offset_color ] = c1.r; - colorArray[ offset_color + 1 ] = c1.g; - colorArray[ offset_color + 2 ] = c1.b; + var geometry = this.geometry; + var matrixWorld = this.matrixWorld; - colorArray[ offset_color + 3 ] = c2.r; - colorArray[ offset_color + 4 ] = c2.g; - colorArray[ offset_color + 5 ] = c2.b; + // Checking boundingSphere distance to ray - colorArray[ offset_color + 6 ] = c3.r; - colorArray[ offset_color + 7 ] = c3.g; - colorArray[ offset_color + 8 ] = c3.b; + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - offset_color += 9; + sphere.copy( geometry.boundingSphere ); + sphere.applyMatrix4( matrixWorld ); - } + if ( raycaster.ray.intersectsSphere( sphere ) === false ) return; - if ( offset_color > 0 ) { + // - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); + inverseMatrix.getInverse( 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; - } + if ( geometry instanceof THREE.BufferGeometry ) { - if ( dirtyTangents && geometry.hasTangents ) { + var index = geometry.index; + var attributes = geometry.attributes; + var positions = attributes.position.array; - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + if ( index !== null ) { - face = obj_faces[ chunk_faces3[ f ] ]; + var indices = index.array; - vertexTangents = face.vertexTangents; + for ( var i = 0, l = indices.length - 1; i < l; i += step ) { - t1 = vertexTangents[ 0 ]; - t2 = vertexTangents[ 1 ]; - t3 = vertexTangents[ 2 ]; + var a = indices[ i ]; + var b = indices[ i + 1 ]; - tangentArray[ offset_tangent ] = t1.x; - tangentArray[ offset_tangent + 1 ] = t1.y; - tangentArray[ offset_tangent + 2 ] = t1.z; - tangentArray[ offset_tangent + 3 ] = t1.w; + vStart.fromArray( positions, a * 3 ); + vEnd.fromArray( positions, b * 3 ); - tangentArray[ offset_tangent + 4 ] = t2.x; - tangentArray[ offset_tangent + 5 ] = t2.y; - tangentArray[ offset_tangent + 6 ] = t2.z; - tangentArray[ offset_tangent + 7 ] = t2.w; + var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); - tangentArray[ offset_tangent + 8 ] = t3.x; - tangentArray[ offset_tangent + 9 ] = t3.y; - tangentArray[ offset_tangent + 10 ] = t3.z; - tangentArray[ offset_tangent + 11 ] = t3.w; + if ( distSq > precisionSq ) continue; - offset_tangent += 12; + interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation - } + var distance = raycaster.ray.origin.distanceTo( interRay ); - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, tangentArray, hint ); + if ( distance < raycaster.near || distance > raycaster.far ) continue; - } + intersects.push( { - if ( dirtyNormals && normalType ) { + 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 - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + } ); - face = obj_faces[ chunk_faces3[ f ] ]; + } - vertexNormals = face.vertexNormals; - faceNormal = face.normal; + } else { - if ( vertexNormals.length === 3 && needsSmoothNormals ) { + for ( var i = 0, l = positions.length / 3 - 1; i < l; i += step ) { - for ( i = 0; i < 3; i ++ ) { + vStart.fromArray( positions, 3 * i ); + vEnd.fromArray( positions, 3 * i + 3 ); - vn = vertexNormals[ i ]; + var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); - normalArray[ offset_normal ] = vn.x; - normalArray[ offset_normal + 1 ] = vn.y; - normalArray[ offset_normal + 2 ] = vn.z; + if ( distSq > precisionSq ) continue; - offset_normal += 3; + interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation - } + var distance = raycaster.ray.origin.distanceTo( interRay ); - } else { + if ( distance < raycaster.near || distance > raycaster.far ) continue; - for ( i = 0; i < 3; i ++ ) { + intersects.push( { - normalArray[ offset_normal ] = faceNormal.x; - normalArray[ offset_normal + 1 ] = faceNormal.y; - normalArray[ offset_normal + 2 ] = faceNormal.z; + 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 - offset_normal += 3; + } ); } } - } - - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, normalArray, hint ); + } else if ( geometry instanceof THREE.Geometry ) { - } + var vertices = geometry.vertices; + var nbVertices = vertices.length; - if ( dirtyUvs && obj_uvs && uvType ) { + for ( var i = 0; i < nbVertices - 1; i += step ) { - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + var distSq = ray.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment ); - fi = chunk_faces3[ f ]; + if ( distSq > precisionSq ) continue; - uv = obj_uvs[ fi ]; + interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation - if ( uv === undefined ) continue; + var distance = raycaster.ray.origin.distanceTo( interRay ); - for ( i = 0; i < 3; i ++ ) { + if ( distance < raycaster.near || distance > raycaster.far ) continue; - uvi = uv[ i ]; + intersects.push( { - uvArray[ offset_uv ] = uvi.x; - uvArray[ offset_uv + 1 ] = uvi.y; + 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 - offset_uv += 2; + } ); } } - if ( offset_uv > 0 ) { + }; - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, uvArray, hint ); + }() ), - } + clone: function () { - } + return new this.constructor( this.geometry, this.material ).copy( this ); - if ( dirtyUvs && obj_uvs2 && uvType ) { + } - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { +} ); - fi = chunk_faces3[ f ]; +// File:src/objects/LineSegments.js - uv2 = obj_uvs2[ fi ]; +/** + * @author mrdoob / http://mrdoob.com/ + */ - if ( uv2 === undefined ) continue; +THREE.LineSegments = function ( geometry, material ) { - for ( i = 0; i < 3; i ++ ) { + THREE.Line.call( this, geometry, material ); - uv2i = uv2[ i ]; + this.type = 'LineSegments'; - uv2Array[ offset_uv2 ] = uv2i.x; - uv2Array[ offset_uv2 + 1 ] = uv2i.y; +}; - offset_uv2 += 2; +THREE.LineSegments.prototype = Object.assign( Object.create( THREE.Line.prototype ), { - } + constructor: THREE.LineSegments - } +} ); - if ( offset_uv2 > 0 ) { +// File:src/objects/Mesh.js - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, uv2Array, hint ); +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author mikael emtinger / http://gomo.se/ + * @author jonobr1 / http://jonobr1.com/ + */ - } +THREE.Mesh = function ( geometry, material ) { - } + THREE.Object3D.call( this ); - if ( dirtyElements ) { + this.type = 'Mesh'; - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + this.geometry = geometry !== undefined ? geometry : new THREE.BufferGeometry(); + this.material = material !== undefined ? material : new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff } ); - faceArray[ offset_face ] = vertexIndex; - faceArray[ offset_face + 1 ] = vertexIndex + 1; - faceArray[ offset_face + 2 ] = vertexIndex + 2; + this.drawMode = THREE.TrianglesDrawMode; - offset_face += 3; + this.updateMorphTargets(); - lineArray[ offset_line ] = vertexIndex; - lineArray[ offset_line + 1 ] = vertexIndex + 1; +}; - lineArray[ offset_line + 2 ] = vertexIndex; - lineArray[ offset_line + 3 ] = vertexIndex + 2; +THREE.Mesh.prototype = Object.assign( Object.create( THREE.Object3D.prototype ), { - lineArray[ offset_line + 4 ] = vertexIndex + 1; - lineArray[ offset_line + 5 ] = vertexIndex + 2; + constructor: THREE.Mesh, - offset_line += 6; + setDrawMode: function ( value ) { - vertexIndex += 3; + this.drawMode = value; - } + }, - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer ); - _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, faceArray, hint ); + copy: function ( source ) { - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer ); - _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, lineArray, hint ); + THREE.Object3D.prototype.copy.call( this, source ); - } + this.drawMode = source.drawMode; - if ( customAttributes ) { + return this; - for ( i = 0, il = customAttributes.length; i < il; i ++ ) { + }, - customAttribute = customAttributes[ i ]; + updateMorphTargets: function () { - if ( ! customAttribute.__original.needsUpdate ) continue; + if ( this.geometry.morphTargets !== undefined && this.geometry.morphTargets.length > 0 ) { - offset_custom = 0; - offset_customSrc = 0; + this.morphTargetBase = - 1; + this.morphTargetInfluences = []; + this.morphTargetDictionary = {}; - if ( customAttribute.size === 1 ) { + for ( var m = 0, ml = this.geometry.morphTargets.length; m < ml; m ++ ) { - if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) { + this.morphTargetInfluences.push( 0 ); + this.morphTargetDictionary[ this.geometry.morphTargets[ m ].name ] = m; - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + } - face = obj_faces[ chunk_faces3[ f ] ]; + } - customAttribute.array[ offset_custom ] = customAttribute.value[ face.a ]; - customAttribute.array[ offset_custom + 1 ] = customAttribute.value[ face.b ]; - customAttribute.array[ offset_custom + 2 ] = customAttribute.value[ face.c ]; + }, - offset_custom += 3; + getMorphTargetIndexByName: function ( name ) { - } + if ( this.morphTargetDictionary[ name ] !== undefined ) { - } else if ( customAttribute.boundTo === "faces" ) { + return this.morphTargetDictionary[ name ]; - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + } - value = customAttribute.value[ chunk_faces3[ f ] ]; + console.warn( 'THREE.Mesh.getMorphTargetIndexByName: morph target ' + name + ' does not exist. Returning 0.' ); - customAttribute.array[ offset_custom ] = value; - customAttribute.array[ offset_custom + 1 ] = value; - customAttribute.array[ offset_custom + 2 ] = value; + return 0; - offset_custom += 3; + }, - } + raycast: ( function () { - } + var inverseMatrix = new THREE.Matrix4(); + var ray = new THREE.Ray(); + var sphere = new THREE.Sphere(); - } else if ( customAttribute.size === 2 ) { + var vA = new THREE.Vector3(); + var vB = new THREE.Vector3(); + var vC = new THREE.Vector3(); - if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) { + var tempA = new THREE.Vector3(); + var tempB = new THREE.Vector3(); + var tempC = new THREE.Vector3(); - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + var uvA = new THREE.Vector2(); + var uvB = new THREE.Vector2(); + var uvC = new THREE.Vector2(); - face = obj_faces[ chunk_faces3[ f ] ]; + var barycoord = new THREE.Vector3(); - v1 = customAttribute.value[ face.a ]; - v2 = customAttribute.value[ face.b ]; - v3 = customAttribute.value[ face.c ]; + var intersectionPoint = new THREE.Vector3(); + var intersectionPointWorld = new THREE.Vector3(); - customAttribute.array[ offset_custom ] = v1.x; - customAttribute.array[ offset_custom + 1 ] = v1.y; + function uvIntersection( point, p1, p2, p3, uv1, uv2, uv3 ) { - customAttribute.array[ offset_custom + 2 ] = v2.x; - customAttribute.array[ offset_custom + 3 ] = v2.y; + THREE.Triangle.barycoordFromPoint( point, p1, p2, p3, barycoord ); - customAttribute.array[ offset_custom + 4 ] = v3.x; - customAttribute.array[ offset_custom + 5 ] = v3.y; + uv1.multiplyScalar( barycoord.x ); + uv2.multiplyScalar( barycoord.y ); + uv3.multiplyScalar( barycoord.z ); - offset_custom += 6; + uv1.add( uv2 ).add( uv3 ); - } + return uv1.clone(); - } else if ( customAttribute.boundTo === "faces" ) { - - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { - - value = customAttribute.value[ chunk_faces3[ f ] ]; + } - v1 = value; - v2 = value; - v3 = value; + function checkIntersection( object, raycaster, ray, pA, pB, pC, point ) { - customAttribute.array[ offset_custom ] = v1.x; - customAttribute.array[ offset_custom + 1 ] = v1.y; + var intersect; + var material = object.material; - customAttribute.array[ offset_custom + 2 ] = v2.x; - customAttribute.array[ offset_custom + 3 ] = v2.y; + if ( material.side === THREE.BackSide ) { - customAttribute.array[ offset_custom + 4 ] = v3.x; - customAttribute.array[ offset_custom + 5 ] = v3.y; + intersect = ray.intersectTriangle( pC, pB, pA, true, point ); - offset_custom += 6; + } else { - } + intersect = ray.intersectTriangle( pA, pB, pC, material.side !== THREE.DoubleSide, point ); - } + } - } else if ( customAttribute.size === 3 ) { + if ( intersect === null ) return null; - var pp; + intersectionPointWorld.copy( point ); + intersectionPointWorld.applyMatrix4( object.matrixWorld ); - if ( customAttribute.type === "c" ) { + var distance = raycaster.ray.origin.distanceTo( intersectionPointWorld ); - pp = [ "r", "g", "b" ]; + if ( distance < raycaster.near || distance > raycaster.far ) return null; - } else { + return { + distance: distance, + point: intersectionPointWorld.clone(), + object: object + }; - pp = [ "x", "y", "z" ]; + } - } + function checkBufferGeometryIntersection( object, raycaster, ray, positions, uvs, a, b, c ) { - if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) { + vA.fromArray( positions, a * 3 ); + vB.fromArray( positions, b * 3 ); + vC.fromArray( positions, c * 3 ); - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + var intersection = checkIntersection( object, raycaster, ray, vA, vB, vC, intersectionPoint ); - face = obj_faces[ chunk_faces3[ f ] ]; + if ( intersection ) { - v1 = customAttribute.value[ face.a ]; - v2 = customAttribute.value[ face.b ]; - v3 = customAttribute.value[ face.c ]; + if ( uvs ) { - customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; - customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; - customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; + uvA.fromArray( uvs, a * 2 ); + uvB.fromArray( uvs, b * 2 ); + uvC.fromArray( uvs, c * 2 ); - customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; - customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; - customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; + intersection.uv = uvIntersection( intersectionPoint, vA, vB, vC, uvA, uvB, uvC ); - customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; - customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; - customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; + } - offset_custom += 9; + intersection.face = new THREE.Face3( a, b, c, THREE.Triangle.normal( vA, vB, vC ) ); + intersection.faceIndex = a; - } + } - } else if ( customAttribute.boundTo === "faces" ) { + return intersection; - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + } - value = customAttribute.value[ chunk_faces3[ f ] ]; + return function raycast( raycaster, intersects ) { - v1 = value; - v2 = value; - v3 = value; + var geometry = this.geometry; + var material = this.material; + var matrixWorld = this.matrixWorld; - customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; - customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; - customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; + if ( material === undefined ) return; - customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; - customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; - customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; + // Checking boundingSphere distance to ray - customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; - customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; - customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; + if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); - offset_custom += 9; + sphere.copy( geometry.boundingSphere ); + sphere.applyMatrix4( matrixWorld ); - } + if ( raycaster.ray.intersectsSphere( sphere ) === false ) return; - } else if ( customAttribute.boundTo === "faceVertices" ) { + // - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + inverseMatrix.getInverse( matrixWorld ); + ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); - value = customAttribute.value[ chunk_faces3[ f ] ]; + // Check boundingBox before continuing - v1 = value[ 0 ]; - v2 = value[ 1 ]; - v3 = value[ 2 ]; + if ( geometry.boundingBox !== null ) { - customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; - customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; - customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; + if ( ray.intersectsBox( geometry.boundingBox ) === false ) return; - customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; - customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; - customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; + } - customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; - customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; - customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; + var uvs, intersection; - offset_custom += 9; + if ( geometry instanceof THREE.BufferGeometry ) { - } + var a, b, c; + var index = geometry.index; + var attributes = geometry.attributes; + var positions = attributes.position.array; - } + if ( attributes.uv !== undefined ) { - } else if ( customAttribute.size === 4 ) { + uvs = attributes.uv.array; - if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) { + } - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + if ( index !== null ) { - face = obj_faces[ chunk_faces3[ f ] ]; + var indices = index.array; - v1 = customAttribute.value[ face.a ]; - v2 = customAttribute.value[ face.b ]; - v3 = customAttribute.value[ face.c ]; + for ( var i = 0, l = indices.length; i < l; i += 3 ) { - customAttribute.array[ offset_custom ] = v1.x; - customAttribute.array[ offset_custom + 1 ] = v1.y; - customAttribute.array[ offset_custom + 2 ] = v1.z; - customAttribute.array[ offset_custom + 3 ] = v1.w; + a = indices[ i ]; + b = indices[ i + 1 ]; + c = indices[ i + 2 ]; - customAttribute.array[ offset_custom + 4 ] = v2.x; - customAttribute.array[ offset_custom + 5 ] = v2.y; - customAttribute.array[ offset_custom + 6 ] = v2.z; - customAttribute.array[ offset_custom + 7 ] = v2.w; + intersection = checkBufferGeometryIntersection( this, raycaster, ray, positions, uvs, a, b, c ); - customAttribute.array[ offset_custom + 8 ] = v3.x; - customAttribute.array[ offset_custom + 9 ] = v3.y; - customAttribute.array[ offset_custom + 10 ] = v3.z; - customAttribute.array[ offset_custom + 11 ] = v3.w; + if ( intersection ) { - offset_custom += 12; + intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indices buffer semantics + intersects.push( intersection ); } - } else if ( customAttribute.boundTo === "faces" ) { + } - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + } else { - value = customAttribute.value[ chunk_faces3[ f ] ]; - v1 = value; - v2 = value; - v3 = value; + for ( var i = 0, l = positions.length; i < l; i += 9 ) { - customAttribute.array[ offset_custom ] = v1.x; - customAttribute.array[ offset_custom + 1 ] = v1.y; - customAttribute.array[ offset_custom + 2 ] = v1.z; - customAttribute.array[ offset_custom + 3 ] = v1.w; + a = i / 3; + b = a + 1; + c = a + 2; - customAttribute.array[ offset_custom + 4 ] = v2.x; - customAttribute.array[ offset_custom + 5 ] = v2.y; - customAttribute.array[ offset_custom + 6 ] = v2.z; - customAttribute.array[ offset_custom + 7 ] = v2.w; + intersection = checkBufferGeometryIntersection( this, raycaster, ray, positions, uvs, a, b, c ); - customAttribute.array[ offset_custom + 8 ] = v3.x; - customAttribute.array[ offset_custom + 9 ] = v3.y; - customAttribute.array[ offset_custom + 10 ] = v3.z; - customAttribute.array[ offset_custom + 11 ] = v3.w; + if ( intersection ) { - offset_custom += 12; + intersection.index = a; // triangle number in positions buffer semantics + intersects.push( intersection ); } - } else if ( customAttribute.boundTo === "faceVertices" ) { + } - for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + } - value = customAttribute.value[ chunk_faces3[ f ] ]; + } else if ( geometry instanceof THREE.Geometry ) { - v1 = value[ 0 ]; - v2 = value[ 1 ]; - v3 = value[ 2 ]; + var fvA, fvB, fvC; + var isFaceMaterial = material instanceof THREE.MultiMaterial; + var materials = isFaceMaterial === true ? material.materials : null; - customAttribute.array[ offset_custom ] = v1.x; - customAttribute.array[ offset_custom + 1 ] = v1.y; - customAttribute.array[ offset_custom + 2 ] = v1.z; - customAttribute.array[ offset_custom + 3 ] = v1.w; + var vertices = geometry.vertices; + var faces = geometry.faces; + var faceVertexUvs = geometry.faceVertexUvs[ 0 ]; + if ( faceVertexUvs.length > 0 ) uvs = faceVertexUvs; - customAttribute.array[ offset_custom + 4 ] = v2.x; - customAttribute.array[ offset_custom + 5 ] = v2.y; - customAttribute.array[ offset_custom + 6 ] = v2.z; - customAttribute.array[ offset_custom + 7 ] = v2.w; + for ( var f = 0, fl = faces.length; f < fl; f ++ ) { - customAttribute.array[ offset_custom + 8 ] = v3.x; - customAttribute.array[ offset_custom + 9 ] = v3.y; - customAttribute.array[ offset_custom + 10 ] = v3.z; - customAttribute.array[ offset_custom + 11 ] = v3.w; + var face = faces[ f ]; + var faceMaterial = isFaceMaterial === true ? materials[ face.materialIndex ] : material; - offset_custom += 12; + if ( faceMaterial === undefined ) continue; - } + fvA = vertices[ face.a ]; + fvB = vertices[ face.b ]; + fvC = vertices[ face.c ]; - } + if ( faceMaterial.morphTargets === true ) { - } + var morphTargets = geometry.morphTargets; + var morphInfluences = this.morphTargetInfluences; - _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); + vA.set( 0, 0, 0 ); + vB.set( 0, 0, 0 ); + vC.set( 0, 0, 0 ); - } + for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) { - } + var influence = morphInfluences[ t ]; - if ( dispose ) { + if ( influence === 0 ) continue; - delete geometryGroup.__inittedArrays; - delete geometryGroup.__colorArray; - delete geometryGroup.__normalArray; - delete geometryGroup.__tangentArray; - delete geometryGroup.__uvArray; - delete geometryGroup.__uv2Array; - delete geometryGroup.__faceArray; - delete geometryGroup.__vertexArray; - delete geometryGroup.__lineArray; - delete geometryGroup.__skinIndexArray; - delete geometryGroup.__skinWeightArray; + var targets = morphTargets[ t ].vertices; - } + vA.addScaledVector( tempA.subVectors( targets[ face.a ], fvA ), influence ); + vB.addScaledVector( tempB.subVectors( targets[ face.b ], fvB ), influence ); + vC.addScaledVector( tempC.subVectors( targets[ face.c ], fvC ), influence ); - }; + } - function setDirectBuffers ( geometry, hint, dispose ) { + vA.add( fvA ); + vB.add( fvB ); + vC.add( fvC ); - var attributes = geometry.attributes; + fvA = vA; + fvB = vB; + fvC = vC; - var attributeName, attributeItem; + } - for ( attributeName in attributes ) { + intersection = checkIntersection( this, raycaster, ray, fvA, fvB, fvC, intersectionPoint ); - attributeItem = attributes[ attributeName ]; + if ( intersection ) { - if ( attributeItem.needsUpdate ) { + if ( uvs ) { - if ( attributeName === 'index' ) { + var uvs_f = uvs[ f ]; + uvA.copy( uvs_f[ 0 ] ); + uvB.copy( uvs_f[ 1 ] ); + uvC.copy( uvs_f[ 2 ] ); - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, attributeItem.buffer ); - _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, attributeItem.array, hint ); + intersection.uv = uvIntersection( intersectionPoint, fvA, fvB, fvC, uvA, uvB, uvC ); - } else { + } - _gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, attributeItem.array, hint ); + intersection.face = face; + intersection.faceIndex = f; + intersects.push( intersection ); - } + } - attributeItem.needsUpdate = false; + } } - if ( dispose && ! attributeItem.dynamic ) { + }; - attributeItem.array = null; + }() ), - } + clone: function () { - } + return new this.constructor( this.geometry, this.material ).copy( this ); - }; + } - // Buffer rendering +} ); - this.renderBufferImmediate = function ( object, program, material ) { +// File:src/objects/Bone.js - if ( object.hasPositions && ! object.__webglVertexBuffer ) object.__webglVertexBuffer = _gl.createBuffer(); - if ( object.hasNormals && ! object.__webglNormalBuffer ) object.__webglNormalBuffer = _gl.createBuffer(); - if ( object.hasUvs && ! object.__webglUvBuffer ) object.__webglUvBuffer = _gl.createBuffer(); - if ( object.hasColors && ! object.__webglColorBuffer ) object.__webglColorBuffer = _gl.createBuffer(); +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author ikerr / http://verold.com + */ - if ( object.hasPositions ) { +THREE.Bone = function ( skin ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglVertexBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW ); - _gl.enableVertexAttribArray( program.attributes.position ); - _gl.vertexAttribPointer( program.attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + THREE.Object3D.call( this ); - } + this.type = 'Bone'; - if ( object.hasNormals ) { + this.skin = skin; - _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglNormalBuffer ); +}; - if ( material.shading === THREE.FlatShading ) { +THREE.Bone.prototype = Object.assign( Object.create( THREE.Object3D.prototype ), { - var nx, ny, nz, - nax, nbx, ncx, nay, nby, ncy, naz, nbz, ncz, - normalArray, - i, il = object.count * 3; + constructor: THREE.Bone, - for( i = 0; i < il; i += 9 ) { + copy: function ( source ) { - normalArray = object.normalArray; + THREE.Object3D.prototype.copy.call( this, source ); - nax = normalArray[ i ]; - nay = normalArray[ i + 1 ]; - naz = normalArray[ i + 2 ]; + this.skin = source.skin; - nbx = normalArray[ i + 3 ]; - nby = normalArray[ i + 4 ]; - nbz = normalArray[ i + 5 ]; + return this; - ncx = normalArray[ i + 6 ]; - ncy = normalArray[ i + 7 ]; - ncz = normalArray[ i + 8 ]; + } - nx = ( nax + nbx + ncx ) / 3; - ny = ( nay + nby + ncy ) / 3; - nz = ( naz + nbz + ncz ) / 3; +} ); - normalArray[ i ] = nx; - normalArray[ i + 1 ] = ny; - normalArray[ i + 2 ] = nz; +// File:src/objects/Skeleton.js - normalArray[ i + 3 ] = nx; - normalArray[ i + 4 ] = ny; - normalArray[ i + 5 ] = nz; +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author michael guerrero / http://realitymeltdown.com + * @author ikerr / http://verold.com + */ - normalArray[ i + 6 ] = nx; - normalArray[ i + 7 ] = ny; - normalArray[ i + 8 ] = nz; +THREE.Skeleton = function ( bones, boneInverses, useVertexTexture ) { - } + this.useVertexTexture = useVertexTexture !== undefined ? useVertexTexture : true; - } + this.identityMatrix = new THREE.Matrix4(); - _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW ); - _gl.enableVertexAttribArray( program.attributes.normal ); - _gl.vertexAttribPointer( program.attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); + // copy the bone array - } + bones = bones || []; - if ( object.hasUvs && material.map ) { + this.bones = bones.slice( 0 ); - _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglUvBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW ); - _gl.enableVertexAttribArray( program.attributes.uv ); - _gl.vertexAttribPointer( program.attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); + // create a bone texture or an array of floats - } + if ( this.useVertexTexture ) { - if ( object.hasColors && material.vertexColors !== THREE.NoColors ) { + // 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) - _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglColorBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW ); - _gl.enableVertexAttribArray( program.attributes.color ); - _gl.vertexAttribPointer( program.attributes.color, 3, _gl.FLOAT, false, 0, 0 ); - } + 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 ); - _gl.drawArrays( _gl.TRIANGLES, 0, object.count ); + this.boneTextureWidth = size; + this.boneTextureHeight = size; - object.count = 0; + 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 ); - }; + } else { - this.renderBufferDirect = function ( camera, lights, fog, material, geometry, object ) { + this.boneMatrices = new Float32Array( 16 * this.bones.length ); - if ( material.visible === false ) return; + } - var linewidth, a, attribute; - var attributeItem, attributeName, attributePointer, attributeSize; + // use the supplied bone inverses or calculate the inverses - var program = setProgram( camera, lights, fog, material, object ); + if ( boneInverses === undefined ) { - var programAttributes = program.attributes; - var geometryAttributes = geometry.attributes; + this.calculateInverses(); - var updateBuffers = false, - wireframeBit = material.wireframe ? 1 : 0, - geometryHash = ( geometry.id * 0xffffff ) + ( program.id * 2 ) + wireframeBit; + } else { - if ( geometryHash !== _currentGeometryGroupHash ) { + if ( this.bones.length === boneInverses.length ) { - _currentGeometryGroupHash = geometryHash; - updateBuffers = true; + this.boneInverses = boneInverses.slice( 0 ); - } + } else { - if ( updateBuffers ) { + console.warn( 'THREE.Skeleton bonInverses is the wrong length.' ); - disableAttributes(); + this.boneInverses = []; - } + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { - // render mesh + this.boneInverses.push( new THREE.Matrix4() ); - if ( object instanceof THREE.Mesh ) { + } - var index = geometryAttributes[ "index" ]; + } - // indexed triangles + } - if ( index ) { +}; - var offsets = geometry.offsets; +Object.assign( THREE.Skeleton.prototype, { - // if there is more than 1 chunk - // must set attribute pointers to use new offsets for each chunk - // even if geometry and materials didn't change + calculateInverses: function () { - if ( offsets.length > 1 ) updateBuffers = true; + this.boneInverses = []; - for ( var i = 0, il = offsets.length; i < il; i ++ ) { + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + + var inverse = new THREE.Matrix4(); - var startIndex = offsets[ i ].index; + if ( this.bones[ b ] ) { - if ( updateBuffers ) { + inverse.getInverse( this.bones[ b ].matrixWorld ); + + } - for ( attributeName in programAttributes ) { + this.boneInverses.push( inverse ); - attributePointer = programAttributes[ attributeName ]; - attributeItem = geometryAttributes[ attributeName ]; + } - if ( attributePointer >= 0 ) { + }, - if ( attributeItem ) { + pose: function () { - attributeSize = attributeItem.itemSize; - _gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer ); - enableAttribute( attributePointer ); - _gl.vertexAttribPointer( attributePointer, attributeSize, _gl.FLOAT, false, 0, startIndex * attributeSize * 4 ); // 4 bytes per Float32 + var bone; - } else if ( material.defaultAttributeValues ) { + // recover the bind-time world matrices - if ( material.defaultAttributeValues[ attributeName ].length === 2 ) { + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { - _gl.vertexAttrib2fv( attributePointer, material.defaultAttributeValues[ attributeName ] ); + bone = this.bones[ b ]; - } else if ( material.defaultAttributeValues[ attributeName ].length === 3 ) { + if ( bone ) { - _gl.vertexAttrib3fv( attributePointer, material.defaultAttributeValues[ attributeName ] ); + bone.matrixWorld.getInverse( this.boneInverses[ b ] ); - } + } - } + } - } + // compute the local matrices, positions, rotations and scales - } + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { - // indices + bone = this.bones[ b ]; - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); + if ( bone ) { - } + if ( bone.parent instanceof THREE.Bone ) { - // render indexed triangles + bone.matrix.getInverse( bone.parent.matrixWorld ); + bone.matrix.multiply( bone.matrixWorld ); - _gl.drawElements( _gl.TRIANGLES, offsets[ i ].count, _gl.UNSIGNED_SHORT, offsets[ i ].start * 2 ); // 2 bytes per Uint16 + } else { - _this.info.render.calls ++; - _this.info.render.vertices += offsets[ i ].count; // not really true, here vertices can be shared - _this.info.render.faces += offsets[ i ].count / 3; + bone.matrix.copy( bone.matrixWorld ); } - // non-indexed triangles + bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); - } else { + } + + } - if ( updateBuffers ) { + }, - for ( attributeName in programAttributes ) { + update: ( function () { - if ( attributeName === 'index') continue; + var offsetMatrix = new THREE.Matrix4(); - attributePointer = programAttributes[ attributeName ]; - attributeItem = geometryAttributes[ attributeName ]; - - if ( attributePointer >= 0 ) { + return function update() { - if ( attributeItem ) { + // flatten bone matrices to array - attributeSize = attributeItem.itemSize; - _gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer ); - enableAttribute( attributePointer ); - _gl.vertexAttribPointer( attributePointer, attributeSize, _gl.FLOAT, false, 0, 0 ); + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { - } else if ( material.defaultAttributeValues && material.defaultAttributeValues[ attributeName ] ) { + // compute the offset between the current and the original transform - if ( material.defaultAttributeValues[ attributeName ].length === 2 ) { + var matrix = this.bones[ b ] ? this.bones[ b ].matrixWorld : this.identityMatrix; - _gl.vertexAttrib2fv( attributePointer, material.defaultAttributeValues[ attributeName ] ); + offsetMatrix.multiplyMatrices( matrix, this.boneInverses[ b ] ); + offsetMatrix.toArray( this.boneMatrices, b * 16 ); - } else if ( material.defaultAttributeValues[ attributeName ].length === 3 ) { + } - _gl.vertexAttrib3fv( attributePointer, material.defaultAttributeValues[ attributeName ] ); + if ( this.useVertexTexture ) { - } + this.boneTexture.needsUpdate = true; - } + } - } + }; - } + } )(), - } + clone: function () { - var position = geometry.attributes[ "position" ]; + return new THREE.Skeleton( this.bones, this.boneInverses, this.useVertexTexture ); - // render non-indexed triangles + } - _gl.drawArrays( _gl.TRIANGLES, 0, position.numItems / 3 ); +} ); - _this.info.render.calls ++; - _this.info.render.vertices += position.numItems / 3; - _this.info.render.faces += position.numItems / 3 / 3; +// File:src/objects/SkinnedMesh.js - } +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author ikerr / http://verold.com + */ - // render particles +THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) { - } else if ( object instanceof THREE.ParticleSystem ) { + THREE.Mesh.call( this, geometry, material ); - if ( updateBuffers ) { + this.type = 'SkinnedMesh'; - for ( attributeName in programAttributes ) { + this.bindMode = "attached"; + this.bindMatrix = new THREE.Matrix4(); + this.bindMatrixInverse = new THREE.Matrix4(); - attributePointer = programAttributes[ attributeName ]; - attributeItem = geometryAttributes[ attributeName ]; - - if ( attributePointer >= 0 ) { + // init bones - if ( attributeItem ) { + // TODO: remove bone creation as there is no reason (other than + // convenience) for THREE.SkinnedMesh to do this. - attributeSize = attributeItem.itemSize; - _gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer ); - enableAttribute( attributePointer ); - _gl.vertexAttribPointer( attributePointer, attributeSize, _gl.FLOAT, false, 0, 0 ); + var bones = []; - } else if ( material.defaultAttributeValues && material.defaultAttributeValues[ attributeName ] ) { + if ( this.geometry && this.geometry.bones !== undefined ) { - if ( material.defaultAttributeValues[ attributeName ].length === 2 ) { + var bone, gbone; - _gl.vertexAttrib2fv( attributePointer, material.defaultAttributeValues[ attributeName ] ); + for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++ b ) { - } else if ( material.defaultAttributeValues[ attributeName ].length === 3 ) { + gbone = this.geometry.bones[ b ]; - _gl.vertexAttrib3fv( attributePointer, material.defaultAttributeValues[ attributeName ] ); + bone = new THREE.Bone( this ); + bones.push( bone ); - } + bone.name = gbone.name; + bone.position.fromArray( gbone.pos ); + bone.quaternion.fromArray( gbone.rotq ); + if ( gbone.scl !== undefined ) bone.scale.fromArray( gbone.scl ); - } + } - } + for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++ b ) { - } + gbone = this.geometry.bones[ b ]; - var position = geometryAttributes[ "position" ]; + if ( gbone.parent !== - 1 && gbone.parent !== null && + bones[ gbone.parent ] !== undefined ) { - // render particles + bones[ gbone.parent ].add( bones[ b ] ); - _gl.drawArrays( _gl.POINTS, 0, position.numItems / 3 ); + } else { - _this.info.render.calls ++; - _this.info.render.points += position.numItems / 3; + this.add( bones[ b ] ); } - } else if ( object instanceof THREE.Line ) { - - if ( updateBuffers ) { + } - for ( attributeName in programAttributes ) { + } - attributePointer = programAttributes[ attributeName ]; - attributeItem = geometryAttributes[ attributeName ]; - - if ( attributePointer >= 0 ) { + this.normalizeSkinWeights(); - if ( attributeItem ) { + this.updateMatrixWorld( true ); + this.bind( new THREE.Skeleton( bones, undefined, useVertexTexture ), this.matrixWorld ); - attributeSize = attributeItem.itemSize; - _gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer ); - enableAttribute( attributePointer ); - _gl.vertexAttribPointer( attributePointer, attributeSize, _gl.FLOAT, false, 0, 0 ); +}; - } else if ( material.defaultAttributeValues && material.defaultAttributeValues[ attributeName ] ) { - if ( material.defaultAttributeValues[ attributeName ].length === 2 ) { +THREE.SkinnedMesh.prototype = Object.assign( Object.create( THREE.Mesh.prototype ), { - _gl.vertexAttrib2fv( attributePointer, material.defaultAttributeValues[ attributeName ] ); + constructor: THREE.SkinnedMesh, - } else if ( material.defaultAttributeValues[ attributeName ].length === 3 ) { + bind: function( skeleton, bindMatrix ) { - _gl.vertexAttrib3fv( attributePointer, material.defaultAttributeValues[ attributeName ] ); + this.skeleton = skeleton; - } + if ( bindMatrix === undefined ) { - } + this.updateMatrixWorld( true ); - } + this.skeleton.calculateInverses(); - } + bindMatrix = this.matrixWorld; - // render lines + } - var primitives = ( object.type === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES; + this.bindMatrix.copy( bindMatrix ); + this.bindMatrixInverse.getInverse( bindMatrix ); - setLineWidth( material.linewidth ); + }, - var position = geometryAttributes[ "position" ]; + pose: function () { - _gl.drawArrays( primitives, 0, position.numItems / 3 ); + this.skeleton.pose(); - _this.info.render.calls ++; - _this.info.render.points += position.numItems; + }, - } + normalizeSkinWeights: function () { - } + if ( this.geometry instanceof THREE.Geometry ) { - }; + for ( var i = 0; i < this.geometry.skinWeights.length; i ++ ) { - this.renderBuffer = function ( camera, lights, fog, material, geometryGroup, object ) { + var sw = this.geometry.skinWeights[ i ]; - if ( material.visible === false ) return; + var scale = 1.0 / sw.lengthManhattan(); - var linewidth, a, attribute, i, il; + if ( scale !== Infinity ) { - var program = setProgram( camera, lights, fog, material, object ); + sw.multiplyScalar( scale ); - var attributes = program.attributes; + } else { - var updateBuffers = false, - wireframeBit = material.wireframe ? 1 : 0, - geometryGroupHash = ( geometryGroup.id * 0xffffff ) + ( program.id * 2 ) + wireframeBit; + sw.set( 1, 0, 0, 0 ); // do something reasonable - if ( geometryGroupHash !== _currentGeometryGroupHash ) { + } - _currentGeometryGroupHash = geometryGroupHash; - updateBuffers = true; + } - } + } else if ( this.geometry instanceof THREE.BufferGeometry ) { - if ( updateBuffers ) { + var vec = new THREE.Vector4(); - disableAttributes(); + var skinWeight = this.geometry.attributes.skinWeight; - } + for ( var i = 0; i < skinWeight.count; i ++ ) { - // vertices + vec.x = skinWeight.getX( i ); + vec.y = skinWeight.getY( i ); + vec.z = skinWeight.getZ( i ); + vec.w = skinWeight.getW( i ); - if ( !material.morphTargets && attributes.position >= 0 ) { + var scale = 1.0 / vec.lengthManhattan(); - if ( updateBuffers ) { + if ( scale !== Infinity ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); - enableAttribute( attributes.position ); - _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + vec.multiplyScalar( scale ); - } + } else { - } else { + vec.set( 1, 0, 0, 0 ); // do something reasonable - if ( object.morphTargetBase ) { + } - setupMorphTargets( material, geometryGroup, object ); + skinWeight.setXYZW( i, vec.x, vec.y, vec.z, vec.w ); } } + }, - if ( updateBuffers ) { - - // custom attributes - - // Use the per-geometryGroup custom attribute arrays which are setup in initMeshBuffers - - if ( geometryGroup.__webglCustomAttributesList ) { + updateMatrixWorld: function( force ) { - for ( i = 0, il = geometryGroup.__webglCustomAttributesList.length; i < il; i ++ ) { + THREE.Mesh.prototype.updateMatrixWorld.call( this, true ); - attribute = geometryGroup.__webglCustomAttributesList[ i ]; + if ( this.bindMode === "attached" ) { - if ( attributes[ attribute.buffer.belongsToAttribute ] >= 0 ) { + this.bindMatrixInverse.getInverse( this.matrixWorld ); - _gl.bindBuffer( _gl.ARRAY_BUFFER, attribute.buffer ); - enableAttribute( attributes[ attribute.buffer.belongsToAttribute ] ); - _gl.vertexAttribPointer( attributes[ attribute.buffer.belongsToAttribute ], attribute.size, _gl.FLOAT, false, 0, 0 ); + } else if ( this.bindMode === "detached" ) { - } + this.bindMatrixInverse.getInverse( this.bindMatrix ); - } + } else { - } + console.warn( 'THREE.SkinnedMesh unrecognized bindMode: ' + this.bindMode ); + } - // colors + }, - if ( attributes.color >= 0 ) { + clone: function() { - if ( object.geometry.colors.length > 0 || object.geometry.faces.length > 0 ) { + return new this.constructor( this.geometry, this.material, this.skeleton.useVertexTexture ).copy( this ); - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer ); - enableAttribute( attributes.color ); - _gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 ); + } - } else if ( material.defaultAttributeValues ) { +} ); +// File:src/objects/LOD.js - _gl.vertexAttrib3fv( attributes.color, material.defaultAttributeValues.color ); +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ - } +THREE.LOD = function () { - } + THREE.Object3D.call( this ); - // normals + this.type = 'LOD'; - if ( attributes.normal >= 0 ) { + Object.defineProperties( this, { + levels: { + enumerable: true, + value: [] + } + } ); - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer ); - enableAttribute( attributes.normal ); - _gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); +}; - } - // tangents +THREE.LOD.prototype = Object.assign( Object.create( THREE.Object3D.prototype ), { - if ( attributes.tangent >= 0 ) { + constructor: THREE.LOD, - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer ); - enableAttribute( attributes.tangent ); - _gl.vertexAttribPointer( attributes.tangent, 4, _gl.FLOAT, false, 0, 0 ); + copy: function ( source ) { - } + THREE.Object3D.prototype.copy.call( this, source, false ); - // uvs + var levels = source.levels; - if ( attributes.uv >= 0 ) { + for ( var i = 0, l = levels.length; i < l; i ++ ) { - if ( object.geometry.faceVertexUvs[0] ) { + var level = levels[ i ]; - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer ); - enableAttribute( attributes.uv ); - _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); + this.addLevel( level.object.clone(), level.distance ); - } else if ( material.defaultAttributeValues ) { + } + return this; - _gl.vertexAttrib2fv( attributes.uv, material.defaultAttributeValues.uv ); + }, - } + addLevel: function ( object, distance ) { - } + if ( distance === undefined ) distance = 0; - if ( attributes.uv2 >= 0 ) { + distance = Math.abs( distance ); - if ( object.geometry.faceVertexUvs[1] ) { + var levels = this.levels; - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer ); - enableAttribute( attributes.uv2 ); - _gl.vertexAttribPointer( attributes.uv2, 2, _gl.FLOAT, false, 0, 0 ); + for ( var l = 0; l < levels.length; l ++ ) { - } else if ( material.defaultAttributeValues ) { + if ( distance < levels[ l ].distance ) { + break; - _gl.vertexAttrib2fv( attributes.uv2, material.defaultAttributeValues.uv2 ); + } - } + } - } + levels.splice( l, 0, { distance: distance, object: object } ); - if ( material.skinning && - attributes.skinIndex >= 0 && attributes.skinWeight >= 0 ) { + this.add( object ); - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer ); - enableAttribute( attributes.skinIndex ); - _gl.vertexAttribPointer( attributes.skinIndex, 4, _gl.FLOAT, false, 0, 0 ); + }, - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer ); - enableAttribute( attributes.skinWeight ); - _gl.vertexAttribPointer( attributes.skinWeight, 4, _gl.FLOAT, false, 0, 0 ); + getObjectForDistance: function ( distance ) { - } + var levels = this.levels; - // line distances + for ( var i = 1, l = levels.length; i < l; i ++ ) { - if ( attributes.lineDistance >= 0 ) { + if ( distance < levels[ i ].distance ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglLineDistanceBuffer ); - enableAttribute( attributes.lineDistance ); - _gl.vertexAttribPointer( attributes.lineDistance, 1, _gl.FLOAT, false, 0, 0 ); + break; } } - // render mesh + return levels[ i - 1 ].object; - if ( object instanceof THREE.Mesh ) { + }, + + raycast: ( function () { - // wireframe + var matrixPosition = new THREE.Vector3(); - if ( material.wireframe ) { + return function raycast( raycaster, intersects ) { - setLineWidth( material.wireframeLinewidth ); + matrixPosition.setFromMatrixPosition( this.matrixWorld ); - if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer ); - _gl.drawElements( _gl.LINES, geometryGroup.__webglLineCount, _gl.UNSIGNED_SHORT, 0 ); + var distance = raycaster.ray.origin.distanceTo( matrixPosition ); - // triangles + this.getObjectForDistance( distance ).raycast( raycaster, intersects ); - } else { + }; - if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer ); - _gl.drawElements( _gl.TRIANGLES, geometryGroup.__webglFaceCount, _gl.UNSIGNED_SHORT, 0 ); + }() ), - } + update: function () { - _this.info.render.calls ++; - _this.info.render.vertices += geometryGroup.__webglFaceCount; - _this.info.render.faces += geometryGroup.__webglFaceCount / 3; + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); - // render lines + return function update( camera ) { - } else if ( object instanceof THREE.Line ) { + var levels = this.levels; - var primitives = ( object.type === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES; + if ( levels.length > 1 ) { - setLineWidth( material.linewidth ); + v1.setFromMatrixPosition( camera.matrixWorld ); + v2.setFromMatrixPosition( this.matrixWorld ); - _gl.drawArrays( primitives, 0, geometryGroup.__webglLineCount ); + var distance = v1.distanceTo( v2 ); - _this.info.render.calls ++; + levels[ 0 ].object.visible = true; - // render particles + for ( var i = 1, l = levels.length; i < l; i ++ ) { - } else if ( object instanceof THREE.ParticleSystem ) { + if ( distance >= levels[ i ].distance ) { - _gl.drawArrays( _gl.POINTS, 0, geometryGroup.__webglParticleCount ); + levels[ i - 1 ].object.visible = false; + levels[ i ].object.visible = true; - _this.info.render.calls ++; - _this.info.render.points += geometryGroup.__webglParticleCount; + } else { - // render ribbon + break; - } else if ( object instanceof THREE.Ribbon ) { + } - _gl.drawArrays( _gl.TRIANGLE_STRIP, 0, geometryGroup.__webglVertexCount ); + } - _this.info.render.calls ++; + for ( ; i < l; i ++ ) { - } + levels[ i ].object.visible = false; - }; + } - function enableAttribute( attribute ) { + } - if ( ! _enabledAttributes[ attribute ] ) { + }; - _gl.enableVertexAttribArray( attribute ); - _enabledAttributes[ attribute ] = true; + }(), - } + toJSON: function ( meta ) { - }; + var data = THREE.Object3D.prototype.toJSON.call( this, meta ); - function disableAttributes() { + data.object.levels = []; - for ( var attribute in _enabledAttributes ) { + var levels = this.levels; - if ( _enabledAttributes[ attribute ] ) { + for ( var i = 0, l = levels.length; i < l; i ++ ) { - _gl.disableVertexAttribArray( attribute ); - _enabledAttributes[ attribute ] = false; + var level = levels[ i ]; - } + data.object.levels.push( { + object: level.object.uuid, + distance: level.distance + } ); } - }; - - function setupMorphTargets ( material, geometryGroup, object ) { + return data; - // set base + } - var attributes = material.program.attributes; +} ); - if ( object.morphTargetBase !== -1 && attributes.position >= 0 ) { +// File:src/objects/Sprite.js - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ object.morphTargetBase ] ); - enableAttribute( attributes.position ); - _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ - } else if ( attributes.position >= 0 ) { +THREE.Sprite = function ( material ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); - enableAttribute( attributes.position ); - _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + THREE.Object3D.call( this ); - } + this.type = 'Sprite'; - if ( object.morphTargetForcedOrder.length ) { + this.material = ( material !== undefined ) ? material : new THREE.SpriteMaterial(); - // set forced order +}; - var m = 0; - var order = object.morphTargetForcedOrder; - var influences = object.morphTargetInfluences; +THREE.Sprite.prototype = Object.assign( Object.create( THREE.Object3D.prototype ), { - while ( m < material.numSupportedMorphTargets && m < order.length ) { + constructor: THREE.Sprite, - if ( attributes[ "morphTarget" + m ] >= 0 ) { + raycast: ( function () { - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ order[ m ] ] ); - enableAttribute( attributes[ "morphTarget" + m ] ); - _gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 ); + var matrixPosition = new THREE.Vector3(); - } + return function raycast( raycaster, intersects ) { - if ( attributes[ "morphNormal" + m ] >= 0 && material.morphNormals ) { + matrixPosition.setFromMatrixPosition( this.matrixWorld ); - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ order[ m ] ] ); - enableAttribute( attributes[ "morphNormal" + m ] ); - _gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 ); + var distanceSq = raycaster.ray.distanceSqToPoint( matrixPosition ); + var guessSizeSq = this.scale.x * this.scale.y / 4; - } + if ( distanceSq > guessSizeSq ) { - object.__webglMorphTargetInfluences[ m ] = influences[ order[ m ] ]; + return; - m ++; } - } else { + intersects.push( { - // find the most influencing + distance: Math.sqrt( distanceSq ), + point: this.position, + face: null, + object: this - var influence, activeInfluenceIndices = []; - var influences = object.morphTargetInfluences; - var i, il = influences.length; + } ); - for ( i = 0; i < il; i ++ ) { + }; - influence = influences[ i ]; + }() ), - if ( influence > 0 ) { + clone: function () { - activeInfluenceIndices.push( [ influence, i ] ); + return new this.constructor( this.material ).copy( this ); - } + } - } +} ); - if ( activeInfluenceIndices.length > material.numSupportedMorphTargets ) { +// File:src/objects/LensFlare.js - activeInfluenceIndices.sort( numericalSort ); - activeInfluenceIndices.length = material.numSupportedMorphTargets; +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ - } else if ( activeInfluenceIndices.length > material.numSupportedMorphNormals ) { +THREE.LensFlare = function ( texture, size, distance, blending, color ) { - activeInfluenceIndices.sort( numericalSort ); + THREE.Object3D.call( this ); - } else if ( activeInfluenceIndices.length === 0 ) { + this.lensFlares = []; - activeInfluenceIndices.push( [ 0, 0 ] ); + this.positionScreen = new THREE.Vector3(); + this.customUpdateCallback = undefined; - }; + if ( texture !== undefined ) { - var influenceIndex, m = 0; + this.add( texture, size, distance, blending, color ); - while ( m < material.numSupportedMorphTargets ) { + } - if ( activeInfluenceIndices[ m ] ) { +}; - influenceIndex = activeInfluenceIndices[ m ][ 1 ]; +THREE.LensFlare.prototype = Object.assign( Object.create( THREE.Object3D.prototype ), { - if ( attributes[ "morphTarget" + m ] >= 0 ) { + constructor: THREE.LensFlare, - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ influenceIndex ] ); - enableAttribute( attributes[ "morphTarget" + m ] ); - _gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 ); + copy: function ( source ) { - } + THREE.Object3D.prototype.copy.call( this, source ); - if ( attributes[ "morphNormal" + m ] >= 0 && material.morphNormals ) { + this.positionScreen.copy( source.positionScreen ); + this.customUpdateCallback = source.customUpdateCallback; - _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ influenceIndex ] ); - enableAttribute( attributes[ "morphNormal" + m ] ); - _gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 ); + for ( var i = 0, l = source.lensFlares.length; i < l; i ++ ) { + this.lensFlares.push( source.lensFlares[ i ] ); - } + } - object.__webglMorphTargetInfluences[ m ] = influences[ influenceIndex ]; + return this; - } else { + }, - /* - _gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 ); + add: function ( texture, size, distance, blending, color, opacity ) { - if ( material.morphNormals ) { + 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; - _gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 ); + distance = Math.min( distance, Math.max( 0, distance ) ); - } - */ + this.lensFlares.push( { + texture: texture, // THREE.Texture + size: size, // size in pixels (-1 = use texture.width) + distance: distance, // distance (0-1) from light source (0=at light source) + x: 0, y: 0, z: 0, // screen position (-1 => 1) z = 0 is in front z = 1 is back + scale: 1, // scale + rotation: 0, // rotation + opacity: opacity, // opacity + color: color, // color + blending: blending // blending + } ); - object.__webglMorphTargetInfluences[ m ] = 0; + }, - } + /* + * 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. + */ - m ++; + updateLensFlares: function () { - } + var f, fl = this.lensFlares.length; + var flare; + var vecX = - this.positionScreen.x * 2; + var vecY = - this.positionScreen.y * 2; - } + for ( f = 0; f < fl; f ++ ) { - // load updated influences uniform + flare = this.lensFlares[ f ]; - if ( material.program.uniforms.morphTargetInfluences !== null ) { + flare.x = this.positionScreen.x + vecX * flare.distance; + flare.y = this.positionScreen.y + vecY * flare.distance; - _gl.uniform1fv( material.program.uniforms.morphTargetInfluences, object.__webglMorphTargetInfluences ); + flare.wantedRotation = flare.x * Math.PI * 0.25; + flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25; } - }; - - // Sorting - - function painterSortStable ( a, b ) { - - if ( a.z !== b.z ) { - - return b.z - a.z; - - } else { + } - return a.id - b.id; +} ); - } +// File:src/scenes/Scene.js - }; +/** + * @author mrdoob / http://mrdoob.com/ + */ - function numericalSort ( a, b ) { +THREE.Scene = function () { - return b[ 0 ] - a[ 0 ]; + THREE.Object3D.call( this ); - }; + this.type = 'Scene'; + this.background = null; + this.fog = null; + this.overrideMaterial = null; - // Rendering + this.autoUpdate = true; // checked by the renderer - this.render = function ( scene, camera, renderTarget, forceClear ) { +}; - if ( camera instanceof THREE.Camera === false ) { +THREE.Scene.prototype = Object.create( THREE.Object3D.prototype ); +THREE.Scene.prototype.constructor = THREE.Scene; - console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); - return; +THREE.Scene.prototype.copy = function ( source, recursive ) { - } + THREE.Object3D.prototype.copy.call( this, source, recursive ); - var i, il, + if ( source.background !== null ) this.background = source.background.clone(); + if ( source.fog !== null ) this.fog = source.fog.clone(); + if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone(); - webglObject, object, - renderList, + this.autoUpdate = source.autoUpdate; + this.matrixAutoUpdate = source.matrixAutoUpdate; - lights = scene.__lights, - fog = scene.fog; + return this; - // reset caching for this frame +}; - _currentMaterialId = -1; - _lightsNeedUpdate = true; +// File:src/scenes/Fog.js - // update scene graph +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ - if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); +THREE.Fog = function ( color, near, far ) { - // update camera matrices and frustum + this.name = ''; - if ( camera.parent === undefined ) camera.updateMatrixWorld(); + this.color = new THREE.Color( color ); - camera.matrixWorldInverse.getInverse( camera.matrixWorld ); + this.near = ( near !== undefined ) ? near : 1; + this.far = ( far !== undefined ) ? far : 1000; - _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); - _frustum.setFromMatrix( _projScreenMatrix ); +}; - // update WebGL objects +THREE.Fog.prototype.clone = function () { - if ( this.autoUpdateObjects ) this.initWebGLObjects( scene ); + return new THREE.Fog( this.color.getHex(), this.near, this.far ); - // custom render plugins (pre pass) +}; - renderPlugins( this.renderPluginsPre, scene, camera ); +// File:src/scenes/FogExp2.js - // +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ - _this.info.render.calls = 0; - _this.info.render.vertices = 0; - _this.info.render.faces = 0; - _this.info.render.points = 0; +THREE.FogExp2 = function ( color, density ) { - this.setRenderTarget( renderTarget ); + this.name = ''; - if ( this.autoClear || forceClear ) { + this.color = new THREE.Color( color ); + this.density = ( density !== undefined ) ? density : 0.00025; - this.clear( this.autoClearColor, this.autoClearDepth, this.autoClearStencil ); +}; - } +THREE.FogExp2.prototype.clone = function () { - // set matrices for regular objects (frustum culled) + return new THREE.FogExp2( this.color.getHex(), this.density ); - renderList = scene.__webglObjects; +}; - for ( i = 0, il = renderList.length; i < il; i ++ ) { +// File:src/renderers/shaders/ShaderChunk.js - webglObject = renderList[ i ]; - object = webglObject.object; +THREE.ShaderChunk = {}; - webglObject.id = i; - webglObject.render = false; +// File:src/renderers/shaders/ShaderChunk/alphamap_fragment.glsl - if ( object.visible ) { +THREE.ShaderChunk[ 'alphamap_fragment' ] = "#ifdef USE_ALPHAMAP\n diffuseColor.a *= texture2D( alphaMap, vUv ).g;\n#endif\n"; - if ( ! ( object instanceof THREE.Mesh || object instanceof THREE.ParticleSystem ) || ! ( object.frustumCulled ) || _frustum.intersectsObject( object ) ) { +// File:src/renderers/shaders/ShaderChunk/alphamap_pars_fragment.glsl - setupMatrices( object, camera ); +THREE.ShaderChunk[ 'alphamap_pars_fragment' ] = "#ifdef USE_ALPHAMAP\n uniform sampler2D alphaMap;\n#endif\n"; - unrollBufferMaterial( webglObject ); +// File:src/renderers/shaders/ShaderChunk/alphatest_fragment.glsl - webglObject.render = true; +THREE.ShaderChunk[ 'alphatest_fragment' ] = "#ifdef ALPHATEST\n if ( diffuseColor.a < ALPHATEST ) discard;\n#endif\n"; - if ( this.sortObjects === true ) { +// File:src/renderers/shaders/ShaderChunk/aomap_fragment.glsl - if ( object.renderDepth !== null ) { +THREE.ShaderChunk[ 'aomap_fragment' ] = "#ifdef USE_AOMAP\n float ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n reflectedLight.indirectDiffuse *= ambientOcclusion;\n #if defined( USE_ENVMAP ) && defined( PHYSICAL )\n float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n reflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.specularRoughness );\n #endif\n#endif\n"; - webglObject.z = object.renderDepth; +// File:src/renderers/shaders/ShaderChunk/aomap_pars_fragment.glsl - } else { +THREE.ShaderChunk[ 'aomap_pars_fragment' ] = "#ifdef USE_AOMAP\n uniform sampler2D aoMap;\n uniform float aoMapIntensity;\n#endif"; - _vector3.getPositionFromMatrix( object.matrixWorld ); - _vector3.applyProjection( _projScreenMatrix ); +// File:src/renderers/shaders/ShaderChunk/begin_vertex.glsl - webglObject.z = _vector3.z; +THREE.ShaderChunk[ 'begin_vertex' ] = "\nvec3 transformed = vec3( position );\n"; - } +// File:src/renderers/shaders/ShaderChunk/beginnormal_vertex.glsl - } +THREE.ShaderChunk[ 'beginnormal_vertex' ] = "\nvec3 objectNormal = vec3( normal );\n"; - } +// File:src/renderers/shaders/ShaderChunk/bsdfs.glsl - } +THREE.ShaderChunk[ 'bsdfs' ] = "bool testLightInRange( const in float lightDistance, const in float cutoffDistance ) {\n return any( bvec2( cutoffDistance == 0.0, lightDistance < cutoffDistance ) );\n}\nfloat punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n if( decayExponent > 0.0 ) {\n#if defined ( PHYSICALLY_CORRECT_LIGHTS )\n float distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\n float maxDistanceCutoffFactor = pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\n return distanceFalloff * maxDistanceCutoffFactor;\n#else\n return pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n#endif\n }\n return 1.0;\n}\nvec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {\n return RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) {\n float fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );\n return ( 1.0 - specularColor ) * fresnel + specularColor;\n}\nfloat G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {\n float a2 = pow2( alpha );\n float gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n float gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n return 1.0 / ( gl * gv );\n}\nfloat G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\n float a2 = pow2( alpha );\n float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\n float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\n return 0.5 / max( gv + gl, EPSILON );\n}\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n float a2 = pow2( alpha );\n float denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\n return RECIPROCAL_PI * a2 / pow2( denom );\n}\nvec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\n float alpha = pow2( roughness );\n vec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n float dotNL = saturate( dot( geometry.normal, incidentLight.direction ) );\n float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n float dotNH = saturate( dot( geometry.normal, halfDir ) );\n float dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n vec3 F = F_Schlick( specularColor, dotLH );\n float G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );\n float D = D_GGX( alpha, dotNH );\n return F * ( G * D );\n}\nvec3 BRDF_Specular_GGX_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\n float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n const vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\n const vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\n vec4 r = roughness * c0 + c1;\n float a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\n vec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw;\n return specularColor * AB.x + AB.y;\n}\nfloat G_BlinnPhong_Implicit( ) {\n return 0.25;\n}\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\n return RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\n}\nvec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) {\n vec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n float dotNH = saturate( dot( geometry.normal, halfDir ) );\n float dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n vec3 F = F_Schlick( specularColor, dotLH );\n float G = G_BlinnPhong_Implicit( );\n float D = D_BlinnPhong( shininess, dotNH );\n return F * ( G * D );\n}\nfloat GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {\n return ( 2.0 / pow2( ggxRoughness + 0.0001 ) - 2.0 );\n}\nfloat BlinnExponentToGGXRoughness( const in float blinnExponent ) {\n return sqrt( 2.0 / ( blinnExponent + 2.0 ) );\n}\n"; - } +// File:src/renderers/shaders/ShaderChunk/bumpmap_pars_fragment.glsl - if ( this.sortObjects ) { +THREE.ShaderChunk[ 'bumpmap_pars_fragment' ] = "#ifdef USE_BUMPMAP\n uniform sampler2D bumpMap;\n uniform float bumpScale;\n vec2 dHdxy_fwd() {\n vec2 dSTdx = dFdx( vUv );\n vec2 dSTdy = dFdy( vUv );\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 return vec2( dBx, dBy );\n }\n vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n vec3 vSigmaX = dFdx( surf_pos );\n vec3 vSigmaY = dFdy( surf_pos );\n vec3 vN = surf_norm;\n vec3 R1 = cross( vSigmaY, vN );\n vec3 R2 = cross( vN, vSigmaX );\n float fDet = dot( vSigmaX, R1 );\n vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n return normalize( abs( fDet ) * surf_norm - vGrad );\n }\n#endif\n"; - renderList.sort( painterSortStable ); +// File:src/renderers/shaders/ShaderChunk/clipping_planes_fragment.glsl - } +THREE.ShaderChunk[ 'clipping_planes_fragment' ] = "#if NUM_CLIPPING_PLANES > 0\n for ( int i = 0; i < NUM_CLIPPING_PLANES; ++ i ) {\n vec4 plane = clippingPlanes[ i ];\n if ( dot( vViewPosition, plane.xyz ) > plane.w ) discard;\n }\n#endif\n"; - // set matrices for immediate objects +// File:src/renderers/shaders/ShaderChunk/clipping_planes_pars_fragment.glsl - renderList = scene.__webglObjectsImmediate; +THREE.ShaderChunk[ 'clipping_planes_pars_fragment' ] = "#if NUM_CLIPPING_PLANES > 0\n #if ! defined( PHYSICAL ) && ! defined( PHONG )\n varying vec3 vViewPosition;\n #endif\n uniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];\n#endif\n"; - for ( i = 0, il = renderList.length; i < il; i ++ ) { +// File:src/renderers/shaders/ShaderChunk/clipping_planes_pars_vertex.glsl - webglObject = renderList[ i ]; - object = webglObject.object; +THREE.ShaderChunk[ 'clipping_planes_pars_vertex' ] = "#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )\n varying vec3 vViewPosition;\n#endif\n"; - if ( object.visible ) { +// File:src/renderers/shaders/ShaderChunk/clipping_planes_vertex.glsl - setupMatrices( object, camera ); +THREE.ShaderChunk[ 'clipping_planes_vertex' ] = "#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )\n vViewPosition = - mvPosition.xyz;\n#endif\n"; - unrollImmediateBufferMaterial( webglObject ); +// File:src/renderers/shaders/ShaderChunk/color_fragment.glsl - } +THREE.ShaderChunk[ 'color_fragment' ] = "#ifdef USE_COLOR\n diffuseColor.rgb *= vColor;\n#endif"; - } +// File:src/renderers/shaders/ShaderChunk/color_pars_fragment.glsl - if ( scene.overrideMaterial ) { +THREE.ShaderChunk[ 'color_pars_fragment' ] = "#ifdef USE_COLOR\n varying vec3 vColor;\n#endif\n"; - var material = scene.overrideMaterial; +// File:src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl - this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); - this.setDepthTest( material.depthTest ); - this.setDepthWrite( material.depthWrite ); - setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); +THREE.ShaderChunk[ 'color_pars_vertex' ] = "#ifdef USE_COLOR\n varying vec3 vColor;\n#endif"; - renderObjects( scene.__webglObjects, false, "", camera, lights, fog, true, material ); - renderObjectsImmediate( scene.__webglObjectsImmediate, "", camera, lights, fog, false, material ); +// File:src/renderers/shaders/ShaderChunk/color_vertex.glsl - } else { +THREE.ShaderChunk[ 'color_vertex' ] = "#ifdef USE_COLOR\n vColor.xyz = color.xyz;\n#endif"; - var material = null; +// File:src/renderers/shaders/ShaderChunk/common.glsl - // opaque pass (front-to-back order) +THREE.ShaderChunk[ 'common' ] = "#define PI 3.14159265359\n#define PI2 6.28318530718\n#define RECIPROCAL_PI 0.31830988618\n#define RECIPROCAL_PI2 0.15915494\n#define LOG2 1.442695\n#define EPSILON 1e-6\n#define saturate(a) clamp( a, 0.0, 1.0 )\n#define whiteCompliment(a) ( 1.0 - saturate( a ) )\nfloat pow2( const in float x ) { return x*x; }\nfloat pow3( const in float x ) { return x*x*x; }\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\nfloat average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }\nhighp float rand( const in vec2 uv ) {\n const highp float a = 12.9898, b = 78.233, c = 43758.5453;\n highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\n return fract(sin(sn) * c);\n}\nstruct IncidentLight {\n vec3 color;\n vec3 direction;\n bool visible;\n};\nstruct ReflectedLight {\n vec3 directDiffuse;\n vec3 directSpecular;\n vec3 indirectDiffuse;\n vec3 indirectSpecular;\n};\nstruct GeometricContext {\n vec3 position;\n vec3 normal;\n vec3 viewDir;\n};\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n}\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n return normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n}\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n float distance = dot( planeNormal, point - pointOnPlane );\n return - distance * planeNormal + point;\n}\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n return sign( dot( point - pointOnPlane, planeNormal ) );\n}\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n return lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n}\n"; - this.setBlending( THREE.NoBlending ); +// File:src/renderers/shaders/ShaderChunk/cube_uv_reflection_fragment.glsl - renderObjects( scene.__webglObjects, true, "opaque", camera, lights, fog, false, material ); - renderObjectsImmediate( scene.__webglObjectsImmediate, "opaque", camera, lights, fog, false, material ); +THREE.ShaderChunk[ 'cube_uv_reflection_fragment' ] = "#ifdef ENVMAP_TYPE_CUBE_UV\n#define cubeUV_textureSize (1024.0)\nint getFaceFromDirection(vec3 direction) {\n vec3 absDirection = abs(direction);\n int face = -1;\n if( absDirection.x > absDirection.z ) {\n if(absDirection.x > absDirection.y )\n face = direction.x > 0.0 ? 0 : 3;\n else\n face = direction.y > 0.0 ? 1 : 4;\n }\n else {\n if(absDirection.z > absDirection.y )\n face = direction.z > 0.0 ? 2 : 5;\n else\n face = direction.y > 0.0 ? 1 : 4;\n }\n return face;\n}\n#define cubeUV_maxLods1 (log2(cubeUV_textureSize*0.25) - 1.0)\n#define cubeUV_rangeClamp (exp2((6.0 - 1.0) * 2.0))\nvec2 MipLevelInfo( vec3 vec, float roughnessLevel, float roughness ) {\n float scale = exp2(cubeUV_maxLods1 - roughnessLevel);\n float dxRoughness = dFdx(roughness);\n float dyRoughness = dFdy(roughness);\n vec3 dx = dFdx( vec * scale * dxRoughness );\n vec3 dy = dFdy( vec * scale * dyRoughness );\n float d = max( dot( dx, dx ), dot( dy, dy ) );\n d = clamp(d, 1.0, cubeUV_rangeClamp);\n float mipLevel = 0.5 * log2(d);\n return vec2(floor(mipLevel), fract(mipLevel));\n}\n#define cubeUV_maxLods2 (log2(cubeUV_textureSize*0.25) - 2.0)\n#define cubeUV_rcpTextureSize (1.0 / cubeUV_textureSize)\nvec2 getCubeUV(vec3 direction, float roughnessLevel, float mipLevel) {\n mipLevel = roughnessLevel > cubeUV_maxLods2 - 3.0 ? 0.0 : mipLevel;\n float a = 16.0 * cubeUV_rcpTextureSize;\n vec2 exp2_packed = exp2( vec2( roughnessLevel, mipLevel ) );\n vec2 rcp_exp2_packed = vec2( 1.0 ) / exp2_packed;\n float powScale = exp2_packed.x * exp2_packed.y;\n float scale = rcp_exp2_packed.x * rcp_exp2_packed.y * 0.25;\n float mipOffset = 0.75*(1.0 - rcp_exp2_packed.y) * rcp_exp2_packed.x;\n bool bRes = mipLevel == 0.0;\n scale = bRes && (scale < a) ? a : scale;\n vec3 r;\n vec2 offset;\n int face = getFaceFromDirection(direction);\n float rcpPowScale = 1.0 / powScale;\n if( face == 0) {\n r = vec3(direction.x, -direction.z, direction.y);\n offset = vec2(0.0+mipOffset,0.75 * rcpPowScale);\n offset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n }\n else if( face == 1) {\n r = vec3(direction.y, direction.x, direction.z);\n offset = vec2(scale+mipOffset, 0.75 * rcpPowScale);\n offset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n }\n else if( face == 2) {\n r = vec3(direction.z, direction.x, direction.y);\n offset = vec2(2.0*scale+mipOffset, 0.75 * rcpPowScale);\n offset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\n }\n else if( face == 3) {\n r = vec3(direction.x, direction.z, direction.y);\n offset = vec2(0.0+mipOffset,0.5 * rcpPowScale);\n offset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n }\n else if( face == 4) {\n r = vec3(direction.y, direction.x, -direction.z);\n offset = vec2(scale+mipOffset, 0.5 * rcpPowScale);\n offset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n }\n else {\n r = vec3(direction.z, -direction.x, direction.y);\n offset = vec2(2.0*scale+mipOffset, 0.5 * rcpPowScale);\n offset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\n }\n r = normalize(r);\n float texelOffset = 0.5 * cubeUV_rcpTextureSize;\n vec2 s = ( r.yz / abs( r.x ) + vec2( 1.0 ) ) * 0.5;\n vec2 base = offset + vec2( texelOffset );\n return base + s * ( scale - 2.0 * texelOffset );\n}\n#define cubeUV_maxLods3 (log2(cubeUV_textureSize*0.25) - 3.0)\nvec4 textureCubeUV(vec3 reflectedDirection, float roughness ) {\n float roughnessVal = roughness* cubeUV_maxLods3;\n float r1 = floor(roughnessVal);\n float r2 = r1 + 1.0;\n float t = fract(roughnessVal);\n vec2 mipInfo = MipLevelInfo(reflectedDirection, r1, roughness);\n float s = mipInfo.y;\n float level0 = mipInfo.x;\n float level1 = level0 + 1.0;\n level1 = level1 > 5.0 ? 5.0 : level1;\n level0 += min( floor( s + 0.5 ), 5.0 );\n vec2 uv_10 = getCubeUV(reflectedDirection, r1, level0);\n vec4 color10 = envMapTexelToLinear(texture2D(envMap, uv_10));\n vec2 uv_20 = getCubeUV(reflectedDirection, r2, level0);\n vec4 color20 = envMapTexelToLinear(texture2D(envMap, uv_20));\n vec4 result = mix(color10, color20, t);\n return vec4(result.rgb, 1.0);\n}\n#endif\n"; - // transparent pass (back-to-front order) +// File:src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl - renderObjects( scene.__webglObjects, false, "transparent", camera, lights, fog, true, material ); - renderObjectsImmediate( scene.__webglObjectsImmediate, "transparent", camera, lights, fog, true, material ); +THREE.ShaderChunk[ 'defaultnormal_vertex' ] = "#ifdef FLIP_SIDED\n objectNormal = -objectNormal;\n#endif\nvec3 transformedNormal = normalMatrix * objectNormal;\n"; - } +// File:src/renderers/shaders/ShaderChunk/displacementmap_vertex.glsl - // custom render plugins (post pass) +THREE.ShaderChunk[ 'displacementmap_vertex' ] = "#ifdef USE_DISPLACEMENTMAP\n transformed += normal * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );\n#endif\n"; - renderPlugins( this.renderPluginsPost, scene, camera ); +// File:src/renderers/shaders/ShaderChunk/displacementmap_pars_vertex.glsl +THREE.ShaderChunk[ 'displacementmap_pars_vertex' ] = "#ifdef USE_DISPLACEMENTMAP\n uniform sampler2D displacementMap;\n uniform float displacementScale;\n uniform float displacementBias;\n#endif\n"; - // Generate mipmap if we're using any kind of mipmap filtering +// File:src/renderers/shaders/ShaderChunk/emissivemap_fragment.glsl - if ( renderTarget && renderTarget.generateMipmaps && renderTarget.minFilter !== THREE.NearestFilter && renderTarget.minFilter !== THREE.LinearFilter ) { +THREE.ShaderChunk[ 'emissivemap_fragment' ] = "#ifdef USE_EMISSIVEMAP\n vec4 emissiveColor = texture2D( emissiveMap, vUv );\n emissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb;\n totalEmissiveRadiance *= emissiveColor.rgb;\n#endif\n"; - updateRenderTargetMipmap( renderTarget ); +// File:src/renderers/shaders/ShaderChunk/emissivemap_pars_fragment.glsl - } +THREE.ShaderChunk[ 'emissivemap_pars_fragment' ] = "#ifdef USE_EMISSIVEMAP\n uniform sampler2D emissiveMap;\n#endif\n"; - // Ensure depth buffer writing is enabled so it can be cleared on next render +// File:src/renderers/shaders/ShaderChunk/encodings_pars_fragment.glsl - this.setDepthTest( true ); - this.setDepthWrite( true ); +THREE.ShaderChunk[ 'encodings_pars_fragment' ] = "\nvec4 LinearToLinear( in vec4 value ) {\n return value;\n}\nvec4 GammaToLinear( in vec4 value, in float gammaFactor ) {\n return vec4( pow( value.xyz, vec3( gammaFactor ) ), value.w );\n}\nvec4 LinearToGamma( in vec4 value, in float gammaFactor ) {\n return vec4( pow( value.xyz, vec3( 1.0 / gammaFactor ) ), value.w );\n}\nvec4 sRGBToLinear( in vec4 value ) {\n return vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.w );\n}\nvec4 LinearTosRGB( in vec4 value ) {\n return vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.w );\n}\nvec4 RGBEToLinear( in vec4 value ) {\n return vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 );\n}\nvec4 LinearToRGBE( in vec4 value ) {\n float maxComponent = max( max( value.r, value.g ), value.b );\n float fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 );\n return vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 );\n}\nvec4 RGBMToLinear( in vec4 value, in float maxRange ) {\n return vec4( value.xyz * value.w * maxRange, 1.0 );\n}\nvec4 LinearToRGBM( in vec4 value, in float maxRange ) {\n float maxRGB = max( value.x, max( value.g, value.b ) );\n float M = clamp( maxRGB / maxRange, 0.0, 1.0 );\n M = ceil( M * 255.0 ) / 255.0;\n return vec4( value.rgb / ( M * maxRange ), M );\n}\nvec4 RGBDToLinear( in vec4 value, in float maxRange ) {\n return vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 );\n}\nvec4 LinearToRGBD( in vec4 value, in float maxRange ) {\n float maxRGB = max( value.x, max( value.g, value.b ) );\n float D = max( maxRange / maxRGB, 1.0 );\n D = min( floor( D ) / 255.0, 1.0 );\n return vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D );\n}\nconst mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 );\nvec4 LinearToLogLuv( in vec4 value ) {\n vec3 Xp_Y_XYZp = value.rgb * cLogLuvM;\n Xp_Y_XYZp = max(Xp_Y_XYZp, vec3(1e-6, 1e-6, 1e-6));\n vec4 vResult;\n vResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;\n float Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;\n vResult.w = fract(Le);\n vResult.z = (Le - (floor(vResult.w*255.0))/255.0)/255.0;\n return vResult;\n}\nconst mat3 cLogLuvInverseM = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 );\nvec4 LogLuvToLinear( in vec4 value ) {\n float Le = value.z * 255.0 + value.w;\n vec3 Xp_Y_XYZp;\n Xp_Y_XYZp.y = exp2((Le - 127.0) / 2.0);\n Xp_Y_XYZp.z = Xp_Y_XYZp.y / value.y;\n Xp_Y_XYZp.x = value.x * Xp_Y_XYZp.z;\n vec3 vRGB = Xp_Y_XYZp.rgb * cLogLuvInverseM;\n return vec4( max(vRGB, 0.0), 1.0 );\n}\n"; - // _gl.finish(); +// File:src/renderers/shaders/ShaderChunk/encodings_fragment.glsl - }; +THREE.ShaderChunk[ 'encodings_fragment' ] = " gl_FragColor = linearToOutputTexel( gl_FragColor );\n"; - function renderPlugins( plugins, scene, camera ) { +// File:src/renderers/shaders/ShaderChunk/envmap_fragment.glsl - if ( ! plugins.length ) return; +THREE.ShaderChunk[ 'envmap_fragment' ] = "#ifdef USE_ENVMAP\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n vec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n #ifdef ENVMAP_MODE_REFLECTION\n vec3 reflectVec = reflect( cameraToVertex, worldNormal );\n #else\n vec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n #endif\n #else\n vec3 reflectVec = vReflect;\n #endif\n #ifdef ENVMAP_TYPE_CUBE\n vec4 envColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\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 #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 envColor = envMapTexelToLinear( envColor );\n #ifdef ENVMAP_BLENDING_MULTIPLY\n outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n #elif defined( ENVMAP_BLENDING_MIX )\n outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n #elif defined( ENVMAP_BLENDING_ADD )\n outgoingLight += envColor.xyz * specularStrength * reflectivity;\n #endif\n#endif\n"; - for ( var i = 0, il = plugins.length; i < il; i ++ ) { +// File:src/renderers/shaders/ShaderChunk/envmap_pars_fragment.glsl - // reset state for plugin (to start from clean slate) +THREE.ShaderChunk[ 'envmap_pars_fragment' ] = "#if defined( USE_ENVMAP ) || defined( PHYSICAL )\n uniform float reflectivity;\n uniform float envMapIntenstiy;\n#endif\n#ifdef USE_ENVMAP\n #if ! defined( PHYSICAL ) && ( defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) )\n varying vec3 vWorldPosition;\n #endif\n #ifdef ENVMAP_TYPE_CUBE\n uniform samplerCube envMap;\n #else\n uniform sampler2D envMap;\n #endif\n uniform float flipEnvMap;\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( PHYSICAL )\n uniform float refractionRatio;\n #else\n varying vec3 vReflect;\n #endif\n#endif\n"; - _currentProgram = null; - _currentCamera = null; +// File:src/renderers/shaders/ShaderChunk/envmap_pars_vertex.glsl - _oldBlending = -1; - _oldDepthTest = -1; - _oldDepthWrite = -1; - _oldDoubleSided = -1; - _oldFlipSided = -1; - _currentGeometryGroupHash = -1; - _currentMaterialId = -1; +THREE.ShaderChunk[ 'envmap_pars_vertex' ] = "#ifdef USE_ENVMAP\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n varying vec3 vWorldPosition;\n #else\n varying vec3 vReflect;\n uniform float refractionRatio;\n #endif\n#endif\n"; - _lightsNeedUpdate = true; +// File:src/renderers/shaders/ShaderChunk/envmap_vertex.glsl - plugins[ i ].render( scene, camera, _currentWidth, _currentHeight ); +THREE.ShaderChunk[ 'envmap_vertex' ] = "#ifdef USE_ENVMAP\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n vWorldPosition = worldPosition.xyz;\n #else\n vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n #ifdef ENVMAP_MODE_REFLECTION\n vReflect = reflect( cameraToVertex, worldNormal );\n #else\n vReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n #endif\n #endif\n#endif\n"; - // reset state after plugin (anything could have changed) +// File:src/renderers/shaders/ShaderChunk/fog_fragment.glsl - _currentProgram = null; - _currentCamera = null; +THREE.ShaderChunk[ 'fog_fragment' ] = "#ifdef USE_FOG\n #ifdef USE_LOGDEPTHBUF_EXT\n float depth = gl_FragDepthEXT / gl_FragCoord.w;\n #else\n float depth = gl_FragCoord.z / gl_FragCoord.w;\n #endif\n #ifdef FOG_EXP2\n float fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * depth * depth * LOG2 ) );\n #else\n float fogFactor = smoothstep( fogNear, fogFar, depth );\n #endif\n gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\n#endif\n"; - _oldBlending = -1; - _oldDepthTest = -1; - _oldDepthWrite = -1; - _oldDoubleSided = -1; - _oldFlipSided = -1; - _currentGeometryGroupHash = -1; - _currentMaterialId = -1; +// File:src/renderers/shaders/ShaderChunk/fog_pars_fragment.glsl - _lightsNeedUpdate = true; +THREE.ShaderChunk[ 'fog_pars_fragment' ] = "#ifdef USE_FOG\n uniform vec3 fogColor;\n #ifdef FOG_EXP2\n uniform float fogDensity;\n #else\n uniform float fogNear;\n uniform float fogFar;\n #endif\n#endif"; - } +// File:src/renderers/shaders/ShaderChunk/lightmap_fragment.glsl - }; +THREE.ShaderChunk[ 'lightmap_fragment' ] = "#ifdef USE_LIGHTMAP\n reflectedLight.indirectDiffuse += PI * texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n#endif\n"; - function renderObjects ( renderList, reverse, materialType, camera, lights, fog, useBlending, overrideMaterial ) { +// File:src/renderers/shaders/ShaderChunk/lightmap_pars_fragment.glsl - var webglObject, object, buffer, material, start, end, delta; +THREE.ShaderChunk[ 'lightmap_pars_fragment' ] = "#ifdef USE_LIGHTMAP\n uniform sampler2D lightMap;\n uniform float lightMapIntensity;\n#endif"; - if ( reverse ) { +// File:src/renderers/shaders/ShaderChunk/lights_lambert_vertex.glsl - start = renderList.length - 1; - end = -1; - delta = -1; +THREE.ShaderChunk[ 'lights_lambert_vertex' ] = "vec3 diffuse = vec3( 1.0 );\nGeometricContext geometry;\ngeometry.position = mvPosition.xyz;\ngeometry.normal = normalize( transformedNormal );\ngeometry.viewDir = normalize( -mvPosition.xyz );\nGeometricContext backGeometry;\nbackGeometry.position = geometry.position;\nbackGeometry.normal = -geometry.normal;\nbackGeometry.viewDir = geometry.viewDir;\nvLightFront = vec3( 0.0 );\n#ifdef DOUBLE_SIDED\n vLightBack = vec3( 0.0 );\n#endif\nIncidentLight directLight;\nfloat dotNL;\nvec3 directLightColor_Diffuse;\n#if NUM_POINT_LIGHTS > 0\n for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n getPointDirectLightIrradiance( pointLights[ i ], geometry, directLight );\n dotNL = dot( geometry.normal, directLight.direction );\n directLightColor_Diffuse = PI * directLight.color;\n vLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n #ifdef DOUBLE_SIDED\n vLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n #endif\n }\n#endif\n#if NUM_SPOT_LIGHTS > 0\n for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n getSpotDirectLightIrradiance( spotLights[ i ], geometry, directLight );\n dotNL = dot( geometry.normal, directLight.direction );\n directLightColor_Diffuse = PI * directLight.color;\n vLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n #ifdef DOUBLE_SIDED\n vLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n #endif\n }\n#endif\n#if NUM_DIR_LIGHTS > 0\n for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n getDirectionalDirectLightIrradiance( directionalLights[ i ], geometry, directLight );\n dotNL = dot( geometry.normal, directLight.direction );\n directLightColor_Diffuse = PI * directLight.color;\n vLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n #ifdef DOUBLE_SIDED\n vLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n #endif\n }\n#endif\n#if NUM_HEMI_LIGHTS > 0\n for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n vLightFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n #ifdef DOUBLE_SIDED\n vLightBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry );\n #endif\n }\n#endif\n"; - } else { +// File:src/renderers/shaders/ShaderChunk/lights_pars.glsl - start = 0; - end = renderList.length; - delta = 1; - } +THREE.ShaderChunk[ 'lights_pars' ] = "uniform vec3 ambientLightColor;\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n vec3 irradiance = ambientLightColor;\n #ifndef PHYSICALLY_CORRECT_LIGHTS\n irradiance *= PI;\n #endif\n return irradiance;\n}\n#if NUM_DIR_LIGHTS > 0\n struct DirectionalLight {\n vec3 direction;\n vec3 color;\n int shadow;\n float shadowBias;\n float shadowRadius;\n vec2 shadowMapSize;\n };\n uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n void getDirectionalDirectLightIrradiance( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n directLight.color = directionalLight.color;\n directLight.direction = directionalLight.direction;\n directLight.visible = true;\n }\n#endif\n#if NUM_POINT_LIGHTS > 0\n struct PointLight {\n vec3 position;\n vec3 color;\n float distance;\n float decay;\n int shadow;\n float shadowBias;\n float shadowRadius;\n vec2 shadowMapSize;\n };\n uniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n void getPointDirectLightIrradiance( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n vec3 lVector = pointLight.position - geometry.position;\n directLight.direction = normalize( lVector );\n float lightDistance = length( lVector );\n if ( testLightInRange( lightDistance, pointLight.distance ) ) {\n directLight.color = pointLight.color;\n directLight.color *= punctualLightIntensityToIrradianceFactor( lightDistance, pointLight.distance, pointLight.decay );\n directLight.visible = true;\n } else {\n directLight.color = vec3( 0.0 );\n directLight.visible = false;\n }\n }\n#endif\n#if NUM_SPOT_LIGHTS > 0\n struct SpotLight {\n vec3 position;\n vec3 direction;\n vec3 color;\n float distance;\n float decay;\n float coneCos;\n float penumbraCos;\n int shadow;\n float shadowBias;\n float shadowRadius;\n vec2 shadowMapSize;\n };\n uniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n void getSpotDirectLightIrradiance( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight directLight ) {\n vec3 lVector = spotLight.position - geometry.position;\n directLight.direction = normalize( lVector );\n float lightDistance = length( lVector );\n float angleCos = dot( directLight.direction, spotLight.direction );\n if ( all( bvec2( angleCos > spotLight.coneCos, testLightInRange( lightDistance, spotLight.distance ) ) ) ) {\n float spotEffect = smoothstep( spotLight.coneCos, spotLight.penumbraCos, angleCos );\n directLight.color = spotLight.color;\n directLight.color *= spotEffect * punctualLightIntensityToIrradianceFactor( lightDistance, spotLight.distance, spotLight.decay );\n directLight.visible = true;\n } else {\n directLight.color = vec3( 0.0 );\n directLight.visible = false;\n }\n }\n#endif\n#if NUM_HEMI_LIGHTS > 0\n struct HemisphereLight {\n vec3 direction;\n vec3 skyColor;\n vec3 groundColor;\n };\n uniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\n vec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) {\n float dotNL = dot( geometry.normal, hemiLight.direction );\n float hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n vec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n #ifndef PHYSICALLY_CORRECT_LIGHTS\n irradiance *= PI;\n #endif\n return irradiance;\n }\n#endif\n#if defined( USE_ENVMAP ) && defined( PHYSICAL )\n vec3 getLightProbeIndirectIrradiance( const in GeometricContext geometry, const in int maxMIPLevel ) {\n #include \n vec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n #ifdef ENVMAP_TYPE_CUBE\n vec3 queryVec = flipNormal * vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n #ifdef TEXTURE_LOD_EXT\n vec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) );\n #else\n vec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) );\n #endif\n envMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n #elif defined( ENVMAP_TYPE_CUBE_UV )\n vec3 queryVec = flipNormal * vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n vec4 envMapColor = textureCubeUV( queryVec, 1.0 );\n #else\n vec4 envMapColor = vec4( 0.0 );\n #endif\n return PI * envMapColor.rgb * envMapIntensity;\n }\n float getSpecularMIPLevel( const in float blinnShininessExponent, const in int maxMIPLevel ) {\n float maxMIPLevelScalar = float( maxMIPLevel );\n float desiredMIPLevel = maxMIPLevelScalar - 0.79248 - 0.5 * log2( pow2( blinnShininessExponent ) + 1.0 );\n return clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );\n }\n vec3 getLightProbeIndirectRadiance( const in GeometricContext geometry, const in float blinnShininessExponent, const in int maxMIPLevel ) {\n #ifdef ENVMAP_MODE_REFLECTION\n vec3 reflectVec = reflect( -geometry.viewDir, geometry.normal );\n #else\n vec3 reflectVec = refract( -geometry.viewDir, geometry.normal, refractionRatio );\n #endif\n #include \n reflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n float specularMIPLevel = getSpecularMIPLevel( blinnShininessExponent, maxMIPLevel );\n #ifdef ENVMAP_TYPE_CUBE\n vec3 queryReflectVec = flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n #ifdef TEXTURE_LOD_EXT\n vec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel );\n #else\n vec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel );\n #endif\n envMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n #elif defined( ENVMAP_TYPE_CUBE_UV )\n vec3 queryReflectVec = flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n vec4 envMapColor = textureCubeUV(queryReflectVec, BlinnExponentToGGXRoughness(blinnShininessExponent));\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 #ifdef TEXTURE_LOD_EXT\n vec4 envMapColor = texture2DLodEXT( envMap, sampleUV, specularMIPLevel );\n #else\n vec4 envMapColor = texture2D( envMap, sampleUV, specularMIPLevel );\n #endif\n envMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\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 #ifdef TEXTURE_LOD_EXT\n vec4 envMapColor = texture2DLodEXT( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n #else\n vec4 envMapColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n #endif\n envMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\n #endif\n return envMapColor.rgb * envMapIntensity;\n }\n#endif\n"; - for ( var i = start; i !== end; i += delta ) { +// File:src/renderers/shaders/ShaderChunk/lights_phong_fragment.glsl - webglObject = renderList[ i ]; +THREE.ShaderChunk[ 'lights_phong_fragment' ] = "BlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;\n"; - if ( webglObject.render ) { +// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_fragment.glsl - object = webglObject.object; - buffer = webglObject.buffer; +THREE.ShaderChunk[ 'lights_phong_pars_fragment' ] = "varying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n varying vec3 vNormal;\n#endif\nstruct BlinnPhongMaterial {\n vec3 diffuseColor;\n vec3 specularColor;\n float specularShininess;\n float specularStrength;\n};\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n float dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n vec3 irradiance = dotNL * directLight.color;\n #ifndef PHYSICALLY_CORRECT_LIGHTS\n irradiance *= PI;\n #endif\n reflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n reflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;\n}\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n reflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct RE_Direct_BlinnPhong\n#define RE_IndirectDiffuse RE_IndirectDiffuse_BlinnPhong\n#define Material_LightProbeLOD( material ) (0)\n"; - if ( overrideMaterial ) { +// File:src/renderers/shaders/ShaderChunk/lights_physical_fragment.glsl - material = overrideMaterial; +THREE.ShaderChunk[ 'lights_physical_fragment' ] = "PhysicalMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nmaterial.specularRoughness = clamp( roughnessFactor, 0.04, 1.0 );\n#ifdef STANDARD\n material.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor );\n#else\n material.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor );\n material.clearCoat = saturate( clearCoat ); material.clearCoatRoughness = clamp( clearCoatRoughness, 0.04, 1.0 );\n#endif\n"; - } else { +// File:src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl - material = webglObject[ materialType ]; +THREE.ShaderChunk[ 'lights_physical_pars_fragment' ] = "struct PhysicalMaterial {\n vec3 diffuseColor;\n float specularRoughness;\n vec3 specularColor;\n #ifndef STANDARD\n float clearCoat;\n float clearCoatRoughness;\n #endif\n};\n#define MAXIMUM_SPECULAR_COEFFICIENT 0.16\n#define DEFAULT_SPECULAR_COEFFICIENT 0.04\nfloat clearCoatDHRApprox( const in float roughness, const in float dotNL ) {\n return DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) );\n}\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n float dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n vec3 irradiance = dotNL * directLight.color;\n #ifndef PHYSICALLY_CORRECT_LIGHTS\n irradiance *= PI;\n #endif\n #ifndef STANDARD\n float clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );\n #else\n float clearCoatDHR = 0.0;\n #endif\n reflectedLight.directSpecular += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry, material.specularColor, material.specularRoughness );\n reflectedLight.directDiffuse += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n #ifndef STANDARD\n reflectedLight.directSpecular += irradiance * material.clearCoat * BRDF_Specular_GGX( directLight, geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );\n #endif\n}\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n reflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 clearCoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\n #ifndef STANDARD\n float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n float dotNL = dotNV;\n float clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );\n #else\n float clearCoatDHR = 0.0;\n #endif\n reflectedLight.indirectSpecular += ( 1.0 - clearCoatDHR ) * radiance * BRDF_Specular_GGX_Environment( geometry, material.specularColor, material.specularRoughness );\n #ifndef STANDARD\n reflectedLight.indirectSpecular += clearCoatRadiance * material.clearCoat * BRDF_Specular_GGX_Environment( geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );\n #endif\n}\n#define RE_Direct RE_Direct_Physical\n#define RE_IndirectDiffuse RE_IndirectDiffuse_Physical\n#define RE_IndirectSpecular RE_IndirectSpecular_Physical\n#define Material_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.specularRoughness )\n#define Material_ClearCoat_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.clearCoatRoughness )\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\n return saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\n}\n"; - if ( ! material ) continue; +// File:src/renderers/shaders/ShaderChunk/lights_template.glsl - if ( useBlending ) _this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); +THREE.ShaderChunk[ 'lights_template' ] = "\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = normalize( vViewPosition );\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n PointLight pointLight;\n for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n pointLight = pointLights[ i ];\n getPointDirectLightIrradiance( pointLight, geometry, directLight );\n #ifdef USE_SHADOWMAP\n directLight.color *= all( bvec2( pointLight.shadow, directLight.visible ) ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ] ) : 1.0;\n #endif\n RE_Direct( directLight, geometry, material, reflectedLight );\n }\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n SpotLight spotLight;\n for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n spotLight = spotLights[ i ];\n getSpotDirectLightIrradiance( spotLight, geometry, directLight );\n #ifdef USE_SHADOWMAP\n directLight.color *= all( bvec2( spotLight.shadow, directLight.visible ) ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n #endif\n RE_Direct( directLight, geometry, material, reflectedLight );\n }\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n DirectionalLight directionalLight;\n for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n directionalLight = directionalLights[ i ];\n getDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );\n #ifdef USE_SHADOWMAP\n directLight.color *= all( bvec2( directionalLight.shadow, directLight.visible ) ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n #endif\n RE_Direct( directLight, geometry, material, reflectedLight );\n }\n#endif\n#if defined( RE_IndirectDiffuse )\n vec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n #ifdef USE_LIGHTMAP\n vec3 lightMapIrradiance = texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n #ifndef PHYSICALLY_CORRECT_LIGHTS\n lightMapIrradiance *= PI;\n #endif\n irradiance += lightMapIrradiance;\n #endif\n #if ( NUM_HEMI_LIGHTS > 0 )\n for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n irradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n }\n #endif\n #if defined( USE_ENVMAP ) && defined( PHYSICAL ) && defined( ENVMAP_TYPE_CUBE_UV )\n irradiance += getLightProbeIndirectIrradiance( geometry, 8 );\n #endif\n RE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n vec3 radiance = getLightProbeIndirectRadiance( geometry, Material_BlinnShininessExponent( material ), 8 );\n #ifndef STANDARD\n vec3 clearCoatRadiance = getLightProbeIndirectRadiance( geometry, Material_ClearCoat_BlinnShininessExponent( material ), 8 );\n #else\n vec3 clearCoatRadiance = vec3( 0.0 );\n #endif\n \n RE_IndirectSpecular( radiance, clearCoatRadiance, geometry, material, reflectedLight );\n#endif\n"; - _this.setDepthTest( material.depthTest ); - _this.setDepthWrite( material.depthWrite ); - setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_fragment.glsl - } +THREE.ShaderChunk[ 'logdepthbuf_fragment' ] = "#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)\n gl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;\n#endif"; - _this.setMaterialFaces( material ); +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_fragment.glsl - if ( buffer instanceof THREE.BufferGeometry ) { +THREE.ShaderChunk[ 'logdepthbuf_pars_fragment' ] = "#ifdef USE_LOGDEPTHBUF\n uniform float logDepthBufFC;\n #ifdef USE_LOGDEPTHBUF_EXT\n varying float vFragDepth;\n #endif\n#endif\n"; - _this.renderBufferDirect( camera, lights, fog, material, buffer, object ); +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_vertex.glsl - } else { +THREE.ShaderChunk[ 'logdepthbuf_pars_vertex' ] = "#ifdef USE_LOGDEPTHBUF\n #ifdef USE_LOGDEPTHBUF_EXT\n varying float vFragDepth;\n #endif\n uniform float logDepthBufFC;\n#endif"; - _this.renderBuffer( camera, lights, fog, material, buffer, object ); +// File:src/renderers/shaders/ShaderChunk/logdepthbuf_vertex.glsl - } +THREE.ShaderChunk[ 'logdepthbuf_vertex' ] = "#ifdef USE_LOGDEPTHBUF\n gl_Position.z = log2(max( EPSILON, gl_Position.w + 1.0 )) * logDepthBufFC;\n #ifdef USE_LOGDEPTHBUF_EXT\n vFragDepth = 1.0 + gl_Position.w;\n #else\n gl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;\n #endif\n#endif\n"; - } +// File:src/renderers/shaders/ShaderChunk/map_fragment.glsl - } +THREE.ShaderChunk[ 'map_fragment' ] = "#ifdef USE_MAP\n vec4 texelColor = texture2D( map, vUv );\n texelColor = mapTexelToLinear( texelColor );\n diffuseColor *= texelColor;\n#endif\n"; - }; +// File:src/renderers/shaders/ShaderChunk/map_pars_fragment.glsl - function renderObjectsImmediate ( renderList, materialType, camera, lights, fog, useBlending, overrideMaterial ) { +THREE.ShaderChunk[ 'map_pars_fragment' ] = "#ifdef USE_MAP\n uniform sampler2D map;\n#endif\n"; - var webglObject, object, material, program; +// File:src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl - for ( var i = 0, il = renderList.length; i < il; i ++ ) { +THREE.ShaderChunk[ 'map_particle_fragment' ] = "#ifdef USE_MAP\n vec4 mapTexel = texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) * offsetRepeat.zw + offsetRepeat.xy );\n diffuseColor *= mapTexelToLinear( mapTexel );\n#endif\n"; - webglObject = renderList[ i ]; - object = webglObject.object; +// File:src/renderers/shaders/ShaderChunk/map_particle_pars_fragment.glsl - if ( object.visible ) { +THREE.ShaderChunk[ 'map_particle_pars_fragment' ] = "#ifdef USE_MAP\n uniform vec4 offsetRepeat;\n uniform sampler2D map;\n#endif\n"; - if ( overrideMaterial ) { +// File:src/renderers/shaders/ShaderChunk/metalnessmap_fragment.glsl - material = overrideMaterial; +THREE.ShaderChunk[ 'metalnessmap_fragment' ] = "float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n vec4 texelMetalness = texture2D( metalnessMap, vUv );\n metalnessFactor *= texelMetalness.r;\n#endif\n"; - } else { +// File:src/renderers/shaders/ShaderChunk/metalnessmap_pars_fragment.glsl - material = webglObject[ materialType ]; +THREE.ShaderChunk[ 'metalnessmap_pars_fragment' ] = "#ifdef USE_METALNESSMAP\n uniform sampler2D metalnessMap;\n#endif"; - if ( ! material ) continue; +// File:src/renderers/shaders/ShaderChunk/morphnormal_vertex.glsl - if ( useBlending ) _this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); +THREE.ShaderChunk[ 'morphnormal_vertex' ] = "#ifdef USE_MORPHNORMALS\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#endif\n"; - _this.setDepthTest( material.depthTest ); - _this.setDepthWrite( material.depthWrite ); - setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); +// File:src/renderers/shaders/ShaderChunk/morphtarget_pars_vertex.glsl - } +THREE.ShaderChunk[ 'morphtarget_pars_vertex' ] = "#ifdef USE_MORPHTARGETS\n #ifndef USE_MORPHNORMALS\n uniform float morphTargetInfluences[ 8 ];\n #else\n uniform float morphTargetInfluences[ 4 ];\n #endif\n#endif"; - _this.renderImmediateObject( camera, lights, fog, material, object ); +// File:src/renderers/shaders/ShaderChunk/morphtarget_vertex.glsl - } +THREE.ShaderChunk[ 'morphtarget_vertex' ] = "#ifdef USE_MORPHTARGETS\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 #ifndef USE_MORPHNORMALS\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 #endif\n#endif\n"; - } +// File:src/renderers/shaders/ShaderChunk/normal_flip.glsl - }; +THREE.ShaderChunk[ 'normal_flip' ] = "#ifdef DOUBLE_SIDED\n float flipNormal = ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n#else\n float flipNormal = 1.0;\n#endif\n"; - this.renderImmediateObject = function ( camera, lights, fog, material, object ) { +// File:src/renderers/shaders/ShaderChunk/normal_fragment.glsl - var program = setProgram( camera, lights, fog, material, object ); +THREE.ShaderChunk[ 'normal_fragment' ] = "#ifdef FLAT_SHADED\n vec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) );\n vec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) );\n vec3 normal = normalize( cross( fdx, fdy ) );\n#else\n vec3 normal = normalize( vNormal ) * flipNormal;\n#endif\n#ifdef USE_NORMALMAP\n normal = perturbNormal2Arb( -vViewPosition, normal );\n#elif defined( USE_BUMPMAP )\n normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n#endif\n"; - _currentGeometryGroupHash = -1; +// File:src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl - _this.setMaterialFaces( material ); +THREE.ShaderChunk[ 'normalmap_pars_fragment' ] = "#ifdef USE_NORMALMAP\n uniform sampler2D normalMap;\n uniform vec2 normalScale;\n vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\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 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 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#endif\n"; - if ( object.immediateRenderCallback ) { +// File:src/renderers/shaders/ShaderChunk/packing.glsl - object.immediateRenderCallback( program, _gl, _frustum ); +THREE.ShaderChunk[ 'packing' ] = "vec3 packNormalToRGB( const in vec3 normal ) {\n return normalize( normal ) * 0.5 + 0.5;\n}\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\n return 1.0 - 2.0 * rgb.xyz;\n}\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;\nconst vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. );\nconst vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );\nconst float ShiftRight8 = 1. / 256.;\nvec4 packDepthToRGBA( const in float v ) {\n vec4 r = vec4( fract( v * PackFactors ), v );\n r.yzw -= r.xyz * ShiftRight8; return r * PackUpscale;\n}\nfloat unpackRGBAToDepth( const in vec4 v ) {\n return dot( v, UnpackFactors );\n}\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\n return ( viewZ + near ) / ( near - far );\n}\nfloat orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) {\n return linearClipZ * ( near - far ) - near;\n}\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\n return (( near + viewZ ) * far ) / (( far - near ) * viewZ );\n}\nfloat perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {\n return ( near * far ) / ( ( far - near ) * invClipZ - far );\n}\n"; - } else { +// File:src/renderers/shaders/ShaderChunk/premultiplied_alpha_fragment.glsl - object.render( function( object ) { _this.renderBufferImmediate( object, program, material ); } ); +THREE.ShaderChunk[ 'premultiplied_alpha_fragment' ] = "#ifdef PREMULTIPLIED_ALPHA\n gl_FragColor.rgb *= gl_FragColor.a;\n#endif\n"; - } +// File:src/renderers/shaders/ShaderChunk/project_vertex.glsl - }; +THREE.ShaderChunk[ 'project_vertex' ] = "#ifdef USE_SKINNING\n vec4 mvPosition = modelViewMatrix * skinned;\n#else\n vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\n#endif\ngl_Position = projectionMatrix * mvPosition;\n"; - function unrollImmediateBufferMaterial ( globject ) { +// File:src/renderers/shaders/ShaderChunk/roughnessmap_fragment.glsl - var object = globject.object, - material = object.material; +THREE.ShaderChunk[ 'roughnessmap_fragment' ] = "float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n vec4 texelRoughness = texture2D( roughnessMap, vUv );\n roughnessFactor *= texelRoughness.r;\n#endif\n"; - if ( material.transparent ) { +// File:src/renderers/shaders/ShaderChunk/roughnessmap_pars_fragment.glsl - globject.transparent = material; - globject.opaque = null; +THREE.ShaderChunk[ 'roughnessmap_pars_fragment' ] = "#ifdef USE_ROUGHNESSMAP\n uniform sampler2D roughnessMap;\n#endif"; - } else { +// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl - globject.opaque = material; - globject.transparent = null; +THREE.ShaderChunk[ 'shadowmap_pars_fragment' ] = "#ifdef USE_SHADOWMAP\n #if NUM_DIR_LIGHTS > 0\n uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHTS ];\n varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\n #endif\n #if NUM_SPOT_LIGHTS > 0\n uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHTS ];\n varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\n #endif\n #if NUM_POINT_LIGHTS > 0\n uniform sampler2D pointShadowMap[ NUM_POINT_LIGHTS ];\n varying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\n #endif\n float texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\n return step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) );\n }\n float texture2DShadowLerp( sampler2D depths, vec2 size, vec2 uv, float compare ) {\n const vec2 offset = vec2( 0.0, 1.0 );\n vec2 texelSize = vec2( 1.0 ) / size;\n vec2 centroidUV = floor( uv * size + 0.5 ) / size;\n float lb = texture2DCompare( depths, centroidUV + texelSize * offset.xx, compare );\n float lt = texture2DCompare( depths, centroidUV + texelSize * offset.xy, compare );\n float rb = texture2DCompare( depths, centroidUV + texelSize * offset.yx, compare );\n float rt = texture2DCompare( depths, centroidUV + texelSize * offset.yy, compare );\n vec2 f = fract( uv * size + 0.5 );\n float a = mix( lb, lt, f.y );\n float b = mix( rb, rt, f.y );\n float c = mix( a, b, f.x );\n return c;\n }\n float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n shadowCoord.xyz /= shadowCoord.w;\n shadowCoord.z += shadowBias;\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 bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n bool frustumTest = all( frustumTestVec );\n if ( frustumTest ) {\n #if defined( SHADOWMAP_TYPE_PCF )\n vec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n float dx0 = - texelSize.x * shadowRadius;\n float dy0 = - texelSize.y * shadowRadius;\n float dx1 = + texelSize.x * shadowRadius;\n float dy1 = + texelSize.y * shadowRadius;\n return (\n texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\n texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n ) * ( 1.0 / 9.0 );\n #elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n vec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n float dx0 = - texelSize.x * shadowRadius;\n float dy0 = - texelSize.y * shadowRadius;\n float dx1 = + texelSize.x * shadowRadius;\n float dy1 = + texelSize.y * shadowRadius;\n return (\n texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy, shadowCoord.z ) +\n texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n ) * ( 1.0 / 9.0 );\n #else\n return texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\n #endif\n }\n return 1.0;\n }\n vec2 cubeToUV( vec3 v, float texelSizeY ) {\n vec3 absV = abs( v );\n float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n absV *= scaleToCube;\n v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n vec2 planar = v.xy;\n float almostATexel = 1.5 * texelSizeY;\n float almostOne = 1.0 - almostATexel;\n if ( absV.z >= almostOne ) {\n if ( v.z > 0.0 )\n planar.x = 4.0 - v.x;\n } else if ( absV.x >= almostOne ) {\n float signX = sign( v.x );\n planar.x = v.z * signX + 2.0 * signX;\n } else if ( absV.y >= almostOne ) {\n float signY = sign( v.y );\n planar.x = v.x + 2.0 * signY + 2.0;\n planar.y = v.z * signY - 2.0;\n }\n return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n }\n float getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n vec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\n vec3 lightToPosition = shadowCoord.xyz;\n vec3 bd3D = normalize( lightToPosition );\n float dp = ( length( lightToPosition ) - shadowBias ) / 1000.0;\n #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT )\n vec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\n return (\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\n ) * ( 1.0 / 9.0 );\n #else\n return texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\n #endif\n }\n#endif\n"; - } +// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl - }; +THREE.ShaderChunk[ 'shadowmap_pars_vertex' ] = "#ifdef USE_SHADOWMAP\n #if NUM_DIR_LIGHTS > 0\n uniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHTS ];\n varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\n #endif\n #if NUM_SPOT_LIGHTS > 0\n uniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHTS ];\n varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\n #endif\n #if NUM_POINT_LIGHTS > 0\n uniform mat4 pointShadowMatrix[ NUM_POINT_LIGHTS ];\n varying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\n #endif\n#endif\n"; - function unrollBufferMaterial ( globject ) { +// File:src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl - var object = globject.object, - buffer = globject.buffer, - material, materialIndex, meshMaterial; +THREE.ShaderChunk[ 'shadowmap_vertex' ] = "#ifdef USE_SHADOWMAP\n #if NUM_DIR_LIGHTS > 0\n for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n vDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * worldPosition;\n }\n #endif\n #if NUM_SPOT_LIGHTS > 0\n for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n vSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * worldPosition;\n }\n #endif\n #if NUM_POINT_LIGHTS > 0\n for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n vPointShadowCoord[ i ] = pointShadowMatrix[ i ] * worldPosition;\n }\n #endif\n#endif\n"; - meshMaterial = object.material; +// File:src/renderers/shaders/ShaderChunk/shadowmask_pars_fragment.glsl - if ( meshMaterial instanceof THREE.MeshFaceMaterial ) { +THREE.ShaderChunk[ 'shadowmask_pars_fragment' ] = "float getShadowMask() {\n float shadow = 1.0;\n #ifdef USE_SHADOWMAP\n #if NUM_DIR_LIGHTS > 0\n DirectionalLight directionalLight;\n for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n directionalLight = directionalLights[ i ];\n shadow *= bool( directionalLight.shadow ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n }\n #endif\n #if NUM_SPOT_LIGHTS > 0\n SpotLight spotLight;\n for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n spotLight = spotLights[ i ];\n shadow *= bool( spotLight.shadow ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n }\n #endif\n #if NUM_POINT_LIGHTS > 0\n PointLight pointLight;\n for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n pointLight = pointLights[ i ];\n shadow *= bool( pointLight.shadow ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ] ) : 1.0;\n }\n #endif\n #endif\n return shadow;\n}\n"; - materialIndex = buffer.materialIndex; +// File:src/renderers/shaders/ShaderChunk/skinbase_vertex.glsl - material = meshMaterial.materials[ materialIndex ]; +THREE.ShaderChunk[ 'skinbase_vertex' ] = "#ifdef USE_SKINNING\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#endif"; - if ( material.transparent ) { +// File:src/renderers/shaders/ShaderChunk/skinning_pars_vertex.glsl - globject.transparent = material; - globject.opaque = null; +THREE.ShaderChunk[ 'skinning_pars_vertex' ] = "#ifdef USE_SKINNING\n uniform mat4 bindMatrix;\n uniform mat4 bindMatrixInverse;\n #ifdef BONE_TEXTURE\n uniform sampler2D boneTexture;\n uniform int boneTextureWidth;\n uniform int boneTextureHeight;\n mat4 getBoneMatrix( const in float i ) {\n float j = i * 4.0;\n float x = mod( j, float( boneTextureWidth ) );\n float y = floor( j / float( boneTextureWidth ) );\n float dx = 1.0 / float( boneTextureWidth );\n float dy = 1.0 / float( boneTextureHeight );\n y = dy * ( y + 0.5 );\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 mat4 bone = mat4( v1, v2, v3, v4 );\n return bone;\n }\n #else\n uniform mat4 boneMatrices[ MAX_BONES ];\n mat4 getBoneMatrix( const in float i ) {\n mat4 bone = boneMatrices[ int(i) ];\n return bone;\n }\n #endif\n#endif\n"; - } else { +// File:src/renderers/shaders/ShaderChunk/skinning_vertex.glsl - globject.opaque = material; - globject.transparent = null; +THREE.ShaderChunk[ 'skinning_vertex' ] = "#ifdef USE_SKINNING\n vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\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#endif\n"; - } +// File:src/renderers/shaders/ShaderChunk/skinnormal_vertex.glsl - } else { +THREE.ShaderChunk[ 'skinnormal_vertex' ] = "#ifdef USE_SKINNING\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 objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n#endif\n"; - material = meshMaterial; +// File:src/renderers/shaders/ShaderChunk/specularmap_fragment.glsl - if ( material ) { +THREE.ShaderChunk[ 'specularmap_fragment' ] = "float specularStrength;\n#ifdef USE_SPECULARMAP\n vec4 texelSpecular = texture2D( specularMap, vUv );\n specularStrength = texelSpecular.r;\n#else\n specularStrength = 1.0;\n#endif"; - if ( material.transparent ) { +// File:src/renderers/shaders/ShaderChunk/specularmap_pars_fragment.glsl - globject.transparent = material; - globject.opaque = null; +THREE.ShaderChunk[ 'specularmap_pars_fragment' ] = "#ifdef USE_SPECULARMAP\n uniform sampler2D specularMap;\n#endif"; - } else { +// File:src/renderers/shaders/ShaderChunk/tonemapping_fragment.glsl - globject.opaque = material; - globject.transparent = null; +THREE.ShaderChunk[ 'tonemapping_fragment' ] = "#if defined( TONE_MAPPING )\n gl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\n#endif\n"; - } +// File:src/renderers/shaders/ShaderChunk/tonemapping_pars_fragment.glsl - } +THREE.ShaderChunk[ 'tonemapping_pars_fragment' ] = "#define saturate(a) clamp( a, 0.0, 1.0 )\nuniform float toneMappingExposure;\nuniform float toneMappingWhitePoint;\nvec3 LinearToneMapping( vec3 color ) {\n return toneMappingExposure * color;\n}\nvec3 ReinhardToneMapping( vec3 color ) {\n color *= toneMappingExposure;\n return saturate( color / ( vec3( 1.0 ) + color ) );\n}\n#define Uncharted2Helper( x ) max( ( ( x * ( 0.15 * x + 0.10 * 0.50 ) + 0.20 * 0.02 ) / ( x * ( 0.15 * x + 0.50 ) + 0.20 * 0.30 ) ) - 0.02 / 0.30, vec3( 0.0 ) )\nvec3 Uncharted2ToneMapping( vec3 color ) {\n color *= toneMappingExposure;\n return saturate( Uncharted2Helper( color ) / Uncharted2Helper( vec3( toneMappingWhitePoint ) ) );\n}\nvec3 OptimizedCineonToneMapping( vec3 color ) {\n color *= toneMappingExposure;\n color = max( vec3( 0.0 ), color - 0.004 );\n return pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\n}\n"; - } +// File:src/renderers/shaders/ShaderChunk/uv2_pars_fragment.glsl - }; +THREE.ShaderChunk[ 'uv2_pars_fragment' ] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n varying vec2 vUv2;\n#endif"; - // Geometry splitting +// File:src/renderers/shaders/ShaderChunk/uv2_pars_vertex.glsl - function sortFacesByMaterial ( geometry, material ) { +THREE.ShaderChunk[ 'uv2_pars_vertex' ] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n attribute vec2 uv2;\n varying vec2 vUv2;\n#endif"; - var f, fl, face, materialIndex, vertices, - groupHash, hash_map = {}; +// File:src/renderers/shaders/ShaderChunk/uv2_vertex.glsl - var numMorphTargets = geometry.morphTargets.length; - var numMorphNormals = geometry.morphNormals.length; +THREE.ShaderChunk[ 'uv2_vertex' ] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n vUv2 = uv2;\n#endif"; - var usesFaceMaterial = material instanceof THREE.MeshFaceMaterial; +// File:src/renderers/shaders/ShaderChunk/uv_pars_fragment.glsl - geometry.geometryGroups = {}; +THREE.ShaderChunk[ 'uv_pars_fragment' ] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n varying vec2 vUv;\n#endif"; - for ( f = 0, fl = geometry.faces.length; f < fl; f ++ ) { +// File:src/renderers/shaders/ShaderChunk/uv_pars_vertex.glsl - face = geometry.faces[ f ]; - materialIndex = usesFaceMaterial ? face.materialIndex : 0; +THREE.ShaderChunk[ 'uv_pars_vertex' ] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n varying vec2 vUv;\n uniform vec4 offsetRepeat;\n#endif\n"; - if ( hash_map[ materialIndex ] === undefined ) { +// File:src/renderers/shaders/ShaderChunk/uv_vertex.glsl - hash_map[ materialIndex ] = { 'hash': materialIndex, 'counter': 0 }; +THREE.ShaderChunk[ 'uv_vertex' ] = "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\n vUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n#endif"; - } +// File:src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl - groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter; +THREE.ShaderChunk[ 'worldpos_vertex' ] = "#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( PHYSICAL ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )\n #ifdef USE_SKINNING\n vec4 worldPosition = modelMatrix * skinned;\n #else\n vec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\n #endif\n#endif\n"; - if ( geometry.geometryGroups[ groupHash ] === undefined ) { +// File:src/renderers/shaders/UniformsUtils.js - geometry.geometryGroups[ groupHash ] = { 'faces3': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets, 'numMorphNormals': numMorphNormals }; +/** + * Uniform Utilities + */ - } +THREE.UniformsUtils = { - vertices = 3; + merge: function ( uniforms ) { - if ( geometry.geometryGroups[ groupHash ].vertices + vertices > 65535 ) { + var merged = {}; - hash_map[ materialIndex ].counter += 1; - groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter; + for ( var u = 0; u < uniforms.length; u ++ ) { - if ( geometry.geometryGroups[ groupHash ] === undefined ) { + var tmp = this.clone( uniforms[ u ] ); - geometry.geometryGroups[ groupHash ] = { 'faces3': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets, 'numMorphNormals': numMorphNormals }; + for ( var p in tmp ) { - } + merged[ p ] = tmp[ p ]; } - geometry.geometryGroups[ groupHash ].faces3.push( f ); - geometry.geometryGroups[ groupHash ].vertices += vertices; - } - geometry.geometryGroupsList = []; + return merged; - for ( var g in geometry.geometryGroups ) { + }, - geometry.geometryGroups[ g ].id = _geometryGroupCounter ++; + clone: function ( uniforms_src ) { - geometry.geometryGroupsList.push( geometry.geometryGroups[ g ] ); + var uniforms_dst = {}; - } + for ( var u in uniforms_src ) { - }; + uniforms_dst[ u ] = {}; - // Objects refresh + for ( var p in uniforms_src[ u ] ) { - this.initWebGLObjects = function ( scene ) { + var parameter_src = uniforms_src[ u ][ p ]; - if ( !scene.__webglObjects ) { + 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 ) { - scene.__webglObjects = []; - scene.__webglObjectsImmediate = []; - scene.__webglSprites = []; - scene.__webglFlares = []; + uniforms_dst[ u ][ p ] = parameter_src.clone(); - } + } else if ( Array.isArray( parameter_src ) ) { - while ( scene.__objectsAdded.length ) { + uniforms_dst[ u ][ p ] = parameter_src.slice(); - addObject( scene.__objectsAdded[ 0 ], scene ); - scene.__objectsAdded.splice( 0, 1 ); + } else { - } + uniforms_dst[ u ][ p ] = parameter_src; - while ( scene.__objectsRemoved.length ) { + } - removeObject( scene.__objectsRemoved[ 0 ], scene ); - scene.__objectsRemoved.splice( 0, 1 ); + } } - // update must be called after objects adding / removal + return uniforms_dst; - for ( var o = 0, ol = scene.__webglObjects.length; o < ol; o ++ ) { + } - var object = scene.__webglObjects[ o ].object; +}; - // TODO: Remove this hack (WebGLRenderer refactoring) +// File:src/renderers/shaders/UniformsLib.js - if ( object.__webglInit === undefined ) { +/** + * Uniforms library for shared webgl shaders + */ - if ( object.__webglActive !== undefined ) { +THREE.UniformsLib = { - removeObject( object, scene ); + common: { - } + "diffuse": { value: new THREE.Color( 0xeeeeee ) }, + "opacity": { value: 1.0 }, - addObject( object, scene ); + "map": { value: null }, + "offsetRepeat": { value: new THREE.Vector4( 0, 0, 1, 1 ) }, - } + "specularMap": { value: null }, + "alphaMap": { value: null }, - updateObject( object ); + "envMap": { value: null }, + "flipEnvMap": { value: - 1 }, + "reflectivity": { value: 1.0 }, + "refractionRatio": { value: 0.98 } - } + }, - }; + aomap: { - // Objects adding + "aoMap": { value: null }, + "aoMapIntensity": { value: 1 } - function addObject( object, scene ) { + }, - var g, geometry, material, geometryGroup; + lightmap: { - if ( object.__webglInit === undefined ) { + "lightMap": { value: null }, + "lightMapIntensity": { value: 1 } - object.__webglInit = true; + }, - object._modelViewMatrix = new THREE.Matrix4(); - object._normalMatrix = new THREE.Matrix3(); + emissivemap: { - if ( object.geometry !== undefined && object.geometry.__webglInit === undefined ) { + "emissiveMap": { value: null } - object.geometry.__webglInit = true; - object.geometry.addEventListener( 'dispose', onGeometryDispose ); + }, - } + bumpmap: { - geometry = object.geometry; + "bumpMap": { value: null }, + "bumpScale": { value: 1 } - if ( geometry === undefined ) { + }, - // fail silently for now + normalmap: { - } else if ( geometry instanceof THREE.BufferGeometry ) { + "normalMap": { value: null }, + "normalScale": { value: new THREE.Vector2( 1, 1 ) } - initDirectBuffers( geometry ); + }, - } else if ( object instanceof THREE.Mesh ) { + displacementmap: { - material = object.material; + "displacementMap": { value: null }, + "displacementScale": { value: 1 }, + "displacementBias": { value: 0 } - if ( geometry.geometryGroups === undefined ) { + }, - sortFacesByMaterial( geometry, material ); + roughnessmap: { - } + "roughnessMap": { value: null } - // create separate VBOs per geometry chunk + }, - for ( g in geometry.geometryGroups ) { + metalnessmap: { - geometryGroup = geometry.geometryGroups[ g ]; + "metalnessMap": { value: null } - // initialise VBO on the first access + }, - if ( ! geometryGroup.__webglVertexBuffer ) { + fog: { - createMeshBuffers( geometryGroup ); - initMeshBuffers( geometryGroup, object ); + "fogDensity": { value: 0.00025 }, + "fogNear": { value: 1 }, + "fogFar": { value: 2000 }, + "fogColor": { value: new THREE.Color( 0xffffff ) } - geometry.verticesNeedUpdate = true; - geometry.morphTargetsNeedUpdate = true; - geometry.elementsNeedUpdate = true; - geometry.uvsNeedUpdate = true; - geometry.normalsNeedUpdate = true; - geometry.tangentsNeedUpdate = true; - geometry.colorsNeedUpdate = true; + }, - } + lights: { - } + "ambientLightColor": { value: [] }, - } else if ( object instanceof THREE.Ribbon ) { + "directionalLights": { value: [], properties: { + "direction": {}, + "color": {}, - if ( ! geometry.__webglVertexBuffer ) { + "shadow": {}, + "shadowBias": {}, + "shadowRadius": {}, + "shadowMapSize": {} + } }, - createRibbonBuffers( geometry ); - initRibbonBuffers( geometry, object ); + "directionalShadowMap": { value: [] }, + "directionalShadowMatrix": { value: [] }, - geometry.verticesNeedUpdate = true; - geometry.colorsNeedUpdate = true; - geometry.normalsNeedUpdate = true; + "spotLights": { value: [], properties: { + "color": {}, + "position": {}, + "direction": {}, + "distance": {}, + "coneCos": {}, + "penumbraCos": {}, + "decay": {}, - } + "shadow": {}, + "shadowBias": {}, + "shadowRadius": {}, + "shadowMapSize": {} + } }, - } else if ( object instanceof THREE.Line ) { + "spotShadowMap": { value: [] }, + "spotShadowMatrix": { value: [] }, - if ( ! geometry.__webglVertexBuffer ) { + "pointLights": { value: [], properties: { + "color": {}, + "position": {}, + "decay": {}, + "distance": {}, - createLineBuffers( geometry ); - initLineBuffers( geometry, object ); + "shadow": {}, + "shadowBias": {}, + "shadowRadius": {}, + "shadowMapSize": {} + } }, - geometry.verticesNeedUpdate = true; - geometry.colorsNeedUpdate = true; - geometry.lineDistancesNeedUpdate = true; + "pointShadowMap": { value: [] }, + "pointShadowMatrix": { value: [] }, - } + "hemisphereLights": { value: [], properties: { + "direction": {}, + "skyColor": {}, + "groundColor": {} + } } - } else if ( object instanceof THREE.ParticleSystem ) { + }, - if ( ! geometry.__webglVertexBuffer ) { + points: { - createParticleBuffers( geometry ); - initParticleBuffers( geometry, object ); + "diffuse": { value: new THREE.Color( 0xeeeeee ) }, + "opacity": { value: 1.0 }, + "size": { value: 1.0 }, + "scale": { value: 1.0 }, + "map": { value: null }, + "offsetRepeat": { value: new THREE.Vector4( 0, 0, 1, 1 ) } - geometry.verticesNeedUpdate = true; - geometry.colorsNeedUpdate = true; + } - } +}; - } +// File:src/renderers/shaders/ShaderLib/cube_frag.glsl - } +THREE.ShaderChunk[ 'cube_frag' ] = "uniform samplerCube tCube;\nuniform float tFlip;\nuniform float opacity;\nvarying vec3 vWorldPosition;\n#include \nvoid main() {\n gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );\n gl_FragColor.a *= opacity;\n}\n"; - if ( object.__webglActive === undefined ) { +// File:src/renderers/shaders/ShaderLib/cube_vert.glsl - if ( object instanceof THREE.Mesh ) { +THREE.ShaderChunk[ 'cube_vert' ] = "varying vec3 vWorldPosition;\n#include \nvoid main() {\n vWorldPosition = transformDirection( position, modelMatrix );\n #include \n #include \n}\n"; - geometry = object.geometry; +// File:src/renderers/shaders/ShaderLib/depth_frag.glsl - if ( geometry instanceof THREE.BufferGeometry ) { +THREE.ShaderChunk[ 'depth_frag' ] = "#if DEPTH_PACKING == 3200\n uniform float opacity;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n #include \n vec4 diffuseColor = vec4( 1.0 );\n #if DEPTH_PACKING == 3200\n diffuseColor.a = opacity;\n #endif\n #include \n #include \n #include \n #include \n #if DEPTH_PACKING == 3200\n gl_FragColor = vec4( vec3( gl_FragCoord.z ), opacity );\n #elif DEPTH_PACKING == 3201\n gl_FragColor = packDepthToRGBA( gl_FragCoord.z );\n #endif\n}\n"; - addBuffer( scene.__webglObjects, geometry, object ); +// File:src/renderers/shaders/ShaderLib/depth_vert.glsl - } else if ( geometry instanceof THREE.Geometry ) { +THREE.ShaderChunk[ 'depth_vert' ] = "#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n #include \n #include \n #include \n #include \n #include \n #include \n #include \n #include \n #include \n}\n"; - for ( g in geometry.geometryGroups ) { +// File:src/renderers/shaders/ShaderLib/distanceRGBA_frag.glsl - geometryGroup = geometry.geometryGroups[ g ]; +THREE.ShaderChunk[ 'distanceRGBA_frag' ] = "uniform vec3 lightPos;\nvarying vec4 vWorldPosition;\n#include \n#include \n#include \nvoid main () {\n #include \n gl_FragColor = packDepthToRGBA( length( vWorldPosition.xyz - lightPos.xyz ) / 1000.0 );\n}\n"; - addBuffer( scene.__webglObjects, geometryGroup, object ); +// File:src/renderers/shaders/ShaderLib/distanceRGBA_vert.glsl - } +THREE.ShaderChunk[ 'distanceRGBA_vert' ] = "varying vec4 vWorldPosition;\n#include \n#include \n#include \n#include \nvoid main() {\n #include \n #include \n #include \n #include \n #include \n #include \n #include \n vWorldPosition = worldPosition;\n}\n"; - } +// File:src/renderers/shaders/ShaderLib/equirect_frag.glsl - } else if ( object instanceof THREE.Ribbon || - object instanceof THREE.Line || - object instanceof THREE.ParticleSystem ) { +THREE.ShaderChunk[ 'equirect_frag' ] = "uniform sampler2D tEquirect;\nuniform float tFlip;\nvarying vec3 vWorldPosition;\n#include \nvoid main() {\n vec3 direction = normalize( vWorldPosition );\n vec2 sampleUV;\n sampleUV.y = saturate( tFlip * direction.y * -0.5 + 0.5 );\n sampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;\n gl_FragColor = texture2D( tEquirect, sampleUV );\n}\n"; - geometry = object.geometry; - addBuffer( scene.__webglObjects, geometry, object ); +// File:src/renderers/shaders/ShaderLib/equirect_vert.glsl - } else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) { +THREE.ShaderChunk[ 'equirect_vert' ] = "varying vec3 vWorldPosition;\n#include \nvoid main() {\n vWorldPosition = transformDirection( position, modelMatrix );\n #include \n #include \n}\n"; - addBufferImmediate( scene.__webglObjectsImmediate, object ); +// File:src/renderers/shaders/ShaderLib/linedashed_frag.glsl - } else if ( object instanceof THREE.Sprite ) { +THREE.ShaderChunk[ 'linedashed_frag' ] = "uniform vec3 diffuse;\nuniform float opacity;\nuniform float dashSize;\nuniform float totalSize;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n #include \n if ( mod( vLineDistance, totalSize ) > dashSize ) {\n discard;\n }\n vec3 outgoingLight = vec3( 0.0 );\n vec4 diffuseColor = vec4( diffuse, opacity );\n #include \n #include \n outgoingLight = diffuseColor.rgb;\n gl_FragColor = vec4( outgoingLight, diffuseColor.a );\n #include \n #include \n #include \n #include \n}\n"; - scene.__webglSprites.push( object ); +// File:src/renderers/shaders/ShaderLib/linedashed_vert.glsl - } else if ( object instanceof THREE.LensFlare ) { +THREE.ShaderChunk[ 'linedashed_vert' ] = "uniform float scale;\nattribute float lineDistance;\nvarying float vLineDistance;\n#include \n#include \n#include \n#include \nvoid main() {\n #include \n vLineDistance = scale * lineDistance;\n vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n gl_Position = projectionMatrix * mvPosition;\n #include \n #include \n}\n"; - scene.__webglFlares.push( object ); +// File:src/renderers/shaders/ShaderLib/meshbasic_frag.glsl - } +THREE.ShaderChunk[ 'meshbasic_frag' ] = "uniform vec3 diffuse;\nuniform float opacity;\n#ifndef FLAT_SHADED\n varying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n #include \n vec4 diffuseColor = vec4( diffuse, opacity );\n #include \n #include \n #include \n #include \n #include \n #include \n ReflectedLight reflectedLight;\n reflectedLight.directDiffuse = vec3( 0.0 );\n reflectedLight.directSpecular = vec3( 0.0 );\n reflectedLight.indirectDiffuse = diffuseColor.rgb;\n reflectedLight.indirectSpecular = vec3( 0.0 );\n #include \n vec3 outgoingLight = reflectedLight.indirectDiffuse;\n #include \n #include \n gl_FragColor = vec4( outgoingLight, diffuseColor.a );\n #include \n #include \n #include \n #include \n}\n"; - object.__webglActive = true; +// File:src/renderers/shaders/ShaderLib/meshbasic_vert.glsl - } +THREE.ShaderChunk[ 'meshbasic_vert' ] = "#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n #include \n #include \n #include \n #include \n #ifdef USE_ENVMAP\n #include \n #include \n #include \n #include \n #endif\n #include \n #include \n #include \n #include \n #include \n #include \n #include \n #include \n}\n"; - }; +// File:src/renderers/shaders/ShaderLib/meshlambert_frag.glsl - function addBuffer( objlist, buffer, object ) { +THREE.ShaderChunk[ 'meshlambert_frag' ] = "uniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float opacity;\nvarying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\n varying vec3 vLightBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n #include \n vec4 diffuseColor = vec4( diffuse, opacity );\n ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n vec3 totalEmissiveRadiance = emissive;\n #include \n #include \n #include \n #include \n #include \n #include \n #include \n reflectedLight.indirectDiffuse = getAmbientLightIrradiance( ambientLightColor );\n #include \n reflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb );\n #ifdef DOUBLE_SIDED\n reflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack;\n #else\n reflectedLight.directDiffuse = vLightFront;\n #endif\n reflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask();\n #include \n vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\n #include \n #include \n gl_FragColor = vec4( outgoingLight, diffuseColor.a );\n #include \n #include \n #include \n #include \n}\n"; - objlist.push( - { - id: null, - buffer: buffer, - object: object, - opaque: null, - transparent: null, - z: 0 - } - ); +// File:src/renderers/shaders/ShaderLib/meshlambert_vert.glsl - }; +THREE.ShaderChunk[ 'meshlambert_vert' ] = "#define LAMBERT\nvarying vec3 vLightFront;\n#ifdef DOUBLE_SIDED\n varying vec3 vLightBack;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n #include \n #include \n #include \n #include \n #include \n #include \n #include \n #include \n #include \n #include \n #include \n #include \n #include \n #include \n #include \n #include \n #include \n #include \n}\n"; - function addBufferImmediate( objlist, object ) { +// File:src/renderers/shaders/ShaderLib/meshphong_frag.glsl - objlist.push( - { - id: null, - object: object, - opaque: null, - transparent: null, - z: 0 - } - ); +THREE.ShaderChunk[ 'meshphong_frag' ] = "#define PHONG\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform vec3 specular;\nuniform float shininess;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n #include \n vec4 diffuseColor = vec4( diffuse, opacity );\n ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n vec3 totalEmissiveRadiance = emissive;\n #include \n #include \n #include \n #include \n #include \n #include \n #include \n #include \n #include \n #include \n #include \n #include \n vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n #include \n gl_FragColor = vec4( outgoingLight, diffuseColor.a );\n #include \n #include \n #include \n #include \n}\n"; - }; +// File:src/renderers/shaders/ShaderLib/meshphong_vert.glsl - // Objects updates +THREE.ShaderChunk[ 'meshphong_vert' ] = "#define PHONG\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n varying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n #include \n #include \n #include \n #include \n #include \n #include \n #include \n #include \n#ifndef FLAT_SHADED\n vNormal = normalize( transformedNormal );\n#endif\n #include \n #include \n #include \n #include \n #include \n #include \n #include \n vViewPosition = - mvPosition.xyz;\n #include \n #include \n #include \n}\n"; - function updateObject( object ) { +// File:src/renderers/shaders/ShaderLib/meshphysical_frag.glsl - var geometry = object.geometry, - geometryGroup, customAttributesDirty, material; +THREE.ShaderChunk[ 'meshphysical_frag' ] = "#define PHYSICAL\nuniform vec3 diffuse;\nuniform vec3 emissive;\nuniform float roughness;\nuniform float metalness;\nuniform float opacity;\n#ifndef STANDARD\n uniform float clearCoat;\n uniform float clearCoatRoughness;\n#endif\nuniform float envMapIntensity;\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n varying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n #include \n vec4 diffuseColor = vec4( diffuse, opacity );\n ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\n vec3 totalEmissiveRadiance = emissive;\n #include \n #include \n #include \n #include \n #include \n #include \n #include \n #include \n #include \n #include \n #include \n #include \n #include \n #include \n vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\n gl_FragColor = vec4( outgoingLight, diffuseColor.a );\n #include \n #include \n #include \n #include \n}\n"; - if ( geometry instanceof THREE.BufferGeometry ) { +// File:src/renderers/shaders/ShaderLib/meshphysical_vert.glsl - setDirectBuffers( geometry, _gl.DYNAMIC_DRAW, !geometry.dynamic ); +THREE.ShaderChunk[ 'meshphysical_vert' ] = "#define PHYSICAL\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n varying vec3 vNormal;\n#endif\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n #include \n #include \n #include \n #include \n #include \n #include \n #include \n #include \n#ifndef FLAT_SHADED\n vNormal = normalize( transformedNormal );\n#endif\n #include \n #include \n #include \n #include \n #include \n #include \n #include \n vViewPosition = - mvPosition.xyz;\n #include \n #include \n}\n"; - } else if ( object instanceof THREE.Mesh ) { +// File:src/renderers/shaders/ShaderLib/normal_frag.glsl - // check all geometry groups +THREE.ShaderChunk[ 'normal_frag' ] = "uniform float opacity;\nvarying vec3 vNormal;\n#include \n#include \n#include \n#include \nvoid main() {\n #include \n gl_FragColor = vec4( packNormalToRGB( vNormal ), opacity );\n #include \n}\n"; - for( var i = 0, il = geometry.geometryGroupsList.length; i < il; i ++ ) { +// File:src/renderers/shaders/ShaderLib/normal_vert.glsl - geometryGroup = geometry.geometryGroupsList[ i ]; +THREE.ShaderChunk[ 'normal_vert' ] = "varying vec3 vNormal;\n#include \n#include \n#include \n#include \nvoid main() {\n vNormal = normalize( normalMatrix * normal );\n #include \n #include \n #include \n #include \n #include \n}\n"; - material = getBufferMaterial( object, geometryGroup ); +// File:src/renderers/shaders/ShaderLib/points_frag.glsl - if ( geometry.buffersNeedUpdate ) { +THREE.ShaderChunk[ 'points_frag' ] = "uniform vec3 diffuse;\nuniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n #include \n vec3 outgoingLight = vec3( 0.0 );\n vec4 diffuseColor = vec4( diffuse, opacity );\n #include \n #include \n #include \n #include \n outgoingLight = diffuseColor.rgb;\n gl_FragColor = vec4( outgoingLight, diffuseColor.a );\n #include \n #include \n #include \n #include \n}\n"; - initMeshBuffers( geometryGroup, object ); +// File:src/renderers/shaders/ShaderLib/points_vert.glsl - } +THREE.ShaderChunk[ 'points_vert' ] = "uniform float size;\nuniform float scale;\n#include \n#include \n#include \n#include \n#include \nvoid main() {\n #include \n #include \n #include \n #ifdef USE_SIZEATTENUATION\n gl_PointSize = size * ( scale / - mvPosition.z );\n #else\n gl_PointSize = size;\n #endif\n #include \n #include \n #include \n #include \n}\n"; - customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); +// File:src/renderers/shaders/ShaderLib/shadow_frag.glsl - if ( geometry.verticesNeedUpdate || geometry.morphTargetsNeedUpdate || geometry.elementsNeedUpdate || - geometry.uvsNeedUpdate || geometry.normalsNeedUpdate || - geometry.colorsNeedUpdate || geometry.tangentsNeedUpdate || customAttributesDirty ) { +THREE.ShaderChunk[ 'shadow_frag' ] = "uniform float opacity;\n#include \n#include \n#include \n#include \n#include \n#include \nvoid main() {\n gl_FragColor = vec4( 0.0, 0.0, 0.0, opacity * ( 1.0 - getShadowMask() ) );\n}\n"; - setMeshBuffers( geometryGroup, object, _gl.DYNAMIC_DRAW, !geometry.dynamic, material ); +// File:src/renderers/shaders/ShaderLib/shadow_vert.glsl - } +THREE.ShaderChunk[ 'shadow_vert' ] = "#include \nvoid main() {\n #include \n #include \n #include \n #include \n}\n"; - } +// File:src/renderers/shaders/ShaderLib.js - geometry.verticesNeedUpdate = false; - geometry.morphTargetsNeedUpdate = false; - geometry.elementsNeedUpdate = false; - geometry.uvsNeedUpdate = false; - geometry.normalsNeedUpdate = false; - geometry.colorsNeedUpdate = false; - geometry.tangentsNeedUpdate = false; +/** + * Webgl Shader Library for three.js + * + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + */ - geometry.buffersNeedUpdate = false; - material.attributes && clearCustomAttributes( material ); +THREE.ShaderLib = { - } else if ( object instanceof THREE.Ribbon ) { + 'basic': { - material = getBufferMaterial( object, geometry ); + uniforms: THREE.UniformsUtils.merge( [ - customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); + THREE.UniformsLib[ 'common' ], + THREE.UniformsLib[ 'aomap' ], + THREE.UniformsLib[ 'fog' ] - if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || geometry.normalsNeedUpdate || customAttributesDirty ) { + ] ), - setRibbonBuffers( geometry, _gl.DYNAMIC_DRAW ); + vertexShader: THREE.ShaderChunk[ 'meshbasic_vert' ], + fragmentShader: THREE.ShaderChunk[ 'meshbasic_frag' ] - } + }, - geometry.verticesNeedUpdate = false; - geometry.colorsNeedUpdate = false; - geometry.normalsNeedUpdate = false; + 'lambert': { - material.attributes && clearCustomAttributes( material ); + uniforms: THREE.UniformsUtils.merge( [ - } else if ( object instanceof THREE.Line ) { + THREE.UniformsLib[ 'common' ], + THREE.UniformsLib[ 'aomap' ], + THREE.UniformsLib[ 'lightmap' ], + THREE.UniformsLib[ 'emissivemap' ], + THREE.UniformsLib[ 'fog' ], + THREE.UniformsLib[ 'lights' ], - material = getBufferMaterial( object, geometry ); + { + "emissive" : { value: new THREE.Color( 0x000000 ) } + } - customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); + ] ), - if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || geometry.lineDistancesNeedUpdate || customAttributesDirty ) { + vertexShader: THREE.ShaderChunk[ 'meshlambert_vert' ], + fragmentShader: THREE.ShaderChunk[ 'meshlambert_frag' ] - setLineBuffers( geometry, _gl.DYNAMIC_DRAW ); + }, - } + 'phong': { - geometry.verticesNeedUpdate = false; - geometry.colorsNeedUpdate = false; - geometry.lineDistancesNeedUpdate = false; + 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' ], - material.attributes && clearCustomAttributes( material ); + { + "emissive" : { value: new THREE.Color( 0x000000 ) }, + "specular" : { value: new THREE.Color( 0x111111 ) }, + "shininess": { value: 30 } + } + ] ), - } else if ( object instanceof THREE.ParticleSystem ) { + vertexShader: THREE.ShaderChunk[ 'meshphong_vert' ], + fragmentShader: THREE.ShaderChunk[ 'meshphong_frag' ] - material = getBufferMaterial( object, geometry ); + }, - customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); + 'standard': { - if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || object.sortParticles || customAttributesDirty ) { + uniforms: THREE.UniformsUtils.merge( [ - setParticleBuffers( geometry, _gl.DYNAMIC_DRAW, object ); + THREE.UniformsLib[ 'common' ], + THREE.UniformsLib[ 'aomap' ], + THREE.UniformsLib[ 'lightmap' ], + THREE.UniformsLib[ 'emissivemap' ], + THREE.UniformsLib[ 'bumpmap' ], + THREE.UniformsLib[ 'normalmap' ], + THREE.UniformsLib[ 'displacementmap' ], + THREE.UniformsLib[ 'roughnessmap' ], + THREE.UniformsLib[ 'metalnessmap' ], + THREE.UniformsLib[ 'fog' ], + THREE.UniformsLib[ 'lights' ], + { + "emissive" : { value: new THREE.Color( 0x000000 ) }, + "roughness": { value: 0.5 }, + "metalness": { value: 0 }, + "envMapIntensity" : { value: 1 }, // temporary } - geometry.verticesNeedUpdate = false; - geometry.colorsNeedUpdate = false; + ] ), - material.attributes && clearCustomAttributes( material ); + vertexShader: THREE.ShaderChunk[ 'meshphysical_vert' ], + fragmentShader: THREE.ShaderChunk[ 'meshphysical_frag' ] - } + }, - }; + 'points': { - // Objects updates - custom attributes check + uniforms: THREE.UniformsUtils.merge( [ - function areCustomAttributesDirty( material ) { + THREE.UniformsLib[ 'points' ], + THREE.UniformsLib[ 'fog' ] - for ( var a in material.attributes ) { + ] ), - if ( material.attributes[ a ].needsUpdate ) return true; + vertexShader: THREE.ShaderChunk[ 'points_vert' ], + fragmentShader: THREE.ShaderChunk[ 'points_frag' ] - } + }, - return false; + 'dashed': { - }; + uniforms: THREE.UniformsUtils.merge( [ - function clearCustomAttributes( material ) { + THREE.UniformsLib[ 'common' ], + THREE.UniformsLib[ 'fog' ], - for ( var a in material.attributes ) { + { + "scale" : { value: 1 }, + "dashSize" : { value: 1 }, + "totalSize": { value: 2 } + } - material.attributes[ a ].needsUpdate = false; + ] ), - } + vertexShader: THREE.ShaderChunk[ 'linedashed_vert' ], + fragmentShader: THREE.ShaderChunk[ 'linedashed_frag' ] - }; + }, - // Objects removal + 'depth': { - function removeObject( object, scene ) { + uniforms: THREE.UniformsUtils.merge( [ - if ( object instanceof THREE.Mesh || - object instanceof THREE.ParticleSystem || - object instanceof THREE.Ribbon || - object instanceof THREE.Line ) { + THREE.UniformsLib[ 'common' ], + THREE.UniformsLib[ 'displacementmap' ] - removeInstances( scene.__webglObjects, object ); + ] ), - } else if ( object instanceof THREE.Sprite ) { + vertexShader: THREE.ShaderChunk[ 'depth_vert' ], + fragmentShader: THREE.ShaderChunk[ 'depth_frag' ] - removeInstancesDirect( scene.__webglSprites, object ); + }, - } else if ( object instanceof THREE.LensFlare ) { + 'normal': { - removeInstancesDirect( scene.__webglFlares, object ); + uniforms: { - } else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) { + "opacity" : { value: 1.0 } - removeInstances( scene.__webglObjectsImmediate, object ); + }, - } + vertexShader: THREE.ShaderChunk[ 'normal_vert' ], + fragmentShader: THREE.ShaderChunk[ 'normal_frag' ] - delete object.__webglActive; + }, - }; + /* ------------------------------------------------------------------------- + // Cube map shader + ------------------------------------------------------------------------- */ - function removeInstances( objlist, object ) { + 'cube': { - for ( var o = objlist.length - 1; o >= 0; o -- ) { + uniforms: { + "tCube": { value: null }, + "tFlip": { value: - 1 }, + "opacity": { value: 1.0 } + }, - if ( objlist[ o ].object === object ) { + vertexShader: THREE.ShaderChunk[ 'cube_vert' ], + fragmentShader: THREE.ShaderChunk[ 'cube_frag' ] - objlist.splice( o, 1 ); + }, - } + /* ------------------------------------------------------------------------- + // Cube map shader + ------------------------------------------------------------------------- */ - } + 'equirect': { - }; + uniforms: { + "tEquirect": { value: null }, + "tFlip": { value: - 1 } + }, - function removeInstancesDirect( objlist, object ) { + vertexShader: THREE.ShaderChunk[ 'equirect_vert' ], + fragmentShader: THREE.ShaderChunk[ 'equirect_frag' ] - for ( var o = objlist.length - 1; o >= 0; o -- ) { + }, - if ( objlist[ o ] === object ) { + 'distanceRGBA': { - objlist.splice( o, 1 ); + uniforms: { - } + "lightPos": { value: new THREE.Vector3() } - } + }, - }; + vertexShader: THREE.ShaderChunk[ 'distanceRGBA_vert' ], + fragmentShader: THREE.ShaderChunk[ 'distanceRGBA_frag' ] - // Materials + } - this.initMaterial = function ( material, lights, fog, object ) { +}; - material.addEventListener( 'dispose', onMaterialDispose ); +THREE.ShaderLib[ 'physical' ] = { - var u, a, identifiers, i, parameters, maxLightCount, maxBones, maxShadows, shaderID; + uniforms: THREE.UniformsUtils.merge( [ - if ( material instanceof THREE.MeshDepthMaterial ) { + THREE.ShaderLib[ 'standard' ].uniforms, - shaderID = 'depth'; + { + "clearCoat": { value: 0 }, + "clearCoatRoughness": { value: 0 } + } - } else if ( material instanceof THREE.MeshNormalMaterial ) { + ] ), - shaderID = 'normal'; + vertexShader: THREE.ShaderChunk[ 'meshphysical_vert' ], + fragmentShader: THREE.ShaderChunk[ 'meshphysical_frag' ] - } else if ( material instanceof THREE.MeshBasicMaterial ) { +}; - shaderID = 'basic'; +// File:src/renderers/WebGLRenderer.js - } else if ( material instanceof THREE.MeshLambertMaterial ) { +/** + * @author supereggbert / http://www.paulbrunt.co.uk/ + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author szimek / https://github.com/szimek/ + * @author tschw + */ - shaderID = 'lambert'; +THREE.WebGLRenderer = function ( parameters ) { - } else if ( material instanceof THREE.MeshPhongMaterial ) { + console.log( 'THREE.WebGLRenderer', THREE.REVISION ); - shaderID = 'phong'; + parameters = parameters || {}; - } else if ( material instanceof THREE.LineBasicMaterial ) { + var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ), + _context = parameters.context !== undefined ? parameters.context : null, - shaderID = 'basic'; + _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; - } else if ( material instanceof THREE.LineDashedMaterial ) { + var lights = []; - shaderID = 'dashed'; + var opaqueObjects = []; + var opaqueObjectsLastIndex = - 1; + var transparentObjects = []; + var transparentObjectsLastIndex = - 1; - } else if ( material instanceof THREE.ParticleBasicMaterial ) { + var morphInfluences = new Float32Array( 8 ); - shaderID = 'particle_basic'; + var sprites = []; + var lensFlares = []; - } + // public properties - if ( shaderID ) { + this.domElement = _canvas; + this.context = null; - setMaterialShaders( material, THREE.ShaderLib[ shaderID ] ); + // clearing - } + this.autoClear = true; + this.autoClearColor = true; + this.autoClearDepth = true; + this.autoClearStencil = true; - // heuristics to create shader parameters according to lights in the scene - // (not to blow over maxLights budget) + // scene graph - maxLightCount = allocateLights( lights ); + this.sortObjects = true; - maxShadows = allocateShadows( lights ); + // user-defined clipping - maxBones = allocateBones( object ); + this.clippingPlanes = []; + this.localClippingEnabled = false; - parameters = { + // physically based shading - map: !!material.map, - envMap: !!material.envMap, - lightMap: !!material.lightMap, - bumpMap: !!material.bumpMap, - normalMap: !!material.normalMap, - specularMap: !!material.specularMap, + this.gammaFactor = 2.0; // for backwards compatibility + this.gammaInput = false; + this.gammaOutput = false; - vertexColors: material.vertexColors, + // physical lights - fog: fog, - useFog: material.fog, - fogExp: fog instanceof THREE.FogExp2, + this.physicallyCorrectLights = false; - sizeAttenuation: material.sizeAttenuation, + // tone mapping - skinning: material.skinning, - maxBones: maxBones, - useVertexTexture: _supportsBoneTextures && object && object.useVertexTexture, - boneTextureWidth: object && object.boneTextureWidth, - boneTextureHeight: object && object.boneTextureHeight, + this.toneMapping = THREE.LinearToneMapping; + this.toneMappingExposure = 1.0; + this.toneMappingWhitePoint = 1.0; - morphTargets: material.morphTargets, - morphNormals: material.morphNormals, - maxMorphTargets: this.maxMorphTargets, - maxMorphNormals: this.maxMorphNormals, + // morphs - maxDirLights: maxLightCount.directional, - maxPointLights: maxLightCount.point, - maxSpotLights: maxLightCount.spot, - maxHemiLights: maxLightCount.hemi, + this.maxMorphTargets = 8; + this.maxMorphNormals = 4; - maxShadows: maxShadows, - shadowMapEnabled: this.shadowMapEnabled && object.receiveShadow, - shadowMapType: this.shadowMapType, - shadowMapDebug: this.shadowMapDebug, - shadowMapCascade: this.shadowMapCascade, + // internal properties - alphaTest: material.alphaTest, - metal: material.metal, - perPixel: material.perPixel, - wrapAround: material.wrapAround, - doubleSided: material.side === THREE.DoubleSide, - flipSided: material.side === THREE.BackSide + var _this = this, - }; + // internal state cache - material.program = buildProgram( shaderID, material.fragmentShader, material.vertexShader, material.uniforms, material.attributes, material.defines, parameters, material.index0AttributeName ); + _currentProgram = null, + _currentRenderTarget = null, + _currentFramebuffer = null, + _currentMaterialId = - 1, + _currentGeometryProgram = '', + _currentCamera = null, - var attributes = material.program.attributes; + _currentScissor = new THREE.Vector4(), + _currentScissorTest = null, - if ( material.morphTargets ) { + _currentViewport = new THREE.Vector4(), - material.numSupportedMorphTargets = 0; + // - var id, base = "morphTarget"; + _usedTextureUnits = 0, - for ( i = 0; i < this.maxMorphTargets; i ++ ) { + // - id = base + i; + _clearColor = new THREE.Color( 0x000000 ), + _clearAlpha = 0, - if ( attributes[ id ] >= 0 ) { + _width = _canvas.width, + _height = _canvas.height, - material.numSupportedMorphTargets ++; + _pixelRatio = 1, - } + _scissor = new THREE.Vector4( 0, 0, _width, _height ), + _scissorTest = false, - } + _viewport = new THREE.Vector4( 0, 0, _width, _height ), - } + // frustum - if ( material.morphNormals ) { + _frustum = new THREE.Frustum(), - material.numSupportedMorphNormals = 0; + // clipping - var id, base = "morphNormal"; + _clipping = new THREE.WebGLClipping(), + _clippingEnabled = false, + _localClippingEnabled = false, - for ( i = 0; i < this.maxMorphNormals; i ++ ) { + _sphere = new THREE.Sphere(), - id = base + i; + // camera matrices cache - if ( attributes[ id ] >= 0 ) { + _projScreenMatrix = new THREE.Matrix4(), - material.numSupportedMorphNormals ++; + _vector3 = new THREE.Vector3(), - } + // light arrays cache - } + _lights = { - } + hash: '', - material.uniformsList = []; + ambient: [ 0, 0, 0 ], + directional: [], + directionalShadowMap: [], + directionalShadowMatrix: [], + spot: [], + spotShadowMap: [], + spotShadowMatrix: [], + point: [], + pointShadowMap: [], + pointShadowMatrix: [], + hemi: [], - for ( u in material.uniforms ) { + shadows: [] - material.uniformsList.push( [ material.uniforms[ u ], u ] ); + }, - } + // info + + _infoRender = { + + calls: 0, + vertices: 0, + faces: 0, + points: 0 }; - function setMaterialShaders( material, shaders ) { + this.info = { + + render: _infoRender, + memory: { + + geometries: 0, + textures: 0 - material.uniforms = THREE.UniformsUtils.clone( shaders.uniforms ); - material.vertexShader = shaders.vertexShader; - material.fragmentShader = shaders.fragmentShader; + }, + programs: null }; - function setProgram( camera, lights, fog, material, object ) { - _usedTextureUnits = 0; + // initialize - if ( material.needsUpdate ) { + var _gl; - if ( material.program ) deallocateMaterial( material ); + try { - _this.initMaterial( material, lights, fog, object ); - material.needsUpdate = false; + var attributes = { + alpha: _alpha, + depth: _depth, + stencil: _stencil, + antialias: _antialias, + premultipliedAlpha: _premultipliedAlpha, + preserveDrawingBuffer: _preserveDrawingBuffer + }; - } + _gl = _context || _canvas.getContext( 'webgl', attributes ) || _canvas.getContext( 'experimental-webgl', attributes ); - if ( material.morphTargets ) { + if ( _gl === null ) { + + if ( _canvas.getContext( 'webgl' ) !== null ) { + + throw 'Error creating WebGL context with your selected attributes.'; - if ( ! object.__webglMorphTargetInfluences ) { + } else { - object.__webglMorphTargetInfluences = new Float32Array( _this.maxMorphTargets ); + throw 'Error creating WebGL context.'; } } - var refreshMaterial = false; + // Some experimental-webgl implementations do not have getShaderPrecisionFormat - var program = material.program, - p_uniforms = program.uniforms, - m_uniforms = material.uniforms; + if ( _gl.getShaderPrecisionFormat === undefined ) { - if ( program !== _currentProgram ) { + _gl.getShaderPrecisionFormat = function () { - _gl.useProgram( program ); - _currentProgram = program; + return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 }; - refreshMaterial = true; + }; } - if ( material.id !== _currentMaterialId ) { + _canvas.addEventListener( 'webglcontextlost', onContextLost, false ); - _currentMaterialId = material.id; - refreshMaterial = true; + } catch ( error ) { - } + console.error( 'THREE.WebGLRenderer: ' + error ); - if ( refreshMaterial || camera !== _currentCamera ) { + } - _gl.uniformMatrix4fv( p_uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); + var extensions = new THREE.WebGLExtensions( _gl ); - if ( camera !== _currentCamera ) _currentCamera = camera; + extensions.get( 'WEBGL_depth_texture' ); + 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' ); - } + if ( extensions.get( 'OES_element_index_uint' ) ) { - // 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 + THREE.BufferGeometry.MaxIndex = 4294967296; - if ( material.skinning ) { + } - if ( _supportsBoneTextures && object.useVertexTexture ) { + var capabilities = new THREE.WebGLCapabilities( _gl, extensions, parameters ); - if ( p_uniforms.boneTexture !== null ) { + var state = new THREE.WebGLState( _gl, extensions, paramThreeToGL ); + var properties = new THREE.WebGLProperties(); + var textures = new THREE.WebGLTextures( _gl, extensions, state, properties, capabilities, paramThreeToGL, this.info ); + var objects = new THREE.WebGLObjects( _gl, properties, this.info ); + var programCache = new THREE.WebGLPrograms( this, capabilities ); + var lightCache = new THREE.WebGLLights(); - var textureUnit = getTextureUnit(); + this.info.programs = programCache.programs; - _gl.uniform1i( p_uniforms.boneTexture, textureUnit ); - _this.setTexture( object.boneTexture, textureUnit ); + var bufferRenderer = new THREE.WebGLBufferRenderer( _gl, extensions, _infoRender ); + var indexedBufferRenderer = new THREE.WebGLIndexedBufferRenderer( _gl, extensions, _infoRender ); - } + // - } else { + var backgroundCamera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); + var backgroundCamera2 = new THREE.PerspectiveCamera(); + var backgroundPlaneMesh = new THREE.Mesh( + new THREE.PlaneBufferGeometry( 2, 2 ), + new THREE.MeshBasicMaterial( { depthTest: false, depthWrite: false, fog: false } ) + ); + var backgroundBoxShader = THREE.ShaderLib[ 'cube' ]; + var backgroundBoxMesh = new THREE.Mesh( + new THREE.BoxBufferGeometry( 5, 5, 5 ), + new THREE.ShaderMaterial( { + uniforms: backgroundBoxShader.uniforms, + vertexShader: backgroundBoxShader.vertexShader, + fragmentShader: backgroundBoxShader.fragmentShader, + side: THREE.BackSide, + depthTest: false, + depthWrite: false, + fog: false + } ) + ); - if ( p_uniforms.boneGlobalMatrices !== null ) { + // - _gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, object.boneMatrices ); + function getTargetPixelRatio() { - } + return _currentRenderTarget === null ? _pixelRatio : 1; - } + } - } + function glClearColor( r, g, b, a ) { - if ( refreshMaterial ) { + if ( _premultipliedAlpha === true ) { - // refresh uniforms common to several materials + r *= a; g *= a; b *= a; - if ( fog && material.fog ) { + } - refreshUniformsFog( m_uniforms, fog ); + state.clearColor( r, g, b, a ); - } + } - if ( material instanceof THREE.MeshPhongMaterial || - material instanceof THREE.MeshLambertMaterial || - material.lights ) { + function setDefaultGLState() { - if ( _lightsNeedUpdate ) { + state.init(); - setupLights( program, lights ); - _lightsNeedUpdate = false; + state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ) ); + state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ) ); - } + glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); - refreshUniformsLights( m_uniforms, _lights ); + } - } + function resetGLState() { - if ( material instanceof THREE.MeshBasicMaterial || - material instanceof THREE.MeshLambertMaterial || - material instanceof THREE.MeshPhongMaterial ) { + _currentProgram = null; + _currentCamera = null; - refreshUniformsCommon( m_uniforms, material ); + _currentGeometryProgram = ''; + _currentMaterialId = - 1; - } + state.reset(); - // refresh single material specific uniforms + } - if ( material instanceof THREE.LineBasicMaterial ) { + setDefaultGLState(); - refreshUniformsLine( m_uniforms, material ); + this.context = _gl; + this.capabilities = capabilities; + this.extensions = extensions; + this.properties = properties; + this.state = state; - } else if ( material instanceof THREE.LineDashedMaterial ) { + // shadow map - refreshUniformsLine( m_uniforms, material ); - refreshUniformsDash( m_uniforms, material ); + var shadowMap = new THREE.WebGLShadowMap( this, _lights, objects, capabilities ); - } else if ( material instanceof THREE.ParticleBasicMaterial ) { + this.shadowMap = shadowMap; - refreshUniformsParticle( m_uniforms, material ); - } else if ( material instanceof THREE.MeshPhongMaterial ) { + // Plugins - refreshUniformsPhong( m_uniforms, material ); + var spritePlugin = new THREE.SpritePlugin( this, sprites ); + var lensFlarePlugin = new THREE.LensFlarePlugin( this, lensFlares ); - } else if ( material instanceof THREE.MeshLambertMaterial ) { + // API - refreshUniformsLambert( m_uniforms, material ); + this.getContext = function () { - } else if ( material instanceof THREE.MeshDepthMaterial ) { + return _gl; - m_uniforms.mNear.value = camera.near; - m_uniforms.mFar.value = camera.far; - m_uniforms.opacity.value = material.opacity; + }; - } else if ( material instanceof THREE.MeshNormalMaterial ) { + this.getContextAttributes = function () { - m_uniforms.opacity.value = material.opacity; + return _gl.getContextAttributes(); - } + }; - if ( object.receiveShadow && ! material._shadowPass ) { + this.forceContextLoss = function () { - refreshUniformsShadow( m_uniforms, lights ); + extensions.get( 'WEBGL_lose_context' ).loseContext(); - } + }; - // load common uniforms + this.getMaxAnisotropy = function () { - loadUniformsGeneric( program, material.uniformsList ); + return capabilities.getMaxAnisotropy(); - // 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 ) { + this.getPrecision = function () { - if ( p_uniforms.cameraPosition !== null ) { + return capabilities.precision; - _vector3.getPositionFromMatrix( camera.matrixWorld ); - _gl.uniform3f( p_uniforms.cameraPosition, _vector3.x, _vector3.y, _vector3.z ); + }; - } + this.getPixelRatio = function () { - } + return _pixelRatio; - if ( material instanceof THREE.MeshPhongMaterial || - material instanceof THREE.MeshLambertMaterial || - material instanceof THREE.ShaderMaterial || - material.skinning ) { + }; - if ( p_uniforms.viewMatrix !== null ) { + this.setPixelRatio = function ( value ) { - _gl.uniformMatrix4fv( p_uniforms.viewMatrix, false, camera.matrixWorldInverse.elements ); + if ( value === undefined ) return; - } + _pixelRatio = value; - } + this.setSize( _viewport.z, _viewport.w, false ); - } + }; - loadUniformsMatrices( p_uniforms, object ); + this.getSize = function () { - if ( p_uniforms.modelMatrix !== null ) { + return { + width: _width, + height: _height + }; - _gl.uniformMatrix4fv( p_uniforms.modelMatrix, false, object.matrixWorld.elements ); + }; - } + this.setSize = function ( width, height, updateStyle ) { - return program; + _width = width; + _height = height; - }; + _canvas.width = width * _pixelRatio; + _canvas.height = height * _pixelRatio; - // Uniforms (refresh uniforms objects) + if ( updateStyle !== false ) { - function refreshUniformsCommon ( uniforms, material ) { + _canvas.style.width = width + 'px'; + _canvas.style.height = height + 'px'; - uniforms.opacity.value = material.opacity; + } - if ( _this.gammaInput ) { + this.setViewport( 0, 0, width, height ); - uniforms.diffuse.value.copyGammaToLinear( material.color ); + }; - } else { + this.setViewport = function ( x, y, width, height ) { - uniforms.diffuse.value = material.color; + state.viewport( _viewport.set( x, y, width, height ) ); - } + }; - uniforms.map.value = material.map; - uniforms.lightMap.value = material.lightMap; - uniforms.specularMap.value = material.specularMap; + this.setScissor = function ( x, y, width, height ) { - if ( material.bumpMap ) { + state.scissor( _scissor.set( x, y, width, height ) ); - uniforms.bumpMap.value = material.bumpMap; - uniforms.bumpScale.value = material.bumpScale; + }; - } + this.setScissorTest = function ( boolean ) { - if ( material.normalMap ) { + state.setScissorTest( _scissorTest = boolean ); - uniforms.normalMap.value = material.normalMap; - uniforms.normalScale.value.copy( material.normalScale ); + }; - } + // Clearing - // uv repeat and offset setting priorities - // 1. color map - // 2. specular map - // 3. normal map - // 4. bump map + this.getClearColor = function () { - var uvScaleMap; + return _clearColor; - if ( material.map ) { + }; - uvScaleMap = material.map; + this.setClearColor = function ( color, alpha ) { - } else if ( material.specularMap ) { + _clearColor.set( color ); - uvScaleMap = material.specularMap; + _clearAlpha = alpha !== undefined ? alpha : 1; - } else if ( material.normalMap ) { + glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); - uvScaleMap = material.normalMap; + }; - } else if ( material.bumpMap ) { + this.getClearAlpha = function () { - uvScaleMap = material.bumpMap; + return _clearAlpha; - } + }; - if ( uvScaleMap !== undefined ) { + this.setClearAlpha = function ( alpha ) { - var offset = uvScaleMap.offset; - var repeat = uvScaleMap.repeat; + _clearAlpha = alpha; - uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y ); + glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); - } + }; - uniforms.envMap.value = material.envMap; - uniforms.flipEnvMap.value = ( material.envMap instanceof THREE.WebGLRenderTargetCube ) ? 1 : -1; + this.clear = function ( color, depth, stencil ) { - if ( _this.gammaInput ) { + var bits = 0; - //uniforms.reflectivity.value = material.reflectivity * material.reflectivity; - uniforms.reflectivity.value = material.reflectivity; + if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT; + if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT; + if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT; - } else { + _gl.clear( bits ); - uniforms.reflectivity.value = material.reflectivity; + }; - } + this.clearColor = function () { - uniforms.refractionRatio.value = material.refractionRatio; - uniforms.combine.value = material.combine; - uniforms.useRefract.value = material.envMap && material.envMap.mapping instanceof THREE.CubeRefractionMapping; + this.clear( true, false, false ); }; - function refreshUniformsLine ( uniforms, material ) { + this.clearDepth = function () { - uniforms.diffuse.value = material.color; - uniforms.opacity.value = material.opacity; + this.clear( false, true, false ); }; - function refreshUniformsDash ( uniforms, material ) { + this.clearStencil = function () { - uniforms.dashSize.value = material.dashSize; - uniforms.totalSize.value = material.dashSize + material.gapSize; - uniforms.scale.value = material.scale; + this.clear( false, false, true ); }; - function refreshUniformsParticle ( uniforms, material ) { - - 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. + this.clearTarget = function ( renderTarget, color, depth, stencil ) { - uniforms.map.value = material.map; + this.setRenderTarget( renderTarget ); + this.clear( color, depth, stencil ); }; - function refreshUniformsFog ( uniforms, fog ) { + // Reset - uniforms.fogColor.value = fog.color; + this.resetGLState = resetGLState; - if ( fog instanceof THREE.Fog ) { + this.dispose = function() { - uniforms.fogNear.value = fog.near; - uniforms.fogFar.value = fog.far; + transparentObjects = []; + transparentObjectsLastIndex = -1; + opaqueObjects = []; + opaqueObjectsLastIndex = -1; - } else if ( fog instanceof THREE.FogExp2 ) { + _canvas.removeEventListener( 'webglcontextlost', onContextLost, false ); - uniforms.fogDensity.value = fog.density; + }; - } + // Events - }; + function onContextLost( event ) { - function refreshUniformsPhong ( uniforms, material ) { + event.preventDefault(); - uniforms.shininess.value = material.shininess; + resetGLState(); + setDefaultGLState(); - if ( _this.gammaInput ) { + properties.clear(); - uniforms.ambient.value.copyGammaToLinear( material.ambient ); - uniforms.emissive.value.copyGammaToLinear( material.emissive ); - uniforms.specular.value.copyGammaToLinear( material.specular ); + } - } else { + function onMaterialDispose( event ) { - uniforms.ambient.value = material.ambient; - uniforms.emissive.value = material.emissive; - uniforms.specular.value = material.specular; + var material = event.target; - } + material.removeEventListener( 'dispose', onMaterialDispose ); + + deallocateMaterial( material ); - if ( material.wrapAround ) { + } - uniforms.wrapRGB.value.copy( material.wrapRGB ); + // Buffer deallocation - } + function deallocateMaterial( material ) { - }; + releaseMaterialProgramReference( material ); - function refreshUniformsLambert ( uniforms, material ) { + properties.delete( material ); - if ( _this.gammaInput ) { + } - uniforms.ambient.value.copyGammaToLinear( material.ambient ); - uniforms.emissive.value.copyGammaToLinear( material.emissive ); - } else { + function releaseMaterialProgramReference( material ) { - uniforms.ambient.value = material.ambient; - uniforms.emissive.value = material.emissive; + var programInfo = properties.get( material ).program; - } + material.program = undefined; - if ( material.wrapAround ) { + if ( programInfo !== undefined ) { - uniforms.wrapRGB.value.copy( material.wrapRGB ); + programCache.releaseProgram( programInfo ); } - }; + } - function refreshUniformsLights ( uniforms, lights ) { + // Buffer rendering - uniforms.ambientLightColor.value = lights.ambient; + this.renderBufferImmediate = function ( object, program, material ) { - uniforms.directionalLightColor.value = lights.directional.colors; - uniforms.directionalLightDirection.value = lights.directional.positions; + state.initAttributes(); - uniforms.pointLightColor.value = lights.point.colors; - uniforms.pointLightPosition.value = lights.point.positions; - uniforms.pointLightDistance.value = lights.point.distances; + var buffers = properties.get( object ); - uniforms.spotLightColor.value = lights.spot.colors; - uniforms.spotLightPosition.value = lights.spot.positions; - uniforms.spotLightDistance.value = lights.spot.distances; - uniforms.spotLightDirection.value = lights.spot.directions; - uniforms.spotLightAngleCos.value = lights.spot.anglesCos; - uniforms.spotLightExponent.value = lights.spot.exponents; + if ( 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(); - uniforms.hemisphereLightSkyColor.value = lights.hemi.skyColors; - uniforms.hemisphereLightGroundColor.value = lights.hemi.groundColors; - uniforms.hemisphereLightDirection.value = lights.hemi.positions; + var attributes = program.getAttributes(); - }; + if ( object.hasPositions ) { - function refreshUniformsShadow ( uniforms, lights ) { + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.position ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW ); + + state.enableAttribute( attributes.position ); + _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); - if ( uniforms.shadowMatrix ) { + } - var j = 0; + if ( object.hasNormals ) { - for ( var i = 0, il = lights.length; i < il; i ++ ) { + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.normal ); - var light = lights[ i ]; + if ( material.type !== 'MeshPhongMaterial' && material.type !== 'MeshStandardMaterial' && material.type !== 'MeshPhysicalMaterial' && material.shading === THREE.FlatShading ) { - if ( ! light.castShadow ) continue; + for ( var i = 0, l = object.count * 3; i < l; i += 9 ) { - if ( light instanceof THREE.SpotLight || ( light instanceof THREE.DirectionalLight && ! light.shadowCascade ) ) { + var array = object.normalArray; - uniforms.shadowMap.value[ j ] = light.shadowMap; - uniforms.shadowMapSize.value[ j ] = light.shadowMapSize; + 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; - uniforms.shadowMatrix.value[ j ] = light.shadowMatrix; + array[ i + 0 ] = nx; + array[ i + 1 ] = ny; + array[ i + 2 ] = nz; - uniforms.shadowDarkness.value[ j ] = light.shadowDarkness; - uniforms.shadowBias.value[ j ] = light.shadowBias; + array[ i + 3 ] = nx; + array[ i + 4 ] = ny; + array[ i + 5 ] = nz; - j ++; + array[ i + 6 ] = nx; + array[ i + 7 ] = ny; + array[ i + 8 ] = nz; } } - } + _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW ); - }; + state.enableAttribute( attributes.normal ); - // Uniforms (load to GPU) + _gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); - function loadUniformsMatrices ( uniforms, object ) { + } - _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, object._modelViewMatrix.elements ); + if ( object.hasUvs && material.map ) { - if ( uniforms.normalMatrix ) { + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.uv ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW ); - _gl.uniformMatrix3fv( uniforms.normalMatrix, false, object._normalMatrix.elements ); + state.enableAttribute( attributes.uv ); - } + _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); - }; + } - function getTextureUnit() { + if ( object.hasColors && material.vertexColors !== THREE.NoColors ) { - var textureUnit = _usedTextureUnits; + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.color ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW ); - if ( textureUnit >= _maxTextures ) { + state.enableAttribute( attributes.color ); - console.warn( "WebGLRenderer: trying to use " + textureUnit + " texture units while this GPU supports only " + _maxTextures ); + _gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 ); } - _usedTextureUnits += 1; - - return textureUnit; - - }; + state.disableUnusedAttributes(); - function loadUniformsGeneric ( program, uniforms ) { + _gl.drawArrays( _gl.TRIANGLES, 0, object.count ); - var uniform, value, type, location, texture, textureUnit, i, il, j, jl, offset; + object.count = 0; - for ( j = 0, jl = uniforms.length; j < jl; j ++ ) { + }; - location = program.uniforms[ uniforms[ j ][ 1 ] ]; - if ( !location ) continue; + this.renderBufferDirect = function ( camera, fog, geometry, material, object, group ) { - uniform = uniforms[ j ][ 0 ]; + setMaterial( material ); - type = uniform.type; - value = uniform.value; + var program = setProgram( camera, fog, material, object ); - if ( type === "i" ) { // single integer + var updateBuffers = false; + var geometryProgram = geometry.id + '_' + program.id + '_' + material.wireframe; - _gl.uniform1i( location, value ); + if ( geometryProgram !== _currentGeometryProgram ) { - } else if ( type === "f" ) { // single float + _currentGeometryProgram = geometryProgram; + updateBuffers = true; - _gl.uniform1f( location, value ); + } - } else if ( type === "v2" ) { // single THREE.Vector2 + // morph targets - _gl.uniform2f( location, value.x, value.y ); + var morphTargetInfluences = object.morphTargetInfluences; - } else if ( type === "v3" ) { // single THREE.Vector3 + if ( morphTargetInfluences !== undefined ) { - _gl.uniform3f( location, value.x, value.y, value.z ); + var activeInfluences = []; - } else if ( type === "v4" ) { // single THREE.Vector4 + for ( var i = 0, l = morphTargetInfluences.length; i < l; i ++ ) { - _gl.uniform4f( location, value.x, value.y, value.z, value.w ); + var influence = morphTargetInfluences[ i ]; + activeInfluences.push( [ influence, i ] ); - } else if ( type === "c" ) { // single THREE.Color + } - _gl.uniform3f( location, value.r, value.g, value.b ); + activeInfluences.sort( absNumericalSort ); - } else if ( type === "iv1" ) { // flat array of integers (JS or typed array) + if ( activeInfluences.length > 8 ) { - _gl.uniform1iv( location, value ); + activeInfluences.length = 8; - } else if ( type === "iv" ) { // flat array of integers with 3 x N size (JS or typed array) + } - _gl.uniform3iv( location, value ); + var morphAttributes = geometry.morphAttributes; - } else if ( type === "fv1" ) { // flat array of floats (JS or typed array) + for ( var i = 0, l = activeInfluences.length; i < l; i ++ ) { - _gl.uniform1fv( location, value ); + var influence = activeInfluences[ i ]; + morphInfluences[ i ] = influence[ 0 ]; - } else if ( type === "fv" ) { // flat array of floats with 3 x N size (JS or typed array) + if ( influence[ 0 ] !== 0 ) { - _gl.uniform3fv( location, value ); + var index = influence[ 1 ]; - } else if ( type === "v2v" ) { // array of THREE.Vector2 + 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 ] ); - if ( uniform._array === undefined ) { + } else { - uniform._array = new Float32Array( 2 * value.length ); + if ( material.morphTargets === true ) geometry.removeAttribute( 'morphTarget' + i ); + if ( material.morphNormals === true ) geometry.removeAttribute( 'morphNormal' + i ); } - for ( i = 0, il = value.length; i < il; i ++ ) { - - offset = i * 2; + } - uniform._array[ offset ] = value[ i ].x; - uniform._array[ offset + 1 ] = value[ i ].y; + program.getUniforms().setValue( + _gl, 'morphTargetInfluences', morphInfluences ); - } + updateBuffers = true; - _gl.uniform2fv( location, uniform._array ); + } - } else if ( type === "v3v" ) { // array of THREE.Vector3 + // - if ( uniform._array === undefined ) { + var index = geometry.index; + var position = geometry.attributes.position; - uniform._array = new Float32Array( 3 * value.length ); + if ( material.wireframe === true ) { - } + index = objects.getWireframeAttribute( geometry ); - for ( i = 0, il = value.length; i < il; i ++ ) { + } - offset = i * 3; + var renderer; - uniform._array[ offset ] = value[ i ].x; - uniform._array[ offset + 1 ] = value[ i ].y; - uniform._array[ offset + 2 ] = value[ i ].z; + if ( index !== null ) { - } + renderer = indexedBufferRenderer; + renderer.setIndex( index ); - _gl.uniform3fv( location, uniform._array ); + } else { - } else if ( type === "v4v" ) { // array of THREE.Vector4 + renderer = bufferRenderer; - if ( uniform._array === undefined ) { + } - uniform._array = new Float32Array( 4 * value.length ); + if ( updateBuffers ) { - } + setupVertexAttributes( material, program, geometry ); - for ( i = 0, il = value.length; i < il; i ++ ) { + if ( index !== null ) { - offset = i * 4; + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, objects.getAttributeBuffer( index ) ); - uniform._array[ offset ] = value[ i ].x; - uniform._array[ offset + 1 ] = value[ i ].y; - uniform._array[ offset + 2 ] = value[ i ].z; - uniform._array[ offset + 3 ] = value[ i ].w; + } - } + } - _gl.uniform4fv( location, uniform._array ); + // - } else if ( type === "m4") { // single THREE.Matrix4 + var dataStart = 0; + var dataCount = Infinity; - if ( uniform._array === undefined ) { + if ( index !== null ) { - uniform._array = new Float32Array( 16 ); + dataCount = index.count; - } + } else if ( position !== undefined ) { - value.flattenToArray( uniform._array ); - _gl.uniformMatrix4fv( location, false, uniform._array ); + dataCount = position.count; - } else if ( type === "m4v" ) { // array of THREE.Matrix4 + } - if ( uniform._array === undefined ) { + var rangeStart = geometry.drawRange.start; + var rangeCount = geometry.drawRange.count; - uniform._array = new Float32Array( 16 * value.length ); + var groupStart = group !== null ? group.start : 0; + var groupCount = group !== null ? group.count : Infinity; - } + var drawStart = Math.max( dataStart, rangeStart, groupStart ); + var drawEnd = Math.min( dataStart + dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1; - for ( i = 0, il = value.length; i < il; i ++ ) { + var drawCount = Math.max( 0, drawEnd - drawStart + 1 ); - value[ i ].flattenToArrayOffset( uniform._array, i * 16 ); + // - } + if ( object instanceof THREE.Mesh ) { - _gl.uniformMatrix4fv( location, false, uniform._array ); + if ( material.wireframe === true ) { - } else if ( type === "t" ) { // single THREE.Texture (2d or cube) + state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() ); + renderer.setMode( _gl.LINES ); - texture = value; - textureUnit = getTextureUnit(); + } else { - _gl.uniform1i( location, textureUnit ); + switch ( object.drawMode ) { - if ( !texture ) continue; + case THREE.TrianglesDrawMode: + renderer.setMode( _gl.TRIANGLES ); + break; - if ( texture.image instanceof Array && texture.image.length === 6 ) { + case THREE.TriangleStripDrawMode: + renderer.setMode( _gl.TRIANGLE_STRIP ); + break; - setCubeTexture( texture, textureUnit ); + case THREE.TriangleFanDrawMode: + renderer.setMode( _gl.TRIANGLE_FAN ); + break; - } else if ( texture instanceof THREE.WebGLRenderTargetCube ) { + } - setCubeTextureDynamic( texture, textureUnit ); + } - } else { - _this.setTexture( texture, textureUnit ); + } else if ( object instanceof THREE.Line ) { - } + var lineWidth = material.linewidth; - } else if ( type === "tv" ) { // array of THREE.Texture (2d) + if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material - if ( uniform._array === undefined ) { + state.setLineWidth( lineWidth * getTargetPixelRatio() ); - uniform._array = []; + if ( object instanceof THREE.LineSegments ) { - } + renderer.setMode( _gl.LINES ); - for( i = 0, il = uniform.value.length; i < il; i ++ ) { + } else { - uniform._array[ i ] = getTextureUnit(); + renderer.setMode( _gl.LINE_STRIP ); - } + } - _gl.uniform1iv( location, uniform._array ); + } else if ( object instanceof THREE.Points ) { - for( i = 0, il = uniform.value.length; i < il; i ++ ) { + renderer.setMode( _gl.POINTS ); - texture = uniform.value[ i ]; - textureUnit = uniform._array[ i ]; + } - if ( !texture ) continue; + if ( geometry instanceof THREE.InstancedBufferGeometry ) { - _this.setTexture( texture, textureUnit ); + if ( geometry.maxInstancedCount > 0 ) { - } + renderer.renderInstances( geometry, drawStart, drawCount ); - } else { + } - console.warn( 'THREE.WebGLRenderer: Unknown uniform type: ' + type ); + } else { - } + renderer.render( drawStart, drawCount ); } }; - function setupMatrices ( object, camera ) { + function setupVertexAttributes( material, program, geometry, startIndex ) { - object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); - object._normalMatrix.getNormalMatrix( object._modelViewMatrix ); + var extension; - }; + if ( geometry instanceof THREE.InstancedBufferGeometry ) { - // + extension = extensions.get( 'ANGLE_instanced_arrays' ); - function setColorGamma( array, offset, color, intensitySq ) { + if ( extension === null ) { - array[ offset ] = color.r * color.r * intensitySq; - array[ offset + 1 ] = color.g * color.g * intensitySq; - array[ offset + 2 ] = color.b * color.b * intensitySq; + console.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); + return; - }; + } - function setColorLinear( array, offset, color, intensity ) { + } - array[ offset ] = color.r * intensity; - array[ offset + 1 ] = color.g * intensity; - array[ offset + 2 ] = color.b * intensity; + if ( startIndex === undefined ) startIndex = 0; - }; + state.initAttributes(); - function setupLights ( program, lights ) { + var geometryAttributes = geometry.attributes; - var l, ll, light, n, - r = 0, g = 0, b = 0, - color, skyColor, groundColor, - intensity, intensitySq, - position, - distance, + var programAttributes = program.getAttributes(); - zlights = _lights, + var materialDefaultAttributeValues = material.defaultAttributeValues; - dirColors = zlights.directional.colors, - dirPositions = zlights.directional.positions, + for ( var name in programAttributes ) { - pointColors = zlights.point.colors, - pointPositions = zlights.point.positions, - pointDistances = zlights.point.distances, + var programAttribute = programAttributes[ name ]; - spotColors = zlights.spot.colors, - spotPositions = zlights.spot.positions, - spotDistances = zlights.spot.distances, - spotDirections = zlights.spot.directions, - spotAnglesCos = zlights.spot.anglesCos, - spotExponents = zlights.spot.exponents, + if ( programAttribute >= 0 ) { - hemiSkyColors = zlights.hemi.skyColors, - hemiGroundColors = zlights.hemi.groundColors, - hemiPositions = zlights.hemi.positions, + var geometryAttribute = geometryAttributes[ name ]; - dirLength = 0, - pointLength = 0, - spotLength = 0, - hemiLength = 0, + if ( geometryAttribute !== undefined ) { - dirCount = 0, - pointCount = 0, - spotCount = 0, - hemiCount = 0, + var type = _gl.FLOAT; + var array = geometryAttribute.array; + var normalized = geometryAttribute.normalized; - dirOffset = 0, - pointOffset = 0, - spotOffset = 0, - hemiOffset = 0; + if ( array instanceof Float32Array ) { - for ( l = 0, ll = lights.length; l < ll; l ++ ) { + type = _gl.FLOAT; - light = lights[ l ]; + } else if ( array instanceof Float64Array ) { - if ( light.onlyShadow ) continue; + console.warn("Unsupported data buffer format: Float64Array"); - color = light.color; - intensity = light.intensity; - distance = light.distance; + } else if ( array instanceof Uint16Array ) { - if ( light instanceof THREE.AmbientLight ) { + type = _gl.UNSIGNED_SHORT; - if ( ! light.visible ) continue; + } else if ( array instanceof Int16Array ) { - if ( _this.gammaInput ) { + type = _gl.SHORT; - r += color.r * color.r; - g += color.g * color.g; - b += color.b * color.b; + } else if ( array instanceof Uint32Array ) { - } else { + type = _gl.UNSIGNED_INT; - r += color.r; - g += color.g; - b += color.b; + } else if ( array instanceof Int32Array ) { - } + type = _gl.INT; - } else if ( light instanceof THREE.DirectionalLight ) { + } else if ( array instanceof Int8Array ) { - dirCount += 1; + type = _gl.BYTE; - if ( ! light.visible ) continue; + } else if ( array instanceof Uint8Array ) { - _direction.getPositionFromMatrix( light.matrixWorld ); - _vector3.getPositionFromMatrix( light.target.matrixWorld ); - _direction.sub( _vector3 ); - _direction.normalize(); + type = _gl.UNSIGNED_BYTE; - // skip lights with undefined direction - // these create troubles in OpenGL (making pixel black) + } - if ( _direction.x === 0 && _direction.y === 0 && _direction.z === 0 ) continue; + var size = geometryAttribute.itemSize; + var buffer = objects.getAttributeBuffer( geometryAttribute ); - dirOffset = dirLength * 3; + if ( geometryAttribute instanceof THREE.InterleavedBufferAttribute ) { - dirPositions[ dirOffset ] = _direction.x; - dirPositions[ dirOffset + 1 ] = _direction.y; - dirPositions[ dirOffset + 2 ] = _direction.z; + var data = geometryAttribute.data; + var stride = data.stride; + var offset = geometryAttribute.offset; - if ( _this.gammaInput ) { + if ( data instanceof THREE.InstancedInterleavedBuffer ) { - setColorGamma( dirColors, dirOffset, color, intensity * intensity ); + state.enableAttributeAndDivisor( programAttribute, data.meshPerAttribute, extension ); - } else { + if ( geometry.maxInstancedCount === undefined ) { - setColorLinear( dirColors, dirOffset, color, intensity ); + geometry.maxInstancedCount = data.meshPerAttribute * data.count; - } + } - dirLength += 1; + } else { - } else if ( light instanceof THREE.PointLight ) { + state.enableAttribute( programAttribute ); - pointCount += 1; + } - if ( ! light.visible ) continue; + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); + _gl.vertexAttribPointer( programAttribute, size, type, normalized, stride * data.array.BYTES_PER_ELEMENT, ( startIndex * stride + offset ) * data.array.BYTES_PER_ELEMENT ); - pointOffset = pointLength * 3; + } else { - if ( _this.gammaInput ) { + if ( geometryAttribute instanceof THREE.InstancedBufferAttribute ) { - setColorGamma( pointColors, pointOffset, color, intensity * intensity ); + state.enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute, extension ); - } else { + if ( geometry.maxInstancedCount === undefined ) { - setColorLinear( pointColors, pointOffset, color, intensity ); + geometry.maxInstancedCount = geometryAttribute.meshPerAttribute * geometryAttribute.count; - } + } - _vector3.getPositionFromMatrix( light.matrixWorld ); + } else { - pointPositions[ pointOffset ] = _vector3.x; - pointPositions[ pointOffset + 1 ] = _vector3.y; - pointPositions[ pointOffset + 2 ] = _vector3.z; + state.enableAttribute( programAttribute ); - pointDistances[ pointLength ] = distance; + } - pointLength += 1; + _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer ); + _gl.vertexAttribPointer( programAttribute, size, type, normalized, 0, startIndex * size * geometryAttribute.array.BYTES_PER_ELEMENT ); - } else if ( light instanceof THREE.SpotLight ) { + } - spotCount += 1; + } else if ( materialDefaultAttributeValues !== undefined ) { - if ( ! light.visible ) continue; + var value = materialDefaultAttributeValues[ name ]; - spotOffset = spotLength * 3; + if ( value !== undefined ) { - if ( _this.gammaInput ) { + switch ( value.length ) { - setColorGamma( spotColors, spotOffset, color, intensity * intensity ); + case 2: + _gl.vertexAttrib2fv( programAttribute, value ); + break; - } else { + case 3: + _gl.vertexAttrib3fv( programAttribute, value ); + break; - setColorLinear( spotColors, spotOffset, color, intensity ); + case 4: + _gl.vertexAttrib4fv( programAttribute, value ); + break; - } - - _vector3.getPositionFromMatrix( light.matrixWorld ); + default: + _gl.vertexAttrib1fv( programAttribute, value ); - spotPositions[ spotOffset ] = _vector3.x; - spotPositions[ spotOffset + 1 ] = _vector3.y; - spotPositions[ spotOffset + 2 ] = _vector3.z; - - spotDistances[ spotLength ] = distance; + } - _direction.copy( _vector3 ); - _vector3.getPositionFromMatrix( light.target.matrixWorld ); - _direction.sub( _vector3 ); - _direction.normalize(); + } - spotDirections[ spotOffset ] = _direction.x; - spotDirections[ spotOffset + 1 ] = _direction.y; - spotDirections[ spotOffset + 2 ] = _direction.z; + } - spotAnglesCos[ spotLength ] = Math.cos( light.angle ); - spotExponents[ spotLength ] = light.exponent; + } - spotLength += 1; + } - } else if ( light instanceof THREE.HemisphereLight ) { + state.disableUnusedAttributes(); - hemiCount += 1; + } - if ( ! light.visible ) continue; + // Sorting - _direction.getPositionFromMatrix( light.matrixWorld ); - _direction.normalize(); + function absNumericalSort( a, b ) { - // skip lights with undefined direction - // these create troubles in OpenGL (making pixel black) + return Math.abs( b[ 0 ] ) - Math.abs( a[ 0 ] ); - if ( _direction.x === 0 && _direction.y === 0 && _direction.z === 0 ) continue; + } - hemiOffset = hemiLength * 3; + function painterSortStable ( a, b ) { - hemiPositions[ hemiOffset ] = _direction.x; - hemiPositions[ hemiOffset + 1 ] = _direction.y; - hemiPositions[ hemiOffset + 2 ] = _direction.z; + if ( a.object.renderOrder !== b.object.renderOrder ) { - skyColor = light.color; - groundColor = light.groundColor; + return a.object.renderOrder - b.object.renderOrder; - if ( _this.gammaInput ) { + } else if ( a.material.program && b.material.program && a.material.program !== b.material.program ) { - intensitySq = intensity * intensity; + return a.material.program.id - b.material.program.id; - setColorGamma( hemiSkyColors, hemiOffset, skyColor, intensitySq ); - setColorGamma( hemiGroundColors, hemiOffset, groundColor, intensitySq ); + } else if ( a.material.id !== b.material.id ) { - } else { + return a.material.id - b.material.id; - setColorLinear( hemiSkyColors, hemiOffset, skyColor, intensity ); - setColorLinear( hemiGroundColors, hemiOffset, groundColor, intensity ); + } else if ( a.z !== b.z ) { - } + return a.z - b.z; - hemiLength += 1; + } else { - } + return a.id - b.id; } - // null eventual remains from removed lights - // (this is to avoid if in shader) - - for ( l = dirLength * 3, ll = Math.max( dirColors.length, dirCount * 3 ); l < ll; l ++ ) dirColors[ l ] = 0.0; - for ( l = pointLength * 3, ll = Math.max( pointColors.length, pointCount * 3 ); l < ll; l ++ ) pointColors[ l ] = 0.0; - for ( l = spotLength * 3, ll = Math.max( spotColors.length, spotCount * 3 ); l < ll; l ++ ) spotColors[ l ] = 0.0; - for ( l = hemiLength * 3, ll = Math.max( hemiSkyColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiSkyColors[ l ] = 0.0; - for ( l = hemiLength * 3, ll = Math.max( hemiGroundColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiGroundColors[ l ] = 0.0; - - zlights.directional.length = dirLength; - zlights.point.length = pointLength; - zlights.spot.length = spotLength; - zlights.hemi.length = hemiLength; - - zlights.ambient[ 0 ] = r; - zlights.ambient[ 1 ] = g; - zlights.ambient[ 2 ] = b; + } - }; + function reversePainterSortStable ( a, b ) { - // GL state setting + if ( a.object.renderOrder !== b.object.renderOrder ) { - this.setFaceCulling = function ( cullFace, frontFaceDirection ) { + return a.object.renderOrder - b.object.renderOrder; - if ( cullFace === THREE.CullFaceNone ) { + } if ( a.z !== b.z ) { - _gl.disable( _gl.CULL_FACE ); + return b.z - a.z; } else { - if ( frontFaceDirection === THREE.FrontFaceDirectionCW ) { - - _gl.frontFace( _gl.CW ); - - } else { - - _gl.frontFace( _gl.CCW ); - - } - - if ( cullFace === THREE.CullFaceBack ) { - - _gl.cullFace( _gl.BACK ); + return a.id - b.id; - } else if ( cullFace === THREE.CullFaceFront ) { + } - _gl.cullFace( _gl.FRONT ); + } - } else { + // Rendering - _gl.cullFace( _gl.FRONT_AND_BACK ); + this.render = function ( scene, camera, renderTarget, forceClear ) { - } + if ( camera instanceof THREE.Camera === false ) { - _gl.enable( _gl.CULL_FACE ); + console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); + return; } - }; - - this.setMaterialFaces = function ( material ) { + var fog = scene.fog; - var doubleSided = material.side === THREE.DoubleSide; - var flipSided = material.side === THREE.BackSide; + // reset caching for this frame - if ( _oldDoubleSided !== doubleSided ) { + _currentGeometryProgram = ''; + _currentMaterialId = - 1; + _currentCamera = null; - if ( doubleSided ) { + // update scene graph - _gl.disable( _gl.CULL_FACE ); + if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); - } else { + // update camera matrices and frustum - _gl.enable( _gl.CULL_FACE ); + if ( camera.parent === null ) camera.updateMatrixWorld(); - } + camera.matrixWorldInverse.getInverse( camera.matrixWorld ); - _oldDoubleSided = doubleSided; + _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); + _frustum.setFromMatrix( _projScreenMatrix ); - } + lights.length = 0; - if ( _oldFlipSided !== flipSided ) { + opaqueObjectsLastIndex = - 1; + transparentObjectsLastIndex = - 1; - if ( flipSided ) { + sprites.length = 0; + lensFlares.length = 0; - _gl.frontFace( _gl.CW ); + _localClippingEnabled = this.localClippingEnabled; + _clippingEnabled = _clipping.init( this.clippingPlanes, _localClippingEnabled, camera ); - } else { + projectObject( scene, camera ); - _gl.frontFace( _gl.CCW ); + opaqueObjects.length = opaqueObjectsLastIndex + 1; + transparentObjects.length = transparentObjectsLastIndex + 1; - } + if ( _this.sortObjects === true ) { - _oldFlipSided = flipSided; + opaqueObjects.sort( painterSortStable ); + transparentObjects.sort( reversePainterSortStable ); } - }; - - this.setDepthTest = function ( depthTest ) { - - if ( _oldDepthTest !== depthTest ) { + // - if ( depthTest ) { + if ( _clippingEnabled ) _clipping.beginShadows(); - _gl.enable( _gl.DEPTH_TEST ); + setupShadows( lights ); - } else { + shadowMap.render( scene, camera ); - _gl.disable( _gl.DEPTH_TEST ); + setupLights( lights, camera ); - } + if ( _clippingEnabled ) _clipping.endShadows(); - _oldDepthTest = depthTest; + // - } + _infoRender.calls = 0; + _infoRender.vertices = 0; + _infoRender.faces = 0; + _infoRender.points = 0; - }; + if ( renderTarget === undefined ) { - this.setDepthWrite = function ( depthWrite ) { + renderTarget = null; - if ( _oldDepthWrite !== depthWrite ) { + } - _gl.depthMask( depthWrite ); - _oldDepthWrite = depthWrite; + this.setRenderTarget( renderTarget ); - } + // - }; + var background = scene.background; - function setLineWidth ( width ) { + if ( background === null ) { - if ( width !== _oldLineWidth ) { + glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); - _gl.lineWidth( width ); + } else if ( background instanceof THREE.Color ) { - _oldLineWidth = width; + glClearColor( background.r, background.g, background.b, 1 ); } - }; + if ( this.autoClear || forceClear ) { - function setPolygonOffset ( polygonoffset, factor, units ) { + this.clear( this.autoClearColor, this.autoClearDepth, this.autoClearStencil ); - if ( _oldPolygonOffset !== polygonoffset ) { + } - if ( polygonoffset ) { + if ( background instanceof THREE.CubeTexture ) { - _gl.enable( _gl.POLYGON_OFFSET_FILL ); + backgroundCamera2.projectionMatrix.copy( camera.projectionMatrix ); - } else { + backgroundCamera2.matrixWorld.extractRotation( camera.matrixWorld ); + backgroundCamera2.matrixWorldInverse.getInverse( backgroundCamera2.matrixWorld ); - _gl.disable( _gl.POLYGON_OFFSET_FILL ); + backgroundBoxMesh.material.uniforms[ "tCube" ].value = background; + backgroundBoxMesh.modelViewMatrix.multiplyMatrices( backgroundCamera2.matrixWorldInverse, backgroundBoxMesh.matrixWorld ); - } + objects.update( backgroundBoxMesh ); - _oldPolygonOffset = polygonoffset; + _this.renderBufferDirect( backgroundCamera2, null, backgroundBoxMesh.geometry, backgroundBoxMesh.material, backgroundBoxMesh, null ); - } + } else if ( background instanceof THREE.Texture ) { - if ( polygonoffset && ( _oldPolygonOffsetFactor !== factor || _oldPolygonOffsetUnits !== units ) ) { + backgroundPlaneMesh.material.map = background; - _gl.polygonOffset( factor, units ); + objects.update( backgroundPlaneMesh ); - _oldPolygonOffsetFactor = factor; - _oldPolygonOffsetUnits = units; + _this.renderBufferDirect( backgroundCamera, null, backgroundPlaneMesh.geometry, backgroundPlaneMesh.material, backgroundPlaneMesh, null ); } - }; - - this.setBlending = function ( blending, blendEquation, blendSrc, blendDst ) { - - if ( blending !== _oldBlending ) { + // - if ( blending === THREE.NoBlending ) { + if ( scene.overrideMaterial ) { - _gl.disable( _gl.BLEND ); + var overrideMaterial = scene.overrideMaterial; - } else if ( blending === THREE.AdditiveBlending ) { + renderObjects( opaqueObjects, camera, fog, overrideMaterial ); + renderObjects( transparentObjects, camera, fog, overrideMaterial ); - _gl.enable( _gl.BLEND ); - _gl.blendEquation( _gl.FUNC_ADD ); - _gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE ); + } else { - } else if ( blending === THREE.SubtractiveBlending ) { + // opaque pass (front-to-back order) - // TODO: Find blendFuncSeparate() combination - _gl.enable( _gl.BLEND ); - _gl.blendEquation( _gl.FUNC_ADD ); - _gl.blendFunc( _gl.ZERO, _gl.ONE_MINUS_SRC_COLOR ); + state.setBlending( THREE.NoBlending ); + renderObjects( opaqueObjects, camera, fog ); - } else if ( blending === THREE.MultiplyBlending ) { + // transparent pass (back-to-front order) - // TODO: Find blendFuncSeparate() combination - _gl.enable( _gl.BLEND ); - _gl.blendEquation( _gl.FUNC_ADD ); - _gl.blendFunc( _gl.ZERO, _gl.SRC_COLOR ); + renderObjects( transparentObjects, camera, fog ); - } else if ( blending === THREE.CustomBlending ) { + } - _gl.enable( _gl.BLEND ); + // custom render plugins (post pass) - } else { + spritePlugin.render( scene, camera ); + lensFlarePlugin.render( scene, camera, _currentViewport ); - _gl.enable( _gl.BLEND ); - _gl.blendEquationSeparate( _gl.FUNC_ADD, _gl.FUNC_ADD ); - _gl.blendFuncSeparate( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA, _gl.ONE, _gl.ONE_MINUS_SRC_ALPHA ); + // Generate mipmap if we're using any kind of mipmap filtering - } + if ( renderTarget ) { - _oldBlending = blending; + textures.updateRenderTargetMipmap( renderTarget ); } - if ( blending === THREE.CustomBlending ) { + // Ensure depth buffer writing is enabled so it can be cleared on next render - if ( blendEquation !== _oldBlendEquation ) { + state.setDepthTest( true ); + state.setDepthWrite( true ); + state.setColorWrite( true ); - _gl.blendEquation( paramThreeToGL( blendEquation ) ); + // _gl.finish(); - _oldBlendEquation = blendEquation; + }; - } + function pushRenderItem( object, geometry, material, z, group ) { - if ( blendSrc !== _oldBlendSrc || blendDst !== _oldBlendDst ) { + var array, index; - _gl.blendFunc( paramThreeToGL( blendSrc ), paramThreeToGL( blendDst ) ); + // allocate the next position in the appropriate array - _oldBlendSrc = blendSrc; - _oldBlendDst = blendDst; + if ( material.transparent ) { - } + array = transparentObjects; + index = ++ transparentObjectsLastIndex; } else { - _oldBlendEquation = null; - _oldBlendSrc = null; - _oldBlendDst = null; + array = opaqueObjects; + index = ++ opaqueObjectsLastIndex; } - }; + // recycle existing render item or grow the array - // Defines + var renderItem = array[ index ]; - function generateDefines ( defines ) { + if ( renderItem !== undefined ) { - var value, chunk, chunks = []; + renderItem.id = object.id; + renderItem.object = object; + renderItem.geometry = geometry; + renderItem.material = material; + renderItem.z = _vector3.z; + renderItem.group = group; - for ( var d in defines ) { + } else { - value = defines[ d ]; - if ( value === false ) continue; + renderItem = { + id: object.id, + object: object, + geometry: geometry, + material: material, + z: _vector3.z, + group: group + }; - chunk = "#define " + d + " " + value; - chunks.push( chunk ); + // assert( index === array.length ); + array.push( renderItem ); } - return chunks.join( "\n" ); + } - }; + // TODO Duplicated code (Frustum) - // Shaders + function isObjectViewable( object ) { - function buildProgram ( shaderID, fragmentShader, vertexShader, uniforms, attributes, defines, parameters, index0AttributeName ) { + var geometry = object.geometry; - var p, pl, d, program, code; - var chunks = []; + if ( geometry.boundingSphere === null ) + geometry.computeBoundingSphere(); - // Generate code + _sphere.copy( geometry.boundingSphere ). + applyMatrix4( object.matrixWorld ); - if ( shaderID ) { + return isSphereViewable( _sphere ); - chunks.push( shaderID ); + } - } else { + function isSpriteViewable( sprite ) { - chunks.push( fragmentShader ); - chunks.push( vertexShader ); + _sphere.center.set( 0, 0, 0 ); + _sphere.radius = 0.7071067811865476; + _sphere.applyMatrix4( sprite.matrixWorld ); - } + return isSphereViewable( _sphere ); - for ( d in defines ) { + } - chunks.push( d ); - chunks.push( defines[ d ] ); + function isSphereViewable( sphere ) { - } + if ( ! _frustum.intersectsSphere( sphere ) ) return false; - for ( p in parameters ) { + var numPlanes = _clipping.numPlanes; - chunks.push( p ); - chunks.push( parameters[ p ] ); + if ( numPlanes === 0 ) return true; - } + var planes = _this.clippingPlanes, - code = chunks.join(); + center = sphere.center, + negRad = - sphere.radius, + i = 0; - // Check if code has been already compiled + do { - for ( p = 0, pl = _programs.length; p < pl; p ++ ) { + // out when deeper than radius in the negative halfspace + if ( planes[ i ].distanceToPoint( center ) < negRad ) return false; - var programInfo = _programs[ p ]; + } while ( ++ i !== numPlanes ); - if ( programInfo.code === code ) { + return true; - // console.log( "Code already compiled." /*: \n\n" + code*/ ); + } - programInfo.usedTimes ++; + function projectObject( object, camera ) { - return programInfo.program; + if ( object.visible === false ) return; - } + if ( object.layers.test( camera.layers ) ) { - } + if ( object instanceof THREE.Light ) { - var shadowMapTypeDefine = "SHADOWMAP_TYPE_BASIC"; + lights.push( object ); - if ( parameters.shadowMapType === THREE.PCFShadowMap ) { + } else if ( object instanceof THREE.Sprite ) { - shadowMapTypeDefine = "SHADOWMAP_TYPE_PCF"; + if ( object.frustumCulled === false || isSpriteViewable( object ) === true ) { - } else if ( parameters.shadowMapType === THREE.PCFSoftShadowMap ) { + sprites.push( object ); - shadowMapTypeDefine = "SHADOWMAP_TYPE_PCF_SOFT"; + } - } + } else if ( object instanceof THREE.LensFlare ) { - // console.log( "building new program " ); + lensFlares.push( object ); - // + } else if ( object instanceof THREE.ImmediateRenderObject ) { - var customDefines = generateDefines( defines ); + if ( _this.sortObjects === true ) { - // + _vector3.setFromMatrixPosition( object.matrixWorld ); + _vector3.applyProjection( _projScreenMatrix ); - program = _gl.createProgram(); + } - var prefix_vertex = [ + pushRenderItem( object, null, object.material, _vector3.z, null ); - "precision " + _precision + " float;", - "precision " + _precision + " int;", + } else if ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Points ) { - customDefines, + if ( object instanceof THREE.SkinnedMesh ) { - _supportsVertexTextures ? "#define VERTEX_TEXTURES" : "", + object.skeleton.update(); - _this.gammaInput ? "#define GAMMA_INPUT" : "", - _this.gammaOutput ? "#define GAMMA_OUTPUT" : "", - _this.physicallyBasedShading ? "#define PHYSICALLY_BASED_SHADING" : "", + } - "#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 ( object.frustumCulled === false || isObjectViewable( object ) === true ) { - "#define MAX_SHADOWS " + parameters.maxShadows, + var material = object.material; - "#define MAX_BONES " + parameters.maxBones, + if ( material.visible === true ) { - parameters.map ? "#define USE_MAP" : "", - parameters.envMap ? "#define USE_ENVMAP" : "", - parameters.lightMap ? "#define USE_LIGHTMAP" : "", - parameters.bumpMap ? "#define USE_BUMPMAP" : "", - parameters.normalMap ? "#define USE_NORMALMAP" : "", - parameters.specularMap ? "#define USE_SPECULARMAP" : "", - parameters.vertexColors ? "#define USE_COLOR" : "", + if ( _this.sortObjects === true ) { - parameters.skinning ? "#define USE_SKINNING" : "", - parameters.useVertexTexture ? "#define BONE_TEXTURE" : "", - parameters.boneTextureWidth ? "#define N_BONE_PIXEL_X " + parameters.boneTextureWidth.toFixed( 1 ) : "", - parameters.boneTextureHeight ? "#define N_BONE_PIXEL_Y " + parameters.boneTextureHeight.toFixed( 1 ) : "", + _vector3.setFromMatrixPosition( object.matrixWorld ); + _vector3.applyProjection( _projScreenMatrix ); - parameters.morphTargets ? "#define USE_MORPHTARGETS" : "", - parameters.morphNormals ? "#define USE_MORPHNORMALS" : "", - parameters.perPixel ? "#define PHONG_PER_PIXEL" : "", - parameters.wrapAround ? "#define WRAP_AROUND" : "", - parameters.doubleSided ? "#define DOUBLE_SIDED" : "", - parameters.flipSided ? "#define FLIP_SIDED" : "", + } - parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", - parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", - parameters.shadowMapDebug ? "#define SHADOWMAP_DEBUG" : "", - parameters.shadowMapCascade ? "#define SHADOWMAP_CASCADE" : "", + var geometry = objects.update( object ); - parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "", + if ( material instanceof THREE.MultiMaterial ) { - "uniform mat4 modelMatrix;", - "uniform mat4 modelViewMatrix;", - "uniform mat4 projectionMatrix;", - "uniform mat4 viewMatrix;", - "uniform mat3 normalMatrix;", - "uniform vec3 cameraPosition;", + var groups = geometry.groups; + var materials = material.materials; - "attribute vec3 position;", - "attribute vec3 normal;", - "attribute vec2 uv;", - "attribute vec2 uv2;", + for ( var i = 0, l = groups.length; i < l; i ++ ) { - "#ifdef USE_COLOR", + var group = groups[ i ]; + var groupMaterial = materials[ group.materialIndex ]; - "attribute vec3 color;", + if ( groupMaterial.visible === true ) { - "#endif", + pushRenderItem( object, geometry, groupMaterial, _vector3.z, group ); - "#ifdef USE_MORPHTARGETS", + } - "attribute vec3 morphTarget0;", - "attribute vec3 morphTarget1;", - "attribute vec3 morphTarget2;", - "attribute vec3 morphTarget3;", + } - "#ifdef USE_MORPHNORMALS", + } else { - "attribute vec3 morphNormal0;", - "attribute vec3 morphNormal1;", - "attribute vec3 morphNormal2;", - "attribute vec3 morphNormal3;", + pushRenderItem( object, geometry, material, _vector3.z, null ); - "#else", + } - "attribute vec3 morphTarget4;", - "attribute vec3 morphTarget5;", - "attribute vec3 morphTarget6;", - "attribute vec3 morphTarget7;", + } - "#endif", + } - "#endif", + } - "#ifdef USE_SKINNING", + } - "attribute vec4 skinIndex;", - "attribute vec4 skinWeight;", + var children = object.children; - "#endif", + for ( var i = 0, l = children.length; i < l; i ++ ) { - "" + projectObject( children[ i ], camera ); - ].join("\n"); + } - var prefix_fragment = [ + } - "precision " + _precision + " float;", - "precision " + _precision + " int;", + function renderObjects( renderList, camera, fog, overrideMaterial ) { - ( parameters.bumpMap || parameters.normalMap ) ? "#extension GL_OES_standard_derivatives : enable" : "", + for ( var i = 0, l = renderList.length; i < l; i ++ ) { - customDefines, + var renderItem = renderList[ i ]; - "#define MAX_DIR_LIGHTS " + parameters.maxDirLights, - "#define MAX_POINT_LIGHTS " + parameters.maxPointLights, - "#define MAX_SPOT_LIGHTS " + parameters.maxSpotLights, - "#define MAX_HEMI_LIGHTS " + parameters.maxHemiLights, + var object = renderItem.object; + var geometry = renderItem.geometry; + var material = overrideMaterial === undefined ? renderItem.material : overrideMaterial; + var group = renderItem.group; - "#define MAX_SHADOWS " + parameters.maxShadows, + object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + object.normalMatrix.getNormalMatrix( object.modelViewMatrix ); - parameters.alphaTest ? "#define ALPHATEST " + parameters.alphaTest: "", + if ( object instanceof THREE.ImmediateRenderObject ) { - _this.gammaInput ? "#define GAMMA_INPUT" : "", - _this.gammaOutput ? "#define GAMMA_OUTPUT" : "", - _this.physicallyBasedShading ? "#define PHYSICALLY_BASED_SHADING" : "", + setMaterial( material ); - ( parameters.useFog && parameters.fog ) ? "#define USE_FOG" : "", - ( parameters.useFog && parameters.fogExp ) ? "#define FOG_EXP2" : "", + var program = setProgram( camera, fog, material, object ); - parameters.map ? "#define USE_MAP" : "", - parameters.envMap ? "#define USE_ENVMAP" : "", - parameters.lightMap ? "#define USE_LIGHTMAP" : "", - parameters.bumpMap ? "#define USE_BUMPMAP" : "", - parameters.normalMap ? "#define USE_NORMALMAP" : "", - parameters.specularMap ? "#define USE_SPECULARMAP" : "", - parameters.vertexColors ? "#define USE_COLOR" : "", + _currentGeometryProgram = ''; - parameters.metal ? "#define METAL" : "", - parameters.perPixel ? "#define PHONG_PER_PIXEL" : "", - parameters.wrapAround ? "#define WRAP_AROUND" : "", - parameters.doubleSided ? "#define DOUBLE_SIDED" : "", - parameters.flipSided ? "#define FLIP_SIDED" : "", + object.render( function ( object ) { - parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", - parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "", - parameters.shadowMapDebug ? "#define SHADOWMAP_DEBUG" : "", - parameters.shadowMapCascade ? "#define SHADOWMAP_CASCADE" : "", + _this.renderBufferImmediate( object, program, material ); - "uniform mat4 viewMatrix;", - "uniform vec3 cameraPosition;", - "" + } ); - ].join("\n"); + } else { - var glVertexShader = getShader( "vertex", prefix_vertex + vertexShader ); - var glFragmentShader = getShader( "fragment", prefix_fragment + fragmentShader ); + _this.renderBufferDirect( camera, fog, geometry, material, object, group ); - _gl.attachShader( program, glVertexShader ); - _gl.attachShader( program, glFragmentShader ); + } - //Force a particular attribute to index 0. - // because potentially expensive emulation is done by browser if attribute 0 is disabled. - //And, color, for example is often automatically bound to index 0 so disabling it - if ( index0AttributeName ) { - _gl.bindAttribLocation( program, 0, index0AttributeName ); } - _gl.linkProgram( program ); - - if ( !_gl.getProgramParameter( program, _gl.LINK_STATUS ) ) { - - console.error( "Could not initialise shader\n" + "VALIDATE_STATUS: " + _gl.getProgramParameter( program, _gl.VALIDATE_STATUS ) + ", gl error [" + _gl.getError() + "]" ); - console.error( "Program Info Log: " + _gl.getProgramInfoLog( program ) ); - } + } - // clean up + function initMaterial( material, fog, object ) { - _gl.deleteShader( glFragmentShader ); - _gl.deleteShader( glVertexShader ); + var materialProperties = properties.get( material ); - // console.log( prefix_fragment + fragmentShader ); - // console.log( prefix_vertex + vertexShader ); + var parameters = programCache.getParameters( + material, _lights, fog, _clipping.numPlanes, object ); - program.uniforms = {}; - program.attributes = {}; + var code = programCache.getProgramCode( material, parameters ); - var identifiers, u, a, i; + var program = materialProperties.program; + var programChange = true; - // cache uniform locations + if ( program === undefined ) { - identifiers = [ + // new material + material.addEventListener( 'dispose', onMaterialDispose ); - 'viewMatrix', 'modelViewMatrix', 'projectionMatrix', 'normalMatrix', 'modelMatrix', 'cameraPosition', - 'morphTargetInfluences' + } else if ( program.code !== code ) { - ]; + // changed glsl or parameters + releaseMaterialProgramReference( material ); - if ( parameters.useVertexTexture ) { + } else if ( parameters.shaderID !== undefined ) { - identifiers.push( 'boneTexture' ); + // same glsl and uniform list + return; } else { - identifiers.push( 'boneGlobalMatrices' ); + // only rebuild uniform list + programChange = false; } - for ( u in uniforms ) { + if ( programChange ) { - identifiers.push( u ); + if ( parameters.shaderID ) { - } + var shader = THREE.ShaderLib[ parameters.shaderID ]; - cacheUniformLocations( program, identifiers ); + materialProperties.__webglShader = { + name: material.type, + uniforms: THREE.UniformsUtils.clone( shader.uniforms ), + vertexShader: shader.vertexShader, + fragmentShader: shader.fragmentShader + }; - // cache attributes locations + } else { - identifiers = [ + materialProperties.__webglShader = { + name: material.type, + uniforms: material.uniforms, + vertexShader: material.vertexShader, + fragmentShader: material.fragmentShader + }; - "position", "normal", "uv", "uv2", "tangent", "color", - "skinIndex", "skinWeight", "lineDistance" + } - ]; + material.__webglShader = materialProperties.__webglShader; - for ( i = 0; i < parameters.maxMorphTargets; i ++ ) { + program = programCache.acquireProgram( material, parameters, code ); - identifiers.push( "morphTarget" + i ); + materialProperties.program = program; + material.program = program; } - for ( i = 0; i < parameters.maxMorphNormals; i ++ ) { + var attributes = program.getAttributes(); - identifiers.push( "morphNormal" + i ); - - } + if ( material.morphTargets ) { - for ( a in attributes ) { + material.numSupportedMorphTargets = 0; - identifiers.push( a ); + for ( var i = 0; i < _this.maxMorphTargets; i ++ ) { - } + if ( attributes[ 'morphTarget' + i ] >= 0 ) { - cacheAttributeLocations( program, identifiers ); + material.numSupportedMorphTargets ++; - program.id = _programs_counter ++; + } - _programs.push( { program: program, code: code, usedTimes: 1 } ); + } - _this.info.memory.programs = _programs.length; + } - return program; + if ( material.morphNormals ) { - }; + material.numSupportedMorphNormals = 0; - // Shader parameters cache + for ( var i = 0; i < _this.maxMorphNormals; i ++ ) { - function cacheUniformLocations ( program, identifiers ) { + if ( attributes[ 'morphNormal' + i ] >= 0 ) { - var i, l, id; + material.numSupportedMorphNormals ++; - for( i = 0, l = identifiers.length; i < l; i ++ ) { + } - id = identifiers[ i ]; - program.uniforms[ id ] = _gl.getUniformLocation( program, id ); + } } - }; - - function cacheAttributeLocations ( program, identifiers ) { + var uniforms = materialProperties.__webglShader.uniforms; - var i, l, id; + if ( ! ( material instanceof THREE.ShaderMaterial ) && + ! ( material instanceof THREE.RawShaderMaterial ) || + material.clipping === true ) { - for( i = 0, l = identifiers.length; i < l; i ++ ) { - - id = identifiers[ i ]; - program.attributes[ id ] = _gl.getAttribLocation( program, id ); + materialProperties.numClippingPlanes = _clipping.numPlanes; + uniforms.clippingPlanes = _clipping.uniform; } - }; + if ( material.lights ) { - function addLineNumbers ( string ) { + // store the light setup it was created for - var chunks = string.split( "\n" ); + materialProperties.lightsHash = _lights.hash; - for ( var i = 0, il = chunks.length; i < il; i ++ ) { + // wire up the material to this renderer's lighting state - // Chrome reports shader errors on lines - // starting counting from 1 + uniforms.ambientLightColor.value = _lights.ambient; + uniforms.directionalLights.value = _lights.directional; + uniforms.spotLights.value = _lights.spot; + uniforms.pointLights.value = _lights.point; + uniforms.hemisphereLights.value = _lights.hemi; - chunks[ i ] = ( i + 1 ) + ": " + chunks[ i ]; + uniforms.directionalShadowMap.value = _lights.directionalShadowMap; + uniforms.directionalShadowMatrix.value = _lights.directionalShadowMatrix; + uniforms.spotShadowMap.value = _lights.spotShadowMap; + uniforms.spotShadowMatrix.value = _lights.spotShadowMatrix; + uniforms.pointShadowMap.value = _lights.pointShadowMap; + uniforms.pointShadowMatrix.value = _lights.pointShadowMatrix; } - return chunks.join( "\n" ); + var progUniforms = materialProperties.program.getUniforms(), + uniformsList = + THREE.WebGLUniforms.seqWithValue( progUniforms.seq, uniforms ); - }; + materialProperties.uniformsList = uniformsList; + materialProperties.dynamicUniforms = + THREE.WebGLUniforms.splitDynamic( uniformsList, uniforms ); - function getShader ( type, string ) { + } - var shader; + function setMaterial( material ) { - if ( type === "fragment" ) { + if ( material.side !== THREE.DoubleSide ) + state.enable( _gl.CULL_FACE ); + else + state.disable( _gl.CULL_FACE ); - shader = _gl.createShader( _gl.FRAGMENT_SHADER ); + state.setFlipSided( material.side === THREE.BackSide ); - } else if ( type === "vertex" ) { + if ( material.transparent === true ) { - shader = _gl.createShader( _gl.VERTEX_SHADER ); + state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha ); - } + } else { - _gl.shaderSource( shader, string ); - _gl.compileShader( shader ); + state.setBlending( THREE.NoBlending ); - if ( !_gl.getShaderParameter( shader, _gl.COMPILE_STATUS ) ) { + } - console.error( _gl.getShaderInfoLog( shader ) ); - console.error( addLineNumbers( string ) ); - return null; + state.setDepthFunc( material.depthFunc ); + state.setDepthTest( material.depthTest ); + state.setDepthWrite( material.depthWrite ); + state.setColorWrite( material.colorWrite ); + state.setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); - } + } - return shader; + function setProgram( camera, fog, material, object ) { - }; + _usedTextureUnits = 0; - // Textures + var materialProperties = properties.get( material ); + if ( _clippingEnabled ) { - function isPowerOfTwo ( value ) { + if ( _localClippingEnabled || camera !== _currentCamera ) { - return ( value & ( value - 1 ) ) === 0; + var useCache = + camera === _currentCamera && + material.id === _currentMaterialId; - }; + // we might want to call this function with some ClippingGroup + // object instead of the material, once it becomes feasible + // (#8465, #8379) + _clipping.setState( + material.clippingPlanes, material.clipShadows, + camera, materialProperties, useCache ); - function setTextureParameters ( textureType, texture, isImagePowerOfTwo ) { + } - if ( isImagePowerOfTwo ) { + if ( materialProperties.numClippingPlanes !== undefined && + materialProperties.numClippingPlanes !== _clipping.numPlanes ) { - _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, paramThreeToGL( texture.wrapS ) ); - _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, paramThreeToGL( texture.wrapT ) ); + material.needsUpdate = true; - _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, paramThreeToGL( texture.magFilter ) ); - _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, paramThreeToGL( texture.minFilter ) ); + } - } else { + } - _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); - _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); + if ( materialProperties.program === undefined ) { - _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) ); - _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) ); + material.needsUpdate = true; } - if ( _glExtensionTextureFilterAnisotropic && texture.type !== THREE.FloatType ) { + if ( materialProperties.lightsHash !== undefined && + materialProperties.lightsHash !== _lights.hash ) { + + material.needsUpdate = true; - if ( texture.anisotropy > 1 || texture.__oldAnisotropy ) { + } - _gl.texParameterf( textureType, _glExtensionTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, _maxAnisotropy ) ); - texture.__oldAnisotropy = texture.anisotropy; + if ( material.needsUpdate ) { - } + initMaterial( material, fog, object ); + material.needsUpdate = false; } - }; + var refreshProgram = false; + var refreshMaterial = false; + var refreshLights = false; - this.setTexture = function ( texture, slot ) { + var program = materialProperties.program, + p_uniforms = program.getUniforms(), + m_uniforms = materialProperties.__webglShader.uniforms; - if ( texture.needsUpdate ) { + if ( program.id !== _currentProgram ) { - if ( ! texture.__webglInit ) { + _gl.useProgram( program.program ); + _currentProgram = program.id; - texture.__webglInit = true; + refreshProgram = true; + refreshMaterial = true; + refreshLights = true; - texture.addEventListener( 'dispose', onTextureDispose ); + } - texture.__webglTexture = _gl.createTexture(); + if ( material.id !== _currentMaterialId ) { - _this.info.memory.textures ++; + _currentMaterialId = material.id; - } + refreshMaterial = true; - _gl.activeTexture( _gl.TEXTURE0 + slot ); - _gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture ); + } + + if ( refreshProgram || camera !== _currentCamera ) { - _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 ); + p_uniforms.set( _gl, camera, 'projectionMatrix' ); - var image = texture.image, - isImagePowerOfTwo = isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ), - glFormat = paramThreeToGL( texture.format ), - glType = paramThreeToGL( texture.type ); + if ( capabilities.logarithmicDepthBuffer ) { - setTextureParameters( _gl.TEXTURE_2D, texture, isImagePowerOfTwo ); + p_uniforms.setValue( _gl, 'logDepthBufFC', + 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); - var mipmap, mipmaps = texture.mipmaps; + } - if ( texture instanceof THREE.DataTexture ) { - // use manually created mipmaps if available - // if there are no manual mipmaps - // set 0 level mipmap and then use GL to generate other mipmap levels + if ( camera !== _currentCamera ) { - if ( mipmaps.length > 0 && isImagePowerOfTwo ) { + _currentCamera = camera; - for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { + // lighting uniforms depend on the camera so enforce an update + // now, in case this material supports lights - or later, when + // the next material that does gets activated: - mipmap = mipmaps[ i ]; - _gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + refreshMaterial = true; // set to true on material change + refreshLights = true; // remains set until update done - } + } - texture.generateMipmaps = false; + // load material specific uniforms + // (shader material also gets them for the sake of genericity) - } else { + if ( material instanceof THREE.ShaderMaterial || + material instanceof THREE.MeshPhongMaterial || + material instanceof THREE.MeshStandardMaterial || + material.envMap ) { + + var uCamPos = p_uniforms.map.cameraPosition; - _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data ); + if ( uCamPos !== undefined ) { + + uCamPos.setValue( _gl, + _vector3.setFromMatrixPosition( camera.matrixWorld ) ); } - } else if ( texture instanceof THREE.CompressedTexture ) { + } + + if ( material instanceof THREE.MeshPhongMaterial || + material instanceof THREE.MeshLambertMaterial || + material instanceof THREE.MeshBasicMaterial || + material instanceof THREE.MeshStandardMaterial || + material instanceof THREE.ShaderMaterial || + material.skinning ) { - // compressed textures can only use manually created mipmaps - // WebGL can't generate mipmaps for DDS textures + p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse ); - for( var i = 0, il = mipmaps.length; i < il; i ++ ) { + } - mipmap = mipmaps[ i ]; - _gl.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + p_uniforms.set( _gl, _this, 'toneMappingExposure' ); + p_uniforms.set( _gl, _this, 'toneMappingWhitePoint' ); - } + } - } else { // regular Texture (image, video, canvas) + // 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 - // 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 ( material.skinning ) { - if ( mipmaps.length > 0 && isImagePowerOfTwo ) { + p_uniforms.setOptional( _gl, object, 'bindMatrix' ); + p_uniforms.setOptional( _gl, object, 'bindMatrixInverse' ); - for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { + var skeleton = object.skeleton; - mipmap = mipmaps[ i ]; - _gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap ); + if ( skeleton ) { - } + if ( capabilities.floatVertexTextures && skeleton.useVertexTexture ) { - texture.generateMipmaps = false; + p_uniforms.set( _gl, skeleton, 'boneTexture' ); + p_uniforms.set( _gl, skeleton, 'boneTextureWidth' ); + p_uniforms.set( _gl, skeleton, 'boneTextureHeight' ); } else { - _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, texture.image ); + p_uniforms.setOptional( _gl, skeleton, 'boneMatrices' ); } } - if ( texture.generateMipmaps && isImagePowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); + } - texture.needsUpdate = false; + if ( refreshMaterial ) { - if ( texture.onUpdate ) texture.onUpdate(); + if ( material.lights ) { - } else { + // the current material requires lighting info - _gl.activeTexture( _gl.TEXTURE0 + slot ); - _gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture ); + // note: all lighting uniforms are always set correctly + // they simply reference the renderer's state for their + // values + // + // use the current material's .needsUpdate flags to set + // the GL state when required - } + markUniformsLightsNeedsUpdate( m_uniforms, refreshLights ); - }; + } - function clampToMaxSize ( image, maxSize ) { + // refresh uniforms common to several materials - if ( image.width <= maxSize && image.height <= maxSize ) { + if ( fog && material.fog ) { - return image; + refreshUniformsFog( m_uniforms, fog ); - } + } - // Warning: Scaling through the canvas will only work with images that use - // premultiplied alpha. + if ( material instanceof THREE.MeshBasicMaterial || + material instanceof THREE.MeshLambertMaterial || + material instanceof THREE.MeshPhongMaterial || + material instanceof THREE.MeshStandardMaterial || + material instanceof THREE.MeshDepthMaterial ) { - var maxDimension = Math.max( image.width, image.height ); - var newWidth = Math.floor( image.width * maxSize / maxDimension ); - var newHeight = Math.floor( image.height * maxSize / maxDimension ); + refreshUniformsCommon( m_uniforms, material ); - var canvas = document.createElement( 'canvas' ); - canvas.width = newWidth; - canvas.height = newHeight; + } - var ctx = canvas.getContext( "2d" ); - ctx.drawImage( image, 0, 0, image.width, image.height, 0, 0, newWidth, newHeight ); + // refresh single material specific uniforms - return canvas; + if ( material instanceof THREE.LineBasicMaterial ) { - } + refreshUniformsLine( m_uniforms, material ); - function setCubeTexture ( texture, slot ) { + } else if ( material instanceof THREE.LineDashedMaterial ) { - if ( texture.image.length === 6 ) { + refreshUniformsLine( m_uniforms, material ); + refreshUniformsDash( m_uniforms, material ); - if ( texture.needsUpdate ) { + } else if ( material instanceof THREE.PointsMaterial ) { - if ( ! texture.image.__webglTextureCube ) { + refreshUniformsPoints( m_uniforms, material ); - texture.addEventListener( 'dispose', onTextureDispose ); + } else if ( material instanceof THREE.MeshLambertMaterial ) { - texture.image.__webglTextureCube = _gl.createTexture(); + refreshUniformsLambert( m_uniforms, material ); - _this.info.memory.textures ++; + } else if ( material instanceof THREE.MeshPhongMaterial ) { - } + refreshUniformsPhong( m_uniforms, material ); - _gl.activeTexture( _gl.TEXTURE0 + slot ); - _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube ); + } else if ( material instanceof THREE.MeshPhysicalMaterial ) { - _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); + refreshUniformsPhysical( m_uniforms, material ); - var isCompressed = texture instanceof THREE.CompressedTexture; + } else if ( material instanceof THREE.MeshStandardMaterial ) { - var cubeImage = []; + refreshUniformsStandard( m_uniforms, material ); - for ( var i = 0; i < 6; i ++ ) { + } else if ( material instanceof THREE.MeshDepthMaterial ) { - if ( _this.autoScaleCubemaps && ! isCompressed ) { + if ( material.displacementMap ) { - cubeImage[ i ] = clampToMaxSize( texture.image[ i ], _maxCubemapSize ); + m_uniforms.displacementMap.value = material.displacementMap; + m_uniforms.displacementScale.value = material.displacementScale; + m_uniforms.displacementBias.value = material.displacementBias; - } else { + } - cubeImage[ i ] = texture.image[ i ]; + } else if ( material instanceof THREE.MeshNormalMaterial ) { - } + m_uniforms.opacity.value = material.opacity; - } + } - var image = cubeImage[ 0 ], - isImagePowerOfTwo = isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ), - glFormat = paramThreeToGL( texture.format ), - glType = paramThreeToGL( texture.type ); + THREE.WebGLUniforms.upload( + _gl, materialProperties.uniformsList, m_uniforms, _this ); - setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isImagePowerOfTwo ); + } - for ( var i = 0; i < 6; i ++ ) { - if ( isCompressed ) { + // common matrices - var mipmap, mipmaps = cubeImage[ i ].mipmaps; + p_uniforms.set( _gl, object, 'modelViewMatrix' ); + p_uniforms.set( _gl, object, 'normalMatrix' ); + p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld ); - for( var j = 0, jl = mipmaps.length; j < jl; j ++ ) { - mipmap = mipmaps[ j ]; - _gl.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + // dynamic uniforms - } + var dynUniforms = materialProperties.dynamicUniforms; - } else { + if ( dynUniforms !== null ) { - _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] ); + THREE.WebGLUniforms.evalDynamic( + dynUniforms, m_uniforms, object, camera ); - } + THREE.WebGLUniforms.upload( _gl, dynUniforms, m_uniforms, _this ); - } + } - if ( texture.generateMipmaps && isImagePowerOfTwo ) { + return program; - _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); + } - } + // Uniforms (refresh uniforms objects) - texture.needsUpdate = false; + function refreshUniformsCommon ( uniforms, material ) { - if ( texture.onUpdate ) texture.onUpdate(); + uniforms.opacity.value = material.opacity; - } else { + uniforms.diffuse.value = material.color; - _gl.activeTexture( _gl.TEXTURE0 + slot ); - _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube ); + if ( material.emissive ) { - } + uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity ); } - }; - - function setCubeTextureDynamic ( texture, slot ) { + uniforms.map.value = material.map; + uniforms.specularMap.value = material.specularMap; + uniforms.alphaMap.value = material.alphaMap; - _gl.activeTexture( _gl.TEXTURE0 + slot ); - _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.__webglTexture ); + if ( material.aoMap ) { - }; + uniforms.aoMap.value = material.aoMap; + uniforms.aoMapIntensity.value = material.aoMapIntensity; - // Render targets + } - function setupFrameBuffer ( framebuffer, renderTarget, textureTarget ) { + // uv repeat and offset setting priorities + // 1. color map + // 2. specular map + // 3. normal map + // 4. bump map + // 5. alpha map + // 6. emissive map - _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); - _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureTarget, renderTarget.__webglTexture, 0 ); + var uvScaleMap; - }; + if ( material.map ) { - function setupRenderBuffer ( renderbuffer, renderTarget ) { + uvScaleMap = material.map; - _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer ); + } else if ( material.specularMap ) { - if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { + uvScaleMap = material.specularMap; - _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height ); - _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); + } else if ( material.displacementMap ) { - /* For some reason this is not working. Defaulting to RGBA4. - } else if( ! renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + uvScaleMap = material.displacementMap; - _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.STENCIL_INDEX8, renderTarget.width, renderTarget.height ); - _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); - */ - } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + } else if ( material.normalMap ) { - _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height ); - _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); + uvScaleMap = material.normalMap; - } else { + } else if ( material.bumpMap ) { - _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height ); + uvScaleMap = material.bumpMap; - } + } else if ( material.roughnessMap ) { - }; + uvScaleMap = material.roughnessMap; - this.setRenderTarget = function ( renderTarget ) { + } else if ( material.metalnessMap ) { - var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube ); + uvScaleMap = material.metalnessMap; - if ( renderTarget && ! renderTarget.__webglFramebuffer ) { + } else if ( material.alphaMap ) { - if ( renderTarget.depthBuffer === undefined ) renderTarget.depthBuffer = true; - if ( renderTarget.stencilBuffer === undefined ) renderTarget.stencilBuffer = true; + uvScaleMap = material.alphaMap; - renderTarget.addEventListener( 'dispose', onRenderTargetDispose ); + } else if ( material.emissiveMap ) { - renderTarget.__webglTexture = _gl.createTexture(); + uvScaleMap = material.emissiveMap; - _this.info.memory.textures ++; + } - // Setup texture, create render and frame buffers + if ( uvScaleMap !== undefined ) { - var isTargetPowerOfTwo = isPowerOfTwo( renderTarget.width ) && isPowerOfTwo( renderTarget.height ), - glFormat = paramThreeToGL( renderTarget.format ), - glType = paramThreeToGL( renderTarget.type ); + // backwards compatibility + if ( uvScaleMap instanceof THREE.WebGLRenderTarget ) { - if ( isCube ) { + uvScaleMap = uvScaleMap.texture; - renderTarget.__webglFramebuffer = []; - renderTarget.__webglRenderbuffer = []; + } - _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTarget.__webglTexture ); - setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget, isTargetPowerOfTwo ); + var offset = uvScaleMap.offset; + var repeat = uvScaleMap.repeat; - for ( var i = 0; i < 6; i ++ ) { + uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y ); - renderTarget.__webglFramebuffer[ i ] = _gl.createFramebuffer(); - renderTarget.__webglRenderbuffer[ i ] = _gl.createRenderbuffer(); + } - _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); + uniforms.envMap.value = material.envMap; - setupFrameBuffer( renderTarget.__webglFramebuffer[ i ], renderTarget, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i ); - setupRenderBuffer( renderTarget.__webglRenderbuffer[ i ], renderTarget ); + // don't flip CubeTexture envMaps, flip everything else: + // WebGLRenderTargetCube will be flipped for backwards compatibility + // WebGLRenderTargetCube.texture will be flipped because it's a Texture and NOT a CubeTexture + // this check must be handled differently, or removed entirely, if WebGLRenderTargetCube uses a CubeTexture in the future + uniforms.flipEnvMap.value = ( ! ( material.envMap instanceof THREE.CubeTexture ) ) ? 1 : - 1; - } + uniforms.reflectivity.value = material.reflectivity; + uniforms.refractionRatio.value = material.refractionRatio; - if ( isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); + } - } else { + function refreshUniformsLine ( uniforms, material ) { - renderTarget.__webglFramebuffer = _gl.createFramebuffer(); + uniforms.diffuse.value = material.color; + uniforms.opacity.value = material.opacity; - if ( renderTarget.shareDepthFrom ) { + } - renderTarget.__webglRenderbuffer = renderTarget.shareDepthFrom.__webglRenderbuffer; + function refreshUniformsDash ( uniforms, material ) { - } else { + uniforms.dashSize.value = material.dashSize; + uniforms.totalSize.value = material.dashSize + material.gapSize; + uniforms.scale.value = material.scale; - renderTarget.__webglRenderbuffer = _gl.createRenderbuffer(); + } - } + function refreshUniformsPoints ( uniforms, material ) { - _gl.bindTexture( _gl.TEXTURE_2D, renderTarget.__webglTexture ); - setTextureParameters( _gl.TEXTURE_2D, renderTarget, isTargetPowerOfTwo ); + uniforms.diffuse.value = material.color; + uniforms.opacity.value = material.opacity; + uniforms.size.value = material.size * _pixelRatio; + uniforms.scale.value = _canvas.clientHeight * 0.5; - _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); + uniforms.map.value = material.map; - setupFrameBuffer( renderTarget.__webglFramebuffer, renderTarget, _gl.TEXTURE_2D ); + if ( material.map !== null ) { - if ( renderTarget.shareDepthFrom ) { + var offset = material.map.offset; + var repeat = material.map.repeat; - if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { + uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y ); - _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderTarget.__webglRenderbuffer ); + } - } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + } - _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderTarget.__webglRenderbuffer ); + function refreshUniformsFog ( uniforms, fog ) { - } + uniforms.fogColor.value = fog.color; - } else { + if ( fog instanceof THREE.Fog ) { - setupRenderBuffer( renderTarget.__webglRenderbuffer, renderTarget ); + uniforms.fogNear.value = fog.near; + uniforms.fogFar.value = fog.far; - } + } else if ( fog instanceof THREE.FogExp2 ) { - if ( isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); + uniforms.fogDensity.value = fog.density; - } + } - // Release everything + } - if ( isCube ) { + function refreshUniformsLambert ( uniforms, material ) { - _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); + if ( material.lightMap ) { - } else { + uniforms.lightMap.value = material.lightMap; + uniforms.lightMapIntensity.value = material.lightMapIntensity; - _gl.bindTexture( _gl.TEXTURE_2D, null ); + } - } + if ( material.emissiveMap ) { - _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); - _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); + uniforms.emissiveMap.value = material.emissiveMap; } - var framebuffer, width, height, vx, vy; - - if ( renderTarget ) { - - if ( isCube ) { + } - framebuffer = renderTarget.__webglFramebuffer[ renderTarget.activeCubeFace ]; + function refreshUniformsPhong ( uniforms, material ) { - } else { + uniforms.specular.value = material.specular; + uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 ) - framebuffer = renderTarget.__webglFramebuffer; + if ( material.lightMap ) { - } + uniforms.lightMap.value = material.lightMap; + uniforms.lightMapIntensity.value = material.lightMapIntensity; - width = renderTarget.width; - height = renderTarget.height; + } - vx = 0; - vy = 0; + if ( material.emissiveMap ) { - } else { + uniforms.emissiveMap.value = material.emissiveMap; - framebuffer = null; + } - width = _viewportWidth; - height = _viewportHeight; + if ( material.bumpMap ) { - vx = _viewportX; - vy = _viewportY; + uniforms.bumpMap.value = material.bumpMap; + uniforms.bumpScale.value = material.bumpScale; } - if ( framebuffer !== _currentFramebuffer ) { - - _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); - _gl.viewport( vx, vy, width, height ); + if ( material.normalMap ) { - _currentFramebuffer = framebuffer; + uniforms.normalMap.value = material.normalMap; + uniforms.normalScale.value.copy( material.normalScale ); } - _currentWidth = width; - _currentHeight = height; + if ( material.displacementMap ) { - }; + uniforms.displacementMap.value = material.displacementMap; + uniforms.displacementScale.value = material.displacementScale; + uniforms.displacementBias.value = material.displacementBias; - function updateRenderTargetMipmap ( renderTarget ) { + } - if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) { + } - _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTarget.__webglTexture ); - _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); - _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); + function refreshUniformsStandard ( uniforms, material ) { - } else { + uniforms.roughness.value = material.roughness; + uniforms.metalness.value = material.metalness; - _gl.bindTexture( _gl.TEXTURE_2D, renderTarget.__webglTexture ); - _gl.generateMipmap( _gl.TEXTURE_2D ); - _gl.bindTexture( _gl.TEXTURE_2D, null ); + if ( material.roughnessMap ) { + + uniforms.roughnessMap.value = material.roughnessMap; } - }; + if ( material.metalnessMap ) { - // Fallback filters for non-power-of-2 textures + uniforms.metalnessMap.value = material.metalnessMap; - function filterFallback ( f ) { + } - if ( f === THREE.NearestFilter || f === THREE.NearestMipMapNearestFilter || f === THREE.NearestMipMapLinearFilter ) { + if ( material.lightMap ) { - return _gl.NEAREST; + uniforms.lightMap.value = material.lightMap; + uniforms.lightMapIntensity.value = material.lightMapIntensity; } - return _gl.LINEAR; - - }; + if ( material.emissiveMap ) { - // Map three.js constants to WebGL constants + uniforms.emissiveMap.value = material.emissiveMap; - function paramThreeToGL ( p ) { + } - if ( p === THREE.RepeatWrapping ) return _gl.REPEAT; - if ( p === THREE.ClampToEdgeWrapping ) return _gl.CLAMP_TO_EDGE; - if ( p === THREE.MirroredRepeatWrapping ) return _gl.MIRRORED_REPEAT; + if ( material.bumpMap ) { - 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; + uniforms.bumpMap.value = material.bumpMap; + uniforms.bumpScale.value = material.bumpScale; - if ( p === THREE.LinearFilter ) return _gl.LINEAR; - if ( p === THREE.LinearMipMapNearestFilter ) return _gl.LINEAR_MIPMAP_NEAREST; - if ( p === THREE.LinearMipMapLinearFilter ) return _gl.LINEAR_MIPMAP_LINEAR; + } - if ( p === THREE.UnsignedByteType ) return _gl.UNSIGNED_BYTE; - if ( p === THREE.UnsignedShort4444Type ) return _gl.UNSIGNED_SHORT_4_4_4_4; - if ( p === THREE.UnsignedShort5551Type ) return _gl.UNSIGNED_SHORT_5_5_5_1; - if ( p === THREE.UnsignedShort565Type ) return _gl.UNSIGNED_SHORT_5_6_5; + if ( material.normalMap ) { - 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; + uniforms.normalMap.value = material.normalMap; + uniforms.normalScale.value.copy( material.normalScale ); - 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; + } - if ( p === THREE.AddEquation ) return _gl.FUNC_ADD; - if ( p === THREE.SubtractEquation ) return _gl.FUNC_SUBTRACT; - if ( p === THREE.ReverseSubtractEquation ) return _gl.FUNC_REVERSE_SUBTRACT; + if ( material.displacementMap ) { - 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; + uniforms.displacementMap.value = material.displacementMap; + uniforms.displacementScale.value = material.displacementScale; + uniforms.displacementBias.value = material.displacementBias; - 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; + } - if ( _glExtensionCompressedTextureS3TC !== undefined ) { + if ( material.envMap ) { - if ( p === THREE.RGB_S3TC_DXT1_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGB_S3TC_DXT1_EXT; - if ( p === THREE.RGBA_S3TC_DXT1_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGBA_S3TC_DXT1_EXT; - if ( p === THREE.RGBA_S3TC_DXT3_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGBA_S3TC_DXT3_EXT; - if ( p === THREE.RGBA_S3TC_DXT5_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGBA_S3TC_DXT5_EXT; + //uniforms.envMap.value = material.envMap; // part of uniforms common + uniforms.envMapIntensity.value = material.envMapIntensity; } - return 0; + } - }; + function refreshUniformsPhysical ( uniforms, material ) { - // Allocations + uniforms.clearCoat.value = material.clearCoat; + uniforms.clearCoatRoughness.value = material.clearCoatRoughness; - function allocateBones ( object ) { + refreshUniformsStandard( uniforms, material ); - if ( _supportsBoneTextures && object && object.useVertexTexture ) { + } - return 1024; + // If uniforms are marked as clean, they don't need to be loaded to the GPU. - } else { + function markUniformsLightsNeedsUpdate ( uniforms, value ) { - // 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) + uniforms.ambientLightColor.needsUpdate = value; - var nVertexUniforms = _gl.getParameter( _gl.MAX_VERTEX_UNIFORM_VECTORS ); - var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); + uniforms.directionalLights.needsUpdate = value; + uniforms.pointLights.needsUpdate = value; + uniforms.spotLights.needsUpdate = value; + uniforms.hemisphereLights.needsUpdate = value; - var maxBones = nVertexMatrices; + } - if ( object !== undefined && object instanceof THREE.SkinnedMesh ) { + // Lighting - maxBones = Math.min( object.bones.length, maxBones ); + function setupShadows ( lights ) { - if ( maxBones < object.bones.length ) { + var lightShadowsLength = 0; - console.warn( "WebGLRenderer: too many bones - " + object.bones.length + ", this GPU supports just " + maxBones + " (try OpenGL instead of ANGLE)" ); + for ( var i = 0, l = lights.length; i < l; i ++ ) { - } + var light = lights[ i ]; - } + if ( light.castShadow ) { - return maxBones; + _lights.shadows[ lightShadowsLength ++ ] = light; + + } } - }; + _lights.shadows.length = lightShadowsLength; - function allocateLights( lights ) { + } - var dirLights = 0; - var pointLights = 0; - var spotLights = 0; - var hemiLights = 0; + function setupLights ( lights, camera ) { - for ( var l = 0, ll = lights.length; l < ll; l ++ ) { + var l, ll, light, + r = 0, g = 0, b = 0, + color, + intensity, + distance, + shadowMap, - var light = lights[ l ]; + viewMatrix = camera.matrixWorldInverse, - if ( light.onlyShadow ) continue; + directionalLength = 0, + pointLength = 0, + spotLength = 0, + hemiLength = 0; - if ( light instanceof THREE.DirectionalLight ) dirLights ++; - if ( light instanceof THREE.PointLight ) pointLights ++; - if ( light instanceof THREE.SpotLight ) spotLights ++; - if ( light instanceof THREE.HemisphereLight ) hemiLights ++; + for ( l = 0, ll = lights.length; l < ll; l ++ ) { - } + light = lights[ l ]; - return { 'directional' : dirLights, 'point' : pointLights, 'spot': spotLights, 'hemi': hemiLights }; + color = light.color; + intensity = light.intensity; + distance = light.distance; - }; + shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null; - function allocateShadows( lights ) { + if ( light instanceof THREE.AmbientLight ) { - var maxShadows = 0; + r += color.r * intensity; + g += color.g * intensity; + b += color.b * intensity; - for ( var l = 0, ll = lights.length; l < ll; l++ ) { + } else if ( light instanceof THREE.DirectionalLight ) { - var light = lights[ l ]; + var uniforms = lightCache.get( light ); - if ( ! light.castShadow ) continue; + uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); + uniforms.direction.setFromMatrixPosition( light.matrixWorld ); + _vector3.setFromMatrixPosition( light.target.matrixWorld ); + uniforms.direction.sub( _vector3 ); + uniforms.direction.transformDirection( viewMatrix ); - if ( light instanceof THREE.SpotLight ) maxShadows ++; - if ( light instanceof THREE.DirectionalLight && ! light.shadowCascade ) maxShadows ++; + uniforms.shadow = light.castShadow; - } + if ( light.castShadow ) { - return maxShadows; + uniforms.shadowBias = light.shadow.bias; + uniforms.shadowRadius = light.shadow.radius; + uniforms.shadowMapSize = light.shadow.mapSize; - }; + } - // Initialization + _lights.directionalShadowMap[ directionalLength ] = shadowMap; + _lights.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix; + _lights.directional[ directionalLength ++ ] = uniforms; - function initGL() { + } else if ( light instanceof THREE.SpotLight ) { - try { + var uniforms = lightCache.get( light ); - var attributes = { - alpha: _alpha, - premultipliedAlpha: _premultipliedAlpha, - antialias: _antialias, - stencil: _stencil, - preserveDrawingBuffer: _preserveDrawingBuffer - }; + uniforms.position.setFromMatrixPosition( light.matrixWorld ); + uniforms.position.applyMatrix4( viewMatrix ); - _gl = _canvas.getContext( 'webgl', attributes ) || _canvas.getContext( 'experimental-webgl', attributes ); + uniforms.color.copy( color ).multiplyScalar( intensity ); + uniforms.distance = distance; - if ( _gl === null ) { + uniforms.direction.setFromMatrixPosition( light.matrixWorld ); + _vector3.setFromMatrixPosition( light.target.matrixWorld ); + uniforms.direction.sub( _vector3 ); + uniforms.direction.transformDirection( viewMatrix ); - throw 'Error creating WebGL context.'; + uniforms.coneCos = Math.cos( light.angle ); + uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) ); + uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay; - } + uniforms.shadow = light.castShadow; - } catch ( error ) { + if ( light.castShadow ) { - console.error( error ); + uniforms.shadowBias = light.shadow.bias; + uniforms.shadowRadius = light.shadow.radius; + uniforms.shadowMapSize = light.shadow.mapSize; - } + } - _glExtensionTextureFloat = _gl.getExtension( 'OES_texture_float' ); - _glExtensionTextureFloatLinear = _gl.getExtension( 'OES_texture_float_linear' ); - _glExtensionStandardDerivatives = _gl.getExtension( 'OES_standard_derivatives' ); + _lights.spotShadowMap[ spotLength ] = shadowMap; + _lights.spotShadowMatrix[ spotLength ] = light.shadow.matrix; + _lights.spot[ spotLength ++ ] = uniforms; - _glExtensionTextureFilterAnisotropic = _gl.getExtension( 'EXT_texture_filter_anisotropic' ) || _gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || _gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' ); + } else if ( light instanceof THREE.PointLight ) { - _glExtensionCompressedTextureS3TC = _gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || _gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || _gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' ); + var uniforms = lightCache.get( light ); - if ( ! _glExtensionTextureFloat ) { + uniforms.position.setFromMatrixPosition( light.matrixWorld ); + uniforms.position.applyMatrix4( viewMatrix ); - console.log( 'THREE.WebGLRenderer: Float textures not supported.' ); + uniforms.color.copy( light.color ).multiplyScalar( light.intensity ); + uniforms.distance = light.distance; + uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay; - } + uniforms.shadow = light.castShadow; - if ( ! _glExtensionStandardDerivatives ) { + if ( light.castShadow ) { - console.log( 'THREE.WebGLRenderer: Standard derivatives not supported.' ); + uniforms.shadowBias = light.shadow.bias; + uniforms.shadowRadius = light.shadow.radius; + uniforms.shadowMapSize = light.shadow.mapSize; - } + } - if ( ! _glExtensionTextureFilterAnisotropic ) { + _lights.pointShadowMap[ pointLength ] = shadowMap; - console.log( 'THREE.WebGLRenderer: Anisotropic texture filtering not supported.' ); + if ( _lights.pointShadowMatrix[ pointLength ] === undefined ) { - } + _lights.pointShadowMatrix[ pointLength ] = new THREE.Matrix4(); - if ( ! _glExtensionCompressedTextureS3TC ) { + } - console.log( 'THREE.WebGLRenderer: S3TC compressed textures not supported.' ); + // for point lights we set the shadow matrix to be a translation-only matrix + // equal to inverse of the light's position + _vector3.setFromMatrixPosition( light.matrixWorld ).negate(); + _lights.pointShadowMatrix[ pointLength ].identity().setPosition( _vector3 ); - } + _lights.point[ pointLength ++ ] = uniforms; - if ( _gl.getShaderPrecisionFormat === undefined ) { + } else if ( light instanceof THREE.HemisphereLight ) { - _gl.getShaderPrecisionFormat = function() { + var uniforms = lightCache.get( light ); - return { - "rangeMin" : 1, - "rangeMax" : 1, - "precision" : 1 - }; + uniforms.direction.setFromMatrixPosition( light.matrixWorld ); + uniforms.direction.transformDirection( viewMatrix ); + uniforms.direction.normalize(); + + uniforms.skyColor.copy( light.color ).multiplyScalar( intensity ); + uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity ); + + _lights.hemi[ hemiLength ++ ] = uniforms; } + } - }; + _lights.ambient[ 0 ] = r; + _lights.ambient[ 1 ] = g; + _lights.ambient[ 2 ] = b; - function setDefaultGLState () { + _lights.directional.length = directionalLength; + _lights.spot.length = spotLength; + _lights.point.length = pointLength; + _lights.hemi.length = hemiLength; - _gl.clearColor( 0, 0, 0, 1 ); - _gl.clearDepth( 1 ); - _gl.clearStencil( 0 ); + _lights.hash = directionalLength + ',' + pointLength + ',' + spotLength + ',' + hemiLength + ',' + _lights.shadows.length; - _gl.enable( _gl.DEPTH_TEST ); - _gl.depthFunc( _gl.LEQUAL ); + } - _gl.frontFace( _gl.CCW ); - _gl.cullFace( _gl.BACK ); - _gl.enable( _gl.CULL_FACE ); + // GL state setting - _gl.enable( _gl.BLEND ); - _gl.blendEquation( _gl.FUNC_ADD ); - _gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA ); + this.setFaceCulling = function ( cullFace, frontFaceDirection ) { - _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); + state.setCullFace( cullFace ); + state.setFlipSided( frontFaceDirection === THREE.FrontFaceDirectionCW ); }; - // default plugins (order is important) + // Textures - this.shadowMapPlugin = new THREE.ShadowMapPlugin(); - this.addPrePlugin( this.shadowMapPlugin ); + function allocTextureUnit() { - this.addPostPlugin( new THREE.SpritePlugin() ); - this.addPostPlugin( new THREE.LensFlarePlugin() ); + var textureUnit = _usedTextureUnits; -}; + if ( textureUnit >= capabilities.maxTextures ) { -/** - * @author szimek / https://github.com/szimek/ - * @author alteredq / http://alteredqualia.com/ - */ + console.warn( 'WebGLRenderer: trying to use ' + textureUnit + ' texture units while this GPU supports only ' + capabilities.maxTextures ); -THREE.WebGLRenderTarget = function ( width, height, options ) { + } - this.width = width; - this.height = height; + _usedTextureUnits += 1; - options = options || {}; + return textureUnit; - this.wrapS = options.wrapS !== undefined ? options.wrapS : THREE.ClampToEdgeWrapping; - this.wrapT = options.wrapT !== undefined ? options.wrapT : THREE.ClampToEdgeWrapping; + } - this.magFilter = options.magFilter !== undefined ? options.magFilter : THREE.LinearFilter; - this.minFilter = options.minFilter !== undefined ? options.minFilter : THREE.LinearMipMapLinearFilter; + this.allocTextureUnit = allocTextureUnit; - this.anisotropy = options.anisotropy !== undefined ? options.anisotropy : 1; + // this.setTexture2D = setTexture2D; + this.setTexture2D = ( function() { - this.offset = new THREE.Vector2( 0, 0 ); - this.repeat = new THREE.Vector2( 1, 1 ); + var warned = false; - this.format = options.format !== undefined ? options.format : THREE.RGBAFormat; - this.type = options.type !== undefined ? options.type : THREE.UnsignedByteType; + // backwards compatibility: peel texture.texture + return function setTexture2D( texture, slot ) { - this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; - this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true; + if ( texture instanceof THREE.WebGLRenderTarget ) { - this.generateMipmaps = true; + if ( ! warned ) { - this.shareDepthFrom = null; + console.warn( "THREE.WebGLRenderer.setTexture2D: don't use render targets as textures. Use their .texture property instead." ); + warned = true; -}; + } -THREE.WebGLRenderTarget.prototype = { + texture = texture.texture; - constructor: THREE.WebGLRenderTarget, + } - clone: function () { + textures.setTexture2D( texture, slot ); + + }; - var tmp = new THREE.WebGLRenderTarget( this.width, this.height ); + }() ); - tmp.wrapS = this.wrapS; - tmp.wrapT = this.wrapT; + this.setTexture = ( function() { - tmp.magFilter = this.magFilter; - tmp.minFilter = this.minFilter; + var warned = false; - tmp.anisotropy = this.anisotropy; + return function setTexture( texture, slot ) { - tmp.offset.copy( this.offset ); - tmp.repeat.copy( this.repeat ); + if ( ! warned ) { - tmp.format = this.format; - tmp.type = this.type; + console.warn( "THREE.WebGLRenderer: .setTexture is deprecated, use setTexture2D instead." ); + warned = true; - tmp.depthBuffer = this.depthBuffer; - tmp.stencilBuffer = this.stencilBuffer; + } - tmp.generateMipmaps = this.generateMipmaps; + textures.setTexture2D( texture, slot ); - tmp.shareDepthFrom = this.shareDepthFrom; + }; - return tmp; + }() ); - }, + this.setTextureCube = ( function() { - dispose: function () { + var warned = false; - this.dispatchEvent( { type: 'dispose' } ); + return function setTextureCube( texture, slot ) { - } + // backwards compatibility: peel texture.texture + if ( texture instanceof THREE.WebGLRenderTargetCube ) { -}; + if ( ! warned ) { -THREE.EventDispatcher.prototype.apply( THREE.WebGLRenderTarget.prototype ); + console.warn( "THREE.WebGLRenderer.setTextureCube: don't use cube render targets as textures. Use their .texture property instead." ); + warned = true; -/** - * @author alteredq / http://alteredqualia.com - */ + } -THREE.WebGLRenderTargetCube = function ( width, height, options ) { + texture = texture.texture; - THREE.WebGLRenderTarget.call( this, width, height, options ); + } - this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5 + // currently relying on the fact that WebGLRenderTargetCube.texture is a Texture and NOT a CubeTexture + // TODO: unify these code paths + if ( texture instanceof THREE.CubeTexture || + ( Array.isArray( texture.image ) && texture.image.length === 6 ) ) { -}; + // CompressedTexture can have Array in image :/ -THREE.WebGLRenderTargetCube.prototype = Object.create( THREE.WebGLRenderTarget.prototype ); + // this function alone should take care of cube textures + textures.setTextureCube( texture, slot ); -/** - * @author mrdoob / http://mrdoob.com/ - */ + } else { -THREE.RenderableVertex = function () { + // assumed: texture property of THREE.WebGLRenderTargetCube - this.positionWorld = new THREE.Vector3(); - this.positionScreen = new THREE.Vector4(); + textures.setTextureCubeDynamic( texture, slot ); - this.visible = true; + } -}; + }; -THREE.RenderableVertex.prototype.copy = function ( vertex ) { + }() ); - this.positionWorld.copy( vertex.positionWorld ); - this.positionScreen.copy( vertex.positionScreen ); + this.getCurrentRenderTarget = function() { -}; + return _currentRenderTarget; -/** - * @author mrdoob / http://mrdoob.com/ - */ + }; + + this.setRenderTarget = function ( renderTarget ) { -THREE.RenderableFace3 = function () { + _currentRenderTarget = renderTarget; - this.id = 0; + if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) { - this.v1 = new THREE.RenderableVertex(); - this.v2 = new THREE.RenderableVertex(); - this.v3 = new THREE.RenderableVertex(); + textures.setupRenderTarget( renderTarget ); - this.centroidModel = new THREE.Vector3(); + } - this.normalModel = new THREE.Vector3(); - this.normalModelView = new THREE.Vector3(); + var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube ); + var framebuffer; - this.vertexNormalsLength = 0; - this.vertexNormalsModel = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ]; - this.vertexNormalsModelView = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ]; + if ( renderTarget ) { - this.color = null; - this.material = null; - this.uvs = [[]]; + var renderTargetProperties = properties.get( renderTarget ); - this.z = 0; + if ( isCube ) { -}; + framebuffer = renderTargetProperties.__webglFramebuffer[ renderTarget.activeCubeFace ]; -/** - * @author mrdoob / http://mrdoob.com/ - */ + } else { -THREE.RenderableObject = function () { + framebuffer = renderTargetProperties.__webglFramebuffer; - this.id = 0; + } - this.object = null; - this.z = 0; + _currentScissor.copy( renderTarget.scissor ); + _currentScissorTest = renderTarget.scissorTest; -}; + _currentViewport.copy( renderTarget.viewport ); -/** - * @author mrdoob / http://mrdoob.com/ - */ + } else { -THREE.RenderableParticle = function () { + framebuffer = null; - this.id = 0; + _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ); + _currentScissorTest = _scissorTest; - this.object = null; + _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ); - this.x = 0; - this.y = 0; - this.z = 0; + } - this.rotation = null; - this.scale = new THREE.Vector2(); + if ( _currentFramebuffer !== framebuffer ) { - this.material = null; + _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + _currentFramebuffer = framebuffer; -}; + } -/** - * @author mrdoob / http://mrdoob.com/ - */ + state.scissor( _currentScissor ); + state.setScissorTest( _currentScissorTest ); -THREE.RenderableLine = function () { + state.viewport( _currentViewport ); - this.id = 0; + if ( isCube ) { - this.v1 = new THREE.RenderableVertex(); - this.v2 = new THREE.RenderableVertex(); + var textureProperties = properties.get( renderTarget.texture ); + _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + renderTarget.activeCubeFace, textureProperties.__webglTexture, renderTarget.activeMipMapLevel ); - this.vertexColors = [ new THREE.Color(), new THREE.Color() ]; - this.material = null; + } - this.z = 0; + }; -}; + this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer ) { -/** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ + if ( renderTarget instanceof THREE.WebGLRenderTarget === false ) { -THREE.GeometryUtils = { + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' ); + return; - // Merge two geometries or geometry and geometry from object (using object's transform) + } - merge: function ( geometry1, object2 /* mesh | geometry */, materialIndexOffset ) { + var framebuffer = properties.get( renderTarget ).__webglFramebuffer; - var matrix, normalMatrix, - vertexOffset = geometry1.vertices.length, - uvPosition = geometry1.faceVertexUvs[ 0 ].length, - geometry2 = object2 instanceof THREE.Mesh ? object2.geometry : object2, - vertices1 = geometry1.vertices, - vertices2 = geometry2.vertices, - faces1 = geometry1.faces, - faces2 = geometry2.faces, - uvs1 = geometry1.faceVertexUvs[ 0 ], - uvs2 = geometry2.faceVertexUvs[ 0 ]; + if ( framebuffer ) { - if ( materialIndexOffset === undefined ) materialIndexOffset = 0; + var restore = false; - if ( object2 instanceof THREE.Mesh ) { + if ( framebuffer !== _currentFramebuffer ) { - object2.matrixAutoUpdate && object2.updateMatrix(); + _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); - matrix = object2.matrix; + restore = true; - normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); + } - } + try { - // vertices + var texture = renderTarget.texture; - for ( var i = 0, il = vertices2.length; i < il; i ++ ) { + if ( texture.format !== THREE.RGBAFormat && paramThreeToGL( texture.format ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) { - var vertex = vertices2[ i ]; + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' ); + return; - var vertexCopy = vertex.clone(); + } - if ( matrix ) vertexCopy.applyMatrix4( matrix ); + if ( texture.type !== THREE.UnsignedByteType && + paramThreeToGL( texture.type ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) && + ! ( texture.type === THREE.FloatType && extensions.get( 'WEBGL_color_buffer_float' ) ) && + ! ( texture.type === THREE.HalfFloatType && extensions.get( 'EXT_color_buffer_half_float' ) ) ) { - vertices1.push( vertexCopy ); + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' ); + return; - } + } - // faces + if ( _gl.checkFramebufferStatus( _gl.FRAMEBUFFER ) === _gl.FRAMEBUFFER_COMPLETE ) { - for ( i = 0, il = faces2.length; i < il; i ++ ) { + // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604) - var face = faces2[ i ], faceCopy, normal, color, - faceVertexNormals = face.vertexNormals, - faceVertexColors = face.vertexColors; + if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) { - faceCopy = new THREE.Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset ); - faceCopy.normal.copy( face.normal ); + _gl.readPixels( x, y, width, height, paramThreeToGL( texture.format ), paramThreeToGL( texture.type ), buffer ); - if ( normalMatrix ) { + } - faceCopy.normal.applyMatrix3( normalMatrix ).normalize(); + } else { - } + console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' ); - for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) { + } - normal = faceVertexNormals[ j ].clone(); + } finally { - if ( normalMatrix ) { + if ( restore ) { - normal.applyMatrix3( normalMatrix ).normalize(); + _gl.bindFramebuffer( _gl.FRAMEBUFFER, _currentFramebuffer ); } - faceCopy.vertexNormals.push( normal ); - } - faceCopy.color.copy( face.color ); + } - for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) { + }; - color = faceVertexColors[ j ]; - faceCopy.vertexColors.push( color.clone() ); + // Map three.js constants to WebGL constants - } + function paramThreeToGL ( p ) { - faceCopy.materialIndex = face.materialIndex + materialIndexOffset; + var extension; - faceCopy.centroid.copy( face.centroid ); + if ( p === THREE.RepeatWrapping ) return _gl.REPEAT; + if ( p === THREE.ClampToEdgeWrapping ) return _gl.CLAMP_TO_EDGE; + if ( p === THREE.MirroredRepeatWrapping ) return _gl.MIRRORED_REPEAT; - if ( matrix ) { + 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; - faceCopy.centroid.applyMatrix4( matrix ); + if ( p === THREE.LinearFilter ) return _gl.LINEAR; + if ( p === THREE.LinearMipMapNearestFilter ) return _gl.LINEAR_MIPMAP_NEAREST; + if ( p === THREE.LinearMipMapLinearFilter ) return _gl.LINEAR_MIPMAP_LINEAR; - } + if ( p === THREE.UnsignedByteType ) return _gl.UNSIGNED_BYTE; + if ( p === THREE.UnsignedShort4444Type ) return _gl.UNSIGNED_SHORT_4_4_4_4; + if ( p === THREE.UnsignedShort5551Type ) return _gl.UNSIGNED_SHORT_5_5_5_1; + if ( p === THREE.UnsignedShort565Type ) return _gl.UNSIGNED_SHORT_5_6_5; - faces1.push( faceCopy ); + 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; - } + extension = extensions.get( 'OES_texture_half_float' ); - // uvs + if ( extension !== null ) { - for ( i = 0, il = uvs2.length; i < il; i ++ ) { + if ( p === THREE.HalfFloatType ) return extension.HALF_FLOAT_OES; - var uv = uvs2[ i ], uvCopy = []; + } - for ( var j = 0, jl = uv.length; j < jl; j ++ ) { + 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; + if ( p === THREE.DepthFormat ) return _gl.DEPTH_COMPONENT; - uvCopy.push( new THREE.Vector2( uv[ j ].x, uv[ j ].y ) ); + if ( p === THREE.AddEquation ) return _gl.FUNC_ADD; + if ( p === THREE.SubtractEquation ) return _gl.FUNC_SUBTRACT; + if ( p === THREE.ReverseSubtractEquation ) return _gl.FUNC_REVERSE_SUBTRACT; - } + if ( p === THREE.ZeroFactor ) return _gl.ZERO; + if ( p === THREE.OneFactor ) return _gl.ONE; + if ( p === THREE.SrcColorFactor ) return _gl.SRC_COLOR; + if ( p === THREE.OneMinusSrcColorFactor ) return _gl.ONE_MINUS_SRC_COLOR; + if ( p === THREE.SrcAlphaFactor ) return _gl.SRC_ALPHA; + if ( p === THREE.OneMinusSrcAlphaFactor ) return _gl.ONE_MINUS_SRC_ALPHA; + if ( p === THREE.DstAlphaFactor ) return _gl.DST_ALPHA; + if ( p === THREE.OneMinusDstAlphaFactor ) return _gl.ONE_MINUS_DST_ALPHA; - uvs1.push( uvCopy ); + 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; - } + extension = extensions.get( 'WEBGL_compressed_texture_s3tc' ); - }, + if ( extension !== null ) { - // Get random point in triangle (via barycentric coordinates) - // (uniform distribution) - // http://www.cgafaq.info/wiki/Random_Point_In_Triangle + 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; - randomPointInTriangle: function () { + } - var vector = new THREE.Vector3(); + extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' ); - return function ( vectorA, vectorB, vectorC ) { + if ( extension !== null ) { - var point = new THREE.Vector3(); + 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; - var a = THREE.Math.random16(); - var b = THREE.Math.random16(); + } - if ( ( a + b ) > 1 ) { + extension = extensions.get( 'WEBGL_compressed_texture_etc1' ); - a = 1 - a; - b = 1 - b; + if ( extension !== null ) { - } + if ( p === THREE.RGB_ETC1_Format ) return extension.COMPRESSED_RGB_ETC1_WEBGL; - var c = 1 - a - b; + } - point.copy( vectorA ); - point.multiplyScalar( a ); + extension = extensions.get( 'EXT_blend_minmax' ); - vector.copy( vectorB ); - vector.multiplyScalar( b ); + if ( extension !== null ) { - point.add( vector ); + if ( p === THREE.MinEquation ) return extension.MIN_EXT; + if ( p === THREE.MaxEquation ) return extension.MAX_EXT; - vector.copy( vectorC ); - vector.multiplyScalar( c ); + } - point.add( vector ); + return 0; - return point; + } - }; +}; - }(), +// File:src/renderers/WebGLRenderTarget.js - // Get random point in face (triangle / quad) - // (uniform distribution) +/** + * @author szimek / https://github.com/szimek/ + * @author alteredq / http://alteredqualia.com/ + * @author Marius Kintel / https://github.com/kintel + */ - randomPointInFace: function ( face, geometry, useCachedAreas ) { +/* + In options, we can specify: + * Texture parameters for an auto-generated target texture + * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers +*/ +THREE.WebGLRenderTarget = function ( width, height, options ) { - var vA, vB, vC, vD; + this.uuid = THREE.Math.generateUUID(); - vA = geometry.vertices[ face.a ]; - vB = geometry.vertices[ face.b ]; - vC = geometry.vertices[ face.c ]; + this.width = width; + this.height = height; - return THREE.GeometryUtils.randomPointInTriangle( vA, vB, vC ); + this.scissor = new THREE.Vector4( 0, 0, width, height ); + this.scissorTest = false; - }, + this.viewport = new THREE.Vector4( 0, 0, width, height ); - // Get uniformly distributed random points in mesh - // - create array with cumulative sums of face areas - // - pick random number from 0 to total area - // - find corresponding place in area array by binary search - // - get random point in face + options = options || {}; - randomPointsInGeometry: function ( geometry, n ) { + if ( options.minFilter === undefined ) options.minFilter = THREE.LinearFilter; - var face, i, - faces = geometry.faces, - vertices = geometry.vertices, - il = faces.length, - totalArea = 0, - cumulativeAreas = [], - vA, vB, vC, vD; + this.texture = new THREE.Texture( undefined, undefined, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding ); - // precompute face areas + this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; + this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true; + this.depthTexture = null; - for ( i = 0; i < il; i ++ ) { +}; - face = faces[ i ]; +Object.assign( THREE.WebGLRenderTarget.prototype, THREE.EventDispatcher.prototype, { - vA = vertices[ face.a ]; - vB = vertices[ face.b ]; - vC = vertices[ face.c ]; + setSize: function ( width, height ) { - face._area = THREE.GeometryUtils.triangleArea( vA, vB, vC ); + if ( this.width !== width || this.height !== height ) { - totalArea += face._area; + this.width = width; + this.height = height; - cumulativeAreas[ i ] = totalArea; + this.dispose(); } - // binary search cumulative areas array + this.viewport.set( 0, 0, width, height ); + this.scissor.set( 0, 0, width, height ); - function binarySearchIndices( value ) { + }, - function binarySearch( start, end ) { + clone: function () { - // return closest larger index - // if exact number is not found + return new this.constructor().copy( this ); - if ( end < start ) - return start; + }, - var mid = start + Math.floor( ( end - start ) / 2 ); + copy: function ( source ) { - if ( cumulativeAreas[ mid ] > value ) { + this.width = source.width; + this.height = source.height; - return binarySearch( start, mid - 1 ); + this.viewport.copy( source.viewport ); - } else if ( cumulativeAreas[ mid ] < value ) { + this.texture = source.texture.clone(); - return binarySearch( mid + 1, end ); + this.depthBuffer = source.depthBuffer; + this.stencilBuffer = source.stencilBuffer; + this.depthTexture = source.depthTexture; - } else { + return this; - return mid; + }, - } + dispose: function () { - } + this.dispatchEvent( { type: 'dispose' } ); - var result = binarySearch( 0, cumulativeAreas.length - 1 ) - return result; + } - } +} ); - // pick random face weighted by face area +// File:src/renderers/WebGLRenderTargetCube.js - var r, index, - result = []; +/** + * @author alteredq / http://alteredqualia.com + */ - var stats = {}; +THREE.WebGLRenderTargetCube = function ( width, height, options ) { - for ( i = 0; i < n; i ++ ) { + THREE.WebGLRenderTarget.call( this, width, height, options ); - r = THREE.Math.random16() * totalArea; + this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5 + this.activeMipMapLevel = 0; - index = binarySearchIndices( r ); +}; - result[ i ] = THREE.GeometryUtils.randomPointInFace( faces[ index ], geometry, true ); +THREE.WebGLRenderTargetCube.prototype = Object.create( THREE.WebGLRenderTarget.prototype ); +THREE.WebGLRenderTargetCube.prototype.constructor = THREE.WebGLRenderTargetCube; - if ( ! stats[ index ] ) { +// File:src/renderers/webgl/WebGLBufferRenderer.js - stats[ index ] = 1; +/** +* @author mrdoob / http://mrdoob.com/ +*/ - } else { +THREE.WebGLBufferRenderer = function ( _gl, extensions, _infoRender ) { - stats[ index ] += 1; + var mode; - } + function setMode( value ) { - } + mode = value; - return result; + } - }, + function render( start, count ) { - // Get triangle area (half of parallelogram) - // http://mathworld.wolfram.com/TriangleArea.html + _gl.drawArrays( mode, start, count ); - triangleArea: function () { + _infoRender.calls ++; + _infoRender.vertices += count; + if ( mode === _gl.TRIANGLES ) _infoRender.faces += count / 3; - var vector1 = new THREE.Vector3(); - var vector2 = new THREE.Vector3(); + } - return function ( vectorA, vectorB, vectorC ) { + function renderInstances( geometry ) { - vector1.subVectors( vectorB, vectorA ); - vector2.subVectors( vectorC, vectorA ); - vector1.cross( vector2 ); + var extension = extensions.get( 'ANGLE_instanced_arrays' ); - return 0.5 * vector1.length(); + if ( extension === null ) { - }; + console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); + return; - }(), + } - // Center geometry so that 0,0,0 is in center of bounding box + var position = geometry.attributes.position; - center: function ( geometry ) { + var count = 0; - geometry.computeBoundingBox(); + if ( position instanceof THREE.InterleavedBufferAttribute ) { - var bb = geometry.boundingBox; + count = position.data.count; - var offset = new THREE.Vector3(); + extension.drawArraysInstancedANGLE( mode, 0, count, geometry.maxInstancedCount ); - offset.addVectors( bb.min, bb.max ); - offset.multiplyScalar( -0.5 ); + } else { - geometry.applyMatrix( new THREE.Matrix4().makeTranslation( offset.x, offset.y, offset.z ) ); - geometry.computeBoundingBox(); + count = position.count; - return offset; + extension.drawArraysInstancedANGLE( mode, 0, count, geometry.maxInstancedCount ); - }, + } - triangulateQuads: function ( geometry ) { + _infoRender.calls ++; + _infoRender.vertices += count * geometry.maxInstancedCount; + if ( mode === _gl.TRIANGLES ) _infoRender.faces += geometry.maxInstancedCount * count / 3; - var i, il, j, jl; + } - var faces = []; - var faceVertexUvs = []; + this.setMode = setMode; + this.render = render; + this.renderInstances = renderInstances; - for ( i = 0, il = geometry.faceVertexUvs.length; i < il; i ++ ) { +}; - faceVertexUvs[ i ] = []; +// File:src/renderers/webgl/WebGLClipping.js - } +THREE.WebGLClipping = function() { - for ( i = 0, il = geometry.faces.length; i < il; i ++ ) { + var scope = this, - var face = geometry.faces[ i ]; + globalState = null, + numGlobalPlanes = 0, + localClippingEnabled = false, + renderingShadows = false, - faces.push( face ); + plane = new THREE.Plane(), + viewNormalMatrix = new THREE.Matrix3(), - for ( j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) { + uniform = { value: null, needsUpdate: false }; - faceVertexUvs[ j ].push( geometry.faceVertexUvs[ j ][ i ] ); + this.uniform = uniform; + this.numPlanes = 0; - } + this.init = function( planes, enableLocalClipping, camera ) { - } + var enabled = + planes.length !== 0 || + enableLocalClipping || + // enable state of previous frame - the clipping code has to + // run another frame in order to reset the state: + numGlobalPlanes !== 0 || + localClippingEnabled; - geometry.faces = faces; - geometry.faceVertexUvs = faceVertexUvs; + localClippingEnabled = enableLocalClipping; - geometry.computeCentroids(); - geometry.computeFaceNormals(); - geometry.computeVertexNormals(); + globalState = projectPlanes( planes, camera, 0 ); + numGlobalPlanes = planes.length; - if ( geometry.hasTangents ) geometry.computeTangents(); + return enabled; - } + }; -}; + this.beginShadows = function() { -/** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - */ + renderingShadows = true; + projectPlanes( null ); -THREE.ImageUtils = { + }; - crossOrigin: 'anonymous', + this.endShadows = function() { - loadTexture: function ( url, mapping, onLoad, onError ) { + renderingShadows = false; + resetGlobalState(); - var image = new Image(); - var texture = new THREE.Texture( image, mapping ); + }; - var loader = new THREE.ImageLoader(); - loader.crossOrigin = this.crossOrigin; - loader.load( url, function ( image ) { + this.setState = function( planes, clipShadows, camera, cache, fromCache ) { - texture.image = image; - texture.needsUpdate = true; + if ( ! localClippingEnabled || + planes === null || planes.length === 0 || + renderingShadows && ! clipShadows ) { + // there's no local clipping - if ( onLoad ) onLoad( texture ); + if ( renderingShadows ) { + // there's no global clipping - } ); + projectPlanes( null ); - texture.sourceFile = url; + } else { - return texture; + resetGlobalState(); + } - }, + } else { - loadCompressedTexture: function ( url, mapping, onLoad, onError ) { + var nGlobal = renderingShadows ? 0 : numGlobalPlanes, + lGlobal = nGlobal * 4, - var texture = new THREE.CompressedTexture(); - texture.mapping = mapping; + dstArray = cache.clippingState || null; - var request = new XMLHttpRequest(); + uniform.value = dstArray; // ensure unique state - request.onload = function () { + dstArray = projectPlanes( planes, camera, lGlobal, fromCache ); - var buffer = request.response; - var dds = THREE.ImageUtils.parseDDS( buffer, true ); + for ( var i = 0; i !== lGlobal; ++ i ) { - texture.format = dds.format; + dstArray[ i ] = globalState[ i ]; - texture.mipmaps = dds.mipmaps; - texture.image.width = dds.width; - texture.image.height = dds.height; + } - // gl.generateMipmap fails for compressed textures - // mipmaps must be embedded in the DDS file - // or texture filters must not use mipmapping + cache.clippingState = dstArray; + this.numPlanes += nGlobal; - texture.generateMipmaps = false; + } - texture.needsUpdate = true; - if ( onLoad ) onLoad( texture ); + }; + + function resetGlobalState() { + + if ( uniform.value !== globalState ) { + + uniform.value = globalState; + uniform.needsUpdate = numGlobalPlanes > 0; } - request.onerror = onError; + scope.numPlanes = numGlobalPlanes; - request.open( 'GET', url, true ); - request.responseType = "arraybuffer"; - request.send( null ); + } - return texture; + function projectPlanes( planes, camera, dstOffset, skipTransform ) { - }, + var nPlanes = planes !== null ? planes.length : 0, + dstArray = null; - loadTextureCube: function ( array, mapping, onLoad, onError ) { + if ( nPlanes !== 0 ) { - var images = []; - images.loadCount = 0; + dstArray = uniform.value; - var texture = new THREE.Texture(); - texture.image = images; - if ( mapping !== undefined ) texture.mapping = mapping; + if ( skipTransform !== true || dstArray === null ) { - // no flipping needed for cube textures + var flatSize = dstOffset + nPlanes * 4, + viewMatrix = camera.matrixWorldInverse; - texture.flipY = false; + viewNormalMatrix.getNormalMatrix( viewMatrix ); - for ( var i = 0, il = array.length; i < il; ++ i ) { + if ( dstArray === null || dstArray.length < flatSize ) { - var cubeImage = new Image(); - images[ i ] = cubeImage; + dstArray = new Float32Array( flatSize ); - cubeImage.onload = function () { + } - images.loadCount += 1; + for ( var i = 0, i4 = dstOffset; + i !== nPlanes; ++ i, i4 += 4 ) { - if ( images.loadCount === 6 ) { + plane.copy( planes[ i ] ). + applyMatrix4( viewMatrix, viewNormalMatrix ); - texture.needsUpdate = true; - if ( onLoad ) onLoad( texture ); + plane.normal.toArray( dstArray, i4 ); + dstArray[ i4 + 3 ] = plane.constant; } - }; - - cubeImage.onerror = onError; + } - cubeImage.crossOrigin = this.crossOrigin; - cubeImage.src = array[ i ]; + uniform.value = dstArray; + uniform.needsUpdate = true; } - return texture; + scope.numPlanes = nPlanes; + return dstArray; - }, + } - loadCompressedTextureCube: function ( array, mapping, onLoad, onError ) { +}; - var images = []; - images.loadCount = 0; - var texture = new THREE.CompressedTexture(); - texture.image = images; - if ( mapping !== undefined ) texture.mapping = mapping; +// File:src/renderers/webgl/WebGLIndexedBufferRenderer.js - // no flipping for cube textures - // (also flipping doesn't work for compressed textures ) +/** +* @author mrdoob / http://mrdoob.com/ +*/ - texture.flipY = false; +THREE.WebGLIndexedBufferRenderer = function ( _gl, extensions, _infoRender ) { - // can't generate mipmaps for compressed textures - // mips must be embedded in DDS files + var mode; - texture.generateMipmaps = false; + function setMode( value ) { - var generateCubeFaceCallback = function ( rq, img ) { + mode = value; - return function () { + } - var buffer = rq.response; - var dds = THREE.ImageUtils.parseDDS( buffer, true ); + var type, size; - img.format = dds.format; + function setIndex( index ) { - img.mipmaps = dds.mipmaps; - img.width = dds.width; - img.height = dds.height; + if ( index.array instanceof Uint32Array && extensions.get( 'OES_element_index_uint' ) ) { - images.loadCount += 1; + type = _gl.UNSIGNED_INT; + size = 4; - if ( images.loadCount === 6 ) { + } else { - texture.format = dds.format; - texture.needsUpdate = true; - if ( onLoad ) onLoad( texture ); + type = _gl.UNSIGNED_SHORT; + size = 2; - } + } - } + } - } + function render( start, count ) { - // compressed cubemap textures as 6 separate DDS files + _gl.drawElements( mode, count, type, start * size ); - if ( array instanceof Array ) { + _infoRender.calls ++; + _infoRender.vertices += count; + if ( mode === _gl.TRIANGLES ) _infoRender.faces += count / 3; - for ( var i = 0, il = array.length; i < il; ++ i ) { + } - var cubeImage = {}; - images[ i ] = cubeImage; + function renderInstances( geometry, start, count ) { - var request = new XMLHttpRequest(); + var extension = extensions.get( 'ANGLE_instanced_arrays' ); - request.onload = generateCubeFaceCallback( request, cubeImage ); - request.onerror = onError; + if ( extension === null ) { - var url = array[ i ]; + console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' ); + return; - request.open( 'GET', url, true ); - request.responseType = "arraybuffer"; - request.send( null ); + } - } + extension.drawElementsInstancedANGLE( mode, count, type, start * size, geometry.maxInstancedCount ); - // compressed cubemap texture stored in a single DDS file + _infoRender.calls ++; + _infoRender.vertices += count * geometry.maxInstancedCount; + if ( mode === _gl.TRIANGLES ) _infoRender.faces += geometry.maxInstancedCount * count / 3; + } - } else { + this.setMode = setMode; + this.setIndex = setIndex; + this.render = render; + this.renderInstances = renderInstances; + +}; - var url = array; - var request = new XMLHttpRequest(); +// File:src/renderers/webgl/WebGLExtensions.js - request.onload = function( ) { +/** +* @author mrdoob / http://mrdoob.com/ +*/ - var buffer = request.response; - var dds = THREE.ImageUtils.parseDDS( buffer, true ); +THREE.WebGLExtensions = function ( gl ) { - if ( dds.isCubemap ) { + var extensions = {}; - var faces = dds.mipmaps.length / dds.mipmapCount; + this.get = function ( name ) { - for ( var f = 0; f < faces; f ++ ) { + if ( extensions[ name ] !== undefined ) { - images[ f ] = { mipmaps : [] }; + return extensions[ name ]; - for ( var i = 0; i < dds.mipmapCount; i ++ ) { + } - images[ f ].mipmaps.push( dds.mipmaps[ f * dds.mipmapCount + i ] ); - images[ f ].format = dds.format; - images[ f ].width = dds.width; - images[ f ].height = dds.height; + var extension; - } + switch ( name ) { - } + case 'WEBGL_depth_texture': + extension = gl.getExtension( 'WEBGL_depth_texture' ) || gl.getExtension( 'MOZ_WEBGL_depth_texture' ) || gl.getExtension( 'WEBKIT_WEBGL_depth_texture' ); + break; - texture.format = dds.format; - texture.needsUpdate = true; - if ( onLoad ) onLoad( texture ); + case 'EXT_texture_filter_anisotropic': + extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' ); + break; - } + case 'WEBGL_compressed_texture_s3tc': + extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' ); + break; - } + case 'WEBGL_compressed_texture_pvrtc': + extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' ); + break; - request.onerror = onError; + case 'WEBGL_compressed_texture_etc1': + extension = gl.getExtension( 'WEBGL_compressed_texture_etc1' ); + break; - request.open( 'GET', url, true ); - request.responseType = "arraybuffer"; - request.send( null ); + default: + extension = gl.getExtension( name ); } - return texture; + if ( extension === null ) { - }, + console.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' ); + + } - parseDDS: function ( buffer, loadMipmaps ) { + extensions[ name ] = extension; - var dds = { mipmaps: [], width: 0, height: 0, format: null, mipmapCount: 1 }; + return extension; - // Adapted from @toji's DDS utils - // https://github.com/toji/webgl-texture-utils/blob/master/texture-util/dds.js + }; - // All values and structures referenced from: - // http://msdn.microsoft.com/en-us/library/bb943991.aspx/ +}; - var DDS_MAGIC = 0x20534444; +// File:src/renderers/webgl/WebGLCapabilities.js - var DDSD_CAPS = 0x1, - DDSD_HEIGHT = 0x2, - DDSD_WIDTH = 0x4, - DDSD_PITCH = 0x8, - DDSD_PIXELFORMAT = 0x1000, - DDSD_MIPMAPCOUNT = 0x20000, - DDSD_LINEARSIZE = 0x80000, - DDSD_DEPTH = 0x800000; +THREE.WebGLCapabilities = function ( gl, extensions, parameters ) { - var DDSCAPS_COMPLEX = 0x8, - DDSCAPS_MIPMAP = 0x400000, - DDSCAPS_TEXTURE = 0x1000; + var maxAnisotropy; - var DDSCAPS2_CUBEMAP = 0x200, - DDSCAPS2_CUBEMAP_POSITIVEX = 0x400, - DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800, - DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000, - DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000, - DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000, - DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000, - DDSCAPS2_VOLUME = 0x200000; + function getMaxAnisotropy() { - var DDPF_ALPHAPIXELS = 0x1, - DDPF_ALPHA = 0x2, - DDPF_FOURCC = 0x4, - DDPF_RGB = 0x40, - DDPF_YUV = 0x200, - DDPF_LUMINANCE = 0x20000; + if ( maxAnisotropy !== undefined ) return maxAnisotropy; - function fourCCToInt32( value ) { + var extension = extensions.get( 'EXT_texture_filter_anisotropic' ); - return value.charCodeAt(0) + - (value.charCodeAt(1) << 8) + - (value.charCodeAt(2) << 16) + - (value.charCodeAt(3) << 24); + if ( extension !== null ) { - } + maxAnisotropy = gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ); - function int32ToFourCC( value ) { + } else { + + maxAnisotropy = 0; - return String.fromCharCode( - value & 0xff, - (value >> 8) & 0xff, - (value >> 16) & 0xff, - (value >> 24) & 0xff - ); } - var FOURCC_DXT1 = fourCCToInt32("DXT1"); - var FOURCC_DXT3 = fourCCToInt32("DXT3"); - var FOURCC_DXT5 = fourCCToInt32("DXT5"); + return maxAnisotropy; + + } - var headerLengthInt = 31; // The header length in 32 bit ints + function getMaxPrecision( precision ) { - // Offsets into the header array + if ( precision === 'highp' ) { - var off_magic = 0; + if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.HIGH_FLOAT ).precision > 0 && + gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).precision > 0 ) { - var off_size = 1; - var off_flags = 2; - var off_height = 3; - var off_width = 4; + return 'highp'; - var off_mipmapCount = 7; + } - var off_pfFlags = 20; - var off_pfFourCC = 21; + precision = 'mediump'; - var off_caps = 27; - var off_caps2 = 28; - var off_caps3 = 29; - var off_caps4 = 30; + } - // Parse header + if ( precision === 'mediump' ) { - var header = new Int32Array( buffer, 0, headerLengthInt ); + if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).precision > 0 && + gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).precision > 0 ) { - if ( header[ off_magic ] !== DDS_MAGIC ) { + return 'mediump'; - console.error( "ImageUtils.parseDDS(): Invalid magic number in DDS header" ); - return dds; + } } - if ( ! header[ off_pfFlags ] & DDPF_FOURCC ) { + return 'lowp'; - console.error( "ImageUtils.parseDDS(): Unsupported format, must contain a FourCC code" ); - return dds; - - } + } - var blockBytes; + this.getMaxAnisotropy = getMaxAnisotropy; + this.getMaxPrecision = getMaxPrecision; - var fourCC = header[ off_pfFourCC ]; + this.precision = parameters.precision !== undefined ? parameters.precision : 'highp'; + this.logarithmicDepthBuffer = parameters.logarithmicDepthBuffer !== undefined ? parameters.logarithmicDepthBuffer : false; - switch ( fourCC ) { + 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 ); - case FOURCC_DXT1: + 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 ); - blockBytes = 8; - dds.format = THREE.RGB_S3TC_DXT1_Format; - break; + this.vertexTextures = this.maxVertexTextures > 0; + this.floatFragmentTextures = !! extensions.get( 'OES_texture_float' ); + this.floatVertexTextures = this.vertexTextures && this.floatFragmentTextures; - case FOURCC_DXT3: + var _maxPrecision = getMaxPrecision( this.precision ); - blockBytes = 16; - dds.format = THREE.RGBA_S3TC_DXT3_Format; - break; + if ( _maxPrecision !== this.precision ) { - case FOURCC_DXT5: + console.warn( 'THREE.WebGLRenderer:', this.precision, 'not supported, using', _maxPrecision, 'instead.' ); + this.precision = _maxPrecision; - blockBytes = 16; - dds.format = THREE.RGBA_S3TC_DXT5_Format; - break; + } - default: + if ( this.logarithmicDepthBuffer ) { - console.error( "ImageUtils.parseDDS(): Unsupported FourCC code: ", int32ToFourCC( fourCC ) ); - return dds; + this.logarithmicDepthBuffer = !! extensions.get( 'EXT_frag_depth' ); - } + } - dds.mipmapCount = 1; +}; - if ( header[ off_flags ] & DDSD_MIPMAPCOUNT && loadMipmaps !== false ) { +// File:src/renderers/webgl/WebGLGeometries.js - dds.mipmapCount = Math.max( 1, header[ off_mipmapCount ] ); +/** +* @author mrdoob / http://mrdoob.com/ +*/ - } +THREE.WebGLGeometries = function ( gl, properties, info ) { - //TODO: Verify that all faces of the cubemap are present with DDSCAPS2_CUBEMAP_POSITIVEX, etc. + var geometries = {}; - dds.isCubemap = header[ off_caps2 ] & DDSCAPS2_CUBEMAP ? true : false; + function get( object ) { - dds.width = header[ off_width ]; - dds.height = header[ off_height ]; + var geometry = object.geometry; - var dataOffset = header[ off_size ] + 4; + if ( geometries[ geometry.id ] !== undefined ) { - // Extract mipmaps buffers + return geometries[ geometry.id ]; - var width = dds.width; - var height = dds.height; + } - var faces = dds.isCubemap ? 6 : 1; + geometry.addEventListener( 'dispose', onGeometryDispose ); - for ( var face = 0; face < faces; face ++ ) { + var buffergeometry; - for ( var i = 0; i < dds.mipmapCount; i ++ ) { + if ( geometry instanceof THREE.BufferGeometry ) { - var dataLength = Math.max( 4, width ) / 4 * Math.max( 4, height ) / 4 * blockBytes; - var byteArray = new Uint8Array( buffer, dataOffset, dataLength ); + buffergeometry = geometry; - var mipmap = { "data": byteArray, "width": width, "height": height }; - dds.mipmaps.push( mipmap ); + } else if ( geometry instanceof THREE.Geometry ) { - dataOffset += dataLength; + if ( geometry._bufferGeometry === undefined ) { - width = Math.max( width * 0.5, 1 ); - height = Math.max( height * 0.5, 1 ); + geometry._bufferGeometry = new THREE.BufferGeometry().setFromObject( object ); } - width = dds.width; - height = dds.height; + buffergeometry = geometry._bufferGeometry; } - return dds; + geometries[ geometry.id ] = buffergeometry; - }, + info.memory.geometries ++; + + return buffergeometry; + + } - getNormalMap: function ( image, depth ) { + function onGeometryDispose( event ) { - // Adapted from http://www.paulbrunt.co.uk/lab/heightnormal/ + var geometry = event.target; + var buffergeometry = geometries[ geometry.id ]; - var cross = function ( a, b ) { + if ( buffergeometry.index !== null ) { - 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 ] ]; + deleteAttribute( buffergeometry.index ); } - var subtract = function ( a, b ) { + deleteAttributes( buffergeometry.attributes ); - return [ a[ 0 ] - b[ 0 ], a[ 1 ] - b[ 1 ], a[ 2 ] - b[ 2 ] ]; + geometry.removeEventListener( 'dispose', onGeometryDispose ); - } + delete geometries[ geometry.id ]; - var normalize = function ( a ) { + // TODO - 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 ]; + var property = properties.get( geometry ); - } + if ( property.wireframe ) { - depth = depth | 1; + deleteAttribute( property.wireframe ); - var width = image.width; - var height = image.height; + } - var canvas = document.createElement( 'canvas' ); - canvas.width = width; - canvas.height = height; + properties.delete( geometry ); - var context = canvas.getContext( '2d' ); - context.drawImage( image, 0, 0 ); + var bufferproperty = properties.get( buffergeometry ); - var data = context.getImageData( 0, 0, width, height ).data; - var imageData = context.createImageData( width, height ); - var output = imageData.data; + if ( bufferproperty.wireframe ) { - for ( var x = 0; x < width; x ++ ) { + deleteAttribute( bufferproperty.wireframe ); - 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; + properties.delete( buffergeometry ); - 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 ] ); + // - var normals = []; - var num_points = points.length; + info.memory.geometries --; - for ( var i = 0; i < num_points; i ++ ) { + } - 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 ) ) ); + function getAttributeBuffer( attribute ) { - } + if ( attribute instanceof THREE.InterleavedBufferAttribute ) { - var normal = [ 0, 0, 0 ]; + return properties.get( attribute.data ).__webglBuffer; - for ( var i = 0; i < normals.length; i ++ ) { + } - normal[ 0 ] += normals[ i ][ 0 ]; - normal[ 1 ] += normals[ i ][ 1 ]; - normal[ 2 ] += normals[ i ][ 2 ]; + return properties.get( attribute ).__webglBuffer; - } + } - normal[ 0 ] /= normals.length; - normal[ 1 ] /= normals.length; - normal[ 2 ] /= normals.length; + function deleteAttribute( attribute ) { - var idx = ( y * width + x ) * 4; + var buffer = getAttributeBuffer( attribute ); - 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 ( buffer !== undefined ) { - } + gl.deleteBuffer( buffer ); + removeAttributeBuffer( attribute ); } - context.putImageData( imageData, 0, 0 ); + } - return canvas; + function deleteAttributes( attributes ) { - }, + for ( var name in attributes ) { - generateDataTexture: function ( width, height, color ) { + deleteAttribute( attributes[ name ] ); - 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 ); + } - for ( var i = 0; i < size; i ++ ) { + function removeAttributeBuffer( attribute ) { - data[ i * 3 ] = r; - data[ i * 3 + 1 ] = g; - data[ i * 3 + 2 ] = b; + if ( attribute instanceof THREE.InterleavedBufferAttribute ) { - } + properties.delete( attribute.data ); - var texture = new THREE.DataTexture( data, width, height, THREE.RGBFormat ); - texture.needsUpdate = true; + } else { - return texture; + properties.delete( attribute ); + + } } + this.get = get; + }; +// File:src/renderers/webgl/WebGLLights.js + /** - * @author alteredq / http://alteredqualia.com/ - */ +* @author mrdoob / http://mrdoob.com/ +*/ -THREE.SceneUtils = { +THREE.WebGLLights = function () { - createMultiMaterialObject: function ( geometry, materials ) { + var lights = {}; - var group = new THREE.Object3D(); + this.get = function ( light ) { - for ( var i = 0, l = materials.length; i < l; i ++ ) { + if ( lights[ light.id ] !== undefined ) { - group.add( new THREE.Mesh( geometry, materials[ i ] ) ); + return lights[ light.id ]; } - return group; + var uniforms; - }, + switch ( light.type ) { - detach : function ( child, parent, scene ) { + case 'DirectionalLight': + uniforms = { + direction: new THREE.Vector3(), + color: new THREE.Color(), - child.applyMatrix( parent.matrixWorld ); - parent.remove( child ); - scene.add( child ); + shadow: false, + shadowBias: 0, + shadowRadius: 1, + shadowMapSize: new THREE.Vector2() + }; + break; - }, + case 'SpotLight': + uniforms = { + position: new THREE.Vector3(), + direction: new THREE.Vector3(), + color: new THREE.Color(), + distance: 0, + coneCos: 0, + penumbraCos: 0, + decay: 0, + + shadow: false, + shadowBias: 0, + shadowRadius: 1, + shadowMapSize: new THREE.Vector2() + }; + break; - attach: function ( child, scene, parent ) { + case 'PointLight': + uniforms = { + position: new THREE.Vector3(), + color: new THREE.Color(), + distance: 0, + decay: 0, + + shadow: false, + shadowBias: 0, + shadowRadius: 1, + shadowMapSize: new THREE.Vector2() + }; + break; - var matrixWorldInverse = new THREE.Matrix4(); - matrixWorldInverse.getInverse( parent.matrixWorld ); - child.applyMatrix( matrixWorldInverse ); + case 'HemisphereLight': + uniforms = { + direction: new THREE.Vector3(), + skyColor: new THREE.Color(), + groundColor: new THREE.Color() + }; + break; - scene.remove( child ); - parent.add( child ); + } - } + lights[ light.id ] = uniforms; + + return uniforms; + + }; }; -/** - * @author zz85 / http://www.lab4games.net/zz85/blog - * @author alteredq / http://alteredqualia.com/ - * - * For Text operations in three.js (See TextGeometry) - * - * It uses techniques used in: - * - * typeface.js and canvastext - * For converting fonts and rendering with javascript - * http://typeface.neocracy.org - * - * Triangulation ported from AS3 - * Simple Polygon Triangulation - * http://actionsnippet.com/?p=1462 - * - * A Method to triangulate shapes with holes - * http://www.sakri.net/blog/2009/06/12/an-approach-to-triangulating-polygons-with-holes/ - * - */ +// File:src/renderers/webgl/WebGLObjects.js -THREE.FontUtils = { +/** +* @author mrdoob / http://mrdoob.com/ +*/ - faces : {}, +THREE.WebGLObjects = function ( gl, properties, info ) { - // Just for now. face[weight][style] + var geometries = new THREE.WebGLGeometries( gl, properties, info ); - face : "helvetiker", - weight: "normal", - style : "normal", - size : 150, - divisions : 10, + // - getFace : function() { + function update( object ) { - return this.faces[ this.face ][ this.weight ][ this.style ]; + // TODO: Avoid updating twice (when using shadowMap). Maybe add frame counter. - }, + var geometry = geometries.get( object ); - loadFace : function( data ) { + if ( object.geometry instanceof THREE.Geometry ) { - var family = data.familyName.toLowerCase(); + geometry.updateFromObject( object ); - var ThreeFont = this; + } - ThreeFont.faces[ family ] = ThreeFont.faces[ family ] || {}; + var index = geometry.index; + var attributes = geometry.attributes; - ThreeFont.faces[ family ][ data.cssFontWeight ] = ThreeFont.faces[ family ][ data.cssFontWeight ] || {}; - ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data; + if ( index !== null ) { - var face = ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data; + updateAttribute( index, gl.ELEMENT_ARRAY_BUFFER ); - return data; + } - }, + for ( var name in attributes ) { - drawText : function( text ) { + updateAttribute( attributes[ name ], gl.ARRAY_BUFFER ); - var characterPts = [], allPts = []; + } - // RenderText + // morph targets - var i, p, - face = this.getFace(), - scale = this.size / face.resolution, - offset = 0, - chars = String( text ).split( '' ), - length = chars.length; + var morphAttributes = geometry.morphAttributes; - var fontPaths = []; + for ( var name in morphAttributes ) { - for ( i = 0; i < length; i ++ ) { + var array = morphAttributes[ name ]; - var path = new THREE.Path(); + for ( var i = 0, l = array.length; i < l; i ++ ) { - var ret = this.extractGlyphPoints( chars[ i ], face, scale, offset, path ); - offset += ret.offset; + updateAttribute( array[ i ], gl.ARRAY_BUFFER ); - fontPaths.push( ret.path ); + } } - // get the width + return geometry; - var width = offset / 2; - // - // for ( p = 0; p < allPts.length; p++ ) { - // - // allPts[ p ].x -= width; - // - // } + } - //var extract = this.extractPoints( allPts, characterPts ); - //extract.contour = allPts; + function updateAttribute( attribute, bufferType ) { - //extract.paths = fontPaths; - //extract.offset = width; + var data = ( attribute instanceof THREE.InterleavedBufferAttribute ) ? attribute.data : attribute; - return { paths : fontPaths, offset : width }; + var attributeProperties = properties.get( data ); - }, + if ( attributeProperties.__webglBuffer === undefined ) { + createBuffer( attributeProperties, data, bufferType ); + } else if ( attributeProperties.version !== data.version ) { + updateBuffer( attributeProperties, data, bufferType ); - extractGlyphPoints : function( c, face, scale, offset, path ) { + } - var pts = []; + } - var i, i2, divisions, - outline, action, length, - scaleX, scaleY, - x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2, - laste, - glyph = face.glyphs[ c ] || face.glyphs[ '?' ]; + function createBuffer( attributeProperties, data, bufferType ) { - if ( !glyph ) return; + attributeProperties.__webglBuffer = gl.createBuffer(); + gl.bindBuffer( bufferType, attributeProperties.__webglBuffer ); - if ( glyph.o ) { + var usage = data.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW; - outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) ); - length = outline.length; + gl.bufferData( bufferType, data.array, usage ); - scaleX = scale; - scaleY = scale; + attributeProperties.version = data.version; - for ( i = 0; i < length; ) { + } - action = outline[ i ++ ]; + function updateBuffer( attributeProperties, data, bufferType ) { - //console.log( action ); + gl.bindBuffer( bufferType, attributeProperties.__webglBuffer ); - switch( action ) { + if ( data.dynamic === false || data.updateRange.count === - 1 ) { - case 'm': + // Not using update ranges - // Move To + gl.bufferSubData( bufferType, 0, data.array ); - x = outline[ i++ ] * scaleX + offset; - y = outline[ i++ ] * scaleY; + } else if ( data.updateRange.count === 0 ) { - path.moveTo( x, y ); - break; + 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.' ); - case 'l': + } else { - // Line To + gl.bufferSubData( bufferType, data.updateRange.offset * data.array.BYTES_PER_ELEMENT, + data.array.subarray( data.updateRange.offset, data.updateRange.offset + data.updateRange.count ) ); - x = outline[ i++ ] * scaleX + offset; - y = outline[ i++ ] * scaleY; - path.lineTo(x,y); - break; + data.updateRange.count = 0; // reset range - case 'q': + } - // QuadraticCurveTo + attributeProperties.version = data.version; - cpx = outline[ i++ ] * scaleX + offset; - cpy = outline[ i++ ] * scaleY; - cpx1 = outline[ i++ ] * scaleX + offset; - cpy1 = outline[ i++ ] * scaleY; + } - path.quadraticCurveTo(cpx1, cpy1, cpx, cpy); + function getAttributeBuffer( attribute ) { - laste = pts[ pts.length - 1 ]; + if ( attribute instanceof THREE.InterleavedBufferAttribute ) { - if ( laste ) { + return properties.get( attribute.data ).__webglBuffer; - cpx0 = laste.x; - cpy0 = laste.y; + } - for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) { + return properties.get( attribute ).__webglBuffer; - var t = i2 / divisions; - var tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx ); - var ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy ); - } + } - } + function getWireframeAttribute( geometry ) { - break; + var property = properties.get( geometry ); - case 'b': + if ( property.wireframe !== undefined ) { - // Cubic Bezier Curve + return property.wireframe; - 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; + } - path.bezierCurveTo( cpx, cpy, cpx1, cpy1, cpx2, cpy2 ); + var indices = []; - laste = pts[ pts.length - 1 ]; + var index = geometry.index; + var attributes = geometry.attributes; + var position = attributes.position; - if ( laste ) { + // console.time( 'wireframe' ); - cpx0 = laste.x; - cpy0 = laste.y; + if ( index !== null ) { - for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) { + var edges = {}; + var array = index.array; - var t = i2 / divisions; - var tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx ); - var ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy ); + for ( var i = 0, l = array.length; i < l; i += 3 ) { - } + 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 ); - break; + } - } + } else { + + var array = attributes.position.array; + + for ( var i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) { + + var a = i + 0; + var b = i + 1; + var c = i + 2; + + indices.push( a, b, b, c, c, a ); } + } + // console.timeEnd( 'wireframe' ); + var TypeArray = position.count > 65535 ? Uint32Array : Uint16Array; + var attribute = new THREE.BufferAttribute( new TypeArray( indices ), 1 ); - return { offset: glyph.ha*scale, path:path}; - } + updateAttribute( attribute, gl.ELEMENT_ARRAY_BUFFER ); -}; + property.wireframe = attribute; + return attribute; -THREE.FontUtils.generateShapes = function( text, parameters ) { + } - // Parameters + function checkEdge( edges, a, b ) { - parameters = parameters || {}; + if ( a > b ) { - var size = parameters.size !== undefined ? parameters.size : 100; - var curveSegments = parameters.curveSegments !== undefined ? parameters.curveSegments: 4; + var tmp = a; + a = b; + b = tmp; - var font = parameters.font !== undefined ? parameters.font : "helvetiker"; - var weight = parameters.weight !== undefined ? parameters.weight : "normal"; - var style = parameters.style !== undefined ? parameters.style : "normal"; + } - THREE.FontUtils.size = size; - THREE.FontUtils.divisions = curveSegments; + var list = edges[ a ]; - THREE.FontUtils.face = font; - THREE.FontUtils.weight = weight; - THREE.FontUtils.style = style; + if ( list === undefined ) { - // Get a Font data json object + edges[ a ] = [ b ]; + return true; - var data = THREE.FontUtils.drawText( text ); + } else if ( list.indexOf( b ) === -1 ) { - var paths = data.paths; - var shapes = []; + list.push( b ); + return true; - for ( var p = 0, pl = paths.length; p < pl; p ++ ) { + } - Array.prototype.push.apply( shapes, paths[ p ].toShapes() ); + return false; } - return shapes; + this.getAttributeBuffer = getAttributeBuffer; + this.getWireframeAttribute = getWireframeAttribute; + + this.update = update; }; +// File:src/renderers/webgl/WebGLProgram.js -/** - * 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.WebGLProgram = ( function () { + var programIdCount = 0; -( function( namespace ) { + function getEncodingComponents( encoding ) { - var EPSILON = 0.0000000001; + switch ( encoding ) { - // takes in an contour array and returns + case THREE.LinearEncoding: + return [ 'Linear','( value )' ]; + case THREE.sRGBEncoding: + return [ 'sRGB','( value )' ]; + case THREE.RGBEEncoding: + return [ 'RGBE','( value )' ]; + case THREE.RGBM7Encoding: + return [ 'RGBM','( value, 7.0 )' ]; + case THREE.RGBM16Encoding: + return [ 'RGBM','( value, 16.0 )' ]; + case THREE.RGBDEncoding: + return [ 'RGBD','( value, 256.0 )' ]; + case THREE.GammaEncoding: + return [ 'Gamma','( value, float( GAMMA_FACTOR ) )' ]; + default: + throw new Error( 'unsupported encoding: ' + encoding ); - var process = function( contour, indices ) { + } - var n = contour.length; + } - if ( n < 3 ) return null; + function getTexelDecodingFunction( functionName, encoding ) { - var result = [], - verts = [], - vertIndices = []; + var components = getEncodingComponents( encoding ); + return "vec4 " + functionName + "( vec4 value ) { return " + components[ 0 ] + "ToLinear" + components[ 1 ] + "; }"; - /* we want a counter-clockwise polygon in verts */ + } - var u, v, w; + function getTexelEncodingFunction( functionName, encoding ) { - if ( area( contour ) > 0.0 ) { + var components = getEncodingComponents( encoding ); + return "vec4 " + functionName + "( vec4 value ) { return LinearTo" + components[ 0 ] + components[ 1 ] + "; }"; - for ( v = 0; v < n; v++ ) verts[ v ] = v; + } - } else { + function getToneMappingFunction( functionName, toneMapping ) { - for ( v = 0; v < n; v++ ) verts[ v ] = ( n - 1 ) - v; + var toneMappingName; - } + switch ( toneMapping ) { - var nv = n; + case THREE.LinearToneMapping: + toneMappingName = "Linear"; + break; + + case THREE.ReinhardToneMapping: + toneMappingName = "Reinhard"; + break; - /* remove nv - 2 vertices, creating 1 triangle every time */ + case THREE.Uncharted2ToneMapping: + toneMappingName = "Uncharted2"; + break; - var count = 2 * nv; /* error detection */ + case THREE.CineonToneMapping: + toneMappingName = "OptimizedCineon"; + break; - for( v = nv - 1; nv > 2; ) { + default: + throw new Error( 'unsupported toneMapping: ' + toneMapping ); - /* if we loop, it is probably a non-simple polygon */ + } - if ( ( count-- ) <= 0 ) { + return "vec3 " + functionName + "( vec3 color ) { return " + toneMappingName + "ToneMapping( color ); }"; - //** Triangulate: ERROR - probable bad polygon! + } - //throw ( "Warning, unable to triangulate polygon!" ); - //return null; - // Sometimes warning is fine, especially polygons are triangulated in reverse. - console.log( "Warning, unable to triangulate polygon!" ); + function generateExtensions( extensions, parameters, rendererExtensions ) { - if ( indices ) return vertIndices; - return result; + extensions = extensions || {}; - } + var chunks = [ + ( extensions.derivatives || parameters.envMapCubeUV || parameters.bumpMap || parameters.normalMap || parameters.flatShading ) ? '#extension GL_OES_standard_derivatives : enable' : '', + ( extensions.fragDepth || parameters.logarithmicDepthBuffer ) && rendererExtensions.get( 'EXT_frag_depth' ) ? '#extension GL_EXT_frag_depth : enable' : '', + ( extensions.drawBuffers ) && rendererExtensions.get( 'WEBGL_draw_buffers' ) ? '#extension GL_EXT_draw_buffers : require' : '', + ( extensions.shaderTextureLOD || parameters.envMap ) && rendererExtensions.get( 'EXT_shader_texture_lod' ) ? '#extension GL_EXT_shader_texture_lod : enable' : '', + ]; - /* three consecutive vertices in current polygon, */ + return chunks.filter( filterEmptyLine ).join( '\n' ); - u = v; if ( nv <= u ) u = 0; /* previous */ - v = u + 1; if ( nv <= v ) v = 0; /* new v */ - w = v + 1; if ( nv <= w ) w = 0; /* next */ + } - if ( snip( contour, u, v, w, nv, verts ) ) { + function generateDefines( defines ) { - var a, b, c, s, t; + var chunks = []; - /* true names of the vertices */ + for ( var name in defines ) { - a = verts[ u ]; - b = verts[ v ]; - c = verts[ w ]; + var value = defines[ name ]; - /* output Triangle */ + if ( value === false ) continue; - result.push( [ contour[ a ], - contour[ b ], - contour[ c ] ] ); + chunks.push( '#define ' + name + ' ' + value ); + } - vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] ); + return chunks.join( '\n' ); - /* remove v from the remaining polygon */ + } - for( s = v, t = v + 1; t < nv; s++, t++ ) { + function fetchAttributeLocations( gl, program, identifiers ) { - verts[ s ] = verts[ t ]; + var attributes = {}; - } + var n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES ); - nv--; + for ( var i = 0; i < n; i ++ ) { - /* reset error detection counter */ + var info = gl.getActiveAttrib( program, i ); + var name = info.name; - count = 2 * nv; + // console.log("THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name, i ); - } + attributes[ name ] = gl.getAttribLocation( program, name ); } - if ( indices ) return vertIndices; - return result; + return attributes; - }; + } - // calculate area of the contour polygon + function filterEmptyLine( string ) { - var area = function ( contour ) { + return string !== ''; - var n = contour.length; - var a = 0.0; + } - for( var p = n - 1, q = 0; q < n; p = q++ ) { + function replaceLightNums( string, parameters ) { - a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; + return string + .replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights ) + .replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights ) + .replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights ) + .replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights ); - } + } - return a * 0.5; + function parseIncludes( string ) { - }; + var pattern = /#include +<([\w\d.]+)>/g; - var snip = function ( contour, u, v, w, n, verts ) { + function replace( match, include ) { - var p; - var ax, ay, bx, by; - var cx, cy, px, py; + var replace = THREE.ShaderChunk[ include ]; - ax = contour[ verts[ u ] ].x; - ay = contour[ verts[ u ] ].y; + if ( replace === undefined ) { - bx = contour[ verts[ v ] ].x; - by = contour[ verts[ v ] ].y; + throw new Error( 'Can not resolve #include <' + include + '>' ); - cx = contour[ verts[ w ] ].x; - cy = contour[ verts[ w ] ].y; + } - if ( EPSILON > (((bx-ax)*(cy-ay)) - ((by-ay)*(cx-ax))) ) return false; + return parseIncludes( replace ); - 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; + return string.replace( pattern, replace ); - for ( p = 0; p < n; p++ ) { + } - if( (p === u) || (p === v) || (p === w) ) continue; + function unrollLoops( string ) { - px = contour[ verts[ p ] ].x - py = contour[ verts[ p ] ].y + var pattern = /for \( int i \= (\d+)\; i < (\d+)\; i \+\+ \) \{([\s\S]+?)(?=\})\}/g; - apx = px - ax; apy = py - ay; - bpx = px - bx; bpy = py - by; - cpx = px - cx; cpy = py - cy; + function replace( match, start, end, snippet ) { - // see if p is inside triangle abc + var unroll = ''; - aCROSSbp = aX*bpy - aY*bpx; - cCROSSap = cX*apy - cY*apx; - bCROSScp = bX*cpy - bY*cpx; + for ( var i = parseInt( start ); i < parseInt( end ); i ++ ) { - if ( (aCROSSbp >= -EPSILON) && (bCROSScp >= -EPSILON) && (cCROSSap >= -EPSILON) ) return false; + unroll += snippet.replace( /\[ i \]/g, '[ ' + i + ' ]' ); - } + } - return true; + return unroll; - }; + } + return string.replace( pattern, replace ); - namespace.Triangulate = process; - namespace.Triangulate.area = area; + } - return namespace; + return function WebGLProgram( renderer, code, material, parameters ) { -})(THREE.FontUtils); + var gl = renderer.context; -// To use the typeface.js face files, hook up the API -self._typeface_js = { faces: THREE.FontUtils.faces, loadFace: THREE.FontUtils.loadFace }; -THREE.typeface_js = self._typeface_js; + var extensions = material.extensions; + var defines = material.defines; -/** - * @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 - * - **/ + var vertexShader = material.__webglShader.vertexShader; + var fragmentShader = material.__webglShader.fragmentShader; -/************************************************************** - * Abstract Curve base class - **************************************************************/ + var shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC'; -THREE.Curve = function () { + if ( parameters.shadowMapType === THREE.PCFShadowMap ) { -}; + shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF'; -// Virtual base class method to overwrite and implement in subclasses -// - t [0 .. 1] + } else if ( parameters.shadowMapType === THREE.PCFSoftShadowMap ) { -THREE.Curve.prototype.getPoint = function ( t ) { + shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT'; - console.log( "Warning, getPoint() not implemented!" ); - return null; + } -}; + var envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; + var envMapModeDefine = 'ENVMAP_MODE_REFLECTION'; + var envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; -// Get point at relative position in curve according to arc length -// - u [0 .. 1] + if ( parameters.envMap ) { -THREE.Curve.prototype.getPointAt = function ( u ) { + switch ( material.envMap.mapping ) { - var t = this.getUtoTmapping( u ); - return this.getPoint( t ); + case THREE.CubeReflectionMapping: + case THREE.CubeRefractionMapping: + envMapTypeDefine = 'ENVMAP_TYPE_CUBE'; + break; -}; + case THREE.CubeUVReflectionMapping: + case THREE.CubeUVRefractionMapping: + envMapTypeDefine = 'ENVMAP_TYPE_CUBE_UV'; + break; + + case THREE.EquirectangularReflectionMapping: + case THREE.EquirectangularRefractionMapping: + envMapTypeDefine = 'ENVMAP_TYPE_EQUIREC'; + break; -// Get sequence of points using getPoint( t ) + case THREE.SphericalReflectionMapping: + envMapTypeDefine = 'ENVMAP_TYPE_SPHERE'; + break; -THREE.Curve.prototype.getPoints = function ( divisions ) { + } - if ( !divisions ) divisions = 5; + switch ( material.envMap.mapping ) { - var d, pts = []; + case THREE.CubeRefractionMapping: + case THREE.EquirectangularRefractionMapping: + envMapModeDefine = 'ENVMAP_MODE_REFRACTION'; + break; - for ( d = 0; d <= divisions; d ++ ) { + } - pts.push( this.getPoint( d / divisions ) ); + switch ( material.combine ) { - } + case THREE.MultiplyOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY'; + break; - return pts; + case THREE.MixOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_MIX'; + break; -}; + case THREE.AddOperation: + envMapBlendingDefine = 'ENVMAP_BLENDING_ADD'; + break; -// Get sequence of points using getPointAt( u ) + } -THREE.Curve.prototype.getSpacedPoints = function ( divisions ) { + } - if ( !divisions ) divisions = 5; + var gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0; - var d, pts = []; + // console.log( 'building new program ' ); - for ( d = 0; d <= divisions; d ++ ) { + // - pts.push( this.getPointAt( d / divisions ) ); + var customExtensions = generateExtensions( extensions, parameters, renderer.extensions ); - } + var customDefines = generateDefines( defines ); - return pts; + // -}; + var program = gl.createProgram(); -// Get total curve arc length + var prefixVertex, prefixFragment; -THREE.Curve.prototype.getLength = function () { + if ( material instanceof THREE.RawShaderMaterial ) { - var lengths = this.getLengths(); - return lengths[ lengths.length - 1 ]; + prefixVertex = [ -}; + customDefines -// Get list of cumulative segment lengths + ].filter( filterEmptyLine ).join( '\n' ); -THREE.Curve.prototype.getLengths = function ( divisions ) { + prefixFragment = [ - if ( !divisions ) divisions = (this.__arcLengthDivisions) ? (this.__arcLengthDivisions): 200; + customDefines - if ( this.cacheArcLengths - && ( this.cacheArcLengths.length == divisions + 1 ) - && !this.needsUpdate) { + ].filter( filterEmptyLine ).join( '\n' ); - //console.log( "cached", this.cacheArcLengths ); - return this.cacheArcLengths; + } else { - } + prefixVertex = [ - this.needsUpdate = false; + 'precision ' + parameters.precision + ' float;', + 'precision ' + parameters.precision + ' int;', - var cache = []; - var current, last = this.getPoint( 0 ); - var p, sum = 0; + '#define SHADER_NAME ' + material.__webglShader.name, - cache.push( 0 ); + customDefines, - for ( p = 1; p <= divisions; p ++ ) { + parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '', - current = this.getPoint ( p / divisions ); - sum += current.distanceTo( last ); - cache.push( sum ); - last = current; + '#define GAMMA_FACTOR ' + gammaFactorDefine, - } + '#define MAX_BONES ' + parameters.maxBones, - this.cacheArcLengths = cache; + 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.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', + parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', + parameters.alphaMap ? '#define USE_ALPHAMAP' : '', + parameters.vertexColors ? '#define USE_COLOR' : '', - return cache; // { sums: cache, sum:sum }; Sum is in the last element. + parameters.flatShading ? '#define FLAT_SHADED' : '', -}; + parameters.skinning ? '#define USE_SKINNING' : '', + parameters.useVertexTexture ? '#define BONE_TEXTURE' : '', + parameters.morphTargets ? '#define USE_MORPHTARGETS' : '', + parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '', + parameters.doubleSided ? '#define DOUBLE_SIDED' : '', + parameters.flipSided ? '#define FLIP_SIDED' : '', -THREE.Curve.prototype.updateArcLengths = function() { - this.needsUpdate = true; - this.getLengths(); -}; + '#define NUM_CLIPPING_PLANES ' + parameters.numClippingPlanes, -// Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equi distance + parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', + parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', -THREE.Curve.prototype.getUtoTmapping = function ( u, distance ) { + parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '', - var arcLengths = this.getLengths(); + parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', + parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '', - var i = 0, il = arcLengths.length; + 'uniform mat4 modelMatrix;', + 'uniform mat4 modelViewMatrix;', + 'uniform mat4 projectionMatrix;', + 'uniform mat4 viewMatrix;', + 'uniform mat3 normalMatrix;', + 'uniform vec3 cameraPosition;', - var targetArcLength; // The targeted u distance value to get + 'attribute vec3 position;', + 'attribute vec3 normal;', + 'attribute vec2 uv;', - if ( distance ) { + '#ifdef USE_COLOR', - targetArcLength = distance; + ' attribute vec3 color;', - } else { + '#endif', - targetArcLength = u * arcLengths[ il - 1 ]; + '#ifdef USE_MORPHTARGETS', - } + ' attribute vec3 morphTarget0;', + ' attribute vec3 morphTarget1;', + ' attribute vec3 morphTarget2;', + ' attribute vec3 morphTarget3;', - //var time = Date.now(); + ' #ifdef USE_MORPHNORMALS', - // binary search for the index with largest value smaller than target u distance + ' attribute vec3 morphNormal0;', + ' attribute vec3 morphNormal1;', + ' attribute vec3 morphNormal2;', + ' attribute vec3 morphNormal3;', - var low = 0, high = il - 1, comparison; + ' #else', - while ( low <= high ) { + ' attribute vec3 morphTarget4;', + ' attribute vec3 morphTarget5;', + ' attribute vec3 morphTarget6;', + ' attribute vec3 morphTarget7;', - 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 + ' #endif', - comparison = arcLengths[ i ] - targetArcLength; + '#endif', - if ( comparison < 0 ) { + '#ifdef USE_SKINNING', - low = i + 1; - continue; + ' attribute vec4 skinIndex;', + ' attribute vec4 skinWeight;', - } else if ( comparison > 0 ) { + '#endif', - high = i - 1; - continue; + '\n' - } else { + ].filter( filterEmptyLine ).join( '\n' ); - high = i; - break; + prefixFragment = [ - // DONE + customExtensions, - } + 'precision ' + parameters.precision + ' float;', + 'precision ' + parameters.precision + ' int;', - } + '#define SHADER_NAME ' + material.__webglShader.name, - i = high; + customDefines, - //console.log('b' , i, low, high, Date.now()- time); + parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest : '', - if ( arcLengths[ i ] == targetArcLength ) { + '#define GAMMA_FACTOR ' + gammaFactorDefine, - var t = i / ( il - 1 ); - return t; + ( 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.roughnessMap ? '#define USE_ROUGHNESSMAP' : '', + parameters.metalnessMap ? '#define USE_METALNESSMAP' : '', + parameters.alphaMap ? '#define USE_ALPHAMAP' : '', + parameters.vertexColors ? '#define USE_COLOR' : '', - // we could get finer grain at lengths, or use simple interpolatation between two points + parameters.flatShading ? '#define FLAT_SHADED' : '', - var lengthBefore = arcLengths[ i ]; - var lengthAfter = arcLengths[ i + 1 ]; + parameters.doubleSided ? '#define DOUBLE_SIDED' : '', + parameters.flipSided ? '#define FLIP_SIDED' : '', - var segmentLength = lengthAfter - lengthBefore; + '#define NUM_CLIPPING_PLANES ' + parameters.numClippingPlanes, - // determine where we are between the 'before' and 'after' points + parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '', + parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '', - var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; + parameters.premultipliedAlpha ? "#define PREMULTIPLIED_ALPHA" : '', - // add that fractional amount to t + parameters.physicallyCorrectLights ? "#define PHYSICALLY_CORRECT_LIGHTS" : '', - var t = ( i + segmentFraction ) / ( il -1 ); + parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '', + parameters.logarithmicDepthBuffer && renderer.extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '', - return t; + parameters.envMap && renderer.extensions.get( 'EXT_shader_texture_lod' ) ? '#define TEXTURE_LOD_EXT' : '', -}; + 'uniform mat4 viewMatrix;', + 'uniform vec3 cameraPosition;', -// 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 + ( parameters.toneMapping !== THREE.NoToneMapping ) ? "#define TONE_MAPPING" : '', + ( parameters.toneMapping !== THREE.NoToneMapping ) ? THREE.ShaderChunk[ 'tonemapping_pars_fragment' ] : '', // this code is required here because it is used by the toneMapping() function defined below + ( parameters.toneMapping !== THREE.NoToneMapping ) ? getToneMappingFunction( "toneMapping", parameters.toneMapping ) : '', -THREE.Curve.prototype.getTangent = function( t ) { + ( parameters.outputEncoding || parameters.mapEncoding || parameters.envMapEncoding || parameters.emissiveMapEncoding ) ? THREE.ShaderChunk[ 'encodings_pars_fragment' ] : '', // this code is required here because it is used by the various encoding/decoding function defined below + parameters.mapEncoding ? getTexelDecodingFunction( 'mapTexelToLinear', parameters.mapEncoding ) : '', + parameters.envMapEncoding ? getTexelDecodingFunction( 'envMapTexelToLinear', parameters.envMapEncoding ) : '', + parameters.emissiveMapEncoding ? getTexelDecodingFunction( 'emissiveMapTexelToLinear', parameters.emissiveMapEncoding ) : '', + parameters.outputEncoding ? getTexelEncodingFunction( "linearToOutputTexel", parameters.outputEncoding ) : '', - var delta = 0.0001; - var t1 = t - delta; - var t2 = t + delta; + parameters.depthPacking ? "#define DEPTH_PACKING " + material.depthPacking : '', - // Capping in case of danger + '\n' - if ( t1 < 0 ) t1 = 0; - if ( t2 > 1 ) t2 = 1; + ].filter( filterEmptyLine ).join( '\n' ); - var pt1 = this.getPoint( t1 ); - var pt2 = this.getPoint( t2 ); + } - var vec = pt2.clone().sub(pt1); - return vec.normalize(); + vertexShader = parseIncludes( vertexShader, parameters ); + vertexShader = replaceLightNums( vertexShader, parameters ); -}; + fragmentShader = parseIncludes( fragmentShader, parameters ); + fragmentShader = replaceLightNums( fragmentShader, parameters ); + if ( material instanceof THREE.ShaderMaterial === false ) { -THREE.Curve.prototype.getTangentAt = function ( u ) { + vertexShader = unrollLoops( vertexShader ); + fragmentShader = unrollLoops( fragmentShader ); - var t = this.getUtoTmapping( u ); - return this.getTangent( t ); + } -}; + var vertexGlsl = prefixVertex + vertexShader; + var fragmentGlsl = prefixFragment + fragmentShader; + // console.log( '*VERTEX*', vertexGlsl ); + // console.log( '*FRAGMENT*', fragmentGlsl ); + var glVertexShader = THREE.WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl ); + var glFragmentShader = THREE.WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl ); + gl.attachShader( program, glVertexShader ); + gl.attachShader( program, glFragmentShader ); + // Force a particular attribute to index 0. -/************************************************************** - * Utils - **************************************************************/ + if ( material.index0AttributeName !== undefined ) { -THREE.Curve.Utils = { + gl.bindAttribLocation( program, 0, material.index0AttributeName ); - tangentQuadraticBezier: function ( t, p0, p1, p2 ) { + } else if ( parameters.morphTargets === true ) { - return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 ); + // programs with morphTargets displace position out of attribute 0 + gl.bindAttribLocation( program, 0, 'position' ); - }, + } - // Puay Bing, thanks for helping with this derivative! + gl.linkProgram( program ); - tangentCubicBezier: function (t, p0, p1, p2, p3 ) { + var programLog = gl.getProgramInfoLog( program ); + var vertexLog = gl.getShaderInfoLog( glVertexShader ); + var fragmentLog = gl.getShaderInfoLog( glFragmentShader ); - 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; - }, + var runnable = true; + var haveDiagnostics = true; + // console.log( '**VERTEX**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glVertexShader ) ); + // console.log( '**FRAGMENT**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glFragmentShader ) ); - tangentSpline: function ( t, p0, p1, p2, p3 ) { + if ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) { - // To check if my formulas are correct + runnable = false; - 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 + console.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), 'gl.VALIDATE_STATUS', gl.getProgramParameter( program, gl.VALIDATE_STATUS ), 'gl.getProgramInfoLog', programLog, vertexLog, fragmentLog ); - return h00 + h10 + h01 + h11; + } else if ( programLog !== '' ) { - }, + console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', programLog ); - // Catmull-Rom + } else if ( vertexLog === '' || fragmentLog === '' ) { - interpolate: function( p0, p1, p2, p3, t ) { + haveDiagnostics = false; - 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; + } - } + if ( haveDiagnostics ) { -}; + this.diagnostics = { + runnable: runnable, + material: material, -// TODO: Transformation for Curves? + programLog: programLog, -/************************************************************** - * 3D Curves - **************************************************************/ + vertexShader: { -// A Factory method for creating new curve subclasses + log: vertexLog, + prefix: prefixVertex -THREE.Curve.create = function ( constructor, getPointFunc ) { + }, - constructor.prototype = Object.create( THREE.Curve.prototype ); - constructor.prototype.getPoint = getPointFunc; + fragmentShader: { - return constructor; + log: fragmentLog, + prefix: prefixFragment -}; + } -/** - * @author zz85 / http://www.lab4games.net/zz85/blog - * - **/ + }; -/************************************************************** - * Curved Path - a curve path is simply a array of connected - * curves, but retains the api of a curve - **************************************************************/ + } -THREE.CurvePath = function () { + // clean up - this.curves = []; - this.bends = []; - - this.autoClose = false; // Automatically closes the path -}; + gl.deleteShader( glVertexShader ); + gl.deleteShader( glFragmentShader ); -THREE.CurvePath.prototype = Object.create( THREE.Curve.prototype ); + // set up caching for uniform locations -THREE.CurvePath.prototype.add = function ( curve ) { + var cachedUniforms; - this.curves.push( curve ); + this.getUniforms = function() { -}; + if ( cachedUniforms === undefined ) { -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 -}; + cachedUniforms = + new THREE.WebGLUniforms( gl, program, renderer ); -THREE.CurvePath.prototype.closePath = function() { - // TODO Test - // and verify for vector3 (needs to implement equals) - // Add a line curve if start and end of lines are not connected - var startPoint = this.curves[0].getPoint(0); - var endPoint = this.curves[this.curves.length-1].getPoint(1); - - if (!startPoint.equals(endPoint)) { - this.curves.push( new THREE.LineCurve(endPoint, startPoint) ); - } - -}; + } -// To get accurate point with reference to -// entire path distance at time t, -// following has to be done: + return cachedUniforms; -// 1. Length of each sub path have to be known -// 2. Locate and identify type of curve -// 3. Get t for the curve -// 4. Return curve.getPointAt(t') + }; -THREE.CurvePath.prototype.getPoint = function( t ) { + // set up caching for attribute locations - var d = t * this.getLength(); - var curveLengths = this.getCurveLengths(); - var i = 0, diff, curve; + var cachedAttributes; - // To think about boundaries points. + this.getAttributes = function() { - while ( i < curveLengths.length ) { + if ( cachedAttributes === undefined ) { - if ( curveLengths[ i ] >= d ) { + cachedAttributes = fetchAttributeLocations( gl, program ); - diff = curveLengths[ i ] - d; - curve = this.curves[ i ]; + } - var u = 1 - diff / curve.getLength(); + return cachedAttributes; - return curve.getPointAt( u ); + }; - break; - } + // free resource - i ++; + this.destroy = function() { - } + gl.deleteProgram( program ); + this.program = undefined; - return null; + }; - // loop where sum != 0, sum > d , sum+1 maxX ) maxX = p.x; - else if ( p.x < minX ) minX = p.x; + return maxBones; - if ( p.y > maxY ) maxY = p.y; - else if ( p.y < minY ) minY = p.y; + } - if ( v3 ) { + } - if ( p.z > maxZ ) maxZ = p.z; - else if ( p.z < minZ ) minZ = p.z; + function getTextureEncodingFromMap( map, gammaOverrideLinear ) { - } + var encoding; - sum.add( p ); + if ( ! map ) { - } + encoding = THREE.LinearEncoding; - var ret = { + } else if ( map instanceof THREE.Texture ) { - minX: minX, - minY: minY, - maxX: maxX, - maxY: maxY, - centroid: sum.divideScalar( il ) + encoding = map.encoding; - }; + } else if ( map instanceof THREE.WebGLRenderTarget ) { - if ( v3 ) { + console.warn( "THREE.WebGLPrograms.getTextureEncodingFromMap: don't use render targets as textures. Use their .texture property instead." ); + encoding = map.texture.encoding; - ret.maxZ = maxZ; - ret.minZ = minZ; + } - } + // add backwards compatibility for WebGLRenderer.gammaInput/gammaOutput parameter, should probably be removed at some point. + if ( encoding === THREE.LinearEncoding && gammaOverrideLinear ) { - return ret; + encoding = THREE.GammaEncoding; -}; + } -/************************************************************** - * Create Geometries Helpers - **************************************************************/ + return encoding; -/// Generate geometry from path points (for Line or ParticleSystem objects) + } -THREE.CurvePath.prototype.createPointsGeometry = function( divisions ) { + this.getParameters = function ( material, lights, fog, nClipPlanes, object ) { - var pts = this.getPoints( divisions, true ); - return this.createGeometry( pts ); + var shaderID = shaderIDs[ material.type ]; -}; + // heuristics to create shader parameters according to lights in the scene + // (not to blow over maxLights budget) -// Generate geometry from equidistance sampling along the path + var maxBones = allocateBones( object ); + var precision = renderer.getPrecision(); -THREE.CurvePath.prototype.createSpacedPointsGeometry = function( divisions ) { + if ( material.precision !== null ) { - var pts = this.getSpacedPoints( divisions, true ); - return this.createGeometry( pts ); + precision = capabilities.getMaxPrecision( material.precision ); -}; + if ( precision !== material.precision ) { -THREE.CurvePath.prototype.createGeometry = function( points ) { + console.warn( 'THREE.WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.' ); - var geometry = new THREE.Geometry(); + } - for ( var i = 0; i < points.length; i ++ ) { + } - geometry.vertices.push( new THREE.Vector3( points[ i ].x, points[ i ].y, points[ i ].z || 0) ); + var currentRenderTarget = renderer.getCurrentRenderTarget(); - } + var parameters = { - return geometry; + shaderID: shaderID, -}; + precision: precision, + supportsVertexTextures: capabilities.vertexTextures, + outputEncoding: getTextureEncodingFromMap( ( ! currentRenderTarget ) ? null : currentRenderTarget.texture, renderer.gammaOutput ), + map: !! material.map, + mapEncoding: getTextureEncodingFromMap( material.map, renderer.gammaInput ), + envMap: !! material.envMap, + envMapMode: material.envMap && material.envMap.mapping, + envMapEncoding: getTextureEncodingFromMap( material.envMap, renderer.gammaInput ), + envMapCubeUV: ( !! material.envMap ) && ( ( material.envMap.mapping === THREE.CubeUVReflectionMapping ) || ( material.envMap.mapping === THREE.CubeUVRefractionMapping ) ), + lightMap: !! material.lightMap, + aoMap: !! material.aoMap, + emissiveMap: !! material.emissiveMap, + emissiveMapEncoding: getTextureEncodingFromMap( material.emissiveMap, renderer.gammaInput ), + bumpMap: !! material.bumpMap, + normalMap: !! material.normalMap, + displacementMap: !! material.displacementMap, + roughnessMap: !! material.roughnessMap, + metalnessMap: !! material.metalnessMap, + specularMap: !! material.specularMap, + alphaMap: !! material.alphaMap, + combine: material.combine, -/************************************************************** - * Bend / Wrap Helper Methods - **************************************************************/ + vertexColors: material.vertexColors, -// Wrap path / Bend modifiers? + fog: !! fog, + useFog: material.fog, + fogExp: fog instanceof THREE.FogExp2, -THREE.CurvePath.prototype.addWrapPath = function ( bendpath ) { + flatShading: material.shading === THREE.FlatShading, - this.bends.push( bendpath ); + sizeAttenuation: material.sizeAttenuation, + logarithmicDepthBuffer: capabilities.logarithmicDepthBuffer, -}; + skinning: material.skinning, + maxBones: maxBones, + useVertexTexture: capabilities.floatVertexTextures && object && object.skeleton && object.skeleton.useVertexTexture, + + morphTargets: material.morphTargets, + morphNormals: material.morphNormals, + maxMorphTargets: renderer.maxMorphTargets, + maxMorphNormals: renderer.maxMorphNormals, -THREE.CurvePath.prototype.getTransformedPoints = function( segments, bends ) { + numDirLights: lights.directional.length, + numPointLights: lights.point.length, + numSpotLights: lights.spot.length, + numHemiLights: lights.hemi.length, - var oldPts = this.getPoints( segments ); // getPoints getSpacedPoints - var i, il; + numClippingPlanes: nClipPlanes, - if ( !bends ) { + shadowMapEnabled: renderer.shadowMap.enabled && object.receiveShadow && lights.shadows.length > 0, + shadowMapType: renderer.shadowMap.type, - bends = this.bends; + toneMapping: renderer.toneMapping, + physicallyCorrectLights: renderer.physicallyCorrectLights, - } + premultipliedAlpha: material.premultipliedAlpha, - for ( i = 0, il = bends.length; i < il; i ++ ) { + alphaTest: material.alphaTest, + doubleSided: material.side === THREE.DoubleSide, + flipSided: material.side === THREE.BackSide, - oldPts = this.getWrapPoints( oldPts, bends[ i ] ); + depthPacking: ( material.depthPacking !== undefined ) ? material.depthPacking : false - } + }; - return oldPts; + return parameters; -}; + }; -THREE.CurvePath.prototype.getTransformedSpacedPoints = function( segments, bends ) { + this.getProgramCode = function ( material, parameters ) { - var oldPts = this.getSpacedPoints( segments ); + var array = []; + + if ( parameters.shaderID ) { - var i, il; + array.push( parameters.shaderID ); - if ( !bends ) { + } else { - bends = this.bends; + array.push( material.fragmentShader ); + array.push( material.vertexShader ); - } + } - for ( i = 0, il = bends.length; i < il; i ++ ) { + if ( material.defines !== undefined ) { - oldPts = this.getWrapPoints( oldPts, bends[ i ] ); + for ( var name in material.defines ) { - } + array.push( name ); + array.push( material.defines[ name ] ); - return oldPts; + } -}; + } -// This returns getPoints() bend/wrapped around the contour of a path. -// Read http://www.planetclegg.com/projects/WarpingTextToSplines.html + for ( var i = 0; i < parameterNames.length; i ++ ) { -THREE.CurvePath.prototype.getWrapPoints = function ( oldPts, path ) { + array.push( parameters[ parameterNames[ i ] ] ); - var bounds = this.getBoundingBox(); + } - var i, il, p, oldX, oldY, xNorm; + return array.join(); - for ( i = 0, il = oldPts.length; i < il; i ++ ) { + }; - p = oldPts[ i ]; + this.acquireProgram = function ( material, parameters, code ) { - oldX = p.x; - oldY = p.y; + var program; - xNorm = oldX / bounds.maxX; + // Check if code has been already compiled + for ( var p = 0, pl = programs.length; p < pl; p ++ ) { - // 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 + var programInfo = programs[ p ]; - xNorm = path.getUtoTmapping( xNorm, oldX ); + if ( programInfo.code === code ) { - // check for out of bounds? + program = programInfo; + ++ program.usedTimes; - var pathPt = path.getPoint( xNorm ); - var normal = path.getNormalVector( xNorm ).multiplyScalar( oldY ); + break; - p.x = pathPt.x + normal.x; - p.y = pathPt.y + normal.y; + } - } + } - return oldPts; + if ( program === undefined ) { -}; + program = new THREE.WebGLProgram( renderer, code, material, parameters ); + programs.push( program ); + } -/** - * @author alteredq / http://alteredqualia.com/ - */ + return program; -THREE.Gyroscope = function () { + }; - THREE.Object3D.call( this ); + this.releaseProgram = function( program ) { -}; + if ( -- program.usedTimes === 0 ) { -THREE.Gyroscope.prototype = Object.create( THREE.Object3D.prototype ); + // Remove from unordered set + var i = programs.indexOf( program ); + programs[ i ] = programs[ programs.length - 1 ]; + programs.pop(); -THREE.Gyroscope.prototype.updateMatrixWorld = function ( force ) { + // Free WebGL resources + program.destroy(); - this.matrixAutoUpdate && this.updateMatrix(); + } - // update matrixWorld + }; - if ( this.matrixWorldNeedsUpdate || force ) { + // Exposed for resource monitoring & error feedback via renderer.info: + this.programs = programs; - if ( this.parent ) { +}; - this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); +// File:src/renderers/webgl/WebGLProperties.js - this.matrixWorld.decompose( this.translationWorld, this.quaternionWorld, this.scaleWorld ); - this.matrix.decompose( this.translationObject, this.quaternionObject, this.scaleObject ); +/** +* @author fordacious / fordacious.github.io +*/ - this.matrixWorld.compose( this.translationWorld, this.quaternionObject, this.scaleWorld ); +THREE.WebGLProperties = function () { + var properties = {}; - } else { + this.get = function ( object ) { - this.matrixWorld.copy( this.matrix ); + var uuid = object.uuid; + var map = properties[ uuid ]; + + if ( map === undefined ) { + + map = {}; + properties[ uuid ] = map; } + return map; - this.matrixWorldNeedsUpdate = false; + }; - force = true; + this.delete = function ( object ) { - } + delete properties[ object.uuid ]; - // update children + }; - for ( var i = 0, l = this.children.length; i < l; i ++ ) { + this.clear = function () { - this.children[ i ].updateMatrixWorld( force ); + properties = {}; - } + }; }; -THREE.Gyroscope.prototype.translationWorld = new THREE.Vector3(); -THREE.Gyroscope.prototype.translationObject = new THREE.Vector3(); -THREE.Gyroscope.prototype.quaternionWorld = new THREE.Quaternion(); -THREE.Gyroscope.prototype.quaternionObject = new THREE.Quaternion(); -THREE.Gyroscope.prototype.scaleWorld = new THREE.Vector3(); -THREE.Gyroscope.prototype.scaleObject = new THREE.Vector3(); +// File:src/renderers/webgl/WebGLShader.js +THREE.WebGLShader = ( function () { -/** - * @author zz85 / http://www.lab4games.net/zz85/blog - * Creates free form 2d path using series of points, lines or curves. - * - **/ + function addLineNumbers( string ) { -THREE.Path = function ( points ) { + var lines = string.split( '\n' ); - THREE.CurvePath.call(this); + for ( var i = 0; i < lines.length; i ++ ) { - this.actions = []; + lines[ i ] = ( i + 1 ) + ': ' + lines[ i ]; - if ( points ) { + } - this.fromPoints( points ); + return lines.join( '\n' ); } -}; + return function WebGLShader( gl, type, string ) { -THREE.Path.prototype = Object.create( THREE.CurvePath.prototype ); + var shader = gl.createShader( type ); -THREE.PathActions = { + gl.shaderSource( shader, string ); + gl.compileShader( shader ); - 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' -}; + if ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === false ) { -// TODO Clean up PATH API + console.error( 'THREE.WebGLShader: Shader couldn\'t compile.' ); -// Create path using straight lines to connect all points -// - vectors: array of Vector2 + } -THREE.Path.prototype.fromPoints = function ( vectors ) { + if ( gl.getShaderInfoLog( shader ) !== '' ) { - this.moveTo( vectors[ 0 ].x, vectors[ 0 ].y ); + console.warn( 'THREE.WebGLShader: gl.getShaderInfoLog()', type === gl.VERTEX_SHADER ? 'vertex' : 'fragment', gl.getShaderInfoLog( shader ), addLineNumbers( string ) ); - for ( var v = 1, vlen = vectors.length; v < vlen; v ++ ) { + } - this.lineTo( vectors[ v ].x, vectors[ v ].y ); + // --enable-privileged-webgl-extension + // console.log( type, gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) ); - }; + return shader; -}; + }; -// startPath() endPath()? +} )(); -THREE.Path.prototype.moveTo = function ( x, y ) { +// File:src/renderers/webgl/WebGLShadowMap.js - var args = Array.prototype.slice.call( arguments ); - this.actions.push( { action: THREE.PathActions.MOVE_TO, args: args } ); +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ -}; +THREE.WebGLShadowMap = function ( _renderer, _lights, _objects, capabilities ) { -THREE.Path.prototype.lineTo = function ( x, y ) { + var _gl = _renderer.context, + _state = _renderer.state, + _frustum = new THREE.Frustum(), + _projScreenMatrix = new THREE.Matrix4(), - var args = Array.prototype.slice.call( arguments ); + _lightShadows = _lights.shadows, - var lastargs = this.actions[ this.actions.length - 1 ].args; + _shadowMapSize = new THREE.Vector2(), + _maxShadowMapSize = new THREE.Vector2( capabilities.maxTextureSize, capabilities.maxTextureSize ), - var x0 = lastargs[ lastargs.length - 2 ]; - var y0 = lastargs[ lastargs.length - 1 ]; + _lookTarget = new THREE.Vector3(), + _lightPositionWorld = new THREE.Vector3(), - var curve = new THREE.LineCurve( new THREE.Vector2( x0, y0 ), new THREE.Vector2( x, y ) ); - this.curves.push( curve ); + _renderList = [], - this.actions.push( { action: THREE.PathActions.LINE_TO, args: args } ); + _MorphingFlag = 1, + _SkinningFlag = 2, -}; + _NumberOfMaterialVariants = ( _MorphingFlag | _SkinningFlag ) + 1, -THREE.Path.prototype.quadraticCurveTo = function( aCPx, aCPy, aX, aY ) { + _depthMaterials = new Array( _NumberOfMaterialVariants ), + _distanceMaterials = new Array( _NumberOfMaterialVariants ), - var args = Array.prototype.slice.call( arguments ); + _materialCache = {}; - var lastargs = this.actions[ this.actions.length - 1 ].args; + var cubeDirections = [ + new THREE.Vector3( 1, 0, 0 ), new THREE.Vector3( - 1, 0, 0 ), new THREE.Vector3( 0, 0, 1 ), + new THREE.Vector3( 0, 0, - 1 ), new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, - 1, 0 ) + ]; - var x0 = lastargs[ lastargs.length - 2 ]; - var y0 = lastargs[ lastargs.length - 1 ]; + var cubeUps = [ + new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 1, 0 ), + new THREE.Vector3( 0, 1, 0 ), new THREE.Vector3( 0, 0, 1 ), new THREE.Vector3( 0, 0, - 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 ); + var cube2DViewPorts = [ + new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4(), + new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4() + ]; - this.actions.push( { action: THREE.PathActions.QUADRATIC_CURVE_TO, args: args } ); + // init -}; + var depthMaterialTemplate = new THREE.MeshDepthMaterial(); + depthMaterialTemplate.depthPacking = THREE.RGBADepthPacking; + depthMaterialTemplate.clipping = true; -THREE.Path.prototype.bezierCurveTo = function( aCP1x, aCP1y, - aCP2x, aCP2y, - aX, aY ) { + var distanceShader = THREE.ShaderLib[ "distanceRGBA" ]; + var distanceUniforms = THREE.UniformsUtils.clone( distanceShader.uniforms ); - var args = Array.prototype.slice.call( arguments ); + for ( var i = 0; i !== _NumberOfMaterialVariants; ++ i ) { - var lastargs = this.actions[ this.actions.length - 1 ].args; + var useMorphing = ( i & _MorphingFlag ) !== 0; + var useSkinning = ( i & _SkinningFlag ) !== 0; - var x0 = lastargs[ lastargs.length - 2 ]; - var y0 = lastargs[ lastargs.length - 1 ]; + var depthMaterial = depthMaterialTemplate.clone(); + depthMaterial.morphTargets = useMorphing; + depthMaterial.skinning = useSkinning; - 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 ); + _depthMaterials[ i ] = depthMaterial; - this.actions.push( { action: THREE.PathActions.BEZIER_CURVE_TO, args: args } ); + var distanceMaterial = new THREE.ShaderMaterial( { + defines: { + 'USE_SHADOWMAP': '' + }, + uniforms: distanceUniforms, + vertexShader: distanceShader.vertexShader, + fragmentShader: distanceShader.fragmentShader, + morphTargets: useMorphing, + skinning: useSkinning, + clipping: true + } ); -}; + _distanceMaterials[ i ] = distanceMaterial; -THREE.Path.prototype.splineThru = function( pts /*Array of Vector*/ ) { + } - var args = Array.prototype.slice.call( arguments ); - var lastargs = this.actions[ this.actions.length - 1 ].args; + // - 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 ); + var scope = this; - var curve = new THREE.SplineCurve( npts ); - this.curves.push( curve ); + this.enabled = false; - this.actions.push( { action: THREE.PathActions.CSPLINE_THRU, args: args } ); + this.autoUpdate = true; + this.needsUpdate = false; -}; + this.type = THREE.PCFShadowMap; -// FUTURE: Change the API or follow canvas API? + this.renderReverseSided = true; + this.renderSingleSided = true; -THREE.Path.prototype.arc = function ( aX, aY, aRadius, - aStartAngle, aEndAngle, aClockwise ) { + this.render = function ( scene, camera ) { - var lastargs = this.actions[ this.actions.length - 1].args; - var x0 = lastargs[ lastargs.length - 2 ]; - var y0 = lastargs[ lastargs.length - 1 ]; + if ( scope.enabled === false ) return; + if ( scope.autoUpdate === false && scope.needsUpdate === false ) return; - this.absarc(aX + x0, aY + y0, aRadius, - aStartAngle, aEndAngle, aClockwise ); + if ( _lightShadows.length === 0 ) return; - }; + // Set GL state for depth map. + _state.clearColor( 1, 1, 1, 1 ); + _state.disable( _gl.BLEND ); + _state.setDepthTest( true ); + _state.setScissorTest( false ); - THREE.Path.prototype.absarc = function ( aX, aY, aRadius, - aStartAngle, aEndAngle, aClockwise ) { - this.absellipse(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise); - }; + // render depth map -THREE.Path.prototype.ellipse = function ( aX, aY, xRadius, yRadius, - aStartAngle, aEndAngle, aClockwise ) { + var faceCount, isPointLight; - var lastargs = this.actions[ this.actions.length - 1].args; - var x0 = lastargs[ lastargs.length - 2 ]; - var y0 = lastargs[ lastargs.length - 1 ]; + for ( var i = 0, il = _lightShadows.length; i < il; i ++ ) { - this.absellipse(aX + x0, aY + y0, xRadius, yRadius, - aStartAngle, aEndAngle, aClockwise ); + var light = _lightShadows[ i ]; + var shadow = light.shadow; - }; + if ( shadow === undefined ) { + console.warn( 'THREE.WebGLShadowMap:', light, 'has no shadow.' ); + continue; -THREE.Path.prototype.absellipse = function ( aX, aY, xRadius, yRadius, - aStartAngle, aEndAngle, aClockwise ) { + } - var args = Array.prototype.slice.call( arguments ); - var curve = new THREE.EllipseCurve( aX, aY, xRadius, yRadius, - aStartAngle, aEndAngle, aClockwise ); - this.curves.push( curve ); + var shadowCamera = shadow.camera; - var lastPoint = curve.getPoint(aClockwise ? 1 : 0); - args.push(lastPoint.x); - args.push(lastPoint.y); + _shadowMapSize.copy( shadow.mapSize ); + _shadowMapSize.min( _maxShadowMapSize ); - this.actions.push( { action: THREE.PathActions.ELLIPSE, args: args } ); + if ( light instanceof THREE.PointLight ) { - }; + faceCount = 6; + isPointLight = true; -THREE.Path.prototype.getSpacedPoints = function ( divisions, closedPath ) { + var vpWidth = _shadowMapSize.x; + var vpHeight = _shadowMapSize.y; - if ( ! divisions ) divisions = 40; + // These viewports map a cube-map onto a 2D texture with the + // following orientation: + // + // xzXZ + // y Y + // + // X - Positive x direction + // x - Negative x direction + // Y - Positive y direction + // y - Negative y direction + // Z - Positive z direction + // z - Negative z direction + + // positive X + cube2DViewPorts[ 0 ].set( vpWidth * 2, vpHeight, vpWidth, vpHeight ); + // negative X + cube2DViewPorts[ 1 ].set( 0, vpHeight, vpWidth, vpHeight ); + // positive Z + cube2DViewPorts[ 2 ].set( vpWidth * 3, vpHeight, vpWidth, vpHeight ); + // negative Z + cube2DViewPorts[ 3 ].set( vpWidth, vpHeight, vpWidth, vpHeight ); + // positive Y + cube2DViewPorts[ 4 ].set( vpWidth * 3, 0, vpWidth, vpHeight ); + // negative Y + cube2DViewPorts[ 5 ].set( vpWidth, 0, vpWidth, vpHeight ); + + _shadowMapSize.x *= 4.0; + _shadowMapSize.y *= 2.0; - var points = []; + } else { - for ( var i = 0; i < divisions; i ++ ) { + faceCount = 1; + isPointLight = false; - points.push( this.getPoint( i / divisions ) ); + } - //if( !this.getPoint( i / divisions ) ) throw "DIE"; + if ( shadow.map === null ) { - } + var pars = { minFilter: THREE.NearestFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat }; - // if ( closedPath ) { - // - // points.push( points[ 0 ] ); - // - // } + shadow.map = new THREE.WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars ); - return points; + shadowCamera.updateProjectionMatrix(); -}; + } -/* Return an array of vectors based on contour of the path */ + if ( shadow instanceof THREE.SpotLightShadow ) { -THREE.Path.prototype.getPoints = function( divisions, closedPath ) { + shadow.update( light ); - if (this.useSpacedPoints) { - console.log('tata'); - return this.getSpacedPoints( divisions, closedPath ); - } + } - divisions = divisions || 12; + var shadowMap = shadow.map; + var shadowMatrix = shadow.matrix; - var points = []; + _lightPositionWorld.setFromMatrixPosition( light.matrixWorld ); + shadowCamera.position.copy( _lightPositionWorld ); - var i, il, item, action, args; - var cpx, cpy, cpx2, cpy2, cpx1, cpy1, cpx0, cpy0, - laste, j, - t, tx, ty; + _renderer.setRenderTarget( shadowMap ); + _renderer.clear(); - for ( i = 0, il = this.actions.length; i < il; i ++ ) { + // render shadow map for each cube face (if omni-directional) or + // run a single pass if not - item = this.actions[ i ]; + for ( var face = 0; face < faceCount; face ++ ) { - action = item.action; - args = item.args; + if ( isPointLight ) { - switch( action ) { + _lookTarget.copy( shadowCamera.position ); + _lookTarget.add( cubeDirections[ face ] ); + shadowCamera.up.copy( cubeUps[ face ] ); + shadowCamera.lookAt( _lookTarget ); - case THREE.PathActions.MOVE_TO: + var vpDimensions = cube2DViewPorts[ face ]; + _state.viewport( vpDimensions ); - points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) ); + } else { - break; + _lookTarget.setFromMatrixPosition( light.target.matrixWorld ); + shadowCamera.lookAt( _lookTarget ); - case THREE.PathActions.LINE_TO: + } - points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) ); + shadowCamera.updateMatrixWorld(); + shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld ); - break; + // compute shadow matrix - case THREE.PathActions.QUADRATIC_CURVE_TO: + 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 + ); - cpx = args[ 2 ]; - cpy = args[ 3 ]; + shadowMatrix.multiply( shadowCamera.projectionMatrix ); + shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); - cpx1 = args[ 0 ]; - cpy1 = args[ 1 ]; + // update camera matrices and frustum - if ( points.length > 0 ) { + _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); + _frustum.setFromMatrix( _projScreenMatrix ); - laste = points[ points.length - 1 ]; + // set object matrices & frustum culling - cpx0 = laste.x; - cpy0 = laste.y; + _renderList.length = 0; - } else { + projectObject( scene, camera, shadowCamera ); - laste = this.actions[ i - 1 ].args; + // render shadow map + // render regular objects - cpx0 = laste[ laste.length - 2 ]; - cpy0 = laste[ laste.length - 1 ]; + for ( var j = 0, jl = _renderList.length; j < jl; j ++ ) { - } + var object = _renderList[ j ]; + var geometry = _objects.update( object ); + var material = object.material; - for ( j = 1; j <= divisions; j ++ ) { + if ( material instanceof THREE.MultiMaterial ) { - t = j / divisions; + var groups = geometry.groups; + var materials = material.materials; - tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx ); - ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy ); + for ( var k = 0, kl = groups.length; k < kl; k ++ ) { - points.push( new THREE.Vector2( tx, ty ) ); + var group = groups[ k ]; + var groupMaterial = materials[ group.materialIndex ]; - } + if ( groupMaterial.visible === true ) { - break; + var depthMaterial = getDepthMaterial( object, groupMaterial, isPointLight, _lightPositionWorld ); + _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group ); - case THREE.PathActions.BEZIER_CURVE_TO: + } - cpx = args[ 4 ]; - cpy = args[ 5 ]; + } - cpx1 = args[ 0 ]; - cpy1 = args[ 1 ]; + } else { - cpx2 = args[ 2 ]; - cpy2 = args[ 3 ]; + var depthMaterial = getDepthMaterial( object, material, isPointLight, _lightPositionWorld ); + _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null ); - if ( points.length > 0 ) { + } - laste = points[ points.length - 1 ]; + } - cpx0 = laste.x; - cpy0 = laste.y; + } - } else { + } - laste = this.actions[ i - 1 ].args; + // Restore GL state. + var clearColor = _renderer.getClearColor(), + clearAlpha = _renderer.getClearAlpha(); + _renderer.setClearColor( clearColor, clearAlpha ); - cpx0 = laste[ laste.length - 2 ]; - cpy0 = laste[ laste.length - 1 ]; + scope.needsUpdate = false; - } + }; + function getDepthMaterial( object, material, isPointLight, lightPositionWorld ) { - for ( j = 1; j <= divisions; j ++ ) { + var geometry = object.geometry; - t = j / divisions; + var result = null; - tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx ); - ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy ); + var materialVariants = _depthMaterials; + var customMaterial = object.customDepthMaterial; - points.push( new THREE.Vector2( tx, ty ) ); + if ( isPointLight ) { - } + materialVariants = _distanceMaterials; + customMaterial = object.customDistanceMaterial; - break; + } - case THREE.PathActions.CSPLINE_THRU: + if ( ! customMaterial ) { - laste = this.actions[ i - 1 ].args; + var useMorphing = false; - var last = new THREE.Vector2( laste[ laste.length - 2 ], laste[ laste.length - 1 ] ); - var spts = [ last ]; + if ( material.morphTargets ) { - var n = divisions * args[ 0 ].length; + if ( geometry instanceof THREE.BufferGeometry ) { - spts = spts.concat( args[ 0 ] ); + useMorphing = geometry.morphAttributes && geometry.morphAttributes.position && geometry.morphAttributes.position.length > 0; - var spline = new THREE.SplineCurve( spts ); + } else if ( geometry instanceof THREE.Geometry ) { - for ( j = 1; j <= n; j ++ ) { + useMorphing = geometry.morphTargets && geometry.morphTargets.length > 0; - points.push( spline.getPointAt( j / n ) ) ; + } } - break; + var useSkinning = object instanceof THREE.SkinnedMesh && material.skinning; - case THREE.PathActions.ARC: + var variantIndex = 0; - var aX = args[ 0 ], aY = args[ 1 ], - aRadius = args[ 2 ], - aStartAngle = args[ 3 ], aEndAngle = args[ 4 ], - aClockwise = !!args[ 5 ]; + if ( useMorphing ) variantIndex |= _MorphingFlag; + if ( useSkinning ) variantIndex |= _SkinningFlag; - var deltaAngle = aEndAngle - aStartAngle; - var angle; - var tdivisions = divisions * 2; + result = materialVariants[ variantIndex ]; - for ( j = 1; j <= tdivisions; j ++ ) { + } else { - t = j / tdivisions; + result = customMaterial; - if ( ! aClockwise ) { + } - t = 1 - t; + if ( _renderer.localClippingEnabled && + material.clipShadows === true && + material.clippingPlanes.length !== 0 ) { - } + // in this case we need a unique material instance reflecting the + // appropriate state - angle = aStartAngle + t * deltaAngle; + var keyA = result.uuid, keyB = material.uuid; - tx = aX + aRadius * Math.cos( angle ); - ty = aY + aRadius * Math.sin( angle ); + var materialsForVariant = _materialCache[ keyA ]; - //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty); + if ( materialsForVariant === undefined ) { - points.push( new THREE.Vector2( tx, ty ) ); + materialsForVariant = {}; + _materialCache[ keyA ] = materialsForVariant; } - //console.log(points); + var cachedMaterial = materialsForVariant[ keyB ]; - break; - - case THREE.PathActions.ELLIPSE: + if ( cachedMaterial === undefined ) { - var aX = args[ 0 ], aY = args[ 1 ], - xRadius = args[ 2 ], - yRadius = args[ 3 ], - aStartAngle = args[ 4 ], aEndAngle = args[ 5 ], - aClockwise = !!args[ 6 ]; + cachedMaterial = result.clone(); + materialsForVariant[ keyB ] = cachedMaterial; + } - var deltaAngle = aEndAngle - aStartAngle; - var angle; - var tdivisions = divisions * 2; - - for ( j = 1; j <= tdivisions; j ++ ) { - - t = j / tdivisions; + result = cachedMaterial; - if ( ! aClockwise ) { + } - t = 1 - t; + result.visible = material.visible; + result.wireframe = material.wireframe; - } + var side = material.side; - angle = aStartAngle + t * deltaAngle; + if ( scope.renderSingleSided && side == THREE.DoubleSide ) { - tx = aX + xRadius * Math.cos( angle ); - ty = aY + yRadius * Math.sin( angle ); + side = THREE.FrontSide; - //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty); + } - points.push( new THREE.Vector2( tx, ty ) ); + if ( scope.renderReverseSided ) { - } + if ( side === THREE.FrontSide ) side = THREE.BackSide; + else if ( side === THREE.BackSide ) side = THREE.FrontSide; - //console.log(points); + } - break; + result.side = side; - } // end switch + result.clipShadows = material.clipShadows; + result.clippingPlanes = material.clippingPlanes; - } + result.wireframeLinewidth = material.wireframeLinewidth; + result.linewidth = material.linewidth; + if ( isPointLight && result.uniforms.lightPos !== undefined ) { + result.uniforms.lightPos.value.copy( lightPositionWorld ); - // Normalize to remove the closing point by default. - var lastPoint = points[ points.length - 1]; - var EPSILON = 0.0000000001; - if ( Math.abs(lastPoint.x - points[ 0 ].x) < EPSILON && - Math.abs(lastPoint.y - points[ 0 ].y) < EPSILON) - points.splice( points.length - 1, 1); - if ( closedPath ) { + } - points.push( points[ 0 ] ); + return result; } - return points; + function projectObject( object, camera, shadowCamera ) { -}; + if ( object.visible === false ) return; -// Breaks path into shapes + if ( object.layers.test( camera.layers ) && ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Points ) ) { -THREE.Path.prototype.toShapes = function( isCCW ) { + if ( object.castShadow && ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) ) { - var i, il, item, action, args; + var material = object.material; - var subPaths = [], lastPath = new THREE.Path(); + if ( material.visible === true ) { - for ( i = 0, il = this.actions.length; i < il; i ++ ) { + object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); + _renderList.push( object ); - item = this.actions[ i ]; + } - args = item.args; - action = item.action; + } - if ( action == THREE.PathActions.MOVE_TO ) { + } - if ( lastPath.actions.length != 0 ) { + var children = object.children; - subPaths.push( lastPath ); - lastPath = new THREE.Path(); + for ( var i = 0, l = children.length; i < l; i ++ ) { - } + projectObject( children[ i ], camera, shadowCamera ); } - lastPath[ action ].apply( lastPath, args ); - } - if ( lastPath.actions.length != 0 ) { - - subPaths.push( lastPath ); +}; - } +// File:src/renderers/webgl/WebGLState.js - // console.log(subPaths); +/** +* @author mrdoob / http://mrdoob.com/ +*/ - if ( subPaths.length == 0 ) return []; +THREE.WebGLState = function ( gl, extensions, paramThreeToGL ) { - var solid, tmpPath, tmpShape, shapes = []; + var _this = this; - if ( subPaths.length == 1) { + this.buffers = { + color: new THREE.WebGLColorBuffer( gl, this ), + depth: new THREE.WebGLDepthBuffer( gl, this ), + stencil: new THREE.WebGLStencilBuffer( gl, this ) + }; - tmpPath = subPaths[0]; - tmpShape = new THREE.Shape(); - tmpShape.actions = tmpPath.actions; - tmpShape.curves = tmpPath.curves; - shapes.push( tmpShape ); - return shapes; + var maxVertexAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS ); + var newAttributes = new Uint8Array( maxVertexAttributes ); + var enabledAttributes = new Uint8Array( maxVertexAttributes ); + var attributeDivisors = new Uint8Array( maxVertexAttributes ); - } + var capabilities = {}; - var holesFirst = !THREE.Shape.Utils.isClockWise( subPaths[ 0 ].getPoints() ); - holesFirst = isCCW ? !holesFirst : holesFirst; + var compressedTextureFormats = null; - // console.log("Holes first", holesFirst); + var currentBlending = null; + var currentBlendEquation = null; + var currentBlendSrc = null; + var currentBlendDst = null; + var currentBlendEquationAlpha = null; + var currentBlendSrcAlpha = null; + var currentBlendDstAlpha = null; + var currentPremultipledAlpha = false; - if ( holesFirst ) { + var currentFlipSided = null; + var currentCullFace = null; - tmpShape = new THREE.Shape(); + var currentLineWidth = null; - for ( i = 0, il = subPaths.length; i < il; i ++ ) { + var currentPolygonOffsetFactor = null; + var currentPolygonOffsetUnits = null; - tmpPath = subPaths[ i ]; - solid = THREE.Shape.Utils.isClockWise( tmpPath.getPoints() ); - solid = isCCW ? !solid : solid; + var currentScissorTest = null; - if ( solid ) { + var maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS ); - tmpShape.actions = tmpPath.actions; - tmpShape.curves = tmpPath.curves; + var currentTextureSlot = null; + var currentBoundTextures = {}; - shapes.push( tmpShape ); - tmpShape = new THREE.Shape(); + var currentScissor = new THREE.Vector4(); + var currentViewport = new THREE.Vector4(); - //console.log('cw', i); + function createTexture( type, target, count ) { - } else { + var data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4. + var texture = gl.createTexture(); - tmpShape.holes.push( tmpPath ); + gl.bindTexture( type, texture ); + gl.texParameteri( type, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); + gl.texParameteri( type, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); - //console.log('ccw', i); + for ( var i = 0; i < count; i ++ ) { - } + gl.texImage2D( target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data ); } - } else { - - // Shapes first - tmpShape = undefined; + return texture; - for ( i = 0, il = subPaths.length; i < il; i ++ ) { + } - tmpPath = subPaths[ i ]; - solid = THREE.Shape.Utils.isClockWise( tmpPath.getPoints() ); - solid = isCCW ? !solid : solid; + var emptyTextures = {}; + emptyTextures[ gl.TEXTURE_2D ] = createTexture( gl.TEXTURE_2D, gl.TEXTURE_2D, 1 ); + emptyTextures[ gl.TEXTURE_CUBE_MAP ] = createTexture( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6 ); - if ( solid ) { + // - if ( tmpShape ) shapes.push( tmpShape ); + this.init = function () { - tmpShape = new THREE.Shape(); - tmpShape.actions = tmpPath.actions; - tmpShape.curves = tmpPath.curves; + this.clearColor( 0, 0, 0, 1 ); + this.clearDepth( 1 ); + this.clearStencil( 0 ); - } else { + this.enable( gl.DEPTH_TEST ); + this.setDepthFunc( THREE.LessEqualDepth ); - tmpShape.holes.push( tmpPath ); + this.setFlipSided( false ); + this.setCullFace( THREE.CullFaceBack ); + this.enable( gl.CULL_FACE ); - } + this.enable( gl.BLEND ); + this.setBlending( THREE.NormalBlending ); - } + }; - shapes.push( tmpShape ); + this.initAttributes = function () { - } + for ( var i = 0, l = newAttributes.length; i < l; i ++ ) { - //console.log("shape", shapes); + newAttributes[ i ] = 0; - return shapes; + } -}; + }; -/** - * @author zz85 / http://www.lab4games.net/zz85/blog - * Defines a 2d shape plane using paths. - **/ + this.enableAttribute = function ( attribute ) { -// 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. + newAttributes[ attribute ] = 1; -THREE.Shape = function () { + if ( enabledAttributes[ attribute ] === 0 ) { - THREE.Path.apply( this, arguments ); - this.holes = []; + gl.enableVertexAttribArray( attribute ); + enabledAttributes[ attribute ] = 1; -}; + } -THREE.Shape.prototype = Object.create( THREE.Path.prototype ); + if ( attributeDivisors[ attribute ] !== 0 ) { -// Convenience method to return ExtrudeGeometry + var extension = extensions.get( 'ANGLE_instanced_arrays' ); -THREE.Shape.prototype.extrude = function ( options ) { + extension.vertexAttribDivisorANGLE( attribute, 0 ); + attributeDivisors[ attribute ] = 0; - var extruded = new THREE.ExtrudeGeometry( this, options ); - return extruded; + } -}; + }; -// Convenience method to return ShapeGeometry + this.enableAttributeAndDivisor = function ( attribute, meshPerAttribute, extension ) { -THREE.Shape.prototype.makeGeometry = function ( options ) { + newAttributes[ attribute ] = 1; - var geometry = new THREE.ShapeGeometry( this, options ); - return geometry; + if ( enabledAttributes[ attribute ] === 0 ) { -}; + gl.enableVertexAttribArray( attribute ); + enabledAttributes[ attribute ] = 1; -// Get points of holes + } -THREE.Shape.prototype.getPointsHoles = function ( divisions ) { + if ( attributeDivisors[ attribute ] !== meshPerAttribute ) { - var i, il = this.holes.length, holesPts = []; + extension.vertexAttribDivisorANGLE( attribute, meshPerAttribute ); + attributeDivisors[ attribute ] = meshPerAttribute; - for ( i = 0; i < il; i ++ ) { + } - holesPts[ i ] = this.holes[ i ].getTransformedPoints( divisions, this.bends ); + }; - } + this.disableUnusedAttributes = function () { - return holesPts; + for ( var i = 0, l = enabledAttributes.length; i !== l; ++ i ) { -}; + if ( enabledAttributes[ i ] !== newAttributes[ i ] ) { -// Get points of holes (spaced by regular distance) + gl.disableVertexAttribArray( i ); + enabledAttributes[ i ] = 0; -THREE.Shape.prototype.getSpacedPointsHoles = function ( divisions ) { + } - var i, il = this.holes.length, holesPts = []; + } - for ( i = 0; i < il; i ++ ) { + }; - holesPts[ i ] = this.holes[ i ].getTransformedSpacedPoints( divisions, this.bends ); + this.enable = function ( id ) { - } + if ( capabilities[ id ] !== true ) { - return holesPts; + gl.enable( id ); + capabilities[ id ] = true; -}; + } + }; -// Get points of shape and holes (keypoints based on segments parameter) + this.disable = function ( id ) { -THREE.Shape.prototype.extractAllPoints = function ( divisions ) { + if ( capabilities[ id ] !== false ) { - return { + gl.disable( id ); + capabilities[ id ] = false; - shape: this.getTransformedPoints( divisions ), - holes: this.getPointsHoles( divisions ) + } }; -}; + this.getCompressedTextureFormats = function () { -THREE.Shape.prototype.extractPoints = function ( divisions ) { + if ( compressedTextureFormats === null ) { - if (this.useSpacedPoints) { - return this.extractAllSpacedPoints(divisions); - } + compressedTextureFormats = []; - return this.extractAllPoints(divisions); + if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) || + extensions.get( 'WEBGL_compressed_texture_s3tc' ) || + extensions.get( 'WEBGL_compressed_texture_etc1' ) ) { -}; + var formats = gl.getParameter( gl.COMPRESSED_TEXTURE_FORMATS ); -// -// THREE.Shape.prototype.extractAllPointsWithBend = function ( divisions, bend ) { -// -// return { -// -// shape: this.transform( bend, divisions ), -// holes: this.getPointsHoles( divisions, bend ) -// -// }; -// -// }; + for ( var i = 0; i < formats.length; i ++ ) { -// Get points of shape and holes (spaced by regular distance) + compressedTextureFormats.push( formats[ i ] ); + + } -THREE.Shape.prototype.extractAllSpacedPoints = function ( divisions ) { + } - return { + } - shape: this.getTransformedSpacedPoints( divisions ), - holes: this.getSpacedPointsHoles( divisions ) + return compressedTextureFormats; }; -}; + this.setBlending = function ( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) { -/************************************************************** - * Utils - **************************************************************/ + if ( blending !== THREE.NoBlending ) { -THREE.Shape.Utils = { + this.enable( gl.BLEND ); - /* - contour - array of vector2 for contour - holes - array of array of vector2 - */ + } else { - removeHoles: function ( contour, holes ) { + this.disable( gl.BLEND ); + currentBlending = blending; // no blending, that is + return; - var shape = contour.concat(); // work on this shape - var allpoints = shape.concat(); + } - /* For each isolated shape, find the closest points and break to the hole to allow triangulation */ + if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) { + if ( blending === THREE.AdditiveBlending ) { - var prevShapeVert, nextShapeVert, - prevHoleVert, nextHoleVert, - holeIndex, shapeIndex, - shapeId, shapeGroup, - h, h2, - hole, shortest, d, - p, pts1, pts2, - tmpShape1, tmpShape2, - tmpHole1, tmpHole2, - verts = []; + if ( premultipliedAlpha ) { - for ( h = 0; h < holes.length; h ++ ) { + gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); + gl.blendFuncSeparate( gl.ONE, gl.ONE, gl.ONE, gl.ONE ); - hole = holes[ h ]; + } else { - /* - shapeholes[ h ].concat(); // preserves original - holes.push( hole ); - */ + gl.blendEquation( gl.FUNC_ADD ); + gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); - Array.prototype.push.apply( allpoints, hole ); + } - shortest = Number.POSITIVE_INFINITY; + } else if ( blending === THREE.SubtractiveBlending ) { + if ( premultipliedAlpha ) { - // Find the shortest pair of pts between shape and hole + gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); + gl.blendFuncSeparate( gl.ZERO, gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ONE_MINUS_SRC_ALPHA ); - // Note: Actually, I'm not sure now if we could optimize this to be faster than O(m*n) - // Using distanceToSquared() intead of distanceTo() should speed a little - // since running square roots operations are reduced. + } else { - for ( h2 = 0; h2 < hole.length; h2 ++ ) { + gl.blendEquation( gl.FUNC_ADD ); + gl.blendFunc( gl.ZERO, gl.ONE_MINUS_SRC_COLOR ); - pts1 = hole[ h2 ]; - var dist = []; + } - for ( p = 0; p < shape.length; p++ ) { + } else if ( blending === THREE.MultiplyBlending ) { - pts2 = shape[ p ]; - d = pts1.distanceToSquared( pts2 ); - dist.push( d ); + if ( premultipliedAlpha ) { - if ( d < shortest ) { + gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); + gl.blendFuncSeparate( gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA ); - shortest = d; - holeIndex = h2; - shapeIndex = p; + } else { - } + gl.blendEquation( gl.FUNC_ADD ); + gl.blendFunc( gl.ZERO, gl.SRC_COLOR ); } - } - - //console.log("shortest", shortest, dist); - - prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1; - prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1; - - var areaapts = [ + } else { - hole[ holeIndex ], - shape[ shapeIndex ], - shape[ prevShapeVert ] + if ( premultipliedAlpha ) { - ]; + gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD ); + gl.blendFuncSeparate( gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); - var areaa = THREE.FontUtils.Triangulate.area( areaapts ); + } else { - var areabpts = [ + 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 ); - hole[ holeIndex ], - hole[ prevHoleVert ], - shape[ shapeIndex ] + } - ]; + } - var areab = THREE.FontUtils.Triangulate.area( areabpts ); + currentBlending = blending; + currentPremultipledAlpha = premultipliedAlpha; - var shapeOffset = 1; - var holeOffset = -1; + } - var oldShapeIndex = shapeIndex, oldHoleIndex = holeIndex; - shapeIndex += shapeOffset; - holeIndex += holeOffset; + if ( blending === THREE.CustomBlending ) { - if ( shapeIndex < 0 ) { shapeIndex += shape.length; } - shapeIndex %= shape.length; + blendEquationAlpha = blendEquationAlpha || blendEquation; + blendSrcAlpha = blendSrcAlpha || blendSrc; + blendDstAlpha = blendDstAlpha || blendDst; - if ( holeIndex < 0 ) { holeIndex += hole.length; } - holeIndex %= hole.length; + if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) { - prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1; - prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1; + gl.blendEquationSeparate( paramThreeToGL( blendEquation ), paramThreeToGL( blendEquationAlpha ) ); - areaapts = [ + currentBlendEquation = blendEquation; + currentBlendEquationAlpha = blendEquationAlpha; - hole[ holeIndex ], - shape[ shapeIndex ], - shape[ prevShapeVert ] + } - ]; + if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) { - var areaa2 = THREE.FontUtils.Triangulate.area( areaapts ); + gl.blendFuncSeparate( paramThreeToGL( blendSrc ), paramThreeToGL( blendDst ), paramThreeToGL( blendSrcAlpha ), paramThreeToGL( blendDstAlpha ) ); - areabpts = [ + currentBlendSrc = blendSrc; + currentBlendDst = blendDst; + currentBlendSrcAlpha = blendSrcAlpha; + currentBlendDstAlpha = blendDstAlpha; - hole[ holeIndex ], - hole[ prevHoleVert ], - shape[ shapeIndex ] + } - ]; + } else { - var areab2 = THREE.FontUtils.Triangulate.area( areabpts ); - //console.log(areaa,areab ,areaa2,areab2, ( areaa + areab ), ( areaa2 + areab2 )); + currentBlendEquation = null; + currentBlendSrc = null; + currentBlendDst = null; + currentBlendEquationAlpha = null; + currentBlendSrcAlpha = null; + currentBlendDstAlpha = null; - if ( ( areaa + areab ) > ( areaa2 + areab2 ) ) { + } - // In case areas are not correct. - //console.log("USE THIS"); + }; - shapeIndex = oldShapeIndex; - holeIndex = oldHoleIndex ; + // TODO Deprecate - if ( shapeIndex < 0 ) { shapeIndex += shape.length; } - shapeIndex %= shape.length; + this.setColorWrite = function ( colorWrite ) { - if ( holeIndex < 0 ) { holeIndex += hole.length; } - holeIndex %= hole.length; + this.buffers.color.setMask( colorWrite ); - prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1; - prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1; + }; - } else { + this.setDepthTest = function ( depthTest ) { - //console.log("USE THAT ") + this.buffers.depth.setTest( depthTest ); - } + }; - tmpShape1 = shape.slice( 0, shapeIndex ); - tmpShape2 = shape.slice( shapeIndex ); - tmpHole1 = hole.slice( holeIndex ); - tmpHole2 = hole.slice( 0, holeIndex ); + this.setDepthWrite = function ( depthWrite ) { - // Should check orders here again? + this.buffers.depth.setMask( depthWrite ); - var trianglea = [ + }; - hole[ holeIndex ], - shape[ shapeIndex ], - shape[ prevShapeVert ] + this.setDepthFunc = function ( depthFunc ) { - ]; + this.buffers.depth.setFunc( depthFunc ); - var triangleb = [ + }; - hole[ holeIndex ] , - hole[ prevHoleVert ], - shape[ shapeIndex ] + this.setStencilTest = function ( stencilTest ) { - ]; + this.buffers.stencil.setTest( stencilTest ); - verts.push( trianglea ); - verts.push( triangleb ); + }; - shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 ); + this.setStencilWrite = function ( stencilWrite ) { - } + this.buffers.stencil.setMask( stencilWrite ); - return { + }; - shape:shape, /* shape with no holes */ - isolatedPts: verts, /* isolated faces */ - allpoints: allpoints + this.setStencilFunc = function ( stencilFunc, stencilRef, stencilMask ) { - } + this.buffers.stencil.setFunc( stencilFunc, stencilRef, stencilMask ); + }; - }, + this.setStencilOp = function ( stencilFail, stencilZFail, stencilZPass ) { - triangulateShape: function ( contour, holes ) { + this.buffers.stencil.setOp( stencilFail, stencilZFail, stencilZPass ); - var shapeWithoutHoles = THREE.Shape.Utils.removeHoles( contour, holes ); + }; - var shape = shapeWithoutHoles.shape, - allpoints = shapeWithoutHoles.allpoints, - isolatedPts = shapeWithoutHoles.isolatedPts; + // - var triangles = THREE.FontUtils.Triangulate( shape, false ); // True returns indices for points of spooled shape + this.setFlipSided = function ( flipSided ) { - // 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. + if ( currentFlipSided !== flipSided ) { - //console.log( "triangles",triangles, triangles.length ); - //console.log( "allpoints",allpoints, allpoints.length ); + if ( flipSided ) { - var i, il, f, face, - key, index, - allPointsMap = {}, - isolatedPointsMap = {}; + gl.frontFace( gl.CW ); - // prepare all points map + } else { - for ( i = 0, il = allpoints.length; i < il; i ++ ) { + gl.frontFace( gl.CCW ); - key = allpoints[ i ].x + ":" + allpoints[ i ].y; + } - if ( allPointsMap[ key ] !== undefined ) { + currentFlipSided = flipSided; - console.log( "Duplicate point", key ); + } - } + }; - allPointsMap[ key ] = i; + this.setCullFace = function ( cullFace ) { - } + if ( cullFace !== THREE.CullFaceNone ) { - // check all face vertices against all points map + this.enable( gl.CULL_FACE ); - for ( i = 0, il = triangles.length; i < il; i ++ ) { + if ( cullFace !== currentCullFace ) { - face = triangles[ i ]; + if ( cullFace === THREE.CullFaceBack ) { - for ( f = 0; f < 3; f ++ ) { + gl.cullFace( gl.BACK ); - key = face[ f ].x + ":" + face[ f ].y; + } else if ( cullFace === THREE.CullFaceFront ) { - index = allPointsMap[ key ]; + gl.cullFace( gl.FRONT ); - if ( index !== undefined ) { + } else { - face[ f ] = index; + gl.cullFace( gl.FRONT_AND_BACK ); } } - } - - // check isolated points vertices against all points map - - for ( i = 0, il = isolatedPts.length; i < il; i ++ ) { + } else { - face = isolatedPts[ i ]; + this.disable( gl.CULL_FACE ); - for ( f = 0; f < 3; f ++ ) { + } - key = face[ f ].x + ":" + face[ f ].y; + currentCullFace = cullFace; - index = allPointsMap[ key ]; + }; - if ( index !== undefined ) { + this.setLineWidth = function ( width ) { - face[ f ] = index; + if ( width !== currentLineWidth ) { - } + gl.lineWidth( width ); - } + currentLineWidth = width; } - return triangles.concat( isolatedPts ); + }; - }, // end triangulate shapes + this.setPolygonOffset = function ( polygonOffset, factor, units ) { - /* - triangulate2 : function( pts, holes ) { + if ( polygonOffset ) { - // For use with Poly2Tri.js + this.enable( gl.POLYGON_OFFSET_FILL ); - var allpts = pts.concat(); - var shape = []; - for (var p in pts) { - shape.push(new js.poly2tri.Point(pts[p].x, pts[p].y)); - } + if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) { - var swctx = new js.poly2tri.SweepContext(shape); + gl.polygonOffset( factor, units ); - for (var h in holes) { - var aHole = holes[h]; - var newHole = [] - for (i in aHole) { - newHole.push(new js.poly2tri.Point(aHole[i].x, aHole[i].y)); - allpts.push(aHole[i]); - } - swctx.AddHole(newHole); - } + currentPolygonOffsetFactor = factor; + currentPolygonOffsetUnits = units; - var find; - var findIndexForPt = function (pt) { - find = new THREE.Vector2(pt.x, pt.y); - var p; - for (p=0, pl = allpts.length; p points.length - 2 ? points.length -1 : intPoint + 1; - c[ 3 ] = intPoint > points.length - 3 ? points.length -1 : intPoint + 2; + color.set( r, g, b, a ); - v.x = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].x, points[ c[ 1 ] ].x, points[ c[ 2 ] ].x, points[ c[ 3 ] ].x, weight ); - v.y = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].y, points[ c[ 1 ] ].y, points[ c[ 2 ] ].y, points[ c[ 3 ] ].y, weight ); + if ( currentColorClear.equals( color ) === false ) { - return v; + gl.clearColor( r, g, b, a ); + currentColorClear.copy( color ); -}; -/************************************************************** - * Ellipse curve - **************************************************************/ + } -THREE.EllipseCurve = function ( aX, aY, xRadius, yRadius, - aStartAngle, aEndAngle, - aClockwise ) { + }; - this.aX = aX; - this.aY = aY; + this.reset = function () { - this.xRadius = xRadius; - this.yRadius = yRadius; + locked = false; - this.aStartAngle = aStartAngle; - this.aEndAngle = aEndAngle; + currentColorMask = null; + currentColorClear = new THREE.Vector4(); - this.aClockwise = aClockwise; + }; }; -THREE.EllipseCurve.prototype = Object.create( THREE.Curve.prototype ); +THREE.WebGLDepthBuffer = function( gl, state ) { -THREE.EllipseCurve.prototype.getPoint = function ( t ) { + var locked = false; - var deltaAngle = this.aEndAngle - this.aStartAngle; + var currentDepthMask = null; + var currentDepthFunc = null; + var currentDepthClear = null; - if ( !this.aClockwise ) { + this.setTest = function ( depthTest ) { - t = 1 - t; + if ( depthTest ) { - } + state.enable( gl.DEPTH_TEST ); - var angle = this.aStartAngle + t * deltaAngle; + } else { - var tx = this.aX + this.xRadius * Math.cos( angle ); - var ty = this.aY + this.yRadius * Math.sin( angle ); + state.disable( gl.DEPTH_TEST ); - return new THREE.Vector2( tx, ty ); + } -}; -/************************************************************** - * Arc curve - **************************************************************/ + }; -THREE.ArcCurve = function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { + this.setMask = function( depthMask ){ - THREE.EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); -}; + if ( currentDepthMask !== depthMask && ! locked ) { -THREE.ArcCurve.prototype = Object.create( THREE.EllipseCurve.prototype ); -/************************************************************** - * Line3D - **************************************************************/ + gl.depthMask( depthMask ); + currentDepthMask = depthMask; -THREE.LineCurve3 = THREE.Curve.create( + } - function ( v1, v2 ) { + }; - this.v1 = v1; - this.v2 = v2; + this.setFunc = function ( depthFunc ) { - }, + if ( currentDepthFunc !== depthFunc ) { - function ( t ) { + if ( depthFunc ) { - var r = new THREE.Vector3(); + switch ( depthFunc ) { + case THREE.NeverDepth: - r.subVectors( this.v2, this.v1 ); // diff - r.multiplyScalar( t ); - r.add( this.v1 ); + gl.depthFunc( gl.NEVER ); + break; - return r; + case THREE.AlwaysDepth: - } + gl.depthFunc( gl.ALWAYS ); + break; -); + case THREE.LessDepth: -/************************************************************** - * Quadratic Bezier 3D curve - **************************************************************/ + gl.depthFunc( gl.LESS ); + break; -THREE.QuadraticBezierCurve3 = THREE.Curve.create( + case THREE.LessEqualDepth: - function ( v0, v1, v2 ) { + gl.depthFunc( gl.LEQUAL ); + break; - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; + case THREE.EqualDepth: - }, + gl.depthFunc( gl.EQUAL ); + break; - function ( t ) { + case THREE.GreaterEqualDepth: - var tx, ty, tz; + gl.depthFunc( gl.GEQUAL ); + break; - tx = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x ); - ty = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y ); - tz = THREE.Shape.Utils.b2( t, this.v0.z, this.v1.z, this.v2.z ); + case THREE.GreaterDepth: - return new THREE.Vector3( tx, ty, tz ); + gl.depthFunc( gl.GREATER ); + break; - } + case THREE.NotEqualDepth: -); -/************************************************************** - * Cubic Bezier 3D curve - **************************************************************/ + gl.depthFunc( gl.NOTEQUAL ); + break; -THREE.CubicBezierCurve3 = THREE.Curve.create( + default: - function ( v0, v1, v2, v3 ) { + gl.depthFunc( gl.LEQUAL ); - this.v0 = v0; - this.v1 = v1; - this.v2 = v2; - this.v3 = v3; + } - }, + } else { - function ( t ) { + gl.depthFunc( gl.LEQUAL ); - var tx, ty, tz; + } - tx = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ); - ty = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ); - tz = THREE.Shape.Utils.b3( t, this.v0.z, this.v1.z, this.v2.z, this.v3.z ); + currentDepthFunc = depthFunc; - return new THREE.Vector3( tx, ty, tz ); + } - } + }; -); -/************************************************************** - * Spline 3D curve - **************************************************************/ + this.setLocked = function ( lock ) { + locked = lock; -THREE.SplineCurve3 = THREE.Curve.create( + }; - function ( points /* array of Vector3 */) { + this.setClear = function ( depth ) { - this.points = (points == undefined) ? [] : points; + if ( currentDepthClear !== depth ) { - }, + gl.clearDepth( depth ); + currentDepthClear = depth; - function ( t ) { + } - var v = new THREE.Vector3(); - var c = []; - var points = this.points, point, intPoint, weight; - point = ( points.length - 1 ) * t; + }; - intPoint = Math.floor( point ); - weight = point - intPoint; + this.reset = function () { - c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1; - c[ 1 ] = intPoint; - c[ 2 ] = intPoint > points.length - 2 ? points.length - 1 : intPoint + 1; - c[ 3 ] = intPoint > points.length - 3 ? points.length - 1 : intPoint + 2; + locked = false; - var pt0 = points[ c[0] ], - pt1 = points[ c[1] ], - pt2 = points[ c[2] ], - pt3 = points[ c[3] ]; + currentDepthMask = null; + currentDepthFunc = null; + currentDepthClear = null; - v.x = THREE.Curve.Utils.interpolate(pt0.x, pt1.x, pt2.x, pt3.x, weight); - v.y = THREE.Curve.Utils.interpolate(pt0.y, pt1.y, pt2.y, pt3.y, weight); - v.z = THREE.Curve.Utils.interpolate(pt0.z, pt1.z, pt2.z, pt3.z, weight); + }; - return v; +}; - } +THREE.WebGLStencilBuffer = function ( gl, state ) { -); + var locked = false; + var currentStencilMask = null; + var currentStencilFunc = null; + var currentStencilRef = null; + var currentStencilFuncMask = null; + var currentStencilFail = null; + var currentStencilZFail = null; + var currentStencilZPass = null; + var currentStencilClear = null; -// THREE.SplineCurve3.prototype.getTangent = function(t) { -// var v = new THREE.Vector3(); -// var c = []; -// var points = this.points, point, intPoint, weight; -// point = ( points.length - 1 ) * t; + this.setTest = function ( stencilTest ) { -// intPoint = Math.floor( point ); -// weight = point - intPoint; + if ( stencilTest ) { -// c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1; -// c[ 1 ] = intPoint; -// c[ 2 ] = intPoint > points.length - 2 ? points.length - 1 : intPoint + 1; -// c[ 3 ] = intPoint > points.length - 3 ? points.length - 1 : intPoint + 2; + state.enable( gl.STENCIL_TEST ); -// var pt0 = points[ c[0] ], -// pt1 = points[ c[1] ], -// pt2 = points[ c[2] ], -// pt3 = points[ c[3] ]; + } else { -// // t = weight; -// v.x = THREE.Curve.Utils.tangentSpline( t, pt0.x, pt1.x, pt2.x, pt3.x ); -// v.y = THREE.Curve.Utils.tangentSpline( t, pt0.y, pt1.y, pt2.y, pt3.y ); -// v.z = THREE.Curve.Utils.tangentSpline( t, pt0.z, pt1.z, pt2.z, pt3.z ); + state.disable( gl.STENCIL_TEST ); -// return v; + } -// } -/************************************************************** - * Closed Spline 3D curve - **************************************************************/ + }; + this.setMask = function ( stencilMask ) { -THREE.ClosedSplineCurve3 = THREE.Curve.create( + if ( currentStencilMask !== stencilMask && ! locked ) { - function ( points /* array of Vector3 */) { + gl.stencilMask( stencilMask ); + currentStencilMask = stencilMask; - this.points = (points == undefined) ? [] : points; + } - }, + }; - function ( t ) { + this.setFunc = function ( stencilFunc, stencilRef, stencilMask ) { - var v = new THREE.Vector3(); - var c = []; - var points = this.points, point, intPoint, weight; - point = ( points.length - 0 ) * t; - // This needs to be from 0-length +1 + if ( currentStencilFunc !== stencilFunc || + currentStencilRef !== stencilRef || + currentStencilFuncMask !== stencilMask ) { - intPoint = Math.floor( point ); - weight = point - intPoint; + gl.stencilFunc( stencilFunc, stencilRef, stencilMask ); - intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length; - c[ 0 ] = ( intPoint - 1 ) % points.length; - c[ 1 ] = ( intPoint ) % points.length; - c[ 2 ] = ( intPoint + 1 ) % points.length; - c[ 3 ] = ( intPoint + 2 ) % points.length; + currentStencilFunc = stencilFunc; + currentStencilRef = stencilRef; + currentStencilFuncMask = stencilMask; - v.x = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].x, points[ c[ 1 ] ].x, points[ c[ 2 ] ].x, points[ c[ 3 ] ].x, weight ); - v.y = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].y, points[ c[ 1 ] ].y, points[ c[ 2 ] ].y, points[ c[ 3 ] ].y, weight ); - v.z = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].z, points[ c[ 1 ] ].z, points[ c[ 2 ] ].z, points[ c[ 3 ] ].z, weight ); + } - return v; + }; - } + this.setOp = function ( stencilFail, stencilZFail, stencilZPass ) { -); -/** - * @author mikael emtinger / http://gomo.se/ - */ + if ( currentStencilFail !== stencilFail || + currentStencilZFail !== stencilZFail || + currentStencilZPass !== stencilZPass ) { -THREE.AnimationHandler = (function() { + gl.stencilOp( stencilFail, stencilZFail, stencilZPass ); - var playing = []; - var library = {}; - var that = {}; + currentStencilFail = stencilFail; + currentStencilZFail = stencilZFail; + currentStencilZPass = stencilZPass; + } - //--- update --- + }; - that.update = function( deltaTimeMS ) { + this.setLocked = function ( lock ) { - for( var i = 0; i < playing.length; i ++ ) - playing[ i ].update( deltaTimeMS ); + locked = lock; }; + this.setClear = function ( stencil ) { - //--- add --- + if ( currentStencilClear !== stencil ) { - that.addToUpdate = function( animation ) { + gl.clearStencil( stencil ); + currentStencilClear = stencil; - if ( playing.indexOf( animation ) === -1 ) - playing.push( animation ); + } }; + this.reset = function () { - //--- remove --- - - that.removeFromUpdate = function( animation ) { + locked = false; - var index = playing.indexOf( animation ); - - if( index !== -1 ) - playing.splice( index, 1 ); + currentStencilMask = null; + currentStencilFunc = null; + currentStencilRef = null; + currentStencilFuncMask = null; + currentStencilFail = null; + currentStencilZFail = null; + currentStencilZPass = null; + currentStencilClear = null; }; +}; - //--- add --- - - that.add = function( data ) { - - if ( library[ data.name ] !== undefined ) - console.log( "THREE.AnimationHandler.add: Warning! " + data.name + " already exists in library. Overwriting." ); - - library[ data.name ] = data; - initData( data ); +// File:src/renderers/webgl/WebGLTextures.js - }; +/** +* @author mrdoob / http://mrdoob.com/ +*/ +THREE.WebGLTextures = function ( _gl, extensions, state, properties, capabilities, paramThreeToGL, info ) { - //--- get --- + var _infoMemory = info.memory; + var _isWebGL2 = ( typeof WebGL2RenderingContext !== 'undefined' && _gl instanceof WebGL2RenderingContext ); - that.get = function( name ) { + // - if ( typeof name === "string" ) { + function clampToMaxSize ( image, maxSize ) { - if ( library[ name ] ) { + if ( image.width > maxSize || image.height > maxSize ) { - return library[ name ]; + // Warning: Scaling through the canvas will only work with images that use + // premultiplied alpha. - } else { + var scale = maxSize / Math.max( image.width, image.height ); - console.log( "THREE.AnimationHandler.get: Couldn't find animation " + name ); - return null; + var canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); + canvas.width = Math.floor( image.width * scale ); + canvas.height = Math.floor( image.height * scale ); - } + var context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height ); - } else { + console.warn( 'THREE.WebGLRenderer: image is too big (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height, image ); - // todo: add simple tween library + return canvas; } - }; + return image; - //--- parse --- + } - that.parse = function( root ) { + function isPowerOfTwo( image ) { - // setup hierarchy + return THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height ); - var hierarchy = []; + } - if ( root instanceof THREE.SkinnedMesh ) { + function makePowerOfTwo( image ) { - for( var b = 0; b < root.bones.length; b++ ) { + if ( image instanceof HTMLImageElement || image instanceof HTMLCanvasElement ) { - hierarchy.push( root.bones[ b ] ); + var canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); + canvas.width = THREE.Math.nearestPowerOfTwo( image.width ); + canvas.height = THREE.Math.nearestPowerOfTwo( image.height ); - } + var context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0, canvas.width, canvas.height ); - } else { + console.warn( 'THREE.WebGLRenderer: image is not power of two (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height, image ); - parseRecurseHierarchy( root, hierarchy ); + return canvas; } - return hierarchy; + return image; - }; + } - var parseRecurseHierarchy = function( root, hierarchy ) { + function textureNeedsPowerOfTwo( texture ) { - hierarchy.push( root ); + if ( texture.wrapS !== THREE.ClampToEdgeWrapping || texture.wrapT !== THREE.ClampToEdgeWrapping ) return true; + if ( texture.minFilter !== THREE.NearestFilter && texture.minFilter !== THREE.LinearFilter ) return true; - for( var c = 0; c < root.children.length; c++ ) - parseRecurseHierarchy( root.children[ c ], hierarchy ); + return false; } + // Fallback filters for non-power-of-2 textures - //--- init data --- + function filterFallback ( f ) { - var initData = function( data ) { + if ( f === THREE.NearestFilter || f === THREE.NearestMipMapNearestFilter || f === THREE.NearestMipMapLinearFilter ) { - if( data.initialized === true ) - return; + return _gl.NEAREST; + } - // loop through all keys + return _gl.LINEAR; - for( var h = 0; h < data.hierarchy.length; h ++ ) { + } - for( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { + // - // remove minus times + function onTextureDispose( event ) { - if( data.hierarchy[ h ].keys[ k ].time < 0 ) - data.hierarchy[ h ].keys[ k ].time = 0; + var texture = event.target; + texture.removeEventListener( 'dispose', onTextureDispose ); - // create quaternions + deallocateTexture( texture ); - if( data.hierarchy[ h ].keys[ k ].rot !== undefined && - !( data.hierarchy[ h ].keys[ k ].rot instanceof THREE.Quaternion ) ) { + _infoMemory.textures --; - var quat = data.hierarchy[ h ].keys[ k ].rot; - data.hierarchy[ h ].keys[ k ].rot = new THREE.Quaternion( quat[0], quat[1], quat[2], quat[3] ); - } + } - } + function onRenderTargetDispose( event ) { + var renderTarget = event.target; - // prepare morph target keys + renderTarget.removeEventListener( 'dispose', onRenderTargetDispose ); - if( data.hierarchy[ h ].keys.length && data.hierarchy[ h ].keys[ 0 ].morphTargets !== undefined ) { + deallocateRenderTarget( renderTarget ); - // get all used + _infoMemory.textures --; - var usedMorphTargets = {}; + } - for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { + // - for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) { + function deallocateTexture( texture ) { - var morphTargetName = data.hierarchy[ h ].keys[ k ].morphTargets[ m ]; - usedMorphTargets[ morphTargetName ] = -1; + var textureProperties = properties.get( texture ); - } + if ( texture.image && textureProperties.__image__webglTextureCube ) { - } + // cube texture - data.hierarchy[ h ].usedMorphTargets = usedMorphTargets; + _gl.deleteTexture( textureProperties.__image__webglTextureCube ); + } else { - // set all used on all frames + // 2D texture - for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { + if ( textureProperties.__webglInit === undefined ) return; - var influences = {}; + _gl.deleteTexture( textureProperties.__webglTexture ); - for ( var morphTargetName in usedMorphTargets ) { + } - for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) { + // remove all webgl properties + properties.delete( texture ); - if ( data.hierarchy[ h ].keys[ k ].morphTargets[ m ] === morphTargetName ) { + } - influences[ morphTargetName ] = data.hierarchy[ h ].keys[ k ].morphTargetsInfluences[ m ]; - break; + function deallocateRenderTarget( renderTarget ) { - } + var renderTargetProperties = properties.get( renderTarget ); + var textureProperties = properties.get( renderTarget.texture ); - } + if ( ! renderTarget ) return; - if ( m === data.hierarchy[ h ].keys[ k ].morphTargets.length ) { + if ( textureProperties.__webglTexture !== undefined ) { - influences[ morphTargetName ] = 0; + _gl.deleteTexture( textureProperties.__webglTexture ); - } + } - } + if ( renderTarget.depthTexture ) { - data.hierarchy[ h ].keys[ k ].morphTargetsInfluences = influences; + renderTarget.depthTexture.dispose(); - } + } - } + if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) { + for ( var i = 0; i < 6; i ++ ) { - // remove all keys that are on the same time + _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] ); + if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] ); - for ( var k = 1; k < data.hierarchy[ h ].keys.length; k ++ ) { + } - if ( data.hierarchy[ h ].keys[ k ].time === data.hierarchy[ h ].keys[ k - 1 ].time ) { + } else { - data.hierarchy[ h ].keys.splice( k, 1 ); - k --; + _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer ); + if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer ); - } + } - } + properties.delete( renderTarget.texture ); + properties.delete( renderTarget ); + } - // set index + // - for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { - data.hierarchy[ h ].keys[ k ].index = k; - } + function setTexture2D( texture, slot ) { - } + var textureProperties = properties.get( texture ); + if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - // JIT + var image = texture.image; - var lengthInFrames = parseInt( data.length * data.fps, 10 ); + if ( image === undefined ) { - data.JIT = {}; - data.JIT.hierarchy = []; + console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is undefined', texture ); - for( var h = 0; h < data.hierarchy.length; h ++ ) - data.JIT.hierarchy.push( new Array( lengthInFrames ) ); + } else if ( image.complete === false ) { + console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete', texture ); - // done + } else { - data.initialized = true; + uploadTexture( textureProperties, texture, slot ); + return; - }; + } + } - // interpolation types + state.activeTexture( _gl.TEXTURE0 + slot ); + state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); - that.LINEAR = 0; - that.CATMULLROM = 1; - that.CATMULLROM_FORWARD = 2; + } - return that; + function setTextureCube ( texture, slot ) { -}()); + var textureProperties = properties.get( texture ); -/** - * @author mikael emtinger / http://gomo.se/ - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ + if ( texture.image.length === 6 ) { -THREE.Animation = function ( root, name, interpolationType ) { + if ( texture.version > 0 && textureProperties.__version !== texture.version ) { - this.root = root; - this.data = THREE.AnimationHandler.get( name ); - this.hierarchy = THREE.AnimationHandler.parse( root ); + if ( ! textureProperties.__image__webglTextureCube ) { - this.currentTime = 0; - this.timeScale = 1; + texture.addEventListener( 'dispose', onTextureDispose ); - this.isPlaying = false; - this.isPaused = true; - this.loop = true; + textureProperties.__image__webglTextureCube = _gl.createTexture(); - this.interpolationType = interpolationType !== undefined ? interpolationType : THREE.AnimationHandler.LINEAR; + _infoMemory.textures ++; - this.points = []; - this.target = new THREE.Vector3(); + } -}; + state.activeTexture( _gl.TEXTURE0 + slot ); + state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube ); -THREE.Animation.prototype.play = function ( loop, startTimeMS ) { + _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); - if ( this.isPlaying === false ) { + var isCompressed = texture instanceof THREE.CompressedTexture; + var isDataTexture = texture.image[ 0 ] instanceof THREE.DataTexture; - this.isPlaying = true; - this.loop = loop !== undefined ? loop : true; - this.currentTime = startTimeMS !== undefined ? startTimeMS : 0; + var cubeImage = []; - // reset key cache + for ( var i = 0; i < 6; i ++ ) { - var h, hl = this.hierarchy.length, - object; + if ( ! isCompressed && ! isDataTexture ) { - for ( h = 0; h < hl; h ++ ) { + cubeImage[ i ] = clampToMaxSize( texture.image[ i ], capabilities.maxCubemapSize ); - object = this.hierarchy[ h ]; + } else { - object.matrixAutoUpdate = true; + cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; - if ( object.animationCache === undefined ) { + } - object.animationCache = {}; - object.animationCache.prevKey = { pos: 0, rot: 0, scl: 0 }; - object.animationCache.nextKey = { pos: 0, rot: 0, scl: 0 }; - object.animationCache.originalMatrix = object instanceof THREE.Bone ? object.skinMatrix : object.matrix; + } - } + var image = cubeImage[ 0 ], + isPowerOfTwoImage = isPowerOfTwo( image ), + glFormat = paramThreeToGL( texture.format ), + glType = paramThreeToGL( texture.type ); - var prevKey = object.animationCache.prevKey; - var nextKey = object.animationCache.nextKey; + setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isPowerOfTwoImage ); - prevKey.pos = this.data.hierarchy[ h ].keys[ 0 ]; - prevKey.rot = this.data.hierarchy[ h ].keys[ 0 ]; - prevKey.scl = this.data.hierarchy[ h ].keys[ 0 ]; + for ( var i = 0; i < 6; i ++ ) { - nextKey.pos = this.getNextKeyWith( "pos", h, 1 ); - nextKey.rot = this.getNextKeyWith( "rot", h, 1 ); - nextKey.scl = this.getNextKeyWith( "scl", h, 1 ); + if ( ! isCompressed ) { - } + if ( isDataTexture ) { - this.update( 0 ); + state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); - } + } else { - this.isPaused = false; + state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] ); - THREE.AnimationHandler.addToUpdate( this ); + } -}; + } else { + var mipmap, mipmaps = cubeImage[ i ].mipmaps; -THREE.Animation.prototype.pause = function() { + for ( var j = 0, jl = mipmaps.length; j < jl; j ++ ) { - if ( this.isPaused === true ) { + mipmap = mipmaps[ j ]; - THREE.AnimationHandler.addToUpdate( this ); + if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) { - } else { + if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) { - THREE.AnimationHandler.removeFromUpdate( this ); + state.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); - } + } else { - this.isPaused = !this.isPaused; + console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()" ); -}; + } + } else { -THREE.Animation.prototype.stop = function() { + state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); - this.isPlaying = false; - this.isPaused = false; - THREE.AnimationHandler.removeFromUpdate( this ); + } -}; + } + } -THREE.Animation.prototype.update = function ( deltaTimeMS ) { + } - // early out + if ( texture.generateMipmaps && isPowerOfTwoImage ) { - if ( this.isPlaying === false ) return; + _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); + } - // vars + textureProperties.__version = texture.version; - var types = [ "pos", "rot", "scl" ]; - var type; - var scale; - var vector; - var prevXYZ, nextXYZ; - var prevKey, nextKey; - var object; - var animationCache; - var frame; - var JIThierarchy = this.data.JIT.hierarchy; - var currentTime, unloopedCurrentTime; - var currentPoint, forwardPoint, angle; + if ( texture.onUpdate ) texture.onUpdate( texture ); + } else { - this.currentTime += deltaTimeMS * this.timeScale; + state.activeTexture( _gl.TEXTURE0 + slot ); + state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube ); - unloopedCurrentTime = this.currentTime; - currentTime = this.currentTime = this.currentTime % this.data.length; - frame = parseInt( Math.min( currentTime * this.data.fps, this.data.length * this.data.fps ), 10 ); + } + } - for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { + } - object = this.hierarchy[ h ]; - animationCache = object.animationCache; + function setTextureCubeDynamic ( texture, slot ) { - // loop through pos/rot/scl + state.activeTexture( _gl.TEXTURE0 + slot ); + state.bindTexture( _gl.TEXTURE_CUBE_MAP, properties.get( texture ).__webglTexture ); - for ( var t = 0; t < 3; t ++ ) { + } - // get keys + function setTextureParameters ( textureType, texture, isPowerOfTwoImage ) { - type = types[ t ]; - prevKey = animationCache.prevKey[ type ]; - nextKey = animationCache.nextKey[ type ]; + var extension; - // switch keys? + if ( isPowerOfTwoImage ) { - if ( nextKey.time <= unloopedCurrentTime ) { + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, paramThreeToGL( texture.wrapS ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, paramThreeToGL( texture.wrapT ) ); - // did we loop? + _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, paramThreeToGL( texture.magFilter ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, paramThreeToGL( texture.minFilter ) ); - if ( currentTime < unloopedCurrentTime ) { + } else { - if ( this.loop ) { + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); - prevKey = this.data.hierarchy[ h ].keys[ 0 ]; - nextKey = this.getNextKeyWith( type, h, 1 ); + if ( texture.wrapS !== THREE.ClampToEdgeWrapping || texture.wrapT !== THREE.ClampToEdgeWrapping ) { - while( nextKey.time < currentTime ) { + console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.', texture ); - prevKey = nextKey; - nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 ); + } - } + _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) ); - } else { + if ( texture.minFilter !== THREE.NearestFilter && texture.minFilter !== THREE.LinearFilter ) { - this.stop(); - return; + console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.', texture ); - } + } - } else { + } - do { + extension = extensions.get( 'EXT_texture_filter_anisotropic' ); - prevKey = nextKey; - nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 ); + if ( extension ) { - } while( nextKey.time < currentTime ) + 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 ) { - animationCache.prevKey[ type ] = prevKey; - animationCache.nextKey[ type ] = nextKey; + _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) ); + properties.get( texture ).__currentAnisotropy = texture.anisotropy; } + } + + } - object.matrixAutoUpdate = true; - object.matrixWorldNeedsUpdate = true; + function uploadTexture( textureProperties, texture, slot ) { - scale = ( currentTime - prevKey.time ) / ( nextKey.time - prevKey.time ); - prevXYZ = prevKey[ type ]; - nextXYZ = nextKey[ type ]; + if ( textureProperties.__webglInit === undefined ) { + textureProperties.__webglInit = true; - // check scale error + texture.addEventListener( 'dispose', onTextureDispose ); - if ( scale < 0 || scale > 1 ) { + textureProperties.__webglTexture = _gl.createTexture(); - console.log( "THREE.Animation.update: Warning! Scale out of bounds:" + scale + " on bone " + h ); - scale = scale < 0 ? 0 : 1; + _infoMemory.textures ++; - } + } - // interpolate + state.activeTexture( _gl.TEXTURE0 + slot ); + state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); - if ( type === "pos" ) { + _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 ); - vector = object.position; + var image = clampToMaxSize( texture.image, capabilities.maxTextureSize ); - if ( this.interpolationType === THREE.AnimationHandler.LINEAR ) { + if ( textureNeedsPowerOfTwo( texture ) && isPowerOfTwo( image ) === false ) { - vector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale; - vector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale; - vector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale; + image = makePowerOfTwo( image ); - } else if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || - this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + } - this.points[ 0 ] = this.getPrevKeyWith( "pos", h, prevKey.index - 1 )[ "pos" ]; - this.points[ 1 ] = prevXYZ; - this.points[ 2 ] = nextXYZ; - this.points[ 3 ] = this.getNextKeyWith( "pos", h, nextKey.index + 1 )[ "pos" ]; + var isPowerOfTwoImage = isPowerOfTwo( image ), + glFormat = paramThreeToGL( texture.format ), + glType = paramThreeToGL( texture.type ); - scale = scale * 0.33 + 0.33; + setTextureParameters( _gl.TEXTURE_2D, texture, isPowerOfTwoImage ); - currentPoint = this.interpolateCatmullRom( this.points, scale ); + var mipmap, mipmaps = texture.mipmaps; - vector.x = currentPoint[ 0 ]; - vector.y = currentPoint[ 1 ]; - vector.z = currentPoint[ 2 ]; + if ( texture instanceof THREE.DepthTexture ) { - if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + // populate depth texture with dummy data - forwardPoint = this.interpolateCatmullRom( this.points, scale * 1.01 ); + var internalFormat = _gl.DEPTH_COMPONENT; - this.target.set( forwardPoint[ 0 ], forwardPoint[ 1 ], forwardPoint[ 2 ] ); - this.target.sub( vector ); - this.target.y = 0; - this.target.normalize(); + if ( texture.type === THREE.FloatType ) { - angle = Math.atan2( this.target.x, this.target.z ); - object.rotation.set( 0, angle, 0 ); + if ( !_isWebGL2 ) throw new Error('Float Depth Texture only supported in WebGL2.0'); + internalFormat = _gl.DEPTH_COMPONENT32F; - } + } else if ( _isWebGL2 ) { - } + // WebGL 2.0 requires signed internalformat for glTexImage2D + internalFormat = _gl.DEPTH_COMPONENT16; - } else if ( type === "rot" ) { + } - THREE.Quaternion.slerp( prevXYZ, nextXYZ, object.quaternion, scale ); + state.texImage2D( _gl.TEXTURE_2D, 0, internalFormat, image.width, image.height, 0, glFormat, glType, null ); - } else if ( type === "scl" ) { + } else if ( texture instanceof THREE.DataTexture ) { - vector = object.scale; + // 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 - vector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale; - vector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale; - vector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale; + if ( mipmaps.length > 0 && isPowerOfTwoImage ) { - } + 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 ); - } + } -}; + texture.generateMipmaps = false; -// Catmull-Rom spline + } else { -THREE.Animation.prototype.interpolateCatmullRom = function ( points, scale ) { + state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data ); - var c = [], v3 = [], - point, intPoint, weight, w2, w3, - pa, pb, pc, pd; + } - point = ( points.length - 1 ) * scale; - intPoint = Math.floor( point ); - weight = point - intPoint; + } else if ( texture instanceof THREE.CompressedTexture ) { - 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; + for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { - pa = points[ c[ 0 ] ]; - pb = points[ c[ 1 ] ]; - pc = points[ c[ 2 ] ]; - pd = points[ c[ 3 ] ]; + mipmap = mipmaps[ i ]; - w2 = weight * weight; - w3 = weight * w2; + if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) { - v3[ 0 ] = this.interpolate( pa[ 0 ], pb[ 0 ], pc[ 0 ], pd[ 0 ], weight, w2, w3 ); - v3[ 1 ] = this.interpolate( pa[ 1 ], pb[ 1 ], pc[ 1 ], pd[ 1 ], weight, w2, w3 ); - v3[ 2 ] = this.interpolate( pa[ 2 ], pb[ 2 ], pc[ 2 ], pd[ 2 ], weight, w2, w3 ); + if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) { - return v3; + state.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); -}; + } else { -THREE.Animation.prototype.interpolate = function ( p0, p1, p2, p3, t, t2, t3 ) { + console.warn( "THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()" ); - 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; + } else { -}; + state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); + } + } -// Get next key with + } else { -THREE.Animation.prototype.getNextKeyWith = function ( type, h, key ) { + // regular Texture (image, video, canvas) - var keys = this.data.hierarchy[ h ].keys; + // 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 ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || - this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + if ( mipmaps.length > 0 && isPowerOfTwoImage ) { - key = key < keys.length - 1 ? key : keys.length - 1; + for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { - } else { + mipmap = mipmaps[ i ]; + state.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap ); - key = key % keys.length; + } - } + texture.generateMipmaps = false; - for ( ; key < keys.length; key++ ) { + } else { - if ( keys[ key ][ type ] !== undefined ) { + state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, image ); - return keys[ key ]; + } } - } + if ( texture.generateMipmaps && isPowerOfTwoImage ) _gl.generateMipmap( _gl.TEXTURE_2D ); - return this.data.hierarchy[ h ].keys[ 0 ]; + textureProperties.__version = texture.version; -}; + if ( texture.onUpdate ) texture.onUpdate( texture ); -// Get previous key with + } -THREE.Animation.prototype.getPrevKeyWith = function ( type, h, key ) { + // Render targets - var keys = this.data.hierarchy[ h ].keys; + // Setup storage for target texture and bind it to correct framebuffer + function setupFrameBufferTexture ( framebuffer, renderTarget, attachment, textureTarget ) { - if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || - this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + var glFormat = paramThreeToGL( renderTarget.texture.format ); + var glType = paramThreeToGL( renderTarget.texture.type ); + state.texImage2D( textureTarget, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); + _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + _gl.framebufferTexture2D( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( renderTarget.texture ).__webglTexture, 0 ); + _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); - key = key > 0 ? key : 0; + } - } else { + // Setup storage for internal depth/stencil buffers and bind to correct framebuffer + function setupRenderBufferStorage ( renderbuffer, renderTarget ) { - key = key >= 0 ? key : key + keys.length; + _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer ); - } + if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); - for ( ; key >= 0; key -- ) { + } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { - if ( keys[ key ][ type ] !== undefined ) { + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); - return keys[ key ]; + } else { - } - - } - - return this.data.hierarchy[ h ].keys[ keys.length - 1 ]; - -}; - -/** - * @author mikael emtinger / http://gomo.se/ - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * @author khang duong - * @author erik kitson - */ - -THREE.KeyFrameAnimation = function( root, data, JITCompile ) { - - this.root = root; - this.data = THREE.AnimationHandler.get( data ); - this.hierarchy = THREE.AnimationHandler.parse( root ); - this.currentTime = 0; - this.timeScale = 0.001; - this.isPlaying = false; - this.isPaused = true; - this.loop = true; - this.JITCompile = JITCompile !== undefined ? JITCompile : true; - - // initialize to first keyframes - - for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) { - - var keys = this.data.hierarchy[h].keys, - sids = this.data.hierarchy[h].sids, - obj = this.hierarchy[h]; + // FIXME: We don't support !depth !stencil + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height ); - if ( keys.length && sids ) { + } - for ( var s = 0; s < sids.length; s++ ) { + _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); - var sid = sids[ s ], - next = this.getNextKeyWith( sid, h, 0 ); + } - if ( next ) { + // Setup resources for a Depth Texture for a FBO (needs an extension) + function setupDepthTexture ( framebuffer, renderTarget ) { - next.apply( sid ); + var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube ); + if ( isCube ) throw new Error('Depth Texture with cube render targets is not supported!'); - } + _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); - } + if ( !( renderTarget.depthTexture instanceof THREE.DepthTexture ) ) { - obj.matrixAutoUpdate = false; - this.data.hierarchy[h].node.updateMatrix(); - obj.matrixWorldNeedsUpdate = true; + throw new Error('renderTarget.depthTexture must be an instance of THREE.DepthTexture'); } - } + // upload an empty depth texture with framebuffer size + if ( !properties.get( renderTarget.depthTexture ).__webglTexture || + renderTarget.depthTexture.image.width !== renderTarget.width || + renderTarget.depthTexture.image.height !== renderTarget.height ) { + renderTarget.depthTexture.image.width = renderTarget.width; + renderTarget.depthTexture.image.height = renderTarget.height; + renderTarget.depthTexture.needsUpdate = true; + } -}; + setTexture2D( renderTarget.depthTexture, 0 ); -// Play + var webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture; + _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 ); -THREE.KeyFrameAnimation.prototype.play = function( loop, startTimeMS ) { + } - if( !this.isPlaying ) { + // Setup GL resources for a non-texture depth buffer + function setupDepthRenderbuffer( renderTarget ) { - this.isPlaying = true; - this.loop = loop !== undefined ? loop : true; - this.currentTime = startTimeMS !== undefined ? startTimeMS : 0; - this.startTimeMs = startTimeMS; - this.startTime = 10000000; - this.endTime = -this.startTime; + var renderTargetProperties = properties.get( renderTarget ); + var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube ); - // reset key cache + if ( renderTarget.depthTexture ) { - var h, hl = this.hierarchy.length, - object, - node; + if ( isCube ) throw new Error('target.depthTexture not supported in Cube render targets'); - for ( h = 0; h < hl; h++ ) { + setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget ); - object = this.hierarchy[ h ]; - node = this.data.hierarchy[ h ]; + } else { - if ( node.animationCache === undefined ) { + if ( isCube ) { - node.animationCache = {}; - node.animationCache.prevKey = null; - node.animationCache.nextKey = null; - node.animationCache.originalMatrix = object instanceof THREE.Bone ? object.skinMatrix : object.matrix; + renderTargetProperties.__webglDepthbuffer = []; - } + for ( var i = 0; i < 6; i ++ ) { - var keys = this.data.hierarchy[h].keys; + _gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[ i ] ); + renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer(); + setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget ); - if (keys.length) { + } - node.animationCache.prevKey = keys[ 0 ]; - node.animationCache.nextKey = keys[ 1 ]; + } else { - this.startTime = Math.min( keys[0].time, this.startTime ); - this.endTime = Math.max( keys[keys.length - 1].time, this.endTime ); + _gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer ); + renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer(); + setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget ); } } - this.update( 0 ); + _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); } - this.isPaused = false; + // Set up GL resources for the render target + function setupRenderTarget( renderTarget ) { - THREE.AnimationHandler.addToUpdate( this ); + var renderTargetProperties = properties.get( renderTarget ); + var textureProperties = properties.get( renderTarget.texture ); -}; + renderTarget.addEventListener( 'dispose', onRenderTargetDispose ); + textureProperties.__webglTexture = _gl.createTexture(); + _infoMemory.textures ++; -// Pause + var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube ); + var isTargetPowerOfTwo = isPowerOfTwo( renderTarget ); -THREE.KeyFrameAnimation.prototype.pause = function() { + // Setup framebuffer - if( this.isPaused ) { + if ( isCube ) { - THREE.AnimationHandler.addToUpdate( this ); + renderTargetProperties.__webglFramebuffer = []; - } else { + for ( var i = 0; i < 6; i ++ ) { - THREE.AnimationHandler.removeFromUpdate( this ); + renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer(); - } + } - this.isPaused = !this.isPaused; + } else { -}; + renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); + } -// Stop + // Setup color buffer -THREE.KeyFrameAnimation.prototype.stop = function() { + if ( isCube ) { - this.isPlaying = false; - this.isPaused = false; - THREE.AnimationHandler.removeFromUpdate( this ); + state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture ); + setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget.texture, isTargetPowerOfTwo ); + for ( var i = 0; i < 6; i ++ ) { - // reset JIT matrix and remove cache + setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i ); - for ( var h = 0; h < this.data.hierarchy.length; h++ ) { - - var obj = this.hierarchy[ h ]; - var node = this.data.hierarchy[ h ]; + } - if ( node.animationCache !== undefined ) { + if ( renderTarget.texture.generateMipmaps && isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); + state.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); - var original = node.animationCache.originalMatrix; + } else { - if( obj instanceof THREE.Bone ) { + state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture ); + setTextureParameters( _gl.TEXTURE_2D, renderTarget.texture, isTargetPowerOfTwo ); + setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D ); - original.copy( obj.skinMatrix ); - obj.skinMatrix = original; + if ( renderTarget.texture.generateMipmaps && isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); + state.bindTexture( _gl.TEXTURE_2D, null ); - } else { + } - original.copy( obj.matrix ); - obj.matrix = original; + // Setup depth and stencil buffers - } + if ( renderTarget.depthBuffer ) { - delete node.animationCache; + setupDepthRenderbuffer( renderTarget ); } } -}; + function updateRenderTargetMipmap( renderTarget ) { + var texture = renderTarget.texture; -// Update + if ( texture.generateMipmaps && isPowerOfTwo( renderTarget ) && + texture.minFilter !== THREE.NearestFilter && + texture.minFilter !== THREE.LinearFilter ) { -THREE.KeyFrameAnimation.prototype.update = function( deltaTimeMS ) { + var target = renderTarget instanceof THREE.WebGLRenderTargetCube ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D; + var webglTexture = properties.get( texture ).__webglTexture; - // early out + state.bindTexture( target, webglTexture ); + _gl.generateMipmap( target ); + state.bindTexture( target, null ); - if( !this.isPlaying ) return; + } + } - // vars + this.setTexture2D = setTexture2D; + this.setTextureCube = setTextureCube; + this.setTextureCubeDynamic = setTextureCubeDynamic; + this.setupRenderTarget = setupRenderTarget; + this.updateRenderTargetMipmap = updateRenderTargetMipmap; - var prevKey, nextKey; - var object; - var node; - var frame; - var JIThierarchy = this.data.JIT.hierarchy; - var currentTime, unloopedCurrentTime; - var looped; +}; +// File:src/renderers/webgl/WebGLUniforms.js - // update +/** + * + * Uniforms of a program. + * Those form a tree structure with a special top-level container for the root, + * which you get by calling 'new WebGLUniforms( gl, program, renderer )'. + * + * + * Properties of inner nodes including the top-level container: + * + * .seq - array of nested uniforms + * .map - nested uniforms by name + * + * + * Methods of all nodes except the top-level container: + * + * .setValue( gl, value, [renderer] ) + * + * uploads a uniform value(s) + * the 'renderer' parameter is needed for sampler uniforms + * + * + * Static methods of the top-level container (renderer factorizations): + * + * .upload( gl, seq, values, renderer ) + * + * sets uniforms in 'seq' to 'values[id].value' + * + * .seqWithValue( seq, values ) : filteredSeq + * + * filters 'seq' entries with corresponding entry in values + * + * .splitDynamic( seq, values ) : filteredSeq + * + * filters 'seq' entries with dynamic entry and removes them from 'seq' + * + * + * Methods of the top-level container (renderer factorizations): + * + * .setValue( gl, name, value ) + * + * sets uniform with name 'name' to 'value' + * + * .set( gl, obj, prop ) + * + * sets uniform from object and property with same name than uniform + * + * .setOptional( gl, obj, prop ) + * + * like .set for an optional property of the object + * + * + * @author tschw + * + */ - this.currentTime += deltaTimeMS * this.timeScale; +THREE.WebGLUniforms = ( function() { // scope - unloopedCurrentTime = this.currentTime; - currentTime = this.currentTime = this.currentTime % this.data.length; + var emptyTexture = new THREE.Texture(); + var emptyCubeTexture = new THREE.CubeTexture(); - // if looped around, the current time should be based on the startTime - if ( currentTime < this.startTimeMs ) { + // --- Base for inner nodes (including the root) --- - currentTime = this.currentTime = this.startTimeMs + currentTime; + var UniformContainer = function() { - } + this.seq = []; + this.map = {}; - frame = parseInt( Math.min( currentTime * this.data.fps, this.data.length * this.data.fps ), 10 ); - looped = currentTime < unloopedCurrentTime; + }, - if ( looped && !this.loop ) { + // --- Utilities --- - // Set the animation to the last keyframes and stop - for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) { + // Array Caches (provide typed arrays for temporary by size) - var keys = this.data.hierarchy[h].keys, - sids = this.data.hierarchy[h].sids, - end = keys.length-1, - obj = this.hierarchy[h]; + arrayCacheF32 = [], + arrayCacheI32 = [], - if ( keys.length ) { + uncacheTemporaryArrays = function() { - for ( var s = 0; s < sids.length; s++ ) { + arrayCacheF32.length = 0; + arrayCacheI32.length = 0; - var sid = sids[ s ], - prev = this.getPrevKeyWith( sid, h, end ); + }, - if ( prev ) { - prev.apply( sid ); + // Flattening for arrays of vectors and matrices - } + flatten = function( array, nBlocks, blockSize ) { - } + var firstElem = array[ 0 ]; - this.data.hierarchy[h].node.updateMatrix(); - obj.matrixWorldNeedsUpdate = true; + if ( firstElem <= 0 || firstElem > 0 ) return array; + // unoptimized: ! isNaN( firstElem ) + // see http://jacksondunstan.com/articles/983 - } + var n = nBlocks * blockSize, + r = arrayCacheF32[ n ]; - } + if ( r === undefined ) { - this.stop(); - return; + r = new Float32Array( n ); + arrayCacheF32[ n ] = r; - } + } - // check pre-infinity - if ( currentTime < this.startTime ) { + if ( nBlocks !== 0 ) { - return; + firstElem.toArray( r, 0 ); - } + for ( var i = 1, offset = 0; i !== nBlocks; ++ i ) { - // update + offset += blockSize; + array[ i ].toArray( r, offset ); - for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) { + } - object = this.hierarchy[ h ]; - node = this.data.hierarchy[ h ]; + } - var keys = node.keys, - animationCache = node.animationCache; + return r; - // use JIT? + }, - if ( this.JITCompile && JIThierarchy[ h ][ frame ] !== undefined ) { + // Texture unit allocation - if( object instanceof THREE.Bone ) { + allocTexUnits = function( renderer, n ) { - object.skinMatrix = JIThierarchy[ h ][ frame ]; - object.matrixWorldNeedsUpdate = false; + var r = arrayCacheI32[ n ]; - } else { + if ( r === undefined ) { - object.matrix = JIThierarchy[ h ][ frame ]; - object.matrixWorldNeedsUpdate = true; + r = new Int32Array( n ); + arrayCacheI32[ n ] = r; } - // use interpolation + for ( var i = 0; i !== n; ++ i ) + r[ i ] = renderer.allocTextureUnit(); - } else if ( keys.length ) { + return r; - // make sure so original matrix and not JIT matrix is set + }, - if ( this.JITCompile && animationCache ) { + // --- Setters --- - if( object instanceof THREE.Bone ) { + // Note: Defining these methods externally, because they come in a bunch + // and this way their names minify. - object.skinMatrix = animationCache.originalMatrix; + // Single scalar - } else { + setValue1f = function( gl, v ) { gl.uniform1f( this.addr, v ); }, + setValue1i = function( gl, v ) { gl.uniform1i( this.addr, v ); }, - object.matrix = animationCache.originalMatrix; + // Single float vector (from flat array or THREE.VectorN) - } + setValue2fv = function( gl, v ) { - } + if ( v.x === undefined ) gl.uniform2fv( this.addr, v ); + else gl.uniform2f( this.addr, v.x, v.y ); + + }, - prevKey = animationCache.prevKey; - nextKey = animationCache.nextKey; + setValue3fv = function( gl, v ) { - if ( prevKey && nextKey ) { + if ( v.x !== undefined ) + gl.uniform3f( this.addr, v.x, v.y, v.z ); + else if ( v.r !== undefined ) + gl.uniform3f( this.addr, v.r, v.g, v.b ); + else + gl.uniform3fv( this.addr, v ); - // switch keys? + }, - if ( nextKey.time <= unloopedCurrentTime ) { + setValue4fv = function( gl, v ) { - // did we loop? + if ( v.x === undefined ) gl.uniform4fv( this.addr, v ); + else gl.uniform4f( this.addr, v.x, v.y, v.z, v.w ); - if ( looped && this.loop ) { + }, - prevKey = keys[ 0 ]; - nextKey = keys[ 1 ]; + // Single matrix (from flat array or MatrixN) - while ( nextKey.time < currentTime ) { + setValue2fm = function( gl, v ) { - prevKey = nextKey; - nextKey = keys[ prevKey.index + 1 ]; + gl.uniformMatrix2fv( this.addr, false, v.elements || v ); - } + }, - } else if ( !looped ) { + setValue3fm = function( gl, v ) { - var lastIndex = keys.length - 1; + gl.uniformMatrix3fv( this.addr, false, v.elements || v ); - while ( nextKey.time < currentTime && nextKey.index !== lastIndex ) { + }, - prevKey = nextKey; - nextKey = keys[ prevKey.index + 1 ]; + setValue4fm = function( gl, v ) { - } + gl.uniformMatrix4fv( this.addr, false, v.elements || v ); - } + }, - animationCache.prevKey = prevKey; - animationCache.nextKey = nextKey; + // Single texture (2D / Cube) - } - if(nextKey.time >= currentTime) - prevKey.interpolate( nextKey, currentTime ); - else - prevKey.interpolate( nextKey, nextKey.time); + setValueT1 = function( gl, v, renderer ) { - } + var unit = renderer.allocTextureUnit(); + gl.uniform1i( this.addr, unit ); + renderer.setTexture2D( v || emptyTexture, unit ); - this.data.hierarchy[h].node.updateMatrix(); - object.matrixWorldNeedsUpdate = true; + }, - } + setValueT6 = function( gl, v, renderer ) { - } + var unit = renderer.allocTextureUnit(); + gl.uniform1i( this.addr, unit ); + renderer.setTextureCube( v || emptyCubeTexture, unit ); - // update JIT? + }, - if ( this.JITCompile ) { + // Integer / Boolean vectors or arrays thereof (always flat arrays) - if ( JIThierarchy[ 0 ][ frame ] === undefined ) { + setValue2iv = function( gl, v ) { gl.uniform2iv( this.addr, v ); }, + setValue3iv = function( gl, v ) { gl.uniform3iv( this.addr, v ); }, + setValue4iv = function( gl, v ) { gl.uniform4iv( this.addr, v ); }, - this.hierarchy[ 0 ].updateMatrixWorld( true ); + // Helper to pick the right setter for the singular case - for ( var h = 0; h < this.hierarchy.length; h++ ) { + getSingularSetter = function( type ) { - if( this.hierarchy[ h ] instanceof THREE.Bone ) { + switch ( type ) { - JIThierarchy[ h ][ frame ] = this.hierarchy[ h ].skinMatrix.clone(); + case 0x1406: return setValue1f; // FLOAT + case 0x8b50: return setValue2fv; // _VEC2 + case 0x8b51: return setValue3fv; // _VEC3 + case 0x8b52: return setValue4fv; // _VEC4 - } else { + case 0x8b5a: return setValue2fm; // _MAT2 + case 0x8b5b: return setValue3fm; // _MAT3 + case 0x8b5c: return setValue4fm; // _MAT4 - JIThierarchy[ h ][ frame ] = this.hierarchy[ h ].matrix.clone(); + case 0x8b5e: return setValueT1; // SAMPLER_2D + case 0x8b60: return setValueT6; // SAMPLER_CUBE - } + case 0x1404: case 0x8b56: return setValue1i; // INT, BOOL + case 0x8b53: case 0x8b57: return setValue2iv; // _VEC2 + case 0x8b54: case 0x8b58: return setValue3iv; // _VEC3 + case 0x8b55: case 0x8b59: return setValue4iv; // _VEC4 } - } + }, - } + // Array of scalars -}; + setValue1fv = function( gl, v ) { gl.uniform1fv( this.addr, v ); }, + setValue1iv = function( gl, v ) { gl.uniform1iv( this.addr, v ); }, -// Get next key with + // Array of vectors (flat or from THREE classes) -THREE.KeyFrameAnimation.prototype.getNextKeyWith = function( sid, h, key ) { + setValueV2a = function( gl, v ) { - var keys = this.data.hierarchy[ h ].keys; - key = key % keys.length; + gl.uniform2fv( this.addr, flatten( v, this.size, 2 ) ); - for ( ; key < keys.length; key++ ) { + }, - if ( keys[ key ].hasTarget( sid ) ) { + setValueV3a = function( gl, v ) { - return keys[ key ]; + gl.uniform3fv( this.addr, flatten( v, this.size, 3 ) ); - } + }, - } + setValueV4a = function( gl, v ) { - return keys[ 0 ]; + gl.uniform4fv( this.addr, flatten( v, this.size, 4 ) ); -}; + }, -// Get previous key with + // Array of matrices (flat or from THREE clases) -THREE.KeyFrameAnimation.prototype.getPrevKeyWith = function( sid, h, key ) { + setValueM2a = function( gl, v ) { - var keys = this.data.hierarchy[ h ].keys; - key = key >= 0 ? key : key + keys.length; + gl.uniformMatrix2fv( this.addr, false, flatten( v, this.size, 4 ) ); - for ( ; key >= 0; key-- ) { + }, - if ( keys[ key ].hasTarget( sid ) ) { + setValueM3a = function( gl, v ) { - return keys[ key ]; + gl.uniformMatrix3fv( this.addr, false, flatten( v, this.size, 9 ) ); - } + }, - } + setValueM4a = function( gl, v ) { - return keys[ keys.length - 1 ]; + gl.uniformMatrix4fv( this.addr, false, flatten( v, this.size, 16 ) ); -}; + }, -/** - * Camera for rendering cube maps - * - renders scene into axis-aligned cube - * - * @author alteredq / http://alteredqualia.com/ - */ + // Array of textures (2D / Cube) -THREE.CubeCamera = function ( near, far, cubeResolution ) { + setValueT1a = function( gl, v, renderer ) { - THREE.Object3D.call( this ); + var n = v.length, + units = allocTexUnits( renderer, n ); - var fov = 90, aspect = 1; + gl.uniform1iv( this.addr, units ); - 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 ); + for ( var i = 0; i !== n; ++ i ) { - 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 ); + renderer.setTexture2D( v[ i ] || emptyTexture, units[ i ] ); - var cameraPY = new THREE.PerspectiveCamera( fov, aspect, near, far ); - cameraPY.up.set( 0, 0, 1 ); - cameraPY.lookAt( new THREE.Vector3( 0, 1, 0 ) ); - this.add( cameraPY ); + } - var cameraNY = new THREE.PerspectiveCamera( fov, aspect, near, far ); - cameraNY.up.set( 0, 0, -1 ); - cameraNY.lookAt( new THREE.Vector3( 0, -1, 0 ) ); - this.add( cameraNY ); + }, - 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 ); + setValueT6a = function( gl, v, renderer ) { - 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 ); + var n = v.length, + units = allocTexUnits( renderer, n ); - this.renderTarget = new THREE.WebGLRenderTargetCube( cubeResolution, cubeResolution, { format: THREE.RGBFormat, magFilter: THREE.LinearFilter, minFilter: THREE.LinearFilter } ); + gl.uniform1iv( this.addr, units ); - this.updateCubeMap = function ( renderer, scene ) { + for ( var i = 0; i !== n; ++ i ) { - var renderTarget = this.renderTarget; - var generateMipmaps = renderTarget.generateMipmaps; + renderer.setTextureCube( v[ i ] || emptyCubeTexture, units[ i ] ); - renderTarget.generateMipmaps = false; + } - renderTarget.activeCubeFace = 0; - renderer.render( scene, cameraPX, renderTarget ); + }, - renderTarget.activeCubeFace = 1; - renderer.render( scene, cameraNX, renderTarget ); - renderTarget.activeCubeFace = 2; - renderer.render( scene, cameraPY, renderTarget ); + // Helper to pick the right setter for a pure (bottom-level) array - renderTarget.activeCubeFace = 3; - renderer.render( scene, cameraNY, renderTarget ); + getPureArraySetter = function( type ) { - renderTarget.activeCubeFace = 4; - renderer.render( scene, cameraPZ, renderTarget ); + switch ( type ) { - renderTarget.generateMipmaps = generateMipmaps; + case 0x1406: return setValue1fv; // FLOAT + case 0x8b50: return setValueV2a; // _VEC2 + case 0x8b51: return setValueV3a; // _VEC3 + case 0x8b52: return setValueV4a; // _VEC4 - renderTarget.activeCubeFace = 5; - renderer.render( scene, cameraNZ, renderTarget ); + case 0x8b5a: return setValueM2a; // _MAT2 + case 0x8b5b: return setValueM3a; // _MAT3 + case 0x8b5c: return setValueM4a; // _MAT4 - }; + case 0x8b5e: return setValueT1a; // SAMPLER_2D + case 0x8b60: return setValueT6a; // SAMPLER_CUBE -}; + case 0x1404: case 0x8b56: return setValue1iv; // INT, BOOL + case 0x8b53: case 0x8b57: return setValue2iv; // _VEC2 + case 0x8b54: case 0x8b58: return setValue3iv; // _VEC3 + case 0x8b55: case 0x8b59: return setValue4iv; // _VEC4 -THREE.CubeCamera.prototype = Object.create( THREE.Object3D.prototype ); + } -/* - * @author zz85 / http://twitter.com/blurspline / http://www.lab4games.net/zz85/blog - * - * A general perpose camera, for setting FOV, Lens Focal Length, - * and switching between perspective and orthographic views easily. - * Use this only if you do not wish to manage - * both a Orthographic and Perspective Camera - * - */ + }, + // --- Uniform Classes --- -THREE.CombinedCamera = function ( width, height, fov, near, far, orthoNear, orthoFar ) { + SingleUniform = function SingleUniform( id, activeInfo, addr ) { - THREE.Camera.call( this ); + this.id = id; + this.addr = addr; + this.setValue = getSingularSetter( activeInfo.type ); - this.fov = fov; + // this.path = activeInfo.name; // DEBUG - this.left = -width / 2; - this.right = width / 2 - this.top = height / 2; - this.bottom = -height / 2; + }, - // We could also handle the projectionMatrix internally, but just wanted to test nested camera objects + PureArrayUniform = function( id, activeInfo, addr ) { - this.cameraO = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, orthoNear, orthoFar ); - this.cameraP = new THREE.PerspectiveCamera( fov, width / height, near, far ); + this.id = id; + this.addr = addr; + this.size = activeInfo.size; + this.setValue = getPureArraySetter( activeInfo.type ); - this.zoom = 1; + // this.path = activeInfo.name; // DEBUG - this.toPerspective(); + }, - var aspect = width/height; + StructuredUniform = function( id ) { -}; + this.id = id; -THREE.CombinedCamera.prototype = Object.create( THREE.Camera.prototype ); + UniformContainer.call( this ); // mix-in -THREE.CombinedCamera.prototype.toPerspective = function () { + }; - // Switches to the Perspective Camera + StructuredUniform.prototype.setValue = function( gl, value ) { - this.near = this.cameraP.near; - this.far = this.cameraP.far; + // Note: Don't need an extra 'renderer' parameter, since samplers + // are not allowed in structured uniforms. - this.cameraP.fov = this.fov / this.zoom ; + var seq = this.seq; - this.cameraP.updateProjectionMatrix(); + for ( var i = 0, n = seq.length; i !== n; ++ i ) { - this.projectionMatrix = this.cameraP.projectionMatrix; + var u = seq[ i ]; + u.setValue( gl, value[ u.id ] ); - this.inPerspectiveMode = true; - this.inOrthographicMode = false; + } -}; + }; -THREE.CombinedCamera.prototype.toOrthographic = function () { + // --- Top-level --- - // Switches to the Orthographic camera estimating viewport from Perspective + // Parser - builds up the property tree from the path strings - var fov = this.fov; - var aspect = this.cameraP.aspect; - var near = this.cameraP.near; - var far = this.cameraP.far; + var RePathPart = /([\w\d_]+)(\])?(\[|\.)?/g, + // extracts + // - the identifier (member name or array index) + // - followed by an optional right bracket (found when array index) + // - followed by an optional left bracket or dot (type of subscript) + // + // Note: These portions can be read in a non-overlapping fashion and + // allow straightforward parsing of the hierarchy that WebGL encodes + // in the uniform names. - // The size that we set is the mid plane of the viewing frustum + addUniform = function( container, uniformObject ) { - var hyperfocus = ( near + far ) / 2; + container.seq.push( uniformObject ); + container.map[ uniformObject.id ] = uniformObject; - var halfHeight = Math.tan( fov / 2 ) * hyperfocus; - var planeHeight = 2 * halfHeight; - var planeWidth = planeHeight * aspect; - var halfWidth = planeWidth / 2; + }, - halfHeight /= this.zoom; - halfWidth /= this.zoom; + parseUniform = function( activeInfo, addr, container ) { - this.cameraO.left = -halfWidth; - this.cameraO.right = halfWidth; - this.cameraO.top = halfHeight; - this.cameraO.bottom = -halfHeight; + var path = activeInfo.name, + pathLength = path.length; - // this.cameraO.left = -farHalfWidth; - // this.cameraO.right = farHalfWidth; - // this.cameraO.top = farHalfHeight; - // this.cameraO.bottom = -farHalfHeight; + // reset RegExp object, because of the early exit of a previous run + RePathPart.lastIndex = 0; - // this.cameraO.left = this.left / this.zoom; - // this.cameraO.right = this.right / this.zoom; - // this.cameraO.top = this.top / this.zoom; - // this.cameraO.bottom = this.bottom / this.zoom; + for (; ;) { - this.cameraO.updateProjectionMatrix(); + var match = RePathPart.exec( path ), + matchEnd = RePathPart.lastIndex, - this.near = this.cameraO.near; - this.far = this.cameraO.far; - this.projectionMatrix = this.cameraO.projectionMatrix; + id = match[ 1 ], + idIsIndex = match[ 2 ] === ']', + subscript = match[ 3 ]; - this.inPerspectiveMode = false; - this.inOrthographicMode = true; + if ( idIsIndex ) id = id | 0; // convert to integer -}; + if ( subscript === undefined || + subscript === '[' && matchEnd + 2 === pathLength ) { + // bare name or "pure" bottom-level array "[0]" suffix + addUniform( container, subscript === undefined ? + new SingleUniform( id, activeInfo, addr ) : + new PureArrayUniform( id, activeInfo, addr ) ); -THREE.CombinedCamera.prototype.setSize = function( width, height ) { + break; - this.cameraP.aspect = width / height; - this.left = -width / 2; - this.right = width / 2 - this.top = height / 2; - this.bottom = -height / 2; + } else { + // step into inner node / create it in case it doesn't exist -}; + var map = container.map, + next = map[ id ]; + if ( next === undefined ) { -THREE.CombinedCamera.prototype.setFov = function( fov ) { + next = new StructuredUniform( id ); + addUniform( container, next ); - this.fov = fov; + } - if ( this.inPerspectiveMode ) { + container = next; - this.toPerspective(); + } - } else { + } - this.toOrthographic(); + }, - } + // Root Container -}; + WebGLUniforms = function WebGLUniforms( gl, program, renderer ) { -// For mantaining similar API with PerspectiveCamera + UniformContainer.call( this ); -THREE.CombinedCamera.prototype.updateProjectionMatrix = function() { + this.renderer = renderer; - if ( this.inPerspectiveMode ) { + var n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS ); - this.toPerspective(); + for ( var i = 0; i !== n; ++ i ) { - } else { + var info = gl.getActiveUniform( program, i ), + path = info.name, + addr = gl.getUniformLocation( program, path ); - this.toPerspective(); - this.toOrthographic(); + parseUniform( info, addr, this ); - } + } -}; + }; -/* -* Uses Focal Length (in mm) to estimate and set FOV -* 35mm (fullframe) camera is used if frame size is not specified; -* Formula based on http://www.bobatkins.com/photography/technical/field_of_view.html -*/ -THREE.CombinedCamera.prototype.setLens = function ( focalLength, frameHeight ) { - if ( frameHeight === undefined ) frameHeight = 24; + WebGLUniforms.prototype.setValue = function( gl, name, value ) { - var fov = 2 * THREE.Math.radToDeg( Math.atan( frameHeight / ( focalLength * 2 ) ) ); + var u = this.map[ name ]; - this.setFov( fov ); + if ( u !== undefined ) u.setValue( gl, value, this.renderer ); - return fov; -}; + }; + WebGLUniforms.prototype.set = function( gl, object, name ) { -THREE.CombinedCamera.prototype.setZoom = function( zoom ) { + var u = this.map[ name ]; - this.zoom = zoom; + if ( u !== undefined ) u.setValue( gl, object[ name ], this.renderer ); - if ( this.inPerspectiveMode ) { + }; - this.toPerspective(); + WebGLUniforms.prototype.setOptional = function( gl, object, name ) { - } else { + var v = object[ name ]; - this.toOrthographic(); + if ( v !== undefined ) this.setValue( gl, name, v ); - } + }; -}; -THREE.CombinedCamera.prototype.toFrontView = function() { + // Static interface - this.rotation.x = 0; - this.rotation.y = 0; - this.rotation.z = 0; + WebGLUniforms.upload = function( gl, seq, values, renderer ) { - // should we be modifing the matrix instead? + for ( var i = 0, n = seq.length; i !== n; ++ i ) { - this.rotationAutoUpdate = false; + var u = seq[ i ], + v = values[ u.id ]; -}; + if ( v.needsUpdate !== false ) { + // note: always updating when .needsUpdate is undefined -THREE.CombinedCamera.prototype.toBackView = function() { + u.setValue( gl, v.value, renderer ); - this.rotation.x = 0; - this.rotation.y = Math.PI; - this.rotation.z = 0; - this.rotationAutoUpdate = false; + } -}; + } -THREE.CombinedCamera.prototype.toLeftView = function() { + }; - this.rotation.x = 0; - this.rotation.y = - Math.PI / 2; - this.rotation.z = 0; - this.rotationAutoUpdate = false; + WebGLUniforms.seqWithValue = function( seq, values ) { -}; + var r = []; -THREE.CombinedCamera.prototype.toRightView = function() { + for ( var i = 0, n = seq.length; i !== n; ++ i ) { - this.rotation.x = 0; - this.rotation.y = Math.PI / 2; - this.rotation.z = 0; - this.rotationAutoUpdate = false; + var u = seq[ i ]; + if ( u.id in values ) r.push( u ); -}; + } -THREE.CombinedCamera.prototype.toTopView = function() { + return r; - this.rotation.x = - Math.PI / 2; - this.rotation.y = 0; - this.rotation.z = 0; - this.rotationAutoUpdate = false; + }; -}; + WebGLUniforms.splitDynamic = function( seq, values ) { -THREE.CombinedCamera.prototype.toBottomView = function() { + var r = null, + n = seq.length, + w = 0; - this.rotation.x = Math.PI / 2; - this.rotation.y = 0; - this.rotation.z = 0; - this.rotationAutoUpdate = false; + for ( var i = 0; i !== n; ++ i ) { -}; + var u = seq[ i ], + v = values[ u.id ]; + if ( v && v.dynamic === true ) { -/** - * @author hughes - */ + if ( r === null ) r = []; + r.push( u ); -THREE.CircleGeometry = function ( radius, segments, thetaStart, thetaLength ) { + } else { - THREE.Geometry.call( this ); + // in-place compact 'seq', removing the matches + if ( w < i ) seq[ w ] = u; + ++ w; - radius = radius || 50; + } - thetaStart = thetaStart !== undefined ? thetaStart : 0; - thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; - segments = segments !== undefined ? Math.max( 3, segments ) : 8; + } - var i, uvs = [], - center = new THREE.Vector3(), centerUV = new THREE.Vector2( 0.5, 0.5 ); + if ( w < n ) seq.length = w; - this.vertices.push(center); - uvs.push( centerUV ); + return r; - for ( i = 0; i <= segments; i ++ ) { + }; - var vertex = new THREE.Vector3(); - var segment = thetaStart + i / segments * thetaLength; + WebGLUniforms.evalDynamic = function( seq, values, object, camera ) { - vertex.x = radius * Math.cos( segment ); - vertex.y = radius * Math.sin( segment ); + for ( var i = 0, n = seq.length; i !== n; ++ i ) { - this.vertices.push( vertex ); - uvs.push( new THREE.Vector2( ( vertex.x / radius + 1 ) / 2, ( vertex.y / radius + 1 ) / 2 ) ); + var v = values[ seq[ i ].id ], + f = v.onUpdateCallback; - } - - var n = new THREE.Vector3( 0, 0, 1 ); + if ( f !== undefined ) f.call( v, object, camera ); - for ( i = 1; i <= segments; i ++ ) { + } - var v1 = i; - var v2 = i + 1 ; - var v3 = 0; + }; - this.faces.push( new THREE.Face3( v1, v2, v3, [ n, n, n ] ) ); - this.faceVertexUvs[ 0 ].push( [ uvs[ i ], uvs[ i + 1 ], centerUV ] ); + return WebGLUniforms; - } +} )(); - this.computeCentroids(); - this.computeFaceNormals(); +// File:src/renderers/webgl/plugins/LensFlarePlugin.js - this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ -}; +THREE.LensFlarePlugin = function ( renderer, flares ) { -THREE.CircleGeometry.prototype = Object.create( THREE.Geometry.prototype ); + var gl = renderer.context; + var state = renderer.state; -/** - * @author mrdoob / http://mrdoob.com/ - * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Cube.as - */ + var vertexBuffer, elementBuffer; + var shader, program, attributes, uniforms; -THREE.CubeGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) { + var tempTexture, occlusionTexture; - THREE.Geometry.call( this ); + function init() { - var scope = this; + var vertices = new Float32Array( [ + - 1, - 1, 0, 0, + 1, - 1, 1, 0, + 1, 1, 1, 1, + - 1, 1, 0, 1 + ] ); - this.width = width; - this.height = height; - this.depth = depth; + var faces = new Uint16Array( [ + 0, 1, 2, + 0, 2, 3 + ] ); - this.widthSegments = widthSegments || 1; - this.heightSegments = heightSegments || 1; - this.depthSegments = depthSegments || 1; + // buffers - var width_half = this.width / 2; - var height_half = this.height / 2; - var depth_half = this.depth / 2; + vertexBuffer = gl.createBuffer(); + elementBuffer = gl.createBuffer(); - buildPlane( 'z', 'y', - 1, - 1, this.depth, this.height, width_half, 0 ); // px - buildPlane( 'z', 'y', 1, - 1, this.depth, this.height, - width_half, 1 ); // nx - buildPlane( 'x', 'z', 1, 1, this.width, this.depth, height_half, 2 ); // py - buildPlane( 'x', 'z', 1, - 1, this.width, this.depth, - height_half, 3 ); // ny - buildPlane( 'x', 'y', 1, - 1, this.width, this.height, depth_half, 4 ); // pz - buildPlane( 'x', 'y', - 1, - 1, this.width, this.height, - depth_half, 5 ); // nz + gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer ); + gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW ); - function buildPlane( u, v, udir, vdir, width, height, depth, materialIndex ) { + gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); + gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW ); - var w, ix, iy, - gridX = scope.widthSegments, - gridY = scope.heightSegments, - width_half = width / 2, - height_half = height / 2, - offset = scope.vertices.length; + // textures - if ( ( u === 'x' && v === 'y' ) || ( u === 'y' && v === 'x' ) ) { + tempTexture = gl.createTexture(); + occlusionTexture = gl.createTexture(); - w = 'z'; + 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 ); - } else if ( ( u === 'x' && v === 'z' ) || ( u === 'z' && v === 'x' ) ) { + 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 ); - w = 'y'; - gridY = scope.depthSegments; + shader = { - } else if ( ( u === 'z' && v === 'y' ) || ( u === 'y' && v === 'z' ) ) { + vertexShader: [ - w = 'x'; - gridX = scope.depthSegments; + "uniform lowp int renderType;", - } + "uniform vec3 screenPosition;", + "uniform vec2 scale;", + "uniform float rotation;", - var gridX1 = gridX + 1, - gridY1 = gridY + 1, - segment_width = width / gridX, - segment_height = height / gridY, - normal = new THREE.Vector3(); + "uniform sampler2D occlusionMap;", - normal[ w ] = depth > 0 ? 1 : - 1; + "attribute vec2 position;", + "attribute vec2 uv;", - for ( iy = 0; iy < gridY1; iy ++ ) { + "varying vec2 vUV;", + "varying float vVisibility;", - for ( ix = 0; ix < gridX1; ix ++ ) { + "void main() {", - var vector = new THREE.Vector3(); - vector[ u ] = ( ix * segment_width - width_half ) * udir; - vector[ v ] = ( iy * segment_height - height_half ) * vdir; - vector[ w ] = depth; + "vUV = uv;", - scope.vertices.push( vector ); + "vec2 pos = position;", - } + "if ( renderType == 2 ) {", - } + "vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );", + "visibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );", - for ( iy = 0; iy < gridY; iy++ ) { + "vVisibility = visibility.r / 9.0;", + "vVisibility *= 1.0 - visibility.g / 9.0;", + "vVisibility *= visibility.b / 9.0;", + "vVisibility *= 1.0 - visibility.a / 9.0;", - for ( ix = 0; ix < gridX; ix++ ) { + "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", + "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", - var a = ix + gridX1 * iy; - var b = ix + gridX1 * ( iy + 1 ); - var c = ( ix + 1 ) + gridX1 * ( iy + 1 ); - var d = ( ix + 1 ) + gridX1 * iy; + "}", - var uva = new THREE.Vector2( ix / gridX, 1 - iy / gridY ); - var uvb = new THREE.Vector2( ix / gridX, 1 - ( iy + 1 ) / gridY ); - var uvc = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - ( iy + 1 ) / gridY ); - var uvd = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - iy / gridY ); + "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", - var face = new THREE.Face3( a + offset, b + offset, d + offset ); - face.normal.copy( normal ); - face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() ); - face.materialIndex = materialIndex; + "}" - scope.faces.push( face ); - scope.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); + ].join( "\n" ), - face = new THREE.Face3( b + offset, c + offset, d + offset ); - face.normal.copy( normal ); - face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() ); - face.materialIndex = materialIndex; + fragmentShader: [ - scope.faces.push( face ); - scope.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); + "uniform lowp int renderType;", - } + "uniform sampler2D map;", + "uniform float opacity;", + "uniform vec3 color;", - } + "varying vec2 vUV;", + "varying float vVisibility;", - } + "void main() {", - this.computeCentroids(); - this.mergeVertices(); + // pink square -}; + "if ( renderType == 0 ) {", -THREE.CubeGeometry.prototype = Object.create( THREE.Geometry.prototype ); + "gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );", -/** - * @author mrdoob / http://mrdoob.com/ - */ + // restore -THREE.CylinderGeometry = function ( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded ) { + "} else if ( renderType == 1 ) {", - THREE.Geometry.call( this ); + "gl_FragColor = texture2D( map, vUV );", - this.radiusTop = radiusTop = radiusTop !== undefined ? radiusTop : 20; - this.radiusBottom = radiusBottom = radiusBottom !== undefined ? radiusBottom : 20; - this.height = height = height !== undefined ? height : 100; + // flare - this.radialSegments = radialSegments = radialSegments || 8; - this.heightSegments = heightSegments = heightSegments || 1; + "} else {", - this.openEnded = openEnded = openEnded !== undefined ? openEnded : false; + "vec4 texture = texture2D( map, vUV );", + "texture.a *= opacity * vVisibility;", + "gl_FragColor = texture;", + "gl_FragColor.rgb *= color;", - var heightHalf = height / 2; + "}", - var x, y, vertices = [], uvs = []; + "}" - for ( y = 0; y <= heightSegments; y ++ ) { + ].join( "\n" ) - var verticesRow = []; - var uvsRow = []; + }; - var v = y / heightSegments; - var radius = v * ( radiusBottom - radiusTop ) + radiusTop; + program = createProgram( shader ); - for ( x = 0; x <= radialSegments; x ++ ) { + attributes = { + vertex: gl.getAttribLocation ( program, "position" ), + uv: gl.getAttribLocation ( program, "uv" ) + }; - var u = x / radialSegments; + 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" ) + }; - var vertex = new THREE.Vector3(); - vertex.x = radius * Math.sin( u * Math.PI * 2 ); - vertex.y = - v * height + heightHalf; - vertex.z = radius * Math.cos( u * Math.PI * 2 ); + } - this.vertices.push( vertex ); + /* + * Render lens flares + * Method: renders 16x16 0xff00ff-colored points scattered over the light source area, + * reads these back and calculates occlusion. + */ - verticesRow.push( this.vertices.length - 1 ); - uvsRow.push( new THREE.Vector2( u, 1 - v ) ); + this.render = function ( scene, camera, viewport ) { - } + if ( flares.length === 0 ) return; - vertices.push( verticesRow ); - uvs.push( uvsRow ); + var tempPosition = new THREE.Vector3(); - } + var invAspect = viewport.w / viewport.z, + halfViewportWidth = viewport.z * 0.5, + halfViewportHeight = viewport.w * 0.5; - var tanTheta = ( radiusBottom - radiusTop ) / height; - var na, nb; + var size = 16 / viewport.w, + scale = new THREE.Vector2( size * invAspect, size ); - for ( x = 0; x < radialSegments; x ++ ) { + var screenPosition = new THREE.Vector3( 1, 1, 0 ), + screenPositionPixels = new THREE.Vector2( 1, 1 ); - if ( radiusTop !== 0 ) { + var validArea = new THREE.Box2(); - na = this.vertices[ vertices[ 0 ][ x ] ].clone(); - nb = this.vertices[ vertices[ 0 ][ x + 1 ] ].clone(); + validArea.min.set( 0, 0 ); + validArea.max.set( viewport.z - 16, viewport.w - 16 ); - } else { + if ( program === undefined ) { - na = this.vertices[ vertices[ 1 ][ x ] ].clone(); - nb = this.vertices[ vertices[ 1 ][ x + 1 ] ].clone(); + init(); } - na.setY( Math.sqrt( na.x * na.x + na.z * na.z ) * tanTheta ).normalize(); - nb.setY( Math.sqrt( nb.x * nb.x + nb.z * nb.z ) * tanTheta ).normalize(); + gl.useProgram( program ); - for ( y = 0; y < heightSegments; y ++ ) { + state.initAttributes(); + state.enableAttribute( attributes.vertex ); + state.enableAttribute( attributes.uv ); + state.disableUnusedAttributes(); - var v1 = vertices[ y ][ x ]; - var v2 = vertices[ y + 1 ][ x ]; - var v3 = vertices[ y + 1 ][ x + 1 ]; - var v4 = vertices[ y ][ x + 1 ]; - - var n1 = na.clone(); - var n2 = na.clone(); - var n3 = nb.clone(); - var n4 = nb.clone(); + // loop through all lens flares to update their occlusion and positions + // setup gl and common used attribs/uniforms - var uv1 = uvs[ y ][ x ].clone(); - var uv2 = uvs[ y + 1 ][ x ].clone(); - var uv3 = uvs[ y + 1 ][ x + 1 ].clone(); - var uv4 = uvs[ y ][ x + 1 ].clone(); + gl.uniform1i( uniforms.occlusionMap, 0 ); + gl.uniform1i( uniforms.map, 1 ); - this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) ); - this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] ); + 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 ); - this.faces.push( new THREE.Face3( v2, v3, v4, [ n2, n3, n4 ] ) ); - this.faceVertexUvs[ 0 ].push( [ uv2, uv3, uv4 ] ); + gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); - } + state.disable( gl.CULL_FACE ); + state.setDepthWrite( false ); - } + for ( var i = 0, l = flares.length; i < l; i ++ ) { - // top cap + size = 16 / viewport.w; + scale.set( size * invAspect, size ); - if ( openEnded === false && radiusTop > 0 ) { + // calc object screen position - this.vertices.push( new THREE.Vector3( 0, heightHalf, 0 ) ); + var flare = flares[ i ]; - for ( x = 0; x < radialSegments; x ++ ) { + tempPosition.set( flare.matrixWorld.elements[ 12 ], flare.matrixWorld.elements[ 13 ], flare.matrixWorld.elements[ 14 ] ); - var v1 = vertices[ 0 ][ x ]; - var v2 = vertices[ 0 ][ x + 1 ]; - var v3 = this.vertices.length - 1; + tempPosition.applyMatrix4( camera.matrixWorldInverse ); + tempPosition.applyProjection( camera.projectionMatrix ); - var n1 = new THREE.Vector3( 0, 1, 0 ); - var n2 = new THREE.Vector3( 0, 1, 0 ); - var n3 = new THREE.Vector3( 0, 1, 0 ); + // setup arrays for gl programs - var uv1 = uvs[ 0 ][ x ].clone(); - var uv2 = uvs[ 0 ][ x + 1 ].clone(); - var uv3 = new THREE.Vector2( uv2.u, 0 ); + screenPosition.copy( tempPosition ); - this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) ); - this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] ); + // horizontal and vertical coordinate of the lower left corner of the pixels to copy - } + screenPositionPixels.x = viewport.x + ( screenPosition.x * halfViewportWidth ) + halfViewportWidth - 8; + screenPositionPixels.y = viewport.y + ( screenPosition.y * halfViewportHeight ) + halfViewportHeight - 8; - } + // screen cull - // bottom cap + if ( validArea.containsPoint( screenPositionPixels ) === true ) { - if ( openEnded === false && radiusBottom > 0 ) { + // save current RGB to temp texture - this.vertices.push( new THREE.Vector3( 0, - heightHalf, 0 ) ); + 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, screenPositionPixels.y, 16, 16, 0 ); - for ( x = 0; x < radialSegments; x ++ ) { - var v1 = vertices[ y ][ x + 1 ]; - var v2 = vertices[ y ][ x ]; - var v3 = this.vertices.length - 1; + // render pink quad - var n1 = new THREE.Vector3( 0, - 1, 0 ); - var n2 = new THREE.Vector3( 0, - 1, 0 ); - var n3 = new THREE.Vector3( 0, - 1, 0 ); + gl.uniform1i( uniforms.renderType, 0 ); + gl.uniform2f( uniforms.scale, scale.x, scale.y ); + gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); - var uv1 = uvs[ y ][ x + 1 ].clone(); - var uv2 = uvs[ y ][ x ].clone(); - var uv3 = new THREE.Vector2( uv2.u, 1 ); + state.disable( gl.BLEND ); + state.enable( gl.DEPTH_TEST ); - this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) ); - this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] ); + gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - } - } + // copy result to occlusionMap - this.computeCentroids(); - this.computeFaceNormals(); + state.activeTexture( gl.TEXTURE0 ); + state.bindTexture( gl.TEXTURE_2D, occlusionTexture ); + gl.copyTexImage2D( gl.TEXTURE_2D, 0, gl.RGBA, screenPositionPixels.x, screenPositionPixels.y, 16, 16, 0 ); -} -THREE.CylinderGeometry.prototype = Object.create( THREE.Geometry.prototype ); + // restore graphics -/** - * @author zz85 / http://www.lab4games.net/zz85/blog - * - * Creates extruded geometry from a path shape. - * - * parameters = { - * - * size: , // size of the text - * height: , // thickness to extrude text - * curveSegments: , // number of points on the curves - * steps: , // number of points for z-side extrusions / used for subdividing segements of extrude spline too - * amount: , // Amount - * - * bevelEnabled: , // turn on bevel - * bevelThickness: , // how deep into text bevel goes - * bevelSize: , // how far from text outline is bevel - * bevelSegments: , // number of bevel layers - * - * extrudePath: // 3d spline path to extrude shape along. (creates Frames if .frames aren't defined) - * frames: // containing arrays of tangents, normals, binormals - * - * material: // material index for front and back faces - * extrudeMaterial: // material index for extrusion and beveled faces - * uvGenerator: // object that provides UV generator functions - * - * } - **/ + gl.uniform1i( uniforms.renderType, 1 ); + state.disable( gl.DEPTH_TEST ); -THREE.ExtrudeGeometry = function ( shapes, options ) { + state.activeTexture( gl.TEXTURE1 ); + state.bindTexture( gl.TEXTURE_2D, tempTexture ); + gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - if ( typeof( shapes ) === "undefined" ) { - shapes = []; - return; - } - THREE.Geometry.call( this ); + // update object positions - shapes = shapes instanceof Array ? shapes : [ shapes ]; + flare.positionScreen.copy( screenPosition ); - this.shapebb = shapes[ shapes.length - 1 ].getBoundingBox(); + if ( flare.customUpdateCallback ) { - this.addShapeList( shapes, options ); + flare.customUpdateCallback( flare ); - this.computeCentroids(); - this.computeFaceNormals(); + } else { - // can't really use automatic vertex normals - // as then front and back sides get smoothed too - // should do separate smoothing just for sides + flare.updateLensFlares(); - //this.computeVertexNormals(); + } - //console.log( "took", ( Date.now() - startTime ) ); + // render flares -}; + gl.uniform1i( uniforms.renderType, 2 ); + state.enable( gl.BLEND ); -THREE.ExtrudeGeometry.prototype = Object.create( THREE.Geometry.prototype ); + for ( var j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) { -THREE.ExtrudeGeometry.prototype.addShapeList = function ( shapes, options ) { - var sl = shapes.length; + var sprite = flare.lensFlares[ j ]; - for ( var s = 0; s < sl; s ++ ) { - var shape = shapes[ s ]; - this.addShape( shape, options ); - } -}; + if ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) { -THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { + screenPosition.x = sprite.x; + screenPosition.y = sprite.y; + screenPosition.z = sprite.z; - var amount = options.amount !== undefined ? options.amount : 100; + size = sprite.size * sprite.scale / viewport.w; - var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10 - var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8 - var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; + scale.x = size * invAspect; + scale.y = size; - var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false + 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 curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; + gl.uniform1f( uniforms.opacity, sprite.opacity ); + gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b ); - var steps = options.steps !== undefined ? options.steps : 1; + state.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst ); + renderer.setTexture2D( sprite.texture, 1 ); - var extrudePath = options.extrudePath; - var extrudePts, extrudeByPath = false; + gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - var material = options.material; - var extrudeMaterial = options.extrudeMaterial; + } - // Use default WorldUVGenerator if no UV generators are specified. - var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : THREE.ExtrudeGeometry.WorldUVGenerator; + } - var shapebb = this.shapebb; - //shapebb = shape.getBoundingBox(); + } + } + // restore gl - var splineTube, binormal, normal, position2; - if ( extrudePath ) { + state.enable( gl.CULL_FACE ); + state.enable( gl.DEPTH_TEST ); + state.setDepthWrite( true ); - extrudePts = extrudePath.getSpacedPoints( steps ); + renderer.resetGLState(); - extrudeByPath = true; - bevelEnabled = false; // bevels not supported for path extrusion + }; - // SETUP TNB variables + function createProgram ( shader ) { - // Reuse TNB from TubeGeomtry for now. - // TODO1 - have a .isClosed in spline? + var program = gl.createProgram(); - splineTube = options.frames !== undefined ? options.frames : new THREE.TubeGeometry.FrenetFrames(extrudePath, steps, false); + var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER ); + var vertexShader = gl.createShader( gl.VERTEX_SHADER ); - // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); + var prefix = "precision " + renderer.getPrecision() + " float;\n"; - binormal = new THREE.Vector3(); - normal = new THREE.Vector3(); - position2 = new THREE.Vector3(); + gl.shaderSource( fragmentShader, prefix + shader.fragmentShader ); + gl.shaderSource( vertexShader, prefix + shader.vertexShader ); - } + gl.compileShader( fragmentShader ); + gl.compileShader( vertexShader ); - // Safeguards if bevels are not enabled + gl.attachShader( program, fragmentShader ); + gl.attachShader( program, vertexShader ); - if ( ! bevelEnabled ) { + gl.linkProgram( program ); - bevelSegments = 0; - bevelThickness = 0; - bevelSize = 0; + return program; } - // Variables initalization +}; - var ahole, h, hl; // looping of holes - var scope = this; - var bevelPoints = []; +// File:src/renderers/webgl/plugins/SpritePlugin.js - var shapesOffset = this.vertices.length; +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ - var shapePoints = shape.extractPoints( curveSegments ); +THREE.SpritePlugin = function ( renderer, sprites ) { - var vertices = shapePoints.shape; - var holes = shapePoints.holes; + var gl = renderer.context; + var state = renderer.state; - var reverse = !THREE.Shape.Utils.isClockWise( vertices ) ; + var vertexBuffer, elementBuffer; + var program, attributes, uniforms; - if ( reverse ) { + var texture; - vertices = vertices.reverse(); + // decompose matrixWorld - // Maybe we should also check if holes are in the opposite direction, just to be safe ... + var spritePosition = new THREE.Vector3(); + var spriteRotation = new THREE.Quaternion(); + var spriteScale = new THREE.Vector3(); - for ( h = 0, hl = holes.length; h < hl; h ++ ) { + function init() { - ahole = holes[ h ]; + var vertices = new Float32Array( [ + - 0.5, - 0.5, 0, 0, + 0.5, - 0.5, 1, 0, + 0.5, 0.5, 1, 1, + - 0.5, 0.5, 0, 1 + ] ); - if ( THREE.Shape.Utils.isClockWise( ahole ) ) { + var faces = new Uint16Array( [ + 0, 1, 2, + 0, 2, 3 + ] ); - holes[ h ] = ahole.reverse(); + 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 ); - reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)! + program = createProgram(); - } + attributes = { + position: gl.getAttribLocation ( program, 'position' ), + uv: gl.getAttribLocation ( program, 'uv' ) + }; + uniforms = { + uvOffset: gl.getUniformLocation( program, 'uvOffset' ), + uvScale: gl.getUniformLocation( program, 'uvScale' ), - var faces = THREE.Shape.Utils.triangulateShape ( vertices, holes ); + rotation: gl.getUniformLocation( program, 'rotation' ), + scale: gl.getUniformLocation( program, 'scale' ), - /* Vertices */ + color: gl.getUniformLocation( program, 'color' ), + map: gl.getUniformLocation( program, 'map' ), + opacity: gl.getUniformLocation( program, 'opacity' ), - var contour = vertices; // vertices has all points but contour has only points of circumference + modelViewMatrix: gl.getUniformLocation( program, 'modelViewMatrix' ), + projectionMatrix: gl.getUniformLocation( program, 'projectionMatrix' ), - for ( h = 0, hl = holes.length; h < hl; h ++ ) { + 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' ), - ahole = holes[ h ]; + alphaTest: gl.getUniformLocation( program, 'alphaTest' ) + }; - vertices = vertices.concat( ahole ); + var canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); + canvas.width = 8; + canvas.height = 8; + + var context = canvas.getContext( '2d' ); + context.fillStyle = 'white'; + context.fillRect( 0, 0, 8, 8 ); + + texture = new THREE.Texture( canvas ); + texture.needsUpdate = true; } + this.render = function ( scene, camera ) { - function scalePt2 ( pt, vec, size ) { + if ( sprites.length === 0 ) return; - if ( !vec ) console.log( "die" ); + // setup gl - return vec.clone().multiplyScalar( size ).add( pt ); + if ( program === undefined ) { - } + init(); - var b, bs, t, z, - vert, vlen = vertices.length, - face, flen = faces.length, - cont, clen = contour.length; + } + gl.useProgram( program ); - // Find directions for point movement + state.initAttributes(); + state.enableAttribute( attributes.position ); + state.enableAttribute( attributes.uv ); + state.disableUnusedAttributes(); - var RAD_TO_DEGREES = 180 / Math.PI; + 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 getBevelVec( pt_i, pt_j, pt_k ) { + gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer ); - // Algorithm 2 + gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); - return getBevelVec2( pt_i, pt_j, pt_k ); + state.activeTexture( gl.TEXTURE0 ); + gl.uniform1i( uniforms.map, 0 ); - } + var oldFogType = 0; + var sceneFogType = 0; + var fog = scene.fog; - function getBevelVec1( pt_i, pt_j, pt_k ) { + if ( fog ) { - var anglea = Math.atan2( pt_j.y - pt_i.y, pt_j.x - pt_i.x ); - var angleb = Math.atan2( pt_k.y - pt_i.y, pt_k.x - pt_i.x ); + gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b ); - if ( anglea > angleb ) { + if ( fog instanceof THREE.Fog ) { - angleb += Math.PI * 2; + gl.uniform1f( uniforms.fogNear, fog.near ); + gl.uniform1f( uniforms.fogFar, fog.far ); - } + gl.uniform1i( uniforms.fogType, 1 ); + oldFogType = 1; + sceneFogType = 1; + + } else if ( fog instanceof THREE.FogExp2 ) { + + gl.uniform1f( uniforms.fogDensity, fog.density ); - var anglec = ( anglea + angleb ) / 2; + gl.uniform1i( uniforms.fogType, 2 ); + oldFogType = 2; + sceneFogType = 2; + } - //console.log('angle1', anglea * RAD_TO_DEGREES,'angle2', angleb * RAD_TO_DEGREES, 'anglec', anglec *RAD_TO_DEGREES); + } else { - var x = - Math.cos( anglec ); - var y = - Math.sin( anglec ); + gl.uniform1i( uniforms.fogType, 0 ); + oldFogType = 0; + sceneFogType = 0; - var vec = new THREE.Vector2( x, y ); //.normalize(); + } - return vec; - } + // update positions and sort - function getBevelVec2( pt_i, pt_j, pt_k ) { + for ( var i = 0, l = sprites.length; i < l; i ++ ) { - var a = THREE.ExtrudeGeometry.__v1, - b = THREE.ExtrudeGeometry.__v2, - v_hat = THREE.ExtrudeGeometry.__v3, - w_hat = THREE.ExtrudeGeometry.__v4, - p = THREE.ExtrudeGeometry.__v5, - q = THREE.ExtrudeGeometry.__v6, - v, w, - v_dot_w_hat, q_sub_p_dot_w_hat, - s, intersection; + var sprite = sprites[ i ]; - // good reading for line-line intersection - // http://sputsoft.com/blog/2010/03/line-line-intersection.html + sprite.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld ); + sprite.z = - sprite.modelViewMatrix.elements[ 14 ]; - // define a as vector j->i - // define b as vectot k->i + } - a.set( pt_i.x - pt_j.x, pt_i.y - pt_j.y ); - b.set( pt_i.x - pt_k.x, pt_i.y - pt_k.y ); + sprites.sort( painterSortStable ); - // get unit vectors + // render all sprites - v = a.normalize(); - w = b.normalize(); + var scale = []; - // normals from pt i + for ( var i = 0, l = sprites.length; i < l; i ++ ) { - v_hat.set( -v.y, v.x ); - w_hat.set( w.y, -w.x ); + var sprite = sprites[ i ]; + var material = sprite.material; - // pts from i + if ( material.visible === false ) continue; - p.copy( pt_i ).add( v_hat ); - q.copy( pt_i ).add( w_hat ); + gl.uniform1f( uniforms.alphaTest, material.alphaTest ); + gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite.modelViewMatrix.elements ); - if ( p.equals( q ) ) { + sprite.matrixWorld.decompose( spritePosition, spriteRotation, spriteScale ); - //console.log("Warning: lines are straight"); - return w_hat.clone(); + scale[ 0 ] = spriteScale.x; + scale[ 1 ] = spriteScale.y; - } + var fogType = 0; - // Points from j, k. helps prevents points cross overover most of the time + if ( scene.fog && material.fog ) { - p.copy( pt_j ).add( v_hat ); - q.copy( pt_k ).add( w_hat ); + fogType = sceneFogType; - v_dot_w_hat = v.dot( w_hat ); - q_sub_p_dot_w_hat = q.sub( p ).dot( w_hat ); + } - // We should not reach these conditions + if ( oldFogType !== fogType ) { - if ( v_dot_w_hat === 0 ) { + gl.uniform1i( uniforms.fogType, fogType ); + oldFogType = fogType; - console.log( "Either infinite or no solutions!" ); + } - if ( q_sub_p_dot_w_hat === 0 ) { + if ( material.map !== null ) { - console.log( "Its finite solutions." ); + gl.uniform2f( uniforms.uvOffset, material.map.offset.x, material.map.offset.y ); + gl.uniform2f( uniforms.uvScale, material.map.repeat.x, material.map.repeat.y ); } else { - console.log( "Too bad, no solutions." ); + 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 ); - s = q_sub_p_dot_w_hat / v_dot_w_hat; + gl.uniform1f( uniforms.rotation, material.rotation ); + gl.uniform2fv( uniforms.scale, scale ); - if ( s < 0 ) { + state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); + state.setDepthTest( material.depthTest ); + state.setDepthWrite( material.depthWrite ); - // in case of emergecy, revert to algorithm 1. + if ( material.map ) { - return getBevelVec1( pt_i, pt_j, pt_k ); + renderer.setTexture2D( material.map, 0 ); - } + } else { - intersection = v.multiplyScalar( s ).add( p ); + renderer.setTexture2D( texture, 0 ); - return intersection.sub( pt_i ).clone(); // Don't normalize!, otherwise sharp corners become ugly + } + + gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); + + } + + // restore gl + + state.enable( gl.CULL_FACE ); + + renderer.resetGLState(); + + }; + + function createProgram () { + + var program = gl.createProgram(); + + var vertexShader = gl.createShader( gl.VERTEX_SHADER ); + var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER ); + + gl.shaderSource( vertexShader, [ + + 'precision ' + renderer.getPrecision() + ' float;', + + 'uniform mat4 modelViewMatrix;', + 'uniform mat4 projectionMatrix;', + 'uniform float rotation;', + 'uniform vec2 scale;', + 'uniform vec2 uvOffset;', + 'uniform vec2 uvScale;', + + 'attribute vec2 position;', + 'attribute vec2 uv;', + + 'varying vec2 vUV;', + + 'void main() {', + + 'vUV = uvOffset + uv * uvScale;', + + 'vec2 alignedPosition = position * scale;', + + 'vec2 rotatedPosition;', + 'rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;', + 'rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;', + + 'vec4 finalPosition;', + + 'finalPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );', + 'finalPosition.xy += rotatedPosition;', + 'finalPosition = projectionMatrix * finalPosition;', + + 'gl_Position = finalPosition;', + + '}' + + ].join( '\n' ) ); + + gl.shaderSource( fragmentShader, [ + + 'precision ' + renderer.getPrecision() + ' float;', + + 'uniform vec3 color;', + 'uniform sampler2D map;', + 'uniform float opacity;', + + 'uniform int fogType;', + 'uniform vec3 fogColor;', + 'uniform float fogDensity;', + 'uniform float fogNear;', + 'uniform float fogFar;', + 'uniform float alphaTest;', + + 'varying vec2 vUV;', + + 'void main() {', + + 'vec4 texture = texture2D( map, vUV );', + + 'if ( texture.a < alphaTest ) discard;', + + 'gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );', + + 'if ( fogType > 0 ) {', + + 'float depth = gl_FragCoord.z / gl_FragCoord.w;', + 'float fogFactor = 0.0;', + + 'if ( fogType == 1 ) {', + + 'fogFactor = smoothstep( fogNear, fogFar, depth );', + + '} else {', + + 'const float LOG2 = 1.442695;', + 'fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );', + 'fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );', + + '}', + + 'gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );', + + '}', + + '}' + + ].join( '\n' ) ); + + gl.compileShader( vertexShader ); + gl.compileShader( fragmentShader ); + + gl.attachShader( program, vertexShader ); + gl.attachShader( program, fragmentShader ); + + gl.linkProgram( program ); + + return program; + + } + + function painterSortStable ( a, b ) { + + if ( a.renderOrder !== b.renderOrder ) { + + return a.renderOrder - b.renderOrder; + + } else if ( a.z !== b.z ) { + + return b.z - a.z; + + } else { + + return b.id - a.id; + + } + + } + +}; + +// File:src/Three.Legacy.js + +/** + * @author mrdoob / http://mrdoob.com/ + */ + +Object.assign( THREE, { + Face4: function ( a, b, c, d, normal, color, materialIndex ) { + console.warn( 'THREE.Face4 has been removed. A THREE.Face3 will be created instead.' ); + return new THREE.Face3( a, b, c, normal, color, materialIndex ); + }, + LineStrip: 0, + LinePieces: 1, + MeshFaceMaterial: THREE.MultiMaterial, + PointCloud: function ( geometry, material ) { + console.warn( 'THREE.PointCloud has been renamed to THREE.Points.' ); + return new THREE.Points( geometry, material ); + }, + Particle: THREE.Sprite, + ParticleSystem: function ( geometry, material ) { + console.warn( 'THREE.ParticleSystem has been renamed to THREE.Points.' ); + return new THREE.Points( geometry, material ); + }, + PointCloudMaterial: function ( parameters ) { + console.warn( 'THREE.PointCloudMaterial has been renamed to THREE.PointsMaterial.' ); + return new THREE.PointsMaterial( parameters ); + }, + ParticleBasicMaterial: function ( parameters ) { + console.warn( 'THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial.' ); + return new THREE.PointsMaterial( parameters ); + }, + ParticleSystemMaterial: function ( parameters ) { + console.warn( 'THREE.ParticleSystemMaterial has been renamed to THREE.PointsMaterial.' ); + return new THREE.PointsMaterial( parameters ); + }, + Vertex: function ( x, y, z ) { + console.warn( 'THREE.Vertex has been removed. Use THREE.Vector3 instead.' ); + return new THREE.Vector3( x, y, z ); + } +} ); + +// + +Object.assign( THREE.Box2.prototype, { + empty: function () { + console.warn( 'THREE.Box2: .empty() has been renamed to .isEmpty().' ); + return this.isEmpty(); + }, + isIntersectionBox: function ( box ) { + console.warn( 'THREE.Box2: .isIntersectionBox() has been renamed to .intersectsBox().' ); + return this.intersectsBox( box ); + } +} ); + +Object.assign( THREE.Box3.prototype, { + empty: function () { + console.warn( 'THREE.Box3: .empty() has been renamed to .isEmpty().' ); + return this.isEmpty(); + }, + isIntersectionBox: function ( box ) { + console.warn( 'THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox().' ); + return this.intersectsBox( box ); + }, + isIntersectionSphere: function ( sphere ) { + console.warn( 'THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); + return this.intersectsSphere( sphere ); + } +} ); + +Object.assign( THREE.Matrix3.prototype, { + multiplyVector3: function ( vector ) { + console.warn( 'THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' ); + return vector.applyMatrix3( this ); + }, + multiplyVector3Array: function ( a ) { + console.warn( 'THREE.Matrix3: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.' ); + return this.applyToVector3Array( a ); + } +} ); + +Object.assign( THREE.Matrix4.prototype, { + extractPosition: function ( m ) { + console.warn( 'THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().' ); + return this.copyPosition( m ); + }, + setRotationFromQuaternion: function ( q ) { + console.warn( 'THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().' ); + return this.makeRotationFromQuaternion( q ); + }, + multiplyVector3: function ( vector ) { + console.warn( 'THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) or vector.applyProjection( matrix ) instead.' ); + return vector.applyProjection( this ); + }, + multiplyVector4: function ( vector ) { + console.warn( 'THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); + return vector.applyMatrix4( this ); + }, + multiplyVector3Array: function ( a ) { + console.warn( 'THREE.Matrix4: .multiplyVector3Array() has been renamed. Use matrix.applyToVector3Array( array ) instead.' ); + return this.applyToVector3Array( a ); + }, + rotateAxis: function ( v ) { + console.warn( 'THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' ); + v.transformDirection( this ); + }, + crossVector: function ( vector ) { + console.warn( 'THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' ); + return vector.applyMatrix4( this ); + }, + translate: function ( v ) { + console.error( 'THREE.Matrix4: .translate() has been removed.' ); + }, + rotateX: function ( angle ) { + console.error( 'THREE.Matrix4: .rotateX() has been removed.' ); + }, + rotateY: function ( angle ) { + console.error( 'THREE.Matrix4: .rotateY() has been removed.' ); + }, + rotateZ: function ( angle ) { + console.error( 'THREE.Matrix4: .rotateZ() has been removed.' ); + }, + rotateByAxis: function ( axis, angle ) { + console.error( 'THREE.Matrix4: .rotateByAxis() has been removed.' ); + } +} ); + +Object.assign( THREE.Plane.prototype, { + isIntersectionLine: function ( line ) { + console.warn( 'THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine().' ); + return this.intersectsLine( line ); + } +} ); + +Object.assign( THREE.Quaternion.prototype, { + multiplyVector3: function ( vector ) { + console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' ); + return vector.applyQuaternion( this ); + } +} ); + +Object.assign( THREE.Ray.prototype, { + isIntersectionBox: function ( box ) { + console.warn( 'THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox().' ); + return this.intersectsBox( box ); + }, + isIntersectionPlane: function ( plane ) { + console.warn( 'THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane().' ); + return this.intersectsPlane( plane ); + }, + isIntersectionSphere: function ( sphere ) { + console.warn( 'THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere().' ); + return this.intersectsSphere( sphere ); + } +} ); + +Object.assign( THREE.Vector3.prototype, { + setEulerFromRotationMatrix: function () { + console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' ); + }, + setEulerFromQuaternion: function () { + console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' ); + }, + getPositionFromMatrix: function ( m ) { + console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' ); + return this.setFromMatrixPosition( m ); + }, + getScaleFromMatrix: function ( m ) { + console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' ); + return this.setFromMatrixScale( m ); + }, + getColumnFromMatrix: function ( index, matrix ) { + console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' ); + return this.setFromMatrixColumn( matrix, index ); + } +} ); + +// + +Object.assign( THREE.Object3D.prototype, { + getChildByName: function ( name ) { + console.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' ); + return this.getObjectByName( name ); + }, + renderDepth: function ( value ) { + console.warn( 'THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.' ); + }, + translate: function ( distance, axis ) { + console.warn( 'THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.' ); + return this.translateOnAxis( axis, distance ); + } +} ); + +Object.defineProperties( THREE.Object3D.prototype, { + eulerOrder: { + get: function () { + console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); + return this.rotation.order; + }, + set: function ( value ) { + console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' ); + this.rotation.order = value; + } + }, + useQuaternion: { + get: function () { + console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); + }, + set: function ( value ) { + console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' ); + } + } +} ); + +Object.defineProperties( THREE.LOD.prototype, { + objects: { + get: function () { + console.warn( 'THREE.LOD: .objects has been renamed to .levels.' ); + return this.levels; + } + } +} ); + +// + +THREE.PerspectiveCamera.prototype.setLens = function ( focalLength, filmGauge ) { + + console.warn( "THREE.PerspectiveCamera.setLens is deprecated. " + + "Use .setFocalLength and .filmGauge for a photographic setup." ); + + if ( filmGauge !== undefined ) this.filmGauge = filmGauge; + this.setFocalLength( focalLength ); + +}; + +// + +Object.defineProperties( THREE.Light.prototype, { + onlyShadow: { + set: function ( value ) { + console.warn( 'THREE.Light: .onlyShadow has been removed.' ); + } + }, + shadowCameraFov: { + set: function ( value ) { + console.warn( 'THREE.Light: .shadowCameraFov is now .shadow.camera.fov.' ); + this.shadow.camera.fov = value; + } + }, + shadowCameraLeft: { + set: function ( value ) { + console.warn( 'THREE.Light: .shadowCameraLeft is now .shadow.camera.left.' ); + this.shadow.camera.left = value; + } + }, + shadowCameraRight: { + set: function ( value ) { + console.warn( 'THREE.Light: .shadowCameraRight is now .shadow.camera.right.' ); + this.shadow.camera.right = value; + } + }, + shadowCameraTop: { + set: function ( value ) { + console.warn( 'THREE.Light: .shadowCameraTop is now .shadow.camera.top.' ); + this.shadow.camera.top = value; + } + }, + shadowCameraBottom: { + set: function ( value ) { + console.warn( 'THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom.' ); + this.shadow.camera.bottom = value; + } + }, + shadowCameraNear: { + set: function ( value ) { + console.warn( 'THREE.Light: .shadowCameraNear is now .shadow.camera.near.' ); + this.shadow.camera.near = value; + } + }, + shadowCameraFar: { + set: function ( value ) { + console.warn( 'THREE.Light: .shadowCameraFar is now .shadow.camera.far.' ); + this.shadow.camera.far = value; + } + }, + shadowCameraVisible: { + set: function ( value ) { + console.warn( 'THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead.' ); + } + }, + shadowBias: { + set: function ( value ) { + console.warn( 'THREE.Light: .shadowBias is now .shadow.bias.' ); + this.shadow.bias = value; + } + }, + shadowDarkness: { + set: function ( value ) { + console.warn( 'THREE.Light: .shadowDarkness has been removed.' ); + } + }, + shadowMapWidth: { + set: function ( value ) { + console.warn( 'THREE.Light: .shadowMapWidth is now .shadow.mapSize.width.' ); + this.shadow.mapSize.width = value; + } + }, + shadowMapHeight: { + set: function ( value ) { + console.warn( 'THREE.Light: .shadowMapHeight is now .shadow.mapSize.height.' ); + this.shadow.mapSize.height = value; + } + } +} ); + +// + +Object.defineProperties( THREE.BufferAttribute.prototype, { + length: { + get: function () { + console.warn( 'THREE.BufferAttribute: .length has been deprecated. Please use .count.' ); + return this.array.length; + } + } +} ); + +Object.assign( THREE.BufferGeometry.prototype, { + addIndex: function ( index ) { + console.warn( 'THREE.BufferGeometry: .addIndex() has been renamed to .setIndex().' ); + this.setIndex( index ); + }, + addDrawCall: function ( start, count, indexOffset ) { + if ( indexOffset !== undefined ) { + console.warn( 'THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset.' ); + } + console.warn( 'THREE.BufferGeometry: .addDrawCall() is now .addGroup().' ); + this.addGroup( start, count ); + }, + clearDrawCalls: function () { + console.warn( 'THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups().' ); + this.clearGroups(); + }, + computeTangents: function () { + console.warn( 'THREE.BufferGeometry: .computeTangents() has been removed.' ); + }, + computeOffsets: function () { + console.warn( 'THREE.BufferGeometry: .computeOffsets() has been removed.' ); + } +} ); + +Object.defineProperties( THREE.BufferGeometry.prototype, { + drawcalls: { + get: function () { + console.error( 'THREE.BufferGeometry: .drawcalls has been renamed to .groups.' ); + return this.groups; + } + }, + offsets: { + get: function () { + console.warn( 'THREE.BufferGeometry: .offsets has been renamed to .groups.' ); + return this.groups; + } + } +} ); + +// + +Object.defineProperties( THREE.Material.prototype, { + wrapAround: { + get: function () { + console.warn( 'THREE.' + this.type + ': .wrapAround has been removed.' ); + }, + set: function ( value ) { + console.warn( 'THREE.' + this.type + ': .wrapAround has been removed.' ); + } + }, + wrapRGB: { + get: function () { + console.warn( 'THREE.' + this.type + ': .wrapRGB has been removed.' ); + return new THREE.Color(); + } + } +} ); + +Object.defineProperties( THREE.MeshPhongMaterial.prototype, { + metal: { + get: function () { + console.warn( 'THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead.' ); + return false; + }, + set: function ( value ) { + console.warn( 'THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead' ); + } + } +} ); + +Object.defineProperties( THREE.ShaderMaterial.prototype, { + derivatives: { + get: function () { + console.warn( 'THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' ); + return this.extensions.derivatives; + }, + set: function ( value ) { + console.warn( 'THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' ); + this.extensions.derivatives = value; + } + } +} ); + +// + +THREE.EventDispatcher.prototype = Object.assign( Object.create( { + + // Note: Extra base ensures these properties are not 'assign'ed. + + constructor: THREE.EventDispatcher, + + apply: function ( target ) { + + console.warn( "THREE.EventDispatcher: .apply is deprecated, " + + "just inherit or Object.assign the prototype to mix-in." ); + + Object.assign( target, this ); + + } + +} ), THREE.EventDispatcher.prototype ); + +// + +Object.assign( THREE.WebGLRenderer.prototype, { + supportsFloatTextures: function () { + console.warn( 'THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( \'OES_texture_float\' ).' ); + return this.extensions.get( 'OES_texture_float' ); + }, + supportsHalfFloatTextures: function () { + console.warn( 'THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( \'OES_texture_half_float\' ).' ); + return this.extensions.get( 'OES_texture_half_float' ); + }, + supportsStandardDerivatives: function () { + console.warn( 'THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( \'OES_standard_derivatives\' ).' ); + return this.extensions.get( 'OES_standard_derivatives' ); + }, + supportsCompressedTextureS3TC: function () { + console.warn( 'THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( \'WEBGL_compressed_texture_s3tc\' ).' ); + return this.extensions.get( 'WEBGL_compressed_texture_s3tc' ); + }, + supportsCompressedTexturePVRTC: function () { + console.warn( 'THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( \'WEBGL_compressed_texture_pvrtc\' ).' ); + return this.extensions.get( 'WEBGL_compressed_texture_pvrtc' ); + }, + supportsBlendMinMax: function () { + console.warn( 'THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( \'EXT_blend_minmax\' ).' ); + return this.extensions.get( 'EXT_blend_minmax' ); + }, + supportsVertexTextures: function () { + return this.capabilities.vertexTextures; + }, + supportsInstancedArrays: function () { + console.warn( 'THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( \'ANGLE_instanced_arrays\' ).' ); + return this.extensions.get( 'ANGLE_instanced_arrays' ); + }, + enableScissorTest: function ( boolean ) { + console.warn( 'THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest().' ); + this.setScissorTest( boolean ); + }, + initMaterial: function () { + console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' ); + }, + addPrePlugin: function () { + console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' ); + }, + addPostPlugin: function () { + console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' ); + }, + updateShadowMap: function () { + console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' ); + } +} ); + +Object.defineProperties( THREE.WebGLRenderer.prototype, { + shadowMapEnabled: { + get: function () { + return this.shadowMap.enabled; + }, + set: function ( value ) { + console.warn( 'THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled.' ); + this.shadowMap.enabled = value; + } + }, + shadowMapType: { + get: function () { + return this.shadowMap.type; + }, + set: function ( value ) { + console.warn( 'THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type.' ); + this.shadowMap.type = value; + } + }, + shadowMapCullFace: { + get: function () { + return this.shadowMap.cullFace; + }, + set: function ( value ) { + console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace is now .shadowMap.cullFace.' ); + this.shadowMap.cullFace = value; + } + } +} ); + +Object.defineProperties( THREE.WebGLShadowMap.prototype, { + cullFace: { + get: function () { + return this.renderReverseSided ? THREE.CullFaceFront : THREE.CullFaceBack; + }, + set: function ( cullFace ) { + var value = ( cullFace !== THREE.CullFaceBack ); + console.warn( "WebGLRenderer: .shadowMap.cullFace is deprecated. Set .shadowMap.renderReverseSided to " + value + "." ); + this.renderReverseSided = value; + } + } +} ); + +// + +Object.defineProperties( THREE.WebGLRenderTarget.prototype, { + wrapS: { + get: function () { + console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); + return this.texture.wrapS; + }, + set: function ( value ) { + console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' ); + this.texture.wrapS = value; + } + }, + wrapT: { + get: function () { + console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); + return this.texture.wrapT; + }, + set: function ( value ) { + console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' ); + this.texture.wrapT = value; + } + }, + magFilter: { + get: function () { + console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); + return this.texture.magFilter; + }, + set: function ( value ) { + console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' ); + this.texture.magFilter = value; + } + }, + minFilter: { + get: function () { + console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); + return this.texture.minFilter; + }, + set: function ( value ) { + console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' ); + this.texture.minFilter = value; + } + }, + anisotropy: { + get: function () { + console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); + return this.texture.anisotropy; + }, + set: function ( value ) { + console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' ); + this.texture.anisotropy = value; + } + }, + offset: { + get: function () { + console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); + return this.texture.offset; + }, + set: function ( value ) { + console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' ); + this.texture.offset = value; + } + }, + repeat: { + get: function () { + console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); + return this.texture.repeat; + }, + set: function ( value ) { + console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' ); + this.texture.repeat = value; + } + }, + format: { + get: function () { + console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); + return this.texture.format; + }, + set: function ( value ) { + console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' ); + this.texture.format = value; + } + }, + type: { + get: function () { + console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); + return this.texture.type; + }, + set: function ( value ) { + console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' ); + this.texture.type = value; + } + }, + generateMipmaps: { + get: function () { + console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); + return this.texture.generateMipmaps; + }, + set: function ( value ) { + console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' ); + this.texture.generateMipmaps = value; + } + } +} ); + +// + +Object.assign( THREE.Audio.prototype, { + load: function ( file ) { + console.warn( 'THREE.Audio: .load has been deprecated. Please use THREE.AudioLoader.' ); + var scope = this; + var audioLoader = new THREE.AudioLoader(); + audioLoader.load( file, function ( buffer ) { + scope.setBuffer( buffer ); + } ); + return this; + } +} ); + +Object.assign( THREE.AudioAnalyser.prototype, { + getData: function ( file ) { + console.warn( 'THREE.AudioAnalyser: .getData() is now .getFrequencyData().' ); + return this.getFrequencyData(); + } +} ); + +// + +THREE.GeometryUtils = { + + merge: function ( geometry1, geometry2, materialIndexOffset ) { + + console.warn( 'THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.' ); + + var matrix; + + if ( geometry2 instanceof THREE.Mesh ) { + + geometry2.matrixAutoUpdate && geometry2.updateMatrix(); + + matrix = geometry2.matrix; + geometry2 = geometry2.geometry; + + } + + geometry1.merge( geometry2, matrix, materialIndexOffset ); + + }, + + center: function ( geometry ) { + + console.warn( 'THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.' ); + return geometry.center(); + + } + +}; + +THREE.ImageUtils = { + + crossOrigin: undefined, + + loadTexture: function ( url, mapping, onLoad, onError ) { + + console.warn( 'THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead.' ); + + var loader = new THREE.TextureLoader(); + loader.setCrossOrigin( this.crossOrigin ); + + var texture = loader.load( url, onLoad, undefined, onError ); + + if ( mapping ) texture.mapping = mapping; + + return texture; + + }, + + loadTextureCube: function ( urls, mapping, onLoad, onError ) { + + console.warn( 'THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead.' ); + + var loader = new THREE.CubeTextureLoader(); + loader.setCrossOrigin( this.crossOrigin ); + + var texture = loader.load( urls, onLoad, undefined, onError ); + + if ( mapping ) texture.mapping = mapping; + + return texture; + + }, + + loadCompressedTexture: function () { + + console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' ); + + }, + + loadCompressedTextureCube: function () { + + console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' ); + + } + +}; + +// + +THREE.Projector = function () { + + console.error( 'THREE.Projector has been moved to /examples/js/renderers/Projector.js.' ); + + this.projectVector = function ( vector, camera ) { + + console.warn( 'THREE.Projector: .projectVector() is now vector.project().' ); + vector.project( camera ); + + }; + + this.unprojectVector = function ( vector, camera ) { + + console.warn( 'THREE.Projector: .unprojectVector() is now vector.unproject().' ); + vector.unproject( camera ); + + }; + + this.pickingRay = function ( vector, camera ) { + + 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.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ); + this.clear = function () {}; + this.render = function () {}; + this.setClearColor = function () {}; + this.setSize = function () {}; + +}; + +// File:src/extras/CurveUtils.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + */ + +THREE.CurveUtils = { + + tangentQuadraticBezier: function ( t, p0, p1, p2 ) { + + return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 ); + + }, + + // Puay Bing, thanks for helping with this derivative! + + tangentCubicBezier: function ( t, p0, p1, p2, p3 ) { + + return - 3 * p0 * ( 1 - t ) * ( 1 - t ) + + 3 * p1 * ( 1 - t ) * ( 1 - t ) - 6 * t * p1 * ( 1 - t ) + + 6 * t * p2 * ( 1 - t ) - 3 * t * t * p2 + + 3 * t * t * p3; + + }, + + tangentSpline: function ( t, p0, p1, p2, p3 ) { + + // To check if my formulas are correct + + var h00 = 6 * t * t - 6 * t; // derived from 2t^3 − 3t^2 + 1 + var h10 = 3 * t * t - 4 * t + 1; // t^3 − 2t^2 + t + var h01 = - 6 * t * t + 6 * t; // − 2t3 + 3t2 + var h11 = 3 * t * t - 2 * t; // t3 − t2 + + return h00 + h10 + h01 + h11; + + }, + + // Catmull-Rom + + interpolate: function( p0, p1, p2, p3, t ) { + + var v0 = ( p2 - p0 ) * 0.5; + var v1 = ( p3 - p1 ) * 0.5; + var t2 = t * t; + var t3 = t * t2; + return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; + + } + +}; + +// File:src/extras/SceneUtils.js + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.SceneUtils = { + + createMultiMaterialObject: function ( geometry, materials ) { + + var group = new THREE.Group(); + + for ( var i = 0, l = materials.length; i < l; i ++ ) { + + group.add( new THREE.Mesh( geometry, materials[ i ] ) ); + + } + + return group; + + }, + + detach: function ( child, parent, scene ) { + + child.applyMatrix( parent.matrixWorld ); + parent.remove( child ); + scene.add( child ); + + }, + + attach: function ( child, scene, parent ) { + + var matrixWorldInverse = new THREE.Matrix4(); + matrixWorldInverse.getInverse( parent.matrixWorld ); + child.applyMatrix( matrixWorldInverse ); + + scene.remove( child ); + parent.add( child ); + + } + +}; + +// File:src/extras/ShapeUtils.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + */ + +THREE.ShapeUtils = { + + // calculate area of the contour polygon + + area: function ( contour ) { + + 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; + + }, + + triangulate: ( function () { + + /** + * 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 + * + */ + + function snip( contour, u, v, w, n, verts ) { + + 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; + + cx = contour[ verts[ w ] ].x; + cy = contour[ verts[ w ] ].y; + + if ( Number.EPSILON > ( ( ( bx - ax ) * ( cy - ay ) ) - ( ( by - ay ) * ( cx - ax ) ) ) ) return false; + + var aX, aY, bX, bY, cX, cY; + var apx, apy, bpx, bpy, cpx, cpy; + var cCROSSap, bCROSScp, aCROSSbp; + + aX = cx - bx; aY = cy - by; + bX = ax - cx; bY = ay - cy; + cX = bx - ax; cY = by - ay; + + for ( p = 0; p < n; p ++ ) { + + px = contour[ verts[ p ] ].x; + py = contour[ verts[ p ] ].y; + + if ( ( ( px === ax ) && ( py === ay ) ) || + ( ( px === bx ) && ( py === by ) ) || + ( ( px === cx ) && ( py === cy ) ) ) continue; + + 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; + + if ( ( aCROSSbp >= - Number.EPSILON ) && ( bCROSScp >= - Number.EPSILON ) && ( cCROSSap >= - Number.EPSILON ) ) return false; + + } + + return true; + + } + + // takes in an contour array and returns + + return function triangulate( contour, indices ) { + + var n = contour.length; + + if ( n < 3 ) return null; + + var result = [], + verts = [], + vertIndices = []; + + /* we want a counter-clockwise polygon in verts */ + + var u, v, w; + + if ( THREE.ShapeUtils.area( contour ) > 0.0 ) { + + for ( v = 0; v < n; v ++ ) verts[ v ] = v; + + } else { + + for ( v = 0; v < n; v ++ ) verts[ v ] = ( n - 1 ) - v; + + } + + var nv = n; + + /* remove nv - 2 vertices, creating 1 triangle every time */ + + var count = 2 * nv; /* error detection */ + + for ( v = nv - 1; nv > 2; ) { + + /* if we loop, it is probably a non-simple polygon */ + + if ( ( count -- ) <= 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.ShapeUtils: Unable to triangulate polygon! in triangulate()' ); + + if ( indices ) return vertIndices; + return result; + + } + + /* three consecutive vertices in current polygon, */ + + u = v; if ( nv <= u ) u = 0; /* previous */ + v = u + 1; if ( nv <= v ) v = 0; /* new v */ + w = v + 1; if ( nv <= w ) w = 0; /* next */ + + if ( snip( contour, u, v, w, nv, verts ) ) { + + var a, b, c, s, t; + + /* true names of the vertices */ + + a = verts[ u ]; + b = verts[ v ]; + c = verts[ w ]; + + /* output Triangle */ + + result.push( [ contour[ a ], + contour[ b ], + contour[ c ] ] ); + + + vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] ); + + /* remove v from the remaining polygon */ + + for ( s = v, t = v + 1; t < nv; s ++, t ++ ) { + + verts[ s ] = verts[ t ]; + + } + + nv --; + + /* reset error detection counter */ + + count = 2 * nv; + + } + + } + + if ( indices ) return vertIndices; + return result; + + } + + } )(), + + triangulateShape: function ( contour, holes ) { + + function removeDupEndPts(points) { + + var l = points.length; + + if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) { + + points.pop(); + + } + + } + + removeDupEndPts( contour ); + holes.forEach( removeDupEndPts ); + + function point_in_segment_2D_colin( inSegPt1, inSegPt2, inOtherPt ) { + + // inOtherPt needs to be collinear to the inSegment + if ( inSegPt1.x !== inSegPt2.x ) { + + if ( inSegPt1.x < inSegPt2.x ) { + + return ( ( inSegPt1.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt2.x ) ); + + } else { + + return ( ( inSegPt2.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt1.x ) ); + + } + + } else { + + if ( inSegPt1.y < inSegPt2.y ) { + + return ( ( inSegPt1.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt2.y ) ); + + } else { + + return ( ( inSegPt2.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt1.y ) ); + + } + + } + + } + + function intersect_segments_2D( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1, inSeg2Pt2, inExcludeAdjacentSegs ) { + + var seg1dx = inSeg1Pt2.x - inSeg1Pt1.x, seg1dy = inSeg1Pt2.y - inSeg1Pt1.y; + var seg2dx = inSeg2Pt2.x - inSeg2Pt1.x, seg2dy = inSeg2Pt2.y - inSeg2Pt1.y; + + var seg1seg2dx = inSeg1Pt1.x - inSeg2Pt1.x; + var seg1seg2dy = inSeg1Pt1.y - inSeg2Pt1.y; + + var limit = seg1dy * seg2dx - seg1dx * seg2dy; + var perpSeg1 = seg1dy * seg1seg2dx - seg1dx * seg1seg2dy; + + if ( Math.abs( limit ) > Number.EPSILON ) { + + // not parallel + + var perpSeg2; + if ( limit > 0 ) { + + if ( ( perpSeg1 < 0 ) || ( perpSeg1 > limit ) ) return []; + perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy; + if ( ( perpSeg2 < 0 ) || ( perpSeg2 > limit ) ) return []; + + } else { + + if ( ( perpSeg1 > 0 ) || ( perpSeg1 < limit ) ) return []; + perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy; + if ( ( perpSeg2 > 0 ) || ( perpSeg2 < limit ) ) return []; + + } + + // i.e. to reduce rounding errors + // intersection at endpoint of segment#1? + if ( perpSeg2 === 0 ) { + + if ( ( inExcludeAdjacentSegs ) && + ( ( perpSeg1 === 0 ) || ( perpSeg1 === limit ) ) ) return []; + return [ inSeg1Pt1 ]; + + } + if ( perpSeg2 === limit ) { + + if ( ( inExcludeAdjacentSegs ) && + ( ( perpSeg1 === 0 ) || ( perpSeg1 === limit ) ) ) return []; + return [ inSeg1Pt2 ]; + + } + // intersection at endpoint of segment#2? + if ( perpSeg1 === 0 ) return [ inSeg2Pt1 ]; + if ( perpSeg1 === limit ) return [ inSeg2Pt2 ]; + + // 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 []; + + // 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 ) { + + if ( ( inSeg1Pt1.x !== inSeg2Pt1.x ) || + ( inSeg1Pt1.y !== inSeg2Pt1.y ) ) return []; // they are distinct points + return [ inSeg1Pt1 ]; // they are the same point + + } + // segment#1 is a single point + if ( seg1Pt ) { + + if ( ! point_in_segment_2D_colin( inSeg2Pt1, inSeg2Pt2, inSeg1Pt1 ) ) return []; // but not in segment#2 + return [ inSeg1Pt1 ]; + + } + // segment#2 is a single point + if ( seg2Pt ) { + + if ( ! point_in_segment_2D_colin( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1 ) ) return []; // but not in segment#1 + return [ inSeg2Pt1 ]; + + } + + // they are collinear segments, which might overlap + var seg1min, seg1max, seg1minVal, seg1maxVal; + var seg2min, seg2max, seg2minVal, seg2maxVal; + if ( seg1dx !== 0 ) { + + // the segments are NOT on a vertical line + if ( inSeg1Pt1.x < inSeg1Pt2.x ) { + + seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.x; + seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.x; + + } else { + + seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.x; + seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.x; + + } + if ( inSeg2Pt1.x < inSeg2Pt2.x ) { + + seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.x; + seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.x; + + } else { + + seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.x; + seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.x; + + } + + } else { + + // the segments are on a vertical line + if ( inSeg1Pt1.y < inSeg1Pt2.y ) { + + seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.y; + seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.y; + + } else { + + seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.y; + seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.y; + + } + if ( inSeg2Pt1.y < inSeg2Pt2.y ) { + + seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.y; + seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.y; + + } else { + + seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.y; + seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.y; + + } + + } + if ( seg1minVal <= seg2minVal ) { + + if ( seg1maxVal < seg2minVal ) return []; + if ( seg1maxVal === seg2minVal ) { + + if ( inExcludeAdjacentSegs ) return []; + return [ seg2min ]; + + } + if ( seg1maxVal <= seg2maxVal ) return [ seg2min, seg1max ]; + return [ seg2min, seg2max ]; + + } else { + + if ( seg1minVal > seg2maxVal ) return []; + if ( seg1minVal === seg2maxVal ) { + + if ( inExcludeAdjacentSegs ) return []; + return [ seg1min ]; + + } + if ( seg1maxVal <= seg2maxVal ) return [ seg1min, seg1max ]; + return [ seg1min, seg2max ]; + + } + + } + + } + + function isPointInsideAngle( inVertex, inLegFromPt, inLegToPt, inOtherPt ) { + + // The order of legs is important + + // 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; + + if ( Math.abs( from2toAngle ) > Number.EPSILON ) { + + // angle != 180 deg. + + var other2toAngle = otherPtX * legToPtY - otherPtY * legToPtX; + // console.log( "from2to: " + from2toAngle + ", from2other: " + from2otherAngle + ", other2to: " + other2toAngle ); + + if ( from2toAngle > 0 ) { + + // main angle < 180 deg. + return ( ( from2otherAngle >= 0 ) && ( other2toAngle >= 0 ) ); + + } else { + + // main angle > 180 deg. + return ( ( from2otherAngle >= 0 ) || ( other2toAngle >= 0 ) ); + + } + + } else { + + // angle == 180 deg. + // console.log( "from2to: 180 deg., from2other: " + from2otherAngle ); + return ( from2otherAngle > 0 ); + + } + + } + + + function removeHoles( contour, holes ) { + + var shape = contour.concat(); // work on this shape + var hole; + + function isCutLineInsideAngles( inShapeIdx, inHoleIdx ) { + + // Check if hole point lies within angle around shape point + var lastShapeIdx = shape.length - 1; + + var prevShapeIdx = inShapeIdx - 1; + if ( prevShapeIdx < 0 ) prevShapeIdx = lastShapeIdx; + + var nextShapeIdx = inShapeIdx + 1; + if ( nextShapeIdx > lastShapeIdx ) nextShapeIdx = 0; + + var insideAngle = isPointInsideAngle( shape[ inShapeIdx ], shape[ prevShapeIdx ], shape[ nextShapeIdx ], hole[ inHoleIdx ] ); + if ( ! insideAngle ) { + + // console.log( "Vertex (Shape): " + inShapeIdx + ", Point: " + hole[inHoleIdx].x + "/" + hole[inHoleIdx].y ); + return false; + + } + + // Check if shape point lies within angle around hole point + var lastHoleIdx = hole.length - 1; + + var prevHoleIdx = inHoleIdx - 1; + if ( prevHoleIdx < 0 ) prevHoleIdx = lastHoleIdx; + + var nextHoleIdx = inHoleIdx + 1; + if ( nextHoleIdx > lastHoleIdx ) nextHoleIdx = 0; + + insideAngle = isPointInsideAngle( hole[ inHoleIdx ], hole[ prevHoleIdx ], hole[ nextHoleIdx ], shape[ inShapeIdx ] ); + if ( ! insideAngle ) { + + // console.log( "Vertex (Hole): " + inHoleIdx + ", Point: " + shape[inShapeIdx].x + "/" + shape[inShapeIdx].y ); + return false; + + } + + return true; + + } + + 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; + + } + + return false; + + } + + var indepHoles = []; + + function intersectsHoleEdge( inShapePt, inHolePt ) { + + // checks for intersections with hole edges + var ihIdx, chkHole, + hIdx, nextIdx, intersection; + for ( ihIdx = 0; ihIdx < indepHoles.length; ihIdx ++ ) { + + chkHole = holes[ indepHoles[ ihIdx ]]; + for ( hIdx = 0; hIdx < chkHole.length; hIdx ++ ) { + + nextIdx = hIdx + 1; nextIdx %= chkHole.length; + intersection = intersect_segments_2D( inShapePt, inHolePt, chkHole[ hIdx ], chkHole[ nextIdx ], true ); + if ( intersection.length > 0 ) return true; + + } + + } + return false; + + } + + var holeIndex, shapeIndex, + shapePt, holePt, + holeIdx, cutKey, failedCuts = [], + tmpShape1, tmpShape2, + tmpHole1, tmpHole2; + + for ( var h = 0, hl = holes.length; h < hl; h ++ ) { + + indepHoles.push( h ); + + } + + var minShapeIndex = 0; + var counter = indepHoles.length * 2; + while ( indepHoles.length > 0 ) { + + counter --; + if ( counter < 0 ) { + + console.log( "Infinite Loop! Holes left:" + indepHoles.length + ", Probably Hole outside Shape!" ); + break; + + } + + // search for shape-vertex and hole-vertex, + // which can be connected without intersections + for ( shapeIndex = minShapeIndex; shapeIndex < shape.length; shapeIndex ++ ) { + + shapePt = shape[ shapeIndex ]; + holeIndex = - 1; + + // search for hole which can be reached without intersections + for ( var h = 0; h < indepHoles.length; h ++ ) { + + holeIdx = indepHoles[ h ]; + + // prevent multiple checks + cutKey = shapePt.x + ":" + shapePt.y + ":" + holeIdx; + if ( failedCuts[ cutKey ] !== undefined ) continue; + + 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; + + holeIndex = h2; + indepHoles.splice( h, 1 ); + + tmpShape1 = shape.slice( 0, shapeIndex + 1 ); + tmpShape2 = shape.slice( shapeIndex ); + tmpHole1 = hole.slice( holeIndex ); + tmpHole2 = hole.slice( 0, holeIndex + 1 ); + + shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 ); + + minShapeIndex = shapeIndex; + + // Debug only, to show the selected cuts + // glob_CutLines.push( [ shapePt, holePt ] ); + + break; + + } + if ( holeIndex >= 0 ) break; // hole-vertex found + + failedCuts[ cutKey ] = true; // remember failure + + } + if ( holeIndex >= 0 ) break; // hole-vertex found + + } + + } + + return shape; /* shape with no holes */ + + } + + + 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. + + var allpoints = contour.concat(); + + for ( var h = 0, hl = holes.length; h < hl; h ++ ) { + + Array.prototype.push.apply( allpoints, holes[ h ] ); + + } + + //console.log( "allpoints",allpoints, allpoints.length ); + + // prepare all points map + + for ( i = 0, il = allpoints.length; i < il; i ++ ) { + + key = allpoints[ i ].x + ":" + allpoints[ i ].y; + + if ( allPointsMap[ key ] !== undefined ) { + + console.warn( "THREE.ShapeUtils: Duplicate point", key, i ); + + } + + allPointsMap[ key ] = i; + + } + + // remove holes by cutting paths to holes and adding them to the shape + var shapeWithoutHoles = removeHoles( contour, holes ); + + var triangles = THREE.ShapeUtils.triangulate( shapeWithoutHoles, false ); // True returns indices for points of spooled shape + //console.log( "triangles",triangles, triangles.length ); + + // check all face vertices against all points map + + for ( i = 0, il = triangles.length; i < il; i ++ ) { + + face = triangles[ i ]; + + for ( f = 0; f < 3; f ++ ) { + + key = face[ f ].x + ":" + face[ f ].y; + + index = allPointsMap[ key ]; + + if ( index !== undefined ) { + + face[ f ] = index; + + } + + } + + } + + return triangles.concat(); + + }, + + isClockWise: function ( pts ) { + + return THREE.ShapeUtils.area( pts ) < 0; + + }, + + // Bezier Curves formulas obtained from + // http://en.wikipedia.org/wiki/B%C3%A9zier_curve + + // Quad Bezier Functions + + b2: ( function () { + + function b2p0( t, p ) { + + var k = 1 - t; + return k * k * p; + + } + + function b2p1( t, p ) { + + return 2 * ( 1 - t ) * t * p; + + } + + function b2p2( t, p ) { + + return t * t * p; + + } + + return function b2( t, p0, p1, p2 ) { + + return b2p0( t, p0 ) + b2p1( t, p1 ) + b2p2( t, p2 ); + + }; + + } )(), + + // Cubic Bezier Functions + + b3: ( function () { + + function b3p0( t, p ) { + + var k = 1 - t; + return k * k * k * p; + + } + + function b3p1( t, p ) { + + var k = 1 - t; + return 3 * k * k * t * p; + + } + + function b3p2( t, p ) { + + var k = 1 - t; + return 3 * k * t * t * p; + + } + + function b3p3( t, p ) { + + return t * t * t * p; + + } + + return function b3( t, p0, p1, p2, p3 ) { + + return b3p0( t, p0 ) + b3p1( t, p1 ) + b3p2( t, p2 ) + b3p3( t, p3 ); + + }; + + } )() + +}; + +// 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 + * + * A series of curves can be represented as a THREE.CurvePath + * + **/ + +/************************************************************** + * Abstract Curve base class + **************************************************************/ + +THREE.Curve = function () { + +}; + +THREE.Curve.prototype = { + + constructor: THREE.Curve, + + // Virtual base class method to overwrite and implement in subclasses + // - t [0 .. 1] + + getPoint: function ( t ) { + + console.warn( "THREE.Curve: Warning, getPoint() not implemented!" ); + return null; + + }, + + // Get point at relative position in curve according to arc length + // - u [0 .. 1] + + getPointAt: function ( u ) { + + var t = this.getUtoTmapping( u ); + return this.getPoint( t ); + + }, + + // Get sequence of points using getPoint( t ) + + getPoints: function ( divisions ) { + + if ( ! divisions ) divisions = 5; + + var points = []; + + for ( var d = 0; d <= divisions; d ++ ) { + + points.push( this.getPoint( d / divisions ) ); + + } + + return points; + + }, + + // Get sequence of points using getPointAt( u ) + + getSpacedPoints: function ( divisions ) { + + if ( ! divisions ) divisions = 5; + + var points = []; + + for ( var d = 0; d <= divisions; d ++ ) { + + points.push( this.getPointAt( d / divisions ) ); + + } + + return points; + + }, + + // Get total curve arc length + + getLength: function () { + + var lengths = this.getLengths(); + return lengths[ lengths.length - 1 ]; + + }, + + // Get list of cumulative segment lengths + + getLengths: function ( divisions ) { + + if ( ! divisions ) divisions = ( this.__arcLengthDivisions ) ? ( this.__arcLengthDivisions ) : 200; + + if ( this.cacheArcLengths + && ( this.cacheArcLengths.length === divisions + 1 ) + && ! this.needsUpdate ) { + + //console.log( "cached", this.cacheArcLengths ); + return this.cacheArcLengths; + + } + + this.needsUpdate = false; + + var cache = []; + var current, last = this.getPoint( 0 ); + var p, sum = 0; + + cache.push( 0 ); + + for ( p = 1; p <= divisions; p ++ ) { + + current = this.getPoint ( p / divisions ); + sum += current.distanceTo( last ); + cache.push( sum ); + last = current; + + } + + this.cacheArcLengths = cache; + + return cache; // { sums: cache, sum:sum }; Sum is in the last element. + + }, + + updateArcLengths: function() { + + this.needsUpdate = true; + this.getLengths(); + + }, + + // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant + + getUtoTmapping: function ( u, distance ) { + + var arcLengths = this.getLengths(); + + var i = 0, il = arcLengths.length; + + var targetArcLength; // The targeted u distance value to get + + if ( distance ) { + + targetArcLength = distance; + + } else { + + targetArcLength = u * arcLengths[ il - 1 ]; + + } + + //var time = Date.now(); + + // binary search for the index with largest value smaller than target u distance + + var low = 0, high = il - 1, comparison; + + while ( low <= high ) { + + 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; + + if ( comparison < 0 ) { + + low = i + 1; + + } else if ( comparison > 0 ) { + + high = i - 1; + + } else { + + high = i; + break; + + // DONE + + } + + } + + i = high; + + //console.log('b' , i, low, high, Date.now()- time); + + if ( arcLengths[ i ] === targetArcLength ) { + + var t = i / ( il - 1 ); + return t; + + } + + // we could get finer grain at lengths, or use simple interpolation between two points + + var lengthBefore = arcLengths[ i ]; + var lengthAfter = arcLengths[ i + 1 ]; + + var segmentLength = lengthAfter - lengthBefore; + + // determine where we are between the 'before' and 'after' points + + var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; + + // add that fractional amount to t + + var t = ( i + segmentFraction ) / ( il - 1 ); + + return t; + + }, + + // Returns a unit vector tangent at t + // In case any sub curve does not implement its tangent derivation, + // 2 points a small delta apart will be used to find its gradient + // which seems to give a reasonable approximation + + getTangent: function( t ) { + + 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; + + var pt1 = this.getPoint( t1 ); + var pt2 = this.getPoint( t2 ); + + var vec = pt2.clone().sub( pt1 ); + return vec.normalize(); + + }, + + getTangentAt: function ( u ) { + + var t = this.getUtoTmapping( u ); + return this.getTangent( t ); + + } + +}; + +// TODO: Transformation for Curves? + +/************************************************************** + * 3D Curves + **************************************************************/ + +// A Factory method for creating new curve subclasses + +THREE.Curve.create = function ( constructor, getPointFunc ) { + + constructor.prototype = Object.create( THREE.Curve.prototype ); + constructor.prototype.constructor = constructor; + constructor.prototype.getPoint = getPointFunc; + + return constructor; + +}; + +// File:src/extras/core/CurvePath.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * + **/ + +/************************************************************** + * Curved Path - a curve path is simply a array of connected + * curves, but retains the api of a curve + **************************************************************/ + +THREE.CurvePath = function () { + + this.curves = []; + + this.autoClose = false; // Automatically closes the path + +}; + +THREE.CurvePath.prototype = Object.assign( Object.create( THREE.Curve.prototype ), { + + constructor: THREE.CurvePath, + + add: function ( curve ) { + + this.curves.push( curve ); + + }, + + closePath: function () { + + // Add a line curve if start and end of lines are not connected + var startPoint = this.curves[ 0 ].getPoint( 0 ); + var endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 ); + + if ( ! startPoint.equals( endPoint ) ) { + + this.curves.push( new THREE.LineCurve( endPoint, startPoint ) ); + + } + + }, + + // To get accurate point with reference to + // entire path distance at time t, + // following has to be done: + + // 1. Length of each sub path have to be known + // 2. Locate and identify type of curve + // 3. Get t for the curve + // 4. Return curve.getPointAt(t') + + getPoint: function ( t ) { + + var d = t * this.getLength(); + var curveLengths = this.getCurveLengths(); + var i = 0; + + // To think about boundaries points. + + while ( i < curveLengths.length ) { + + if ( curveLengths[ i ] >= d ) { + + var diff = curveLengths[ i ] - d; + var curve = this.curves[ i ]; + + var segmentLength = curve.getLength(); + var u = segmentLength === 0 ? 0 : 1 - diff / segmentLength; + + return curve.getPointAt( u ); + + } + + i ++; + + } + + return null; + + // loop where sum != 0, sum > d , sum+1 1 && !points[ points.length - 1 ].equals( points[ 0 ] ) ) { + + points.push( points[ 0 ] ); + + } + + return points; + + }, + + /************************************************************** + * Create Geometries Helpers + **************************************************************/ + + /// Generate geometry from path points (for Line or Points objects) + + createPointsGeometry: function ( divisions ) { + + var pts = this.getPoints( divisions ); + return this.createGeometry( pts ); + + }, + + // Generate geometry from equidistant sampling along the path + + createSpacedPointsGeometry: function ( divisions ) { + + var pts = this.getSpacedPoints( divisions ); + return this.createGeometry( pts ); + + }, + + createGeometry: function ( points ) { + + var geometry = new THREE.Geometry(); + + for ( var i = 0, l = points.length; i < l; i ++ ) { + + var point = points[ i ]; + geometry.vertices.push( new THREE.Vector3( point.x, point.y, point.z || 0 ) ); + + } + + return geometry; + + } + +} ); + +// File:src/extras/core/Font.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Font = function ( data ) { + + this.data = data; + +}; + +Object.assign( THREE.Font.prototype, { + + generateShapes: function ( text, size, divisions ) { + + function createPaths( text ) { + + var chars = String( text ).split( '' ); + var scale = size / data.resolution; + var offset = 0; + + var paths = []; + + for ( var i = 0; i < chars.length; i ++ ) { + + var ret = createPath( chars[ i ], scale, offset ); + offset += ret.offset; + + paths.push( ret.path ); + + } + + return paths; + + } + + function createPath( c, scale, offset ) { + + var glyph = data.glyphs[ c ] || data.glyphs[ '?' ]; + + if ( ! glyph ) return; + + var path = new THREE.ShapePath(); + + var pts = [], b2 = THREE.ShapeUtils.b2, b3 = THREE.ShapeUtils.b3; + var x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2, laste; + + if ( glyph.o ) { + + var outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) ); + + for ( var i = 0, l = outline.length; i < l; ) { + + var action = outline[ i ++ ]; + + switch ( action ) { + + case 'm': // moveTo + + x = outline[ i ++ ] * scale + offset; + y = outline[ i ++ ] * scale; + + path.moveTo( x, y ); + + break; + + case 'l': // lineTo + + x = outline[ i ++ ] * scale + offset; + y = outline[ i ++ ] * scale; + + path.lineTo( x, y ); + + break; + + case 'q': // quadraticCurveTo + + cpx = outline[ i ++ ] * scale + offset; + cpy = outline[ i ++ ] * scale; + cpx1 = outline[ i ++ ] * scale + offset; + cpy1 = outline[ i ++ ] * scale; + + path.quadraticCurveTo( cpx1, cpy1, cpx, cpy ); + + laste = pts[ pts.length - 1 ]; + + if ( laste ) { + + cpx0 = laste.x; + cpy0 = laste.y; + + for ( var i2 = 1; i2 <= divisions; i2 ++ ) { + + var t = i2 / divisions; + b2( t, cpx0, cpx1, cpx ); + b2( t, cpy0, cpy1, cpy ); + + } + + } + + break; + + case 'b': // bezierCurveTo + + cpx = outline[ i ++ ] * scale + offset; + cpy = outline[ i ++ ] * scale; + cpx1 = outline[ i ++ ] * scale + offset; + cpy1 = outline[ i ++ ] * scale; + cpx2 = outline[ i ++ ] * scale + offset; + cpy2 = outline[ i ++ ] * scale; + + path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy ); + + laste = pts[ pts.length - 1 ]; + + if ( laste ) { + + cpx0 = laste.x; + cpy0 = laste.y; + + for ( var i2 = 1; i2 <= divisions; i2 ++ ) { + + var t = i2 / divisions; + b3( t, cpx0, cpx1, cpx2, cpx ); + b3( t, cpy0, cpy1, cpy2, cpy ); + + } + + } + + break; + + } + + } + + } + + return { offset: glyph.ha * scale, path: path }; + + } + + // + + if ( size === undefined ) size = 100; + if ( divisions === undefined ) divisions = 4; + + var data = this.data; + + var paths = createPaths( text ); + var shapes = []; + + for ( var p = 0, pl = paths.length; p < pl; p ++ ) { + + Array.prototype.push.apply( shapes, paths[ p ].toShapes() ); + + } + + return shapes; + + } + +} ); + +// File:src/extras/core/Path.js + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * Creates free form 2d path using series of points, lines or curves. + * + **/ + +THREE.Path = function ( points ) { + + THREE.CurvePath.call( this ); + this.currentPoint = new THREE.Vector2(); + + if ( points ) { + + this.fromPoints( points ); + + } + +}; + +THREE.Path.prototype = Object.assign( Object.create( THREE.CurvePath.prototype ), { + + constructor: THREE.Path, + + // Create path using straight lines to connect all points + // - vectors: array of Vector2 + fromPoints: function ( vectors ) { + + this.moveTo( vectors[ 0 ].x, vectors[ 0 ].y ); + + for ( var i = 1, l = vectors.length; i < l; i ++ ) { + + this.lineTo( vectors[ i ].x, vectors[ i ].y ); + + } + + }, + + moveTo: function ( x, y ) { + + this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying? + + }, + + lineTo: function ( x, y ) { + + var curve = new THREE.LineCurve( this.currentPoint.clone(), new THREE.Vector2( x, y ) ); + this.curves.push( curve ); + + this.currentPoint.set( x, y ); + + }, + + quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) { + + var curve = new THREE.QuadraticBezierCurve( + this.currentPoint.clone(), + new THREE.Vector2( aCPx, aCPy ), + new THREE.Vector2( aX, aY ) + ); + + this.curves.push( curve ); + + this.currentPoint.set( aX, aY ); + + }, + + bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { + + var curve = new THREE.CubicBezierCurve( + this.currentPoint.clone(), + new THREE.Vector2( aCP1x, aCP1y ), + new THREE.Vector2( aCP2x, aCP2y ), + new THREE.Vector2( aX, aY ) + ); + + this.curves.push( curve ); + + this.currentPoint.set( aX, aY ); + + }, + + splineThru: function ( pts /*Array of Vector*/ ) { + + var npts = [ this.currentPoint.clone() ].concat( pts ); + + var curve = new THREE.SplineCurve( npts ); + this.curves.push( curve ); + + this.currentPoint.copy( pts[ pts.length - 1 ] ); + + }, + + arc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { + + var x0 = this.currentPoint.x; + var y0 = this.currentPoint.y; + + this.absarc( aX + x0, aY + y0, aRadius, + aStartAngle, aEndAngle, aClockwise ); + + }, + + absarc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { + + this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); + + }, + + ellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { + + var x0 = this.currentPoint.x; + var y0 = this.currentPoint.y; + + this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); + + }, + + absellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { + + var curve = new THREE.EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ); + + if ( this.curves.length > 0 ) { + + // if a previous curve is present, attempt to join + var firstPoint = curve.getPoint( 0 ); + + if ( ! firstPoint.equals( this.currentPoint ) ) { + + this.lineTo( firstPoint.x, firstPoint.y ); + + } + + } + + this.curves.push( curve ); + + var lastPoint = curve.getPoint( 1 ); + this.currentPoint.copy( lastPoint ); + + } + +} ); + + +// minimal class for proxing functions to Path. Replaces old "extractSubpaths()" +THREE.ShapePath = function() { + this.subPaths = []; + this.currentPath = null; +} + +THREE.ShapePath.prototype = { + moveTo: function ( x, y ) { + this.currentPath = new THREE.Path(); + this.subPaths.push(this.currentPath); + this.currentPath.moveTo( x, y ); + }, + lineTo: function ( x, y ) { + this.currentPath.lineTo( x, y ); + }, + quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) { + this.currentPath.quadraticCurveTo( aCPx, aCPy, aX, aY ); + }, + bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) { + this.currentPath.bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ); + }, + splineThru: function ( pts ) { + this.currentPath.splineThru( pts ); + }, + + toShapes: function ( isCCW, noHoles ) { + + function toShapesNoHoles( inSubpaths ) { + + var shapes = []; + + for ( var i = 0, l = inSubpaths.length; i < l; i ++ ) { + + var tmpPath = inSubpaths[ i ]; + + var tmpShape = new THREE.Shape(); + tmpShape.curves = tmpPath.curves; + + shapes.push( tmpShape ); + + } + + return shapes; + + } + + function isPointInsidePolygon( inPt, inPolygon ) { + + var polyLen = inPolygon.length; + + // inPt on polygon contour => immediate success or + // toggling of inside/outside at every single! intersection point of an edge + // with the horizontal line through inPt, left of inPt + // not counting lowerY endpoints of edges and whole edges on that line + var inside = false; + for ( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) { + + var edgeLowPt = inPolygon[ p ]; + var edgeHighPt = inPolygon[ q ]; + + var edgeDx = edgeHighPt.x - edgeLowPt.x; + var edgeDy = edgeHighPt.y - edgeLowPt.y; + + if ( Math.abs( edgeDy ) > Number.EPSILON ) { + + // not parallel + if ( edgeDy < 0 ) { + + edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx; + edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy; + + } + if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue; + + if ( inPt.y === edgeLowPt.y ) { + + if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ? + // continue; // no intersection or edgeLowPt => doesn't count !!! + + } else { + + var perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y ); + if ( perpEdge === 0 ) return true; // inPt is on contour ? + if ( perpEdge < 0 ) continue; + inside = ! inside; // true intersection left of inPt + + } + + } else { + + // parallel or 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 isClockWise = THREE.ShapeUtils.isClockWise; + + var subPaths = this.subPaths; + if ( subPaths.length === 0 ) return []; + + if ( noHoles === true ) return toShapesNoHoles( subPaths ); + + + var solid, tmpPath, tmpShape, shapes = []; + + if ( subPaths.length === 1 ) { + + tmpPath = subPaths[ 0 ]; + tmpShape = new THREE.Shape(); + tmpShape.curves = tmpPath.curves; + shapes.push( tmpShape ); + return shapes; + + } + + var holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() ); + holesFirst = isCCW ? ! holesFirst : holesFirst; + + // console.log("Holes first", holesFirst); + + var betterShapeHoles = []; + var newShapes = []; + var newShapeHoles = []; + var mainIdx = 0; + var tmpPoints; + + newShapes[ mainIdx ] = undefined; + newShapeHoles[ mainIdx ] = []; + + for ( var i = 0, l = subPaths.length; i < l; i ++ ) { + + tmpPath = subPaths[ i ]; + tmpPoints = tmpPath.getPoints(); + solid = isClockWise( tmpPoints ); + solid = isCCW ? ! solid : solid; + + if ( solid ) { + + if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++; + + newShapes[ mainIdx ] = { s: new THREE.Shape(), p: tmpPoints }; + newShapes[ mainIdx ].s.curves = tmpPath.curves; + + if ( holesFirst ) mainIdx ++; + newShapeHoles[ mainIdx ] = []; + + //console.log('cw', i); + + } else { + + newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } ); + + //console.log('ccw', i); + + } + + } + + // only Holes? -> probably all Shapes with wrong orientation + if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths ); + + + 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 ++ ) { + + var sho = newShapeHoles[ sIdx ]; + + for ( var hIdx = 0; hIdx < sho.length; hIdx ++ ) { + + var ho = sho[ hIdx ]; + var hole_unassigned = true; + + for ( var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) { + + if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) { + + if ( sIdx !== s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } ); + if ( hole_unassigned ) { + + hole_unassigned = false; + betterShapeHoles[ s2Idx ].push( ho ); + + } else { + + ambiguous = true; + + } + + } + + } + if ( hole_unassigned ) { + + betterShapeHoles[ sIdx ].push( ho ); + + } + + } + + } + // console.log("ambiguous: ", ambiguous); + if ( toChange.length > 0 ) { + + // console.log("to change: ", toChange); + if ( ! ambiguous ) newShapeHoles = betterShapeHoles; + + } + + } + + var tmpHoles; + + for ( var i = 0, il = newShapes.length; i < il; i ++ ) { + + tmpShape = newShapes[ i ].s; + shapes.push( tmpShape ); + tmpHoles = newShapeHoles[ i ]; + + for ( var j = 0, jl = tmpHoles.length; j < jl; j ++ ) { + + tmpShape.holes.push( tmpHoles[ j ].h ); + + } + + } + + //console.log("shape", shapes); + + return shapes; + + } +} + +// File:src/extras/core/Shape.js + +/** + * @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. + +THREE.Shape = function () { + + THREE.Path.apply( this, arguments ); + + this.holes = []; + +}; + +THREE.Shape.prototype = Object.assign( Object.create( THREE.Path.prototype ), { + + constructor: THREE.Shape, + + // Convenience method to return ExtrudeGeometry + + extrude: function ( options ) { + + return new THREE.ExtrudeGeometry( this, options ); + + }, + + // Convenience method to return ShapeGeometry + + makeGeometry: function ( options ) { + + return new THREE.ShapeGeometry( this, options ); + + }, + + getPointsHoles: function ( divisions ) { + + var holesPts = []; + + for ( var i = 0, l = this.holes.length; i < l; i ++ ) { + + holesPts[ i ] = this.holes[ i ].getPoints( divisions ); + + } + + return holesPts; + + }, + + // Get points of shape and holes (keypoints based on segments parameter) + + extractAllPoints: function ( divisions ) { + + return { + + shape: this.getPoints( divisions ), + holes: this.getPointsHoles( divisions ) + + }; + + }, + + extractPoints: function ( divisions ) { + + return this.extractAllPoints( divisions ); + + } + +} ); + +// File:src/extras/curves/LineCurve.js + +/************************************************************** + * Line + **************************************************************/ + +THREE.LineCurve = function ( v1, v2 ) { + + this.v1 = v1; + this.v2 = v2; + +}; + +THREE.LineCurve.prototype = Object.create( THREE.Curve.prototype ); +THREE.LineCurve.prototype.constructor = THREE.LineCurve; + +THREE.LineCurve.prototype.getPoint = function ( t ) { + + if ( t === 1 ) { + + return this.v2.clone(); + + } + + var point = this.v2.clone().sub( this.v1 ); + point.multiplyScalar( t ).add( this.v1 ); + + return point; + +}; + +// Line curve is linear, so we can overwrite default getPointAt + +THREE.LineCurve.prototype.getPointAt = function ( u ) { + + return this.getPoint( u ); + +}; + +THREE.LineCurve.prototype.getTangent = function( t ) { + + var tangent = this.v2.clone().sub( this.v1 ); + + return tangent.normalize(); + +}; + +// File:src/extras/curves/QuadraticBezierCurve.js + +/************************************************************** + * Quadratic Bezier curve + **************************************************************/ + + +THREE.QuadraticBezierCurve = function ( v0, v1, v2 ) { + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + +}; + +THREE.QuadraticBezierCurve.prototype = Object.create( THREE.Curve.prototype ); +THREE.QuadraticBezierCurve.prototype.constructor = THREE.QuadraticBezierCurve; + + +THREE.QuadraticBezierCurve.prototype.getPoint = function ( t ) { + + var b2 = THREE.ShapeUtils.b2; + + return new THREE.Vector2( + b2( t, this.v0.x, this.v1.x, this.v2.x ), + b2( t, this.v0.y, this.v1.y, this.v2.y ) + ); + +}; + + +THREE.QuadraticBezierCurve.prototype.getTangent = function( t ) { + + var tangentQuadraticBezier = THREE.CurveUtils.tangentQuadraticBezier; + + return new THREE.Vector2( + tangentQuadraticBezier( t, this.v0.x, this.v1.x, this.v2.x ), + tangentQuadraticBezier( t, this.v0.y, this.v1.y, this.v2.y ) + ).normalize(); + +}; + +// File:src/extras/curves/CubicBezierCurve.js + +/************************************************************** + * Cubic Bezier curve + **************************************************************/ + +THREE.CubicBezierCurve = function ( v0, v1, v2, v3 ) { + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; + +}; + +THREE.CubicBezierCurve.prototype = Object.create( THREE.Curve.prototype ); +THREE.CubicBezierCurve.prototype.constructor = THREE.CubicBezierCurve; + +THREE.CubicBezierCurve.prototype.getPoint = function ( t ) { + + var b3 = THREE.ShapeUtils.b3; + + return new THREE.Vector2( + b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ), + b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ) + ); + +}; + +THREE.CubicBezierCurve.prototype.getTangent = function( t ) { + + var tangentCubicBezier = THREE.CurveUtils.tangentCubicBezier; + + return new THREE.Vector2( + tangentCubicBezier( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ), + tangentCubicBezier( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ) + ).normalize(); + +}; + +// File:src/extras/curves/SplineCurve.js + +/************************************************************** + * Spline curve + **************************************************************/ + +THREE.SplineCurve = function ( points /* array of Vector2 */ ) { + + this.points = ( points == undefined ) ? [] : points; + +}; + +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; + + 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 ]; + + var interpolate = THREE.CurveUtils.interpolate; + + return new THREE.Vector2( + interpolate( point0.x, point1.x, point2.x, point3.x, weight ), + interpolate( point0.y, point1.y, point2.y, point3.y, weight ) + ); + +}; + +// File:src/extras/curves/EllipseCurve.js + +/************************************************************** + * Ellipse curve + **************************************************************/ + +THREE.EllipseCurve = function( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) { + + this.aX = aX; + this.aY = aY; + + this.xRadius = xRadius; + this.yRadius = yRadius; + + this.aStartAngle = aStartAngle; + this.aEndAngle = aEndAngle; + + this.aClockwise = aClockwise; + + this.aRotation = aRotation || 0; + +}; + +THREE.EllipseCurve.prototype = Object.create( THREE.Curve.prototype ); +THREE.EllipseCurve.prototype.constructor = THREE.EllipseCurve; + +THREE.EllipseCurve.prototype.getPoint = function( t ) { + + var twoPi = Math.PI * 2; + var deltaAngle = this.aEndAngle - this.aStartAngle; + var samePoints = Math.abs( deltaAngle ) < Number.EPSILON; + + // ensures that deltaAngle is 0 .. 2 PI + while ( deltaAngle < 0 ) deltaAngle += twoPi; + while ( deltaAngle > twoPi ) deltaAngle -= twoPi; + + if ( deltaAngle < Number.EPSILON ) { + + if ( samePoints ) { + + deltaAngle = 0; + + } else { + + deltaAngle = twoPi; + + } + + } + + if ( this.aClockwise === true && ! samePoints ) { + + if ( deltaAngle === twoPi ) { + + deltaAngle = - twoPi; + + } else { + + deltaAngle = deltaAngle - twoPi; + + } + + } + + var angle = this.aStartAngle + t * deltaAngle; + 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 tx = x - this.aX; + var ty = y - this.aY; + + // Rotate the point about the center of the ellipse. + x = tx * cos - ty * sin + this.aX; + y = tx * sin + ty * cos + this.aY; + + } + + return new THREE.Vector2( x, y ); + +}; + +// File:src/extras/curves/ArcCurve.js + +/************************************************************** + * Arc curve + **************************************************************/ + +THREE.ArcCurve = function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { + + THREE.EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); + +}; + +THREE.ArcCurve.prototype = Object.create( THREE.EllipseCurve.prototype ); +THREE.ArcCurve.prototype.constructor = THREE.ArcCurve; + +// File:src/extras/curves/LineCurve3.js + +/************************************************************** + * Line3D + **************************************************************/ + +THREE.LineCurve3 = THREE.Curve.create( + + function ( v1, v2 ) { + + this.v1 = v1; + this.v2 = v2; + + }, + + function ( t ) { + + if ( t === 1 ) { + + return this.v2.clone(); + + } + + var vector = new THREE.Vector3(); + + vector.subVectors( this.v2, this.v1 ); // diff + vector.multiplyScalar( t ); + vector.add( this.v1 ); + + return vector; } - var contourMovements = []; +); - for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { +// File:src/extras/curves/QuadraticBezierCurve3.js - if ( j === il ) j = 0; - if ( k === il ) k = 0; +/************************************************************** + * Quadratic Bezier 3D curve + **************************************************************/ - // (j)---(i)---(k) - // console.log('i,j,k', i, j , k) +THREE.QuadraticBezierCurve3 = THREE.Curve.create( + + function ( v0, v1, v2 ) { + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + + }, + + function ( t ) { + + var b2 = THREE.ShapeUtils.b2; + + return new THREE.Vector3( + b2( t, this.v0.x, this.v1.x, this.v2.x ), + b2( t, this.v0.y, this.v1.y, this.v2.y ), + b2( t, this.v0.z, this.v1.z, this.v2.z ) + ); + + } + +); + +// File:src/extras/curves/CubicBezierCurve3.js + +/************************************************************** + * Cubic Bezier 3D curve + **************************************************************/ + +THREE.CubicBezierCurve3 = THREE.Curve.create( + + function ( v0, v1, v2, v3 ) { + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; + + }, + + function ( t ) { + + var b3 = THREE.ShapeUtils.b3; + + return new THREE.Vector3( + b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ), + b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ), + b3( t, this.v0.z, this.v1.z, this.v2.z, this.v3.z ) + ); + + } + +); + +// File:src/extras/curves/SplineCurve3.js + +/************************************************************** + * Spline 3D curve + **************************************************************/ + + +THREE.SplineCurve3 = THREE.Curve.create( + + function ( points /* array of Vector3 */ ) { + + console.warn( 'THREE.SplineCurve3 will be deprecated. Please use THREE.CatmullRomCurve3' ); + this.points = ( points == undefined ) ? [] : points; + + }, + + 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 ]; + + var interpolate = THREE.CurveUtils.interpolate; + + return new THREE.Vector3( + interpolate( point0.x, point1.x, point2.x, point3.x, weight ), + interpolate( point0.y, point1.y, point2.y, point3.y, weight ), + interpolate( point0.z, point1.z, point2.z, point3.z, weight ) + ); + + } + +); + +// File:src/extras/curves/CatmullRomCurve3.js + +/** + * @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() { + + 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 + + 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. + */ + + function CubicPoly() { + + } + + /* + * 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 ) { + + this.c0 = x0; + this.c1 = t0; + this.c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1; + this.c3 = 2 * x0 - 2 * x1 + t0 + t1; + + }; + + CubicPoly.prototype.initNonuniformCatmullRom = function( x0, x1, x2, x3, dt0, dt1, dt2 ) { + + // 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; + + // initCubicPoly + this.init( x1, x2, t1, t2 ); + + }; + + // standard Catmull-Rom spline: interpolate between x1 and x2 with previous/following points x1/x4 + CubicPoly.prototype.initCatmullRom = function( x0, x1, x2, x3, tension ) { + + this.init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) ); + + }; + + 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; + + }; + + // Subclass Three.js curve + return THREE.Curve.create( + + function ( p /* array of Vector3 */ ) { + + this.points = p || []; + this.closed = false; + + }, + + function ( t ) { + + var points = this.points, + point, intPoint, weight, l; + + l = points.length; + + if ( l < 2 ) console.log( 'duh, you need at least 2 points' ); + + point = ( l - ( this.closed ? 0 : 1 ) ) * t; + intPoint = Math.floor( point ); + weight = point - intPoint; + + if ( this.closed ) { + + intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length; + + } else if ( weight === 0 && intPoint === l - 1 ) { + + intPoint = l - 2; + weight = 1; + + } + + var p0, p1, p2, p3; // 4 points + + if ( this.closed || intPoint > 0 ) { + + p0 = points[ ( intPoint - 1 ) % l ]; + + } else { + + // extrapolate first point + tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] ); + p0 = tmp; + + } + + p1 = points[ intPoint % l ]; + p2 = points[ ( intPoint + 1 ) % l ]; + + if ( this.closed || intPoint + 2 < l ) { + + p3 = points[ ( intPoint + 2 ) % l ]; + + } else { + + // extrapolate last point + tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] ); + p3 = tmp; + + } + + if ( this.type === undefined || this.type === 'centripetal' || this.type === 'chordal' ) { + + // 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; + + 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 ); + + } else if ( this.type === 'catmullrom' ) { + + 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 ); + + } + + var v = new THREE.Vector3( + px.calc( weight ), + py.calc( weight ), + pz.calc( weight ) + ); + + return v; + + } + + ); + +} )(); + +// File:src/extras/curves/ClosedSplineCurve3.js + +/************************************************************** + * Closed Spline 3D curve + **************************************************************/ + + +THREE.ClosedSplineCurve3 = function ( points ) { + + console.warn( 'THREE.ClosedSplineCurve3 has been deprecated. Please use THREE.CatmullRomCurve3.' ); + + THREE.CatmullRomCurve3.call( this, points ); + this.type = 'catmullrom'; + this.closed = true; + +}; + +THREE.ClosedSplineCurve3.prototype = Object.create( THREE.CatmullRomCurve3.prototype ); + +// File:src/extras/geometries/BoxGeometry.js + +/** + * @author mrdoob / http://mrdoob.com/ + * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Cube.as + */ + +THREE.BoxGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) { + + THREE.Geometry.call( this ); + + this.type = 'BoxGeometry'; + + this.parameters = { + width: width, + height: height, + depth: depth, + widthSegments: widthSegments, + heightSegments: heightSegments, + depthSegments: depthSegments + }; + + this.fromBufferGeometry( new THREE.BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) ); + this.mergeVertices(); + +}; + +THREE.BoxGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.BoxGeometry.prototype.constructor = THREE.BoxGeometry; + +THREE.CubeGeometry = THREE.BoxGeometry; + +// File:src/extras/geometries/BoxBufferGeometry.js + +/** + * @author Mugen87 / https://github.com/Mugen87 + */ + +THREE.BoxBufferGeometry = function ( width, height, depth, widthSegments, heightSegments, depthSegments ) { + + THREE.BufferGeometry.call( this ); + + this.type = 'BoxBufferGeometry'; + + this.parameters = { + width: width, + height: height, + depth: depth, + widthSegments: widthSegments, + heightSegments: heightSegments, + depthSegments: depthSegments + }; + + var scope = this; + + // segments + widthSegments = Math.floor( widthSegments ) || 1; + heightSegments = Math.floor( heightSegments ) || 1; + depthSegments = Math.floor( depthSegments ) || 1; + + // these are used to calculate buffer length + var vertexCount = calculateVertexCount( widthSegments, heightSegments, depthSegments ); + var indexCount = calculateIndexCount( widthSegments, heightSegments, depthSegments ); + + // buffers + var indices = new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ); + var vertices = new Float32Array( vertexCount * 3 ); + var normals = new Float32Array( vertexCount * 3 ); + var uvs = new Float32Array( vertexCount * 2 ); + + // offset variables + var vertexBufferOffset = 0; + var uvBufferOffset = 0; + var indexBufferOffset = 0; + var numberOfVertices = 0; + + // group variables + var groupStart = 0; + + // build each side of the box geometry + buildPlane( 'z', 'y', 'x', - 1, - 1, depth, height, width, depthSegments, heightSegments, 0 ); // px + buildPlane( 'z', 'y', 'x', 1, - 1, depth, height, - width, depthSegments, heightSegments, 1 ); // nx + buildPlane( 'x', 'z', 'y', 1, 1, width, depth, height, widthSegments, depthSegments, 2 ); // py + buildPlane( 'x', 'z', 'y', 1, - 1, width, depth, - height, widthSegments, depthSegments, 3 ); // ny + buildPlane( 'x', 'y', 'z', 1, - 1, width, height, depth, widthSegments, heightSegments, 4 ); // pz + buildPlane( 'x', 'y', 'z', - 1, - 1, width, height, - depth, widthSegments, heightSegments, 5 ); // nz + + // build geometry + this.setIndex( new THREE.BufferAttribute( indices, 1 ) ); + this.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); + this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) ); + this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); + + // helper functions + + function calculateVertexCount ( w, h, d ) { + + var vertices = 0; + + // calculate the amount of vertices for each side (plane) + vertices += (w + 1) * (h + 1) * 2; // xy + vertices += (w + 1) * (d + 1) * 2; // xz + vertices += (d + 1) * (h + 1) * 2; // zy + + return vertices; + + } + + function calculateIndexCount ( w, h, d ) { + + var index = 0; - var pt_i = contour[ i ]; - var pt_j = contour[ j ]; - var pt_k = contour[ k ]; + // calculate the amount of squares for each side + index += w * h * 2; // xy + index += w * d * 2; // xz + index += d * h * 2; // zy - contourMovements[ i ]= getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); + return index * 6; // two triangles per square => six vertices per square } - var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat(); + function buildPlane ( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex ) { - for ( h = 0, hl = holes.length; h < hl; h ++ ) { + var segmentWidth = width / gridX; + var segmentHeight = height / gridY; - ahole = holes[ h ]; + var widthHalf = width / 2; + var heightHalf = height / 2; + var depthHalf = depth / 2; - oneHoleMovements = []; + var gridX1 = gridX + 1; + var gridY1 = gridY + 1; - for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { + var vertexCounter = 0; + var groupCount = 0; - if ( j === il ) j = 0; - if ( k === il ) k = 0; + var vector = new THREE.Vector3(); - // (j)---(i)---(k) - oneHoleMovements[ i ]= getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); + // generate vertices, normals and uvs - } + for ( var iy = 0; iy < gridY1; iy ++ ) { - holesMovements.push( oneHoleMovements ); - verticesMovements = verticesMovements.concat( oneHoleMovements ); + var y = iy * segmentHeight - heightHalf; - } + for ( var ix = 0; ix < gridX1; ix ++ ) { + var x = ix * segmentWidth - widthHalf; - // Loop bevelSegments, 1 for the front, 1 for the back + // set values to correct vector component + vector[ u ] = x * udir; + vector[ v ] = y * vdir; + vector[ w ] = depthHalf; - for ( b = 0; b < bevelSegments; b ++ ) { - //for ( b = bevelSegments; b > 0; b -- ) { + // now apply vector to vertex buffer + vertices[ vertexBufferOffset ] = vector.x; + vertices[ vertexBufferOffset + 1 ] = vector.y; + vertices[ vertexBufferOffset + 2 ] = vector.z; - t = b / bevelSegments; - z = bevelThickness * ( 1 - t ); + // set values to correct vector component + vector[ u ] = 0; + vector[ v ] = 0; + vector[ w ] = depth > 0 ? 1 : - 1; - //z = bevelThickness * t; - bs = bevelSize * ( Math.sin ( t * Math.PI/2 ) ) ; // curved - //bs = bevelSize * t ; // linear + // now apply vector to normal buffer + normals[ vertexBufferOffset ] = vector.x; + normals[ vertexBufferOffset + 1 ] = vector.y; + normals[ vertexBufferOffset + 2 ] = vector.z; - // contract shape + // uvs + uvs[ uvBufferOffset ] = ix / gridX; + uvs[ uvBufferOffset + 1 ] = 1 - ( iy / gridY ); - for ( i = 0, il = contour.length; i < il; i ++ ) { + // update offsets and counters + vertexBufferOffset += 3; + uvBufferOffset += 2; + vertexCounter += 1; - vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); - //vert = scalePt( contour[ i ], contourCentroid, bs, false ); - v( vert.x, vert.y, - z ); + } } - // expand holes + // 1. you need three indices to draw a single face + // 2. a single segment consists of two faces + // 3. so we need to generate six (2*3) indices per segment - for ( h = 0, hl = holes.length; h < hl; h++ ) { + for ( iy = 0; iy < gridY; iy ++ ) { - ahole = holes[ h ]; - oneHoleMovements = holesMovements[ h ]; + for ( ix = 0; ix < gridX; ix ++ ) { - for ( i = 0, il = ahole.length; i < il; i++ ) { + // indices + var a = numberOfVertices + ix + gridX1 * iy; + var b = numberOfVertices + ix + gridX1 * ( iy + 1 ); + var c = numberOfVertices + ( ix + 1 ) + gridX1 * ( iy + 1 ); + var d = numberOfVertices + ( ix + 1 ) + gridX1 * iy; - vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); - //vert = scalePt( ahole[ i ], holesCentroids[ h ], bs, true ); + // face one + indices[ indexBufferOffset ] = a; + indices[ indexBufferOffset + 1 ] = b; + indices[ indexBufferOffset + 2 ] = d; - v( vert.x, vert.y, -z ); + // face two + indices[ indexBufferOffset + 3 ] = b; + indices[ indexBufferOffset + 4 ] = c; + indices[ indexBufferOffset + 5 ] = d; + + // update offsets and counters + indexBufferOffset += 6; + groupCount += 6; } } + // add a group to the geometry. this will ensure multi material support + scope.addGroup( groupStart, groupCount, materialIndex ); + + // calculate new start value for groups + groupStart += groupCount; + + // update total number of vertices + numberOfVertices += vertexCounter; + } - bs = bevelSize; +}; - // Back facing vertices +THREE.BoxBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.BoxBufferGeometry.prototype.constructor = THREE.BoxBufferGeometry; - for ( i = 0; i < vlen; i ++ ) { +// File:src/extras/geometries/CircleGeometry.js - vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; +/** + * @author hughes + */ - if ( !extrudeByPath ) { +THREE.CircleGeometry = function ( radius, segments, thetaStart, thetaLength ) { - v( vert.x, vert.y, 0 ); + THREE.Geometry.call( this ); - } else { + this.type = 'CircleGeometry'; - // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); + this.parameters = { + radius: radius, + segments: segments, + thetaStart: thetaStart, + thetaLength: thetaLength + }; - normal.copy( splineTube.normals[0] ).multiplyScalar(vert.x); - binormal.copy( splineTube.binormals[0] ).multiplyScalar(vert.y); + this.fromBufferGeometry( new THREE.CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) ); - position2.copy( extrudePts[0] ).add(normal).add(binormal); +}; - v( position2.x, position2.y, position2.z ); +THREE.CircleGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.CircleGeometry.prototype.constructor = THREE.CircleGeometry; - } +// File:src/extras/geometries/CircleBufferGeometry.js - } +/** + * @author benaadams / https://twitter.com/ben_a_adams + */ - // Add stepped vertices... - // Including front facing vertices +THREE.CircleBufferGeometry = function ( radius, segments, thetaStart, thetaLength ) { - var s; + THREE.BufferGeometry.call( this ); - for ( s = 1; s <= steps; s ++ ) { + this.type = 'CircleBufferGeometry'; - for ( i = 0; i < vlen; i ++ ) { + this.parameters = { + radius: radius, + segments: segments, + thetaStart: thetaStart, + thetaLength: thetaLength + }; - vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; + radius = radius || 50; + segments = segments !== undefined ? Math.max( 3, segments ) : 8; - if ( !extrudeByPath ) { + thetaStart = thetaStart !== undefined ? thetaStart : 0; + thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; - v( vert.x, vert.y, amount / steps * s ); + var vertices = segments + 2; - } else { + var positions = new Float32Array( vertices * 3 ); + var normals = new Float32Array( vertices * 3 ); + var uvs = new Float32Array( vertices * 2 ); - // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); + // center data is already zero, but need to set a few extras + normals[ 2 ] = 1.0; + uvs[ 0 ] = 0.5; + uvs[ 1 ] = 0.5; - normal.copy( splineTube.normals[s] ).multiplyScalar( vert.x ); - binormal.copy( splineTube.binormals[s] ).multiplyScalar( vert.y ); + for ( var s = 0, i = 3, ii = 2 ; s <= segments; s ++, i += 3, ii += 2 ) { - position2.copy( extrudePts[s] ).add( normal ).add( binormal ); + var segment = thetaStart + s / segments * thetaLength; - v( position2.x, position2.y, position2.z ); + positions[ i ] = radius * Math.cos( segment ); + positions[ i + 1 ] = radius * Math.sin( segment ); - } + normals[ i + 2 ] = 1; // normal z - } + uvs[ ii ] = ( positions[ i ] / radius + 1 ) / 2; + uvs[ ii + 1 ] = ( positions[ i + 1 ] / radius + 1 ) / 2; } + var indices = []; - // Add bevel segments planes + for ( var i = 1; i <= segments; i ++ ) { - //for ( b = 1; b <= bevelSegments; b ++ ) { - for ( b = bevelSegments - 1; b >= 0; b -- ) { + indices.push( i, i + 1, 0 ); - t = b / bevelSegments; - z = bevelThickness * ( 1 - t ); - //bs = bevelSize * ( 1-Math.sin ( ( 1 - t ) * Math.PI/2 ) ); - bs = bevelSize * Math.sin ( t * Math.PI/2 ) ; + } - // contract shape + this.setIndex( new THREE.BufferAttribute( new Uint16Array( indices ), 1 ) ); + this.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) ); + this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) ); + this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); - for ( i = 0, il = contour.length; i < il; i ++ ) { + this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); - vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); - v( vert.x, vert.y, amount + z ); +}; - } +THREE.CircleBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.CircleBufferGeometry.prototype.constructor = THREE.CircleBufferGeometry; - // expand holes +// File:src/extras/geometries/CylinderBufferGeometry.js - for ( h = 0, hl = holes.length; h < hl; h ++ ) { +/** + * @author Mugen87 / https://github.com/Mugen87 + */ - ahole = holes[ h ]; - oneHoleMovements = holesMovements[ h ]; +THREE.CylinderBufferGeometry = function( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { - for ( i = 0, il = ahole.length; i < il; i ++ ) { + THREE.BufferGeometry.call( this ); - vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); + this.type = 'CylinderBufferGeometry'; - if ( !extrudeByPath ) { + this.parameters = { + radiusTop: radiusTop, + radiusBottom: radiusBottom, + height: height, + radialSegments: radialSegments, + heightSegments: heightSegments, + openEnded: openEnded, + thetaStart: thetaStart, + thetaLength: thetaLength + }; - v( vert.x, vert.y, amount + z ); + var scope = this; - } else { + radiusTop = radiusTop !== undefined ? radiusTop : 20; + radiusBottom = radiusBottom !== undefined ? radiusBottom : 20; + height = height !== undefined ? height : 100; - v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); + radialSegments = Math.floor( radialSegments ) || 8; + heightSegments = Math.floor( heightSegments ) || 1; - } + openEnded = openEnded !== undefined ? openEnded : false; + thetaStart = thetaStart !== undefined ? thetaStart : 0.0; + thetaLength = thetaLength !== undefined ? thetaLength : 2.0 * Math.PI; - } + // used to calculate buffer length - } + var nbCap = 0; - } + if ( openEnded === false ) { - /* Faces */ + if ( radiusTop > 0 ) nbCap ++; + if ( radiusBottom > 0 ) nbCap ++; - // Top and bottom faces + } - buildLidFaces(); + var vertexCount = calculateVertexCount(); + var indexCount = calculateIndexCount(); - // Sides faces + // buffers - buildSideFaces(); + var indices = new THREE.BufferAttribute( new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ), 1 ); + var vertices = new THREE.BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); + var normals = new THREE.BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); + var uvs = new THREE.BufferAttribute( new Float32Array( vertexCount * 2 ), 2 ); + // helper variables - ///// Internal functions + var index = 0, + indexOffset = 0, + indexArray = [], + halfHeight = height / 2; - function buildLidFaces() { + // group variables + var groupStart = 0; - if ( bevelEnabled ) { + // generate geometry - var layer = 0 ; // steps + 1 - var offset = vlen * layer; + generateTorso(); - // Bottom faces + if ( openEnded === false ) { - for ( i = 0; i < flen; i ++ ) { + if ( radiusTop > 0 ) generateCap( true ); + if ( radiusBottom > 0 ) generateCap( false ); - face = faces[ i ]; - f3( face[ 2 ]+ offset, face[ 1 ]+ offset, face[ 0 ] + offset, true ); + } - } + // build geometry - layer = steps + bevelSegments * 2; - offset = vlen * layer; + this.setIndex( indices ); + this.addAttribute( 'position', vertices ); + this.addAttribute( 'normal', normals ); + this.addAttribute( 'uv', uvs ); - // Top faces + // helper functions - for ( i = 0; i < flen; i ++ ) { + function calculateVertexCount() { - face = faces[ i ]; - f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset, false ); + var count = ( radialSegments + 1 ) * ( heightSegments + 1 ); - } + if ( openEnded === false ) { - } else { + count += ( ( radialSegments + 1 ) * nbCap ) + ( radialSegments * nbCap ); - // Bottom faces + } - for ( i = 0; i < flen; i++ ) { + return count; - face = faces[ i ]; - f3( face[ 2 ], face[ 1 ], face[ 0 ], true ); + } - } + function calculateIndexCount() { - // Top faces + var count = radialSegments * heightSegments * 2 * 3; - for ( i = 0; i < flen; i ++ ) { + if ( openEnded === false ) { - face = faces[ i ]; - f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps, false ); + count += radialSegments * nbCap * 3; - } } + return count; + } - // Create faces for the z-sides of the shape + function generateTorso() { - function buildSideFaces() { + var x, y; + var normal = new THREE.Vector3(); + var vertex = new THREE.Vector3(); - var layeroffset = 0; - sidewalls( contour, layeroffset ); - layeroffset += contour.length; + var groupCount = 0; - for ( h = 0, hl = holes.length; h < hl; h ++ ) { + // this will be used to calculate the normal + var tanTheta = ( radiusBottom - radiusTop ) / height; - ahole = holes[ h ]; - sidewalls( ahole, layeroffset ); + // generate vertices, normals and uvs - //, true - layeroffset += ahole.length; + for ( y = 0; y <= heightSegments; y ++ ) { - } + var indexRow = []; - } + var v = y / heightSegments; - function sidewalls( contour, layeroffset ) { + // calculate the radius of the current row + var radius = v * ( radiusBottom - radiusTop ) + radiusTop; - var j, k; - i = contour.length; + for ( x = 0; x <= radialSegments; x ++ ) { - while ( --i >= 0 ) { + var u = x / radialSegments; - j = i; - k = i - 1; - if ( k < 0 ) k = contour.length - 1; + // vertex + vertex.x = radius * Math.sin( u * thetaLength + thetaStart ); + vertex.y = - v * height + halfHeight; + vertex.z = radius * Math.cos( u * thetaLength + thetaStart ); + vertices.setXYZ( index, vertex.x, vertex.y, vertex.z ); - //console.log('b', i,j, i-1, k,vertices.length); + // normal + normal.copy( vertex ); - var s = 0, sl = steps + bevelSegments * 2; + // handle special case if radiusTop/radiusBottom is zero - for ( s = 0; s < sl; s ++ ) { + if ( ( radiusTop === 0 && y === 0 ) || ( radiusBottom === 0 && y === heightSegments ) ) { - var slen1 = vlen * s; - var slen2 = vlen * ( s + 1 ); + normal.x = Math.sin( u * thetaLength + thetaStart ); + normal.z = Math.cos( u * thetaLength + thetaStart ); - var a = layeroffset + j + slen1, - b = layeroffset + k + slen1, - c = layeroffset + k + slen2, - d = layeroffset + j + slen2; + } - f4( a, b, c, d, contour, s, sl, j, k ); + normal.setY( Math.sqrt( normal.x * normal.x + normal.z * normal.z ) * tanTheta ).normalize(); + normals.setXYZ( index, normal.x, normal.y, normal.z ); + + // uv + uvs.setXY( index, u, 1 - v ); + + // save index of vertex in respective row + indexRow.push( index ); + + // increase index + index ++; } + + // now save vertices of the row in our index array + indexArray.push( indexRow ); + } - } + // generate indices + for ( x = 0; x < radialSegments; x ++ ) { - function v( x, y, z ) { + for ( y = 0; y < heightSegments; y ++ ) { - scope.vertices.push( new THREE.Vector3( x, y, z ) ); + // we use the index array to access the correct indices + var i1 = indexArray[ y ][ x ]; + var i2 = indexArray[ y + 1 ][ x ]; + var i3 = indexArray[ y + 1 ][ x + 1 ]; + var i4 = indexArray[ y ][ x + 1 ]; - } + // face one + indices.setX( indexOffset, i1 ); indexOffset ++; + indices.setX( indexOffset, i2 ); indexOffset ++; + indices.setX( indexOffset, i4 ); indexOffset ++; - function f3( a, b, c, isBottom ) { + // face two + indices.setX( indexOffset, i2 ); indexOffset ++; + indices.setX( indexOffset, i3 ); indexOffset ++; + indices.setX( indexOffset, i4 ); indexOffset ++; - a += shapesOffset; - b += shapesOffset; - c += shapesOffset; + // update counters + groupCount += 6; + + } - // normal, color, material - scope.faces.push( new THREE.Face3( a, b, c, null, null, material ) ); + } - var uvs = isBottom ? uvgen.generateBottomUV( scope, shape, options, a, b, c ) : uvgen.generateTopUV( scope, shape, options, a, b, c ); + // add a group to the geometry. this will ensure multi material support + scope.addGroup( groupStart, groupCount, 0 ); - scope.faceVertexUvs[ 0 ].push( uvs ); + // calculate new start value for groups + groupStart += groupCount; } - function f4( a, b, c, d, wallContour, stepIndex, stepsLength, contourIndex1, contourIndex2 ) { + function generateCap( top ) { - a += shapesOffset; - b += shapesOffset; - c += shapesOffset; - d += shapesOffset; + var x, centerIndexStart, centerIndexEnd; - scope.faces.push( new THREE.Face3( a, b, d, null, null, extrudeMaterial ) ); - scope.faces.push( new THREE.Face3( b, c, d, null, null, extrudeMaterial ) ); + var uv = new THREE.Vector2(); + var vertex = new THREE.Vector3(); - var uvs = uvgen.generateSideWallUV( scope, shape, wallContour, options, a, b, c, d, - stepIndex, stepsLength, contourIndex1, contourIndex2 ); + var groupCount = 0; - scope.faceVertexUvs[ 0 ].push( [ uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] ] ); - scope.faceVertexUvs[ 0 ].push( [ uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] ] ); + var radius = ( top === true ) ? radiusTop : radiusBottom; + var sign = ( top === true ) ? 1 : - 1; - } + // save the index of the first center vertex + centerIndexStart = index; -}; + // first we generate the center vertex data of the cap. + // because the geometry needs one set of uvs per face, + // we must generate a center vertex per face/segment -THREE.ExtrudeGeometry.WorldUVGenerator = { + for ( x = 1; x <= radialSegments; x ++ ) { - generateTopUV: function( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC ) { - var ax = geometry.vertices[ indexA ].x, - ay = geometry.vertices[ indexA ].y, + // vertex + vertices.setXYZ( index, 0, halfHeight * sign, 0 ); - bx = geometry.vertices[ indexB ].x, - by = geometry.vertices[ indexB ].y, + // normal + normals.setXYZ( index, 0, sign, 0 ); - cx = geometry.vertices[ indexC ].x, - cy = geometry.vertices[ indexC ].y; + // uv + uv.x = 0.5; + uv.y = 0.5; - return [ - new THREE.Vector2( ax, ay ), - new THREE.Vector2( bx, by ), - new THREE.Vector2( cx, cy ) - ]; + uvs.setXY( index, uv.x, uv.y ); - }, + // increase index + index ++; - generateBottomUV: function( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC ) { + } - return this.generateTopUV( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC ); + // save the index of the last center vertex + centerIndexEnd = index; - }, + // now we generate the surrounding vertices, normals and uvs - generateSideWallUV: function( geometry, extrudedShape, wallContour, extrudeOptions, - indexA, indexB, indexC, indexD, stepIndex, stepsLength, - contourIndex1, contourIndex2 ) { + for ( x = 0; x <= radialSegments; x ++ ) { - var ax = geometry.vertices[ indexA ].x, - ay = geometry.vertices[ indexA ].y, - az = geometry.vertices[ indexA ].z, + var u = x / radialSegments; + var theta = u * thetaLength + thetaStart; - bx = geometry.vertices[ indexB ].x, - by = geometry.vertices[ indexB ].y, - bz = geometry.vertices[ indexB ].z, + var cosTheta = Math.cos( theta ); + var sinTheta = Math.sin( theta ); - cx = geometry.vertices[ indexC ].x, - cy = geometry.vertices[ indexC ].y, - cz = geometry.vertices[ indexC ].z, + // vertex + vertex.x = radius * sinTheta; + vertex.y = halfHeight * sign; + vertex.z = radius * cosTheta; + vertices.setXYZ( index, vertex.x, vertex.y, vertex.z ); - dx = geometry.vertices[ indexD ].x, - dy = geometry.vertices[ indexD ].y, - dz = geometry.vertices[ indexD ].z; + // normal + normals.setXYZ( index, 0, sign, 0 ); - if ( Math.abs( ay - by ) < 0.01 ) { - return [ - new THREE.Vector2( ax, 1 - az ), - new THREE.Vector2( bx, 1 - bz ), - new THREE.Vector2( cx, 1 - cz ), - new THREE.Vector2( dx, 1 - dz ) - ]; - } else { - return [ - new THREE.Vector2( ay, 1 - az ), - new THREE.Vector2( by, 1 - bz ), - new THREE.Vector2( cy, 1 - cz ), - new THREE.Vector2( dy, 1 - dz ) - ]; - } - } -}; + // uv + uv.x = ( cosTheta * 0.5 ) + 0.5; + uv.y = ( sinTheta * 0.5 * sign ) + 0.5; + uvs.setXY( index, uv.x, uv.y ); -THREE.ExtrudeGeometry.__v1 = new THREE.Vector2(); -THREE.ExtrudeGeometry.__v2 = new THREE.Vector2(); -THREE.ExtrudeGeometry.__v3 = new THREE.Vector2(); -THREE.ExtrudeGeometry.__v4 = new THREE.Vector2(); -THREE.ExtrudeGeometry.__v5 = new THREE.Vector2(); -THREE.ExtrudeGeometry.__v6 = new THREE.Vector2(); + // increase index + index ++; -/** - * @author jonobr1 / http://jonobr1.com - * - * Creates a one-sided polygonal geometry from a path shape. Similar to - * ExtrudeGeometry. - * - * parameters = { - * - * curveSegments: , // number of points on the curves. NOT USED AT THE MOMENT. - * - * material: // material index for front and back faces - * uvGenerator: // object that provides UV generator functions - * - * } - **/ + } -THREE.ShapeGeometry = function ( shapes, options ) { + // generate indices - THREE.Geometry.call( this ); + for ( x = 0; x < radialSegments; x ++ ) { - if ( shapes instanceof Array === false ) shapes = [ shapes ]; + var c = centerIndexStart + x; + var i = centerIndexEnd + x; - this.shapebb = shapes[ shapes.length - 1 ].getBoundingBox(); + if ( top === true ) { - this.addShapeList( shapes, options ); + // face top + indices.setX( indexOffset, i ); indexOffset ++; + indices.setX( indexOffset, i + 1 ); indexOffset ++; + indices.setX( indexOffset, c ); indexOffset ++; - this.computeCentroids(); - this.computeFaceNormals(); + } else { -}; + // face bottom + indices.setX( indexOffset, i + 1 ); indexOffset ++; + indices.setX( indexOffset, i ); indexOffset ++; + indices.setX( indexOffset, c ); indexOffset ++; -THREE.ShapeGeometry.prototype = Object.create( THREE.Geometry.prototype ); + } -/** - * Add an array of shapes to THREE.ShapeGeometry. - */ -THREE.ShapeGeometry.prototype.addShapeList = function ( shapes, options ) { + // update counters + groupCount += 3; - for ( var i = 0, l = shapes.length; i < l; i++ ) { + } - this.addShape( shapes[ i ], options ); + // add a group to the geometry. this will ensure multi material support + scope.addGroup( groupStart, groupCount, top === true ? 1 : 2 ); - } + // calculate new start value for groups + groupStart += groupCount; - return this; + } }; +THREE.CylinderBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.CylinderBufferGeometry.prototype.constructor = THREE.CylinderBufferGeometry; + +// File:src/extras/geometries/CylinderGeometry.js + /** - * Adds a shape to THREE.ShapeGeometry, based on THREE.ExtrudeGeometry. + * @author mrdoob / http://mrdoob.com/ */ -THREE.ShapeGeometry.prototype.addShape = function ( shape, options ) { - if ( options === undefined ) options = {}; - var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; +THREE.CylinderGeometry = function ( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { - var material = options.material; - var uvgen = options.UVGenerator === undefined ? THREE.ExtrudeGeometry.WorldUVGenerator : options.UVGenerator; + THREE.Geometry.call( this ); - var shapebb = this.shapebb; + this.type = 'CylinderGeometry'; + + this.parameters = { + radiusTop: radiusTop, + radiusBottom: radiusBottom, + height: height, + radialSegments: radialSegments, + heightSegments: heightSegments, + openEnded: openEnded, + thetaStart: thetaStart, + thetaLength: thetaLength + }; - // + this.fromBufferGeometry( new THREE.CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) ); + this.mergeVertices(); + +}; - var i, l, hole, s; +THREE.CylinderGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.CylinderGeometry.prototype.constructor = THREE.CylinderGeometry; - var shapesOffset = this.vertices.length; - var shapePoints = shape.extractPoints( curveSegments ); +// File:src/extras/geometries/ConeBufferGeometry.js - var vertices = shapePoints.shape; - var holes = shapePoints.holes; +/* + * @author: abelnation / http://github.com/abelnation + */ - var reverse = !THREE.Shape.Utils.isClockWise( vertices ); +THREE.ConeBufferGeometry = function ( + radius, height, + radialSegments, heightSegments, + openEnded, thetaStart, thetaLength ) { + + THREE.CylinderBufferGeometry.call( this, + 0, radius, height, + radialSegments, heightSegments, + openEnded, thetaStart, thetaLength ); + + this.type = 'ConeBufferGeometry'; + + this.parameters = { + radius: radius, + height: height, + radialSegments: radialSegments, + heightSegments: heightSegments, + thetaStart: thetaStart, + thetaLength: thetaLength + }; - if ( reverse ) { +}; - vertices = vertices.reverse(); +THREE.ConeBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.ConeBufferGeometry.prototype.constructor = THREE.ConeBufferGeometry; - // Maybe we should also check if holes are in the opposite direction, just to be safe... +// File:src/extras/geometries/ConeGeometry.js - for ( i = 0, l = holes.length; i < l; i++ ) { +/** + * @author abelnation / http://github.com/abelnation + */ - hole = holes[ i ]; +THREE.ConeGeometry = function ( + radius, height, + radialSegments, heightSegments, + openEnded, thetaStart, thetaLength ) { + + THREE.CylinderGeometry.call( this, + 0, radius, height, + radialSegments, heightSegments, + openEnded, thetaStart, thetaLength ); + + this.type = 'ConeGeometry'; + + this.parameters = { + radius: radius, + height: height, + radialSegments: radialSegments, + heightSegments: heightSegments, + openEnded: openEnded, + thetaStart: thetaStart, + thetaLength: thetaLength + }; - if ( THREE.Shape.Utils.isClockWise( hole ) ) { +}; - holes[ i ] = hole.reverse(); +THREE.ConeGeometry.prototype = Object.create( THREE.CylinderGeometry.prototype ); +THREE.ConeGeometry.prototype.constructor = THREE.ConeGeometry; - } +// File:src/extras/geometries/EdgesGeometry.js - } +/** + * @author WestLangley / http://github.com/WestLangley + */ - reverse = false; +THREE.EdgesGeometry = function ( geometry, thresholdAngle ) { - } + THREE.BufferGeometry.call( this ); - var faces = THREE.Shape.Utils.triangulateShape( vertices, holes ); + thresholdAngle = ( thresholdAngle !== undefined ) ? thresholdAngle : 1; - // Vertices + var thresholdDot = Math.cos( THREE.Math.DEG2RAD * thresholdAngle ); - var contour = vertices; + var edge = [ 0, 0 ], hash = {}; - for ( i = 0, l = holes.length; i < l; i++ ) { + function sortFunction( a, b ) { - hole = holes[ i ]; - vertices = vertices.concat( hole ); + return a - b; } - // + var keys = [ 'a', 'b', 'c' ]; - var vert, vlen = vertices.length; - var face, flen = faces.length; - var cont, clen = contour.length; + var geometry2; - for ( i = 0; i < vlen; i++ ) { + if ( geometry instanceof THREE.BufferGeometry ) { - vert = vertices[ i ]; + geometry2 = new THREE.Geometry(); + geometry2.fromBufferGeometry( geometry ); - this.vertices.push( new THREE.Vector3( vert.x, vert.y, 0 ) ); + } else { + + geometry2 = geometry.clone(); } - for ( i = 0; i < flen; i++ ) { + geometry2.mergeVertices(); + geometry2.computeFaceNormals(); - face = faces[ i ]; + var vertices = geometry2.vertices; + var faces = geometry2.faces; - var a = face[ 0 ] + shapesOffset; - var b = face[ 1 ] + shapesOffset; - var c = face[ 2 ] + shapesOffset; + for ( var i = 0, l = faces.length; i < l; i ++ ) { - this.faces.push( new THREE.Face3( a, b, c, null, null, material ) ); - this.faceVertexUvs[ 0 ].push( uvgen.generateBottomUV( this, shape, options, a, b, c ) ); + var face = faces[ i ]; - } + for ( var j = 0; j < 3; j ++ ) { -}; + edge[ 0 ] = face[ keys[ j ] ]; + edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; + edge.sort( sortFunction ); -/** - * @author astrodud / http://astrodud.isgreat.org/ - * @author zz85 / https://github.com/zz85 - * @author bhouston / http://exocortex.com - */ + var key = edge.toString(); -// points - to create a closed torus, one must use a set of points -// like so: [ a, b, c, d, a ], see first is the same as last. -// segments - the number of circumference segments to create -// phiStart - the starting radian -// phiLength - the radian (0 to 2*PI) range of the lathed section -// 2*pi is a closed lathe, less than 2PI is a portion. -THREE.LatheGeometry = function ( points, segments, phiStart, phiLength ) { + if ( hash[ key ] === undefined ) { - THREE.Geometry.call( this ); + hash[ key ] = { vert1: edge[ 0 ], vert2: edge[ 1 ], face1: i, face2: undefined }; - segments = segments || 12; - phiStart = phiStart || 0; - phiLength = phiLength || 2 * Math.PI; + } else { - var inversePointLength = 1.0 / ( points.length - 1 ); - var inverseSegments = 1.0 / segments; + hash[ key ].face2 = i; - for ( var i = 0, il = segments; i <= il; i ++ ) { + } - var phi = phiStart + i * inverseSegments * phiLength; + } - var c = Math.cos( phi ), - s = Math.sin( phi ); + } + + var coords = []; - for ( var j = 0, jl = points.length; j < jl; j ++ ) { + for ( var key in hash ) { - var pt = points[ j ]; + var h = hash[ key ]; - var vertex = new THREE.Vector3(); + if ( h.face2 === undefined || faces[ h.face1 ].normal.dot( faces[ h.face2 ].normal ) <= thresholdDot ) { - vertex.x = c * pt.x - s * pt.y; - vertex.y = s * pt.x + c * pt.y; - vertex.z = pt.z; + var vertex = vertices[ h.vert1 ]; + coords.push( vertex.x ); + coords.push( vertex.y ); + coords.push( vertex.z ); - this.vertices.push( vertex ); + vertex = vertices[ h.vert2 ]; + coords.push( vertex.x ); + coords.push( vertex.y ); + coords.push( vertex.z ); } } - var np = points.length; + this.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( coords ), 3 ) ); - for ( var i = 0, il = segments; i < il; i ++ ) { +}; - for ( var j = 0, jl = points.length - 1; j < jl; j ++ ) { +THREE.EdgesGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.EdgesGeometry.prototype.constructor = THREE.EdgesGeometry; - var base = j + np * i; - var a = base; - var b = base + np; - var c = base + 1 + np; - var d = base + 1; +// File:src/extras/geometries/ExtrudeGeometry.js - var u0 = i * inverseSegments; - var v0 = j * inversePointLength; - var u1 = u0 + inverseSegments; - var v1 = v0 + inversePointLength; +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * + * Creates extruded geometry from a path shape. + * + * parameters = { + * + * curveSegments: , // number of points on the curves + * steps: , // number of points for z-side extrusions / used for subdividing segments of extrude spline too + * amount: , // Depth to extrude the shape + * + * bevelEnabled: , // turn on bevel + * bevelThickness: , // how deep into the original shape bevel goes + * bevelSize: , // how far from shape outline is bevel + * bevelSegments: , // number of bevel layers + * + * extrudePath: // 3d spline path to extrude shape along. (creates Frames if .frames aren't defined) + * frames: // containing arrays of tangents, normals, binormals + * + * uvGenerator: // object that provides UV generator functions + * + * } + **/ - this.faces.push( new THREE.Face3( a, b, d ) ); +THREE.ExtrudeGeometry = function ( shapes, options ) { - this.faceVertexUvs[ 0 ].push( [ + if ( typeof( shapes ) === "undefined" ) { - new THREE.Vector2( u0, v0 ), - new THREE.Vector2( u1, v0 ), - new THREE.Vector2( u0, v1 ) + shapes = []; + return; - ] ); + } - this.faces.push( new THREE.Face3( b, c, d ) ); + THREE.Geometry.call( this ); - this.faceVertexUvs[ 0 ].push( [ + this.type = 'ExtrudeGeometry'; - new THREE.Vector2( u1, v0 ), - new THREE.Vector2( u1, v1 ), - new THREE.Vector2( u0, v1 ) + shapes = Array.isArray( shapes ) ? shapes : [ shapes ]; - ] ); + this.addShapeList( shapes, options ); + this.computeFaceNormals(); - } + // can't really use automatic vertex normals + // as then front and back sides get smoothed too + // should do separate smoothing just for sides - } + //this.computeVertexNormals(); - this.mergeVertices(); - this.computeCentroids(); - this.computeFaceNormals(); - this.computeVertexNormals(); + //console.log( "took", ( Date.now() - startTime ) ); }; -THREE.LatheGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.ExtrudeGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.ExtrudeGeometry.prototype.constructor = THREE.ExtrudeGeometry; -/** - * @author mrdoob / http://mrdoob.com/ - * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as - */ +THREE.ExtrudeGeometry.prototype.addShapeList = function ( shapes, options ) { -THREE.PlaneGeometry = function ( width, height, widthSegments, heightSegments ) { + var sl = shapes.length; - THREE.Geometry.call( this ); + for ( var s = 0; s < sl; s ++ ) { - this.width = width; - this.height = height; + var shape = shapes[ s ]; + this.addShape( shape, options ); + + } - this.widthSegments = widthSegments || 1; - this.heightSegments = heightSegments || 1; +}; - var ix, iz; - var width_half = width / 2; - var height_half = height / 2; +THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { - var gridX = this.widthSegments; - var gridZ = this.heightSegments; + var amount = options.amount !== undefined ? options.amount : 100; - var gridX1 = gridX + 1; - var gridZ1 = gridZ + 1; + var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10 + var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8 + var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; - var segment_width = this.width / gridX; - var segment_height = this.height / gridZ; + var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false - var normal = new THREE.Vector3( 0, 0, 1 ); + var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; - for ( iz = 0; iz < gridZ1; iz ++ ) { + var steps = options.steps !== undefined ? options.steps : 1; - for ( ix = 0; ix < gridX1; ix ++ ) { + var extrudePath = options.extrudePath; + var extrudePts, extrudeByPath = false; - var x = ix * segment_width - width_half; - var y = iz * segment_height - height_half; + // Use default WorldUVGenerator if no UV generators are specified. + var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : THREE.ExtrudeGeometry.WorldUVGenerator; - this.vertices.push( new THREE.Vector3( x, - y, 0 ) ); + var splineTube, binormal, normal, position2; + if ( extrudePath ) { - } + extrudePts = extrudePath.getSpacedPoints( steps ); - } + extrudeByPath = true; + bevelEnabled = false; // bevels not supported for path extrusion - for ( iz = 0; iz < gridZ; iz ++ ) { + // SETUP TNB variables - for ( ix = 0; ix < gridX; ix ++ ) { + // Reuse TNB from TubeGeomtry for now. + // TODO1 - have a .isClosed in spline? - var a = ix + gridX1 * iz; - var b = ix + gridX1 * ( iz + 1 ); - var c = ( ix + 1 ) + gridX1 * ( iz + 1 ); - var d = ( ix + 1 ) + gridX1 * iz; + splineTube = options.frames !== undefined ? options.frames : new THREE.TubeGeometry.FrenetFrames( extrudePath, steps, false ); - var uva = new THREE.Vector2( ix / gridX, 1 - iz / gridZ ); - var uvb = new THREE.Vector2( ix / gridX, 1 - ( iz + 1 ) / gridZ ); - var uvc = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - ( iz + 1 ) / gridZ ); - var uvd = new THREE.Vector2( ( ix + 1 ) / gridX, 1 - iz / gridZ ); + // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); - var face = new THREE.Face3( a, b, d ); - face.normal.copy( normal ); - face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() ); + binormal = new THREE.Vector3(); + normal = new THREE.Vector3(); + position2 = new THREE.Vector3(); - this.faces.push( face ); - this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); + } - face = new THREE.Face3( b, c, d ); - face.normal.copy( normal ); - face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone() ); + // Safeguards if bevels are not enabled - this.faces.push( face ); - this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); + if ( ! bevelEnabled ) { - } + bevelSegments = 0; + bevelThickness = 0; + bevelSize = 0; } - this.computeCentroids(); + // Variables initialization -}; + var ahole, h, hl; // looping of holes + var scope = this; -THREE.PlaneGeometry.prototype = Object.create( THREE.Geometry.prototype ); + var shapesOffset = this.vertices.length; -/** - * @author Kaleb Murphy - */ + var shapePoints = shape.extractPoints( curveSegments ); -THREE.RingGeometry = function ( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { + var vertices = shapePoints.shape; + var holes = shapePoints.holes; - THREE.Geometry.call( this ); + var reverse = ! THREE.ShapeUtils.isClockWise( vertices ); - innerRadius = innerRadius || 0; - outerRadius = outerRadius || 50; + if ( reverse ) { - thetaStart = thetaStart !== undefined ? thetaStart : 0; - thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; + vertices = vertices.reverse(); - thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8; - phiSegments = phiSegments !== undefined ? Math.max( 3, phiSegments ) : 8; + // Maybe we should also check if holes are in the opposite direction, just to be safe ... - var i, o, uvs = [], radius = innerRadius, radiusStep = ( ( outerRadius - innerRadius ) / phiSegments ); + for ( h = 0, hl = holes.length; h < hl; h ++ ) { - for ( i = 0; i <= phiSegments; i ++ ) { // concentric circles inside ring + ahole = holes[ h ]; - for ( o = 0; o <= thetaSegments; o ++ ) { // number of segments per circle + if ( THREE.ShapeUtils.isClockWise( ahole ) ) { - var vertex = new THREE.Vector3(); - var segment = thetaStart + o / thetaSegments * thetaLength; + holes[ h ] = ahole.reverse(); - vertex.x = radius * Math.cos( segment ); - vertex.y = radius * Math.sin( segment ); + } - this.vertices.push( vertex ); - uvs.push( new THREE.Vector2( ( vertex.x / radius + 1 ) / 2, - ( vertex.y / radius + 1 ) / 2 + 1 ) ); } - radius += radiusStep; + reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)! } - var n = new THREE.Vector3( 0, 0, 1 ); - for ( i = 0; i < phiSegments; i ++ ) { // concentric circles inside ring + var faces = THREE.ShapeUtils.triangulateShape( vertices, holes ); - var thetaSegment = i * thetaSegments; + /* Vertices */ - for ( o = 0; o <= thetaSegments; o ++ ) { // number of segments per circle + var contour = vertices; // vertices has all points but contour has only points of circumference - var segment = o + thetaSegment; + for ( h = 0, hl = holes.length; h < hl; h ++ ) { - var v1 = segment + i; - var v2 = segment + thetaSegments + i; - var v3 = segment + thetaSegments + 1 + i; + ahole = holes[ h ]; - this.faces.push( new THREE.Face3( v1, v2, v3, [ n, n, n ] ) ); - this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ], uvs[ v2 ], uvs[ v3 ] ]); + vertices = vertices.concat( ahole ); - v1 = segment + i; - v2 = segment + thetaSegments + 1 + i; - v3 = segment + 1 + i; + } - this.faces.push( new THREE.Face3( v1, v2, v3, [ n, n, n ] ) ); - this.faceVertexUvs[ 0 ].push( [ uvs[ v1 ], uvs[ v2 ], uvs[ v3 ] ]); - } + function scalePt2 ( pt, vec, size ) { + + if ( ! vec ) console.error( "THREE.ExtrudeGeometry: vec does not exist" ); + + return vec.clone().multiplyScalar( size ).add( pt ); + } - this.computeCentroids(); - this.computeFaceNormals(); + var b, bs, t, z, + vert, vlen = vertices.length, + face, flen = faces.length; - this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); -}; + // Find directions for point movement -THREE.RingGeometry.prototype = Object.create( THREE.Geometry.prototype ); -/** - * @author mrdoob / http://mrdoob.com/ - */ + function getBevelVec( inPt, inPrev, inNext ) { -THREE.SphereGeometry = function ( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { + // computes for inPt the corresponding point inPt' on a new contour + // shifted by 1 unit (length of normalized vector) to the left + // if we walk along contour clockwise, this new contour is outside the old one + // + // inPt' is the intersection of the two lines parallel to the two + // adjacent edges of inPt at a distance of 1 unit on the left side. - THREE.Geometry.call( this ); + var v_trans_x, v_trans_y, shrink_by = 1; // resulting translation vector for inPt - this.radius = radius = radius || 50; + // good reading for geometry algorithms (here: line-line intersection) + // http://geomalgorithms.com/a05-_intersect-1.html - this.widthSegments = widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 ); - this.heightSegments = heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 ); + var v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y; + var v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y; - this.phiStart = phiStart = phiStart !== undefined ? phiStart : 0; - this.phiLength = phiLength = phiLength !== undefined ? phiLength : Math.PI * 2; + var v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); - this.thetaStart = thetaStart = thetaStart !== undefined ? thetaStart : 0; - this.thetaLength = thetaLength = thetaLength !== undefined ? thetaLength : Math.PI; + // check for collinear edges + var collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); - var x, y, vertices = [], uvs = []; + if ( Math.abs( collinear0 ) > Number.EPSILON ) { - for ( y = 0; y <= heightSegments; y ++ ) { + // not collinear - var verticesRow = []; - var uvsRow = []; + // length of vectors for normalizing - for ( x = 0; x <= widthSegments; x ++ ) { + var v_prev_len = Math.sqrt( v_prev_lensq ); + var v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); - var u = x / widthSegments; - var v = y / heightSegments; + // shift adjacent points by unit vectors to the left - var vertex = new THREE.Vector3(); - vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); - vertex.y = radius * Math.cos( thetaStart + v * thetaLength ); - vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); + var ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); + var ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); - this.vertices.push( vertex ); + var ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); + var ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); - verticesRow.push( this.vertices.length - 1 ); - uvsRow.push( new THREE.Vector2( u, 1 - v ) ); + // scaling factor for v_prev to intersection point - } + var sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y - + ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) / + ( v_prev_x * v_next_y - v_prev_y * v_next_x ); - vertices.push( verticesRow ); - uvs.push( uvsRow ); + // vector from inPt to intersection point - } + v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); + v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); - for ( y = 0; y < this.heightSegments; y ++ ) { + // Don't normalize!, otherwise sharp corners become ugly + // but prevent crazy spikes + var v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ); + if ( v_trans_lensq <= 2 ) { - for ( x = 0; x < this.widthSegments; x ++ ) { + return new THREE.Vector2( v_trans_x, v_trans_y ); - var v1 = vertices[ y ][ x + 1 ]; - var v2 = vertices[ y ][ x ]; - var v3 = vertices[ y + 1 ][ x ]; - var v4 = vertices[ y + 1 ][ x + 1 ]; + } else { - var n1 = this.vertices[ v1 ].clone().normalize(); - var n2 = this.vertices[ v2 ].clone().normalize(); - var n3 = this.vertices[ v3 ].clone().normalize(); - var n4 = this.vertices[ v4 ].clone().normalize(); + shrink_by = Math.sqrt( v_trans_lensq / 2 ); - var uv1 = uvs[ y ][ x + 1 ].clone(); - var uv2 = uvs[ y ][ x ].clone(); - var uv3 = uvs[ y + 1 ][ x ].clone(); - var uv4 = uvs[ y + 1 ][ x + 1 ].clone(); + } - if ( Math.abs( this.vertices[ v1 ].y ) === this.radius ) { + } else { - this.faces.push( new THREE.Face3( v1, v3, v4, [ n1, n3, n4 ] ) ); - this.faceVertexUvs[ 0 ].push( [ uv1, uv3, uv4 ] ); + // handle special case of collinear edges - } else if ( Math.abs( this.vertices[ v3 ].y ) === this.radius ) { + var direction_eq = false; // assumes: opposite + if ( v_prev_x > Number.EPSILON ) { - this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) ); - this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] ); + if ( v_next_x > Number.EPSILON ) { - } else { + direction_eq = true; - this.faces.push( new THREE.Face3( v1, v2, v4, [ n1, n2, n4 ] ) ); - this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv4 ] ); + } - this.faces.push( new THREE.Face3( v2, v3, v4, [ n2, n3, n4 ] ) ); - this.faceVertexUvs[ 0 ].push( [ uv2.clone(), uv3, uv4.clone() ] ); + } else { - } + if ( v_prev_x < - Number.EPSILON ) { - } + if ( v_next_x < - Number.EPSILON ) { - } + direction_eq = true; - this.computeCentroids(); - this.computeFaceNormals(); + } - this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); + } else { -}; + if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) { -THREE.SphereGeometry.prototype = Object.create( THREE.Geometry.prototype ); + direction_eq = true; -/** - * @author zz85 / http://www.lab4games.net/zz85/blog - * @author alteredq / http://alteredqualia.com/ - * - * For creating 3D text geometry in three.js - * - * Text = 3D Text - * - * parameters = { - * size: , // size of the text - * height: , // thickness to extrude text - * curveSegments: , // number of points on the curves - * - * font: , // font name - * weight: , // font weight (normal, bold) - * style: , // font style (normal, italics) - * - * bevelEnabled: , // turn on bevel - * bevelThickness: , // how deep into text bevel goes - * bevelSize: , // how far from text outline is bevel - * } - * - */ + } -/* Usage Examples + } - // TextGeometry wrapper + } - var text3d = new TextGeometry( text, options ); + if ( direction_eq ) { - // Complete manner + // console.log("Warning: lines are a straight sequence"); + v_trans_x = - v_prev_y; + v_trans_y = v_prev_x; + shrink_by = Math.sqrt( v_prev_lensq ); - var textShapes = THREE.FontUtils.generateShapes( text, options ); - var text3d = new ExtrudeGeometry( textShapes, options ); + } else { -*/ + // console.log("Warning: lines are a straight spike"); + v_trans_x = v_prev_x; + v_trans_y = v_prev_y; + shrink_by = Math.sqrt( v_prev_lensq / 2 ); + } -THREE.TextGeometry = function ( text, parameters ) { + } - parameters = parameters || {}; + return new THREE.Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); - var textShapes = THREE.FontUtils.generateShapes( text, parameters ); + } - // translate parameters to ExtrudeGeometry API - parameters.amount = parameters.height !== undefined ? parameters.height : 50; + var contourMovements = []; - // defaults + for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { - if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10; - if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8; - if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false; + if ( j === il ) j = 0; + if ( k === il ) k = 0; - THREE.ExtrudeGeometry.call( this, textShapes, parameters ); + // (j)---(i)---(k) + // console.log('i,j,k', i, j , k) -}; + contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); -THREE.TextGeometry.prototype = Object.create( THREE.ExtrudeGeometry.prototype ); + } -/** - * @author oosmoxiecode - * @author mrdoob / http://mrdoob.com/ - * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3DLite/src/away3dlite/primitives/Torus.as?r=2888 - */ + var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat(); -THREE.TorusGeometry = function ( radius, tube, radialSegments, tubularSegments, arc ) { + for ( h = 0, hl = holes.length; h < hl; h ++ ) { - THREE.Geometry.call( this ); + ahole = holes[ h ]; - var scope = this; + oneHoleMovements = []; - this.radius = radius || 100; - this.tube = tube || 40; - this.radialSegments = radialSegments || 8; - this.tubularSegments = tubularSegments || 6; - this.arc = arc || Math.PI * 2; + for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { - var center = new THREE.Vector3(), uvs = [], normals = []; + if ( j === il ) j = 0; + if ( k === il ) k = 0; - for ( var j = 0; j <= this.radialSegments; j ++ ) { + // (j)---(i)---(k) + oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); - for ( var i = 0; i <= this.tubularSegments; i ++ ) { + } - var u = i / this.tubularSegments * this.arc; - var v = j / this.radialSegments * Math.PI * 2; + holesMovements.push( oneHoleMovements ); + verticesMovements = verticesMovements.concat( oneHoleMovements ); - center.x = this.radius * Math.cos( u ); - center.y = this.radius * Math.sin( u ); + } - var vertex = new THREE.Vector3(); - vertex.x = ( this.radius + this.tube * Math.cos( v ) ) * Math.cos( u ); - vertex.y = ( this.radius + this.tube * Math.cos( v ) ) * Math.sin( u ); - vertex.z = this.tube * Math.sin( v ); - this.vertices.push( vertex ); + // Loop bevelSegments, 1 for the front, 1 for the back - uvs.push( new THREE.Vector2( i / this.tubularSegments, j / this.radialSegments ) ); - normals.push( vertex.clone().sub( center ).normalize() ); + for ( b = 0; b < bevelSegments; b ++ ) { - } - } + //for ( b = bevelSegments; b > 0; b -- ) { + t = b / bevelSegments; + z = bevelThickness * ( 1 - t ); - for ( var j = 1; j <= this.radialSegments; j ++ ) { + //z = bevelThickness * t; + bs = bevelSize * ( Math.sin ( t * Math.PI / 2 ) ); // curved + //bs = bevelSize * t; // linear - for ( var i = 1; i <= this.tubularSegments; i ++ ) { + // contract shape - var a = ( this.tubularSegments + 1 ) * j + i - 1; - var b = ( this.tubularSegments + 1 ) * ( j - 1 ) + i - 1; - var c = ( this.tubularSegments + 1 ) * ( j - 1 ) + i; - var d = ( this.tubularSegments + 1 ) * j + i; + for ( i = 0, il = contour.length; i < il; i ++ ) { - var face = new THREE.Face3( a, b, d, [ normals[ a ], normals[ b ], normals[ d ] ] ); - face.normal.add( normals[ a ] ); - face.normal.add( normals[ b ] ); - face.normal.add( normals[ d ] ); - face.normal.normalize(); + vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); - this.faces.push( face ); + v( vert.x, vert.y, - z ); - this.faceVertexUvs[ 0 ].push( [ uvs[ a ].clone(), uvs[ b ].clone(), uvs[ d ].clone() ] ); + } - face = new THREE.Face3( b, c, d, [ normals[ b ], normals[ c ], normals[ d ] ] ); - face.normal.add( normals[ b ] ); - face.normal.add( normals[ c ] ); - face.normal.add( normals[ d ] ); - face.normal.normalize(); + // expand holes - this.faces.push( face ); + for ( h = 0, hl = holes.length; h < hl; h ++ ) { - this.faceVertexUvs[ 0 ].push( [ uvs[ b ].clone(), uvs[ c ].clone(), uvs[ d ].clone() ] ); - } + ahole = holes[ h ]; + oneHoleMovements = holesMovements[ h ]; - } + for ( i = 0, il = ahole.length; i < il; i ++ ) { - this.computeCentroids(); + vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); -}; + v( vert.x, vert.y, - z ); -THREE.TorusGeometry.prototype = Object.create( THREE.Geometry.prototype ); + } -/** - * @author oosmoxiecode - * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3D/src/away3d/primitives/TorusKnot.as?spec=svn2473&r=2473 - */ + } -THREE.TorusKnotGeometry = function ( radius, tube, radialSegments, tubularSegments, p, q, heightScale ) { + } - THREE.Geometry.call( this ); + bs = bevelSize; - var scope = this; + // Back facing vertices - this.radius = radius || 100; - this.tube = tube || 40; - this.radialSegments = radialSegments || 64; - this.tubularSegments = tubularSegments || 8; - this.p = p || 2; - this.q = q || 3; - this.heightScale = heightScale || 1; - this.grid = new Array( this.radialSegments ); + for ( i = 0; i < vlen; i ++ ) { - var tang = new THREE.Vector3(); - var n = new THREE.Vector3(); - var bitan = new THREE.Vector3(); + vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; - for ( var i = 0; i < this.radialSegments; ++ i ) { + if ( ! extrudeByPath ) { - this.grid[ i ] = new Array( this.tubularSegments ); - var u = i / this.radialSegments * 2 * this.p * Math.PI; - var p1 = getPos( u, this.q, this.p, this.radius, this.heightScale ); - var p2 = getPos( u + 0.01, this.q, this.p, this.radius, this.heightScale ); - tang.subVectors( p2, p1 ); - n.addVectors( p2, p1 ); + v( vert.x, vert.y, 0 ); - bitan.crossVectors( tang, n ); - n.crossVectors( bitan, tang ); - bitan.normalize(); - n.normalize(); + } else { - for ( var j = 0; j < this.tubularSegments; ++ j ) { + // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); - var v = j / this.tubularSegments * 2 * Math.PI; - var cx = - this.tube * Math.cos( v ); // TODO: Hack: Negating it so it faces outside. - var cy = this.tube * Math.sin( v ); + normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x ); + binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y ); - var pos = new THREE.Vector3(); - pos.x = p1.x + cx * n.x + cy * bitan.x; - pos.y = p1.y + cx * n.y + cy * bitan.y; - pos.z = p1.z + cx * n.z + cy * bitan.z; + position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal ); - this.grid[ i ][ j ] = scope.vertices.push( pos ) - 1; + v( position2.x, position2.y, position2.z ); } } - for ( var i = 0; i < this.radialSegments; ++ i ) { + // Add stepped vertices... + // Including front facing vertices - for ( var j = 0; j < this.tubularSegments; ++ j ) { + var s; - var ip = ( i + 1 ) % this.radialSegments; - var jp = ( j + 1 ) % this.tubularSegments; + for ( s = 1; s <= steps; s ++ ) { - var a = this.grid[ i ][ j ]; - var b = this.grid[ ip ][ j ]; - var c = this.grid[ ip ][ jp ]; - var d = this.grid[ i ][ jp ]; + for ( i = 0; i < vlen; i ++ ) { - var uva = new THREE.Vector2( i / this.radialSegments, j / this.tubularSegments ); - var uvb = new THREE.Vector2( ( i + 1 ) / this.radialSegments, j / this.tubularSegments ); - var uvc = new THREE.Vector2( ( i + 1 ) / this.radialSegments, ( j + 1 ) / this.tubularSegments ); - var uvd = new THREE.Vector2( i / this.radialSegments, ( j + 1 ) / this.tubularSegments ); + vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; - this.faces.push( new THREE.Face3( a, b, d ) ); - this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); + if ( ! extrudeByPath ) { - this.faces.push( new THREE.Face3( b, c, d ) ); - this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); + v( vert.x, vert.y, amount / steps * s ); - } - } + } else { - this.computeCentroids(); - this.computeFaceNormals(); - this.computeVertexNormals(); + // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); - function getPos( u, in_q, in_p, radius, heightScale ) { + normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x ); + binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y ); - var cu = Math.cos( u ); - var su = Math.sin( u ); - var quOverP = in_q / in_p * u; - var cs = Math.cos( quOverP ); + position2.copy( extrudePts[ s ] ).add( normal ).add( binormal ); + + v( position2.x, position2.y, position2.z ); - var tx = radius * ( 2 + cs ) * 0.5 * cu; - var ty = radius * ( 2 + cs ) * su * 0.5; - var tz = heightScale * radius * Math.sin( quOverP ) * 0.5; + } - return new THREE.Vector3( tx, ty, tz ); + } } -}; -THREE.TorusKnotGeometry.prototype = Object.create( THREE.Geometry.prototype ); + // Add bevel segments planes -/** - * @author WestLangley / https://github.com/WestLangley - * @author zz85 / https://github.com/zz85 - * @author miningold / https://github.com/miningold - * - * Modified from the TorusKnotGeometry by @oosmoxiecode - * - * Creates a tube which extrudes along a 3d spline - * - * Uses parallel transport frames as described in - * http://www.cs.indiana.edu/pub/techreports/TR425.pdf - */ + //for ( b = 1; b <= bevelSegments; b ++ ) { + for ( b = bevelSegments - 1; b >= 0; b -- ) { -THREE.TubeGeometry = function( path, segments, radius, radialSegments, closed ) { + t = b / bevelSegments; + z = bevelThickness * ( 1 - t ); + //bs = bevelSize * ( 1-Math.sin ( ( 1 - t ) * Math.PI/2 ) ); + bs = bevelSize * Math.sin ( t * Math.PI / 2 ); - THREE.Geometry.call( this ); + // contract shape - this.path = path; - this.segments = segments || 64; - this.radius = radius || 1; - this.radialSegments = radialSegments || 8; - this.closed = closed || false; + for ( i = 0, il = contour.length; i < il; i ++ ) { - this.grid = []; + vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); + v( vert.x, vert.y, amount + z ); - var scope = this, + } - tangent, - normal, - binormal, + // expand holes - numpoints = this.segments + 1, + for ( h = 0, hl = holes.length; h < hl; h ++ ) { - x, y, z, - tx, ty, tz, - u, v, + ahole = holes[ h ]; + oneHoleMovements = holesMovements[ h ]; - cx, cy, - pos, pos2 = new THREE.Vector3(), - i, j, - ip, jp, - a, b, c, d, - uva, uvb, uvc, uvd; + for ( i = 0, il = ahole.length; i < il; i ++ ) { - var frames = new THREE.TubeGeometry.FrenetFrames( this.path, this.segments, this.closed ), - tangents = frames.tangents, - normals = frames.normals, - binormals = frames.binormals; + vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); - // proxy internals - this.tangents = tangents; - this.normals = normals; - this.binormals = binormals; + if ( ! extrudeByPath ) { - function vert( x, y, z ) { + v( vert.x, vert.y, amount + z ); - return scope.vertices.push( new THREE.Vector3( x, y, z ) ) - 1; + } else { + + v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); + + } + + } + + } } + /* Faces */ - // consruct the grid + // Top and bottom faces - for ( i = 0; i < numpoints; i++ ) { + buildLidFaces(); - this.grid[ i ] = []; + // Sides faces - u = i / ( numpoints - 1 ); + buildSideFaces(); - pos = path.getPointAt( u ); - tangent = tangents[ i ]; - normal = normals[ i ]; - binormal = binormals[ i ]; + ///// Internal functions - for ( j = 0; j < this.radialSegments; j++ ) { + function buildLidFaces() { - v = j / this.radialSegments * 2 * Math.PI; + if ( bevelEnabled ) { - cx = -this.radius * Math.cos( v ); // TODO: Hack: Negating it so it faces outside. - cy = this.radius * Math.sin( v ); + var layer = 0; // steps + 1 + var offset = vlen * layer; - pos2.copy( pos ); - pos2.x += cx * normal.x + cy * binormal.x; - pos2.y += cx * normal.y + cy * binormal.y; - pos2.z += cx * normal.z + cy * binormal.z; + // Bottom faces - this.grid[ i ][ j ] = vert( pos2.x, pos2.y, pos2.z ); + for ( i = 0; i < flen; i ++ ) { - } - } + face = faces[ i ]; + f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset ); + } - // construct the mesh + layer = steps + bevelSegments * 2; + offset = vlen * layer; - for ( i = 0; i < this.segments; i++ ) { + // Top faces + + for ( i = 0; i < flen; i ++ ) { - for ( j = 0; j < this.radialSegments; j++ ) { + face = faces[ i ]; + f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset ); - ip = ( this.closed ) ? (i + 1) % this.segments : i + 1; - jp = (j + 1) % this.radialSegments; + } - a = this.grid[ i ][ j ]; // *** NOT NECESSARILY PLANAR ! *** - b = this.grid[ ip ][ j ]; - c = this.grid[ ip ][ jp ]; - d = this.grid[ i ][ jp ]; + } else { - uva = new THREE.Vector2( i / this.segments, j / this.radialSegments ); - uvb = new THREE.Vector2( ( i + 1 ) / this.segments, j / this.radialSegments ); - uvc = new THREE.Vector2( ( i + 1 ) / this.segments, ( j + 1 ) / this.radialSegments ); - uvd = new THREE.Vector2( i / this.segments, ( j + 1 ) / this.radialSegments ); + // Bottom faces - this.faces.push( new THREE.Face3( a, b, d ) ); - this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); + for ( i = 0; i < flen; i ++ ) { - this.faces.push( new THREE.Face3( b, c, d ) ); - this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); + face = faces[ i ]; + f3( face[ 2 ], face[ 1 ], face[ 0 ] ); + + } + + // Top faces + + for ( i = 0; i < flen; i ++ ) { + + face = faces[ i ]; + f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); + + } } + } - this.computeCentroids(); - this.computeFaceNormals(); - this.computeVertexNormals(); + // Create faces for the z-sides of the shape -}; + function buildSideFaces() { -THREE.TubeGeometry.prototype = Object.create( THREE.Geometry.prototype ); + var layeroffset = 0; + sidewalls( contour, layeroffset ); + layeroffset += contour.length; + for ( h = 0, hl = holes.length; h < hl; h ++ ) { -// For computing of Frenet frames, exposing the tangents, normals and binormals the spline -THREE.TubeGeometry.FrenetFrames = function(path, segments, closed) { + ahole = holes[ h ]; + sidewalls( ahole, layeroffset ); - var tangent = new THREE.Vector3(), - normal = new THREE.Vector3(), - binormal = new THREE.Vector3(), + //, true + layeroffset += ahole.length; - tangents = [], - normals = [], - binormals = [], + } - vec = new THREE.Vector3(), - mat = new THREE.Matrix4(), + } - numpoints = segments + 1, - theta, - epsilon = 0.0001, - smallest, + function sidewalls( contour, layeroffset ) { - tx, ty, tz, - i, u, v; + var j, k; + i = contour.length; + while ( -- i >= 0 ) { - // expose internals - this.tangents = tangents; - this.normals = normals; - this.binormals = binormals; + j = i; + k = i - 1; + if ( k < 0 ) k = contour.length - 1; - // compute the tangent vectors for each segment on the path + //console.log('b', i,j, i-1, k,vertices.length); - for ( i = 0; i < numpoints; i++ ) { + var s = 0, sl = steps + bevelSegments * 2; - u = i / ( numpoints - 1 ); + for ( s = 0; s < sl; s ++ ) { - tangents[ i ] = path.getTangentAt( u ); - tangents[ i ].normalize(); + var slen1 = vlen * s; + var slen2 = vlen * ( s + 1 ); - } + var a = layeroffset + j + slen1, + b = layeroffset + k + slen1, + c = layeroffset + k + slen2, + d = layeroffset + j + slen2; - initialNormal3(); + f4( a, b, c, d, contour, s, sl, j, k ); - function initialNormal1(lastBinormal) { - // fixed start binormal. Has dangers of 0 vectors - normals[ 0 ] = new THREE.Vector3(); - binormals[ 0 ] = new THREE.Vector3(); - if (lastBinormal===undefined) lastBinormal = new THREE.Vector3( 0, 0, 1 ); - normals[ 0 ].crossVectors( lastBinormal, tangents[ 0 ] ).normalize(); - binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize(); - } + } - function initialNormal2() { + } - // This uses the Frenet-Serret formula for deriving binormal - var t2 = path.getTangentAt( epsilon ); + } - normals[ 0 ] = new THREE.Vector3().subVectors( t2, tangents[ 0 ] ).normalize(); - binormals[ 0 ] = new THREE.Vector3().crossVectors( tangents[ 0 ], normals[ 0 ] ); - normals[ 0 ].crossVectors( binormals[ 0 ], tangents[ 0 ] ).normalize(); // last binormal x tangent - binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize(); + function v( x, y, z ) { - } + scope.vertices.push( new THREE.Vector3( x, y, z ) ); - function initialNormal3() { - // select an initial normal vector perpenicular to the first tangent vector, - // and in the direction of the smallest tangent xyz component + } - normals[ 0 ] = new THREE.Vector3(); - binormals[ 0 ] = new THREE.Vector3(); - smallest = Number.MAX_VALUE; - tx = Math.abs( tangents[ 0 ].x ); - ty = Math.abs( tangents[ 0 ].y ); - tz = Math.abs( tangents[ 0 ].z ); + function f3( a, b, c ) { - if ( tx <= smallest ) { - smallest = tx; - normal.set( 1, 0, 0 ); - } + a += shapesOffset; + b += shapesOffset; + c += shapesOffset; - if ( ty <= smallest ) { - smallest = ty; - normal.set( 0, 1, 0 ); - } + scope.faces.push( new THREE.Face3( a, b, c, null, null, 0 ) ); - if ( tz <= smallest ) { - normal.set( 0, 0, 1 ); - } + var uvs = uvgen.generateTopUV( scope, a, b, c ); - vec.crossVectors( tangents[ 0 ], normal ).normalize(); + scope.faceVertexUvs[ 0 ].push( uvs ); - normals[ 0 ].crossVectors( tangents[ 0 ], vec ); - binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); } + function f4( a, b, c, d, wallContour, stepIndex, stepsLength, contourIndex1, contourIndex2 ) { - // compute the slowly-varying normal and binormal vectors for each segment on the path - - for ( i = 1; i < numpoints; i++ ) { - - normals[ i ] = normals[ i-1 ].clone(); + a += shapesOffset; + b += shapesOffset; + c += shapesOffset; + d += shapesOffset; - binormals[ i ] = binormals[ i-1 ].clone(); + scope.faces.push( new THREE.Face3( a, b, d, null, null, 1 ) ); + scope.faces.push( new THREE.Face3( b, c, d, null, null, 1 ) ); - vec.crossVectors( tangents[ i-1 ], tangents[ i ] ); + var uvs = uvgen.generateSideWallUV( scope, a, b, c, d ); - if ( vec.length() > epsilon ) { + scope.faceVertexUvs[ 0 ].push( [ uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] ] ); + scope.faceVertexUvs[ 0 ].push( [ uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] ] ); - vec.normalize(); + } - theta = Math.acos( THREE.Math.clamp( tangents[ i-1 ].dot( tangents[ i ] ), -1, 1 ) ); // clamp for floating pt errors +}; - normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); +THREE.ExtrudeGeometry.WorldUVGenerator = { - } + generateTopUV: function ( geometry, indexA, indexB, indexC ) { - binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); + var vertices = geometry.vertices; - } + var a = vertices[ indexA ]; + var b = vertices[ indexB ]; + var c = vertices[ indexC ]; + return [ + new THREE.Vector2( a.x, a.y ), + new THREE.Vector2( b.x, b.y ), + new THREE.Vector2( c.x, c.y ) + ]; - // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same + }, - if ( closed ) { + generateSideWallUV: function ( geometry, indexA, indexB, indexC, indexD ) { - theta = Math.acos( THREE.Math.clamp( normals[ 0 ].dot( normals[ numpoints-1 ] ), -1, 1 ) ); - theta /= ( numpoints - 1 ); + var vertices = geometry.vertices; - if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ numpoints-1 ] ) ) > 0 ) { + var a = vertices[ indexA ]; + var b = vertices[ indexB ]; + var c = vertices[ indexC ]; + var d = vertices[ indexD ]; - theta = -theta; + if ( Math.abs( a.y - b.y ) < 0.01 ) { - } + return [ + new THREE.Vector2( a.x, 1 - a.z ), + new THREE.Vector2( b.x, 1 - b.z ), + new THREE.Vector2( c.x, 1 - c.z ), + new THREE.Vector2( d.x, 1 - d.z ) + ]; - for ( i = 1; i < numpoints; i++ ) { + } else { - // twist a little... - normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); - binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); + return [ + new THREE.Vector2( a.y, 1 - a.z ), + new THREE.Vector2( b.y, 1 - b.z ), + new THREE.Vector2( c.y, 1 - c.z ), + new THREE.Vector2( d.y, 1 - d.z ) + ]; } } }; +// File:src/extras/geometries/ShapeGeometry.js + /** - * @author clockworkgeek / https://github.com/clockworkgeek - * @author timothypratley / https://github.com/timothypratley - * @author WestLangley / http://github.com/WestLangley -*/ + * @author jonobr1 / http://jonobr1.com + * + * Creates a one-sided polygonal geometry from a path shape. Similar to + * ExtrudeGeometry. + * + * parameters = { + * + * curveSegments: , // number of points on the curves. NOT USED AT THE MOMENT. + * + * material: // material index for front and back faces + * uvGenerator: // object that provides UV generator functions + * + * } + **/ -THREE.PolyhedronGeometry = function ( vertices, faces, radius, detail ) { +THREE.ShapeGeometry = function ( shapes, options ) { THREE.Geometry.call( this ); - radius = radius || 1; - detail = detail || 0; - - var that = this; - - for ( var i = 0, l = vertices.length; i < l; i ++ ) { - - prepare( new THREE.Vector3( vertices[ i ][ 0 ], vertices[ i ][ 1 ], vertices[ i ][ 2 ] ) ); + this.type = 'ShapeGeometry'; - } + if ( Array.isArray( shapes ) === false ) shapes = [ shapes ]; - var midpoints = [], p = this.vertices; + this.addShapeList( shapes, options ); - var f = []; - for ( var i = 0, l = faces.length; i < l; i ++ ) { + this.computeFaceNormals(); - var v1 = p[ faces[ i ][ 0 ] ]; - var v2 = p[ faces[ i ][ 1 ] ]; - var v3 = p[ faces[ i ][ 2 ] ]; +}; - f[ i ] = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] ); +THREE.ShapeGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.ShapeGeometry.prototype.constructor = THREE.ShapeGeometry; - } +/** + * Add an array of shapes to THREE.ShapeGeometry. + */ +THREE.ShapeGeometry.prototype.addShapeList = function ( shapes, options ) { - for ( var i = 0, l = f.length; i < l; i ++ ) { + for ( var i = 0, l = shapes.length; i < l; i ++ ) { - subdivide(f[ i ], detail); + this.addShape( shapes[ i ], options ); } + return this; - // Handle case when face straddles the seam - - for ( var i = 0, l = this.faceVertexUvs[ 0 ].length; i < l; i ++ ) { - - var uvs = this.faceVertexUvs[ 0 ][ i ]; - - var x0 = uvs[ 0 ].x; - var x1 = uvs[ 1 ].x; - var x2 = uvs[ 2 ].x; +}; - var max = Math.max( x0, Math.max( x1, x2 ) ); - var min = Math.min( x0, Math.min( x1, x2 ) ); +/** + * Adds a shape to THREE.ShapeGeometry, based on THREE.ExtrudeGeometry. + */ +THREE.ShapeGeometry.prototype.addShape = function ( shape, options ) { - if ( max > 0.9 && min < 0.1 ) { // 0.9 is somewhat arbitrary + if ( options === undefined ) options = {}; + var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; - if ( x0 < 0.2 ) uvs[ 0 ].x += 1; - if ( x1 < 0.2 ) uvs[ 1 ].x += 1; - if ( x2 < 0.2 ) uvs[ 2 ].x += 1; + var material = options.material; + var uvgen = options.UVGenerator === undefined ? THREE.ExtrudeGeometry.WorldUVGenerator : options.UVGenerator; - } + // - } + var i, l, hole; + var shapesOffset = this.vertices.length; + var shapePoints = shape.extractPoints( curveSegments ); - // Apply radius + var vertices = shapePoints.shape; + var holes = shapePoints.holes; - for ( var i = 0, l = this.vertices.length; i < l; i ++ ) { + var reverse = ! THREE.ShapeUtils.isClockWise( vertices ); - this.vertices[ i ].multiplyScalar( radius ); + if ( reverse ) { - } + vertices = vertices.reverse(); + // Maybe we should also check if holes are in the opposite direction, just to be safe... - // Merge vertices + for ( i = 0, l = holes.length; i < l; i ++ ) { - this.mergeVertices(); + hole = holes[ i ]; - this.computeCentroids(); + if ( THREE.ShapeUtils.isClockWise( hole ) ) { - this.computeFaceNormals(); + holes[ i ] = hole.reverse(); - this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); + } + } - // Project vector onto sphere's surface + reverse = false; - function prepare( vector ) { + } - var vertex = vector.normalize().clone(); - vertex.index = that.vertices.push( vertex ) - 1; + var faces = THREE.ShapeUtils.triangulateShape( vertices, holes ); - // Texture coords are equivalent to map coords, calculate angle and convert to fraction of a circle. + // Vertices - var u = azimuth( vector ) / 2 / Math.PI + 0.5; - var v = inclination( vector ) / Math.PI + 0.5; - vertex.uv = new THREE.Vector2( u, 1 - v ); + for ( i = 0, l = holes.length; i < l; i ++ ) { - return vertex; + hole = holes[ i ]; + vertices = vertices.concat( hole ); } + // - // Approximate a curved face with recursively sub-divided triangles. - - function make( v1, v2, v3 ) { + var vert, vlen = vertices.length; + var face, flen = faces.length; - var face = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] ); - face.centroid.add( v1 ).add( v2 ).add( v3 ).divideScalar( 3 ); - that.faces.push( face ); + for ( i = 0; i < vlen; i ++ ) { - var azi = azimuth( face.centroid ); + vert = vertices[ i ]; - that.faceVertexUvs[ 0 ].push( [ - correctUV( v1.uv, v1, azi ), - correctUV( v2.uv, v2, azi ), - correctUV( v3.uv, v3, azi ) - ] ); + this.vertices.push( new THREE.Vector3( vert.x, vert.y, 0 ) ); } + for ( i = 0; i < flen; i ++ ) { - // Analytically subdivide a face to the required detail level. + face = faces[ i ]; - function subdivide(face, detail ) { + var a = face[ 0 ] + shapesOffset; + var b = face[ 1 ] + shapesOffset; + var c = face[ 2 ] + shapesOffset; - var cols = Math.pow(2, detail); - var cells = Math.pow(4, detail); - var a = prepare( that.vertices[ face.a ] ); - var b = prepare( that.vertices[ face.b ] ); - var c = prepare( that.vertices[ face.c ] ); - var v = []; + this.faces.push( new THREE.Face3( a, b, c, null, null, material ) ); + this.faceVertexUvs[ 0 ].push( uvgen.generateTopUV( this, a, b, c ) ); - // Construct all of the vertices for this subdivision. + } - for ( var i = 0 ; i <= cols; i ++ ) { +}; - v[ i ] = []; +// File:src/extras/geometries/LatheBufferGeometry.js - var aj = prepare( a.clone().lerp( c, i / cols ) ); - var bj = prepare( b.clone().lerp( c, i / cols ) ); - var rows = cols - i; +/** + * @author Mugen87 / https://github.com/Mugen87 + */ - for ( var j = 0; j <= rows; j ++) { + // points - to create a closed torus, one must use a set of points + // like so: [ a, b, c, d, a ], see first is the same as last. + // segments - the number of circumference segments to create + // phiStart - the starting radian + // phiLength - the radian (0 to 2PI) range of the lathed section + // 2PI is a closed lathe, less than 2PI is a portion. - if ( j == 0 && i == cols ) { +THREE.LatheBufferGeometry = function ( points, segments, phiStart, phiLength ) { - v[ i ][ j ] = aj; + THREE.BufferGeometry.call( this ); - } else { + this.type = 'LatheBufferGeometry'; - v[ i ][ j ] = prepare( aj.clone().lerp( bj, j / rows ) ); + this.parameters = { + points: points, + segments: segments, + phiStart: phiStart, + phiLength: phiLength + }; - } + segments = Math.floor( segments ) || 12; + phiStart = phiStart || 0; + phiLength = phiLength || Math.PI * 2; - } + // clamp phiLength so it's in range of [ 0, 2PI ] + phiLength = THREE.Math.clamp( phiLength, 0, Math.PI * 2 ); - } + // these are used to calculate buffer length + var vertexCount = ( segments + 1 ) * points.length; + var indexCount = segments * points.length * 2 * 3; - // Construct all of the faces. + // buffers + var indices = new THREE.BufferAttribute( new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ) , 1 ); + var vertices = new THREE.BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); + var uvs = new THREE.BufferAttribute( new Float32Array( vertexCount * 2 ), 2 ); - for ( var i = 0; i < cols ; i ++ ) { + // helper variables + var index = 0, indexOffset = 0, base; + var inversePointLength = 1.0 / ( points.length - 1 ); + var inverseSegments = 1.0 / segments; + var vertex = new THREE.Vector3(); + var uv = new THREE.Vector2(); + var i, j; - for ( var j = 0; j < 2 * (cols - i) - 1; j ++ ) { + // generate vertices and uvs - var k = Math.floor( j / 2 ); + for ( i = 0; i <= segments; i ++ ) { - if ( j % 2 == 0 ) { + var phi = phiStart + i * inverseSegments * phiLength; - make( - v[ i ][ k + 1], - v[ i + 1 ][ k ], - v[ i ][ k ] - ); + var sin = Math.sin( phi ); + var cos = Math.cos( phi ); - } else { + for ( j = 0; j <= ( points.length - 1 ); j ++ ) { - make( - v[ i ][ k + 1 ], - v[ i + 1][ k + 1], - v[ i + 1 ][ k ] - ); + // vertex + vertex.x = points[ j ].x * sin; + vertex.y = points[ j ].y; + vertex.z = points[ j ].x * cos; + vertices.setXYZ( index, vertex.x, vertex.y, vertex.z ); - } + // uv + uv.x = i / segments; + uv.y = j / ( points.length - 1 ); + uvs.setXY( index, uv.x, uv.y ); - } + // increase index + index ++; } } + // generate indices - // Angle around the Y axis, counter-clockwise when looking from above. - - function azimuth( vector ) { + for ( i = 0; i < segments; i ++ ) { - return Math.atan2( vector.z, -vector.x ); + for ( j = 0; j < ( points.length - 1 ); j ++ ) { - } + base = j + i * points.length; + // indices + var a = base; + var b = base + points.length; + var c = base + points.length + 1; + var d = base + 1; - // Angle above the XZ plane. + // face one + indices.setX( indexOffset, a ); indexOffset++; + indices.setX( indexOffset, b ); indexOffset++; + indices.setX( indexOffset, d ); indexOffset++; - function inclination( vector ) { + // face two + indices.setX( indexOffset, b ); indexOffset++; + indices.setX( indexOffset, c ); indexOffset++; + indices.setX( indexOffset, d ); indexOffset++; - return Math.atan2( -vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) ); + } } + // build geometry - // Texture fixing helper. Spheres have some odd behaviours. + this.setIndex( indices ); + this.addAttribute( 'position', vertices ); + this.addAttribute( 'uv', uvs ); - function correctUV( uv, vector, azimuth ) { + // generate normals - if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) uv = new THREE.Vector2( uv.x - 1, uv.y ); - if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) uv = new THREE.Vector2( azimuth / 2 / Math.PI + 0.5, uv.y ); - return uv.clone(); + this.computeVertexNormals(); - } + // if the geometry is closed, we need to average the normals along the seam. + // because the corresponding vertices are identical (but still have different UVs). + if( phiLength === Math.PI * 2 ) { -}; + var normals = this.attributes.normal.array; + var n1 = new THREE.Vector3(); + var n2 = new THREE.Vector3(); + var n = new THREE.Vector3(); -THREE.PolyhedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); + // this is the buffer offset for the last line of vertices + base = segments * points.length * 3; -/** - * @author timothypratley / https://github.com/timothypratley - */ + for( i = 0, j = 0; i < points.length; i ++, j += 3 ) { -THREE.IcosahedronGeometry = function ( radius, detail ) { + // select the normal of the vertex in the first line + n1.x = normals[ j + 0 ]; + n1.y = normals[ j + 1 ]; + n1.z = normals[ j + 2 ]; - this.radius = radius; - this.detail = detail; + // select the normal of the vertex in the last line + n2.x = normals[ base + j + 0 ]; + n2.y = normals[ base + j + 1 ]; + n2.z = normals[ base + j + 2 ]; - var t = ( 1 + Math.sqrt( 5 ) ) / 2; + // average normals + n.addVectors( n1, n2 ).normalize(); - var vertices = [ - [ -1, t, 0 ], [ 1, t, 0 ], [ -1, -t, 0 ], [ 1, -t, 0 ], - [ 0, -1, t ], [ 0, 1, t ], [ 0, -1, -t ], [ 0, 1, -t ], - [ t, 0, -1 ], [ t, 0, 1 ], [ -t, 0, -1 ], [ -t, 0, 1 ] - ]; + // assign the new values to both normals + normals[ j + 0 ] = normals[ base + j + 0 ] = n.x; + normals[ j + 1 ] = normals[ base + j + 1 ] = n.y; + normals[ j + 2 ] = normals[ base + j + 2 ] = n.z; - var faces = [ - [ 0, 11, 5 ], [ 0, 5, 1 ], [ 0, 1, 7 ], [ 0, 7, 10 ], [ 0, 10, 11 ], - [ 1, 5, 9 ], [ 5, 11, 4 ], [ 11, 10, 2 ], [ 10, 7, 6 ], [ 7, 1, 8 ], - [ 3, 9, 4 ], [ 3, 4, 2 ], [ 3, 2, 6 ], [ 3, 6, 8 ], [ 3, 8, 9 ], - [ 4, 9, 5 ], [ 2, 4, 11 ], [ 6, 2, 10 ], [ 8, 6, 7 ], [ 9, 8, 1 ] - ]; + } // next row - THREE.PolyhedronGeometry.call( this, vertices, faces, radius, detail ); + } }; -THREE.IcosahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.LatheBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.LatheBufferGeometry.prototype.constructor = THREE.LatheBufferGeometry; + +// File:src/extras/geometries/LatheGeometry.js /** - * @author timothypratley / https://github.com/timothypratley + * @author astrodud / http://astrodud.isgreat.org/ + * @author zz85 / https://github.com/zz85 + * @author bhouston / http://clara.io */ -THREE.OctahedronGeometry = function ( radius, detail ) { +// points - to create a closed torus, one must use a set of points +// like so: [ a, b, c, d, a ], see first is the same as last. +// segments - the number of circumference segments to create +// phiStart - the starting radian +// phiLength - the radian (0 to 2PI) range of the lathed section +// 2PI is a closed lathe, less than 2PI is a portion. - var vertices = [ - [ 1, 0, 0 ], [ -1, 0, 0 ], [ 0, 1, 0 ], [ 0, -1, 0 ], [ 0, 0, 1 ], [ 0, 0, -1 ] - ]; +THREE.LatheGeometry = function ( points, segments, phiStart, phiLength ) { - var faces = [ - [ 0, 2, 4 ], [ 0, 4, 3 ], [ 0, 3, 5 ], [ 0, 5, 2 ], [ 1, 2, 5 ], [ 1, 5, 3 ], [ 1, 3, 4 ], [ 1, 4, 2 ] - ]; + THREE.Geometry.call( this ); + + this.type = 'LatheGeometry'; + + this.parameters = { + points: points, + segments: segments, + phiStart: phiStart, + phiLength: phiLength + }; + + this.fromBufferGeometry( new THREE.LatheBufferGeometry( points, segments, phiStart, phiLength ) ); + this.mergeVertices(); - THREE.PolyhedronGeometry.call( this, vertices, faces, radius, detail ); }; -THREE.OctahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.LatheGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.LatheGeometry.prototype.constructor = THREE.LatheGeometry; + +// File:src/extras/geometries/PlaneGeometry.js /** - * @author timothypratley / https://github.com/timothypratley + * @author mrdoob / http://mrdoob.com/ + * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as */ -THREE.TetrahedronGeometry = function ( radius, detail ) { +THREE.PlaneGeometry = function ( width, height, widthSegments, heightSegments ) { - var vertices = [ - [ 1, 1, 1 ], [ -1, -1, 1 ], [ -1, 1, -1 ], [ 1, -1, -1 ] - ]; + THREE.Geometry.call( this ); - var faces = [ - [ 2, 1, 0 ], [ 0, 3, 2 ], [ 1, 3, 0 ], [ 2, 3, 1 ] - ]; + this.type = 'PlaneGeometry'; - THREE.PolyhedronGeometry.call( this, vertices, faces, radius, detail ); + this.parameters = { + width: width, + height: height, + widthSegments: widthSegments, + heightSegments: heightSegments + }; + + this.fromBufferGeometry( new THREE.PlaneBufferGeometry( width, height, widthSegments, heightSegments ) ); }; -THREE.TetrahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.PlaneGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.PlaneGeometry.prototype.constructor = THREE.PlaneGeometry; + +// File:src/extras/geometries/PlaneBufferGeometry.js /** - * @author zz85 / https://github.com/zz85 - * Parametric Surfaces Geometry - * based on the brilliant article by @prideout http://prideout.net/blog/?p=44 - * - * new THREE.ParametricGeometry( parametricFunction, uSegments, ySegements ); - * + * @author mrdoob / http://mrdoob.com/ + * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as */ -THREE.ParametricGeometry = function ( func, slices, stacks ) { +THREE.PlaneBufferGeometry = function ( width, height, widthSegments, heightSegments ) { - THREE.Geometry.call( this ); + THREE.BufferGeometry.call( this ); - var verts = this.vertices; - var faces = this.faces; - var uvs = this.faceVertexUvs[ 0 ]; + this.type = 'PlaneBufferGeometry'; - var i, il, j, p; - var u, v; + this.parameters = { + width: width, + height: height, + widthSegments: widthSegments, + heightSegments: heightSegments + }; - var stackCount = stacks + 1; - var sliceCount = slices + 1; + var width_half = width / 2; + var height_half = height / 2; - for ( i = 0; i <= stacks; i ++ ) { + var gridX = Math.floor( widthSegments ) || 1; + var gridY = Math.floor( heightSegments ) || 1; - v = i / stacks; + var gridX1 = gridX + 1; + var gridY1 = gridY + 1; - for ( j = 0; j <= slices; j ++ ) { + var segment_width = width / gridX; + var segment_height = height / gridY; - u = j / slices; + var vertices = new Float32Array( gridX1 * gridY1 * 3 ); + var normals = new Float32Array( gridX1 * gridY1 * 3 ); + var uvs = new Float32Array( gridX1 * gridY1 * 2 ); - p = func( u, v ); - verts.push( p ); + var offset = 0; + var offset2 = 0; - } - } + for ( var iy = 0; iy < gridY1; iy ++ ) { - var a, b, c, d; - var uva, uvb, uvc, uvd; + var y = iy * segment_height - height_half; - for ( i = 0; i < stacks; i ++ ) { + for ( var ix = 0; ix < gridX1; ix ++ ) { - for ( j = 0; j < slices; j ++ ) { + var x = ix * segment_width - width_half; - a = i * sliceCount + j; - b = i * sliceCount + j + 1; - c = (i + 1) * sliceCount + j + 1; - d = (i + 1) * sliceCount + j; + vertices[ offset ] = x; + vertices[ offset + 1 ] = - y; - uva = new THREE.Vector2( j / slices, i / stacks ); - uvb = new THREE.Vector2( ( j + 1 ) / slices, i / stacks ); - uvc = new THREE.Vector2( ( j + 1 ) / slices, ( i + 1 ) / stacks ); - uvd = new THREE.Vector2( j / slices, ( i + 1 ) / stacks ); + normals[ offset + 2 ] = 1; - faces.push( new THREE.Face3( a, b, d ) ); - uvs.push( [ uva, uvb, uvd ] ); + uvs[ offset2 ] = ix / gridX; + uvs[ offset2 + 1 ] = 1 - ( iy / gridY ); - faces.push( new THREE.Face3( b, c, d ) ); - uvs.push( [ uvb.clone(), uvc, uvd.clone() ] ); + offset += 3; + offset2 += 2; } } - // console.log(this); - - // magic bullet - // var diff = this.mergeVertices(); - // console.log('removed ', diff, ' vertices by merging'); - - this.computeCentroids(); - this.computeFaceNormals(); - this.computeVertexNormals(); + offset = 0; -}; + var indices = new ( ( vertices.length / 3 ) > 65535 ? Uint32Array : Uint16Array )( gridX * gridY * 6 ); -THREE.ParametricGeometry.prototype = Object.create( THREE.Geometry.prototype ); + for ( var iy = 0; iy < gridY; iy ++ ) { -/** - * @author sroucheray / http://sroucheray.org/ - * @author mrdoob / http://mrdoob.com/ - */ + for ( var ix = 0; ix < gridX; ix ++ ) { -THREE.AxisHelper = function ( size ) { + var a = ix + gridX1 * iy; + var b = ix + gridX1 * ( iy + 1 ); + var c = ( ix + 1 ) + gridX1 * ( iy + 1 ); + var d = ( ix + 1 ) + gridX1 * iy; - size = size || 1; + indices[ offset ] = a; + indices[ offset + 1 ] = b; + indices[ offset + 2 ] = d; - var geometry = new THREE.Geometry(); + indices[ offset + 3 ] = b; + indices[ offset + 4 ] = c; + indices[ offset + 5 ] = d; - geometry.vertices.push( - new THREE.Vector3(), new THREE.Vector3( size, 0, 0 ), - new THREE.Vector3(), new THREE.Vector3( 0, size, 0 ), - new THREE.Vector3(), new THREE.Vector3( 0, 0, size ) - ); + offset += 6; - geometry.colors.push( - new THREE.Color( 0xff0000 ), new THREE.Color( 0xffaa00 ), - new THREE.Color( 0x00ff00 ), new THREE.Color( 0xaaff00 ), - new THREE.Color( 0x0000ff ), new THREE.Color( 0x00aaff ) - ); + } - var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } ); + } - THREE.Line.call( this, geometry, material, THREE.LinePieces ); + this.setIndex( new THREE.BufferAttribute( indices, 1 ) ); + this.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); + this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) ); + this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); }; -THREE.AxisHelper.prototype = Object.create( THREE.Line.prototype ); +THREE.PlaneBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.PlaneBufferGeometry.prototype.constructor = THREE.PlaneBufferGeometry; + +// File:src/extras/geometries/RingBufferGeometry.js /** - * @author WestLangley / http://github.com/WestLangley - * @author zz85 / http://github.com/zz85 - * @author bhouston / http://exocortex.com - * - * Creates an arrow for visualizing directions - * - * Parameters: - * dir - Vector3 - * origin - Vector3 - * length - Number - * hex - color in hex value + * @author Mugen87 / https://github.com/Mugen87 */ -THREE.ArrowHelper = function ( dir, origin, length, hex ) { - - // dir is assumed to be normalized - - THREE.Object3D.call( this ); - - if ( hex === undefined ) hex = 0xffff00; - if ( length === undefined ) length = 1; - - this.position = origin; - - var lineGeometry = new THREE.Geometry(); - lineGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ) ); - lineGeometry.vertices.push( new THREE.Vector3( 0, 1, 0 ) ); +THREE.RingBufferGeometry = function ( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { - this.line = new THREE.Line( lineGeometry, new THREE.LineBasicMaterial( { color: hex } ) ); - this.line.matrixAutoUpdate = false; - this.add( this.line ); + THREE.BufferGeometry.call( this ); - var coneGeometry = new THREE.CylinderGeometry( 0, 0.05, 0.25, 5, 1 ); - coneGeometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, 0.875, 0 ) ); + this.type = 'RingBufferGeometry'; - this.cone = new THREE.Mesh( coneGeometry, new THREE.MeshBasicMaterial( { color: hex } ) ); - this.cone.matrixAutoUpdate = false; - this.add( this.cone ); + this.parameters = { + innerRadius: innerRadius, + outerRadius: outerRadius, + thetaSegments: thetaSegments, + phiSegments: phiSegments, + thetaStart: thetaStart, + thetaLength: thetaLength + }; - this.setDirection( dir ); - this.setLength( length ); + innerRadius = innerRadius || 20; + outerRadius = outerRadius || 50; -}; + thetaStart = thetaStart !== undefined ? thetaStart : 0; + thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; -THREE.ArrowHelper.prototype = Object.create( THREE.Object3D.prototype ); + thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8; + phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 1; -THREE.ArrowHelper.prototype.setDirection = function () { + // these are used to calculate buffer length + var vertexCount = ( thetaSegments + 1 ) * ( phiSegments + 1 ); + var indexCount = thetaSegments * phiSegments * 2 * 3; - var axis = new THREE.Vector3(); - var radians; + // buffers + var indices = new THREE.BufferAttribute( new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ) , 1 ); + var vertices = new THREE.BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); + var normals = new THREE.BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); + var uvs = new THREE.BufferAttribute( new Float32Array( vertexCount * 2 ), 2 ); - return function ( dir ) { + // some helper variables + var index = 0, indexOffset = 0, segment; + var radius = innerRadius; + var radiusStep = ( ( outerRadius - innerRadius ) / phiSegments ); + var vertex = new THREE.Vector3(); + var uv = new THREE.Vector2(); + var j, i; - // dir is assumed to be normalized + // generate vertices, normals and uvs - if ( dir.y > 0.99999 ) { + // values are generate from the inside of the ring to the outside - this.quaternion.set( 0, 0, 0, 1 ); + for ( j = 0; j <= phiSegments; j ++ ) { - } else if ( dir.y < - 0.99999 ) { + for ( i = 0; i <= thetaSegments; i ++ ) { - this.quaternion.set( 1, 0, 0, 0 ); + segment = thetaStart + i / thetaSegments * thetaLength; - } else { + // vertex + vertex.x = radius * Math.cos( segment ); + vertex.y = radius * Math.sin( segment ); + vertices.setXYZ( index, vertex.x, vertex.y, vertex.z ); - axis.set( dir.z, 0, - dir.x ).normalize(); + // normal + normals.setXYZ( index, 0, 0, 1 ); - radians = Math.acos( dir.y ); + // uv + uv.x = ( vertex.x / outerRadius + 1 ) / 2; + uv.y = ( vertex.y / outerRadius + 1 ) / 2; + uvs.setXY( index, uv.x, uv.y ); - this.quaternion.setFromAxisAngle( axis, radians ); + // increase index + index++; } - }; - -}(); - -THREE.ArrowHelper.prototype.setLength = function ( length ) { - - this.scale.set( length, length, length ); - -}; - -THREE.ArrowHelper.prototype.setColor = function ( hex ) { + // increase the radius for next row of vertices + radius += radiusStep; - this.line.material.color.setHex( hex ); - this.cone.material.color.setHex( hex ); + } -}; + // generate indices -/** - * @author mrdoob / http://mrdoob.com/ - */ + for ( j = 0; j < phiSegments; j ++ ) { -THREE.BoxHelper = function ( object ) { + var thetaSegmentLevel = j * ( thetaSegments + 1 ); - // 5____4 - // 1/___0/| - // | 6__|_7 - // 2/___3/ + for ( i = 0; i < thetaSegments; i ++ ) { - var vertices = [ - new THREE.Vector3( 1, 1, 1 ), - new THREE.Vector3( - 1, 1, 1 ), - new THREE.Vector3( - 1, - 1, 1 ), - new THREE.Vector3( 1, - 1, 1 ), - - new THREE.Vector3( 1, 1, - 1 ), - new THREE.Vector3( - 1, 1, - 1 ), - new THREE.Vector3( - 1, - 1, - 1 ), - new THREE.Vector3( 1, - 1, - 1 ) - ]; + segment = i + thetaSegmentLevel; - this.vertices = vertices; + // indices + var a = segment; + var b = segment + thetaSegments + 1; + var c = segment + thetaSegments + 2; + var d = segment + 1; - // TODO: Wouldn't be nice if Line had .segments? + // face one + indices.setX( indexOffset, a ); indexOffset++; + indices.setX( indexOffset, b ); indexOffset++; + indices.setX( indexOffset, c ); indexOffset++; - var geometry = new THREE.Geometry(); - geometry.vertices.push( - vertices[ 0 ], vertices[ 1 ], - vertices[ 1 ], vertices[ 2 ], - vertices[ 2 ], vertices[ 3 ], - vertices[ 3 ], vertices[ 0 ], - - vertices[ 4 ], vertices[ 5 ], - vertices[ 5 ], vertices[ 6 ], - vertices[ 6 ], vertices[ 7 ], - vertices[ 7 ], vertices[ 4 ], - - vertices[ 0 ], vertices[ 4 ], - vertices[ 1 ], vertices[ 5 ], - vertices[ 2 ], vertices[ 6 ], - vertices[ 3 ], vertices[ 7 ] - ); + // face two + indices.setX( indexOffset, a ); indexOffset++; + indices.setX( indexOffset, c ); indexOffset++; + indices.setX( indexOffset, d ); indexOffset++; - THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: 0xffff00 } ), THREE.LinePieces ); + } - if ( object !== undefined ) { + } - this.update( object ); + // build geometry - } + this.setIndex( indices ); + this.addAttribute( 'position', vertices ); + this.addAttribute( 'normal', normals ); + this.addAttribute( 'uv', uvs ); }; -THREE.BoxHelper.prototype = Object.create( THREE.Line.prototype ); +THREE.RingBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.RingBufferGeometry.prototype.constructor = THREE.RingBufferGeometry; -THREE.BoxHelper.prototype.update = function ( object ) { +// File:src/extras/geometries/RingGeometry.js - var geometry = object.geometry; +/** + * @author Kaleb Murphy + */ - if ( geometry.boundingBox === null ) { +THREE.RingGeometry = function ( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { - geometry.computeBoundingBox(); + THREE.Geometry.call( this ); - } + this.type = 'RingGeometry'; - var min = geometry.boundingBox.min; - var max = geometry.boundingBox.max; - var vertices = this.vertices; + this.parameters = { + innerRadius: innerRadius, + outerRadius: outerRadius, + thetaSegments: thetaSegments, + phiSegments: phiSegments, + thetaStart: thetaStart, + thetaLength: thetaLength + }; - vertices[ 0 ].set( max.x, max.y, max.z ); - vertices[ 1 ].set( min.x, max.y, max.z ); - vertices[ 2 ].set( min.x, min.y, max.z ); - vertices[ 3 ].set( max.x, min.y, max.z ); - vertices[ 4 ].set( max.x, max.y, min.z ); - vertices[ 5 ].set( min.x, max.y, min.z ); - vertices[ 6 ].set( min.x, min.y, min.z ); - vertices[ 7 ].set( max.x, min.y, min.z ); + this.fromBufferGeometry( new THREE.RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) ); - this.geometry.computeBoundingSphere(); - this.geometry.verticesNeedUpdate = true; +}; - this.matrixAutoUpdate = false; - this.matrixWorld = object.matrixWorld; +THREE.RingGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.RingGeometry.prototype.constructor = THREE.RingGeometry; -}; +// File:src/extras/geometries/SphereGeometry.js /** - * @author WestLangley / http://github.com/WestLangley + * @author mrdoob / http://mrdoob.com/ */ -// a helper to show the world-axis-aligned bounding box for an object - -THREE.BoundingBoxHelper = function ( object, hex ) { +THREE.SphereGeometry = function ( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { - var color = hex || 0x888888; + THREE.Geometry.call( this ); - this.object = object; + this.type = 'SphereGeometry'; - this.box = new THREE.Box3(); + this.parameters = { + radius: radius, + widthSegments: widthSegments, + heightSegments: heightSegments, + phiStart: phiStart, + phiLength: phiLength, + thetaStart: thetaStart, + thetaLength: thetaLength + }; - THREE.Mesh.call( this, new THREE.CubeGeometry( 1, 1, 1 ), new THREE.MeshBasicMaterial( { color: color, wireframe: true } ) ); + this.fromBufferGeometry( new THREE.SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) ); }; -THREE.BoundingBoxHelper.prototype = Object.create( THREE.Mesh.prototype ); - -THREE.BoundingBoxHelper.prototype.update = function () { - - this.box.setFromObject( this.object ); - - this.box.size( this.scale ); - - this.box.center( this.position ); +THREE.SphereGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.SphereGeometry.prototype.constructor = THREE.SphereGeometry; -}; +// File:src/extras/geometries/SphereBufferGeometry.js /** - * @author alteredq / http://alteredqualia.com/ - * - * - shows frustum, line of sight and up of the camera - * - suitable for fast updates - * - based on frustum visualization in lightgl.js shadowmap example - * http://evanw.github.com/lightgl.js/tests/shadowmap.html + * @author benaadams / https://twitter.com/ben_a_adams + * based on THREE.SphereGeometry */ -THREE.CameraHelper = function ( camera ) { +THREE.SphereBufferGeometry = function ( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { - var geometry = new THREE.Geometry(); - var material = new THREE.LineBasicMaterial( { color: 0xffffff, vertexColors: THREE.FaceColors } ); + THREE.BufferGeometry.call( this ); - var pointMap = {}; + this.type = 'SphereBufferGeometry'; - // colors + this.parameters = { + radius: radius, + widthSegments: widthSegments, + heightSegments: heightSegments, + phiStart: phiStart, + phiLength: phiLength, + thetaStart: thetaStart, + thetaLength: thetaLength + }; - var hexFrustum = 0xffaa00; - var hexCone = 0xff0000; - var hexUp = 0x00aaff; - var hexTarget = 0xffffff; - var hexCross = 0x333333; + radius = radius || 50; - // near + widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 ); + heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 ); - addLine( "n1", "n2", hexFrustum ); - addLine( "n2", "n4", hexFrustum ); - addLine( "n4", "n3", hexFrustum ); - addLine( "n3", "n1", hexFrustum ); + phiStart = phiStart !== undefined ? phiStart : 0; + phiLength = phiLength !== undefined ? phiLength : Math.PI * 2; - // far + thetaStart = thetaStart !== undefined ? thetaStart : 0; + thetaLength = thetaLength !== undefined ? thetaLength : Math.PI; - addLine( "f1", "f2", hexFrustum ); - addLine( "f2", "f4", hexFrustum ); - addLine( "f4", "f3", hexFrustum ); - addLine( "f3", "f1", hexFrustum ); + var thetaEnd = thetaStart + thetaLength; - // sides + var vertexCount = ( ( widthSegments + 1 ) * ( heightSegments + 1 ) ); - addLine( "n1", "f1", hexFrustum ); - addLine( "n2", "f2", hexFrustum ); - addLine( "n3", "f3", hexFrustum ); - addLine( "n4", "f4", hexFrustum ); + var positions = new THREE.BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); + var normals = new THREE.BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); + var uvs = new THREE.BufferAttribute( new Float32Array( vertexCount * 2 ), 2 ); - // cone + var index = 0, vertices = [], normal = new THREE.Vector3(); - addLine( "p", "n1", hexCone ); - addLine( "p", "n2", hexCone ); - addLine( "p", "n3", hexCone ); - addLine( "p", "n4", hexCone ); + for ( var y = 0; y <= heightSegments; y ++ ) { - // up + var verticesRow = []; + + var v = y / heightSegments; + + for ( var x = 0; x <= widthSegments; x ++ ) { - addLine( "u1", "u2", hexUp ); - addLine( "u2", "u3", hexUp ); - addLine( "u3", "u1", hexUp ); + var u = x / widthSegments; - // target + var px = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); + var py = radius * Math.cos( thetaStart + v * thetaLength ); + var pz = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); - addLine( "c", "t", hexTarget ); - addLine( "p", "c", hexCross ); + normal.set( px, py, pz ).normalize(); - // cross + positions.setXYZ( index, px, py, pz ); + normals.setXYZ( index, normal.x, normal.y, normal.z ); + uvs.setXY( index, u, 1 - v ); - addLine( "cn1", "cn2", hexCross ); - addLine( "cn3", "cn4", hexCross ); + verticesRow.push( index ); - addLine( "cf1", "cf2", hexCross ); - addLine( "cf3", "cf4", hexCross ); + index ++; - function addLine( a, b, hex ) { + } - addPoint( a, hex ); - addPoint( b, hex ); + vertices.push( verticesRow ); } - function addPoint( id, hex ) { + var indices = []; - geometry.vertices.push( new THREE.Vector3() ); - geometry.colors.push( new THREE.Color( hex ) ); + for ( var y = 0; y < heightSegments; y ++ ) { - if ( pointMap[ id ] === undefined ) { + for ( var x = 0; x < widthSegments; x ++ ) { - pointMap[ id ] = []; + var v1 = vertices[ y ][ x + 1 ]; + var v2 = vertices[ y ][ x ]; + var v3 = vertices[ y + 1 ][ x ]; + var v4 = vertices[ y + 1 ][ x + 1 ]; - } + if ( y !== 0 || thetaStart > 0 ) indices.push( v1, v2, v4 ); + if ( y !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( v2, v3, v4 ); - pointMap[ id ].push( geometry.vertices.length - 1 ); + } } - THREE.Line.call( this, geometry, material, THREE.LinePieces ); - - this.camera = camera; - this.matrixWorld = camera.matrixWorld; - this.matrixAutoUpdate = false; - - this.pointMap = pointMap; + this.setIndex( new ( positions.count > 65535 ? THREE.Uint32Attribute : THREE.Uint16Attribute )( indices, 1 ) ); + this.addAttribute( 'position', positions ); + this.addAttribute( 'normal', normals ); + this.addAttribute( 'uv', uvs ); - this.update(); + this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); }; -THREE.CameraHelper.prototype = Object.create( THREE.Line.prototype ); +THREE.SphereBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.SphereBufferGeometry.prototype.constructor = THREE.SphereBufferGeometry; -THREE.CameraHelper.prototype.update = function () { +// File:src/extras/geometries/TextGeometry.js - var vector = new THREE.Vector3(); - var camera = new THREE.Camera(); - var projector = new THREE.Projector(); +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * @author alteredq / http://alteredqualia.com/ + * + * Text = 3D Text + * + * parameters = { + * font: , // font + * + * size: , // size of the text + * height: , // thickness to extrude text + * curveSegments: , // number of points on the curves + * + * bevelEnabled: , // turn on bevel + * bevelThickness: , // how deep into text bevel goes + * bevelSize: // how far from text outline is bevel + * } + */ - return function () { +THREE.TextGeometry = function ( text, parameters ) { - var scope = this; + parameters = parameters || {}; - var w = 1, h = 1; + var font = parameters.font; - // we need just camera projection matrix - // world matrix must be identity + if ( font instanceof THREE.Font === false ) { - camera.projectionMatrix.copy( this.camera.projectionMatrix ); + console.error( 'THREE.TextGeometry: font parameter is not an instance of THREE.Font.' ); + return new THREE.Geometry(); - // center / target + } - setPoint( "c", 0, 0, -1 ); - setPoint( "t", 0, 0, 1 ); + var shapes = font.generateShapes( text, parameters.size, parameters.curveSegments ); - // near + // translate parameters to ExtrudeGeometry API - setPoint( "n1", -w, -h, -1 ); - setPoint( "n2", w, -h, -1 ); - setPoint( "n3", -w, h, -1 ); - setPoint( "n4", w, h, -1 ); + parameters.amount = parameters.height !== undefined ? parameters.height : 50; - // far + // defaults - setPoint( "f1", -w, -h, 1 ); - setPoint( "f2", w, -h, 1 ); - setPoint( "f3", -w, h, 1 ); - setPoint( "f4", w, h, 1 ); + if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10; + if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8; + if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false; - // up + THREE.ExtrudeGeometry.call( this, shapes, parameters ); - setPoint( "u1", w * 0.7, h * 1.1, -1 ); - setPoint( "u2", -w * 0.7, h * 1.1, -1 ); - setPoint( "u3", 0, h * 2, -1 ); + this.type = 'TextGeometry'; - // cross +}; - setPoint( "cf1", -w, 0, 1 ); - setPoint( "cf2", w, 0, 1 ); - setPoint( "cf3", 0, -h, 1 ); - setPoint( "cf4", 0, h, 1 ); +THREE.TextGeometry.prototype = Object.create( THREE.ExtrudeGeometry.prototype ); +THREE.TextGeometry.prototype.constructor = THREE.TextGeometry; - setPoint( "cn1", -w, 0, -1 ); - setPoint( "cn2", w, 0, -1 ); - setPoint( "cn3", 0, -h, -1 ); - setPoint( "cn4", 0, h, -1 ); +// File:src/extras/geometries/TorusBufferGeometry.js - function setPoint( point, x, y, z ) { +/** + * @author Mugen87 / https://github.com/Mugen87 + */ - vector.set( x, y, z ); - projector.unprojectVector( vector, camera ); +THREE.TorusBufferGeometry = function ( radius, tube, radialSegments, tubularSegments, arc ) { - var points = scope.pointMap[ point ]; + THREE.BufferGeometry.call( this ); - if ( points !== undefined ) { + this.type = 'TorusBufferGeometry'; - for ( var i = 0, il = points.length; i < il; i ++ ) { + this.parameters = { + radius: radius, + tube: tube, + radialSegments: radialSegments, + tubularSegments: tubularSegments, + arc: arc + }; - scope.geometry.vertices[ points[ i ] ].copy( vector ); + radius = radius || 100; + tube = tube || 40; + radialSegments = Math.floor( radialSegments ) || 8; + tubularSegments = Math.floor( tubularSegments ) || 6; + arc = arc || Math.PI * 2; - } + // used to calculate buffer length + var vertexCount = ( ( radialSegments + 1 ) * ( tubularSegments + 1 ) ); + var indexCount = radialSegments * tubularSegments * 2 * 3; - } + // buffers + var indices = new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ); + var vertices = new Float32Array( vertexCount * 3 ); + var normals = new Float32Array( vertexCount * 3 ); + var uvs = new Float32Array( vertexCount * 2 ); - } + // offset variables + var vertexBufferOffset = 0; + var uvBufferOffset = 0; + var indexBufferOffset = 0; - this.geometry.verticesNeedUpdate = true; + // helper variables + var center = new THREE.Vector3(); + var vertex = new THREE.Vector3(); + var normal = new THREE.Vector3(); - }; + var j, i; -}(); + // generate vertices, normals and uvs -/** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - */ + for ( j = 0; j <= radialSegments; j ++ ) { -THREE.DirectionalLightHelper = function ( light, size ) { + for ( i = 0; i <= tubularSegments; i ++ ) { - THREE.Object3D.call( this ); + var u = i / tubularSegments * arc; + var v = j / radialSegments * Math.PI * 2; - this.light = light; - this.light.updateMatrixWorld(); + // vertex + vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u ); + vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u ); + vertex.z = tube * Math.sin( v ); - this.matrixWorld = light.matrixWorld; - this.matrixAutoUpdate = false; + vertices[ vertexBufferOffset ] = vertex.x; + vertices[ vertexBufferOffset + 1 ] = vertex.y; + vertices[ vertexBufferOffset + 2 ] = vertex.z; - var geometry = new THREE.PlaneGeometry( size, size ); - var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } ); - material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + // this vector is used to calculate the normal + center.x = radius * Math.cos( u ); + center.y = radius * Math.sin( u ); - this.lightPlane = new THREE.Mesh( geometry, material ); - this.add( this.lightPlane ); + // normal + normal.subVectors( vertex, center ).normalize(); - geometry = new THREE.Geometry(); - geometry.vertices.push( new THREE.Vector3() ); - geometry.vertices.push( new THREE.Vector3() ); - geometry.computeLineDistances(); + normals[ vertexBufferOffset ] = normal.x; + normals[ vertexBufferOffset + 1 ] = normal.y; + normals[ vertexBufferOffset + 2 ] = normal.z; - material = new THREE.LineBasicMaterial( { fog: false } ); - material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + // uv + uvs[ uvBufferOffset ] = i / tubularSegments; + uvs[ uvBufferOffset + 1 ] = j / radialSegments; - this.targetLine = new THREE.Line( geometry, material ); - this.add( this.targetLine ); + // update offsets + vertexBufferOffset += 3; + uvBufferOffset += 2; - this.update(); + } -}; + } -THREE.DirectionalLightHelper.prototype = Object.create( THREE.Object3D.prototype ); + // generate indices -THREE.DirectionalLightHelper.prototype.update = function () { + for ( j = 1; j <= radialSegments; j ++ ) { - var vector = new THREE.Vector3(); + for ( i = 1; i <= tubularSegments; i ++ ) { - return function () { + // indices + var a = ( tubularSegments + 1 ) * j + i - 1; + var b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1; + var c = ( tubularSegments + 1 ) * ( j - 1 ) + i; + var d = ( tubularSegments + 1 ) * j + i; - vector.getPositionFromMatrix( this.light.matrixWorld ).negate(); + // face one + indices[ indexBufferOffset ] = a; + indices[ indexBufferOffset + 1 ] = b; + indices[ indexBufferOffset + 2 ] = d; - this.lightPlane.lookAt( vector ); - this.lightPlane.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + // face two + indices[ indexBufferOffset + 3 ] = b; + indices[ indexBufferOffset + 4 ] = c; + indices[ indexBufferOffset + 5 ] = d; - this.targetLine.geometry.vertices[ 1 ].copy( vector ); - this.targetLine.geometry.verticesNeedUpdate = true; - this.targetLine.material.color.copy( this.lightPlane.material.color ); + // update offset + indexBufferOffset += 6; + + } } -}(); + // build geometry + this.setIndex( new THREE.BufferAttribute( indices, 1 ) ); + this.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); + this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) ); + this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); + +}; + +THREE.TorusBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.TorusBufferGeometry.prototype.constructor = THREE.TorusBufferGeometry; +// File:src/extras/geometries/TorusGeometry.js /** + * @author oosmoxiecode * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley -*/ + * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3DLite/src/away3dlite/primitives/Torus.as?r=2888 + */ -THREE.FaceNormalsHelper = function ( object, size, hex, linewidth ) { +THREE.TorusGeometry = function ( radius, tube, radialSegments, tubularSegments, arc ) { - this.object = object; + THREE.Geometry.call( this ); - this.size = size || 1; + this.type = 'TorusGeometry'; - var color = hex || 0xffff00; + this.parameters = { + radius: radius, + tube: tube, + radialSegments: radialSegments, + tubularSegments: tubularSegments, + arc: arc + }; - var width = linewidth || 1; + this.fromBufferGeometry( new THREE.TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) ); - var geometry = new THREE.Geometry(); +}; - var faces = this.object.geometry.faces; +THREE.TorusGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.TorusGeometry.prototype.constructor = THREE.TorusGeometry; - for ( var i = 0, l = faces.length; i < l; i ++ ) { +// File:src/extras/geometries/TorusKnotBufferGeometry.js - geometry.vertices.push( new THREE.Vector3() ); - geometry.vertices.push( new THREE.Vector3() ); +/** + * @author Mugen87 / https://github.com/Mugen87 + * + * see: http://www.blackpawn.com/texts/pqtorus/ + */ +THREE.TorusKnotBufferGeometry = function ( radius, tube, tubularSegments, radialSegments, p, q ) { - } + THREE.BufferGeometry.call( this ); - THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces ); + this.type = 'TorusKnotBufferGeometry'; - this.matrixAutoUpdate = false; + this.parameters = { + radius: radius, + tube: tube, + tubularSegments: tubularSegments, + radialSegments: radialSegments, + p: p, + q: q + }; - this.normalMatrix = new THREE.Matrix3(); + radius = radius || 100; + tube = tube || 40; + tubularSegments = Math.floor( tubularSegments ) || 64; + radialSegments = Math.floor( radialSegments ) || 8; + p = p || 2; + q = q || 3; - this.update(); + // used to calculate buffer length + var vertexCount = ( ( radialSegments + 1 ) * ( tubularSegments + 1 ) ); + var indexCount = radialSegments * tubularSegments * 2 * 3; -}; + // buffers + var indices = new THREE.BufferAttribute( new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ) , 1 ); + var vertices = new THREE.BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); + var normals = new THREE.BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); + var uvs = new THREE.BufferAttribute( new Float32Array( vertexCount * 2 ), 2 ); -THREE.FaceNormalsHelper.prototype = Object.create( THREE.Line.prototype ); + // helper variables + var i, j, index = 0, indexOffset = 0; -THREE.FaceNormalsHelper.prototype.update = ( function ( object ) { + var vertex = new THREE.Vector3(); + var normal = new THREE.Vector3(); + var uv = new THREE.Vector2(); - var v1 = new THREE.Vector3(); + var P1 = new THREE.Vector3(); + var P2 = new THREE.Vector3(); - return function ( object ) { + var B = new THREE.Vector3(); + var T = new THREE.Vector3(); + var N = new THREE.Vector3(); - this.object.updateMatrixWorld( true ); + // generate vertices, normals and uvs - this.normalMatrix.getNormalMatrix( this.object.matrixWorld ); + for ( i = 0; i <= tubularSegments; ++ i ) { - var vertices = this.geometry.vertices; + // the radian "u" is used to calculate the position on the torus curve of the current tubular segement - var faces = this.object.geometry.faces; + var u = i / tubularSegments * p * Math.PI * 2; - var worldMatrix = this.object.matrixWorld; + // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead. + // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions - for ( var i = 0, l = faces.length; i < l; i ++ ) { + calculatePositionOnCurve( u, p, q, radius, P1 ); + calculatePositionOnCurve( u + 0.01, p, q, radius, P2 ); - var face = faces[ i ]; + // calculate orthonormal basis - v1.copy( face.normal ).applyMatrix3( this.normalMatrix ).normalize().multiplyScalar( this.size ); + T.subVectors( P2, P1 ); + N.addVectors( P2, P1 ); + B.crossVectors( T, N ); + N.crossVectors( B, T ); - var idx = 2 * i; + // normalize B, N. T can be ignored, we don't use it - vertices[ idx ].copy( face.centroid ).applyMatrix4( worldMatrix ); + B.normalize(); + N.normalize(); - vertices[ idx + 1 ].addVectors( vertices[ idx ], v1 ); + for ( j = 0; j <= radialSegments; ++ j ) { - } + // now calculate the vertices. they are nothing more than an extrusion of the torus curve. + // because we extrude a shape in the xy-plane, there is no need to calculate a z-value. - this.geometry.verticesNeedUpdate = true; + var v = j / radialSegments * Math.PI * 2; + var cx = - tube * Math.cos( v ); + var cy = tube * Math.sin( v ); - return this; + // now calculate the final vertex position. + // first we orient the extrusion with our basis vectos, then we add it to the current position on the curve - } + vertex.x = P1.x + ( cx * N.x + cy * B.x ); + vertex.y = P1.y + ( cx * N.y + cy * B.y ); + vertex.z = P1.z + ( cx * N.z + cy * B.z ); -}()); + // vertex + vertices.setXYZ( index, vertex.x, vertex.y, vertex.z ); + // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal) + normal.subVectors( vertex, P1 ).normalize(); + normals.setXYZ( index, normal.x, normal.y, normal.z ); -/** - * @author mrdoob / http://mrdoob.com/ - */ + // uv + uv.x = i / tubularSegments; + uv.y = j / radialSegments; + uvs.setXY( index, uv.x, uv.y ); -THREE.GridHelper = function ( size, step ) { + // increase index + index ++; - var geometry = new THREE.Geometry(); - var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } ); + } + + } - this.color1 = new THREE.Color( 0x444444 ); - this.color2 = new THREE.Color( 0x888888 ); + // generate indices - for ( var i = - size; i <= size; i += step ) { + for ( j = 1; j <= tubularSegments; j ++ ) { - geometry.vertices.push( - new THREE.Vector3( - size, 0, i ), new THREE.Vector3( size, 0, i ), - new THREE.Vector3( i, 0, - size ), new THREE.Vector3( i, 0, size ) - ); + for ( i = 1; i <= radialSegments; i ++ ) { + + // indices + var a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 ); + var b = ( radialSegments + 1 ) * j + ( i - 1 ); + var c = ( radialSegments + 1 ) * j + i; + var d = ( radialSegments + 1 ) * ( j - 1 ) + i; + + // face one + indices.setX( indexOffset, a ); indexOffset++; + indices.setX( indexOffset, b ); indexOffset++; + indices.setX( indexOffset, d ); indexOffset++; - var color = i === 0 ? this.color1 : this.color2; + // face two + indices.setX( indexOffset, b ); indexOffset++; + indices.setX( indexOffset, c ); indexOffset++; + indices.setX( indexOffset, d ); indexOffset++; - geometry.colors.push( color, color, color, color ); + } } - THREE.Line.call( this, geometry, material, THREE.LinePieces ); + // build geometry -}; + this.setIndex( indices ); + this.addAttribute( 'position', vertices ); + this.addAttribute( 'normal', normals ); + this.addAttribute( 'uv', uvs ); -THREE.GridHelper.prototype = Object.create( THREE.Line.prototype ); + // this function calculates the current position on the torus curve -THREE.GridHelper.prototype.setColors = function( colorCenterLine, colorGrid ) { + function calculatePositionOnCurve( u, p, q, radius, position ) { + + var cu = Math.cos( u ); + var su = Math.sin( u ); + var quOverP = q / p * u; + var cs = Math.cos( quOverP ); - this.color1.set( colorCenterLine ); - this.color2.set( colorGrid ); + position.x = radius * ( 2 + cs ) * 0.5 * cu; + position.y = radius * ( 2 + cs ) * su * 0.5; + position.z = radius * Math.sin( quOverP ) * 0.5; - this.geometry.colorsNeedUpdate = true; + } -} +}; + +THREE.TorusKnotBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.TorusKnotBufferGeometry.prototype.constructor = THREE.TorusKnotBufferGeometry; + +// File:src/extras/geometries/TorusKnotGeometry.js /** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ + * @author oosmoxiecode */ -THREE.HemisphereLightHelper = function ( light, sphereSize, arrowLength, domeSize ) { +THREE.TorusKnotGeometry = function ( radius, tube, tubularSegments, radialSegments, p, q, heightScale ) { - THREE.Object3D.call( this ); + THREE.Geometry.call( this ); - this.light = light; - this.light.updateMatrixWorld(); + this.type = 'TorusKnotGeometry'; - this.matrixWorld = light.matrixWorld; - this.matrixAutoUpdate = false; + this.parameters = { + radius: radius, + tube: tube, + tubularSegments: tubularSegments, + radialSegments: radialSegments, + p: p, + q: q + }; - this.colors = [ new THREE.Color(), new THREE.Color() ]; + if( heightScale !== undefined ) console.warn( 'THREE.TorusKnotGeometry: heightScale has been deprecated. Use .scale( x, y, z ) instead.' ); - var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 ); - geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) ); + this.fromBufferGeometry( new THREE.TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) ); + this.mergeVertices(); - for ( var i = 0, il = 8; i < il; i ++ ) { +}; - geometry.faces[ i ].color = this.colors[ i < 4 ? 0 : 1 ]; +THREE.TorusKnotGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.TorusKnotGeometry.prototype.constructor = THREE.TorusKnotGeometry; - } +// File:src/extras/geometries/TubeGeometry.js - var material = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors, wireframe: true } ); +/** + * @author WestLangley / https://github.com/WestLangley + * @author zz85 / https://github.com/zz85 + * @author miningold / https://github.com/miningold + * @author jonobr1 / https://github.com/jonobr1 + * + * Modified from the TorusKnotGeometry by @oosmoxiecode + * + * Creates a tube which extrudes along a 3d spline + * + * Uses parallel transport frames as described in + * http://www.cs.indiana.edu/pub/techreports/TR425.pdf + */ - this.lightSphere = new THREE.Mesh( geometry, material ); - this.add( this.lightSphere ); +THREE.TubeGeometry = function ( path, segments, radius, radialSegments, closed, taper ) { - this.update(); + THREE.Geometry.call( this ); -}; + this.type = 'TubeGeometry'; -THREE.HemisphereLightHelper.prototype = Object.create( THREE.Object3D.prototype ); + this.parameters = { + path: path, + segments: segments, + radius: radius, + radialSegments: radialSegments, + closed: closed, + taper: taper + }; -THREE.HemisphereLightHelper.prototype.update = function () { + segments = segments || 64; + radius = radius || 1; + radialSegments = radialSegments || 8; + closed = closed || false; + taper = taper || THREE.TubeGeometry.NoTaper; - var vector = new THREE.Vector3(); + var grid = []; - return function () { + var scope = this, - this.colors[ 0 ].copy( this.light.color ).multiplyScalar( this.light.intensity ); - this.colors[ 1 ].copy( this.light.groundColor ).multiplyScalar( this.light.intensity ); + tangent, + normal, + binormal, - this.lightSphere.lookAt( vector.getPositionFromMatrix( this.light.matrixWorld ).negate() ); - this.lightSphere.geometry.colorsNeedUpdate = true; + numpoints = segments + 1, - } + u, v, r, -}(); + cx, cy, + pos, pos2 = new THREE.Vector3(), + i, j, + ip, jp, + a, b, c, d, + uva, uvb, uvc, uvd; + var frames = new THREE.TubeGeometry.FrenetFrames( path, segments, closed ), + tangents = frames.tangents, + normals = frames.normals, + binormals = frames.binormals; -/** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - */ + // proxy internals + this.tangents = tangents; + this.normals = normals; + this.binormals = binormals; -THREE.PointLightHelper = function ( light, sphereSize ) { + function vert( x, y, z ) { - this.light = light; - this.light.updateMatrixWorld(); + return scope.vertices.push( new THREE.Vector3( x, y, z ) ) - 1; - var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 ); - var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } ); - material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + } - THREE.Mesh.call( this, geometry, material ); + // construct the grid - this.matrixWorld = this.light.matrixWorld; - this.matrixAutoUpdate = false; + for ( i = 0; i < numpoints; i ++ ) { - /* - var distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 ); - var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); + grid[ i ] = []; - this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); - this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); + u = i / ( numpoints - 1 ); - var d = light.distance; + pos = path.getPointAt( u ); + + tangent = tangents[ i ]; + normal = normals[ i ]; + binormal = binormals[ i ]; + + r = radius * taper( u ); + + for ( j = 0; j < radialSegments; j ++ ) { + + v = j / radialSegments * 2 * Math.PI; - if ( d === 0.0 ) { + cx = - r * Math.cos( v ); // TODO: Hack: Negating it so it faces outside. + cy = r * Math.sin( v ); - this.lightDistance.visible = false; + pos2.copy( pos ); + pos2.x += cx * normal.x + cy * binormal.x; + pos2.y += cx * normal.y + cy * binormal.y; + pos2.z += cx * normal.z + cy * binormal.z; - } else { + grid[ i ][ j ] = vert( pos2.x, pos2.y, pos2.z ); - this.lightDistance.scale.set( d, d, d ); + } } - this.add( this.lightDistance ); - */ -}; + // construct the mesh -THREE.PointLightHelper.prototype = Object.create( THREE.Mesh.prototype ); + for ( i = 0; i < segments; i ++ ) { -THREE.PointLightHelper.prototype.update = function () { + for ( j = 0; j < radialSegments; j ++ ) { - this.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + ip = ( closed ) ? ( i + 1 ) % segments : i + 1; + jp = ( j + 1 ) % radialSegments; - /* - var d = this.light.distance; + a = grid[ i ][ j ]; // *** NOT NECESSARILY PLANAR ! *** + b = grid[ ip ][ j ]; + c = grid[ ip ][ jp ]; + d = grid[ i ][ jp ]; - if ( d === 0.0 ) { + uva = new THREE.Vector2( i / segments, j / radialSegments ); + uvb = new THREE.Vector2( ( i + 1 ) / segments, j / radialSegments ); + uvc = new THREE.Vector2( ( i + 1 ) / segments, ( j + 1 ) / radialSegments ); + uvd = new THREE.Vector2( i / segments, ( j + 1 ) / radialSegments ); - this.lightDistance.visible = false; + this.faces.push( new THREE.Face3( a, b, d ) ); + this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); - } else { + this.faces.push( new THREE.Face3( b, c, d ) ); + this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); - this.lightDistance.visible = true; - this.lightDistance.scale.set( d, d, d ); + } } - */ + + this.computeFaceNormals(); + this.computeVertexNormals(); }; +THREE.TubeGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.TubeGeometry.prototype.constructor = THREE.TubeGeometry; -/** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley -*/ +THREE.TubeGeometry.NoTaper = function ( u ) { -THREE.SpotLightHelper = function ( light ) { + return 1; - THREE.Object3D.call( this ); +}; - this.light = light; - this.light.updateMatrixWorld(); +THREE.TubeGeometry.SinusoidalTaper = function ( u ) { - this.matrixWorld = light.matrixWorld; - this.matrixAutoUpdate = false; + return Math.sin( Math.PI * u ); - var geometry = new THREE.CylinderGeometry( 0, 1, 1, 8, 1, true ); +}; - geometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, -0.5, 0 ) ); - geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) ); +// For computing of Frenet frames, exposing the tangents, normals and binormals the spline +THREE.TubeGeometry.FrenetFrames = function ( path, segments, closed ) { - var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } ); - - this.cone = new THREE.Mesh( geometry, material ); - this.add( this.cone ); + var normal = new THREE.Vector3(), - this.update(); + tangents = [], + normals = [], + binormals = [], -}; + vec = new THREE.Vector3(), + mat = new THREE.Matrix4(), -THREE.SpotLightHelper.prototype = Object.create( THREE.Object3D.prototype ); + numpoints = segments + 1, + theta, + smallest, -THREE.SpotLightHelper.prototype.update = function () { + tx, ty, tz, + i, u; - var vector = new THREE.Vector3(); - var vector2 = new THREE.Vector3(); - return function () { + // expose internals + this.tangents = tangents; + this.normals = normals; + this.binormals = binormals; - var coneLength = this.light.distance ? this.light.distance : 10000; - var coneWidth = coneLength * Math.tan( this.light.angle ); + // compute the tangent vectors for each segment on the path - this.cone.scale.set( coneWidth, coneWidth, coneLength ); + for ( i = 0; i < numpoints; i ++ ) { - vector.getPositionFromMatrix( this.light.matrixWorld ); - vector2.getPositionFromMatrix( this.light.target.matrixWorld ); + u = i / ( numpoints - 1 ); - this.cone.lookAt( vector2.sub( vector ) ); + tangents[ i ] = path.getTangentAt( u ); + tangents[ i ].normalize(); - this.cone.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); + } - }; + initialNormal3(); -}(); + /* + function initialNormal1(lastBinormal) { + // fixed start binormal. Has dangers of 0 vectors + normals[ 0 ] = new THREE.Vector3(); + binormals[ 0 ] = new THREE.Vector3(); + if (lastBinormal===undefined) lastBinormal = new THREE.Vector3( 0, 0, 1 ); + normals[ 0 ].crossVectors( lastBinormal, tangents[ 0 ] ).normalize(); + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize(); + } -/** - * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley -*/ + function initialNormal2() { -THREE.VertexNormalsHelper = function ( object, size, hex, linewidth ) { + // This uses the Frenet-Serret formula for deriving binormal + var t2 = path.getTangentAt( epsilon ); - this.object = object; + normals[ 0 ] = new THREE.Vector3().subVectors( t2, tangents[ 0 ] ).normalize(); + binormals[ 0 ] = new THREE.Vector3().crossVectors( tangents[ 0 ], normals[ 0 ] ); - this.size = size || 1; + normals[ 0 ].crossVectors( binormals[ 0 ], tangents[ 0 ] ).normalize(); // last binormal x tangent + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize(); - var color = hex || 0xff0000; + } + */ - var width = linewidth || 1; + function initialNormal3() { - var geometry = new THREE.Geometry(); + // select an initial normal vector perpendicular to the first tangent vector, + // and in the direction of the smallest tangent xyz component - var vertices = object.geometry.vertices; + normals[ 0 ] = new THREE.Vector3(); + binormals[ 0 ] = new THREE.Vector3(); + smallest = Number.MAX_VALUE; + tx = Math.abs( tangents[ 0 ].x ); + ty = Math.abs( tangents[ 0 ].y ); + tz = Math.abs( tangents[ 0 ].z ); - var faces = object.geometry.faces; + if ( tx <= smallest ) { - for ( var i = 0, l = faces.length; i < l; i ++ ) { + smallest = tx; + normal.set( 1, 0, 0 ); - var face = faces[ i ]; + } - for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { + if ( ty <= smallest ) { - geometry.vertices.push( new THREE.Vector3() ); - geometry.vertices.push( new THREE.Vector3() ); + smallest = ty; + normal.set( 0, 1, 0 ); } - } + if ( tz <= smallest ) { - THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces ); + normal.set( 0, 0, 1 ); - this.matrixAutoUpdate = false; + } - this.normalMatrix = new THREE.Matrix3(); + vec.crossVectors( tangents[ 0 ], normal ).normalize(); - this.update(); + normals[ 0 ].crossVectors( tangents[ 0 ], vec ); + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); -}; + } -THREE.VertexNormalsHelper.prototype = Object.create( THREE.Line.prototype ); -THREE.VertexNormalsHelper.prototype.update = ( function ( object ) { + // compute the slowly-varying normal and binormal vectors for each segment on the path - var v1 = new THREE.Vector3(); + for ( i = 1; i < numpoints; i ++ ) { - return function( object ) { + normals[ i ] = normals[ i - 1 ].clone(); - var keys = [ 'a', 'b', 'c', 'd' ]; + binormals[ i ] = binormals[ i - 1 ].clone(); - this.object.updateMatrixWorld( true ); + vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); - this.normalMatrix.getNormalMatrix( this.object.matrixWorld ); + if ( vec.length() > Number.EPSILON ) { - var vertices = this.geometry.vertices; + vec.normalize(); - var verts = this.object.geometry.vertices; + theta = Math.acos( THREE.Math.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors - var faces = this.object.geometry.faces; + normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); - var worldMatrix = this.object.matrixWorld; + } - var idx = 0; + binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); - for ( var i = 0, l = faces.length; i < l; i ++ ) { + } - var face = faces[ i ]; - for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { + // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same - var vertexId = face[ keys[ j ] ]; - var vertex = verts[ vertexId ]; + if ( closed ) { - var normal = face.vertexNormals[ j ]; + theta = Math.acos( THREE.Math.clamp( normals[ 0 ].dot( normals[ numpoints - 1 ] ), - 1, 1 ) ); + theta /= ( numpoints - 1 ); - vertices[ idx ].copy( vertex ).applyMatrix4( worldMatrix ); + if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ numpoints - 1 ] ) ) > 0 ) { - v1.copy( normal ).applyMatrix3( this.normalMatrix ).normalize().multiplyScalar( this.size ); + theta = - theta; - v1.add( vertices[ idx ] ); - idx = idx + 1; + } - vertices[ idx ].copy( v1 ); - idx = idx + 1; + for ( i = 1; i < numpoints; i ++ ) { - } + // twist a little... + normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); + binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); } - this.geometry.verticesNeedUpdate = true; - - return this; - } -}()); +}; + +// File:src/extras/geometries/PolyhedronGeometry.js /** - * @author mrdoob / http://mrdoob.com/ + * @author clockworkgeek / https://github.com/clockworkgeek + * @author timothypratley / https://github.com/timothypratley * @author WestLangley / http://github.com/WestLangley */ -THREE.VertexTangentsHelper = function ( object, size, hex, linewidth ) { +THREE.PolyhedronGeometry = function ( vertices, indices, radius, detail ) { - this.object = object; + THREE.Geometry.call( this ); - this.size = size || 1; + this.type = 'PolyhedronGeometry'; - var color = hex || 0x0000ff; + this.parameters = { + vertices: vertices, + indices: indices, + radius: radius, + detail: detail + }; - var width = linewidth || 1; + radius = radius || 1; + detail = detail || 0; - var geometry = new THREE.Geometry(); + var that = this; - var vertices = object.geometry.vertices; + for ( var i = 0, l = vertices.length; i < l; i += 3 ) { - var faces = object.geometry.faces; + prepare( new THREE.Vector3( vertices[ i ], vertices[ i + 1 ], vertices[ i + 2 ] ) ); - for ( var i = 0, l = faces.length; i < l; i ++ ) { + } - var face = faces[ i ]; + var p = this.vertices; - for ( var j = 0, jl = face.vertexTangents.length; j < jl; j ++ ) { + var faces = []; - geometry.vertices.push( new THREE.Vector3() ); - geometry.vertices.push( new THREE.Vector3() ); + for ( var i = 0, j = 0, l = indices.length; i < l; i += 3, j ++ ) { - } + var v1 = p[ indices[ i ] ]; + var v2 = p[ indices[ i + 1 ] ]; + var v3 = p[ indices[ i + 2 ] ]; + + faces[ j ] = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] ); } - THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ), THREE.LinePieces ); + var centroid = new THREE.Vector3(); - this.matrixAutoUpdate = false; + for ( var i = 0, l = faces.length; i < l; i ++ ) { - this.update(); + subdivide( faces[ i ], detail ); -}; + } -THREE.VertexTangentsHelper.prototype = Object.create( THREE.Line.prototype ); -THREE.VertexTangentsHelper.prototype.update = ( function ( object ) { + // Handle case when face straddles the seam - var v1 = new THREE.Vector3(); + for ( var i = 0, l = this.faceVertexUvs[ 0 ].length; i < l; i ++ ) { - return function( object ) { + var uvs = this.faceVertexUvs[ 0 ][ i ]; - var keys = [ 'a', 'b', 'c', 'd' ]; + var x0 = uvs[ 0 ].x; + var x1 = uvs[ 1 ].x; + var x2 = uvs[ 2 ].x; - this.object.updateMatrixWorld( true ); + var max = Math.max( x0, x1, x2 ); + var min = Math.min( x0, x1, x2 ); - var vertices = this.geometry.vertices; + if ( max > 0.9 && min < 0.1 ) { - var verts = this.object.geometry.vertices; + // 0.9 is somewhat arbitrary - var faces = this.object.geometry.faces; + if ( x0 < 0.2 ) uvs[ 0 ].x += 1; + if ( x1 < 0.2 ) uvs[ 1 ].x += 1; + if ( x2 < 0.2 ) uvs[ 2 ].x += 1; - var worldMatrix = this.object.matrixWorld; + } - var idx = 0; + } - for ( var i = 0, l = faces.length; i < l; i ++ ) { - var face = faces[ i ]; + // Apply radius - for ( var j = 0, jl = face.vertexTangents.length; j < jl; j ++ ) { + for ( var i = 0, l = this.vertices.length; i < l; i ++ ) { - var vertexId = face[ keys[ j ] ]; - var vertex = verts[ vertexId ]; + this.vertices[ i ].multiplyScalar( radius ); - var tangent = face.vertexTangents[ j ]; + } - vertices[ idx ].copy( vertex ).applyMatrix4( worldMatrix ); - v1.copy( tangent ).transformDirection( worldMatrix ).multiplyScalar( this.size ); + // Merge vertices - v1.add( vertices[ idx ] ); - idx = idx + 1; + this.mergeVertices(); - vertices[ idx ].copy( v1 ); - idx = idx + 1; + this.computeFaceNormals(); - } + this.boundingSphere = new THREE.Sphere( new THREE.Vector3(), radius ); - } - this.geometry.verticesNeedUpdate = true; + // Project vector onto sphere's surface - return this; + function prepare( vector ) { + + var vertex = vector.normalize().clone(); + vertex.index = that.vertices.push( vertex ) - 1; + + // Texture coords are equivalent to map coords, calculate angle and convert to fraction of a circle. + + var u = azimuth( vector ) / 2 / Math.PI + 0.5; + var v = inclination( vector ) / Math.PI + 0.5; + vertex.uv = new THREE.Vector2( u, 1 - v ); + + return vertex; } -}()); -/** - * @author mrdoob / http://mrdoob.com/ - */ + // Approximate a curved face with recursively sub-divided triangles. -THREE.WireframeHelper = function ( object ) { + function make( v1, v2, v3 ) { - var edge = [ 0, 0 ], hash = {}; - var sortFunction = function ( a, b ) { return a - b }; + var face = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] ); + that.faces.push( face ); - var keys = [ 'a', 'b', 'c', 'd' ]; - var geometry = new THREE.Geometry(); + centroid.copy( v1 ).add( v2 ).add( v3 ).divideScalar( 3 ); + + var azi = azimuth( centroid ); + + that.faceVertexUvs[ 0 ].push( [ + correctUV( v1.uv, v1, azi ), + correctUV( v2.uv, v2, azi ), + correctUV( v3.uv, v3, azi ) + ] ); + + } + + + // Analytically subdivide a face to the required detail level. + + function subdivide( face, detail ) { - var vertices = object.geometry.vertices; - var faces = object.geometry.faces; + var cols = Math.pow( 2, detail ); + var a = prepare( that.vertices[ face.a ] ); + var b = prepare( that.vertices[ face.b ] ); + var c = prepare( that.vertices[ face.c ] ); + var v = []; + + // Construct all of the vertices for this subdivision. + + for ( var i = 0 ; i <= cols; i ++ ) { - for ( var i = 0, l = faces.length; i < l; i ++ ) { + v[ i ] = []; - var face = faces[ i ]; + var aj = prepare( a.clone().lerp( c, i / cols ) ); + var bj = prepare( b.clone().lerp( c, i / cols ) ); + var rows = cols - i; - for ( var j = 0; j < 3; j ++ ) { + for ( var j = 0; j <= rows; j ++ ) { - edge[ 0 ] = face[ keys[ j ] ]; - edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; - edge.sort( sortFunction ); + if ( j === 0 && i === cols ) { - var key = edge.toString(); + v[ i ][ j ] = aj; - if ( hash[ key ] === undefined ) { + } else { - geometry.vertices.push( vertices[ edge[ 0 ] ] ); - geometry.vertices.push( vertices[ edge[ 1 ] ] ); + v[ i ][ j ] = prepare( aj.clone().lerp( bj, j / rows ) ); - hash[ key ] = true; + } } } - } - - THREE.Line.call( this, geometry, new THREE.LineBasicMaterial( { color: 0xffffff } ), THREE.LinePieces ); - - this.matrixAutoUpdate = false; - this.matrixWorld = object.matrixWorld; + // Construct all of the faces. -}; + for ( var i = 0; i < cols ; i ++ ) { -THREE.WireframeHelper.prototype = Object.create( THREE.Line.prototype ); + for ( var j = 0; j < 2 * ( cols - i ) - 1; j ++ ) { -/** - * @author alteredq / http://alteredqualia.com/ - */ + var k = Math.floor( j / 2 ); -THREE.ImmediateRenderObject = function () { + if ( j % 2 === 0 ) { - THREE.Object3D.call( this ); + make( + v[ i ][ k + 1 ], + v[ i + 1 ][ k ], + v[ i ][ k ] + ); - this.render = function ( renderCallback ) { }; + } else { -}; + make( + v[ i ][ k + 1 ], + v[ i + 1 ][ k + 1 ], + v[ i + 1 ][ k ] + ); -THREE.ImmediateRenderObject.prototype = Object.create( THREE.Object3D.prototype ); + } -/** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - */ + } -THREE.LensFlare = function ( texture, size, distance, blending, color ) { + } - THREE.Object3D.call( this ); + } - this.lensFlares = []; - this.positionScreen = new THREE.Vector3(); - this.customUpdateCallback = undefined; + // Angle around the Y axis, counter-clockwise when looking from above. - if( texture !== undefined ) { + function azimuth( vector ) { - this.add( texture, size, distance, blending, color ); + return Math.atan2( vector.z, - vector.x ); } -}; - -THREE.LensFlare.prototype = Object.create( THREE.Object3D.prototype ); + // Angle above the XZ plane. -/* - * Add: adds another flare - */ - -THREE.LensFlare.prototype.add = function ( texture, size, distance, blending, color, opacity ) { + function inclination( vector ) { - 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; + return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) ); - distance = Math.min( distance, Math.max( 0, distance ) ); + } - this.lensFlares.push( { texture: texture, // THREE.Texture - size: size, // size in pixels (-1 = use texture.width) - distance: distance, // distance (0-1) from light source (0=at light source) - x: 0, y: 0, z: 0, // screen position (-1 => 1) z = 0 is ontop z = 1 is back - scale: 1, // scale - rotation: 1, // rotation - opacity: opacity, // opacity - color: color, // color - blending: blending } ); // blending -}; + // Texture fixing helper. Spheres have some odd behaviours. + function correctUV( uv, vector, azimuth ) { -/* - * Update lens flares update positions on all flares based on the screen position - * Set myLensFlare.customUpdateCallback to alter the flares in your project specific way. - */ + if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) uv = new THREE.Vector2( uv.x - 1, uv.y ); + if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) uv = new THREE.Vector2( azimuth / 2 / Math.PI + 0.5, uv.y ); + return uv.clone(); -THREE.LensFlare.prototype.updateLensFlares = function () { + } - var f, fl = this.lensFlares.length; - var flare; - var vecX = -this.positionScreen.x * 2; - var vecY = -this.positionScreen.y * 2; - for( f = 0; f < fl; f ++ ) { +}; - flare = this.lensFlares[ f ]; +THREE.PolyhedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.PolyhedronGeometry.prototype.constructor = THREE.PolyhedronGeometry; - flare.x = this.positionScreen.x + vecX * flare.distance; - flare.y = this.positionScreen.y + vecY * flare.distance; +// File:src/extras/geometries/DodecahedronGeometry.js - flare.wantedRotation = flare.x * Math.PI * 0.25; - flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25; +/** + * @author Abe Pazos / https://hamoid.com + */ - } +THREE.DodecahedronGeometry = function ( radius, detail ) { -}; + var t = ( 1 + Math.sqrt( 5 ) ) / 2; + var r = 1 / t; + var vertices = [ + // (±1, ±1, ±1) + - 1, - 1, - 1, - 1, - 1, 1, + - 1, 1, - 1, - 1, 1, 1, + 1, - 1, - 1, 1, - 1, 1, + 1, 1, - 1, 1, 1, 1, + // (0, ±1/φ, ±φ) + 0, - r, - t, 0, - r, t, + 0, r, - t, 0, r, t, + // (±1/φ, ±φ, 0) + - r, - t, 0, - r, t, 0, + r, - t, 0, r, t, 0, + // (±φ, 0, ±1/φ) + - t, 0, - r, t, 0, - r, + - t, 0, r, t, 0, r + ]; + var indices = [ + 3, 11, 7, 3, 7, 15, 3, 15, 13, + 7, 19, 17, 7, 17, 6, 7, 6, 15, + 17, 4, 8, 17, 8, 10, 17, 10, 6, + 8, 0, 16, 8, 16, 2, 8, 2, 10, + 0, 12, 1, 0, 1, 18, 0, 18, 16, + 6, 10, 2, 6, 2, 13, 6, 13, 15, + 2, 16, 18, 2, 18, 3, 2, 3, 13, + 18, 1, 9, 18, 9, 11, 18, 11, 3, + 4, 14, 12, 4, 12, 0, 4, 0, 8, + 11, 9, 5, 11, 5, 19, 11, 19, 7, + 19, 5, 14, 19, 14, 4, 19, 4, 17, + 1, 12, 14, 1, 14, 5, 1, 5, 9 + ]; + THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); + this.type = 'DodecahedronGeometry'; + this.parameters = { + radius: radius, + detail: detail + }; +}; +THREE.DodecahedronGeometry.prototype = Object.create( THREE.PolyhedronGeometry.prototype ); +THREE.DodecahedronGeometry.prototype.constructor = THREE.DodecahedronGeometry; +// File:src/extras/geometries/IcosahedronGeometry.js /** - * @author alteredq / http://alteredqualia.com/ + * @author timothypratley / https://github.com/timothypratley */ -THREE.MorphBlendMesh = function( geometry, material ) { - - THREE.Mesh.call( this, geometry, material ); - - this.animationsMap = {}; - this.animationsList = []; +THREE.IcosahedronGeometry = function ( radius, detail ) { - // prepare default animation - // (all frames played together in 1 second) + var t = ( 1 + Math.sqrt( 5 ) ) / 2; - var numFrames = this.geometry.morphTargets.length; + var vertices = [ + - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, 0, + 0, - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, + t, 0, - 1, t, 0, 1, - t, 0, - 1, - t, 0, 1 + ]; - var name = "__default"; + var indices = [ + 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, + 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, + 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, + 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1 + ]; - var startFrame = 0; - var endFrame = numFrames - 1; + THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); - var fps = numFrames / 1; + this.type = 'IcosahedronGeometry'; - this.createAnimation( name, startFrame, endFrame, fps ); - this.setAnimationWeight( name, 1 ); + this.parameters = { + radius: radius, + detail: detail + }; }; -THREE.MorphBlendMesh.prototype = Object.create( THREE.Mesh.prototype ); - -THREE.MorphBlendMesh.prototype.createAnimation = function ( name, start, end, fps ) { - - var animation = { +THREE.IcosahedronGeometry.prototype = Object.create( THREE.PolyhedronGeometry.prototype ); +THREE.IcosahedronGeometry.prototype.constructor = THREE.IcosahedronGeometry; - startFrame: start, - endFrame: end, +// File:src/extras/geometries/OctahedronGeometry.js - length: end - start + 1, +/** + * @author timothypratley / https://github.com/timothypratley + */ - fps: fps, - duration: ( end - start ) / fps, +THREE.OctahedronGeometry = function ( radius, detail ) { - lastFrame: 0, - currentFrame: 0, + var vertices = [ + 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1 + ]; - active: false, + var indices = [ + 0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2 + ]; - time: 0, - direction: 1, - weight: 1, + THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); - directionBackwards: false, - mirroredLoop: false + this.type = 'OctahedronGeometry'; + this.parameters = { + radius: radius, + detail: detail }; - this.animationsMap[ name ] = animation; - this.animationsList.push( animation ); - }; -THREE.MorphBlendMesh.prototype.autoCreateAnimations = function ( fps ) { - - var pattern = /([a-z]+)(\d+)/; +THREE.OctahedronGeometry.prototype = Object.create( THREE.PolyhedronGeometry.prototype ); +THREE.OctahedronGeometry.prototype.constructor = THREE.OctahedronGeometry; - var firstAnimation, frameRanges = {}; - - var geometry = this.geometry; - - for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) { +// File:src/extras/geometries/TetrahedronGeometry.js - var morph = geometry.morphTargets[ i ]; - var chunks = morph.name.match( pattern ); +/** + * @author timothypratley / https://github.com/timothypratley + */ - if ( chunks && chunks.length > 1 ) { +THREE.TetrahedronGeometry = function ( radius, detail ) { - var name = chunks[ 1 ]; - var num = chunks[ 2 ]; + var vertices = [ + 1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1 + ]; - if ( ! frameRanges[ name ] ) frameRanges[ name ] = { start: Infinity, end: -Infinity }; + var indices = [ + 2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1 + ]; - var range = frameRanges[ name ]; + THREE.PolyhedronGeometry.call( this, vertices, indices, radius, detail ); - if ( i < range.start ) range.start = i; - if ( i > range.end ) range.end = i; + this.type = 'TetrahedronGeometry'; - if ( ! firstAnimation ) firstAnimation = name; + this.parameters = { + radius: radius, + detail: detail + }; - } +}; - } +THREE.TetrahedronGeometry.prototype = Object.create( THREE.PolyhedronGeometry.prototype ); +THREE.TetrahedronGeometry.prototype.constructor = THREE.TetrahedronGeometry; - for ( var name in frameRanges ) { +// File:src/extras/geometries/ParametricGeometry.js - var range = frameRanges[ name ]; - this.createAnimation( name, range.start, range.end, fps ); +/** + * @author zz85 / https://github.com/zz85 + * Parametric Surfaces Geometry + * based on the brilliant article by @prideout http://prideout.net/blog/?p=44 + * + * new THREE.ParametricGeometry( parametricFunction, uSegments, ySegements ); + * + */ - } +THREE.ParametricGeometry = function ( func, slices, stacks ) { - this.firstAnimation = firstAnimation; + THREE.Geometry.call( this ); -}; + this.type = 'ParametricGeometry'; -THREE.MorphBlendMesh.prototype.setAnimationDirectionForward = function ( name ) { + this.parameters = { + func: func, + slices: slices, + stacks: stacks + }; - var animation = this.animationsMap[ name ]; + var verts = this.vertices; + var faces = this.faces; + var uvs = this.faceVertexUvs[ 0 ]; - if ( animation ) { + var i, j, p; + var u, v; - animation.direction = 1; - animation.directionBackwards = false; + var sliceCount = slices + 1; - } + for ( i = 0; i <= stacks; i ++ ) { -}; + v = i / stacks; -THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward = function ( name ) { + for ( j = 0; j <= slices; j ++ ) { - var animation = this.animationsMap[ name ]; + u = j / slices; - if ( animation ) { + p = func( u, v ); + verts.push( p ); - animation.direction = -1; - animation.directionBackwards = true; + } } -}; + var a, b, c, d; + var uva, uvb, uvc, uvd; -THREE.MorphBlendMesh.prototype.setAnimationFPS = function ( name, fps ) { + for ( i = 0; i < stacks; i ++ ) { - var animation = this.animationsMap[ name ]; + for ( j = 0; j < slices; j ++ ) { - if ( animation ) { + a = i * sliceCount + j; + b = i * sliceCount + j + 1; + c = ( i + 1 ) * sliceCount + j + 1; + d = ( i + 1 ) * sliceCount + j; - animation.fps = fps; - animation.duration = ( animation.end - animation.start ) / animation.fps; + uva = new THREE.Vector2( j / slices, i / stacks ); + uvb = new THREE.Vector2( ( j + 1 ) / slices, i / stacks ); + uvc = new THREE.Vector2( ( j + 1 ) / slices, ( i + 1 ) / stacks ); + uvd = new THREE.Vector2( j / slices, ( i + 1 ) / stacks ); - } + faces.push( new THREE.Face3( a, b, d ) ); + uvs.push( [ uva, uvb, uvd ] ); -}; + faces.push( new THREE.Face3( b, c, d ) ); + uvs.push( [ uvb.clone(), uvc, uvd.clone() ] ); -THREE.MorphBlendMesh.prototype.setAnimationDuration = function ( name, duration ) { + } - var animation = this.animationsMap[ name ]; + } - if ( animation ) { + // console.log(this); - animation.duration = duration; - animation.fps = ( animation.end - animation.start ) / animation.duration; + // magic bullet + // var diff = this.mergeVertices(); + // console.log('removed ', diff, ' vertices by merging'); - } + this.computeFaceNormals(); + this.computeVertexNormals(); }; -THREE.MorphBlendMesh.prototype.setAnimationWeight = function ( name, weight ) { +THREE.ParametricGeometry.prototype = Object.create( THREE.Geometry.prototype ); +THREE.ParametricGeometry.prototype.constructor = THREE.ParametricGeometry; - var animation = this.animationsMap[ name ]; +// File:src/extras/geometries/WireframeGeometry.js - if ( animation ) { +/** + * @author mrdoob / http://mrdoob.com/ + */ - animation.weight = weight; +THREE.WireframeGeometry = function ( geometry ) { - } + THREE.BufferGeometry.call( this ); -}; + var edge = [ 0, 0 ], hash = {}; -THREE.MorphBlendMesh.prototype.setAnimationTime = function ( name, time ) { + function sortFunction( a, b ) { - var animation = this.animationsMap[ name ]; + return a - b; - if ( animation ) { + } - animation.time = time; + var keys = [ 'a', 'b', 'c' ]; - } + if ( geometry instanceof THREE.Geometry ) { -}; + var vertices = geometry.vertices; + var faces = geometry.faces; + var numEdges = 0; -THREE.MorphBlendMesh.prototype.getAnimationTime = function ( name ) { + // allocate maximal size + var edges = new Uint32Array( 6 * faces.length ); + + for ( var i = 0, l = faces.length; i < l; i ++ ) { - var time = 0; + var face = faces[ i ]; - var animation = this.animationsMap[ name ]; + for ( var j = 0; j < 3; j ++ ) { - if ( animation ) { + edge[ 0 ] = face[ keys[ j ] ]; + edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; + edge.sort( sortFunction ); - time = animation.time; + var key = edge.toString(); - } + if ( hash[ key ] === undefined ) { - return time; + edges[ 2 * numEdges ] = edge[ 0 ]; + edges[ 2 * numEdges + 1 ] = edge[ 1 ]; + hash[ key ] = true; + numEdges ++; -}; + } -THREE.MorphBlendMesh.prototype.getAnimationDuration = function ( name ) { + } - var duration = -1; + } - var animation = this.animationsMap[ name ]; + var coords = new Float32Array( numEdges * 2 * 3 ); - if ( animation ) { + for ( var i = 0, l = numEdges; i < l; i ++ ) { - duration = animation.duration; + for ( var j = 0; j < 2; j ++ ) { - } + var vertex = vertices[ edges [ 2 * i + j ] ]; - return duration; + var index = 6 * i + 3 * j; + coords[ index + 0 ] = vertex.x; + coords[ index + 1 ] = vertex.y; + coords[ index + 2 ] = vertex.z; -}; + } -THREE.MorphBlendMesh.prototype.playAnimation = function ( name ) { + } - var animation = this.animationsMap[ name ]; + this.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); - if ( animation ) { + } else if ( geometry instanceof THREE.BufferGeometry ) { - animation.time = 0; - animation.active = true; + if ( geometry.index !== null ) { - } else { + // Indexed BufferGeometry - console.warn( "animation[" + name + "] undefined" ); + var indices = geometry.index.array; + var vertices = geometry.attributes.position; + var groups = geometry.groups; + var numEdges = 0; - } + if ( groups.length === 0 ) { -}; + geometry.addGroup( 0, indices.length ); -THREE.MorphBlendMesh.prototype.stopAnimation = function ( name ) { + } - var animation = this.animationsMap[ name ]; + // allocate maximal size + var edges = new Uint32Array( 2 * indices.length ); - if ( animation ) { + for ( var o = 0, ol = groups.length; o < ol; ++ o ) { - animation.active = false; + var group = groups[ o ]; - } + var start = group.start; + var count = group.count; -}; + for ( var i = start, il = start + count; i < il; i += 3 ) { -THREE.MorphBlendMesh.prototype.update = function ( delta ) { + for ( var j = 0; j < 3; j ++ ) { - for ( var i = 0, il = this.animationsList.length; i < il; i ++ ) { + edge[ 0 ] = indices[ i + j ]; + edge[ 1 ] = indices[ i + ( j + 1 ) % 3 ]; + edge.sort( sortFunction ); - var animation = this.animationsList[ i ]; + var key = edge.toString(); - if ( ! animation.active ) continue; + if ( hash[ key ] === undefined ) { - var frameTime = animation.duration / animation.length; + edges[ 2 * numEdges ] = edge[ 0 ]; + edges[ 2 * numEdges + 1 ] = edge[ 1 ]; + hash[ key ] = true; + numEdges ++; - animation.time += animation.direction * delta; + } - if ( animation.mirroredLoop ) { + } - if ( animation.time > animation.duration || animation.time < 0 ) { + } - animation.direction *= -1; + } - if ( animation.time > animation.duration ) { + var coords = new Float32Array( numEdges * 2 * 3 ); - animation.time = animation.duration; - animation.directionBackwards = true; + for ( var i = 0, l = numEdges; i < l; i ++ ) { - } + for ( var j = 0; j < 2; j ++ ) { - if ( animation.time < 0 ) { + var index = 6 * i + 3 * j; + var index2 = edges[ 2 * i + j ]; - animation.time = 0; - animation.directionBackwards = false; + coords[ index + 0 ] = vertices.getX( index2 ); + coords[ index + 1 ] = vertices.getY( index2 ); + coords[ index + 2 ] = vertices.getZ( index2 ); } } + this.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); + } else { - animation.time = animation.time % animation.duration; + // non-indexed BufferGeometry - if ( animation.time < 0 ) animation.time += animation.duration; + var vertices = geometry.attributes.position.array; + var numEdges = vertices.length / 3; + var numTris = numEdges / 3; - } + var coords = new Float32Array( numEdges * 2 * 3 ); - var keyframe = animation.startFrame + THREE.Math.clamp( Math.floor( animation.time / frameTime ), 0, animation.length - 1 ); - var weight = animation.weight; + for ( var i = 0, l = numTris; i < l; i ++ ) { - if ( keyframe !== animation.currentFrame ) { + for ( var j = 0; j < 3; j ++ ) { - this.morphTargetInfluences[ animation.lastFrame ] = 0; - this.morphTargetInfluences[ animation.currentFrame ] = 1 * weight; + var index = 18 * i + 6 * j; - this.morphTargetInfluences[ keyframe ] = 0; + var index1 = 9 * i + 3 * j; + coords[ index + 0 ] = vertices[ index1 ]; + coords[ index + 1 ] = vertices[ index1 + 1 ]; + coords[ index + 2 ] = vertices[ index1 + 2 ]; - animation.lastFrame = animation.currentFrame; - animation.currentFrame = keyframe; + var index2 = 9 * i + 3 * ( ( j + 1 ) % 3 ); + coords[ index + 3 ] = vertices[ index2 ]; + coords[ index + 4 ] = vertices[ index2 + 1 ]; + coords[ index + 5 ] = vertices[ index2 + 2 ]; - } + } - var mix = ( animation.time % frameTime ) / frameTime; + } - if ( animation.directionBackwards ) mix = 1 - mix; + this.addAttribute( 'position', new THREE.BufferAttribute( coords, 3 ) ); - this.morphTargetInfluences[ animation.currentFrame ] = mix * weight; - this.morphTargetInfluences[ animation.lastFrame ] = ( 1 - mix ) * weight; + } } }; +THREE.WireframeGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); +THREE.WireframeGeometry.prototype.constructor = THREE.WireframeGeometry; + +// File:src/extras/helpers/AxisHelper.js + /** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ + * @author sroucheray / http://sroucheray.org/ + * @author mrdoob / http://mrdoob.com/ */ -THREE.LensFlarePlugin = function () { +THREE.AxisHelper = function ( size ) { - var _gl, _renderer, _precision, _lensFlare = {}; + size = size || 1; - this.init = function ( renderer ) { + var vertices = new Float32Array( [ + 0, 0, 0, size, 0, 0, + 0, 0, 0, 0, size, 0, + 0, 0, 0, 0, 0, size + ] ); - _gl = renderer.context; - _renderer = renderer; + var colors = new Float32Array( [ + 1, 0, 0, 1, 0.6, 0, + 0, 1, 0, 0.6, 1, 0, + 0, 0, 1, 0, 0.6, 1 + ] ); - _precision = renderer.getPrecision(); + var geometry = new THREE.BufferGeometry(); + geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); + geometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) ); - _lensFlare.vertices = new Float32Array( 8 + 8 ); - _lensFlare.faces = new Uint16Array( 6 ); + var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } ); - var i = 0; - _lensFlare.vertices[ i++ ] = -1; _lensFlare.vertices[ i++ ] = -1; // vertex - _lensFlare.vertices[ i++ ] = 0; _lensFlare.vertices[ i++ ] = 0; // uv... etc. + THREE.LineSegments.call( this, geometry, material ); - _lensFlare.vertices[ i++ ] = 1; _lensFlare.vertices[ i++ ] = -1; - _lensFlare.vertices[ i++ ] = 1; _lensFlare.vertices[ i++ ] = 0; +}; - _lensFlare.vertices[ i++ ] = 1; _lensFlare.vertices[ i++ ] = 1; - _lensFlare.vertices[ i++ ] = 1; _lensFlare.vertices[ i++ ] = 1; +THREE.AxisHelper.prototype = Object.create( THREE.LineSegments.prototype ); +THREE.AxisHelper.prototype.constructor = THREE.AxisHelper; - _lensFlare.vertices[ i++ ] = -1; _lensFlare.vertices[ i++ ] = 1; - _lensFlare.vertices[ i++ ] = 0; _lensFlare.vertices[ i++ ] = 1; +// File:src/extras/helpers/ArrowHelper.js - i = 0; - _lensFlare.faces[ i++ ] = 0; _lensFlare.faces[ i++ ] = 1; _lensFlare.faces[ i++ ] = 2; - _lensFlare.faces[ i++ ] = 0; _lensFlare.faces[ i++ ] = 2; _lensFlare.faces[ i++ ] = 3; +/** + * @author WestLangley / http://github.com/WestLangley + * @author zz85 / http://github.com/zz85 + * @author bhouston / http://clara.io + * + * Creates an arrow for visualizing directions + * + * Parameters: + * dir - Vector3 + * origin - Vector3 + * length - Number + * color - color in hex value + * headLength - Number + * headWidth - Number + */ - // buffers +THREE.ArrowHelper = ( function () { - _lensFlare.vertexBuffer = _gl.createBuffer(); - _lensFlare.elementBuffer = _gl.createBuffer(); + var lineGeometry = new THREE.BufferGeometry(); + lineGeometry.addAttribute( 'position', new THREE.Float32Attribute( [ 0, 0, 0, 0, 1, 0 ], 3 ) ); - _gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, _lensFlare.vertices, _gl.STATIC_DRAW ); + var coneGeometry = new THREE.CylinderBufferGeometry( 0, 0.5, 1, 5, 1 ); + coneGeometry.translate( 0, - 0.5, 0 ); - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.elementBuffer ); - _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.faces, _gl.STATIC_DRAW ); + return function ArrowHelper( dir, origin, length, color, headLength, headWidth ) { - // textures + // dir is assumed to be normalized - _lensFlare.tempTexture = _gl.createTexture(); - _lensFlare.occlusionTexture = _gl.createTexture(); + THREE.Object3D.call( this ); - _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture ); - _gl.texImage2D( _gl.TEXTURE_2D, 0, _gl.RGB, 16, 16, 0, _gl.RGB, _gl.UNSIGNED_BYTE, null ); - _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); - _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); - _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, _gl.NEAREST ); - _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, _gl.NEAREST ); + if ( color === undefined ) color = 0xffff00; + if ( length === undefined ) length = 1; + if ( headLength === undefined ) headLength = 0.2 * length; + if ( headWidth === undefined ) headWidth = 0.2 * headLength; - _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.occlusionTexture ); - _gl.texImage2D( _gl.TEXTURE_2D, 0, _gl.RGBA, 16, 16, 0, _gl.RGBA, _gl.UNSIGNED_BYTE, null ); - _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); - _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); - _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, _gl.NEAREST ); - _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, _gl.NEAREST ); + this.position.copy( origin ); - if ( _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) <= 0 ) { + this.line = new THREE.Line( lineGeometry, new THREE.LineBasicMaterial( { color: color } ) ); + this.line.matrixAutoUpdate = false; + this.add( this.line ); - _lensFlare.hasVertexTexture = false; - _lensFlare.program = createProgram( THREE.ShaderFlares[ "lensFlare" ], _precision ); + this.cone = new THREE.Mesh( coneGeometry, new THREE.MeshBasicMaterial( { color: color } ) ); + this.cone.matrixAutoUpdate = false; + this.add( this.cone ); - } else { + this.setDirection( dir ); + this.setLength( length, headLength, headWidth ); - _lensFlare.hasVertexTexture = true; - _lensFlare.program = createProgram( THREE.ShaderFlares[ "lensFlareVertexTexture" ], _precision ); + }; - } +}() ); - _lensFlare.attributes = {}; - _lensFlare.uniforms = {}; +THREE.ArrowHelper.prototype = Object.create( THREE.Object3D.prototype ); +THREE.ArrowHelper.prototype.constructor = THREE.ArrowHelper; - _lensFlare.attributes.vertex = _gl.getAttribLocation ( _lensFlare.program, "position" ); - _lensFlare.attributes.uv = _gl.getAttribLocation ( _lensFlare.program, "uv" ); +THREE.ArrowHelper.prototype.setDirection = ( function () { - _lensFlare.uniforms.renderType = _gl.getUniformLocation( _lensFlare.program, "renderType" ); - _lensFlare.uniforms.map = _gl.getUniformLocation( _lensFlare.program, "map" ); - _lensFlare.uniforms.occlusionMap = _gl.getUniformLocation( _lensFlare.program, "occlusionMap" ); - _lensFlare.uniforms.opacity = _gl.getUniformLocation( _lensFlare.program, "opacity" ); - _lensFlare.uniforms.color = _gl.getUniformLocation( _lensFlare.program, "color" ); - _lensFlare.uniforms.scale = _gl.getUniformLocation( _lensFlare.program, "scale" ); - _lensFlare.uniforms.rotation = _gl.getUniformLocation( _lensFlare.program, "rotation" ); - _lensFlare.uniforms.screenPosition = _gl.getUniformLocation( _lensFlare.program, "screenPosition" ); + var axis = new THREE.Vector3(); + var radians; - }; + return function setDirection( dir ) { + // dir is assumed to be normalized - /* - * Render lens flares - * Method: renders 16x16 0xff00ff-colored points scattered over the light source area, - * reads these back and calculates occlusion. - * Then _lensFlare.update_lensFlares() is called to re-position and - * update transparency of flares. Then they are rendered. - * - */ + if ( dir.y > 0.99999 ) { - this.render = function ( scene, camera, viewportWidth, viewportHeight ) { + this.quaternion.set( 0, 0, 0, 1 ); - var flares = scene.__webglFlares, - nFlares = flares.length; + } else if ( dir.y < - 0.99999 ) { - if ( ! nFlares ) return; + this.quaternion.set( 1, 0, 0, 0 ); - var tempPosition = new THREE.Vector3(); + } else { - var invAspect = viewportHeight / viewportWidth, - halfViewportWidth = viewportWidth * 0.5, - halfViewportHeight = viewportHeight * 0.5; + axis.set( dir.z, 0, - dir.x ).normalize(); - var size = 16 / viewportHeight, - scale = new THREE.Vector2( size * invAspect, size ); + radians = Math.acos( dir.y ); - var screenPosition = new THREE.Vector3( 1, 1, 0 ), - screenPositionPixels = new THREE.Vector2( 1, 1 ); + this.quaternion.setFromAxisAngle( axis, radians ); - var uniforms = _lensFlare.uniforms, - attributes = _lensFlare.attributes; + } - // set _lensFlare program and reset blending + }; - _gl.useProgram( _lensFlare.program ); +}() ); - _gl.enableVertexAttribArray( _lensFlare.attributes.vertex ); - _gl.enableVertexAttribArray( _lensFlare.attributes.uv ); +THREE.ArrowHelper.prototype.setLength = function ( length, headLength, headWidth ) { - // loop through all lens flares to update their occlusion and positions - // setup gl and common used attribs/unforms + if ( headLength === undefined ) headLength = 0.2 * length; + if ( headWidth === undefined ) headWidth = 0.2 * headLength; - _gl.uniform1i( uniforms.occlusionMap, 0 ); - _gl.uniform1i( uniforms.map, 1 ); + this.line.scale.set( 1, Math.max( 0, length - headLength ), 1 ); + this.line.updateMatrix(); - _gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer ); - _gl.vertexAttribPointer( attributes.vertex, 2, _gl.FLOAT, false, 2 * 8, 0 ); - _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 2 * 8, 8 ); + this.cone.scale.set( headWidth, headLength, headWidth ); + this.cone.position.y = length; + this.cone.updateMatrix(); - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.elementBuffer ); +}; - _gl.disable( _gl.CULL_FACE ); - _gl.depthMask( false ); +THREE.ArrowHelper.prototype.setColor = function ( color ) { - var i, j, jl, flare, sprite; + this.line.material.color.copy( color ); + this.cone.material.color.copy( color ); - for ( i = 0; i < nFlares; i ++ ) { +}; - size = 16 / viewportHeight; - scale.set( size * invAspect, size ); +// File:src/extras/helpers/BoxHelper.js - // calc object screen position +/** + * @author mrdoob / http://mrdoob.com/ + */ - flare = flares[ i ]; +THREE.BoxHelper = function ( object, color ) { - tempPosition.set( flare.matrixWorld.elements[12], flare.matrixWorld.elements[13], flare.matrixWorld.elements[14] ); + if ( color === undefined ) color = 0xffff00; - tempPosition.applyMatrix4( camera.matrixWorldInverse ); - tempPosition.applyProjection( camera.projectionMatrix ); + var indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] ); + var positions = new Float32Array( 8 * 3 ); - // setup arrays for gl programs + var geometry = new THREE.BufferGeometry(); + geometry.setIndex( new THREE.BufferAttribute( indices, 1 ) ); + geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) ); - screenPosition.copy( tempPosition ) + THREE.LineSegments.call( this, geometry, new THREE.LineBasicMaterial( { color: color } ) ); - screenPositionPixels.x = screenPosition.x * halfViewportWidth + halfViewportWidth; - screenPositionPixels.y = screenPosition.y * halfViewportHeight + halfViewportHeight; + if ( object !== undefined ) { - // screen cull + this.update( object ); - if ( _lensFlare.hasVertexTexture || ( - screenPositionPixels.x > 0 && - screenPositionPixels.x < viewportWidth && - screenPositionPixels.y > 0 && - screenPositionPixels.y < viewportHeight ) ) { + } - // save current RGB to temp texture +}; - _gl.activeTexture( _gl.TEXTURE1 ); - _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture ); - _gl.copyTexImage2D( _gl.TEXTURE_2D, 0, _gl.RGB, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 ); +THREE.BoxHelper.prototype = Object.create( THREE.LineSegments.prototype ); +THREE.BoxHelper.prototype.constructor = THREE.BoxHelper; +THREE.BoxHelper.prototype.update = ( function () { - // render pink quad + var box = new THREE.Box3(); - _gl.uniform1i( uniforms.renderType, 0 ); - _gl.uniform2f( uniforms.scale, scale.x, scale.y ); - _gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); + return function update( object ) { - _gl.disable( _gl.BLEND ); - _gl.enable( _gl.DEPTH_TEST ); + if ( object instanceof THREE.Box3 ) { - _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); + box.copy( object ); + } else { - // copy result to occlusionMap + box.setFromObject( object ); - _gl.activeTexture( _gl.TEXTURE0 ); - _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.occlusionTexture ); - _gl.copyTexImage2D( _gl.TEXTURE_2D, 0, _gl.RGBA, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 ); + } + if ( box.isEmpty() ) return; - // restore graphics + var min = box.min; + var max = box.max; - _gl.uniform1i( uniforms.renderType, 1 ); - _gl.disable( _gl.DEPTH_TEST ); + /* + 5____4 + 1/___0/| + | 6__|_7 + 2/___3/ - _gl.activeTexture( _gl.TEXTURE1 ); - _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture ); - _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); + 0: max.x, max.y, max.z + 1: min.x, max.y, max.z + 2: min.x, min.y, max.z + 3: max.x, min.y, max.z + 4: max.x, max.y, min.z + 5: min.x, max.y, min.z + 6: min.x, min.y, min.z + 7: max.x, min.y, min.z + */ + var position = this.geometry.attributes.position; + var array = position.array; - // update object positions + array[ 0 ] = max.x; array[ 1 ] = max.y; array[ 2 ] = max.z; + array[ 3 ] = min.x; array[ 4 ] = max.y; array[ 5 ] = max.z; + array[ 6 ] = min.x; array[ 7 ] = min.y; array[ 8 ] = max.z; + array[ 9 ] = max.x; array[ 10 ] = min.y; array[ 11 ] = max.z; + array[ 12 ] = max.x; array[ 13 ] = max.y; array[ 14 ] = min.z; + array[ 15 ] = min.x; array[ 16 ] = max.y; array[ 17 ] = min.z; + array[ 18 ] = min.x; array[ 19 ] = min.y; array[ 20 ] = min.z; + array[ 21 ] = max.x; array[ 22 ] = min.y; array[ 23 ] = min.z; - flare.positionScreen.copy( screenPosition ) + position.needsUpdate = true; - if ( flare.customUpdateCallback ) { + this.geometry.computeBoundingSphere(); - flare.customUpdateCallback( flare ); + }; - } else { +} )(); - flare.updateLensFlares(); +// File:src/extras/helpers/BoundingBoxHelper.js - } +/** + * @author WestLangley / http://github.com/WestLangley + */ - // render flares +// a helper to show the world-axis-aligned bounding box for an object - _gl.uniform1i( uniforms.renderType, 2 ); - _gl.enable( _gl.BLEND ); +THREE.BoundingBoxHelper = function ( object, hex ) { - for ( j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) { + var color = ( hex !== undefined ) ? hex : 0x888888; - sprite = flare.lensFlares[ j ]; + this.object = object; - if ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) { + this.box = new THREE.Box3(); - screenPosition.x = sprite.x; - screenPosition.y = sprite.y; - screenPosition.z = sprite.z; + THREE.Mesh.call( this, new THREE.BoxGeometry( 1, 1, 1 ), new THREE.MeshBasicMaterial( { color: color, wireframe: true } ) ); - size = sprite.size * sprite.scale / viewportHeight; +}; - scale.x = size * invAspect; - scale.y = size; +THREE.BoundingBoxHelper.prototype = Object.create( THREE.Mesh.prototype ); +THREE.BoundingBoxHelper.prototype.constructor = THREE.BoundingBoxHelper; - _gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); - _gl.uniform2f( uniforms.scale, scale.x, scale.y ); - _gl.uniform1f( uniforms.rotation, sprite.rotation ); +THREE.BoundingBoxHelper.prototype.update = function () { - _gl.uniform1f( uniforms.opacity, sprite.opacity ); - _gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b ); + this.box.setFromObject( this.object ); - _renderer.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst ); - _renderer.setTexture( sprite.texture, 1 ); + this.box.size( this.scale ); - _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); + this.box.center( this.position ); - } +}; - } +// File:src/extras/helpers/CameraHelper.js - } +/** + * @author alteredq / http://alteredqualia.com/ + * + * - shows frustum, line of sight and up of the camera + * - suitable for fast updates + * - based on frustum visualization in lightgl.js shadowmap example + * http://evanw.github.com/lightgl.js/tests/shadowmap.html + */ - } +THREE.CameraHelper = function ( camera ) { - // restore gl + var geometry = new THREE.Geometry(); + var material = new THREE.LineBasicMaterial( { color: 0xffffff, vertexColors: THREE.FaceColors } ); - _gl.enable( _gl.CULL_FACE ); - _gl.enable( _gl.DEPTH_TEST ); - _gl.depthMask( true ); + var pointMap = {}; - }; + // colors - function createProgram ( shader, precision ) { + var hexFrustum = 0xffaa00; + var hexCone = 0xff0000; + var hexUp = 0x00aaff; + var hexTarget = 0xffffff; + var hexCross = 0x333333; - var program = _gl.createProgram(); + // near - var fragmentShader = _gl.createShader( _gl.FRAGMENT_SHADER ); - var vertexShader = _gl.createShader( _gl.VERTEX_SHADER ); + addLine( "n1", "n2", hexFrustum ); + addLine( "n2", "n4", hexFrustum ); + addLine( "n4", "n3", hexFrustum ); + addLine( "n3", "n1", hexFrustum ); - var prefix = "precision " + precision + " float;\n"; + // far - _gl.shaderSource( fragmentShader, prefix + shader.fragmentShader ); - _gl.shaderSource( vertexShader, prefix + shader.vertexShader ); + addLine( "f1", "f2", hexFrustum ); + addLine( "f2", "f4", hexFrustum ); + addLine( "f4", "f3", hexFrustum ); + addLine( "f3", "f1", hexFrustum ); - _gl.compileShader( fragmentShader ); - _gl.compileShader( vertexShader ); + // sides - _gl.attachShader( program, fragmentShader ); - _gl.attachShader( program, vertexShader ); + addLine( "n1", "f1", hexFrustum ); + addLine( "n2", "f2", hexFrustum ); + addLine( "n3", "f3", hexFrustum ); + addLine( "n4", "f4", hexFrustum ); - _gl.linkProgram( program ); + // cone - return program; + addLine( "p", "n1", hexCone ); + addLine( "p", "n2", hexCone ); + addLine( "p", "n3", hexCone ); + addLine( "p", "n4", hexCone ); - }; + // up -}; + addLine( "u1", "u2", hexUp ); + addLine( "u2", "u3", hexUp ); + addLine( "u3", "u1", hexUp ); -/** - * @author alteredq / http://alteredqualia.com/ - */ + // target -THREE.ShadowMapPlugin = function () { + addLine( "c", "t", hexTarget ); + addLine( "p", "c", hexCross ); - var _gl, - _renderer, - _depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin, + // cross - _frustum = new THREE.Frustum(), - _projScreenMatrix = new THREE.Matrix4(), + addLine( "cn1", "cn2", hexCross ); + addLine( "cn3", "cn4", hexCross ); - _min = new THREE.Vector3(), - _max = new THREE.Vector3(), + addLine( "cf1", "cf2", hexCross ); + addLine( "cf3", "cf4", hexCross ); + + function addLine( a, b, hex ) { - _matrixPosition = new THREE.Vector3(); + addPoint( a, hex ); + addPoint( b, hex ); - this.init = function ( renderer ) { + } - _gl = renderer.context; - _renderer = renderer; + function addPoint( id, hex ) { - var depthShader = THREE.ShaderLib[ "depthRGBA" ]; - var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms ); + geometry.vertices.push( new THREE.Vector3() ); + geometry.colors.push( new THREE.Color( hex ) ); - _depthMaterial = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms } ); - _depthMaterialMorph = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true } ); - _depthMaterialSkin = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, skinning: true } ); - _depthMaterialMorphSkin = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true, skinning: true } ); + if ( pointMap[ id ] === undefined ) { - _depthMaterial._shadowPass = true; - _depthMaterialMorph._shadowPass = true; - _depthMaterialSkin._shadowPass = true; - _depthMaterialMorphSkin._shadowPass = true; + pointMap[ id ] = []; - }; + } - this.render = function ( scene, camera ) { + pointMap[ id ].push( geometry.vertices.length - 1 ); - if ( ! ( _renderer.shadowMapEnabled && _renderer.shadowMapAutoUpdate ) ) return; + } - this.update( scene, camera ); + THREE.LineSegments.call( this, geometry, material ); - }; + this.camera = camera; + if( this.camera.updateProjectionMatrix ) this.camera.updateProjectionMatrix(); - this.update = function ( scene, camera ) { + this.matrix = camera.matrixWorld; + this.matrixAutoUpdate = false; - var i, il, j, jl, n, + this.pointMap = pointMap; - shadowMap, shadowMatrix, shadowCamera, - program, buffer, material, - webglObject, object, light, - renderList, + this.update(); - lights = [], - k = 0, +}; - fog = null; +THREE.CameraHelper.prototype = Object.create( THREE.LineSegments.prototype ); +THREE.CameraHelper.prototype.constructor = THREE.CameraHelper; - // set GL state for depth map +THREE.CameraHelper.prototype.update = function () { - _gl.clearColor( 1, 1, 1, 1 ); - _gl.disable( _gl.BLEND ); + var geometry, pointMap; - _gl.enable( _gl.CULL_FACE ); - _gl.frontFace( _gl.CCW ); + var vector = new THREE.Vector3(); + var camera = new THREE.Camera(); - if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) { + function setPoint( point, x, y, z ) { - _gl.cullFace( _gl.FRONT ); + vector.set( x, y, z ).unproject( camera ); - } else { + var points = pointMap[ point ]; - _gl.cullFace( _gl.BACK ); + if ( points !== undefined ) { - } + for ( var i = 0, il = points.length; i < il; i ++ ) { - _renderer.setDepthTest( true ); + geometry.vertices[ points[ i ] ].copy( vector ); - // preprocess lights - // - skip lights that are not casting shadows - // - create virtual lights for cascaded shadow maps + } - for ( i = 0, il = scene.__lights.length; i < il; i ++ ) { + } - light = scene.__lights[ i ]; + } - if ( ! light.castShadow ) continue; + return function update() { - if ( ( light instanceof THREE.DirectionalLight ) && light.shadowCascade ) { + geometry = this.geometry; + pointMap = this.pointMap; - for ( n = 0; n < light.shadowCascadeCount; n ++ ) { + var w = 1, h = 1; - var virtualLight; + // we need just camera projection matrix + // world matrix must be identity - if ( ! light.shadowCascadeArray[ n ] ) { + camera.projectionMatrix.copy( this.camera.projectionMatrix ); - virtualLight = createVirtualLight( light, n ); - virtualLight.originalCamera = camera; + // center / target - var gyro = new THREE.Gyroscope(); - gyro.position = light.shadowCascadeOffset; + setPoint( "c", 0, 0, - 1 ); + setPoint( "t", 0, 0, 1 ); - gyro.add( virtualLight ); - gyro.add( virtualLight.target ); + // near - camera.add( gyro ); + setPoint( "n1", - w, - h, - 1 ); + setPoint( "n2", w, - h, - 1 ); + setPoint( "n3", - w, h, - 1 ); + setPoint( "n4", w, h, - 1 ); - light.shadowCascadeArray[ n ] = virtualLight; + // far - console.log( "Created virtualLight", virtualLight ); + setPoint( "f1", - w, - h, 1 ); + setPoint( "f2", w, - h, 1 ); + setPoint( "f3", - w, h, 1 ); + setPoint( "f4", w, h, 1 ); - } else { + // up - virtualLight = light.shadowCascadeArray[ n ]; + setPoint( "u1", w * 0.7, h * 1.1, - 1 ); + setPoint( "u2", - w * 0.7, h * 1.1, - 1 ); + setPoint( "u3", 0, h * 2, - 1 ); - } + // cross - updateVirtualLight( light, n ); + setPoint( "cf1", - w, 0, 1 ); + setPoint( "cf2", w, 0, 1 ); + setPoint( "cf3", 0, - h, 1 ); + setPoint( "cf4", 0, h, 1 ); - lights[ k ] = virtualLight; - k ++; + setPoint( "cn1", - w, 0, - 1 ); + setPoint( "cn2", w, 0, - 1 ); + setPoint( "cn3", 0, - h, - 1 ); + setPoint( "cn4", 0, h, - 1 ); - } + geometry.verticesNeedUpdate = true; - } else { + }; - lights[ k ] = light; - k ++; +}(); - } +// File:src/extras/helpers/DirectionalLightHelper.js - } +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley + */ - // render depth map +THREE.DirectionalLightHelper = function ( light, size ) { - for ( i = 0, il = lights.length; i < il; i ++ ) { + THREE.Object3D.call( this ); - light = lights[ i ]; + this.light = light; + this.light.updateMatrixWorld(); - if ( ! light.shadowMap ) { + this.matrix = light.matrixWorld; + this.matrixAutoUpdate = false; - var shadowFilter = THREE.LinearFilter; + if ( size === undefined ) size = 1; - if ( _renderer.shadowMapType === THREE.PCFSoftShadowMap ) { + var geometry = new THREE.BufferGeometry(); + geometry.addAttribute( 'position', new THREE.Float32Attribute( [ + - size, size, 0, + size, size, 0, + size, - size, 0, + - size, - size, 0, + - size, size, 0 + ], 3 ) ); - shadowFilter = THREE.NearestFilter; + var material = new THREE.LineBasicMaterial( { fog: false } ); - } + this.add( new THREE.Line( geometry, material ) ); - var pars = { minFilter: shadowFilter, magFilter: shadowFilter, format: THREE.RGBAFormat }; + geometry = new THREE.BufferGeometry(); + geometry.addAttribute( 'position', new THREE.Float32Attribute( [ 0, 0, 0, 0, 0, 1 ], 3 ) ); - light.shadowMap = new THREE.WebGLRenderTarget( light.shadowMapWidth, light.shadowMapHeight, pars ); - light.shadowMapSize = new THREE.Vector2( light.shadowMapWidth, light.shadowMapHeight ); + this.add( new THREE.Line( geometry, material )); - light.shadowMatrix = new THREE.Matrix4(); + this.update(); - } +}; - if ( ! light.shadowCamera ) { +THREE.DirectionalLightHelper.prototype = Object.create( THREE.Object3D.prototype ); +THREE.DirectionalLightHelper.prototype.constructor = THREE.DirectionalLightHelper; - if ( light instanceof THREE.SpotLight ) { +THREE.DirectionalLightHelper.prototype.dispose = function () { - light.shadowCamera = new THREE.PerspectiveCamera( light.shadowCameraFov, light.shadowMapWidth / light.shadowMapHeight, light.shadowCameraNear, light.shadowCameraFar ); + var lightPlane = this.children[ 0 ]; + var targetLine = this.children[ 1 ]; - } else if ( light instanceof THREE.DirectionalLight ) { + lightPlane.geometry.dispose(); + lightPlane.material.dispose(); + targetLine.geometry.dispose(); + targetLine.material.dispose(); - light.shadowCamera = new THREE.OrthographicCamera( light.shadowCameraLeft, light.shadowCameraRight, light.shadowCameraTop, light.shadowCameraBottom, light.shadowCameraNear, light.shadowCameraFar ); +}; - } else { +THREE.DirectionalLightHelper.prototype.update = function () { - console.error( "Unsupported light type for shadow" ); - continue; + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + var v3 = new THREE.Vector3(); - } + return function update() { - scene.add( light.shadowCamera ); + v1.setFromMatrixPosition( this.light.matrixWorld ); + v2.setFromMatrixPosition( this.light.target.matrixWorld ); + v3.subVectors( v2, v1 ); - if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); + var lightPlane = this.children[ 0 ]; + var targetLine = this.children[ 1 ]; - } + lightPlane.lookAt( v3 ); + lightPlane.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); - if ( light.shadowCameraVisible && ! light.cameraHelper ) { + targetLine.lookAt( v3 ); + targetLine.scale.z = v3.length(); - light.cameraHelper = new THREE.CameraHelper( light.shadowCamera ); - light.shadowCamera.add( light.cameraHelper ); + }; - } +}(); - if ( light.isVirtual && virtualLight.originalCamera == camera ) { +// File:src/extras/helpers/EdgesHelper.js - updateShadowCamera( camera, light ); +/** + * @author WestLangley / http://github.com/WestLangley + * @param object THREE.Mesh whose geometry will be used + * @param hex line color + * @param thresholdAngle the minimum angle (in degrees), + * between the face normals of adjacent faces, + * that is required to render an edge. A value of 10 means + * an edge is only rendered if the angle is at least 10 degrees. + */ - } +THREE.EdgesHelper = function ( object, hex, thresholdAngle ) { - shadowMap = light.shadowMap; - shadowMatrix = light.shadowMatrix; - shadowCamera = light.shadowCamera; + var color = ( hex !== undefined ) ? hex : 0xffffff; - shadowCamera.position.getPositionFromMatrix( light.matrixWorld ); - _matrixPosition.getPositionFromMatrix( light.target.matrixWorld ); - shadowCamera.lookAt( _matrixPosition ); - shadowCamera.updateMatrixWorld(); + THREE.LineSegments.call( this, new THREE.EdgesGeometry( object.geometry, thresholdAngle ), new THREE.LineBasicMaterial( { color: color } ) ); - shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld ); + this.matrix = object.matrixWorld; + this.matrixAutoUpdate = false; - if ( light.cameraHelper ) light.cameraHelper.visible = light.shadowCameraVisible; - if ( light.shadowCameraVisible ) light.cameraHelper.update(); +}; - // compute shadow matrix +THREE.EdgesHelper.prototype = Object.create( THREE.LineSegments.prototype ); +THREE.EdgesHelper.prototype.constructor = THREE.EdgesHelper; - 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 ); +// File:src/extras/helpers/FaceNormalsHelper.js - shadowMatrix.multiply( shadowCamera.projectionMatrix ); - shadowMatrix.multiply( shadowCamera.matrixWorldInverse ); +/** + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley +*/ - // update camera matrices and frustum +THREE.FaceNormalsHelper = function ( object, size, hex, linewidth ) { - _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); - _frustum.setFromMatrix( _projScreenMatrix ); + // FaceNormalsHelper only supports THREE.Geometry - // render shadow map + this.object = object; - _renderer.setRenderTarget( shadowMap ); - _renderer.clear(); + this.size = ( size !== undefined ) ? size : 1; - // set object matrices & frustum culling + var color = ( hex !== undefined ) ? hex : 0xffff00; - renderList = scene.__webglObjects; + var width = ( linewidth !== undefined ) ? linewidth : 1; - for ( j = 0, jl = renderList.length; j < jl; j ++ ) { + // - webglObject = renderList[ j ]; - object = webglObject.object; + var nNormals = 0; - webglObject.render = false; + var objGeometry = this.object.geometry; - if ( object.visible && object.castShadow ) { + if ( objGeometry instanceof THREE.Geometry ) { - if ( ! ( object instanceof THREE.Mesh || object instanceof THREE.ParticleSystem ) || ! ( object.frustumCulled ) || _frustum.intersectsObject( object ) ) { + nNormals = objGeometry.faces.length; - object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); + } else { - webglObject.render = true; + console.warn( 'THREE.FaceNormalsHelper: only THREE.Geometry is supported. Use THREE.VertexNormalsHelper, instead.' ); - } + } - } + // - } + var geometry = new THREE.BufferGeometry(); - // render regular objects + var positions = new THREE.Float32Attribute( nNormals * 2 * 3, 3 ); - var objectMaterial, useMorphing, useSkinning; + geometry.addAttribute( 'position', positions ); - for ( j = 0, jl = renderList.length; j < jl; j ++ ) { + THREE.LineSegments.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ) ); - webglObject = renderList[ j ]; + // - if ( webglObject.render ) { + this.matrixAutoUpdate = false; + this.update(); - object = webglObject.object; - buffer = webglObject.buffer; +}; - // culling is overriden globally for all objects - // while rendering depth map +THREE.FaceNormalsHelper.prototype = Object.create( THREE.LineSegments.prototype ); +THREE.FaceNormalsHelper.prototype.constructor = THREE.FaceNormalsHelper; - // need to deal with MeshFaceMaterial somehow - // in that case just use the first of material.materials for now - // (proper solution would require to break objects by materials - // similarly to regular rendering and then set corresponding - // depth materials per each chunk instead of just once per object) +THREE.FaceNormalsHelper.prototype.update = ( function () { - objectMaterial = getObjectMaterial( object ); + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + var normalMatrix = new THREE.Matrix3(); - useMorphing = object.geometry.morphTargets.length > 0 && objectMaterial.morphTargets; - useSkinning = object instanceof THREE.SkinnedMesh && objectMaterial.skinning; + return function update() { - if ( object.customDepthMaterial ) { + this.object.updateMatrixWorld( true ); - material = object.customDepthMaterial; + normalMatrix.getNormalMatrix( this.object.matrixWorld ); - } else if ( useSkinning ) { + var matrixWorld = this.object.matrixWorld; - material = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin; + var position = this.geometry.attributes.position; - } else if ( useMorphing ) { + // - material = _depthMaterialMorph; + var objGeometry = this.object.geometry; - } else { + var vertices = objGeometry.vertices; - material = _depthMaterial; + var faces = objGeometry.faces; - } + var idx = 0; - if ( buffer instanceof THREE.BufferGeometry ) { + for ( var i = 0, l = faces.length; i < l; i ++ ) { - _renderer.renderBufferDirect( shadowCamera, scene.__lights, fog, material, buffer, object ); + var face = faces[ i ]; - } else { + var normal = face.normal; - _renderer.renderBuffer( shadowCamera, scene.__lights, fog, material, buffer, object ); + v1.copy( vertices[ face.a ] ) + .add( vertices[ face.b ] ) + .add( vertices[ face.c ] ) + .divideScalar( 3 ) + .applyMatrix4( matrixWorld ); - } + v2.copy( normal ).applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 ); - } + position.setXYZ( idx, v1.x, v1.y, v1.z ); - } + idx = idx + 1; - // set matrices and render immediate objects + position.setXYZ( idx, v2.x, v2.y, v2.z ); - renderList = scene.__webglObjectsImmediate; + idx = idx + 1; - for ( j = 0, jl = renderList.length; j < jl; j ++ ) { + } - webglObject = renderList[ j ]; - object = webglObject.object; + position.needsUpdate = true; - if ( object.visible && object.castShadow ) { + return this; - object._modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld ); + }; - _renderer.renderImmediateObject( shadowCamera, scene.__lights, fog, _depthMaterial, object ); +}() ); - } +// File:src/extras/helpers/GridHelper.js - } +/** + * @author mrdoob / http://mrdoob.com/ + */ - } +THREE.GridHelper = function ( size, divisions, color1, color2 ) { - // restore GL state + divisions = divisions || 1; + color1 = new THREE.Color( color1 !== undefined ? color1 : 0x444444 ); + color2 = new THREE.Color( color2 !== undefined ? color2 : 0x888888 ); - var clearColor = _renderer.getClearColor(), - clearAlpha = _renderer.getClearAlpha(); + var center = divisions / 2; + var step = ( size * 2 ) / divisions; + var vertices = [], colors = []; - _gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha ); - _gl.enable( _gl.BLEND ); + for ( var i = 0, j = 0, k = - size; i <= divisions; i ++, k += step ) { - if ( _renderer.shadowMapCullFace === THREE.CullFaceFront ) { + vertices.push( - size, 0, k, size, 0, k ); + vertices.push( k, 0, - size, k, 0, size ); - _gl.cullFace( _gl.BACK ); + var color = i === center ? color1 : color2; - } + color.toArray( colors, j ); j += 3; + color.toArray( colors, j ); j += 3; + color.toArray( colors, j ); j += 3; + color.toArray( colors, j ); j += 3; - }; + } - function createVirtualLight( light, cascade ) { + var geometry = new THREE.BufferGeometry(); + geometry.addAttribute( 'position', new THREE.Float32Attribute( vertices, 3 ) ); + geometry.addAttribute( 'color', new THREE.Float32Attribute( colors, 3 ) ); - var virtualLight = new THREE.DirectionalLight(); + var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } ); - virtualLight.isVirtual = true; + THREE.LineSegments.call( this, geometry, material ); - virtualLight.onlyShadow = true; - virtualLight.castShadow = true; +}; - virtualLight.shadowCameraNear = light.shadowCameraNear; - virtualLight.shadowCameraFar = light.shadowCameraFar; +THREE.GridHelper.prototype = Object.create( THREE.LineSegments.prototype ); +THREE.GridHelper.prototype.constructor = THREE.GridHelper; - virtualLight.shadowCameraLeft = light.shadowCameraLeft; - virtualLight.shadowCameraRight = light.shadowCameraRight; - virtualLight.shadowCameraBottom = light.shadowCameraBottom; - virtualLight.shadowCameraTop = light.shadowCameraTop; +THREE.GridHelper.prototype.setColors = function () { - virtualLight.shadowCameraVisible = light.shadowCameraVisible; + console.error( 'THREE.GridHelper: setColors() has been deprecated, pass them in the constructor instead.' ); - virtualLight.shadowDarkness = light.shadowDarkness; +}; - virtualLight.shadowBias = light.shadowCascadeBias[ cascade ]; - virtualLight.shadowMapWidth = light.shadowCascadeWidth[ cascade ]; - virtualLight.shadowMapHeight = light.shadowCascadeHeight[ cascade ]; +// File:src/extras/helpers/HemisphereLightHelper.js - virtualLight.pointsWorld = []; - virtualLight.pointsFrustum = []; +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ - var pointsWorld = virtualLight.pointsWorld, - pointsFrustum = virtualLight.pointsFrustum; +THREE.HemisphereLightHelper = function ( light, sphereSize ) { - for ( var i = 0; i < 8; i ++ ) { + THREE.Object3D.call( this ); - pointsWorld[ i ] = new THREE.Vector3(); - pointsFrustum[ i ] = new THREE.Vector3(); + this.light = light; + this.light.updateMatrixWorld(); - } + this.matrix = light.matrixWorld; + this.matrixAutoUpdate = false; - var nearZ = light.shadowCascadeNearZ[ cascade ]; - var farZ = light.shadowCascadeFarZ[ cascade ]; + this.colors = [ new THREE.Color(), new THREE.Color() ]; - pointsFrustum[ 0 ].set( -1, -1, nearZ ); - pointsFrustum[ 1 ].set( 1, -1, nearZ ); - pointsFrustum[ 2 ].set( -1, 1, nearZ ); - pointsFrustum[ 3 ].set( 1, 1, nearZ ); + var geometry = new THREE.SphereGeometry( sphereSize, 4, 2 ); + geometry.rotateX( - Math.PI / 2 ); - pointsFrustum[ 4 ].set( -1, -1, farZ ); - pointsFrustum[ 5 ].set( 1, -1, farZ ); - pointsFrustum[ 6 ].set( -1, 1, farZ ); - pointsFrustum[ 7 ].set( 1, 1, farZ ); + for ( var i = 0, il = 8; i < il; i ++ ) { - return virtualLight; + geometry.faces[ i ].color = this.colors[ i < 4 ? 0 : 1 ]; } - // Synchronize virtual light with the original light + var material = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors, wireframe: true } ); - function updateVirtualLight( light, cascade ) { + this.lightSphere = new THREE.Mesh( geometry, material ); + this.add( this.lightSphere ); - var virtualLight = light.shadowCascadeArray[ cascade ]; + this.update(); - virtualLight.position.copy( light.position ); - virtualLight.target.position.copy( light.target.position ); - virtualLight.lookAt( virtualLight.target ); +}; - virtualLight.shadowCameraVisible = light.shadowCameraVisible; - virtualLight.shadowDarkness = light.shadowDarkness; +THREE.HemisphereLightHelper.prototype = Object.create( THREE.Object3D.prototype ); +THREE.HemisphereLightHelper.prototype.constructor = THREE.HemisphereLightHelper; - virtualLight.shadowBias = light.shadowCascadeBias[ cascade ]; +THREE.HemisphereLightHelper.prototype.dispose = function () { - var nearZ = light.shadowCascadeNearZ[ cascade ]; - var farZ = light.shadowCascadeFarZ[ cascade ]; + this.lightSphere.geometry.dispose(); + this.lightSphere.material.dispose(); - var pointsFrustum = virtualLight.pointsFrustum; +}; - pointsFrustum[ 0 ].z = nearZ; - pointsFrustum[ 1 ].z = nearZ; - pointsFrustum[ 2 ].z = nearZ; - pointsFrustum[ 3 ].z = nearZ; +THREE.HemisphereLightHelper.prototype.update = function () { - pointsFrustum[ 4 ].z = farZ; - pointsFrustum[ 5 ].z = farZ; - pointsFrustum[ 6 ].z = farZ; - pointsFrustum[ 7 ].z = farZ; + var vector = new THREE.Vector3(); - } + return function update() { - // Fit shadow camera's ortho frustum to camera frustum + this.colors[ 0 ].copy( this.light.color ).multiplyScalar( this.light.intensity ); + this.colors[ 1 ].copy( this.light.groundColor ).multiplyScalar( this.light.intensity ); - function updateShadowCamera( camera, light ) { + this.lightSphere.lookAt( vector.setFromMatrixPosition( this.light.matrixWorld ).negate() ); + this.lightSphere.geometry.colorsNeedUpdate = true; - var shadowCamera = light.shadowCamera, - pointsFrustum = light.pointsFrustum, - pointsWorld = light.pointsWorld; + }; - _min.set( Infinity, Infinity, Infinity ); - _max.set( -Infinity, -Infinity, -Infinity ); +}(); - for ( var i = 0; i < 8; i ++ ) { +// File:src/extras/helpers/PointLightHelper.js - var p = pointsWorld[ i ]; +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ - p.copy( pointsFrustum[ i ] ); - THREE.ShadowMapPlugin.__projector.unprojectVector( p, camera ); +THREE.PointLightHelper = function ( light, sphereSize ) { - p.applyMatrix4( shadowCamera.matrixWorldInverse ); + this.light = light; + this.light.updateMatrixWorld(); - if ( p.x < _min.x ) _min.x = p.x; - if ( p.x > _max.x ) _max.x = p.x; + var geometry = new THREE.SphereBufferGeometry( sphereSize, 4, 2 ); + var material = new THREE.MeshBasicMaterial( { wireframe: true, fog: false } ); + material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); - if ( p.y < _min.y ) _min.y = p.y; - if ( p.y > _max.y ) _max.y = p.y; + THREE.Mesh.call( this, geometry, material ); - if ( p.z < _min.z ) _min.z = p.z; - if ( p.z > _max.z ) _max.z = p.z; + this.matrix = this.light.matrixWorld; + this.matrixAutoUpdate = false; - } + /* + var distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 ); + var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } ); - shadowCamera.left = _min.x; - shadowCamera.right = _max.x; - shadowCamera.top = _max.y; - shadowCamera.bottom = _min.y; + this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial ); + this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial ); - // can't really fit near/far - //shadowCamera.near = _min.z; - //shadowCamera.far = _max.z; + var d = light.distance; - shadowCamera.updateProjectionMatrix(); + if ( d === 0.0 ) { - } + this.lightDistance.visible = false; - // For the moment just ignore objects that have multiple materials with different animation methods - // Only the first material will be taken into account for deciding which depth material to use for shadow maps + } else { - function getObjectMaterial( object ) { + this.lightDistance.scale.set( d, d, d ); - return object.material instanceof THREE.MeshFaceMaterial - ? object.material.materials[ 0 ] - : object.material; + } - }; + this.add( this.lightDistance ); + */ }; -THREE.ShadowMapPlugin.__projector = new THREE.Projector(); +THREE.PointLightHelper.prototype = Object.create( THREE.Mesh.prototype ); +THREE.PointLightHelper.prototype.constructor = THREE.PointLightHelper; -/** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - */ +THREE.PointLightHelper.prototype.dispose = function () { -THREE.SpritePlugin = function () { + this.geometry.dispose(); + this.material.dispose(); - var _gl, _renderer, _precision, _sprite = {}; +}; - this.init = function ( renderer ) { +THREE.PointLightHelper.prototype.update = function () { - _gl = renderer.context; - _renderer = renderer; + this.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); - _precision = renderer.getPrecision(); + /* + var d = this.light.distance; - _sprite.vertices = new Float32Array( 8 + 8 ); - _sprite.faces = new Uint16Array( 6 ); + if ( d === 0.0 ) { - var i = 0; + this.lightDistance.visible = false; - _sprite.vertices[ i++ ] = -1; _sprite.vertices[ i++ ] = -1; // vertex 0 - _sprite.vertices[ i++ ] = 0; _sprite.vertices[ i++ ] = 0; // uv 0 + } else { - _sprite.vertices[ i++ ] = 1; _sprite.vertices[ i++ ] = -1; // vertex 1 - _sprite.vertices[ i++ ] = 1; _sprite.vertices[ i++ ] = 0; // uv 1 + this.lightDistance.visible = true; + this.lightDistance.scale.set( d, d, d ); - _sprite.vertices[ i++ ] = 1; _sprite.vertices[ i++ ] = 1; // vertex 2 - _sprite.vertices[ i++ ] = 1; _sprite.vertices[ i++ ] = 1; // uv 2 + } + */ - _sprite.vertices[ i++ ] = -1; _sprite.vertices[ i++ ] = 1; // vertex 3 - _sprite.vertices[ i++ ] = 0; _sprite.vertices[ i++ ] = 1; // uv 3 +}; - i = 0; +// File:src/extras/helpers/SkeletonHelper.js - _sprite.faces[ i++ ] = 0; _sprite.faces[ i++ ] = 1; _sprite.faces[ i++ ] = 2; - _sprite.faces[ i++ ] = 0; _sprite.faces[ i++ ] = 2; _sprite.faces[ i++ ] = 3; +/** + * @author Sean Griffin / http://twitter.com/sgrif + * @author Michael Guerrero / http://realitymeltdown.com + * @author mrdoob / http://mrdoob.com/ + * @author ikerr / http://verold.com + */ - _sprite.vertexBuffer = _gl.createBuffer(); - _sprite.elementBuffer = _gl.createBuffer(); +THREE.SkeletonHelper = function ( object ) { - _gl.bindBuffer( _gl.ARRAY_BUFFER, _sprite.vertexBuffer ); - _gl.bufferData( _gl.ARRAY_BUFFER, _sprite.vertices, _gl.STATIC_DRAW ); + this.bones = this.getBoneList( object ); - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _sprite.elementBuffer ); - _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, _sprite.faces, _gl.STATIC_DRAW ); + var geometry = new THREE.Geometry(); - _sprite.program = createProgram( THREE.ShaderSprite[ "sprite" ], _precision ); + for ( var i = 0; i < this.bones.length; i ++ ) { - _sprite.attributes = {}; - _sprite.uniforms = {}; + var bone = this.bones[ i ]; - _sprite.attributes.position = _gl.getAttribLocation ( _sprite.program, "position" ); - _sprite.attributes.uv = _gl.getAttribLocation ( _sprite.program, "uv" ); + if ( bone.parent instanceof THREE.Bone ) { - _sprite.uniforms.uvOffset = _gl.getUniformLocation( _sprite.program, "uvOffset" ); - _sprite.uniforms.uvScale = _gl.getUniformLocation( _sprite.program, "uvScale" ); + geometry.vertices.push( new THREE.Vector3() ); + geometry.vertices.push( new THREE.Vector3() ); + geometry.colors.push( new THREE.Color( 0, 0, 1 ) ); + geometry.colors.push( new THREE.Color( 0, 1, 0 ) ); - _sprite.uniforms.rotation = _gl.getUniformLocation( _sprite.program, "rotation" ); - _sprite.uniforms.scale = _gl.getUniformLocation( _sprite.program, "scale" ); - _sprite.uniforms.alignment = _gl.getUniformLocation( _sprite.program, "alignment" ); + } - _sprite.uniforms.color = _gl.getUniformLocation( _sprite.program, "color" ); - _sprite.uniforms.map = _gl.getUniformLocation( _sprite.program, "map" ); - _sprite.uniforms.opacity = _gl.getUniformLocation( _sprite.program, "opacity" ); + } - _sprite.uniforms.useScreenCoordinates = _gl.getUniformLocation( _sprite.program, "useScreenCoordinates" ); - _sprite.uniforms.sizeAttenuation = _gl.getUniformLocation( _sprite.program, "sizeAttenuation" ); - _sprite.uniforms.screenPosition = _gl.getUniformLocation( _sprite.program, "screenPosition" ); - _sprite.uniforms.modelViewMatrix = _gl.getUniformLocation( _sprite.program, "modelViewMatrix" ); - _sprite.uniforms.projectionMatrix = _gl.getUniformLocation( _sprite.program, "projectionMatrix" ); + geometry.dynamic = true; - _sprite.uniforms.fogType = _gl.getUniformLocation( _sprite.program, "fogType" ); - _sprite.uniforms.fogDensity = _gl.getUniformLocation( _sprite.program, "fogDensity" ); - _sprite.uniforms.fogNear = _gl.getUniformLocation( _sprite.program, "fogNear" ); - _sprite.uniforms.fogFar = _gl.getUniformLocation( _sprite.program, "fogFar" ); - _sprite.uniforms.fogColor = _gl.getUniformLocation( _sprite.program, "fogColor" ); + var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors, depthTest: false, depthWrite: false, transparent: true } ); - _sprite.uniforms.alphaTest = _gl.getUniformLocation( _sprite.program, "alphaTest" ); + THREE.LineSegments.call( this, geometry, material ); - }; + this.root = object; - this.render = function ( scene, camera, viewportWidth, viewportHeight ) { + this.matrix = object.matrixWorld; + this.matrixAutoUpdate = false; - var sprites = scene.__webglSprites, - nSprites = sprites.length; + this.update(); - if ( ! nSprites ) return; +}; - var attributes = _sprite.attributes, - uniforms = _sprite.uniforms; - var invAspect = viewportHeight / viewportWidth; +THREE.SkeletonHelper.prototype = Object.create( THREE.LineSegments.prototype ); +THREE.SkeletonHelper.prototype.constructor = THREE.SkeletonHelper; - var halfViewportWidth = viewportWidth * 0.5, - halfViewportHeight = viewportHeight * 0.5; +THREE.SkeletonHelper.prototype.getBoneList = function( object ) { - // setup gl + var boneList = []; - _gl.useProgram( _sprite.program ); + if ( object instanceof THREE.Bone ) { - _gl.enableVertexAttribArray( attributes.position ); - _gl.enableVertexAttribArray( attributes.uv ); + boneList.push( object ); - _gl.disable( _gl.CULL_FACE ); - _gl.enable( _gl.BLEND ); + } - _gl.bindBuffer( _gl.ARRAY_BUFFER, _sprite.vertexBuffer ); - _gl.vertexAttribPointer( attributes.position, 2, _gl.FLOAT, false, 2 * 8, 0 ); - _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 2 * 8, 8 ); + for ( var i = 0; i < object.children.length; i ++ ) { - _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _sprite.elementBuffer ); + boneList.push.apply( boneList, this.getBoneList( object.children[ i ] ) ); - _gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); + } - _gl.activeTexture( _gl.TEXTURE0 ); - _gl.uniform1i( uniforms.map, 0 ); + return boneList; - var oldFogType = 0; - var sceneFogType = 0; - var fog = scene.fog; +}; - if ( fog ) { +THREE.SkeletonHelper.prototype.update = function () { - _gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b ); + var geometry = this.geometry; - if ( fog instanceof THREE.Fog ) { + var matrixWorldInv = new THREE.Matrix4().getInverse( this.root.matrixWorld ); - _gl.uniform1f( uniforms.fogNear, fog.near ); - _gl.uniform1f( uniforms.fogFar, fog.far ); + var boneMatrix = new THREE.Matrix4(); - _gl.uniform1i( uniforms.fogType, 1 ); - oldFogType = 1; - sceneFogType = 1; + var j = 0; - } else if ( fog instanceof THREE.FogExp2 ) { + for ( var i = 0; i < this.bones.length; i ++ ) { - _gl.uniform1f( uniforms.fogDensity, fog.density ); + var bone = this.bones[ i ]; - _gl.uniform1i( uniforms.fogType, 2 ); - oldFogType = 2; - sceneFogType = 2; + if ( bone.parent instanceof THREE.Bone ) { - } + boneMatrix.multiplyMatrices( matrixWorldInv, bone.matrixWorld ); + geometry.vertices[ j ].setFromMatrixPosition( boneMatrix ); - } else { + boneMatrix.multiplyMatrices( matrixWorldInv, bone.parent.matrixWorld ); + geometry.vertices[ j + 1 ].setFromMatrixPosition( boneMatrix ); - _gl.uniform1i( uniforms.fogType, 0 ); - oldFogType = 0; - sceneFogType = 0; + j += 2; } + } - // update positions and sort - - var i, sprite, material, screenPosition, size, fogType, scale = []; - - for( i = 0; i < nSprites; i ++ ) { - - sprite = sprites[ i ]; - material = sprite.material; - - if ( ! sprite.visible || material.opacity === 0 ) continue; - - if ( ! material.useScreenCoordinates ) { + geometry.verticesNeedUpdate = true; - sprite._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld ); - sprite.z = - sprite._modelViewMatrix.elements[ 14 ]; + geometry.computeBoundingSphere(); - } else { +}; - sprite.z = - sprite.position.z; +// File:src/extras/helpers/SpotLightHelper.js - } +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley +*/ - } +THREE.SpotLightHelper = function ( light ) { - sprites.sort( painterSortStable ); + THREE.Object3D.call( this ); - // render all sprites + this.light = light; + this.light.updateMatrixWorld(); - for( i = 0; i < nSprites; i ++ ) { + this.matrix = light.matrixWorld; + this.matrixAutoUpdate = false; - sprite = sprites[ i ]; - material = sprite.material; + var geometry = new THREE.BufferGeometry(); - if ( ! sprite.visible || material.opacity === 0 ) continue; + var positions = [ + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 1, 0, 1, + 0, 0, 0, - 1, 0, 1, + 0, 0, 0, 0, 1, 1, + 0, 0, 0, 0, - 1, 1 + ]; - if ( material.map && material.map.image && material.map.image.width ) { + for ( var i = 0, j = 1, l = 32; i < l; i ++, j ++ ) { - _gl.uniform1f( uniforms.alphaTest, material.alphaTest ); + var p1 = ( i / l ) * Math.PI * 2; + var p2 = ( j / l ) * Math.PI * 2; - if ( material.useScreenCoordinates === true ) { + positions.push( + Math.cos( p1 ), Math.sin( p1 ), 1, + Math.cos( p2 ), Math.sin( p2 ), 1 + ); - _gl.uniform1i( uniforms.useScreenCoordinates, 1 ); - _gl.uniform3f( - uniforms.screenPosition, - ( ( sprite.position.x * _renderer.devicePixelRatio ) - halfViewportWidth ) / halfViewportWidth, - ( halfViewportHeight - ( sprite.position.y * _renderer.devicePixelRatio ) ) / halfViewportHeight, - Math.max( 0, Math.min( 1, sprite.position.z ) ) - ); + } - scale[ 0 ] = _renderer.devicePixelRatio; - scale[ 1 ] = _renderer.devicePixelRatio; + geometry.addAttribute( 'position', new THREE.Float32Attribute( positions, 3 ) ); - } else { + var material = new THREE.LineBasicMaterial( { fog: false } ); - _gl.uniform1i( uniforms.useScreenCoordinates, 0 ); - _gl.uniform1i( uniforms.sizeAttenuation, material.sizeAttenuation ? 1 : 0 ); - _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite._modelViewMatrix.elements ); + this.cone = new THREE.LineSegments( geometry, material ); + this.add( this.cone ); - scale[ 0 ] = 1; - scale[ 1 ] = 1; + this.update(); - } +}; - if ( scene.fog && material.fog ) { +THREE.SpotLightHelper.prototype = Object.create( THREE.Object3D.prototype ); +THREE.SpotLightHelper.prototype.constructor = THREE.SpotLightHelper; - fogType = sceneFogType; +THREE.SpotLightHelper.prototype.dispose = function () { - } else { + this.cone.geometry.dispose(); + this.cone.material.dispose(); - fogType = 0; +}; - } +THREE.SpotLightHelper.prototype.update = function () { - if ( oldFogType !== fogType ) { + var vector = new THREE.Vector3(); + var vector2 = new THREE.Vector3(); - _gl.uniform1i( uniforms.fogType, fogType ); - oldFogType = fogType; + return function update() { - } + var coneLength = this.light.distance ? this.light.distance : 1000; + var coneWidth = coneLength * Math.tan( this.light.angle ); - size = 1 / ( material.scaleByViewport ? viewportHeight : 1 ); + this.cone.scale.set( coneWidth, coneWidth, coneLength ); - scale[ 0 ] *= size * invAspect * sprite.scale.x - scale[ 1 ] *= size * sprite.scale.y; + vector.setFromMatrixPosition( this.light.matrixWorld ); + vector2.setFromMatrixPosition( this.light.target.matrixWorld ); - _gl.uniform2f( uniforms.uvScale, material.uvScale.x, material.uvScale.y ); - _gl.uniform2f( uniforms.uvOffset, material.uvOffset.x, material.uvOffset.y ); - _gl.uniform2f( uniforms.alignment, material.alignment.x, material.alignment.y ); + this.cone.lookAt( vector2.sub( vector ) ); - _gl.uniform1f( uniforms.opacity, material.opacity ); - _gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b ); + this.cone.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity ); - _gl.uniform1f( uniforms.rotation, sprite.rotation ); - _gl.uniform2fv( uniforms.scale, scale ); + }; - _renderer.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); - _renderer.setDepthTest( material.depthTest ); - _renderer.setDepthWrite( material.depthWrite ); - _renderer.setTexture( material.map, 0 ); +}(); - _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); +// File:src/extras/helpers/VertexNormalsHelper.js - } +/** + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley +*/ - } +THREE.VertexNormalsHelper = function ( object, size, hex, linewidth ) { - // restore gl + this.object = object; - _gl.enable( _gl.CULL_FACE ); + this.size = ( size !== undefined ) ? size : 1; - }; + var color = ( hex !== undefined ) ? hex : 0xff0000; - function createProgram ( shader, precision ) { + var width = ( linewidth !== undefined ) ? linewidth : 1; - var program = _gl.createProgram(); + // - var fragmentShader = _gl.createShader( _gl.FRAGMENT_SHADER ); - var vertexShader = _gl.createShader( _gl.VERTEX_SHADER ); + var nNormals = 0; - var prefix = "precision " + precision + " float;\n"; + var objGeometry = this.object.geometry; - _gl.shaderSource( fragmentShader, prefix + shader.fragmentShader ); - _gl.shaderSource( vertexShader, prefix + shader.vertexShader ); + if ( objGeometry instanceof THREE.Geometry ) { - _gl.compileShader( fragmentShader ); - _gl.compileShader( vertexShader ); + nNormals = objGeometry.faces.length * 3; - _gl.attachShader( program, fragmentShader ); - _gl.attachShader( program, vertexShader ); + } else if ( objGeometry instanceof THREE.BufferGeometry ) { - _gl.linkProgram( program ); + nNormals = objGeometry.attributes.normal.count; - return program; + } - }; + // - function painterSortStable ( a, b ) { + var geometry = new THREE.BufferGeometry(); - if ( a.z !== b.z ) { + var positions = new THREE.Float32Attribute( nNormals * 2 * 3, 3 ); - return b.z - a.z; + geometry.addAttribute( 'position', positions ); - } else { + THREE.LineSegments.call( this, geometry, new THREE.LineBasicMaterial( { color: color, linewidth: width } ) ); - return b.id - a.id; + // - } + this.matrixAutoUpdate = false; - }; + this.update(); }; -/** - * @author alteredq / http://alteredqualia.com/ - */ +THREE.VertexNormalsHelper.prototype = Object.create( THREE.LineSegments.prototype ); +THREE.VertexNormalsHelper.prototype.constructor = THREE.VertexNormalsHelper; -THREE.DepthPassPlugin = function () { +THREE.VertexNormalsHelper.prototype.update = ( function () { - this.enabled = false; - this.renderTarget = null; + var v1 = new THREE.Vector3(); + var v2 = new THREE.Vector3(); + var normalMatrix = new THREE.Matrix3(); - var _gl, - _renderer, - _depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin, + return function update() { - _frustum = new THREE.Frustum(), - _projScreenMatrix = new THREE.Matrix4(); + var keys = [ 'a', 'b', 'c' ]; - this.init = function ( renderer ) { + this.object.updateMatrixWorld( true ); - _gl = renderer.context; - _renderer = renderer; + normalMatrix.getNormalMatrix( this.object.matrixWorld ); - var depthShader = THREE.ShaderLib[ "depthRGBA" ]; - var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms ); + var matrixWorld = this.object.matrixWorld; - _depthMaterial = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms } ); - _depthMaterialMorph = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true } ); - _depthMaterialSkin = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, skinning: true } ); - _depthMaterialMorphSkin = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true, skinning: true } ); + var position = this.geometry.attributes.position; - _depthMaterial._shadowPass = true; - _depthMaterialMorph._shadowPass = true; - _depthMaterialSkin._shadowPass = true; - _depthMaterialMorphSkin._shadowPass = true; + // - }; + var objGeometry = this.object.geometry; - this.render = function ( scene, camera ) { + if ( objGeometry instanceof THREE.Geometry ) { - if ( ! this.enabled ) return; + var vertices = objGeometry.vertices; - this.update( scene, camera ); + var faces = objGeometry.faces; - }; + var idx = 0; - this.update = function ( scene, camera ) { + for ( var i = 0, l = faces.length; i < l; i ++ ) { - var i, il, j, jl, n, + var face = faces[ i ]; - program, buffer, material, - webglObject, object, light, - renderList, + for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { - fog = null; + var vertex = vertices[ face[ keys[ j ] ] ]; - // set GL state for depth map + var normal = face.vertexNormals[ j ]; - _gl.clearColor( 1, 1, 1, 1 ); - _gl.disable( _gl.BLEND ); + v1.copy( vertex ).applyMatrix4( matrixWorld ); - _renderer.setDepthTest( true ); + v2.copy( normal ).applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 ); - // update scene + position.setXYZ( idx, v1.x, v1.y, v1.z ); - if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); + idx = idx + 1; - // update camera matrices and frustum + position.setXYZ( idx, v2.x, v2.y, v2.z ); - camera.matrixWorldInverse.getInverse( camera.matrixWorld ); + idx = idx + 1; - _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); - _frustum.setFromMatrix( _projScreenMatrix ); + } - // render depth map + } - _renderer.setRenderTarget( this.renderTarget ); - _renderer.clear(); + } else if ( objGeometry instanceof THREE.BufferGeometry ) { - // set object matrices & frustum culling + var objPos = objGeometry.attributes.position; - renderList = scene.__webglObjects; + var objNorm = objGeometry.attributes.normal; - for ( j = 0, jl = renderList.length; j < jl; j ++ ) { + var idx = 0; - webglObject = renderList[ j ]; - object = webglObject.object; + // for simplicity, ignore index and drawcalls, and render every normal - webglObject.render = false; + for ( var j = 0, jl = objPos.count; j < jl; j ++ ) { - if ( object.visible ) { + v1.set( objPos.getX( j ), objPos.getY( j ), objPos.getZ( j ) ).applyMatrix4( matrixWorld ); - if ( ! ( object instanceof THREE.Mesh || object instanceof THREE.ParticleSystem ) || ! ( object.frustumCulled ) || _frustum.intersectsObject( object ) ) { + v2.set( objNorm.getX( j ), objNorm.getY( j ), objNorm.getZ( j ) ); - object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + v2.applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 ); - webglObject.render = true; + position.setXYZ( idx, v1.x, v1.y, v1.z ); - } + idx = idx + 1; + + position.setXYZ( idx, v2.x, v2.y, v2.z ); + + idx = idx + 1; } } - // render regular objects + position.needsUpdate = true; + + return this; + + }; - var objectMaterial, useMorphing, useSkinning; +}() ); - for ( j = 0, jl = renderList.length; j < jl; j ++ ) { +// File:src/extras/helpers/WireframeHelper.js - webglObject = renderList[ j ]; +/** + * @author mrdoob / http://mrdoob.com/ + */ - if ( webglObject.render ) { +THREE.WireframeHelper = function ( object, hex ) { - object = webglObject.object; - buffer = webglObject.buffer; + var color = ( hex !== undefined ) ? hex : 0xffffff; - // todo: create proper depth material for particles + THREE.LineSegments.call( this, new THREE.WireframeGeometry( object.geometry ), new THREE.LineBasicMaterial( { color: color } ) ); - if ( object instanceof THREE.ParticleSystem && !object.customDepthMaterial ) continue; + this.matrix = object.matrixWorld; + this.matrixAutoUpdate = false; - objectMaterial = getObjectMaterial( object ); +}; - if ( objectMaterial ) _renderer.setMaterialFaces( object.material ); +THREE.WireframeHelper.prototype = Object.create( THREE.LineSegments.prototype ); +THREE.WireframeHelper.prototype.constructor = THREE.WireframeHelper; - useMorphing = object.geometry.morphTargets.length > 0 && objectMaterial.morphTargets; - useSkinning = object instanceof THREE.SkinnedMesh && objectMaterial.skinning; +// File:src/extras/objects/ImmediateRenderObject.js - if ( object.customDepthMaterial ) { +/** + * @author alteredq / http://alteredqualia.com/ + */ - material = object.customDepthMaterial; +THREE.ImmediateRenderObject = function ( material ) { - } else if ( useSkinning ) { + THREE.Object3D.call( this ); - material = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin; + this.material = material; + this.render = function ( renderCallback ) {}; - } else if ( useMorphing ) { +}; - material = _depthMaterialMorph; +THREE.ImmediateRenderObject.prototype = Object.create( THREE.Object3D.prototype ); +THREE.ImmediateRenderObject.prototype.constructor = THREE.ImmediateRenderObject; - } else { +// File:src/extras/objects/MorphBlendMesh.js - material = _depthMaterial; +/** + * @author alteredq / http://alteredqualia.com/ + */ - } +THREE.MorphBlendMesh = function( geometry, material ) { - if ( buffer instanceof THREE.BufferGeometry ) { + THREE.Mesh.call( this, geometry, material ); - _renderer.renderBufferDirect( camera, scene.__lights, fog, material, buffer, object ); + this.animationsMap = {}; + this.animationsList = []; - } else { + // prepare default animation + // (all frames played together in 1 second) - _renderer.renderBuffer( camera, scene.__lights, fog, material, buffer, object ); + var numFrames = this.geometry.morphTargets.length; - } + var name = "__default"; - } + var startFrame = 0; + var endFrame = numFrames - 1; - } + var fps = numFrames / 1; - // set matrices and render immediate objects + this.createAnimation( name, startFrame, endFrame, fps ); + this.setAnimationWeight( name, 1 ); - renderList = scene.__webglObjectsImmediate; +}; - for ( j = 0, jl = renderList.length; j < jl; j ++ ) { +THREE.MorphBlendMesh.prototype = Object.create( THREE.Mesh.prototype ); +THREE.MorphBlendMesh.prototype.constructor = THREE.MorphBlendMesh; - webglObject = renderList[ j ]; - object = webglObject.object; +THREE.MorphBlendMesh.prototype.createAnimation = function ( name, start, end, fps ) { - if ( object.visible ) { + var animation = { - object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); + start: start, + end: end, - _renderer.renderImmediateObject( camera, scene.__lights, fog, _depthMaterial, object ); + length: end - start + 1, - } + fps: fps, + duration: ( end - start ) / fps, - } + lastFrame: 0, + currentFrame: 0, - // restore GL state + active: false, - var clearColor = _renderer.getClearColor(), - clearAlpha = _renderer.getClearAlpha(); + time: 0, + direction: 1, + weight: 1, - _gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha ); - _gl.enable( _gl.BLEND ); + directionBackwards: false, + mirroredLoop: false }; - // For the moment just ignore objects that have multiple materials with different animation methods - // Only the first material will be taken into account for deciding which depth material to use + this.animationsMap[ name ] = animation; + this.animationsList.push( animation ); + +}; - function getObjectMaterial( object ) { +THREE.MorphBlendMesh.prototype.autoCreateAnimations = function ( fps ) { - return object.material instanceof THREE.MeshFaceMaterial - ? object.material.materials[ 0 ] - : object.material; + var pattern = /([a-z]+)_?(\d+)/i; - }; + var firstAnimation, frameRanges = {}; -}; + var geometry = this.geometry; + for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) { -/** - * @author mikael emtinger / http://gomo.se/ - */ + var morph = geometry.morphTargets[ i ]; + var chunks = morph.name.match( pattern ); -THREE.ShaderFlares = { + if ( chunks && chunks.length > 1 ) { - 'lensFlareVertexTexture': { + var name = chunks[ 1 ]; - vertexShader: [ + if ( ! frameRanges[ name ] ) frameRanges[ name ] = { start: Infinity, end: - Infinity }; - "uniform lowp int renderType;", + var range = frameRanges[ name ]; - "uniform vec3 screenPosition;", - "uniform vec2 scale;", - "uniform float rotation;", + if ( i < range.start ) range.start = i; + if ( i > range.end ) range.end = i; - "uniform sampler2D occlusionMap;", + if ( ! firstAnimation ) firstAnimation = name; - "attribute vec2 position;", - "attribute vec2 uv;", + } - "varying vec2 vUV;", - "varying float vVisibility;", + } - "void main() {", + for ( var name in frameRanges ) { - "vUV = uv;", + var range = frameRanges[ name ]; + this.createAnimation( name, range.start, range.end, fps ); - "vec2 pos = position;", + } - "if( renderType == 2 ) {", + this.firstAnimation = firstAnimation; - "vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) );", - "visibility += texture2D( occlusionMap, vec2( 0.5, 0.1 ) );", - "visibility += texture2D( occlusionMap, vec2( 0.9, 0.1 ) );", - "visibility += texture2D( occlusionMap, vec2( 0.9, 0.5 ) );", - "visibility += texture2D( occlusionMap, vec2( 0.9, 0.9 ) );", - "visibility += texture2D( occlusionMap, vec2( 0.5, 0.9 ) );", - "visibility += texture2D( occlusionMap, vec2( 0.1, 0.9 ) );", - "visibility += texture2D( occlusionMap, vec2( 0.1, 0.5 ) );", - "visibility += texture2D( occlusionMap, vec2( 0.5, 0.5 ) );", +}; - "vVisibility = visibility.r / 9.0;", - "vVisibility *= 1.0 - visibility.g / 9.0;", - "vVisibility *= visibility.b / 9.0;", - "vVisibility *= 1.0 - visibility.a / 9.0;", +THREE.MorphBlendMesh.prototype.setAnimationDirectionForward = function ( name ) { - "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", - "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", + var animation = this.animationsMap[ name ]; - "}", + if ( animation ) { - "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", + animation.direction = 1; + animation.directionBackwards = false; - "}" + } - ].join( "\n" ), +}; - fragmentShader: [ +THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward = function ( name ) { - "uniform lowp int renderType;", + var animation = this.animationsMap[ name ]; - "uniform sampler2D map;", - "uniform float opacity;", - "uniform vec3 color;", + if ( animation ) { - "varying vec2 vUV;", - "varying float vVisibility;", + animation.direction = - 1; + animation.directionBackwards = true; - "void main() {", + } - // pink square +}; - "if( renderType == 0 ) {", +THREE.MorphBlendMesh.prototype.setAnimationFPS = function ( name, fps ) { - "gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );", + var animation = this.animationsMap[ name ]; - // restore + if ( animation ) { - "} else if( renderType == 1 ) {", + animation.fps = fps; + animation.duration = ( animation.end - animation.start ) / animation.fps; - "gl_FragColor = texture2D( map, vUV );", + } - // flare +}; - "} else {", +THREE.MorphBlendMesh.prototype.setAnimationDuration = function ( name, duration ) { - "vec4 texture = texture2D( map, vUV );", - "texture.a *= opacity * vVisibility;", - "gl_FragColor = texture;", - "gl_FragColor.rgb *= color;", + var animation = this.animationsMap[ name ]; - "}", + if ( animation ) { - "}" - ].join( "\n" ) + animation.duration = duration; + animation.fps = ( animation.end - animation.start ) / animation.duration; - }, + } +}; - 'lensFlare': { +THREE.MorphBlendMesh.prototype.setAnimationWeight = function ( name, weight ) { - vertexShader: [ + var animation = this.animationsMap[ name ]; - "uniform lowp int renderType;", + if ( animation ) { - "uniform vec3 screenPosition;", - "uniform vec2 scale;", - "uniform float rotation;", + animation.weight = weight; - "attribute vec2 position;", - "attribute vec2 uv;", + } - "varying vec2 vUV;", +}; - "void main() {", +THREE.MorphBlendMesh.prototype.setAnimationTime = function ( name, time ) { - "vUV = uv;", + var animation = this.animationsMap[ name ]; - "vec2 pos = position;", + if ( animation ) { - "if( renderType == 2 ) {", + animation.time = time; - "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 );", +THREE.MorphBlendMesh.prototype.getAnimationTime = function ( name ) { - "}" + var time = 0; - ].join( "\n" ), + var animation = this.animationsMap[ name ]; - fragmentShader: [ + if ( animation ) { - "precision mediump float;", + time = animation.time; - "uniform lowp int renderType;", + } - "uniform sampler2D map;", - "uniform sampler2D occlusionMap;", - "uniform float opacity;", - "uniform vec3 color;", + return time; - "varying vec2 vUV;", +}; - "void main() {", +THREE.MorphBlendMesh.prototype.getAnimationDuration = function ( name ) { - // pink square + var duration = - 1; - "if( renderType == 0 ) {", + var animation = this.animationsMap[ name ]; - "gl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );", + if ( animation ) { - // restore + duration = animation.duration; - "} else if( renderType == 1 ) {", + } - "gl_FragColor = texture2D( map, vUV );", + return duration; - // flare +}; - "} else {", +THREE.MorphBlendMesh.prototype.playAnimation = function ( name ) { - "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 );", + var animation = this.animationsMap[ name ]; - "vec4 texture = texture2D( map, vUV );", - "texture.a *= opacity * visibility;", - "gl_FragColor = texture;", - "gl_FragColor.rgb *= color;", + if ( animation ) { - "}", + animation.time = 0; + animation.active = true; - "}" + } else { - ].join( "\n" ) + console.warn( "THREE.MorphBlendMesh: animation[" + name + "] undefined in .playAnimation()" ); } }; -/** - * @author mikael emtinger / http://gomo.se/ - * @author alteredq / http://alteredqualia.com/ - * - */ +THREE.MorphBlendMesh.prototype.stopAnimation = function ( name ) { -THREE.ShaderSprite = { + var animation = this.animationsMap[ name ]; - 'sprite': { + if ( animation ) { - vertexShader: [ + animation.active = false; - "uniform int useScreenCoordinates;", - "uniform int sizeAttenuation;", - "uniform vec3 screenPosition;", - "uniform mat4 modelViewMatrix;", - "uniform mat4 projectionMatrix;", - "uniform float rotation;", - "uniform vec2 scale;", - "uniform vec2 alignment;", - "uniform vec2 uvOffset;", - "uniform vec2 uvScale;", + } - "attribute vec2 position;", - "attribute vec2 uv;", +}; - "varying vec2 vUV;", +THREE.MorphBlendMesh.prototype.update = function ( delta ) { - "void main() {", + for ( var i = 0, il = this.animationsList.length; i < il; i ++ ) { - "vUV = uvOffset + uv * uvScale;", + var animation = this.animationsList[ i ]; - "vec2 alignedPosition = position + alignment;", + if ( ! animation.active ) continue; - "vec2 rotatedPosition;", - "rotatedPosition.x = ( cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y ) * scale.x;", - "rotatedPosition.y = ( sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y ) * scale.y;", + var frameTime = animation.duration / animation.length; - "vec4 finalPosition;", + animation.time += animation.direction * delta; - "if( useScreenCoordinates != 0 ) {", + if ( animation.mirroredLoop ) { - "finalPosition = vec4( screenPosition.xy + rotatedPosition, screenPosition.z, 1.0 );", + if ( animation.time > animation.duration || animation.time < 0 ) { - "} else {", + animation.direction *= - 1; - "finalPosition = projectionMatrix * modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );", - "finalPosition.xy += rotatedPosition * ( sizeAttenuation == 1 ? 1.0 : finalPosition.z );", + if ( animation.time > animation.duration ) { - "}", + animation.time = animation.duration; + animation.directionBackwards = true; - "gl_Position = finalPosition;", + } - "}" + if ( animation.time < 0 ) { - ].join( "\n" ), + animation.time = 0; + animation.directionBackwards = false; - fragmentShader: [ + } - "uniform vec3 color;", - "uniform sampler2D map;", - "uniform float opacity;", + } - "uniform int fogType;", - "uniform vec3 fogColor;", - "uniform float fogDensity;", - "uniform float fogNear;", - "uniform float fogFar;", - "uniform float alphaTest;", + } else { - "varying vec2 vUV;", + animation.time = animation.time % animation.duration; - "void main() {", + if ( animation.time < 0 ) animation.time += animation.duration; - "vec4 texture = texture2D( map, vUV );", + } - "if ( texture.a < alphaTest ) discard;", + var keyframe = animation.start + THREE.Math.clamp( Math.floor( animation.time / frameTime ), 0, animation.length - 1 ); + var weight = animation.weight; - "gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );", + if ( keyframe !== animation.currentFrame ) { - "if ( fogType > 0 ) {", + this.morphTargetInfluences[ animation.lastFrame ] = 0; + this.morphTargetInfluences[ animation.currentFrame ] = 1 * weight; - "float depth = gl_FragCoord.z / gl_FragCoord.w;", - "float fogFactor = 0.0;", + this.morphTargetInfluences[ keyframe ] = 0; - "if ( fogType == 1 ) {", + animation.lastFrame = animation.currentFrame; + animation.currentFrame = keyframe; - "fogFactor = smoothstep( fogNear, fogFar, depth );", + } - "} else {", + var mix = ( animation.time % frameTime ) / frameTime; - "const float LOG2 = 1.442695;", - "float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );", - "fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );", + if ( animation.directionBackwards ) mix = 1 - mix; - "}", + if ( animation.currentFrame !== animation.lastFrame ) { - "gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );", + this.morphTargetInfluences[ animation.currentFrame ] = mix * weight; + this.morphTargetInfluences[ animation.lastFrame ] = ( 1 - mix ) * weight; - "}", + } else { - "}" + this.morphTargetInfluences[ animation.currentFrame ] = weight; - ].join( "\n" ) + } }