diff --git a/build/three.js b/build/three.js index be1738a238a3eebeed5235071e22c3d7cacc0356..7a818dc7071debdb89b1e8ca6c9023439106ecb6 100644 --- a/build/three.js +++ b/build/three.js @@ -9575,466 +9575,567 @@ } /** - * @author mrdoob / http://mrdoob.com/ + * @author bhouston / http://clara.io */ - function BufferAttribute( array, itemSize, normalized ) { - - if ( Array.isArray( array ) ) { - - throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' ); - - } - - this.uuid = exports.Math.generateUUID(); - - this.array = array; - this.itemSize = itemSize; - this.count = array.length / itemSize; - this.normalized = normalized === true; - - this.dynamic = false; - this.updateRange = { offset: 0, count: - 1 }; + function Ray( origin, direction ) { - this.version = 0; + this.origin = ( origin !== undefined ) ? origin : new Vector3(); + this.direction = ( direction !== undefined ) ? direction : new Vector3(); } - BufferAttribute.prototype = { + Ray.prototype = { - constructor: BufferAttribute, + constructor: Ray, - isBufferAttribute: true, + set: function ( origin, direction ) { - set needsUpdate( value ) { + this.origin.copy( origin ); + this.direction.copy( direction ); - if ( value === true ) this.version ++; + return this; }, - setDynamic: function ( value ) { - - this.dynamic = value; + clone: function () { - return this; + return new this.constructor().copy( this ); }, - copy: function ( source ) { - - this.array = new source.array.constructor( source.array ); - this.itemSize = source.itemSize; - this.count = source.count; - this.normalized = source.normalized; + copy: function ( ray ) { - this.dynamic = source.dynamic; + this.origin.copy( ray.origin ); + this.direction.copy( ray.direction ); return this; }, - copyAt: function ( index1, attribute, index2 ) { + at: function ( t, optionalTarget ) { - index1 *= this.itemSize; - index2 *= attribute.itemSize; + var result = optionalTarget || new Vector3(); - for ( var i = 0, l = this.itemSize; i < l; i ++ ) { + return result.copy( this.direction ).multiplyScalar( t ).add( this.origin ); - this.array[ index1 + i ] = attribute.array[ index2 + i ]; + }, - } + lookAt: function ( v ) { + + this.direction.copy( v ).sub( this.origin ).normalize(); return this; }, - copyArray: function ( array ) { - - this.array.set( array ); + recast: function () { - return this; + var v1 = new Vector3(); - }, + return function recast( t ) { - copyColorsArray: function ( colors ) { + this.origin.copy( this.at( t, v1 ) ); - var array = this.array, offset = 0; + return this; - for ( var i = 0, l = colors.length; i < l; i ++ ) { + }; - var color = colors[ i ]; + }(), - if ( color === undefined ) { + closestPointToPoint: function ( point, optionalTarget ) { - console.warn( 'THREE.BufferAttribute.copyColorsArray(): color is undefined', i ); - color = new Color(); + var result = optionalTarget || new Vector3(); + result.subVectors( point, this.origin ); + var directionDistance = result.dot( this.direction ); - } + if ( directionDistance < 0 ) { - array[ offset ++ ] = color.r; - array[ offset ++ ] = color.g; - array[ offset ++ ] = color.b; + return result.copy( this.origin ); } - return this; + return result.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); }, - copyIndicesArray: function ( indices ) { + distanceToPoint: function ( point ) { - var array = this.array, offset = 0; + return Math.sqrt( this.distanceSqToPoint( point ) ); - for ( var i = 0, l = indices.length; i < l; i ++ ) { + }, - var index = indices[ i ]; + distanceSqToPoint: function () { - array[ offset ++ ] = index.a; - array[ offset ++ ] = index.b; - array[ offset ++ ] = index.c; + var v1 = new Vector3(); - } + return function distanceSqToPoint( point ) { - return this; + var directionDistance = v1.subVectors( point, this.origin ).dot( this.direction ); - }, + // point behind the ray - copyVector2sArray: function ( vectors ) { + if ( directionDistance < 0 ) { - var array = this.array, offset = 0; + return this.origin.distanceToSquared( point ); - for ( var i = 0, l = vectors.length; i < l; i ++ ) { + } - var vector = vectors[ i ]; + v1.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); - if ( vector === undefined ) { + return v1.distanceToSquared( point ); - console.warn( 'THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i ); - vector = new Vector2(); + }; - } + }(), - array[ offset ++ ] = vector.x; - array[ offset ++ ] = vector.y; + distanceSqToSegment: function () { - } + var segCenter = new Vector3(); + var segDir = new Vector3(); + var diff = new Vector3(); - return this; + return function distanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { - }, + // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteDistRaySegment.h + // It returns the min distance between the ray and the segment + // defined by v0 and v1 + // It can also set two optional targets : + // - The closest point on the ray + // - The closest point on the segment - copyVector3sArray: function ( vectors ) { + segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 ); + segDir.copy( v1 ).sub( v0 ).normalize(); + diff.copy( this.origin ).sub( segCenter ); - var array = this.array, offset = 0; + var segExtent = v0.distanceTo( v1 ) * 0.5; + var a01 = - this.direction.dot( segDir ); + var b0 = diff.dot( this.direction ); + var b1 = - diff.dot( segDir ); + var c = diff.lengthSq(); + var det = Math.abs( 1 - a01 * a01 ); + var s0, s1, sqrDist, extDet; - for ( var i = 0, l = vectors.length; i < l; i ++ ) { + if ( det > 0 ) { - var vector = vectors[ i ]; + // The ray and segment are not parallel. - if ( vector === undefined ) { + s0 = a01 * b1 - b0; + s1 = a01 * b0 - b1; + extDet = segExtent * det; - console.warn( 'THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i ); - vector = new Vector3(); + if ( s0 >= 0 ) { - } + if ( s1 >= - extDet ) { - array[ offset ++ ] = vector.x; - array[ offset ++ ] = vector.y; - array[ offset ++ ] = vector.z; + if ( s1 <= extDet ) { - } + // region 0 + // Minimum at interior points of ray and segment. - return this; + var invDet = 1 / det; + s0 *= invDet; + s1 *= invDet; + sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; - }, + } else { - copyVector4sArray: function ( vectors ) { + // region 1 - var array = this.array, offset = 0; + s1 = segExtent; + s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - for ( var i = 0, l = vectors.length; i < l; i ++ ) { + } - var vector = vectors[ i ]; + } else { - if ( vector === undefined ) { + // region 5 - console.warn( 'THREE.BufferAttribute.copyVector4sArray(): vector is undefined', i ); - vector = new Vector4(); + s1 = - segExtent; + s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - } + } - array[ offset ++ ] = vector.x; - array[ offset ++ ] = vector.y; - array[ offset ++ ] = vector.z; - array[ offset ++ ] = vector.w; + } else { - } + if ( s1 <= - extDet ) { - return this; + // region 4 - }, + s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); + s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - set: function ( value, offset ) { + } else if ( s1 <= extDet ) { - if ( offset === undefined ) offset = 0; + // region 3 - this.array.set( value, offset ); + s0 = 0; + s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); + sqrDist = s1 * ( s1 + 2 * b1 ) + c; - return this; + } else { - }, + // region 2 - getX: function ( index ) { + s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); + s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - return this.array[ index * this.itemSize ]; + } - }, + } - setX: function ( index, x ) { + } else { - this.array[ index * this.itemSize ] = x; + // Ray and segment are parallel. - return this; + s1 = ( a01 > 0 ) ? - segExtent : segExtent; + s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); + sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; - }, + } - getY: function ( index ) { + if ( optionalPointOnRay ) { - return this.array[ index * this.itemSize + 1 ]; + optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin ); - }, + } - setY: function ( index, y ) { + if ( optionalPointOnSegment ) { - this.array[ index * this.itemSize + 1 ] = y; + optionalPointOnSegment.copy( segDir ).multiplyScalar( s1 ).add( segCenter ); - return this; + } - }, - - getZ: function ( index ) { + return sqrDist; - return this.array[ index * this.itemSize + 2 ]; + }; - }, + }(), - setZ: function ( index, z ) { + intersectSphere: function () { - this.array[ index * this.itemSize + 2 ] = z; + var v1 = new Vector3(); - return this; + return function intersectSphere( sphere, optionalTarget ) { - }, + v1.subVectors( sphere.center, this.origin ); + var tca = v1.dot( this.direction ); + var d2 = v1.dot( v1 ) - tca * tca; + var radius2 = sphere.radius * sphere.radius; - getW: function ( index ) { + if ( d2 > radius2 ) return null; - return this.array[ index * this.itemSize + 3 ]; + var thc = Math.sqrt( radius2 - d2 ); - }, + // t0 = first intersect point - entrance on front of sphere + var t0 = tca - thc; - setW: function ( index, w ) { + // t1 = second intersect point - exit point on back of sphere + var t1 = tca + thc; - this.array[ index * this.itemSize + 3 ] = w; + // test to see if both t0 and t1 are behind the ray - if so, return null + if ( t0 < 0 && t1 < 0 ) return null; - return this; + // test to see if t0 is behind the ray: + // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, + // in order to always return an intersect point that is in front of the ray. + if ( t0 < 0 ) return this.at( t1, optionalTarget ); - }, + // else t0 is in front of the ray, so return the first collision point scaled by t0 + return this.at( t0, optionalTarget ); - setXY: function ( index, x, y ) { + }; - index *= this.itemSize; + }(), - this.array[ index + 0 ] = x; - this.array[ index + 1 ] = y; + intersectsSphere: function ( sphere ) { - return this; + return this.distanceToPoint( sphere.center ) <= sphere.radius; }, - setXYZ: function ( index, x, y, z ) { + distanceToPlane: function ( plane ) { - index *= this.itemSize; + var denominator = plane.normal.dot( this.direction ); - this.array[ index + 0 ] = x; - this.array[ index + 1 ] = y; - this.array[ index + 2 ] = z; + if ( denominator === 0 ) { - return this; + // line is coplanar, return origin + if ( plane.distanceToPoint( this.origin ) === 0 ) { - }, + return 0; - setXYZW: function ( index, x, y, z, w ) { + } - index *= this.itemSize; + // Null is preferable to undefined since undefined means.... it is undefined - this.array[ index + 0 ] = x; - this.array[ index + 1 ] = y; - this.array[ index + 2 ] = z; - this.array[ index + 3 ] = w; + return null; - return this; + } - }, + var t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator; - clone: function () { + // Return if the ray never intersects the plane - return new this.constructor().copy( this ); + return t >= 0 ? t : null; - } + }, - }; + intersectPlane: function ( plane, optionalTarget ) { - // + var t = this.distanceToPlane( plane ); - function Int8Attribute( array, itemSize ) { + if ( t === null ) { - return new BufferAttribute( new Int8Array( array ), itemSize ); + return null; - } + } - function Uint8Attribute( array, itemSize ) { + return this.at( t, optionalTarget ); - return new BufferAttribute( new Uint8Array( array ), itemSize ); + }, - } - function Uint8ClampedAttribute( array, itemSize ) { - return new BufferAttribute( new Uint8ClampedArray( array ), itemSize ); + intersectsPlane: function ( plane ) { - } + // check if the ray lies on the plane first - function Int16Attribute( array, itemSize ) { + var distToPoint = plane.distanceToPoint( this.origin ); - return new BufferAttribute( new Int16Array( array ), itemSize ); + if ( distToPoint === 0 ) { - } + return true; - function Uint16Attribute( array, itemSize ) { + } - return new BufferAttribute( new Uint16Array( array ), itemSize ); + var denominator = plane.normal.dot( this.direction ); - } + if ( denominator * distToPoint < 0 ) { - function Int32Attribute( array, itemSize ) { + return true; - return new BufferAttribute( new Int32Array( array ), itemSize ); + } - } + // ray origin is behind the plane (and is pointing behind it) - function Uint32Attribute( array, itemSize ) { + return false; - return new BufferAttribute( new Uint32Array( array ), itemSize ); + }, - } + intersectBox: function ( box, optionalTarget ) { - function Float32Attribute( array, itemSize ) { + var tmin, tmax, tymin, tymax, tzmin, tzmax; - return new BufferAttribute( new Float32Array( array ), itemSize ); + var invdirx = 1 / this.direction.x, + invdiry = 1 / this.direction.y, + invdirz = 1 / this.direction.z; - } + var origin = this.origin; - function Float64Attribute( array, itemSize ) { + if ( invdirx >= 0 ) { - return new BufferAttribute( new Float64Array( array ), itemSize ); + tmin = ( box.min.x - origin.x ) * invdirx; + tmax = ( box.max.x - origin.x ) * invdirx; - } + } else { - // Deprecated + tmin = ( box.max.x - origin.x ) * invdirx; + tmax = ( box.min.x - origin.x ) * invdirx; - function DynamicBufferAttribute( array, itemSize ) { + } - console.warn( 'THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setDynamic( true ) instead.' ); - return new BufferAttribute( array, itemSize ).setDynamic( true ); + if ( invdiry >= 0 ) { - } + tymin = ( box.min.y - origin.y ) * invdiry; + tymax = ( box.max.y - origin.y ) * invdiry; - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ + } else { - function Face3( a, b, c, normal, color, materialIndex ) { + tymin = ( box.max.y - origin.y ) * invdiry; + tymax = ( box.min.y - origin.y ) * invdiry; - this.a = a; - this.b = b; - this.c = c; + } - this.normal = (normal && normal.isVector3) ? normal : new Vector3(); - this.vertexNormals = Array.isArray( normal ) ? normal : []; + if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null; - this.color = (color && color.isColor) ? color : new Color(); - this.vertexColors = Array.isArray( color ) ? color : []; + // These lines also handle the case where tmin or tmax is NaN + // (result of 0 * Infinity). x !== x returns true if x is NaN - this.materialIndex = materialIndex !== undefined ? materialIndex : 0; + if ( tymin > tmin || tmin !== tmin ) tmin = tymin; - } + if ( tymax < tmax || tmax !== tmax ) tmax = tymax; - Face3.prototype = { + if ( invdirz >= 0 ) { - constructor: Face3, + tzmin = ( box.min.z - origin.z ) * invdirz; + tzmax = ( box.max.z - origin.z ) * invdirz; - clone: function () { + } else { - return new this.constructor().copy( this ); + tzmin = ( box.max.z - origin.z ) * invdirz; + tzmax = ( box.min.z - origin.z ) * invdirz; - }, + } - copy: function ( source ) { + if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null; - this.a = source.a; - this.b = source.b; - this.c = source.c; + if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin; - this.normal.copy( source.normal ); - this.color.copy( source.color ); + if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax; - this.materialIndex = source.materialIndex; + //return point closest to the ray (positive side) - for ( var i = 0, il = source.vertexNormals.length; i < il; i ++ ) { + if ( tmax < 0 ) return null; - this.vertexNormals[ i ] = source.vertexNormals[ i ].clone(); + return this.at( tmin >= 0 ? tmin : tmax, optionalTarget ); - } + }, - for ( var i = 0, il = source.vertexColors.length; i < il; i ++ ) { + intersectsBox: ( function () { - this.vertexColors[ i ] = source.vertexColors[ i ].clone(); + var v = new Vector3(); - } + return function intersectsBox( box ) { - return this; + return this.intersectBox( box, v ) !== null; - } + }; - }; + } )(), - /** - * @author mrdoob / http://mrdoob.com/ - * @author WestLangley / http://github.com/WestLangley - * @author bhouston / http://clara.io - */ + intersectTriangle: function () { - function Euler( x, y, z, order ) { + // Compute the offset origin, edges, and normal. + var diff = new Vector3(); + var edge1 = new Vector3(); + var edge2 = new Vector3(); + var normal = new Vector3(); - this._x = x || 0; - this._y = y || 0; - this._z = z || 0; - this._order = order || Euler.DefaultOrder; + return function intersectTriangle( a, b, c, backfaceCulling, optionalTarget ) { - } + // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h - Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ]; + edge1.subVectors( b, a ); + edge2.subVectors( c, a ); + normal.crossVectors( edge1, edge2 ); - Euler.DefaultOrder = 'XYZ'; + // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, + // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by + // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) + // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) + // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) + var DdN = this.direction.dot( normal ); + var sign; - Euler.prototype = { + if ( DdN > 0 ) { - constructor: Euler, + if ( backfaceCulling ) return null; + sign = 1; - isEuler: true, + } else if ( DdN < 0 ) { - get x () { + sign = - 1; + DdN = - DdN; + + } else { + + return null; + + } + + diff.subVectors( this.origin, a ); + var DdQxE2 = sign * this.direction.dot( edge2.crossVectors( diff, edge2 ) ); + + // b1 < 0, no intersection + if ( DdQxE2 < 0 ) { + + return null; + + } + + var DdE1xQ = sign * this.direction.dot( edge1.cross( diff ) ); + + // b2 < 0, no intersection + if ( DdE1xQ < 0 ) { + + return null; + + } + + // b1+b2 > 1, no intersection + if ( DdQxE2 + DdE1xQ > DdN ) { + + return null; + + } + + // Line intersects triangle, check if ray does. + var QdN = - sign * diff.dot( normal ); + + // t < 0, no intersection + if ( QdN < 0 ) { + + return null; + + } + + // Ray intersects triangle. + return this.at( QdN / DdN, optionalTarget ); + + }; + + }(), + + applyMatrix4: function ( matrix4 ) { + + this.direction.add( this.origin ).applyMatrix4( matrix4 ); + this.origin.applyMatrix4( matrix4 ); + this.direction.sub( this.origin ); + this.direction.normalize(); + + return this; + + }, + + equals: function ( ray ) { + + return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction ); + + } + + }; + + /** + * @author mrdoob / http://mrdoob.com/ + * @author WestLangley / http://github.com/WestLangley + * @author bhouston / http://clara.io + */ + + function Euler( x, y, z, order ) { + + this._x = x || 0; + this._y = y || 0; + this._z = z || 0; + this._order = order || Euler.DefaultOrder; + + } + + Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ]; + + Euler.DefaultOrder = 'XYZ'; + + Euler.prototype = { + + constructor: Euler, + + isEuler: true, + + get x () { return this._x; @@ -11101,3852 +11202,3556 @@ } ); - var count$3 = 0; - function Object3DIdCount() { return count$3++; }; + var count$2 = 0; + function Object3DIdCount() { return count$2++; }; /** - * @author mrdoob / http://mrdoob.com/ - * @author kile / http://kile.stravaganza.org/ - * @author alteredq / http://alteredqualia.com/ - * @author mikael emtinger / http://gomo.se/ - * @author zz85 / http://www.lab4games.net/zz85/blog * @author bhouston / http://clara.io */ - function Geometry() { + function Line3( start, end ) { - Object.defineProperty( this, 'id', { value: GeometryIdCount() } ); + this.start = ( start !== undefined ) ? start : new Vector3(); + this.end = ( end !== undefined ) ? end : new Vector3(); - this.uuid = exports.Math.generateUUID(); + } - this.name = ''; - this.type = 'Geometry'; + Line3.prototype = { - this.vertices = []; - this.colors = []; - this.faces = []; - this.faceVertexUvs = [ [] ]; + constructor: Line3, - this.morphTargets = []; - this.morphNormals = []; + set: function ( start, end ) { - this.skinWeights = []; - this.skinIndices = []; + this.start.copy( start ); + this.end.copy( end ); - this.lineDistances = []; + return this; - this.boundingBox = null; - this.boundingSphere = null; + }, - // update flags + clone: function () { - this.elementsNeedUpdate = false; - this.verticesNeedUpdate = false; - this.uvsNeedUpdate = false; - this.normalsNeedUpdate = false; - this.colorsNeedUpdate = false; - this.lineDistancesNeedUpdate = false; - this.groupsNeedUpdate = false; + return new this.constructor().copy( this ); - } + }, - Object.assign( Geometry.prototype, EventDispatcher.prototype, { + copy: function ( line ) { - isGeometry: true, + this.start.copy( line.start ); + this.end.copy( line.end ); - applyMatrix: function ( matrix ) { + return this; - var normalMatrix = new Matrix3().getNormalMatrix( matrix ); + }, - for ( var i = 0, il = this.vertices.length; i < il; i ++ ) { + getCenter: function ( optionalTarget ) { - var vertex = this.vertices[ i ]; - vertex.applyMatrix4( matrix ); + var result = optionalTarget || new Vector3(); + return result.addVectors( this.start, this.end ).multiplyScalar( 0.5 ); - } + }, - for ( var i = 0, il = this.faces.length; i < il; i ++ ) { + delta: function ( optionalTarget ) { - var face = this.faces[ i ]; - face.normal.applyMatrix3( normalMatrix ).normalize(); + var result = optionalTarget || new Vector3(); + return result.subVectors( this.end, this.start ); - for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { + }, - face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize(); + distanceSq: function () { - } + return this.start.distanceToSquared( this.end ); - } + }, - if ( this.boundingBox !== null ) { + distance: function () { - this.computeBoundingBox(); + return this.start.distanceTo( this.end ); - } + }, - if ( this.boundingSphere !== null ) { + at: function ( t, optionalTarget ) { - this.computeBoundingSphere(); + var result = optionalTarget || new Vector3(); - } + return this.delta( result ).multiplyScalar( t ).add( this.start ); - this.verticesNeedUpdate = true; - this.normalsNeedUpdate = true; + }, - return this; + closestPointToPointParameter: function () { - }, + var startP = new Vector3(); + var startEnd = new Vector3(); - rotateX: function () { + return function closestPointToPointParameter( point, clampToLine ) { - // rotate geometry around world x-axis + startP.subVectors( point, this.start ); + startEnd.subVectors( this.end, this.start ); - var m1; + var startEnd2 = startEnd.dot( startEnd ); + var startEnd_startP = startEnd.dot( startP ); - return function rotateX( angle ) { + var t = startEnd_startP / startEnd2; - if ( m1 === undefined ) m1 = new Matrix4(); + if ( clampToLine ) { - m1.makeRotationX( angle ); + t = exports.Math.clamp( t, 0, 1 ); - this.applyMatrix( m1 ); + } - return this; + return t; }; }(), - rotateY: function () { + closestPointToPoint: function ( point, clampToLine, optionalTarget ) { - // rotate geometry around world y-axis + var t = this.closestPointToPointParameter( point, clampToLine ); - var m1; + var result = optionalTarget || new Vector3(); - return function rotateY( angle ) { + return this.delta( result ).multiplyScalar( t ).add( this.start ); - if ( m1 === undefined ) m1 = new Matrix4(); + }, - m1.makeRotationY( angle ); + applyMatrix4: function ( matrix ) { - this.applyMatrix( m1 ); + this.start.applyMatrix4( matrix ); + this.end.applyMatrix4( matrix ); - return this; + return this; - }; + }, - }(), + equals: function ( line ) { - rotateZ: function () { + return line.start.equals( this.start ) && line.end.equals( this.end ); - // rotate geometry around world z-axis + } - var m1; + }; - return function rotateZ( angle ) { + /** + * @author bhouston / http://clara.io + * @author mrdoob / http://mrdoob.com/ + */ - if ( m1 === undefined ) m1 = new Matrix4(); + function Triangle( a, b, c ) { - m1.makeRotationZ( angle ); + this.a = ( a !== undefined ) ? a : new Vector3(); + this.b = ( b !== undefined ) ? b : new Vector3(); + this.c = ( c !== undefined ) ? c : new Vector3(); - this.applyMatrix( m1 ); + } - return this; + Triangle.normal = function () { - }; + var v0 = new Vector3(); - }(), + return function normal( a, b, c, optionalTarget ) { - translate: function () { - - // translate geometry + var result = optionalTarget || new Vector3(); - var m1; + result.subVectors( c, b ); + v0.subVectors( a, b ); + result.cross( v0 ); - return function translate( x, y, z ) { + var resultLengthSq = result.lengthSq(); + if ( resultLengthSq > 0 ) { - if ( m1 === undefined ) m1 = new Matrix4(); + return result.multiplyScalar( 1 / Math.sqrt( resultLengthSq ) ); - m1.makeTranslation( x, y, z ); + } - this.applyMatrix( m1 ); + return result.set( 0, 0, 0 ); - return this; + }; - }; + }(); - }(), + // static/instance method to calculate barycentric coordinates + // based on: http://www.blackpawn.com/texts/pointinpoly/default.html + Triangle.barycoordFromPoint = function () { - scale: function () { + var v0 = new Vector3(); + var v1 = new Vector3(); + var v2 = new Vector3(); - // scale geometry + return function barycoordFromPoint( point, a, b, c, optionalTarget ) { - var m1; + v0.subVectors( c, a ); + v1.subVectors( b, a ); + v2.subVectors( point, a ); - return function scale( x, y, z ) { + var dot00 = v0.dot( v0 ); + var dot01 = v0.dot( v1 ); + var dot02 = v0.dot( v2 ); + var dot11 = v1.dot( v1 ); + var dot12 = v1.dot( v2 ); - if ( m1 === undefined ) m1 = new Matrix4(); + var denom = ( dot00 * dot11 - dot01 * dot01 ); - m1.makeScale( x, y, z ); + var result = optionalTarget || new Vector3(); - this.applyMatrix( m1 ); + // collinear or singular triangle + if ( denom === 0 ) { - return this; + // arbitrary location outside of triangle? + // not sure if this is the best idea, maybe should be returning undefined + return result.set( - 2, - 1, - 1 ); - }; + } - }(), + var invDenom = 1 / denom; + var u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; + var v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; - lookAt: function () { + // barycentric coordinates must always sum to 1 + return result.set( 1 - u - v, v, u ); - var obj; + }; - return function lookAt( vector ) { + }(); - if ( obj === undefined ) obj = new Object3D(); + Triangle.containsPoint = function () { - obj.lookAt( vector ); + var v1 = new Vector3(); - obj.updateMatrix(); + return function containsPoint( point, a, b, c ) { - this.applyMatrix( obj.matrix ); + var result = Triangle.barycoordFromPoint( point, a, b, c, v1 ); - }; + return ( result.x >= 0 ) && ( result.y >= 0 ) && ( ( result.x + result.y ) <= 1 ); - }(), + }; - fromBufferGeometry: function ( geometry ) { + }(); - var scope = this; + Triangle.prototype = { - var indices = geometry.index !== null ? geometry.index.array : undefined; - var attributes = geometry.attributes; + constructor: Triangle, - var positions = attributes.position.array; - var normals = attributes.normal !== undefined ? attributes.normal.array : undefined; - var colors = attributes.color !== undefined ? attributes.color.array : undefined; - var uvs = attributes.uv !== undefined ? attributes.uv.array : undefined; - var uvs2 = attributes.uv2 !== undefined ? attributes.uv2.array : undefined; + set: function ( a, b, c ) { - if ( uvs2 !== undefined ) this.faceVertexUvs[ 1 ] = []; + this.a.copy( a ); + this.b.copy( b ); + this.c.copy( c ); - var tempNormals = []; - var tempUVs = []; - var tempUVs2 = []; + return this; - for ( var i = 0, j = 0; i < positions.length; i += 3, j += 2 ) { + }, - scope.vertices.push( new Vector3( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ) ); + setFromPointsAndIndices: function ( points, i0, i1, i2 ) { - if ( normals !== undefined ) { + this.a.copy( points[ i0 ] ); + this.b.copy( points[ i1 ] ); + this.c.copy( points[ i2 ] ); - tempNormals.push( new Vector3( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ) ); + return this; - } + }, - if ( colors !== undefined ) { + clone: function () { - scope.colors.push( new Color( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ) ); + return new this.constructor().copy( this ); - } + }, - if ( uvs !== undefined ) { + copy: function ( triangle ) { - tempUVs.push( new Vector2( uvs[ j ], uvs[ j + 1 ] ) ); + this.a.copy( triangle.a ); + this.b.copy( triangle.b ); + this.c.copy( triangle.c ); - } + return this; - if ( uvs2 !== undefined ) { + }, - tempUVs2.push( new Vector2( uvs2[ j ], uvs2[ j + 1 ] ) ); + area: function () { - } + var v0 = new Vector3(); + var v1 = new Vector3(); - } + return function area() { - function addFace( a, b, c, materialIndex ) { + v0.subVectors( this.c, this.b ); + v1.subVectors( this.a, this.b ); - var vertexNormals = normals !== undefined ? [ tempNormals[ a ].clone(), tempNormals[ b ].clone(), tempNormals[ c ].clone() ] : []; - var vertexColors = colors !== undefined ? [ scope.colors[ a ].clone(), scope.colors[ b ].clone(), scope.colors[ c ].clone() ] : []; + return v0.cross( v1 ).length() * 0.5; - var face = new Face3( a, b, c, vertexNormals, vertexColors, materialIndex ); + }; - scope.faces.push( face ); + }(), - if ( uvs !== undefined ) { + midpoint: function ( optionalTarget ) { - scope.faceVertexUvs[ 0 ].push( [ tempUVs[ a ].clone(), tempUVs[ b ].clone(), tempUVs[ c ].clone() ] ); + var result = optionalTarget || new Vector3(); + return result.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 ); - } + }, - if ( uvs2 !== undefined ) { + normal: function ( optionalTarget ) { - scope.faceVertexUvs[ 1 ].push( [ tempUVs2[ a ].clone(), tempUVs2[ b ].clone(), tempUVs2[ c ].clone() ] ); + return Triangle.normal( this.a, this.b, this.c, optionalTarget ); - } + }, - } + plane: function ( optionalTarget ) { - if ( indices !== undefined ) { + var result = optionalTarget || new Plane(); - var groups = geometry.groups; + return result.setFromCoplanarPoints( this.a, this.b, this.c ); - if ( groups.length > 0 ) { + }, - for ( var i = 0; i < groups.length; i ++ ) { + barycoordFromPoint: function ( point, optionalTarget ) { - var group = groups[ i ]; + return Triangle.barycoordFromPoint( point, this.a, this.b, this.c, optionalTarget ); - var start = group.start; - var count = group.count; + }, - for ( var j = start, jl = start + count; j < jl; j += 3 ) { + containsPoint: function ( point ) { - addFace( indices[ j ], indices[ j + 1 ], indices[ j + 2 ], group.materialIndex ); + return Triangle.containsPoint( point, this.a, this.b, this.c ); - } + }, - } + closestPointToPoint: function () { - } else { + var plane, edgeList, projectedPoint, closestPoint; - for ( var i = 0; i < indices.length; i += 3 ) { + return function closestPointToPoint( point, optionalTarget ) { - addFace( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] ); + if ( plane === undefined ) { - } + plane = new Plane(); + edgeList = [ new Line3(), new Line3(), new Line3() ]; + projectedPoint = new Vector3(); + closestPoint = new Vector3(); } - } else { - - for ( var i = 0; i < positions.length / 3; i += 3 ) { + var result = optionalTarget || new Vector3(); + var minDistance = Infinity; - addFace( i, i + 1, i + 2 ); + // project the point onto the plane of the triangle - } + plane.setFromCoplanarPoints( this.a, this.b, this.c ); + plane.projectPoint( point, projectedPoint ); - } + // check if the projection lies within the triangle - this.computeFaceNormals(); + if( this.containsPoint( projectedPoint ) === true ) { - if ( geometry.boundingBox !== null ) { + // if so, this is the closest point - this.boundingBox = geometry.boundingBox.clone(); + result.copy( projectedPoint ); - } + } else { - if ( geometry.boundingSphere !== null ) { + // if not, the point falls outside the triangle. the result is the closest point to the triangle's edges or vertices - this.boundingSphere = geometry.boundingSphere.clone(); + edgeList[ 0 ].set( this.a, this.b ); + edgeList[ 1 ].set( this.b, this.c ); + edgeList[ 2 ].set( this.c, this.a ); - } + for( var i = 0; i < edgeList.length; i ++ ) { - return this; + edgeList[ i ].closestPointToPoint( projectedPoint, true, closestPoint ); - }, + var distance = projectedPoint.distanceToSquared( closestPoint ); - center: function () { + if( distance < minDistance ) { - this.computeBoundingBox(); + minDistance = distance; - var offset = this.boundingBox.getCenter().negate(); + result.copy( closestPoint ); - this.translate( offset.x, offset.y, offset.z ); + } - return offset; + } - }, + } - normalize: function () { + return result; - this.computeBoundingSphere(); + }; - var center = this.boundingSphere.center; - var radius = this.boundingSphere.radius; + }(), - var s = radius === 0 ? 1 : 1.0 / radius; + equals: function ( triangle ) { - var matrix = new Matrix4(); - matrix.set( - s, 0, 0, - s * center.x, - 0, s, 0, - s * center.y, - 0, 0, s, - s * center.z, - 0, 0, 0, 1 - ); + return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c ); - this.applyMatrix( matrix ); + } - return this; + }; - }, + /** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ - computeFaceNormals: function () { + function Face3( a, b, c, normal, color, materialIndex ) { - var cb = new Vector3(), ab = new Vector3(); + this.a = a; + this.b = b; + this.c = c; - for ( var f = 0, fl = this.faces.length; f < fl; f ++ ) { + this.normal = (normal && normal.isVector3) ? normal : new Vector3(); + this.vertexNormals = Array.isArray( normal ) ? normal : []; - var face = this.faces[ f ]; + this.color = (color && color.isColor) ? color : new Color(); + this.vertexColors = Array.isArray( color ) ? color : []; - var vA = this.vertices[ face.a ]; - var vB = this.vertices[ face.b ]; - var vC = this.vertices[ face.c ]; + this.materialIndex = materialIndex !== undefined ? materialIndex : 0; - cb.subVectors( vC, vB ); - ab.subVectors( vA, vB ); - cb.cross( ab ); + } - cb.normalize(); + Face3.prototype = { - face.normal.copy( cb ); + constructor: Face3, - } + clone: function () { + + return new this.constructor().copy( this ); }, - computeVertexNormals: function ( areaWeighted ) { + copy: function ( source ) { - if ( areaWeighted === undefined ) areaWeighted = true; + this.a = source.a; + this.b = source.b; + this.c = source.c; - var v, vl, f, fl, face, vertices; + this.normal.copy( source.normal ); + this.color.copy( source.color ); - vertices = new Array( this.vertices.length ); + this.materialIndex = source.materialIndex; - for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { + for ( var i = 0, il = source.vertexNormals.length; i < il; i ++ ) { - vertices[ v ] = new Vector3(); + this.vertexNormals[ i ] = source.vertexNormals[ i ].clone(); } - if ( areaWeighted ) { + for ( var i = 0, il = source.vertexColors.length; i < il; i ++ ) { - // vertex normals weighted by triangle areas - // http://www.iquilezles.org/www/articles/normals/normals.htm + this.vertexColors[ i ] = source.vertexColors[ i ].clone(); - var vA, vB, vC; - var cb = new Vector3(), ab = new Vector3(); + } - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + return this; - face = this.faces[ f ]; + } - vA = this.vertices[ face.a ]; - vB = this.vertices[ face.b ]; - vC = this.vertices[ face.c ]; + }; - cb.subVectors( vC, vB ); - ab.subVectors( vA, vB ); - cb.cross( ab ); + /** + * @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: + * } + */ - vertices[ face.a ].add( cb ); - vertices[ face.b ].add( cb ); - vertices[ face.c ].add( cb ); + function MeshBasicMaterial( parameters ) { - } + Material.call( this ); - } else { + this.type = 'MeshBasicMaterial'; - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + this.color = new Color( 0xffffff ); // emissive - face = this.faces[ f ]; + this.map = null; - vertices[ face.a ].add( face.normal ); - vertices[ face.b ].add( face.normal ); - vertices[ face.c ].add( face.normal ); + this.aoMap = null; + this.aoMapIntensity = 1.0; - } + this.specularMap = null; - } + this.alphaMap = null; - for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { + this.envMap = null; + this.combine = MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; - vertices[ v ].normalize(); + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; - } + this.skinning = false; + this.morphTargets = false; - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + this.lights = false; - face = this.faces[ f ]; + this.setValues( parameters ); - var vertexNormals = face.vertexNormals; + } - if ( vertexNormals.length === 3 ) { + MeshBasicMaterial.prototype = Object.create( Material.prototype ); + MeshBasicMaterial.prototype.constructor = MeshBasicMaterial; - vertexNormals[ 0 ].copy( vertices[ face.a ] ); - vertexNormals[ 1 ].copy( vertices[ face.b ] ); - vertexNormals[ 2 ].copy( vertices[ face.c ] ); + MeshBasicMaterial.prototype.isMeshBasicMaterial = true; - } else { + MeshBasicMaterial.prototype.copy = function ( source ) { - vertexNormals[ 0 ] = vertices[ face.a ].clone(); - vertexNormals[ 1 ] = vertices[ face.b ].clone(); - vertexNormals[ 2 ] = vertices[ face.c ].clone(); + Material.prototype.copy.call( this, source ); - } + this.color.copy( source.color ); - } + this.map = source.map; - if ( this.faces.length > 0 ) { + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; - this.normalsNeedUpdate = true; + this.specularMap = source.specularMap; - } + this.alphaMap = source.alphaMap; - }, + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; - computeMorphNormals: function () { + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; - var i, il, f, fl, face; + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; - // save original normals - // - create temp variables on first access - // otherwise just copy (for faster repeated calls) + return this; - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + }; - face = this.faces[ f ]; + /** + * @author mrdoob / http://mrdoob.com/ + */ - if ( ! face.__originalFaceNormal ) { + function BufferAttribute( array, itemSize, normalized ) { - face.__originalFaceNormal = face.normal.clone(); + if ( Array.isArray( array ) ) { - } else { + throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' ); - face.__originalFaceNormal.copy( face.normal ); + } - } + this.uuid = exports.Math.generateUUID(); - if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = []; + this.array = array; + this.itemSize = itemSize; + this.count = array.length / itemSize; + this.normalized = normalized === true; - for ( i = 0, il = face.vertexNormals.length; i < il; i ++ ) { + this.dynamic = false; + this.updateRange = { offset: 0, count: - 1 }; - if ( ! face.__originalVertexNormals[ i ] ) { + this.version = 0; - face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone(); + } - } else { + BufferAttribute.prototype = { - face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] ); + constructor: BufferAttribute, - } + isBufferAttribute: true, - } + set needsUpdate( value ) { - } + if ( value === true ) this.version ++; - // use temp geometry to compute face and vertex normals for each morph + }, - var tmpGeo = new Geometry(); - tmpGeo.faces = this.faces; + setDynamic: function ( value ) { - for ( i = 0, il = this.morphTargets.length; i < il; i ++ ) { + this.dynamic = value; - // create on first access + return this; - if ( ! this.morphNormals[ i ] ) { + }, - this.morphNormals[ i ] = {}; - this.morphNormals[ i ].faceNormals = []; - this.morphNormals[ i ].vertexNormals = []; + copy: function ( source ) { - var dstNormalsFace = this.morphNormals[ i ].faceNormals; - var dstNormalsVertex = this.morphNormals[ i ].vertexNormals; + this.array = new source.array.constructor( source.array ); + this.itemSize = source.itemSize; + this.count = source.count; + this.normalized = source.normalized; - var faceNormal, vertexNormals; + this.dynamic = source.dynamic; - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + return this; - faceNormal = new Vector3(); - vertexNormals = { a: new Vector3(), b: new Vector3(), c: new Vector3() }; + }, - dstNormalsFace.push( faceNormal ); - dstNormalsVertex.push( vertexNormals ); + copyAt: function ( index1, attribute, index2 ) { - } + index1 *= this.itemSize; + index2 *= attribute.itemSize; - } + for ( var i = 0, l = this.itemSize; i < l; i ++ ) { - var morphNormals = this.morphNormals[ i ]; + this.array[ index1 + i ] = attribute.array[ index2 + i ]; - // set vertices to morph target + } - tmpGeo.vertices = this.morphTargets[ i ].vertices; + return this; - // compute morph normals + }, - tmpGeo.computeFaceNormals(); - tmpGeo.computeVertexNormals(); + copyArray: function ( array ) { - // store morph normals + this.array.set( array ); - var faceNormal, vertexNormals; + return this; - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + }, - face = this.faces[ f ]; + copyColorsArray: function ( colors ) { - faceNormal = morphNormals.faceNormals[ f ]; - vertexNormals = morphNormals.vertexNormals[ f ]; + var array = this.array, offset = 0; - faceNormal.copy( face.normal ); + for ( var i = 0, l = colors.length; i < l; i ++ ) { - vertexNormals.a.copy( face.vertexNormals[ 0 ] ); - vertexNormals.b.copy( face.vertexNormals[ 1 ] ); - vertexNormals.c.copy( face.vertexNormals[ 2 ] ); + var color = colors[ i ]; + + if ( color === undefined ) { + + console.warn( 'THREE.BufferAttribute.copyColorsArray(): color is undefined', i ); + color = new Color(); } + array[ offset ++ ] = color.r; + array[ offset ++ ] = color.g; + array[ offset ++ ] = color.b; + } - // restore original normals + return this; - for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + }, - face = this.faces[ f ]; + copyIndicesArray: function ( indices ) { - face.normal = face.__originalFaceNormal; - face.vertexNormals = face.__originalVertexNormals; + var array = this.array, offset = 0; - } + for ( var i = 0, l = indices.length; i < l; i ++ ) { - }, + var index = indices[ i ]; - computeTangents: function () { + array[ offset ++ ] = index.a; + array[ offset ++ ] = index.b; + array[ offset ++ ] = index.c; - console.warn( 'THREE.Geometry: .computeTangents() has been removed.' ); + } + + return this; }, - computeLineDistances: function () { + copyVector2sArray: function ( vectors ) { - var d = 0; - var vertices = this.vertices; + var array = this.array, offset = 0; - for ( var i = 0, il = vertices.length; i < il; i ++ ) { + for ( var i = 0, l = vectors.length; i < l; i ++ ) { - if ( i > 0 ) { + var vector = vectors[ i ]; - d += vertices[ i ].distanceTo( vertices[ i - 1 ] ); + if ( vector === undefined ) { + + console.warn( 'THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i ); + vector = new Vector2(); } - this.lineDistances[ i ] = d; + array[ offset ++ ] = vector.x; + array[ offset ++ ] = vector.y; } - }, + return this; - computeBoundingBox: function () { + }, - if ( this.boundingBox === null ) { + copyVector3sArray: function ( vectors ) { - this.boundingBox = new Box3(); + var array = this.array, offset = 0; - } + for ( var i = 0, l = vectors.length; i < l; i ++ ) { - this.boundingBox.setFromPoints( this.vertices ); + var vector = vectors[ i ]; - }, + if ( vector === undefined ) { - computeBoundingSphere: function () { + console.warn( 'THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i ); + vector = new Vector3(); - if ( this.boundingSphere === null ) { + } - this.boundingSphere = new Sphere(); + array[ offset ++ ] = vector.x; + array[ offset ++ ] = vector.y; + array[ offset ++ ] = vector.z; } - this.boundingSphere.setFromPoints( this.vertices ); + return this; }, - merge: function ( geometry, matrix, materialIndexOffset ) { + copyVector4sArray: function ( vectors ) { - if ( (geometry && geometry.isGeometry) === false ) { + var array = this.array, offset = 0; - console.error( 'THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry ); - return; + for ( var i = 0, l = vectors.length; i < l; i ++ ) { - } + var vector = vectors[ i ]; - var normalMatrix, - vertexOffset = this.vertices.length, - vertices1 = this.vertices, - vertices2 = geometry.vertices, - faces1 = this.faces, - faces2 = geometry.faces, - uvs1 = this.faceVertexUvs[ 0 ], - uvs2 = geometry.faceVertexUvs[ 0 ], - colors1 = this.colors, - colors2 = geometry.colors; + if ( vector === undefined ) { - if ( materialIndexOffset === undefined ) materialIndexOffset = 0; + console.warn( 'THREE.BufferAttribute.copyVector4sArray(): vector is undefined', i ); + vector = new Vector4(); - if ( matrix !== undefined ) { + } - normalMatrix = new Matrix3().getNormalMatrix( matrix ); + array[ offset ++ ] = vector.x; + array[ offset ++ ] = vector.y; + array[ offset ++ ] = vector.z; + array[ offset ++ ] = vector.w; } - // vertices - - for ( var i = 0, il = vertices2.length; i < il; i ++ ) { + return this; - var vertex = vertices2[ i ]; + }, - var vertexCopy = vertex.clone(); + set: function ( value, offset ) { - if ( matrix !== undefined ) vertexCopy.applyMatrix4( matrix ); + if ( offset === undefined ) offset = 0; - vertices1.push( vertexCopy ); + this.array.set( value, offset ); - } + return this; - // colors + }, - for ( var i = 0, il = colors2.length; i < il; i ++ ) { + getX: function ( index ) { - colors1.push( colors2[ i ].clone() ); + return this.array[ index * this.itemSize ]; - } + }, - // faces + setX: function ( index, x ) { - for ( i = 0, il = faces2.length; i < il; i ++ ) { + this.array[ index * this.itemSize ] = x; - var face = faces2[ i ], faceCopy, normal, color, - faceVertexNormals = face.vertexNormals, - faceVertexColors = face.vertexColors; + return this; - faceCopy = new Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset ); - faceCopy.normal.copy( face.normal ); + }, - if ( normalMatrix !== undefined ) { + getY: function ( index ) { - faceCopy.normal.applyMatrix3( normalMatrix ).normalize(); + return this.array[ index * this.itemSize + 1 ]; - } + }, - for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) { + setY: function ( index, y ) { - normal = faceVertexNormals[ j ].clone(); + this.array[ index * this.itemSize + 1 ] = y; - if ( normalMatrix !== undefined ) { + return this; - normal.applyMatrix3( normalMatrix ).normalize(); + }, - } + getZ: function ( index ) { - faceCopy.vertexNormals.push( normal ); + return this.array[ index * this.itemSize + 2 ]; - } + }, - faceCopy.color.copy( face.color ); + setZ: function ( index, z ) { - for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) { + this.array[ index * this.itemSize + 2 ] = z; - color = faceVertexColors[ j ]; - faceCopy.vertexColors.push( color.clone() ); + return this; - } + }, - faceCopy.materialIndex = face.materialIndex + materialIndexOffset; + getW: function ( index ) { - faces1.push( faceCopy ); + return this.array[ index * this.itemSize + 3 ]; - } + }, - // uvs + setW: function ( index, w ) { - for ( i = 0, il = uvs2.length; i < il; i ++ ) { + this.array[ index * this.itemSize + 3 ] = w; - var uv = uvs2[ i ], uvCopy = []; + return this; - if ( uv === undefined ) { + }, - continue; + setXY: function ( index, x, y ) { - } + index *= this.itemSize; - for ( var j = 0, jl = uv.length; j < jl; j ++ ) { + this.array[ index + 0 ] = x; + this.array[ index + 1 ] = y; - uvCopy.push( uv[ j ].clone() ); + return this; - } + }, - uvs1.push( uvCopy ); + setXYZ: function ( index, x, y, z ) { - } + index *= this.itemSize; - }, + this.array[ index + 0 ] = x; + this.array[ index + 1 ] = y; + this.array[ index + 2 ] = z; - mergeMesh: function ( mesh ) { + return this; - if ( (mesh && mesh.isMesh) === false ) { + }, - console.error( 'THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.', mesh ); - return; + setXYZW: function ( index, x, y, z, w ) { - } + index *= this.itemSize; - mesh.matrixAutoUpdate && mesh.updateMatrix(); + this.array[ index + 0 ] = x; + this.array[ index + 1 ] = y; + this.array[ index + 2 ] = z; + this.array[ index + 3 ] = w; - this.merge( mesh.geometry, mesh.matrix ); + return this; }, - /* - * Checks for duplicate vertices with hashmap. - * Duplicated vertices are removed - * and faces' vertices are updated. - */ + clone: function () { - mergeVertices: function () { + return new this.constructor().copy( this ); - var verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique) - var unique = [], changes = []; + } - var v, key; - var precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001 - var precision = Math.pow( 10, precisionPoints ); - var i, il, face; - var indices, j, jl; + }; - for ( i = 0, il = this.vertices.length; i < il; i ++ ) { - - v = this.vertices[ i ]; - key = Math.round( v.x * precision ) + '_' + Math.round( v.y * precision ) + '_' + Math.round( v.z * precision ); - - if ( verticesMap[ key ] === undefined ) { + // - verticesMap[ key ] = i; - unique.push( this.vertices[ i ] ); - changes[ i ] = unique.length - 1; + function Int8Attribute( array, itemSize ) { - } else { + return new BufferAttribute( new Int8Array( array ), itemSize ); - //console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]); - changes[ i ] = changes[ verticesMap[ key ] ]; + } - } + function Uint8Attribute( array, itemSize ) { - } + return new BufferAttribute( new Uint8Array( array ), itemSize ); + } - // if faces are completely degenerate after merging vertices, we - // have to remove them from the geometry. - var faceIndicesToRemove = []; + function Uint8ClampedAttribute( array, itemSize ) { - for ( i = 0, il = this.faces.length; i < il; i ++ ) { + return new BufferAttribute( new Uint8ClampedArray( array ), itemSize ); - face = this.faces[ i ]; + } - face.a = changes[ face.a ]; - face.b = changes[ face.b ]; - face.c = changes[ face.c ]; + function Int16Attribute( array, itemSize ) { - indices = [ face.a, face.b, face.c ]; + return new BufferAttribute( new Int16Array( array ), itemSize ); - var dupIndex = - 1; + } - // if any duplicate vertices are found in a Face3 - // we have to remove the face as nothing can be saved - for ( var n = 0; n < 3; n ++ ) { + function Uint16Attribute( array, itemSize ) { - if ( indices[ n ] === indices[ ( n + 1 ) % 3 ] ) { + return new BufferAttribute( new Uint16Array( array ), itemSize ); - dupIndex = n; - faceIndicesToRemove.push( i ); - break; + } - } + function Int32Attribute( array, itemSize ) { - } + return new BufferAttribute( new Int32Array( array ), itemSize ); - } + } - for ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) { + function Uint32Attribute( array, itemSize ) { - var idx = faceIndicesToRemove[ i ]; + return new BufferAttribute( new Uint32Array( array ), itemSize ); - this.faces.splice( idx, 1 ); + } - for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) { + function Float32Attribute( array, itemSize ) { - this.faceVertexUvs[ j ].splice( idx, 1 ); + return new BufferAttribute( new Float32Array( array ), itemSize ); - } + } - } + function Float64Attribute( array, itemSize ) { - // Use unique set of vertices + return new BufferAttribute( new Float64Array( array ), itemSize ); - var diff = this.vertices.length - unique.length; - this.vertices = unique; - return diff; + } - }, + // Deprecated - sortFacesByMaterialIndex: function () { + function DynamicBufferAttribute( array, itemSize ) { - var faces = this.faces; - var length = faces.length; + console.warn( 'THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setDynamic( true ) instead.' ); + return new BufferAttribute( array, itemSize ).setDynamic( true ); - // tag faces + } - for ( var i = 0; i < length; i ++ ) { + /** + * @author mrdoob / http://mrdoob.com/ + * @author kile / http://kile.stravaganza.org/ + * @author alteredq / http://alteredqualia.com/ + * @author mikael emtinger / http://gomo.se/ + * @author zz85 / http://www.lab4games.net/zz85/blog + * @author bhouston / http://clara.io + */ - faces[ i ]._id = i; + function Geometry() { - } + Object.defineProperty( this, 'id', { value: GeometryIdCount() } ); - // sort faces + this.uuid = exports.Math.generateUUID(); - function materialIndexSort( a, b ) { + this.name = ''; + this.type = 'Geometry'; - return a.materialIndex - b.materialIndex; + this.vertices = []; + this.colors = []; + this.faces = []; + this.faceVertexUvs = [ [] ]; - } + this.morphTargets = []; + this.morphNormals = []; - faces.sort( materialIndexSort ); + this.skinWeights = []; + this.skinIndices = []; - // sort uvs + this.lineDistances = []; - var uvs1 = this.faceVertexUvs[ 0 ]; - var uvs2 = this.faceVertexUvs[ 1 ]; + this.boundingBox = null; + this.boundingSphere = null; - var newUvs1, newUvs2; + // update flags - if ( uvs1 && uvs1.length === length ) newUvs1 = []; - if ( uvs2 && uvs2.length === length ) newUvs2 = []; + this.elementsNeedUpdate = false; + this.verticesNeedUpdate = false; + this.uvsNeedUpdate = false; + this.normalsNeedUpdate = false; + this.colorsNeedUpdate = false; + this.lineDistancesNeedUpdate = false; + this.groupsNeedUpdate = false; - for ( var i = 0; i < length; i ++ ) { + } - var id = faces[ i ]._id; + Object.assign( Geometry.prototype, EventDispatcher.prototype, { - if ( newUvs1 ) newUvs1.push( uvs1[ id ] ); - if ( newUvs2 ) newUvs2.push( uvs2[ id ] ); + isGeometry: true, - } + applyMatrix: function ( matrix ) { - if ( newUvs1 ) this.faceVertexUvs[ 0 ] = newUvs1; - if ( newUvs2 ) this.faceVertexUvs[ 1 ] = newUvs2; + var normalMatrix = new Matrix3().getNormalMatrix( matrix ); - }, + for ( var i = 0, il = this.vertices.length; i < il; i ++ ) { - toJSON: function () { + var vertex = this.vertices[ i ]; + vertex.applyMatrix4( matrix ); - var data = { - metadata: { - version: 4.4, - type: 'Geometry', - generator: 'Geometry.toJSON' - } - }; + } - // standard Geometry serialization + for ( var i = 0, il = this.faces.length; i < il; i ++ ) { - data.uuid = this.uuid; - data.type = this.type; - if ( this.name !== '' ) data.name = this.name; + var face = this.faces[ i ]; + face.normal.applyMatrix3( normalMatrix ).normalize(); - if ( this.parameters !== undefined ) { + for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { - var parameters = this.parameters; + face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize(); - for ( var key in parameters ) { + } - if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; + } - } + if ( this.boundingBox !== null ) { - return data; + this.computeBoundingBox(); } - var vertices = []; - - for ( var i = 0; i < this.vertices.length; i ++ ) { + if ( this.boundingSphere !== null ) { - var vertex = this.vertices[ i ]; - vertices.push( vertex.x, vertex.y, vertex.z ); + this.computeBoundingSphere(); } - var faces = []; - var normals = []; - var normalsHash = {}; - var colors = []; - var colorsHash = {}; - var uvs = []; - var uvsHash = {}; + this.verticesNeedUpdate = true; + this.normalsNeedUpdate = true; - for ( var i = 0; i < this.faces.length; i ++ ) { + return this; - var face = this.faces[ i ]; + }, - var hasMaterial = true; - var hasFaceUv = false; // deprecated - var hasFaceVertexUv = this.faceVertexUvs[ 0 ][ i ] !== undefined; - var hasFaceNormal = face.normal.length() > 0; - var hasFaceVertexNormal = face.vertexNormals.length > 0; - var hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1; - var hasFaceVertexColor = face.vertexColors.length > 0; + rotateX: function () { - var faceType = 0; + // rotate geometry around world x-axis - faceType = setBit( faceType, 0, 0 ); // isQuad - faceType = setBit( faceType, 1, hasMaterial ); - faceType = setBit( faceType, 2, hasFaceUv ); - faceType = setBit( faceType, 3, hasFaceVertexUv ); - faceType = setBit( faceType, 4, hasFaceNormal ); - faceType = setBit( faceType, 5, hasFaceVertexNormal ); - faceType = setBit( faceType, 6, hasFaceColor ); - faceType = setBit( faceType, 7, hasFaceVertexColor ); + var m1; - faces.push( faceType ); - faces.push( face.a, face.b, face.c ); - faces.push( face.materialIndex ); + return function rotateX( angle ) { - if ( hasFaceVertexUv ) { + if ( m1 === undefined ) m1 = new Matrix4(); - var faceVertexUvs = this.faceVertexUvs[ 0 ][ i ]; + m1.makeRotationX( angle ); - faces.push( - getUvIndex( faceVertexUvs[ 0 ] ), - getUvIndex( faceVertexUvs[ 1 ] ), - getUvIndex( faceVertexUvs[ 2 ] ) - ); + this.applyMatrix( m1 ); - } + return this; - if ( hasFaceNormal ) { + }; - faces.push( getNormalIndex( face.normal ) ); + }(), - } + rotateY: function () { - if ( hasFaceVertexNormal ) { + // rotate geometry around world y-axis - var vertexNormals = face.vertexNormals; + var m1; - faces.push( - getNormalIndex( vertexNormals[ 0 ] ), - getNormalIndex( vertexNormals[ 1 ] ), - getNormalIndex( vertexNormals[ 2 ] ) - ); + return function rotateY( angle ) { - } + if ( m1 === undefined ) m1 = new Matrix4(); - if ( hasFaceColor ) { + m1.makeRotationY( angle ); - faces.push( getColorIndex( face.color ) ); + this.applyMatrix( m1 ); - } + return this; - if ( hasFaceVertexColor ) { + }; - var vertexColors = face.vertexColors; + }(), - faces.push( - getColorIndex( vertexColors[ 0 ] ), - getColorIndex( vertexColors[ 1 ] ), - getColorIndex( vertexColors[ 2 ] ) - ); - - } + rotateZ: function () { - } + // rotate geometry around world z-axis - function setBit( value, position, enabled ) { + var m1; - return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position ) ); + return function rotateZ( angle ) { - } + if ( m1 === undefined ) m1 = new Matrix4(); - function getNormalIndex( normal ) { + m1.makeRotationZ( angle ); - var hash = normal.x.toString() + normal.y.toString() + normal.z.toString(); + this.applyMatrix( m1 ); - if ( normalsHash[ hash ] !== undefined ) { + return this; - return normalsHash[ hash ]; + }; - } + }(), - normalsHash[ hash ] = normals.length / 3; - normals.push( normal.x, normal.y, normal.z ); + translate: function () { - return normalsHash[ hash ]; + // translate geometry - } + var m1; - function getColorIndex( color ) { + return function translate( x, y, z ) { - var hash = color.r.toString() + color.g.toString() + color.b.toString(); + if ( m1 === undefined ) m1 = new Matrix4(); - if ( colorsHash[ hash ] !== undefined ) { + m1.makeTranslation( x, y, z ); - return colorsHash[ hash ]; + this.applyMatrix( m1 ); - } + return this; - colorsHash[ hash ] = colors.length; - colors.push( color.getHex() ); + }; - return colorsHash[ hash ]; + }(), - } + scale: function () { - function getUvIndex( uv ) { + // scale geometry - var hash = uv.x.toString() + uv.y.toString(); + var m1; - if ( uvsHash[ hash ] !== undefined ) { + return function scale( x, y, z ) { - return uvsHash[ hash ]; + if ( m1 === undefined ) m1 = new Matrix4(); - } + m1.makeScale( x, y, z ); - uvsHash[ hash ] = uvs.length / 2; - uvs.push( uv.x, uv.y ); + this.applyMatrix( m1 ); - return uvsHash[ hash ]; + return this; - } + }; - data.data = {}; + }(), - data.data.vertices = vertices; - data.data.normals = normals; - if ( colors.length > 0 ) data.data.colors = colors; - if ( uvs.length > 0 ) data.data.uvs = [ uvs ]; // temporal backward compatibility - data.data.faces = faces; + lookAt: function () { - return data; + var obj; - }, + return function lookAt( vector ) { - clone: function () { + if ( obj === undefined ) obj = new Object3D(); - /* - // Handle primitives + obj.lookAt( vector ); - var parameters = this.parameters; + obj.updateMatrix(); - if ( parameters !== undefined ) { + this.applyMatrix( obj.matrix ); - var values = []; + }; - for ( var key in parameters ) { + }(), - values.push( parameters[ key ] ); + fromBufferGeometry: function ( geometry ) { - } + var scope = this; - var geometry = Object.create( this.constructor.prototype ); - this.constructor.apply( geometry, values ); - return geometry; + var indices = geometry.index !== null ? geometry.index.array : undefined; + var attributes = geometry.attributes; - } + var positions = attributes.position.array; + var normals = attributes.normal !== undefined ? attributes.normal.array : undefined; + var colors = attributes.color !== undefined ? attributes.color.array : undefined; + var uvs = attributes.uv !== undefined ? attributes.uv.array : undefined; + var uvs2 = attributes.uv2 !== undefined ? attributes.uv2.array : undefined; - return new this.constructor().copy( this ); - */ + if ( uvs2 !== undefined ) this.faceVertexUvs[ 1 ] = []; - return new Geometry().copy( this ); + var tempNormals = []; + var tempUVs = []; + var tempUVs2 = []; - }, + for ( var i = 0, j = 0; i < positions.length; i += 3, j += 2 ) { - copy: function ( source ) { + scope.vertices.push( new Vector3( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ) ); - this.vertices = []; - this.faces = []; - this.faceVertexUvs = [ [] ]; - this.colors = []; + if ( normals !== undefined ) { - var vertices = source.vertices; + tempNormals.push( new Vector3( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ) ); - for ( var i = 0, il = vertices.length; i < il; i ++ ) { + } - this.vertices.push( vertices[ i ].clone() ); + if ( colors !== undefined ) { - } + scope.colors.push( new Color( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ) ); - var colors = source.colors; + } - for ( var i = 0, il = colors.length; i < il; i ++ ) { + if ( uvs !== undefined ) { - this.colors.push( colors[ i ].clone() ); + tempUVs.push( new Vector2( uvs[ j ], uvs[ j + 1 ] ) ); - } + } - var faces = source.faces; + if ( uvs2 !== undefined ) { - for ( var i = 0, il = faces.length; i < il; i ++ ) { + tempUVs2.push( new Vector2( uvs2[ j ], uvs2[ j + 1 ] ) ); - this.faces.push( faces[ i ].clone() ); + } } - for ( var i = 0, il = source.faceVertexUvs.length; i < il; i ++ ) { - - var faceVertexUvs = source.faceVertexUvs[ i ]; - - if ( this.faceVertexUvs[ i ] === undefined ) { - - this.faceVertexUvs[ i ] = []; + function addFace( a, b, c, materialIndex ) { - } + var vertexNormals = normals !== undefined ? [ tempNormals[ a ].clone(), tempNormals[ b ].clone(), tempNormals[ c ].clone() ] : []; + var vertexColors = colors !== undefined ? [ scope.colors[ a ].clone(), scope.colors[ b ].clone(), scope.colors[ c ].clone() ] : []; - for ( var j = 0, jl = faceVertexUvs.length; j < jl; j ++ ) { + var face = new Face3( a, b, c, vertexNormals, vertexColors, materialIndex ); - var uvs = faceVertexUvs[ j ], uvsCopy = []; + scope.faces.push( face ); - for ( var k = 0, kl = uvs.length; k < kl; k ++ ) { + if ( uvs !== undefined ) { - var uv = uvs[ k ]; + scope.faceVertexUvs[ 0 ].push( [ tempUVs[ a ].clone(), tempUVs[ b ].clone(), tempUVs[ c ].clone() ] ); - uvsCopy.push( uv.clone() ); + } - } + if ( uvs2 !== undefined ) { - this.faceVertexUvs[ i ].push( uvsCopy ); + scope.faceVertexUvs[ 1 ].push( [ tempUVs2[ a ].clone(), tempUVs2[ b ].clone(), tempUVs2[ c ].clone() ] ); } } - return this; + if ( indices !== undefined ) { - }, + var groups = geometry.groups; - dispose: function () { + if ( groups.length > 0 ) { - this.dispatchEvent( { type: 'dispose' } ); + for ( var i = 0; i < groups.length; i ++ ) { - } + var group = groups[ i ]; - } ); + var start = group.start; + var count = group.count; - var count$2 = 0; - function GeometryIdCount() { return count$2++; }; + for ( var j = start, jl = start + count; j < jl; j += 3 ) { - /** - * @author mrdoob / http://mrdoob.com/ - */ + addFace( indices[ j ], indices[ j + 1 ], indices[ j + 2 ], group.materialIndex ); - function DirectGeometry() { + } - Object.defineProperty( this, 'id', { value: GeometryIdCount() } ); + } - this.uuid = exports.Math.generateUUID(); + } else { - this.name = ''; - this.type = 'DirectGeometry'; + for ( var i = 0; i < indices.length; i += 3 ) { - this.indices = []; - this.vertices = []; - this.normals = []; - this.colors = []; - this.uvs = []; - this.uvs2 = []; + addFace( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] ); - this.groups = []; + } - this.morphTargets = {}; + } - this.skinWeights = []; - this.skinIndices = []; + } else { - // this.lineDistances = []; + for ( var i = 0; i < positions.length / 3; i += 3 ) { - this.boundingBox = null; - this.boundingSphere = null; + addFace( i, i + 1, i + 2 ); - // update flags + } - this.verticesNeedUpdate = false; - this.normalsNeedUpdate = false; - this.colorsNeedUpdate = false; - this.uvsNeedUpdate = false; - this.groupsNeedUpdate = false; + } - } + this.computeFaceNormals(); - Object.assign( DirectGeometry.prototype, EventDispatcher.prototype, { + if ( geometry.boundingBox !== null ) { - computeBoundingBox: Geometry.prototype.computeBoundingBox, - computeBoundingSphere: Geometry.prototype.computeBoundingSphere, + this.boundingBox = geometry.boundingBox.clone(); - computeFaceNormals: function () { + } - console.warn( 'THREE.DirectGeometry: computeFaceNormals() is not a method of this type of geometry.' ); + if ( geometry.boundingSphere !== null ) { - }, + this.boundingSphere = geometry.boundingSphere.clone(); - computeVertexNormals: function () { + } - console.warn( 'THREE.DirectGeometry: computeVertexNormals() is not a method of this type of geometry.' ); + return this; }, - computeGroups: function ( geometry ) { + center: function () { - var group; - var groups = []; - var materialIndex; + this.computeBoundingBox(); - var faces = geometry.faces; + var offset = this.boundingBox.getCenter().negate(); - for ( var i = 0; i < faces.length; i ++ ) { + this.translate( offset.x, offset.y, offset.z ); - var face = faces[ i ]; + return offset; - // materials + }, - if ( face.materialIndex !== materialIndex ) { - - materialIndex = face.materialIndex; + normalize: function () { - if ( group !== undefined ) { + this.computeBoundingSphere(); - group.count = ( i * 3 ) - group.start; - groups.push( group ); + var center = this.boundingSphere.center; + var radius = this.boundingSphere.radius; - } + var s = radius === 0 ? 1 : 1.0 / radius; - group = { - start: i * 3, - materialIndex: materialIndex - }; + var matrix = new Matrix4(); + matrix.set( + s, 0, 0, - s * center.x, + 0, s, 0, - s * center.y, + 0, 0, s, - s * center.z, + 0, 0, 0, 1 + ); - } + this.applyMatrix( matrix ); - } + return this; - if ( group !== undefined ) { + }, - group.count = ( i * 3 ) - group.start; - groups.push( group ); + computeFaceNormals: function () { - } + var cb = new Vector3(), ab = new Vector3(); - this.groups = groups; + for ( var f = 0, fl = this.faces.length; f < fl; f ++ ) { - }, + var face = this.faces[ f ]; - fromGeometry: function ( geometry ) { + var vA = this.vertices[ face.a ]; + var vB = this.vertices[ face.b ]; + var vC = this.vertices[ face.c ]; - var faces = geometry.faces; - var vertices = geometry.vertices; - var faceVertexUvs = geometry.faceVertexUvs; + cb.subVectors( vC, vB ); + ab.subVectors( vA, vB ); + cb.cross( ab ); - var hasFaceVertexUv = faceVertexUvs[ 0 ] && faceVertexUvs[ 0 ].length > 0; - var hasFaceVertexUv2 = faceVertexUvs[ 1 ] && faceVertexUvs[ 1 ].length > 0; + cb.normalize(); - // morphs + face.normal.copy( cb ); - var morphTargets = geometry.morphTargets; - var morphTargetsLength = morphTargets.length; + } - var morphTargetsPosition; + }, - if ( morphTargetsLength > 0 ) { + computeVertexNormals: function ( areaWeighted ) { - morphTargetsPosition = []; + if ( areaWeighted === undefined ) areaWeighted = true; - for ( var i = 0; i < morphTargetsLength; i ++ ) { + var v, vl, f, fl, face, vertices; - morphTargetsPosition[ i ] = []; + vertices = new Array( this.vertices.length ); - } + for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { - this.morphTargets.position = morphTargetsPosition; + vertices[ v ] = new Vector3(); } - var morphNormals = geometry.morphNormals; - var morphNormalsLength = morphNormals.length; + if ( areaWeighted ) { - var morphTargetsNormal; + // vertex normals weighted by triangle areas + // http://www.iquilezles.org/www/articles/normals/normals.htm - if ( morphNormalsLength > 0 ) { + var vA, vB, vC; + var cb = new Vector3(), ab = new Vector3(); - morphTargetsNormal = []; + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - for ( var i = 0; i < morphNormalsLength; i ++ ) { + face = this.faces[ f ]; - morphTargetsNormal[ i ] = []; + vA = this.vertices[ face.a ]; + vB = this.vertices[ face.b ]; + vC = this.vertices[ face.c ]; + + cb.subVectors( vC, vB ); + ab.subVectors( vA, vB ); + cb.cross( ab ); + + vertices[ face.a ].add( cb ); + vertices[ face.b ].add( cb ); + vertices[ face.c ].add( cb ); } - this.morphTargets.normal = morphTargetsNormal; + } else { - } + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - // skins + face = this.faces[ f ]; - var skinIndices = geometry.skinIndices; - var skinWeights = geometry.skinWeights; + vertices[ face.a ].add( face.normal ); + vertices[ face.b ].add( face.normal ); + vertices[ face.c ].add( face.normal ); - var hasSkinIndices = skinIndices.length === vertices.length; - var hasSkinWeights = skinWeights.length === vertices.length; + } - // + } - for ( var i = 0; i < faces.length; i ++ ) { + for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { - var face = faces[ i ]; + vertices[ v ].normalize(); - this.vertices.push( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ] ); + } + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; var vertexNormals = face.vertexNormals; if ( vertexNormals.length === 3 ) { - this.normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] ); + vertexNormals[ 0 ].copy( vertices[ face.a ] ); + vertexNormals[ 1 ].copy( vertices[ face.b ] ); + vertexNormals[ 2 ].copy( vertices[ face.c ] ); } else { - var normal = face.normal; - - this.normals.push( normal, normal, normal ); + vertexNormals[ 0 ] = vertices[ face.a ].clone(); + vertexNormals[ 1 ] = vertices[ face.b ].clone(); + vertexNormals[ 2 ] = vertices[ face.c ].clone(); } - var vertexColors = face.vertexColors; - - if ( vertexColors.length === 3 ) { + } - this.colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] ); + if ( this.faces.length > 0 ) { - } else { + this.normalsNeedUpdate = true; - var color = face.color; + } - this.colors.push( color, color, color ); + }, - } + computeMorphNormals: function () { - if ( hasFaceVertexUv === true ) { + var i, il, f, fl, face; - var vertexUvs = faceVertexUvs[ 0 ][ i ]; + // save original normals + // - create temp variables on first access + // otherwise just copy (for faster repeated calls) - if ( vertexUvs !== undefined ) { + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - this.uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); + face = this.faces[ f ]; - } else { + if ( ! face.__originalFaceNormal ) { - console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv ', i ); + face.__originalFaceNormal = face.normal.clone(); - this.uvs.push( new Vector2(), new Vector2(), new Vector2() ); + } else { - } + face.__originalFaceNormal.copy( face.normal ); } - if ( hasFaceVertexUv2 === true ) { + if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = []; - var vertexUvs = faceVertexUvs[ 1 ][ i ]; + for ( i = 0, il = face.vertexNormals.length; i < il; i ++ ) { - if ( vertexUvs !== undefined ) { + if ( ! face.__originalVertexNormals[ i ] ) { - this.uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); + face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone(); } else { - console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv2 ', i ); - - this.uvs2.push( new Vector2(), new Vector2(), new Vector2() ); + face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] ); } } - // morphs + } - for ( var j = 0; j < morphTargetsLength; j ++ ) { + // use temp geometry to compute face and vertex normals for each morph - var morphTarget = morphTargets[ j ].vertices; + var tmpGeo = new Geometry(); + tmpGeo.faces = this.faces; - morphTargetsPosition[ j ].push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] ); + for ( i = 0, il = this.morphTargets.length; i < il; i ++ ) { - } + // create on first access - for ( var j = 0; j < morphNormalsLength; j ++ ) { + if ( ! this.morphNormals[ i ] ) { - var morphNormal = morphNormals[ j ].vertexNormals[ i ]; + this.morphNormals[ i ] = {}; + this.morphNormals[ i ].faceNormals = []; + this.morphNormals[ i ].vertexNormals = []; - morphTargetsNormal[ j ].push( morphNormal.a, morphNormal.b, morphNormal.c ); + var dstNormalsFace = this.morphNormals[ i ].faceNormals; + var dstNormalsVertex = this.morphNormals[ i ].vertexNormals; - } + var faceNormal, vertexNormals; - // skins + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - if ( hasSkinIndices ) { + faceNormal = new Vector3(); + vertexNormals = { a: new Vector3(), b: new Vector3(), c: new Vector3() }; - this.skinIndices.push( skinIndices[ face.a ], skinIndices[ face.b ], skinIndices[ face.c ] ); + dstNormalsFace.push( faceNormal ); + dstNormalsVertex.push( vertexNormals ); + + } } - if ( hasSkinWeights ) { + var morphNormals = this.morphNormals[ i ]; - this.skinWeights.push( skinWeights[ face.a ], skinWeights[ face.b ], skinWeights[ face.c ] ); + // set vertices to morph target - } + tmpGeo.vertices = this.morphTargets[ i ].vertices; - } + // compute morph normals - this.computeGroups( geometry ); + tmpGeo.computeFaceNormals(); + tmpGeo.computeVertexNormals(); - this.verticesNeedUpdate = geometry.verticesNeedUpdate; - this.normalsNeedUpdate = geometry.normalsNeedUpdate; - this.colorsNeedUpdate = geometry.colorsNeedUpdate; - this.uvsNeedUpdate = geometry.uvsNeedUpdate; - this.groupsNeedUpdate = geometry.groupsNeedUpdate; + // store morph normals - return this; + var faceNormal, vertexNormals; - }, + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - dispose: function () { + face = this.faces[ f ]; - this.dispatchEvent( { type: 'dispose' } ); + faceNormal = morphNormals.faceNormals[ f ]; + vertexNormals = morphNormals.vertexNormals[ f ]; - } + faceNormal.copy( face.normal ); - } ); + vertexNormals.a.copy( face.vertexNormals[ 0 ] ); + vertexNormals.b.copy( face.vertexNormals[ 1 ] ); + vertexNormals.c.copy( face.vertexNormals[ 2 ] ); - /** - * @author alteredq / http://alteredqualia.com/ - * @author mrdoob / http://mrdoob.com/ - */ + } - function BufferGeometry() { + } - Object.defineProperty( this, 'id', { value: GeometryIdCount() } ); + // restore original normals - this.uuid = exports.Math.generateUUID(); + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { - this.name = ''; - this.type = 'BufferGeometry'; + face = this.faces[ f ]; - this.index = null; - this.attributes = {}; + face.normal = face.__originalFaceNormal; + face.vertexNormals = face.__originalVertexNormals; - this.morphAttributes = {}; - - this.groups = []; - - this.boundingBox = null; - this.boundingSphere = null; - - this.drawRange = { start: 0, count: Infinity }; - - } - - Object.assign( BufferGeometry.prototype, EventDispatcher.prototype, { + } - isBufferGeometry: true, + }, - getIndex: function () { + computeTangents: function () { - return this.index; + console.warn( 'THREE.Geometry: .computeTangents() has been removed.' ); }, - setIndex: function ( index ) { - - this.index = index; + computeLineDistances: function () { - }, + var d = 0; + var vertices = this.vertices; - addAttribute: function ( name, attribute ) { + for ( var i = 0, il = vertices.length; i < il; i ++ ) { - if ( (attribute && attribute.isBufferAttribute) === false && (attribute && attribute.isInterleavedBufferAttribute) === false ) { + if ( i > 0 ) { - console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' ); + d += vertices[ i ].distanceTo( vertices[ i - 1 ] ); - this.addAttribute( name, new BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) ); + } - return; + this.lineDistances[ i ] = d; } - if ( name === 'index' ) { + }, - console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' ); - this.setIndex( attribute ); + computeBoundingBox: function () { - return; + if ( this.boundingBox === null ) { - } + this.boundingBox = new Box3(); - this.attributes[ name ] = attribute; + } - return this; + this.boundingBox.setFromPoints( this.vertices ); }, - getAttribute: function ( name ) { - - return this.attributes[ name ]; + computeBoundingSphere: function () { - }, + if ( this.boundingSphere === null ) { - removeAttribute: function ( name ) { + this.boundingSphere = new Sphere(); - delete this.attributes[ name ]; + } - return this; + this.boundingSphere.setFromPoints( this.vertices ); }, - addGroup: function ( start, count, materialIndex ) { + merge: function ( geometry, matrix, materialIndexOffset ) { - this.groups.push( { + if ( (geometry && geometry.isGeometry) === false ) { - start: start, - count: count, - materialIndex: materialIndex !== undefined ? materialIndex : 0 + console.error( 'THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry ); + return; - } ); + } - }, + var normalMatrix, + vertexOffset = this.vertices.length, + vertices1 = this.vertices, + vertices2 = geometry.vertices, + faces1 = this.faces, + faces2 = geometry.faces, + uvs1 = this.faceVertexUvs[ 0 ], + uvs2 = geometry.faceVertexUvs[ 0 ], + colors1 = this.colors, + colors2 = geometry.colors; - clearGroups: function () { + if ( materialIndexOffset === undefined ) materialIndexOffset = 0; - this.groups = []; + if ( matrix !== undefined ) { - }, + normalMatrix = new Matrix3().getNormalMatrix( matrix ); - setDrawRange: function ( start, count ) { + } - this.drawRange.start = start; - this.drawRange.count = count; + // vertices - }, + for ( var i = 0, il = vertices2.length; i < il; i ++ ) { - applyMatrix: function ( matrix ) { + var vertex = vertices2[ i ]; - var position = this.attributes.position; + var vertexCopy = vertex.clone(); - if ( position !== undefined ) { + if ( matrix !== undefined ) vertexCopy.applyMatrix4( matrix ); - matrix.applyToVector3Array( position.array ); - position.needsUpdate = true; + vertices1.push( vertexCopy ); } - var normal = this.attributes.normal; - - if ( normal !== undefined ) { + // colors - var normalMatrix = new Matrix3().getNormalMatrix( matrix ); + for ( var i = 0, il = colors2.length; i < il; i ++ ) { - normalMatrix.applyToVector3Array( normal.array ); - normal.needsUpdate = true; + colors1.push( colors2[ i ].clone() ); } - if ( this.boundingBox !== null ) { - - this.computeBoundingBox(); + // faces - } + for ( i = 0, il = faces2.length; i < il; i ++ ) { - if ( this.boundingSphere !== null ) { + var face = faces2[ i ], faceCopy, normal, color, + faceVertexNormals = face.vertexNormals, + faceVertexColors = face.vertexColors; - this.computeBoundingSphere(); + faceCopy = new Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset ); + faceCopy.normal.copy( face.normal ); - } + if ( normalMatrix !== undefined ) { - return this; + faceCopy.normal.applyMatrix3( normalMatrix ).normalize(); - }, + } - rotateX: function () { + for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) { - // rotate geometry around world x-axis + normal = faceVertexNormals[ j ].clone(); - var m1; + if ( normalMatrix !== undefined ) { - return function rotateX( angle ) { + normal.applyMatrix3( normalMatrix ).normalize(); - if ( m1 === undefined ) m1 = new Matrix4(); + } - m1.makeRotationX( angle ); + faceCopy.vertexNormals.push( normal ); - this.applyMatrix( m1 ); + } - return this; + faceCopy.color.copy( face.color ); - }; + for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) { - }(), + color = faceVertexColors[ j ]; + faceCopy.vertexColors.push( color.clone() ); - rotateY: function () { + } - // rotate geometry around world y-axis + faceCopy.materialIndex = face.materialIndex + materialIndexOffset; - var m1; + faces1.push( faceCopy ); - return function rotateY( angle ) { + } - if ( m1 === undefined ) m1 = new Matrix4(); + // uvs - m1.makeRotationY( angle ); + for ( i = 0, il = uvs2.length; i < il; i ++ ) { - this.applyMatrix( m1 ); + var uv = uvs2[ i ], uvCopy = []; - return this; + if ( uv === undefined ) { - }; + continue; - }(), + } - rotateZ: function () { + for ( var j = 0, jl = uv.length; j < jl; j ++ ) { - // rotate geometry around world z-axis + uvCopy.push( uv[ j ].clone() ); - var m1; + } - return function rotateZ( angle ) { + uvs1.push( uvCopy ); - if ( m1 === undefined ) m1 = new Matrix4(); + } - m1.makeRotationZ( angle ); + }, - this.applyMatrix( m1 ); + mergeMesh: function ( mesh ) { - return this; + if ( (mesh && mesh.isMesh) === false ) { - }; + console.error( 'THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.', mesh ); + return; - }(), + } - translate: function () { + mesh.matrixAutoUpdate && mesh.updateMatrix(); - // translate geometry + this.merge( mesh.geometry, mesh.matrix ); - var m1; + }, - return function translate( x, y, z ) { + /* + * Checks for duplicate vertices with hashmap. + * Duplicated vertices are removed + * and faces' vertices are updated. + */ - if ( m1 === undefined ) m1 = new Matrix4(); + mergeVertices: function () { - m1.makeTranslation( x, y, z ); + var verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique) + var unique = [], changes = []; - this.applyMatrix( m1 ); + var v, key; + var precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001 + var precision = Math.pow( 10, precisionPoints ); + var i, il, face; + var indices, j, jl; - return this; + for ( i = 0, il = this.vertices.length; i < il; i ++ ) { - }; + v = this.vertices[ i ]; + key = Math.round( v.x * precision ) + '_' + Math.round( v.y * precision ) + '_' + Math.round( v.z * precision ); - }(), + if ( verticesMap[ key ] === undefined ) { - scale: function () { + verticesMap[ key ] = i; + unique.push( this.vertices[ i ] ); + changes[ i ] = unique.length - 1; - // scale geometry + } else { - var m1; + //console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]); + changes[ i ] = changes[ verticesMap[ key ] ]; - return function scale( x, y, z ) { + } - if ( m1 === undefined ) m1 = new Matrix4(); + } - m1.makeScale( x, y, z ); - this.applyMatrix( m1 ); + // if faces are completely degenerate after merging vertices, we + // have to remove them from the geometry. + var faceIndicesToRemove = []; - return this; + for ( i = 0, il = this.faces.length; i < il; i ++ ) { - }; + face = this.faces[ i ]; - }(), + face.a = changes[ face.a ]; + face.b = changes[ face.b ]; + face.c = changes[ face.c ]; - lookAt: function () { + indices = [ face.a, face.b, face.c ]; - var obj; + var dupIndex = - 1; - return function lookAt( vector ) { + // if any duplicate vertices are found in a Face3 + // we have to remove the face as nothing can be saved + for ( var n = 0; n < 3; n ++ ) { - if ( obj === undefined ) obj = new Object3D(); + if ( indices[ n ] === indices[ ( n + 1 ) % 3 ] ) { - obj.lookAt( vector ); + dupIndex = n; + faceIndicesToRemove.push( i ); + break; - obj.updateMatrix(); + } - this.applyMatrix( obj.matrix ); + } - }; + } - }(), + for ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) { - center: function () { + var idx = faceIndicesToRemove[ i ]; - this.computeBoundingBox(); + this.faces.splice( idx, 1 ); - var offset = this.boundingBox.getCenter().negate(); + for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) { - this.translate( offset.x, offset.y, offset.z ); + this.faceVertexUvs[ j ].splice( idx, 1 ); - return offset; + } - }, + } - setFromObject: function ( object ) { + // Use unique set of vertices - // console.log( 'THREE.BufferGeometry.setFromObject(). Converting', object, this ); + var diff = this.vertices.length - unique.length; + this.vertices = unique; + return diff; - var geometry = object.geometry; + }, - if ( (object && object.isPoints) || (object && object.isLine) ) { + sortFacesByMaterialIndex: function () { - var positions = new Float32Attribute( geometry.vertices.length * 3, 3 ); - var colors = new Float32Attribute( geometry.colors.length * 3, 3 ); + var faces = this.faces; + var length = faces.length; - this.addAttribute( 'position', positions.copyVector3sArray( geometry.vertices ) ); - this.addAttribute( 'color', colors.copyColorsArray( geometry.colors ) ); + // tag faces - if ( geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length ) { + for ( var i = 0; i < length; i ++ ) { - var lineDistances = new Float32Attribute( geometry.lineDistances.length, 1 ); + faces[ i ]._id = i; - this.addAttribute( 'lineDistance', lineDistances.copyArray( geometry.lineDistances ) ); + } - } + // sort faces - if ( geometry.boundingSphere !== null ) { + function materialIndexSort( a, b ) { - this.boundingSphere = geometry.boundingSphere.clone(); + return a.materialIndex - b.materialIndex; - } + } - if ( geometry.boundingBox !== null ) { + faces.sort( materialIndexSort ); - this.boundingBox = geometry.boundingBox.clone(); + // sort uvs - } + var uvs1 = this.faceVertexUvs[ 0 ]; + var uvs2 = this.faceVertexUvs[ 1 ]; - } else if ( (object && object.isMesh) ) { + var newUvs1, newUvs2; - if ( (geometry && geometry.isGeometry) ) { + if ( uvs1 && uvs1.length === length ) newUvs1 = []; + if ( uvs2 && uvs2.length === length ) newUvs2 = []; - this.fromGeometry( geometry ); + for ( var i = 0; i < length; i ++ ) { - } + var id = faces[ i ]._id; + + if ( newUvs1 ) newUvs1.push( uvs1[ id ] ); + if ( newUvs2 ) newUvs2.push( uvs2[ id ] ); } - return this; + if ( newUvs1 ) this.faceVertexUvs[ 0 ] = newUvs1; + if ( newUvs2 ) this.faceVertexUvs[ 1 ] = newUvs2; }, - updateFromObject: function ( object ) { - - var geometry = object.geometry; + toJSON: function () { - if ( (object && object.isMesh) ) { + var data = { + metadata: { + version: 4.4, + type: 'Geometry', + generator: 'Geometry.toJSON' + } + }; - var direct = geometry.__directGeometry; + // standard Geometry serialization - if ( geometry.elementsNeedUpdate === true ) { + data.uuid = this.uuid; + data.type = this.type; + if ( this.name !== '' ) data.name = this.name; - direct = undefined; - geometry.elementsNeedUpdate = false; + if ( this.parameters !== undefined ) { - } + var parameters = this.parameters; - if ( direct === undefined ) { + for ( var key in parameters ) { - return this.fromGeometry( geometry ); + if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; } - direct.verticesNeedUpdate = geometry.verticesNeedUpdate; - direct.normalsNeedUpdate = geometry.normalsNeedUpdate; - direct.colorsNeedUpdate = geometry.colorsNeedUpdate; - direct.uvsNeedUpdate = geometry.uvsNeedUpdate; - direct.groupsNeedUpdate = geometry.groupsNeedUpdate; + return data; - geometry.verticesNeedUpdate = false; - geometry.normalsNeedUpdate = false; - geometry.colorsNeedUpdate = false; - geometry.uvsNeedUpdate = false; - geometry.groupsNeedUpdate = false; + } - geometry = direct; + var vertices = []; - } + for ( var i = 0; i < this.vertices.length; i ++ ) { - var attribute; + var vertex = this.vertices[ i ]; + vertices.push( vertex.x, vertex.y, vertex.z ); - if ( geometry.verticesNeedUpdate === true ) { + } - attribute = this.attributes.position; + var faces = []; + var normals = []; + var normalsHash = {}; + var colors = []; + var colorsHash = {}; + var uvs = []; + var uvsHash = {}; - if ( attribute !== undefined ) { + for ( var i = 0; i < this.faces.length; i ++ ) { - attribute.copyVector3sArray( geometry.vertices ); - attribute.needsUpdate = true; + var face = this.faces[ i ]; - } + var hasMaterial = true; + var hasFaceUv = false; // deprecated + var hasFaceVertexUv = this.faceVertexUvs[ 0 ][ i ] !== undefined; + var hasFaceNormal = face.normal.length() > 0; + var hasFaceVertexNormal = face.vertexNormals.length > 0; + var hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1; + var hasFaceVertexColor = face.vertexColors.length > 0; - geometry.verticesNeedUpdate = false; + var faceType = 0; - } + faceType = setBit( faceType, 0, 0 ); // isQuad + faceType = setBit( faceType, 1, hasMaterial ); + faceType = setBit( faceType, 2, hasFaceUv ); + faceType = setBit( faceType, 3, hasFaceVertexUv ); + faceType = setBit( faceType, 4, hasFaceNormal ); + faceType = setBit( faceType, 5, hasFaceVertexNormal ); + faceType = setBit( faceType, 6, hasFaceColor ); + faceType = setBit( faceType, 7, hasFaceVertexColor ); - if ( geometry.normalsNeedUpdate === true ) { + faces.push( faceType ); + faces.push( face.a, face.b, face.c ); + faces.push( face.materialIndex ); - attribute = this.attributes.normal; + if ( hasFaceVertexUv ) { - if ( attribute !== undefined ) { + var faceVertexUvs = this.faceVertexUvs[ 0 ][ i ]; - attribute.copyVector3sArray( geometry.normals ); - attribute.needsUpdate = true; + faces.push( + getUvIndex( faceVertexUvs[ 0 ] ), + getUvIndex( faceVertexUvs[ 1 ] ), + getUvIndex( faceVertexUvs[ 2 ] ) + ); } - geometry.normalsNeedUpdate = false; + if ( hasFaceNormal ) { - } + faces.push( getNormalIndex( face.normal ) ); - if ( geometry.colorsNeedUpdate === true ) { + } - attribute = this.attributes.color; + if ( hasFaceVertexNormal ) { - if ( attribute !== undefined ) { + var vertexNormals = face.vertexNormals; - attribute.copyColorsArray( geometry.colors ); - attribute.needsUpdate = true; + faces.push( + getNormalIndex( vertexNormals[ 0 ] ), + getNormalIndex( vertexNormals[ 1 ] ), + getNormalIndex( vertexNormals[ 2 ] ) + ); } - geometry.colorsNeedUpdate = false; + if ( hasFaceColor ) { - } + faces.push( getColorIndex( face.color ) ); - if ( geometry.uvsNeedUpdate ) { + } - attribute = this.attributes.uv; + if ( hasFaceVertexColor ) { - if ( attribute !== undefined ) { + var vertexColors = face.vertexColors; - attribute.copyVector2sArray( geometry.uvs ); - attribute.needsUpdate = true; + faces.push( + getColorIndex( vertexColors[ 0 ] ), + getColorIndex( vertexColors[ 1 ] ), + getColorIndex( vertexColors[ 2 ] ) + ); } - geometry.uvsNeedUpdate = false; - } - if ( geometry.lineDistancesNeedUpdate ) { + function setBit( value, position, enabled ) { - attribute = this.attributes.lineDistance; + return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position ) ); - if ( attribute !== undefined ) { + } - attribute.copyArray( geometry.lineDistances ); - attribute.needsUpdate = true; + function getNormalIndex( normal ) { - } + var hash = normal.x.toString() + normal.y.toString() + normal.z.toString(); - geometry.lineDistancesNeedUpdate = false; + if ( normalsHash[ hash ] !== undefined ) { - } + return normalsHash[ hash ]; - if ( geometry.groupsNeedUpdate ) { + } - geometry.computeGroups( object.geometry ); - this.groups = geometry.groups; + normalsHash[ hash ] = normals.length / 3; + normals.push( normal.x, normal.y, normal.z ); - geometry.groupsNeedUpdate = false; + return normalsHash[ hash ]; } - return this; + function getColorIndex( color ) { - }, + var hash = color.r.toString() + color.g.toString() + color.b.toString(); - fromGeometry: function ( geometry ) { + if ( colorsHash[ hash ] !== undefined ) { - geometry.__directGeometry = new DirectGeometry().fromGeometry( geometry ); + return colorsHash[ hash ]; - return this.fromDirectGeometry( geometry.__directGeometry ); + } - }, + colorsHash[ hash ] = colors.length; + colors.push( color.getHex() ); - fromDirectGeometry: function ( geometry ) { + return colorsHash[ hash ]; - var positions = new Float32Array( geometry.vertices.length * 3 ); - this.addAttribute( 'position', new BufferAttribute( positions, 3 ).copyVector3sArray( geometry.vertices ) ); + } - if ( geometry.normals.length > 0 ) { + function getUvIndex( uv ) { - var normals = new Float32Array( geometry.normals.length * 3 ); - this.addAttribute( 'normal', new BufferAttribute( normals, 3 ).copyVector3sArray( geometry.normals ) ); - - } - - if ( geometry.colors.length > 0 ) { - - var colors = new Float32Array( geometry.colors.length * 3 ); - this.addAttribute( 'color', new BufferAttribute( colors, 3 ).copyColorsArray( geometry.colors ) ); - - } + var hash = uv.x.toString() + uv.y.toString(); - if ( geometry.uvs.length > 0 ) { + if ( uvsHash[ hash ] !== undefined ) { - var uvs = new Float32Array( geometry.uvs.length * 2 ); - this.addAttribute( 'uv', new BufferAttribute( uvs, 2 ).copyVector2sArray( geometry.uvs ) ); + return uvsHash[ hash ]; - } + } - if ( geometry.uvs2.length > 0 ) { + uvsHash[ hash ] = uvs.length / 2; + uvs.push( uv.x, uv.y ); - var uvs2 = new Float32Array( geometry.uvs2.length * 2 ); - this.addAttribute( 'uv2', new BufferAttribute( uvs2, 2 ).copyVector2sArray( geometry.uvs2 ) ); + return uvsHash[ hash ]; } - if ( geometry.indices.length > 0 ) { - - var TypeArray = geometry.vertices.length > 65535 ? Uint32Array : Uint16Array; - var indices = new TypeArray( geometry.indices.length * 3 ); - this.setIndex( new BufferAttribute( indices, 1 ).copyIndicesArray( geometry.indices ) ); + data.data = {}; - } + data.data.vertices = vertices; + data.data.normals = normals; + if ( colors.length > 0 ) data.data.colors = colors; + if ( uvs.length > 0 ) data.data.uvs = [ uvs ]; // temporal backward compatibility + data.data.faces = faces; - // groups + return data; - this.groups = geometry.groups; + }, - // morphs + clone: function () { - for ( var name in geometry.morphTargets ) { + /* + // Handle primitives - var array = []; - var morphTargets = geometry.morphTargets[ name ]; + var parameters = this.parameters; - for ( var i = 0, l = morphTargets.length; i < l; i ++ ) { + if ( parameters !== undefined ) { - var morphTarget = morphTargets[ i ]; + var values = []; - var attribute = new Float32Attribute( morphTarget.length * 3, 3 ); + for ( var key in parameters ) { - array.push( attribute.copyVector3sArray( morphTarget ) ); + values.push( parameters[ key ] ); } - this.morphAttributes[ name ] = array; + var geometry = Object.create( this.constructor.prototype ); + this.constructor.apply( geometry, values ); + return geometry; } - // skinning + return new this.constructor().copy( this ); + */ - if ( geometry.skinIndices.length > 0 ) { + return new Geometry().copy( this ); - var skinIndices = new Float32Attribute( geometry.skinIndices.length * 4, 4 ); - this.addAttribute( 'skinIndex', skinIndices.copyVector4sArray( geometry.skinIndices ) ); + }, - } + copy: function ( source ) { - if ( geometry.skinWeights.length > 0 ) { + this.vertices = []; + this.faces = []; + this.faceVertexUvs = [ [] ]; + this.colors = []; - var skinWeights = new Float32Attribute( geometry.skinWeights.length * 4, 4 ); - this.addAttribute( 'skinWeight', skinWeights.copyVector4sArray( geometry.skinWeights ) ); + var vertices = source.vertices; + + for ( var i = 0, il = vertices.length; i < il; i ++ ) { + + this.vertices.push( vertices[ i ].clone() ); } - // + var colors = source.colors; - if ( geometry.boundingSphere !== null ) { + for ( var i = 0, il = colors.length; i < il; i ++ ) { - this.boundingSphere = geometry.boundingSphere.clone(); + this.colors.push( colors[ i ].clone() ); } - if ( geometry.boundingBox !== null ) { + var faces = source.faces; - this.boundingBox = geometry.boundingBox.clone(); + for ( var i = 0, il = faces.length; i < il; i ++ ) { - } + this.faces.push( faces[ i ].clone() ); - return this; + } - }, + for ( var i = 0, il = source.faceVertexUvs.length; i < il; i ++ ) { - computeBoundingBox: function () { + var faceVertexUvs = source.faceVertexUvs[ i ]; - if ( this.boundingBox === null ) { + if ( this.faceVertexUvs[ i ] === undefined ) { - this.boundingBox = new Box3(); + this.faceVertexUvs[ i ] = []; - } + } - var positions = this.attributes.position.array; + for ( var j = 0, jl = faceVertexUvs.length; j < jl; j ++ ) { - if ( positions !== undefined ) { + var uvs = faceVertexUvs[ j ], uvsCopy = []; - this.boundingBox.setFromArray( positions ); + for ( var k = 0, kl = uvs.length; k < kl; k ++ ) { - } else { + var uv = uvs[ k ]; - this.boundingBox.makeEmpty(); + uvsCopy.push( uv.clone() ); - } + } - if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { + this.faceVertexUvs[ i ].push( uvsCopy ); - console.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this ); + } } - }, + return this; - computeBoundingSphere: function () { + }, - var box = new Box3(); - var vector = new Vector3(); + dispose: function () { - return function computeBoundingSphere() { + this.dispatchEvent( { type: 'dispose' } ); - if ( this.boundingSphere === null ) { + } - this.boundingSphere = new Sphere(); + } ); - } + var count$3 = 0; + function GeometryIdCount() { return count$3++; }; - var positions = this.attributes.position; + /** + * @author mrdoob / http://mrdoob.com/ + */ - if ( positions ) { + function DirectGeometry() { - var array = positions.array; - var center = this.boundingSphere.center; + Object.defineProperty( this, 'id', { value: GeometryIdCount() } ); - box.setFromArray( array ); - box.getCenter( center ); + this.uuid = exports.Math.generateUUID(); - // hoping to find a boundingSphere with a radius smaller than the - // boundingSphere of the boundingBox: sqrt(3) smaller in the best case + this.name = ''; + this.type = 'DirectGeometry'; - var maxRadiusSq = 0; + this.indices = []; + this.vertices = []; + this.normals = []; + this.colors = []; + this.uvs = []; + this.uvs2 = []; - for ( var i = 0, il = array.length; i < il; i += 3 ) { + this.groups = []; - vector.fromArray( array, i ); - maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) ); + this.morphTargets = {}; - } + this.skinWeights = []; + this.skinIndices = []; - this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); + // this.lineDistances = []; - if ( isNaN( this.boundingSphere.radius ) ) { + this.boundingBox = null; + this.boundingSphere = null; - console.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this ); + // update flags - } + this.verticesNeedUpdate = false; + this.normalsNeedUpdate = false; + this.colorsNeedUpdate = false; + this.uvsNeedUpdate = false; + this.groupsNeedUpdate = false; - } + } - }; + Object.assign( DirectGeometry.prototype, EventDispatcher.prototype, { - }(), + computeBoundingBox: Geometry.prototype.computeBoundingBox, + computeBoundingSphere: Geometry.prototype.computeBoundingSphere, computeFaceNormals: function () { - // backwards compatibility + console.warn( 'THREE.DirectGeometry: computeFaceNormals() is not a method of this type of geometry.' ); }, computeVertexNormals: function () { - var index = this.index; - var attributes = this.attributes; - var groups = this.groups; + console.warn( 'THREE.DirectGeometry: computeVertexNormals() is not a method of this type of geometry.' ); - if ( attributes.position ) { + }, - var positions = attributes.position.array; + computeGroups: function ( geometry ) { - if ( attributes.normal === undefined ) { + var group; + var groups = []; + var materialIndex; - this.addAttribute( 'normal', new BufferAttribute( new Float32Array( positions.length ), 3 ) ); + var faces = geometry.faces; - } else { + for ( var i = 0; i < faces.length; i ++ ) { - // reset existing normals to zero + var face = faces[ i ]; - var array = attributes.normal.array; + // materials - for ( var i = 0, il = array.length; i < il; i ++ ) { + if ( face.materialIndex !== materialIndex ) { - array[ i ] = 0; + materialIndex = face.materialIndex; + + if ( group !== undefined ) { + + group.count = ( i * 3 ) - group.start; + groups.push( group ); } + group = { + start: i * 3, + materialIndex: materialIndex + }; + } - var normals = attributes.normal.array; + } - var vA, vB, vC, + if ( group !== undefined ) { - pA = new Vector3(), - pB = new Vector3(), - pC = new Vector3(), + group.count = ( i * 3 ) - group.start; + groups.push( group ); - cb = new Vector3(), - ab = new Vector3(); + } - // indexed elements + this.groups = groups; - if ( index ) { + }, - var indices = index.array; + fromGeometry: function ( geometry ) { - if ( groups.length === 0 ) { + var faces = geometry.faces; + var vertices = geometry.vertices; + var faceVertexUvs = geometry.faceVertexUvs; - this.addGroup( 0, indices.length ); + var hasFaceVertexUv = faceVertexUvs[ 0 ] && faceVertexUvs[ 0 ].length > 0; + var hasFaceVertexUv2 = faceVertexUvs[ 1 ] && faceVertexUvs[ 1 ].length > 0; - } + // morphs - for ( var j = 0, jl = groups.length; j < jl; ++ j ) { + var morphTargets = geometry.morphTargets; + var morphTargetsLength = morphTargets.length; - var group = groups[ j ]; - - var start = group.start; - var count = group.count; + var morphTargetsPosition; - for ( var i = start, il = start + count; i < il; i += 3 ) { + if ( morphTargetsLength > 0 ) { - vA = indices[ i + 0 ] * 3; - vB = indices[ i + 1 ] * 3; - vC = indices[ i + 2 ] * 3; + morphTargetsPosition = []; - pA.fromArray( positions, vA ); - pB.fromArray( positions, vB ); - pC.fromArray( positions, vC ); + for ( var i = 0; i < morphTargetsLength; i ++ ) { - cb.subVectors( pC, pB ); - ab.subVectors( pA, pB ); - cb.cross( ab ); + morphTargetsPosition[ i ] = []; - normals[ vA ] += cb.x; - normals[ vA + 1 ] += cb.y; - normals[ vA + 2 ] += cb.z; + } - normals[ vB ] += cb.x; - normals[ vB + 1 ] += cb.y; - normals[ vB + 2 ] += cb.z; + this.morphTargets.position = morphTargetsPosition; - normals[ vC ] += cb.x; - normals[ vC + 1 ] += cb.y; - normals[ vC + 2 ] += cb.z; + } - } + var morphNormals = geometry.morphNormals; + var morphNormalsLength = morphNormals.length; - } + var morphTargetsNormal; - } else { + if ( morphNormalsLength > 0 ) { - // non-indexed elements (unconnected triangle soup) + morphTargetsNormal = []; - for ( var i = 0, il = positions.length; i < il; i += 9 ) { + for ( var i = 0; i < morphNormalsLength; i ++ ) { - pA.fromArray( positions, i ); - pB.fromArray( positions, i + 3 ); - pC.fromArray( positions, i + 6 ); + morphTargetsNormal[ i ] = []; - cb.subVectors( pC, pB ); - ab.subVectors( pA, pB ); - cb.cross( ab ); + } - normals[ i ] = cb.x; - normals[ i + 1 ] = cb.y; - normals[ i + 2 ] = cb.z; + this.morphTargets.normal = morphTargetsNormal; - normals[ i + 3 ] = cb.x; - normals[ i + 4 ] = cb.y; - normals[ i + 5 ] = cb.z; + } - normals[ i + 6 ] = cb.x; - normals[ i + 7 ] = cb.y; - normals[ i + 8 ] = cb.z; + // skins - } + var skinIndices = geometry.skinIndices; + var skinWeights = geometry.skinWeights; - } + var hasSkinIndices = skinIndices.length === vertices.length; + var hasSkinWeights = skinWeights.length === vertices.length; - this.normalizeNormals(); + // - attributes.normal.needsUpdate = true; + for ( var i = 0; i < faces.length; i ++ ) { - } + var face = faces[ i ]; - }, + this.vertices.push( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ] ); - merge: function ( geometry, offset ) { + var vertexNormals = face.vertexNormals; - if ( (geometry && geometry.isBufferGeometry) === false ) { + if ( vertexNormals.length === 3 ) { - console.error( 'THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.', geometry ); - return; + this.normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] ); - } + } else { - if ( offset === undefined ) offset = 0; + var normal = face.normal; - var attributes = this.attributes; + this.normals.push( normal, normal, normal ); - for ( var key in attributes ) { + } - if ( geometry.attributes[ key ] === undefined ) continue; + var vertexColors = face.vertexColors; - var attribute1 = attributes[ key ]; - var attributeArray1 = attribute1.array; + if ( vertexColors.length === 3 ) { - var attribute2 = geometry.attributes[ key ]; - var attributeArray2 = attribute2.array; + this.colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] ); - var attributeSize = attribute2.itemSize; + } else { - for ( var i = 0, j = attributeSize * offset; i < attributeArray2.length; i ++, j ++ ) { + var color = face.color; - attributeArray1[ j ] = attributeArray2[ i ]; + this.colors.push( color, color, color ); } - } - - return this; - - }, + if ( hasFaceVertexUv === true ) { - normalizeNormals: function () { + var vertexUvs = faceVertexUvs[ 0 ][ i ]; - var normals = this.attributes.normal.array; + if ( vertexUvs !== undefined ) { - var x, y, z, n; + this.uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); - for ( var i = 0, il = normals.length; i < il; i += 3 ) { + } else { - x = normals[ i ]; - y = normals[ i + 1 ]; - z = normals[ i + 2 ]; + console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv ', i ); - n = 1.0 / Math.sqrt( x * x + y * y + z * z ); + this.uvs.push( new Vector2(), new Vector2(), new Vector2() ); - normals[ i ] *= n; - normals[ i + 1 ] *= n; - normals[ i + 2 ] *= n; + } - } + } - }, + if ( hasFaceVertexUv2 === true ) { - toNonIndexed: function () { + var vertexUvs = faceVertexUvs[ 1 ][ i ]; - if ( this.index === null ) { + if ( vertexUvs !== undefined ) { - console.warn( 'THREE.BufferGeometry.toNonIndexed(): Geometry is already non-indexed.' ); - return this; + this.uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); - } + } else { - var geometry2 = new BufferGeometry(); + console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv2 ', i ); - var indices = this.index.array; - var attributes = this.attributes; + this.uvs2.push( new Vector2(), new Vector2(), new Vector2() ); - for ( var name in attributes ) { + } - var attribute = attributes[ name ]; + } - var array = attribute.array; - var itemSize = attribute.itemSize; + // morphs - var array2 = new array.constructor( indices.length * itemSize ); + for ( var j = 0; j < morphTargetsLength; j ++ ) { - var index = 0, index2 = 0; + var morphTarget = morphTargets[ j ].vertices; - for ( var i = 0, l = indices.length; i < l; i ++ ) { + morphTargetsPosition[ j ].push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] ); - index = indices[ i ] * itemSize; + } - for ( var j = 0; j < itemSize; j ++ ) { + for ( var j = 0; j < morphNormalsLength; j ++ ) { - array2[ index2 ++ ] = array[ index ++ ]; + var morphNormal = morphNormals[ j ].vertexNormals[ i ]; - } + morphTargetsNormal[ j ].push( morphNormal.a, morphNormal.b, morphNormal.c ); } - geometry2.addAttribute( name, new BufferAttribute( array2, itemSize ) ); + // skins - } + if ( hasSkinIndices ) { - return geometry2; + this.skinIndices.push( skinIndices[ face.a ], skinIndices[ face.b ], skinIndices[ face.c ] ); - }, + } - toJSON: function () { + if ( hasSkinWeights ) { + + this.skinWeights.push( skinWeights[ face.a ], skinWeights[ face.b ], skinWeights[ face.c ] ); - var data = { - metadata: { - version: 4.4, - type: 'BufferGeometry', - generator: 'BufferGeometry.toJSON' } - }; - // standard BufferGeometry serialization + } - data.uuid = this.uuid; - data.type = this.type; - if ( this.name !== '' ) data.name = this.name; + this.computeGroups( geometry ); - if ( this.parameters !== undefined ) { + this.verticesNeedUpdate = geometry.verticesNeedUpdate; + this.normalsNeedUpdate = geometry.normalsNeedUpdate; + this.colorsNeedUpdate = geometry.colorsNeedUpdate; + this.uvsNeedUpdate = geometry.uvsNeedUpdate; + this.groupsNeedUpdate = geometry.groupsNeedUpdate; - var parameters = this.parameters; + return this; - for ( var key in parameters ) { + }, - if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; + dispose: function () { - } + this.dispatchEvent( { type: 'dispose' } ); - return data; + } - } + } ); - data.data = { attributes: {} }; + /** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ - var index = this.index; + function BufferGeometry() { - if ( index !== null ) { + Object.defineProperty( this, 'id', { value: GeometryIdCount() } ); - var array = Array.prototype.slice.call( index.array ); + this.uuid = exports.Math.generateUUID(); - data.data.index = { - type: index.array.constructor.name, - array: array - }; + this.name = ''; + this.type = 'BufferGeometry'; - } + this.index = null; + this.attributes = {}; - var attributes = this.attributes; + this.morphAttributes = {}; - for ( var key in attributes ) { + this.groups = []; - var attribute = attributes[ key ]; + this.boundingBox = null; + this.boundingSphere = null; - var array = Array.prototype.slice.call( attribute.array ); + this.drawRange = { start: 0, count: Infinity }; - data.data.attributes[ key ] = { - itemSize: attribute.itemSize, - type: attribute.array.constructor.name, - array: array, - normalized: attribute.normalized - }; + } - } + Object.assign( BufferGeometry.prototype, EventDispatcher.prototype, { - var groups = this.groups; + isBufferGeometry: true, - if ( groups.length > 0 ) { + getIndex: function () { - data.data.groups = JSON.parse( JSON.stringify( groups ) ); + return this.index; - } + }, - var boundingSphere = this.boundingSphere; - - if ( boundingSphere !== null ) { - - data.data.boundingSphere = { - center: boundingSphere.center.toArray(), - radius: boundingSphere.radius - }; - - } + setIndex: function ( index ) { - return data; + this.index = index; }, - clone: function () { + addAttribute: function ( name, attribute ) { - /* - // Handle primitives + if ( (attribute && attribute.isBufferAttribute) === false && (attribute && attribute.isInterleavedBufferAttribute) === false ) { - var parameters = this.parameters; + console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' ); - if ( parameters !== undefined ) { + this.addAttribute( name, new BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) ); - var values = []; + return; - for ( var key in parameters ) { + } - values.push( parameters[ key ] ); + if ( name === 'index' ) { - } + console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' ); + this.setIndex( attribute ); - var geometry = Object.create( this.constructor.prototype ); - this.constructor.apply( geometry, values ); - return geometry; + return; } - return new this.constructor().copy( this ); - */ + this.attributes[ name ] = attribute; - return new BufferGeometry().copy( this ); + return this; }, - copy: function ( source ) { + getAttribute: function ( name ) { - var index = source.index; + return this.attributes[ name ]; - if ( index !== null ) { + }, - this.setIndex( index.clone() ); + removeAttribute: function ( name ) { - } + delete this.attributes[ name ]; - var attributes = source.attributes; + return this; - for ( var name in attributes ) { + }, - var attribute = attributes[ name ]; - this.addAttribute( name, attribute.clone() ); + addGroup: function ( start, count, materialIndex ) { - } + this.groups.push( { - var groups = source.groups; + start: start, + count: count, + materialIndex: materialIndex !== undefined ? materialIndex : 0 - for ( var i = 0, l = groups.length; i < l; i ++ ) { + } ); - var group = groups[ i ]; - this.addGroup( group.start, group.count, group.materialIndex ); + }, - } + clearGroups: function () { - return this; + this.groups = []; }, - dispose: function () { + setDrawRange: function ( start, count ) { - this.dispatchEvent( { type: 'dispose' } ); + this.drawRange.start = start; + this.drawRange.count = count; - } + }, - } ); + applyMatrix: function ( matrix ) { - BufferGeometry.MaxIndex = 65535; + var position = this.attributes.position; - /** - * @author Mugen87 / https://github.com/Mugen87 - */ + if ( position !== undefined ) { - function BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) { + matrix.applyToVector3Array( position.array ); + position.needsUpdate = true; - BufferGeometry.call( this ); + } - this.type = 'BoxBufferGeometry'; + var normal = this.attributes.normal; - this.parameters = { - width: width, - height: height, - depth: depth, - widthSegments: widthSegments, - heightSegments: heightSegments, - depthSegments: depthSegments - }; + if ( normal !== undefined ) { - var scope = this; + var normalMatrix = new Matrix3().getNormalMatrix( matrix ); - // segments - widthSegments = Math.floor( widthSegments ) || 1; - heightSegments = Math.floor( heightSegments ) || 1; - depthSegments = Math.floor( depthSegments ) || 1; + normalMatrix.applyToVector3Array( normal.array ); + normal.needsUpdate = true; - // 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 ); + if ( this.boundingBox !== null ) { - // offset variables - var vertexBufferOffset = 0; - var uvBufferOffset = 0; - var indexBufferOffset = 0; - var numberOfVertices = 0; + this.computeBoundingBox(); - // 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 + if ( this.boundingSphere !== null ) { - // build geometry - this.setIndex( new BufferAttribute( indices, 1 ) ); - this.addAttribute( 'position', new BufferAttribute( vertices, 3 ) ); - this.addAttribute( 'normal', new BufferAttribute( normals, 3 ) ); - this.addAttribute( 'uv', new BufferAttribute( uvs, 2 ) ); + this.computeBoundingSphere(); - // helper functions + } - function calculateVertexCount( w, h, d ) { + return this; - 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 + rotateX: function () { - return vertices; + // rotate geometry around world x-axis - } + var m1; - function calculateIndexCount( w, h, d ) { + return function rotateX( angle ) { - var index = 0; + if ( m1 === undefined ) m1 = new Matrix4(); - // calculate the amount of squares for each side - index += w * h * 2; // xy - index += w * d * 2; // xz - index += d * h * 2; // zy + m1.makeRotationX( angle ); - return index * 6; // two triangles per square => six vertices per square + this.applyMatrix( m1 ); - } + return this; - function buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex ) { + }; - var segmentWidth = width / gridX; - var segmentHeight = height / gridY; + }(), - var widthHalf = width / 2; - var heightHalf = height / 2; - var depthHalf = depth / 2; + rotateY: function () { - var gridX1 = gridX + 1; - var gridY1 = gridY + 1; + // rotate geometry around world y-axis - var vertexCounter = 0; - var groupCount = 0; + var m1; - var vector = new Vector3(); + return function rotateY( angle ) { - // generate vertices, normals and uvs + if ( m1 === undefined ) m1 = new Matrix4(); - for ( var iy = 0; iy < gridY1; iy ++ ) { + m1.makeRotationY( angle ); - var y = iy * segmentHeight - heightHalf; + this.applyMatrix( m1 ); - for ( var ix = 0; ix < gridX1; ix ++ ) { + return this; - var x = ix * segmentWidth - widthHalf; + }; - // set values to correct vector component - vector[ u ] = x * udir; - vector[ v ] = y * vdir; - vector[ w ] = depthHalf; + }(), - // now apply vector to vertex buffer - vertices[ vertexBufferOffset ] = vector.x; - vertices[ vertexBufferOffset + 1 ] = vector.y; - vertices[ vertexBufferOffset + 2 ] = vector.z; + rotateZ: function () { - // set values to correct vector component - vector[ u ] = 0; - vector[ v ] = 0; - vector[ w ] = depth > 0 ? 1 : - 1; + // rotate geometry around world z-axis - // now apply vector to normal buffer - normals[ vertexBufferOffset ] = vector.x; - normals[ vertexBufferOffset + 1 ] = vector.y; - normals[ vertexBufferOffset + 2 ] = vector.z; + var m1; - // uvs - uvs[ uvBufferOffset ] = ix / gridX; - uvs[ uvBufferOffset + 1 ] = 1 - ( iy / gridY ); + return function rotateZ( angle ) { - // update offsets and counters - vertexBufferOffset += 3; - uvBufferOffset += 2; - vertexCounter += 1; + if ( m1 === undefined ) m1 = new Matrix4(); - } + m1.makeRotationZ( angle ); - } + this.applyMatrix( m1 ); - // 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 + return this; - for ( iy = 0; iy < gridY; iy ++ ) { + }; - for ( ix = 0; ix < gridX; ix ++ ) { + }(), - // 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; + translate: function () { - // face one - indices[ indexBufferOffset ] = a; - indices[ indexBufferOffset + 1 ] = b; - indices[ indexBufferOffset + 2 ] = d; + // translate geometry - // face two - indices[ indexBufferOffset + 3 ] = b; - indices[ indexBufferOffset + 4 ] = c; - indices[ indexBufferOffset + 5 ] = d; + var m1; - // update offsets and counters - indexBufferOffset += 6; - groupCount += 6; + return function translate( x, y, z ) { - } + if ( m1 === undefined ) m1 = new Matrix4(); - } + m1.makeTranslation( x, y, z ); - // add a group to the geometry. this will ensure multi material support - scope.addGroup( groupStart, groupCount, materialIndex ); + this.applyMatrix( m1 ); - // calculate new start value for groups - groupStart += groupCount; + return this; - // update total number of vertices - numberOfVertices += vertexCounter; + }; - } + }(), - } + scale: function () { - BoxBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - BoxBufferGeometry.prototype.constructor = BoxBufferGeometry; + // scale geometry - /** - * @author bhouston / http://clara.io - */ + var m1; - function Ray( origin, direction ) { + return function scale( x, y, z ) { - this.origin = ( origin !== undefined ) ? origin : new Vector3(); - this.direction = ( direction !== undefined ) ? direction : new Vector3(); + if ( m1 === undefined ) m1 = new Matrix4(); - } + m1.makeScale( x, y, z ); - Ray.prototype = { + this.applyMatrix( m1 ); - constructor: Ray, + return this; - set: function ( origin, direction ) { + }; - this.origin.copy( origin ); - this.direction.copy( direction ); + }(), - return this; + lookAt: function () { - }, + var obj; - clone: function () { + return function lookAt( vector ) { - return new this.constructor().copy( this ); + if ( obj === undefined ) obj = new Object3D(); - }, + obj.lookAt( vector ); - copy: function ( ray ) { + obj.updateMatrix(); - this.origin.copy( ray.origin ); - this.direction.copy( ray.direction ); + this.applyMatrix( obj.matrix ); - return this; + }; - }, + }(), - at: function ( t, optionalTarget ) { + center: function () { - var result = optionalTarget || new Vector3(); + this.computeBoundingBox(); - return result.copy( this.direction ).multiplyScalar( t ).add( this.origin ); + var offset = this.boundingBox.getCenter().negate(); + + this.translate( offset.x, offset.y, offset.z ); + + return offset; }, - lookAt: function ( v ) { + setFromObject: function ( object ) { - this.direction.copy( v ).sub( this.origin ).normalize(); + // console.log( 'THREE.BufferGeometry.setFromObject(). Converting', object, this ); - return this; + var geometry = object.geometry; - }, + if ( (object && object.isPoints) || (object && object.isLine) ) { - recast: function () { + var positions = new Float32Attribute( geometry.vertices.length * 3, 3 ); + var colors = new Float32Attribute( geometry.colors.length * 3, 3 ); - var v1 = new Vector3(); + this.addAttribute( 'position', positions.copyVector3sArray( geometry.vertices ) ); + this.addAttribute( 'color', colors.copyColorsArray( geometry.colors ) ); - return function recast( t ) { + if ( geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length ) { - this.origin.copy( this.at( t, v1 ) ); + var lineDistances = new Float32Attribute( geometry.lineDistances.length, 1 ); - return this; + this.addAttribute( 'lineDistance', lineDistances.copyArray( geometry.lineDistances ) ); - }; + } - }(), + if ( geometry.boundingSphere !== null ) { - closestPointToPoint: function ( point, optionalTarget ) { + this.boundingSphere = geometry.boundingSphere.clone(); - var result = optionalTarget || new Vector3(); - result.subVectors( point, this.origin ); - var directionDistance = result.dot( this.direction ); + } - if ( directionDistance < 0 ) { + if ( geometry.boundingBox !== null ) { - return result.copy( this.origin ); + this.boundingBox = geometry.boundingBox.clone(); - } + } - return result.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); + } else if ( (object && object.isMesh) ) { - }, + if ( (geometry && geometry.isGeometry) ) { - distanceToPoint: function ( point ) { + this.fromGeometry( geometry ); - return Math.sqrt( this.distanceSqToPoint( point ) ); + } - }, + } - distanceSqToPoint: function () { + return this; - var v1 = new Vector3(); + }, - return function distanceSqToPoint( point ) { + updateFromObject: function ( object ) { - var directionDistance = v1.subVectors( point, this.origin ).dot( this.direction ); + var geometry = object.geometry; - // point behind the ray + if ( (object && object.isMesh) ) { - if ( directionDistance < 0 ) { + var direct = geometry.__directGeometry; - return this.origin.distanceToSquared( point ); + if ( geometry.elementsNeedUpdate === true ) { + + direct = undefined; + geometry.elementsNeedUpdate = false; } - v1.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); + if ( direct === undefined ) { - return v1.distanceToSquared( point ); + return this.fromGeometry( geometry ); - }; + } - }(), + direct.verticesNeedUpdate = geometry.verticesNeedUpdate; + direct.normalsNeedUpdate = geometry.normalsNeedUpdate; + direct.colorsNeedUpdate = geometry.colorsNeedUpdate; + direct.uvsNeedUpdate = geometry.uvsNeedUpdate; + direct.groupsNeedUpdate = geometry.groupsNeedUpdate; - distanceSqToSegment: function () { + geometry.verticesNeedUpdate = false; + geometry.normalsNeedUpdate = false; + geometry.colorsNeedUpdate = false; + geometry.uvsNeedUpdate = false; + geometry.groupsNeedUpdate = false; - var segCenter = new Vector3(); - var segDir = new Vector3(); - var diff = new Vector3(); + geometry = direct; - return function distanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { + } - // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteDistRaySegment.h - // It returns the min distance between the ray and the segment - // defined by v0 and v1 - // It can also set two optional targets : - // - The closest point on the ray - // - The closest point on the segment + var attribute; - segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 ); - segDir.copy( v1 ).sub( v0 ).normalize(); - diff.copy( this.origin ).sub( segCenter ); + if ( geometry.verticesNeedUpdate === true ) { - var segExtent = v0.distanceTo( v1 ) * 0.5; - var a01 = - this.direction.dot( segDir ); - var b0 = diff.dot( this.direction ); - var b1 = - diff.dot( segDir ); - var c = diff.lengthSq(); - var det = Math.abs( 1 - a01 * a01 ); - var s0, s1, sqrDist, extDet; + attribute = this.attributes.position; - if ( det > 0 ) { + if ( attribute !== undefined ) { - // The ray and segment are not parallel. + attribute.copyVector3sArray( geometry.vertices ); + attribute.needsUpdate = true; - s0 = a01 * b1 - b0; - s1 = a01 * b0 - b1; - extDet = segExtent * det; + } - if ( s0 >= 0 ) { + geometry.verticesNeedUpdate = false; - if ( s1 >= - extDet ) { + } - if ( s1 <= extDet ) { + if ( geometry.normalsNeedUpdate === true ) { - // region 0 - // Minimum at interior points of ray and segment. + attribute = this.attributes.normal; - var invDet = 1 / det; - s0 *= invDet; - s1 *= invDet; - sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; + if ( attribute !== undefined ) { - } else { + attribute.copyVector3sArray( geometry.normals ); + attribute.needsUpdate = true; - // region 1 + } - s1 = segExtent; - s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + geometry.normalsNeedUpdate = false; - } + } - } else { + if ( geometry.colorsNeedUpdate === true ) { - // region 5 + attribute = this.attributes.color; - s1 = - segExtent; - s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + if ( attribute !== undefined ) { - } + attribute.copyColorsArray( geometry.colors ); + attribute.needsUpdate = true; - } else { + } - if ( s1 <= - extDet ) { + geometry.colorsNeedUpdate = false; - // region 4 + } - s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); - s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + if ( geometry.uvsNeedUpdate ) { - } else if ( s1 <= extDet ) { + attribute = this.attributes.uv; - // region 3 + if ( attribute !== undefined ) { - s0 = 0; - s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); - sqrDist = s1 * ( s1 + 2 * b1 ) + c; + attribute.copyVector2sArray( geometry.uvs ); + attribute.needsUpdate = true; - } else { + } - // region 2 + geometry.uvsNeedUpdate = false; - s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); - s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + } - } + if ( geometry.lineDistancesNeedUpdate ) { - } + attribute = this.attributes.lineDistance; - } else { + if ( attribute !== undefined ) { - // Ray and segment are parallel. - - s1 = ( a01 > 0 ) ? - segExtent : segExtent; - s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); - sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; + attribute.copyArray( geometry.lineDistances ); + attribute.needsUpdate = true; } - if ( optionalPointOnRay ) { - - optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin ); - - } + geometry.lineDistancesNeedUpdate = false; - if ( optionalPointOnSegment ) { + } - optionalPointOnSegment.copy( segDir ).multiplyScalar( s1 ).add( segCenter ); + if ( geometry.groupsNeedUpdate ) { - } + geometry.computeGroups( object.geometry ); + this.groups = geometry.groups; - return sqrDist; + geometry.groupsNeedUpdate = false; - }; + } - }(), + return this; - intersectSphere: function () { + }, - var v1 = new Vector3(); + fromGeometry: function ( geometry ) { - return function intersectSphere( sphere, optionalTarget ) { + geometry.__directGeometry = new DirectGeometry().fromGeometry( geometry ); - v1.subVectors( sphere.center, this.origin ); - var tca = v1.dot( this.direction ); - var d2 = v1.dot( v1 ) - tca * tca; - var radius2 = sphere.radius * sphere.radius; + return this.fromDirectGeometry( geometry.__directGeometry ); - if ( d2 > radius2 ) return null; + }, - var thc = Math.sqrt( radius2 - d2 ); + fromDirectGeometry: function ( geometry ) { - // t0 = first intersect point - entrance on front of sphere - var t0 = tca - thc; + var positions = new Float32Array( geometry.vertices.length * 3 ); + this.addAttribute( 'position', new BufferAttribute( positions, 3 ).copyVector3sArray( geometry.vertices ) ); - // t1 = second intersect point - exit point on back of sphere - var t1 = tca + thc; + if ( geometry.normals.length > 0 ) { - // test to see if both t0 and t1 are behind the ray - if so, return null - if ( t0 < 0 && t1 < 0 ) return null; + var normals = new Float32Array( geometry.normals.length * 3 ); + this.addAttribute( 'normal', new BufferAttribute( normals, 3 ).copyVector3sArray( geometry.normals ) ); - // test to see if t0 is behind the ray: - // if it is, the ray is inside the sphere, so return the second exit point scaled by t1, - // in order to always return an intersect point that is in front of the ray. - if ( t0 < 0 ) return this.at( t1, optionalTarget ); + } - // else t0 is in front of the ray, so return the first collision point scaled by t0 - return this.at( t0, optionalTarget ); + if ( geometry.colors.length > 0 ) { - }; + var colors = new Float32Array( geometry.colors.length * 3 ); + this.addAttribute( 'color', new BufferAttribute( colors, 3 ).copyColorsArray( geometry.colors ) ); - }(), + } - intersectsSphere: function ( sphere ) { + if ( geometry.uvs.length > 0 ) { - return this.distanceToPoint( sphere.center ) <= sphere.radius; + var uvs = new Float32Array( geometry.uvs.length * 2 ); + this.addAttribute( 'uv', new BufferAttribute( uvs, 2 ).copyVector2sArray( geometry.uvs ) ); - }, + } - distanceToPlane: function ( plane ) { + if ( geometry.uvs2.length > 0 ) { - var denominator = plane.normal.dot( this.direction ); + var uvs2 = new Float32Array( geometry.uvs2.length * 2 ); + this.addAttribute( 'uv2', new BufferAttribute( uvs2, 2 ).copyVector2sArray( geometry.uvs2 ) ); - if ( denominator === 0 ) { + } - // line is coplanar, return origin - if ( plane.distanceToPoint( this.origin ) === 0 ) { + if ( geometry.indices.length > 0 ) { - return 0; + var TypeArray = geometry.vertices.length > 65535 ? Uint32Array : Uint16Array; + var indices = new TypeArray( geometry.indices.length * 3 ); + this.setIndex( new BufferAttribute( indices, 1 ).copyIndicesArray( geometry.indices ) ); - } + } - // Null is preferable to undefined since undefined means.... it is undefined + // groups - return null; + this.groups = geometry.groups; - } + // morphs - var t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator; + for ( var name in geometry.morphTargets ) { - // Return if the ray never intersects the plane + var array = []; + var morphTargets = geometry.morphTargets[ name ]; - return t >= 0 ? t : null; + for ( var i = 0, l = morphTargets.length; i < l; i ++ ) { - }, + var morphTarget = morphTargets[ i ]; - intersectPlane: function ( plane, optionalTarget ) { + var attribute = new Float32Attribute( morphTarget.length * 3, 3 ); - var t = this.distanceToPlane( plane ); + array.push( attribute.copyVector3sArray( morphTarget ) ); - if ( t === null ) { + } - return null; + this.morphAttributes[ name ] = array; } - return this.at( t, optionalTarget ); + // skinning - }, + if ( geometry.skinIndices.length > 0 ) { + var skinIndices = new Float32Attribute( geometry.skinIndices.length * 4, 4 ); + this.addAttribute( 'skinIndex', skinIndices.copyVector4sArray( geometry.skinIndices ) ); + } - intersectsPlane: function ( plane ) { + if ( geometry.skinWeights.length > 0 ) { - // check if the ray lies on the plane first + var skinWeights = new Float32Attribute( geometry.skinWeights.length * 4, 4 ); + this.addAttribute( 'skinWeight', skinWeights.copyVector4sArray( geometry.skinWeights ) ); - var distToPoint = plane.distanceToPoint( this.origin ); + } - if ( distToPoint === 0 ) { + // - return true; + if ( geometry.boundingSphere !== null ) { - } + this.boundingSphere = geometry.boundingSphere.clone(); - var denominator = plane.normal.dot( this.direction ); + } - if ( denominator * distToPoint < 0 ) { + if ( geometry.boundingBox !== null ) { - return true; + this.boundingBox = geometry.boundingBox.clone(); } - // ray origin is behind the plane (and is pointing behind it) - - return false; + return this; }, - intersectBox: function ( box, optionalTarget ) { + computeBoundingBox: function () { - var tmin, tmax, tymin, tymax, tzmin, tzmax; + if ( this.boundingBox === null ) { - var invdirx = 1 / this.direction.x, - invdiry = 1 / this.direction.y, - invdirz = 1 / this.direction.z; + this.boundingBox = new Box3(); - var origin = this.origin; + } - if ( invdirx >= 0 ) { + var positions = this.attributes.position.array; - tmin = ( box.min.x - origin.x ) * invdirx; - tmax = ( box.max.x - origin.x ) * invdirx; + if ( positions !== undefined ) { + + this.boundingBox.setFromArray( positions ); } else { - tmin = ( box.max.x - origin.x ) * invdirx; - tmax = ( box.min.x - origin.x ) * invdirx; + this.boundingBox.makeEmpty(); } - if ( invdiry >= 0 ) { - - tymin = ( box.min.y - origin.y ) * invdiry; - tymax = ( box.max.y - origin.y ) * invdiry; - - } else { + if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { - tymin = ( box.max.y - origin.y ) * invdiry; - tymax = ( box.min.y - origin.y ) * invdiry; + console.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this ); } - if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null; + }, - // These lines also handle the case where tmin or tmax is NaN - // (result of 0 * Infinity). x !== x returns true if x is NaN + computeBoundingSphere: function () { - if ( tymin > tmin || tmin !== tmin ) tmin = tymin; + var box = new Box3(); + var vector = new Vector3(); - if ( tymax < tmax || tmax !== tmax ) tmax = tymax; + return function computeBoundingSphere() { - if ( invdirz >= 0 ) { + if ( this.boundingSphere === null ) { - tzmin = ( box.min.z - origin.z ) * invdirz; - tzmax = ( box.max.z - origin.z ) * invdirz; + this.boundingSphere = new Sphere(); - } else { + } - tzmin = ( box.max.z - origin.z ) * invdirz; - tzmax = ( box.min.z - origin.z ) * invdirz; + var positions = this.attributes.position; - } + if ( positions ) { - if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null; + var array = positions.array; + var center = this.boundingSphere.center; - if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin; + box.setFromArray( array ); + box.getCenter( center ); - if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax; + // hoping to find a boundingSphere with a radius smaller than the + // boundingSphere of the boundingBox: sqrt(3) smaller in the best case - //return point closest to the ray (positive side) + var maxRadiusSq = 0; - if ( tmax < 0 ) return null; + for ( var i = 0, il = array.length; i < il; i += 3 ) { - return this.at( tmin >= 0 ? tmin : tmax, optionalTarget ); + vector.fromArray( array, i ); + maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) ); - }, + } - intersectsBox: ( function () { + this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); - var v = new Vector3(); + if ( isNaN( this.boundingSphere.radius ) ) { - return function intersectsBox( box ) { + console.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this ); - return this.intersectBox( box, v ) !== null; + } + + } }; - } )(), + }(), - intersectTriangle: function () { + computeFaceNormals: function () { - // Compute the offset origin, edges, and normal. - var diff = new Vector3(); - var edge1 = new Vector3(); - var edge2 = new Vector3(); - var normal = new Vector3(); + // backwards compatibility - return function intersectTriangle( a, b, c, backfaceCulling, optionalTarget ) { + }, - // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h + computeVertexNormals: function () { - edge1.subVectors( b, a ); - edge2.subVectors( c, a ); - normal.crossVectors( edge1, edge2 ); + var index = this.index; + var attributes = this.attributes; + var groups = this.groups; - // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, - // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by - // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) - // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) - // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) - var DdN = this.direction.dot( normal ); - var sign; + if ( attributes.position ) { - if ( DdN > 0 ) { + var positions = attributes.position.array; - if ( backfaceCulling ) return null; - sign = 1; + if ( attributes.normal === undefined ) { - } else if ( DdN < 0 ) { - - sign = - 1; - DdN = - DdN; + this.addAttribute( 'normal', new BufferAttribute( new Float32Array( positions.length ), 3 ) ); } else { - return null; + // reset existing normals to zero - } + var array = attributes.normal.array; - diff.subVectors( this.origin, a ); - var DdQxE2 = sign * this.direction.dot( edge2.crossVectors( diff, edge2 ) ); + for ( var i = 0, il = array.length; i < il; i ++ ) { - // b1 < 0, no intersection - if ( DdQxE2 < 0 ) { + array[ i ] = 0; - return null; + } } - var DdE1xQ = sign * this.direction.dot( edge1.cross( diff ) ); + var normals = attributes.normal.array; - // b2 < 0, no intersection - if ( DdE1xQ < 0 ) { + var vA, vB, vC, - return null; + pA = new Vector3(), + pB = new Vector3(), + pC = new Vector3(), - } + cb = new Vector3(), + ab = new Vector3(); - // b1+b2 > 1, no intersection - if ( DdQxE2 + DdE1xQ > DdN ) { + // indexed elements - return null; + if ( index ) { - } + var indices = index.array; - // Line intersects triangle, check if ray does. - var QdN = - sign * diff.dot( normal ); + if ( groups.length === 0 ) { - // t < 0, no intersection - if ( QdN < 0 ) { + this.addGroup( 0, indices.length ); - return null; + } - } + for ( var j = 0, jl = groups.length; j < jl; ++ j ) { - // Ray intersects triangle. - return this.at( QdN / DdN, optionalTarget ); + var group = groups[ j ]; - }; + var start = group.start; + var count = group.count; - }(), + for ( var i = start, il = start + count; i < il; i += 3 ) { - applyMatrix4: function ( matrix4 ) { + vA = indices[ i + 0 ] * 3; + vB = indices[ i + 1 ] * 3; + vC = indices[ i + 2 ] * 3; - this.direction.add( this.origin ).applyMatrix4( matrix4 ); - this.origin.applyMatrix4( matrix4 ); - this.direction.sub( this.origin ); - this.direction.normalize(); + pA.fromArray( positions, vA ); + pB.fromArray( positions, vB ); + pC.fromArray( positions, vC ); - return this; + cb.subVectors( pC, pB ); + ab.subVectors( pA, pB ); + cb.cross( ab ); - }, + normals[ vA ] += cb.x; + normals[ vA + 1 ] += cb.y; + normals[ vA + 2 ] += cb.z; - equals: function ( ray ) { + normals[ vB ] += cb.x; + normals[ vB + 1 ] += cb.y; + normals[ vB + 2 ] += cb.z; - return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction ); + normals[ vC ] += cb.x; + normals[ vC + 1 ] += cb.y; + normals[ vC + 2 ] += cb.z; - } + } - }; + } - /** - * @author bhouston / http://clara.io - */ + } else { - function Line3( start, end ) { + // non-indexed elements (unconnected triangle soup) - this.start = ( start !== undefined ) ? start : new Vector3(); - this.end = ( end !== undefined ) ? end : new Vector3(); + for ( var i = 0, il = positions.length; i < il; i += 9 ) { - } + pA.fromArray( positions, i ); + pB.fromArray( positions, i + 3 ); + pC.fromArray( positions, i + 6 ); - Line3.prototype = { + cb.subVectors( pC, pB ); + ab.subVectors( pA, pB ); + cb.cross( ab ); - constructor: Line3, + normals[ i ] = cb.x; + normals[ i + 1 ] = cb.y; + normals[ i + 2 ] = cb.z; - set: function ( start, end ) { + normals[ i + 3 ] = cb.x; + normals[ i + 4 ] = cb.y; + normals[ i + 5 ] = cb.z; - this.start.copy( start ); - this.end.copy( end ); + normals[ i + 6 ] = cb.x; + normals[ i + 7 ] = cb.y; + normals[ i + 8 ] = cb.z; - return this; + } - }, + } - clone: function () { + this.normalizeNormals(); - return new this.constructor().copy( this ); + attributes.normal.needsUpdate = true; + + } }, - copy: function ( line ) { + merge: function ( geometry, offset ) { - this.start.copy( line.start ); - this.end.copy( line.end ); + if ( (geometry && geometry.isBufferGeometry) === false ) { - return this; + console.error( 'THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.', geometry ); + return; - }, + } - center: function ( optionalTarget ) { + if ( offset === undefined ) offset = 0; - var result = optionalTarget || new Vector3(); - return result.addVectors( this.start, this.end ).multiplyScalar( 0.5 ); + var attributes = this.attributes; - }, + for ( var key in attributes ) { - delta: function ( optionalTarget ) { + if ( geometry.attributes[ key ] === undefined ) continue; - var result = optionalTarget || new Vector3(); - return result.subVectors( this.end, this.start ); + var attribute1 = attributes[ key ]; + var attributeArray1 = attribute1.array; - }, + var attribute2 = geometry.attributes[ key ]; + var attributeArray2 = attribute2.array; - distanceSq: function () { + var attributeSize = attribute2.itemSize; - return this.start.distanceToSquared( this.end ); + for ( var i = 0, j = attributeSize * offset; i < attributeArray2.length; i ++, j ++ ) { - }, + attributeArray1[ j ] = attributeArray2[ i ]; - distance: function () { + } - return this.start.distanceTo( this.end ); + } + + return this; }, - at: function ( t, optionalTarget ) { + normalizeNormals: function () { - var result = optionalTarget || new Vector3(); + var normals = this.attributes.normal.array; - return this.delta( result ).multiplyScalar( t ).add( this.start ); + var x, y, z, n; - }, + for ( var i = 0, il = normals.length; i < il; i += 3 ) { - closestPointToPointParameter: function () { + x = normals[ i ]; + y = normals[ i + 1 ]; + z = normals[ i + 2 ]; - var startP = new Vector3(); - var startEnd = new Vector3(); + n = 1.0 / Math.sqrt( x * x + y * y + z * z ); - return function closestPointToPointParameter( point, clampToLine ) { + normals[ i ] *= n; + normals[ i + 1 ] *= n; + normals[ i + 2 ] *= n; - startP.subVectors( point, this.start ); - startEnd.subVectors( this.end, this.start ); + } - var startEnd2 = startEnd.dot( startEnd ); - var startEnd_startP = startEnd.dot( startP ); + }, - var t = startEnd_startP / startEnd2; + toNonIndexed: function () { - if ( clampToLine ) { + if ( this.index === null ) { - t = exports.Math.clamp( t, 0, 1 ); + console.warn( 'THREE.BufferGeometry.toNonIndexed(): Geometry is already non-indexed.' ); + return this; - } + } - return t; + var geometry2 = new BufferGeometry(); - }; + var indices = this.index.array; + var attributes = this.attributes; - }(), + for ( var name in attributes ) { - closestPointToPoint: function ( point, clampToLine, optionalTarget ) { + var attribute = attributes[ name ]; - var t = this.closestPointToPointParameter( point, clampToLine ); + var array = attribute.array; + var itemSize = attribute.itemSize; - var result = optionalTarget || new Vector3(); + var array2 = new array.constructor( indices.length * itemSize ); - return this.delta( result ).multiplyScalar( t ).add( this.start ); + var index = 0, index2 = 0; - }, + for ( var i = 0, l = indices.length; i < l; i ++ ) { - applyMatrix4: function ( matrix ) { + index = indices[ i ] * itemSize; - this.start.applyMatrix4( matrix ); - this.end.applyMatrix4( matrix ); + for ( var j = 0; j < itemSize; j ++ ) { - return this; + array2[ index2 ++ ] = array[ index ++ ]; - }, + } - equals: function ( line ) { + } - return line.start.equals( this.start ) && line.end.equals( this.end ); + geometry2.addAttribute( name, new BufferAttribute( array2, itemSize ) ); - } + } - }; + return geometry2; - /** - * @author bhouston / http://clara.io - * @author mrdoob / http://mrdoob.com/ - */ + }, - function Triangle( a, b, c ) { + toJSON: function () { - this.a = ( a !== undefined ) ? a : new Vector3(); - this.b = ( b !== undefined ) ? b : new Vector3(); - this.c = ( c !== undefined ) ? c : new Vector3(); + var data = { + metadata: { + version: 4.4, + type: 'BufferGeometry', + generator: 'BufferGeometry.toJSON' + } + }; - } + // standard BufferGeometry serialization - Triangle.normal = function () { + data.uuid = this.uuid; + data.type = this.type; + if ( this.name !== '' ) data.name = this.name; - var v0 = new Vector3(); + if ( this.parameters !== undefined ) { - return function normal( a, b, c, optionalTarget ) { + var parameters = this.parameters; - var result = optionalTarget || new Vector3(); + for ( var key in parameters ) { - result.subVectors( c, b ); - v0.subVectors( a, b ); - result.cross( v0 ); + if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; - var resultLengthSq = result.lengthSq(); - if ( resultLengthSq > 0 ) { + } - return result.multiplyScalar( 1 / Math.sqrt( resultLengthSq ) ); + return data; } - return result.set( 0, 0, 0 ); - - }; + data.data = { attributes: {} }; - }(); + var index = this.index; - // static/instance method to calculate barycentric coordinates - // based on: http://www.blackpawn.com/texts/pointinpoly/default.html - Triangle.barycoordFromPoint = function () { + if ( index !== null ) { - var v0 = new Vector3(); - var v1 = new Vector3(); - var v2 = new Vector3(); + var array = Array.prototype.slice.call( index.array ); - return function barycoordFromPoint( point, a, b, c, optionalTarget ) { + data.data.index = { + type: index.array.constructor.name, + array: array + }; - v0.subVectors( c, a ); - v1.subVectors( b, a ); - v2.subVectors( point, a ); + } - var dot00 = v0.dot( v0 ); - var dot01 = v0.dot( v1 ); - var dot02 = v0.dot( v2 ); - var dot11 = v1.dot( v1 ); - var dot12 = v1.dot( v2 ); + var attributes = this.attributes; - var denom = ( dot00 * dot11 - dot01 * dot01 ); + for ( var key in attributes ) { - var result = optionalTarget || new Vector3(); + var attribute = attributes[ key ]; - // collinear or singular triangle - if ( denom === 0 ) { + var array = Array.prototype.slice.call( attribute.array ); - // arbitrary location outside of triangle? - // not sure if this is the best idea, maybe should be returning undefined - return result.set( - 2, - 1, - 1 ); + data.data.attributes[ key ] = { + itemSize: attribute.itemSize, + type: attribute.array.constructor.name, + array: array, + normalized: attribute.normalized + }; } - var invDenom = 1 / denom; - var u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; - var v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; - - // barycentric coordinates must always sum to 1 - return result.set( 1 - u - v, v, u ); - - }; + var groups = this.groups; - }(); + if ( groups.length > 0 ) { - Triangle.containsPoint = function () { + data.data.groups = JSON.parse( JSON.stringify( groups ) ); - var v1 = new Vector3(); + } - return function containsPoint( point, a, b, c ) { + var boundingSphere = this.boundingSphere; - var result = Triangle.barycoordFromPoint( point, a, b, c, v1 ); + if ( boundingSphere !== null ) { - return ( result.x >= 0 ) && ( result.y >= 0 ) && ( ( result.x + result.y ) <= 1 ); + data.data.boundingSphere = { + center: boundingSphere.center.toArray(), + radius: boundingSphere.radius + }; - }; + } - }(); + return data; - Triangle.prototype = { + }, - constructor: Triangle, + clone: function () { - set: function ( a, b, c ) { + /* + // Handle primitives - this.a.copy( a ); - this.b.copy( b ); - this.c.copy( c ); + var parameters = this.parameters; - return this; + if ( parameters !== undefined ) { - }, + var values = []; - setFromPointsAndIndices: function ( points, i0, i1, i2 ) { + for ( var key in parameters ) { - this.a.copy( points[ i0 ] ); - this.b.copy( points[ i1 ] ); - this.c.copy( points[ i2 ] ); + values.push( parameters[ key ] ); - return this; + } - }, + var geometry = Object.create( this.constructor.prototype ); + this.constructor.apply( geometry, values ); + return geometry; - clone: function () { + } return new this.constructor().copy( this ); + */ - }, - - copy: function ( triangle ) { - - this.a.copy( triangle.a ); - this.b.copy( triangle.b ); - this.c.copy( triangle.c ); - - return this; + return new BufferGeometry().copy( this ); }, - area: function () { - - var v0 = new Vector3(); - var v1 = new Vector3(); - - return function area() { + copy: function ( source ) { - v0.subVectors( this.c, this.b ); - v1.subVectors( this.a, this.b ); + var index = source.index; - return v0.cross( v1 ).length() * 0.5; + if ( index !== null ) { - }; + this.setIndex( index.clone() ); - }(), + } - midpoint: function ( optionalTarget ) { + var attributes = source.attributes; - var result = optionalTarget || new Vector3(); - return result.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 ); + for ( var name in attributes ) { - }, + var attribute = attributes[ name ]; + this.addAttribute( name, attribute.clone() ); - normal: function ( optionalTarget ) { + } - return Triangle.normal( this.a, this.b, this.c, optionalTarget ); + var groups = source.groups; - }, + for ( var i = 0, l = groups.length; i < l; i ++ ) { - plane: function ( optionalTarget ) { + var group = groups[ i ]; + this.addGroup( group.start, group.count, group.materialIndex ); - var result = optionalTarget || new Plane(); + } - return result.setFromCoplanarPoints( this.a, this.b, this.c ); + return this; }, - barycoordFromPoint: function ( point, optionalTarget ) { + dispose: function () { - return Triangle.barycoordFromPoint( point, this.a, this.b, this.c, optionalTarget ); + this.dispatchEvent( { type: 'dispose' } ); - }, + } - containsPoint: function ( point ) { + } ); - return Triangle.containsPoint( point, this.a, this.b, this.c ); + BufferGeometry.MaxIndex = 65535; - }, + /** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author mikael emtinger / http://gomo.se/ + * @author jonobr1 / http://jonobr1.com/ + */ - closestPointToPoint: function () { + function Mesh( geometry, material ) { - var plane, edgeList, projectedPoint, closestPoint; + Object3D.call( this ); - return function closestPointToPoint( point, optionalTarget ) { + this.type = 'Mesh'; - if ( plane === undefined ) { + this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); + this.material = material !== undefined ? material : new MeshBasicMaterial( { color: Math.random() * 0xffffff } ); - plane = new Plane(); - edgeList = [ new Line3(), new Line3(), new Line3() ]; - projectedPoint = new Vector3(); - closestPoint = new Vector3(); + this.drawMode = TrianglesDrawMode; - } + this.updateMorphTargets(); - var result = optionalTarget || new Vector3(); - var minDistance = Infinity; + } - // project the point onto the plane of the triangle + Mesh.prototype = Object.assign( Object.create( Object3D.prototype ), { - plane.setFromCoplanarPoints( this.a, this.b, this.c ); - plane.projectPoint( point, projectedPoint ); + constructor: Mesh, - // check if the projection lies within the triangle + isMesh: true, - if( this.containsPoint( projectedPoint ) === true ) { + setDrawMode: function ( value ) { - // if so, this is the closest point + this.drawMode = value; - result.copy( projectedPoint ); + }, - } else { + copy: function ( source ) { - // if not, the point falls outside the triangle. the result is the closest point to the triangle's edges or vertices + Object3D.prototype.copy.call( this, source ); - edgeList[ 0 ].set( this.a, this.b ); - edgeList[ 1 ].set( this.b, this.c ); - edgeList[ 2 ].set( this.c, this.a ); + this.drawMode = source.drawMode; - for( var i = 0; i < edgeList.length; i ++ ) { + return this; - edgeList[ i ].closestPointToPoint( projectedPoint, true, closestPoint ); + }, - var distance = projectedPoint.distanceToSquared( closestPoint ); + updateMorphTargets: function () { - if( distance < minDistance ) { + var morphTargets = this.geometry.morphTargets; - minDistance = distance; + if ( morphTargets !== undefined && morphTargets.length > 0 ) { - result.copy( closestPoint ); + this.morphTargetInfluences = []; + this.morphTargetDictionary = {}; - } + for ( var m = 0, ml = morphTargets.length; m < ml; m ++ ) { - } + this.morphTargetInfluences.push( 0 ); + this.morphTargetDictionary[ morphTargets[ m ].name ] = m; } - return result; + } - }; + }, - }(), + raycast: ( function () { - equals: function ( triangle ) { + var inverseMatrix = new Matrix4(); + var ray = new Ray(); + var sphere = new Sphere(); - return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c ); + var vA = new Vector3(); + var vB = new Vector3(); + var vC = new Vector3(); - } - - }; - - /** - * @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: - * } - */ - - function MeshBasicMaterial( parameters ) { - - Material.call( this ); - - this.type = 'MeshBasicMaterial'; - - this.color = new Color( 0xffffff ); // emissive - - this.map = null; - - this.aoMap = null; - this.aoMapIntensity = 1.0; - - this.specularMap = null; - - this.alphaMap = null; - - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; - - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; - - this.skinning = false; - this.morphTargets = false; - - this.lights = false; - - this.setValues( parameters ); - - } - - MeshBasicMaterial.prototype = Object.create( Material.prototype ); - MeshBasicMaterial.prototype.constructor = MeshBasicMaterial; - - MeshBasicMaterial.prototype.isMeshBasicMaterial = true; - - MeshBasicMaterial.prototype.copy = function ( source ) { - - Material.prototype.copy.call( this, source ); - - this.color.copy( source.color ); - - this.map = source.map; - - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; - - this.specularMap = source.specularMap; - - this.alphaMap = source.alphaMap; - - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; - - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; - - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - - return this; - - }; - - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - * @author mikael emtinger / http://gomo.se/ - * @author jonobr1 / http://jonobr1.com/ - */ - - function Mesh( geometry, material ) { - - Object3D.call( this ); - - this.type = 'Mesh'; - - this.geometry = geometry !== undefined ? geometry : new BufferGeometry(); - this.material = material !== undefined ? material : new MeshBasicMaterial( { color: Math.random() * 0xffffff } ); - - this.drawMode = TrianglesDrawMode; - - this.updateMorphTargets(); - - } - - Mesh.prototype = Object.assign( Object.create( Object3D.prototype ), { - - constructor: Mesh, - - isMesh: true, - - setDrawMode: function ( value ) { - - this.drawMode = value; - - }, - - copy: function ( source ) { - - Object3D.prototype.copy.call( this, source ); - - this.drawMode = source.drawMode; - - return this; - - }, - - updateMorphTargets: function () { - - var morphTargets = this.geometry.morphTargets; - - if ( morphTargets !== undefined && morphTargets.length > 0 ) { - - this.morphTargetInfluences = []; - this.morphTargetDictionary = {}; - - for ( var m = 0, ml = morphTargets.length; m < ml; m ++ ) { - - this.morphTargetInfluences.push( 0 ); - this.morphTargetDictionary[ morphTargets[ m ].name ] = m; - - } - - } - - }, - - raycast: ( function () { - - var inverseMatrix = new Matrix4(); - var ray = new Ray(); - var sphere = new Sphere(); - - var vA = new Vector3(); - var vB = new Vector3(); - var vC = new Vector3(); - - var tempA = new Vector3(); - var tempB = new Vector3(); - var tempC = new Vector3(); + var tempA = new Vector3(); + var tempB = new Vector3(); + var tempC = new Vector3(); var uvA = new Vector2(); var uvB = new Vector2(); @@ -15213,56 +15018,251 @@ } ); /** - * @author mrdoob / http://mrdoob.com/ - * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as + * @author Mugen87 / https://github.com/Mugen87 */ - function PlaneBufferGeometry( width, height, widthSegments, heightSegments ) { + function BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) { BufferGeometry.call( this ); - this.type = 'PlaneBufferGeometry'; + this.type = 'BoxBufferGeometry'; this.parameters = { width: width, height: height, + depth: depth, widthSegments: widthSegments, - heightSegments: heightSegments + heightSegments: heightSegments, + depthSegments: depthSegments }; - var width_half = width / 2; - var height_half = height / 2; + var scope = this; - var gridX = Math.floor( widthSegments ) || 1; - var gridY = Math.floor( heightSegments ) || 1; + // segments + widthSegments = Math.floor( widthSegments ) || 1; + heightSegments = Math.floor( heightSegments ) || 1; + depthSegments = Math.floor( depthSegments ) || 1; - var gridX1 = gridX + 1; - var gridY1 = gridY + 1; + // these are used to calculate buffer length + var vertexCount = calculateVertexCount( widthSegments, heightSegments, depthSegments ); + var indexCount = calculateIndexCount( widthSegments, heightSegments, depthSegments ); - var segment_width = width / gridX; - var segment_height = height / gridY; + // 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 ); - var vertices = new Float32Array( gridX1 * gridY1 * 3 ); - var normals = new Float32Array( gridX1 * gridY1 * 3 ); - var uvs = new Float32Array( gridX1 * gridY1 * 2 ); + // offset variables + var vertexBufferOffset = 0; + var uvBufferOffset = 0; + var indexBufferOffset = 0; + var numberOfVertices = 0; - var offset = 0; - var offset2 = 0; + // group variables + var groupStart = 0; - for ( var iy = 0; iy < gridY1; iy ++ ) { + // 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 - var y = iy * segment_height - height_half; + // build geometry + this.setIndex( new BufferAttribute( indices, 1 ) ); + this.addAttribute( 'position', new BufferAttribute( vertices, 3 ) ); + this.addAttribute( 'normal', new BufferAttribute( normals, 3 ) ); + this.addAttribute( 'uv', new BufferAttribute( uvs, 2 ) ); - for ( var ix = 0; ix < gridX1; ix ++ ) { + // helper functions - var x = ix * segment_width - width_half; + function calculateVertexCount( w, h, d ) { - vertices[ offset ] = x; - vertices[ offset + 1 ] = - y; + var vertices = 0; - normals[ offset + 2 ] = 1; + // 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 - uvs[ offset2 ] = ix / gridX; + return vertices; + + } + + function calculateIndexCount( w, h, d ) { + + var index = 0; + + // calculate the amount of squares for each side + index += w * h * 2; // xy + index += w * d * 2; // xz + index += d * h * 2; // zy + + return index * 6; // two triangles per square => six vertices per square + + } + + function buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex ) { + + var segmentWidth = width / gridX; + var segmentHeight = height / gridY; + + var widthHalf = width / 2; + var heightHalf = height / 2; + var depthHalf = depth / 2; + + var gridX1 = gridX + 1; + var gridY1 = gridY + 1; + + var vertexCounter = 0; + var groupCount = 0; + + var vector = new Vector3(); + + // generate vertices, normals and uvs + + for ( var iy = 0; iy < gridY1; iy ++ ) { + + var y = iy * segmentHeight - heightHalf; + + for ( var ix = 0; ix < gridX1; ix ++ ) { + + var x = ix * segmentWidth - widthHalf; + + // set values to correct vector component + vector[ u ] = x * udir; + vector[ v ] = y * vdir; + vector[ w ] = depthHalf; + + // now apply vector to vertex buffer + vertices[ vertexBufferOffset ] = vector.x; + vertices[ vertexBufferOffset + 1 ] = vector.y; + vertices[ vertexBufferOffset + 2 ] = vector.z; + + // set values to correct vector component + vector[ u ] = 0; + vector[ v ] = 0; + vector[ w ] = depth > 0 ? 1 : - 1; + + // now apply vector to normal buffer + normals[ vertexBufferOffset ] = vector.x; + normals[ vertexBufferOffset + 1 ] = vector.y; + normals[ vertexBufferOffset + 2 ] = vector.z; + + // uvs + uvs[ uvBufferOffset ] = ix / gridX; + uvs[ uvBufferOffset + 1 ] = 1 - ( iy / gridY ); + + // update offsets and counters + vertexBufferOffset += 3; + uvBufferOffset += 2; + vertexCounter += 1; + + } + + } + + // 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 ( iy = 0; iy < gridY; iy ++ ) { + + for ( ix = 0; ix < gridX; ix ++ ) { + + // 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; + + // face one + indices[ indexBufferOffset ] = a; + indices[ indexBufferOffset + 1 ] = b; + indices[ indexBufferOffset + 2 ] = d; + + // 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; + + } + + } + + BoxBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); + BoxBufferGeometry.prototype.constructor = BoxBufferGeometry; + + /** + * @author mrdoob / http://mrdoob.com/ + * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as + */ + + function PlaneBufferGeometry( width, height, widthSegments, heightSegments ) { + + BufferGeometry.call( this ); + + this.type = 'PlaneBufferGeometry'; + + this.parameters = { + width: width, + height: height, + widthSegments: widthSegments, + heightSegments: heightSegments + }; + + var width_half = width / 2; + var height_half = height / 2; + + var gridX = Math.floor( widthSegments ) || 1; + var gridY = Math.floor( heightSegments ) || 1; + + var gridX1 = gridX + 1; + var gridY1 = gridY + 1; + + var segment_width = width / gridX; + var segment_height = height / gridY; + + var vertices = new Float32Array( gridX1 * gridY1 * 3 ); + var normals = new Float32Array( gridX1 * gridY1 * 3 ); + var uvs = new Float32Array( gridX1 * gridY1 * 2 ); + + var offset = 0; + var offset2 = 0; + + for ( var iy = 0; iy < gridY1; iy ++ ) { + + var y = iy * segment_height - height_half; + + for ( var ix = 0; ix < gridX1; ix ++ ) { + + var x = ix * segment_width - width_half; + + vertices[ offset ] = x; + vertices[ offset + 1 ] = - y; + + normals[ offset + 2 ] = 1; + + uvs[ offset2 ] = ix / gridX; uvs[ offset2 + 1 ] = 1 - ( iy / gridY ); offset += 3; @@ -23685,8790 +23685,8790 @@ * @author mrdoob / http://mrdoob.com/ */ - function ShadowMaterial() { + function WireframeGeometry( geometry ) { - ShaderMaterial.call( this, { - uniforms: exports.UniformsUtils.merge( [ - UniformsLib[ "lights" ], - { - opacity: { value: 1.0 } - } - ] ), - vertexShader: ShaderChunk[ 'shadow_vert' ], - fragmentShader: ShaderChunk[ 'shadow_frag' ] - } ); + BufferGeometry.call( this ); - this.lights = true; - this.transparent = true; + var edge = [ 0, 0 ], hash = {}; - Object.defineProperties( this, { - opacity: { - enumerable: true, - get: function () { - return this.uniforms.opacity.value; - }, - set: function ( value ) { - this.uniforms.opacity.value = value; - } - } - } ); + function sortFunction( a, b ) { - } + return a - b; - ShadowMaterial.prototype = Object.create( ShaderMaterial.prototype ); - ShadowMaterial.prototype.constructor = ShadowMaterial; + } - ShadowMaterial.prototype.isShadowMaterial = true; + var keys = [ 'a', 'b', 'c' ]; - /** - * @author mrdoob / http://mrdoob.com/ - */ + if ( (geometry && geometry.isGeometry) ) { - function RawShaderMaterial( parameters ) { + var vertices = geometry.vertices; + var faces = geometry.faces; + var numEdges = 0; - ShaderMaterial.call( this, parameters ); + // allocate maximal size + var edges = new Uint32Array( 6 * faces.length ); - this.type = 'RawShaderMaterial'; + for ( var i = 0, l = faces.length; i < l; i ++ ) { - } + var face = faces[ i ]; - RawShaderMaterial.prototype = Object.create( ShaderMaterial.prototype ); - RawShaderMaterial.prototype.constructor = RawShaderMaterial; + for ( var j = 0; j < 3; j ++ ) { - RawShaderMaterial.prototype.isRawShaderMaterial = true; + edge[ 0 ] = face[ keys[ j ] ]; + edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; + edge.sort( sortFunction ); - /** - * @author mrdoob / http://mrdoob.com/ - */ + var key = edge.toString(); - function MultiMaterial( materials ) { + if ( hash[ key ] === undefined ) { - this.uuid = exports.Math.generateUUID(); + edges[ 2 * numEdges ] = edge[ 0 ]; + edges[ 2 * numEdges + 1 ] = edge[ 1 ]; + hash[ key ] = true; + numEdges ++; - this.type = 'MultiMaterial'; + } - this.materials = materials instanceof Array ? materials : []; + } - this.visible = true; + } - } + var coords = new Float32Array( numEdges * 2 * 3 ); - MultiMaterial.prototype = { + for ( var i = 0, l = numEdges; i < l; i ++ ) { - constructor: MultiMaterial, + for ( var j = 0; j < 2; j ++ ) { - isMultiMaterial: true, + var vertex = vertices[ edges [ 2 * i + j ] ]; - toJSON: function ( meta ) { + var index = 6 * i + 3 * j; + coords[ index + 0 ] = vertex.x; + coords[ index + 1 ] = vertex.y; + coords[ index + 2 ] = vertex.z; - var output = { - metadata: { - version: 4.2, - type: 'material', - generator: 'MaterialExporter' - }, - uuid: this.uuid, - type: this.type, - materials: [] - }; + } - var materials = this.materials; + } - for ( var i = 0, l = materials.length; i < l; i ++ ) { + this.addAttribute( 'position', new BufferAttribute( coords, 3 ) ); - var material = materials[ i ].toJSON( meta ); - delete material.metadata; + } else if ( (geometry && geometry.isBufferGeometry) ) { - output.materials.push( material ); + if ( geometry.index !== null ) { - } + // Indexed BufferGeometry - output.visible = this.visible; + var indices = geometry.index.array; + var vertices = geometry.attributes.position; + var groups = geometry.groups; + var numEdges = 0; - return output; + if ( groups.length === 0 ) { - }, + geometry.addGroup( 0, indices.length ); - clone: function () { + } - var material = new this.constructor(); + // allocate maximal size + var edges = new Uint32Array( 2 * indices.length ); - for ( var i = 0; i < this.materials.length; i ++ ) { + for ( var o = 0, ol = groups.length; o < ol; ++ o ) { - material.materials.push( this.materials[ i ].clone() ); + var group = groups[ o ]; - } + var start = group.start; + var count = group.count; - material.visible = this.visible; + for ( var i = start, il = start + count; i < il; i += 3 ) { - return material; + for ( var j = 0; j < 3; j ++ ) { - } + edge[ 0 ] = indices[ i + j ]; + edge[ 1 ] = indices[ i + ( j + 1 ) % 3 ]; + edge.sort( sortFunction ); - }; + var key = edge.toString(); - /** - * @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: - * } - */ + if ( hash[ key ] === undefined ) { - function MeshStandardMaterial( parameters ) { + edges[ 2 * numEdges ] = edge[ 0 ]; + edges[ 2 * numEdges + 1 ] = edge[ 1 ]; + hash[ key ] = true; + numEdges ++; - Material.call( this ); + } - this.defines = { 'STANDARD': '' }; + } - this.type = 'MeshStandardMaterial'; + } - this.color = new Color( 0xffffff ); // diffuse - this.roughness = 0.5; - this.metalness = 0.5; + } - this.map = null; + var coords = new Float32Array( numEdges * 2 * 3 ); - this.lightMap = null; - this.lightMapIntensity = 1.0; + for ( var i = 0, l = numEdges; i < l; i ++ ) { - this.aoMap = null; - this.aoMapIntensity = 1.0; + for ( var j = 0; j < 2; j ++ ) { - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; + var index = 6 * i + 3 * j; + var index2 = edges[ 2 * i + j ]; - this.bumpMap = null; - this.bumpScale = 1; + coords[ index + 0 ] = vertices.getX( index2 ); + coords[ index + 1 ] = vertices.getY( index2 ); + coords[ index + 2 ] = vertices.getZ( index2 ); - this.normalMap = null; - this.normalScale = new Vector2( 1, 1 ); + } - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; + } - this.roughnessMap = null; + this.addAttribute( 'position', new BufferAttribute( coords, 3 ) ); - this.metalnessMap = null; + } else { - this.alphaMap = null; + // non-indexed BufferGeometry - this.envMap = null; - this.envMapIntensity = 1.0; + var vertices = geometry.attributes.position.array; + var numEdges = vertices.length / 3; + var numTris = numEdges / 3; - this.refractionRatio = 0.98; + var coords = new Float32Array( numEdges * 2 * 3 ); - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; + for ( var i = 0, l = numTris; i < l; i ++ ) { - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + for ( var j = 0; j < 3; j ++ ) { - this.setValues( parameters ); + var index = 18 * i + 6 * j; - } + var index1 = 9 * i + 3 * j; + coords[ index + 0 ] = vertices[ index1 ]; + coords[ index + 1 ] = vertices[ index1 + 1 ]; + coords[ index + 2 ] = vertices[ index1 + 2 ]; - MeshStandardMaterial.prototype = Object.create( Material.prototype ); - MeshStandardMaterial.prototype.constructor = MeshStandardMaterial; + 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 ]; - MeshStandardMaterial.prototype.isMeshStandardMaterial = true; + } - MeshStandardMaterial.prototype.copy = function ( source ) { + } - Material.prototype.copy.call( this, source ); + this.addAttribute( 'position', new BufferAttribute( coords, 3 ) ); - this.defines = { 'STANDARD': '' }; + } - this.color.copy( source.color ); - this.roughness = source.roughness; - this.metalness = source.metalness; + } - this.map = source.map; + } - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; + WireframeGeometry.prototype = Object.create( BufferGeometry.prototype ); + WireframeGeometry.prototype.constructor = WireframeGeometry; - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; + /** + * @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 ); + * + */ - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; + function ParametricGeometry( func, slices, stacks ) { - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; + Geometry.call( this ); - this.normalMap = source.normalMap; - this.normalScale.copy( source.normalScale ); + this.type = 'ParametricGeometry'; - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; + this.parameters = { + func: func, + slices: slices, + stacks: stacks + }; - this.roughnessMap = source.roughnessMap; + var verts = this.vertices; + var faces = this.faces; + var uvs = this.faceVertexUvs[ 0 ]; - this.metalnessMap = source.metalnessMap; + var i, j, p; + var u, v; - this.alphaMap = source.alphaMap; + var sliceCount = slices + 1; - this.envMap = source.envMap; - this.envMapIntensity = source.envMapIntensity; + for ( i = 0; i <= stacks; i ++ ) { - this.refractionRatio = source.refractionRatio; + v = i / stacks; - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; + for ( j = 0; j <= slices; j ++ ) { - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; + u = j / slices; - return this; + p = func( u, v ); + verts.push( p ); - }; + } - /** - * @author WestLangley / http://github.com/WestLangley - * - * parameters = { - * reflectivity: - * } - */ + } - function MeshPhysicalMaterial( parameters ) { + var a, b, c, d; + var uva, uvb, uvc, uvd; - MeshStandardMaterial.call( this ); + for ( i = 0; i < stacks; i ++ ) { - this.defines = { 'PHYSICAL': '' }; + for ( j = 0; j < slices; j ++ ) { - this.type = 'MeshPhysicalMaterial'; + a = i * sliceCount + j; + b = i * sliceCount + j + 1; + c = ( i + 1 ) * sliceCount + j + 1; + d = ( i + 1 ) * sliceCount + j; - this.reflectivity = 0.5; // maps to F0 = 0.04 + uva = new Vector2( j / slices, i / stacks ); + uvb = new Vector2( ( j + 1 ) / slices, i / stacks ); + uvc = new Vector2( ( j + 1 ) / slices, ( i + 1 ) / stacks ); + uvd = new Vector2( j / slices, ( i + 1 ) / stacks ); - this.clearCoat = 0.0; - this.clearCoatRoughness = 0.0; + faces.push( new Face3( a, b, d ) ); + uvs.push( [ uva, uvb, uvd ] ); - this.setValues( parameters ); + faces.push( new Face3( b, c, d ) ); + uvs.push( [ uvb.clone(), uvc, uvd.clone() ] ); - } + } - MeshPhysicalMaterial.prototype = Object.create( MeshStandardMaterial.prototype ); - MeshPhysicalMaterial.prototype.constructor = MeshPhysicalMaterial; + } - MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true; + // console.log(this); - MeshPhysicalMaterial.prototype.copy = function ( source ) { + // magic bullet + // var diff = this.mergeVertices(); + // console.log('removed ', diff, ' vertices by merging'); - MeshStandardMaterial.prototype.copy.call( this, source ); + this.computeFaceNormals(); + this.computeVertexNormals(); - this.defines = { 'PHYSICAL': '' }; + } - this.reflectivity = source.reflectivity; + ParametricGeometry.prototype = Object.create( Geometry.prototype ); + ParametricGeometry.prototype.constructor = ParametricGeometry; - this.clearCoat = source.clearCoat; - this.clearCoatRoughness = source.clearCoatRoughness; + /** + * @author clockworkgeek / https://github.com/clockworkgeek + * @author timothypratley / https://github.com/timothypratley + * @author WestLangley / http://github.com/WestLangley + */ - return this; + function PolyhedronGeometry( vertices, indices, radius, detail ) { - }; + Geometry.call( this ); - /** - * @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: - * } - */ + this.type = 'PolyhedronGeometry'; - function MeshPhongMaterial( parameters ) { + this.parameters = { + vertices: vertices, + indices: indices, + radius: radius, + detail: detail + }; - Material.call( this ); + radius = radius || 1; + detail = detail || 0; - this.type = 'MeshPhongMaterial'; + var that = this; - this.color = new Color( 0xffffff ); // diffuse - this.specular = new Color( 0x111111 ); - this.shininess = 30; + for ( var i = 0, l = vertices.length; i < l; i += 3 ) { - this.map = null; + prepare( new Vector3( vertices[ i ], vertices[ i + 1 ], vertices[ i + 2 ] ) ); - this.lightMap = null; - this.lightMapIntensity = 1.0; + } - this.aoMap = null; - this.aoMapIntensity = 1.0; + var p = this.vertices; - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; + var faces = []; - this.bumpMap = null; - this.bumpScale = 1; + for ( var i = 0, j = 0, l = indices.length; i < l; i += 3, j ++ ) { - this.normalMap = null; - this.normalScale = new Vector2( 1, 1 ); + var v1 = p[ indices[ i ] ]; + var v2 = p[ indices[ i + 1 ] ]; + var v3 = p[ indices[ i + 2 ] ]; - this.displacementMap = null; - this.displacementScale = 1; - this.displacementBias = 0; + faces[ j ] = new Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] ); - this.specularMap = null; + } - this.alphaMap = null; + var centroid = new Vector3(); - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; + for ( var i = 0, l = faces.length; i < l; i ++ ) { - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; + subdivide( faces[ i ], detail ); - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + } - this.setValues( parameters ); - } + // Handle case when face straddles the seam - MeshPhongMaterial.prototype = Object.create( Material.prototype ); - MeshPhongMaterial.prototype.constructor = MeshPhongMaterial; + for ( var i = 0, l = this.faceVertexUvs[ 0 ].length; i < l; i ++ ) { - MeshPhongMaterial.prototype.isMeshPhongMaterial = true; + var uvs = this.faceVertexUvs[ 0 ][ i ]; - MeshPhongMaterial.prototype.copy = function ( source ) { + var x0 = uvs[ 0 ].x; + var x1 = uvs[ 1 ].x; + var x2 = uvs[ 2 ].x; - Material.prototype.copy.call( this, source ); + var max = Math.max( x0, x1, x2 ); + var min = Math.min( x0, x1, x2 ); - this.color.copy( source.color ); - this.specular.copy( source.specular ); - this.shininess = source.shininess; + if ( max > 0.9 && min < 0.1 ) { - this.map = source.map; + // 0.9 is somewhat arbitrary - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; + if ( x0 < 0.2 ) uvs[ 0 ].x += 1; + if ( x1 < 0.2 ) uvs[ 1 ].x += 1; + if ( x2 < 0.2 ) uvs[ 2 ].x += 1; - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; + } - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; + } - this.bumpMap = source.bumpMap; - this.bumpScale = source.bumpScale; - this.normalMap = source.normalMap; - this.normalScale.copy( source.normalScale ); + // Apply radius - this.displacementMap = source.displacementMap; - this.displacementScale = source.displacementScale; - this.displacementBias = source.displacementBias; + for ( var i = 0, l = this.vertices.length; i < l; i ++ ) { - this.specularMap = source.specularMap; + this.vertices[ i ].multiplyScalar( radius ); - this.alphaMap = source.alphaMap; + } - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; + // Merge vertices - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; + this.mergeVertices(); - return this; + this.computeFaceNormals(); - }; + this.boundingSphere = new Sphere( new Vector3(), radius ); - /** - * @author mrdoob / http://mrdoob.com/ - * - * parameters = { - * opacity: , - * - * wireframe: , - * wireframeLinewidth: - * } - */ - function MeshNormalMaterial( parameters ) { + // Project vector onto sphere's surface - Material.call( this, parameters ); + function prepare( vector ) { - this.type = 'MeshNormalMaterial'; + var vertex = vector.normalize().clone(); + vertex.index = that.vertices.push( vertex ) - 1; - this.wireframe = false; - this.wireframeLinewidth = 1; + // Texture coords are equivalent to map coords, calculate angle and convert to fraction of a circle. - this.fog = false; - this.lights = false; - this.morphTargets = false; + var u = azimuth( vector ) / 2 / Math.PI + 0.5; + var v = inclination( vector ) / Math.PI + 0.5; + vertex.uv = new Vector2( u, 1 - v ); - this.setValues( parameters ); + return vertex; - } + } - MeshNormalMaterial.prototype = Object.create( Material.prototype ); - MeshNormalMaterial.prototype.constructor = MeshNormalMaterial; - MeshNormalMaterial.prototype.isMeshNormalMaterial = true; + // Approximate a curved face with recursively sub-divided triangles. - MeshNormalMaterial.prototype.copy = function ( source ) { + function make( v1, v2, v3 ) { - Material.prototype.copy.call( this, source ); + var face = new Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] ); + that.faces.push( face ); - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; + centroid.copy( v1 ).add( v2 ).add( v3 ).divideScalar( 3 ); - return this; + var azi = azimuth( centroid ); - }; + that.faceVertexUvs[ 0 ].push( [ + correctUV( v1.uv, v1, azi ), + correctUV( v2.uv, v2, azi ), + correctUV( v3.uv, v3, azi ) + ] ); - /** - * @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: - * } - */ + } - function MeshLambertMaterial( parameters ) { - Material.call( this ); + // Analytically subdivide a face to the required detail level. - this.type = 'MeshLambertMaterial'; + function subdivide( face, detail ) { - this.color = new Color( 0xffffff ); // diffuse + 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 = []; - this.map = null; + // Construct all of the vertices for this subdivision. - this.lightMap = null; - this.lightMapIntensity = 1.0; + for ( var i = 0 ; i <= cols; i ++ ) { - this.aoMap = null; - this.aoMapIntensity = 1.0; + v[ i ] = []; - this.emissive = new Color( 0x000000 ); - this.emissiveIntensity = 1.0; - this.emissiveMap = null; + var aj = prepare( a.clone().lerp( c, i / cols ) ); + var bj = prepare( b.clone().lerp( c, i / cols ) ); + var rows = cols - i; - this.specularMap = null; + for ( var j = 0; j <= rows; j ++ ) { - this.alphaMap = null; + if ( j === 0 && i === cols ) { - this.envMap = null; - this.combine = MultiplyOperation; - this.reflectivity = 1; - this.refractionRatio = 0.98; + v[ i ][ j ] = aj; - this.wireframe = false; - this.wireframeLinewidth = 1; - this.wireframeLinecap = 'round'; - this.wireframeLinejoin = 'round'; + } else { - this.skinning = false; - this.morphTargets = false; - this.morphNormals = false; + v[ i ][ j ] = prepare( aj.clone().lerp( bj, j / rows ) ); - this.setValues( parameters ); + } - } + } - MeshLambertMaterial.prototype = Object.create( Material.prototype ); - MeshLambertMaterial.prototype.constructor = MeshLambertMaterial; + } - MeshLambertMaterial.prototype.isMeshLambertMaterial = true; + // Construct all of the faces. - MeshLambertMaterial.prototype.copy = function ( source ) { + for ( var i = 0; i < cols ; i ++ ) { - Material.prototype.copy.call( this, source ); + for ( var j = 0; j < 2 * ( cols - i ) - 1; j ++ ) { - this.color.copy( source.color ); + var k = Math.floor( j / 2 ); - this.map = source.map; + if ( j % 2 === 0 ) { - this.lightMap = source.lightMap; - this.lightMapIntensity = source.lightMapIntensity; + make( + v[ i ][ k + 1 ], + v[ i + 1 ][ k ], + v[ i ][ k ] + ); - this.aoMap = source.aoMap; - this.aoMapIntensity = source.aoMapIntensity; + } else { - this.emissive.copy( source.emissive ); - this.emissiveMap = source.emissiveMap; - this.emissiveIntensity = source.emissiveIntensity; + make( + v[ i ][ k + 1 ], + v[ i + 1 ][ k + 1 ], + v[ i + 1 ][ k ] + ); - this.specularMap = source.specularMap; + } - this.alphaMap = source.alphaMap; + } - this.envMap = source.envMap; - this.combine = source.combine; - this.reflectivity = source.reflectivity; - this.refractionRatio = source.refractionRatio; + } - this.wireframe = source.wireframe; - this.wireframeLinewidth = source.wireframeLinewidth; - this.wireframeLinecap = source.wireframeLinecap; - this.wireframeLinejoin = source.wireframeLinejoin; + } - this.skinning = source.skinning; - this.morphTargets = source.morphTargets; - this.morphNormals = source.morphNormals; - return this; + // Angle around the Y axis, counter-clockwise when looking from above. - }; + function azimuth( vector ) { - /** - * @author alteredq / http://alteredqualia.com/ - * - * parameters = { - * color: , - * opacity: , - * - * linewidth: , - * - * scale: , - * dashSize: , - * gapSize: - * } - */ + return Math.atan2( vector.z, - vector.x ); - function LineDashedMaterial( parameters ) { + } - Material.call( this ); - this.type = 'LineDashedMaterial'; + // Angle above the XZ plane. - this.color = new Color( 0xffffff ); + function inclination( vector ) { - this.linewidth = 1; + return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) ); - this.scale = 1; - this.dashSize = 3; - this.gapSize = 1; + } - this.lights = false; - this.setValues( parameters ); + // Texture fixing helper. Spheres have some odd behaviours. - } + function correctUV( uv, vector, azimuth ) { - LineDashedMaterial.prototype = Object.create( Material.prototype ); - LineDashedMaterial.prototype.constructor = LineDashedMaterial; + if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) uv = new Vector2( uv.x - 1, uv.y ); + if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) uv = new Vector2( azimuth / 2 / Math.PI + 0.5, uv.y ); + return uv.clone(); - LineDashedMaterial.prototype.isLineDashedMaterial = true; + } - LineDashedMaterial.prototype.copy = function ( source ) { + } - Material.prototype.copy.call( this, source ); + PolyhedronGeometry.prototype = Object.create( Geometry.prototype ); + PolyhedronGeometry.prototype.constructor = PolyhedronGeometry; - this.color.copy( source.color ); + /** + * @author timothypratley / https://github.com/timothypratley + */ - this.linewidth = source.linewidth; + function TetrahedronGeometry( radius, detail ) { - this.scale = source.scale; - this.dashSize = source.dashSize; - this.gapSize = source.gapSize; + var vertices = [ + 1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1 + ]; - return this; + var indices = [ + 2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1 + ]; - }; + PolyhedronGeometry.call( this, vertices, indices, radius, detail ); + this.type = 'TetrahedronGeometry'; + this.parameters = { + radius: radius, + detail: detail + }; - var Materials = Object.freeze({ - ShadowMaterial: ShadowMaterial, - SpriteMaterial: SpriteMaterial, - RawShaderMaterial: RawShaderMaterial, - ShaderMaterial: ShaderMaterial, - PointsMaterial: PointsMaterial, - MultiMaterial: MultiMaterial, - MeshPhysicalMaterial: MeshPhysicalMaterial, - MeshStandardMaterial: MeshStandardMaterial, - MeshPhongMaterial: MeshPhongMaterial, - MeshNormalMaterial: MeshNormalMaterial, - MeshLambertMaterial: MeshLambertMaterial, - MeshDepthMaterial: MeshDepthMaterial, - MeshBasicMaterial: MeshBasicMaterial, - LineDashedMaterial: LineDashedMaterial, - LineBasicMaterial: LineBasicMaterial, - Material: Material - }); + } - /** - * @author mrdoob / http://mrdoob.com/ - */ - - exports.Cache = { + TetrahedronGeometry.prototype = Object.create( PolyhedronGeometry.prototype ); + TetrahedronGeometry.prototype.constructor = TetrahedronGeometry; - enabled: false, + /** + * @author timothypratley / https://github.com/timothypratley + */ - files: {}, + function OctahedronGeometry( radius, detail ) { - add: function ( key, file ) { + var vertices = [ + 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1 + ]; - if ( this.enabled === false ) return; + 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 + ]; - // console.log( 'THREE.Cache', 'Adding key:', key ); + PolyhedronGeometry.call( this, vertices, indices, radius, detail ); - this.files[ key ] = file; + this.type = 'OctahedronGeometry'; - }, + this.parameters = { + radius: radius, + detail: detail + }; - get: function ( key ) { + } - if ( this.enabled === false ) return; + OctahedronGeometry.prototype = Object.create( PolyhedronGeometry.prototype ); + OctahedronGeometry.prototype.constructor = OctahedronGeometry; - // console.log( 'THREE.Cache', 'Checking key:', key ); + /** + * @author timothypratley / https://github.com/timothypratley + */ - return this.files[ key ]; + function IcosahedronGeometry( radius, detail ) { - }, + var t = ( 1 + Math.sqrt( 5 ) ) / 2; - remove: function ( key ) { + 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 + ]; - delete this.files[ key ]; + 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 + ]; - }, + PolyhedronGeometry.call( this, vertices, indices, radius, detail ); - clear: function () { + this.type = 'IcosahedronGeometry'; - this.files = {}; + this.parameters = { + radius: radius, + detail: detail + }; - } + } - }; + IcosahedronGeometry.prototype = Object.create( PolyhedronGeometry.prototype ); + IcosahedronGeometry.prototype.constructor = IcosahedronGeometry; /** - * @author mrdoob / http://mrdoob.com/ + * @author Abe Pazos / https://hamoid.com */ - function LoadingManager( onLoad, onProgress, onError ) { - - var scope = this; - - var isLoading = false, itemsLoaded = 0, itemsTotal = 0; + function DodecahedronGeometry( radius, detail ) { - this.onStart = undefined; - this.onLoad = onLoad; - this.onProgress = onProgress; - this.onError = onError; + var t = ( 1 + Math.sqrt( 5 ) ) / 2; + var r = 1 / t; - this.itemStart = function ( url ) { + var vertices = [ - itemsTotal ++; + // (±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, - if ( isLoading === false ) { + // (0, ±1/φ, ±φ) + 0, - r, - t, 0, - r, t, + 0, r, - t, 0, r, t, - if ( scope.onStart !== undefined ) { + // (±1/φ, ±φ, 0) + - r, - t, 0, - r, t, 0, + r, - t, 0, r, t, 0, - scope.onStart( url, itemsLoaded, itemsTotal ); + // (±φ, 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 + ]; - } + PolyhedronGeometry.call( this, vertices, indices, radius, detail ); - isLoading = true; + this.type = 'DodecahedronGeometry'; + this.parameters = { + radius: radius, + detail: detail }; - this.itemEnd = function ( url ) { - - itemsLoaded ++; - - if ( scope.onProgress !== undefined ) { - - scope.onProgress( url, itemsLoaded, itemsTotal ); - - } - - if ( itemsLoaded === itemsTotal ) { + } - isLoading = false; + DodecahedronGeometry.prototype = Object.create( PolyhedronGeometry.prototype ); + DodecahedronGeometry.prototype.constructor = DodecahedronGeometry; - if ( scope.onLoad !== undefined ) { + /** + * @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 + */ - scope.onLoad(); + function TubeGeometry( path, segments, radius, radialSegments, closed, taper ) { - } + Geometry.call( this ); - } + this.type = 'TubeGeometry'; + this.parameters = { + path: path, + segments: segments, + radius: radius, + radialSegments: radialSegments, + closed: closed, + taper: taper }; - this.itemError = function ( url ) { + segments = segments || 64; + radius = radius || 1; + radialSegments = radialSegments || 8; + closed = closed || false; + taper = taper || TubeGeometry.NoTaper; - if ( scope.onError !== undefined ) { + var grid = []; - scope.onError( url ); + var scope = this, - } + tangent, + normal, + binormal, - }; + numpoints = segments + 1, - } + u, v, r, - exports.DefaultLoadingManager = new LoadingManager(); + cx, cy, + pos, pos2 = new Vector3(), + i, j, + ip, jp, + a, b, c, d, + uva, uvb, uvc, uvd; - /** - * @author mrdoob / http://mrdoob.com/ - */ + var frames = new TubeGeometry.FrenetFrames( path, segments, closed ), + tangents = frames.tangents, + normals = frames.normals, + binormals = frames.binormals; - function XHRLoader( manager ) { + // proxy internals + this.tangents = tangents; + this.normals = normals; + this.binormals = binormals; - this.manager = ( manager !== undefined ) ? manager : exports.DefaultLoadingManager; + function vert( x, y, z ) { - } + return scope.vertices.push( new Vector3( x, y, z ) ) - 1; - Object.assign( XHRLoader.prototype, { + } - load: function ( url, onLoad, onProgress, onError ) { + // construct the grid - if ( this.path !== undefined ) url = this.path + url; + for ( i = 0; i < numpoints; i ++ ) { - var scope = this; + grid[ i ] = []; - var cached = exports.Cache.get( url ); + u = i / ( numpoints - 1 ); - if ( cached !== undefined ) { + pos = path.getPointAt( u ); - scope.manager.itemStart( url ); + tangent = tangents[ i ]; + normal = normals[ i ]; + binormal = binormals[ i ]; - setTimeout( function () { + r = radius * taper( u ); - if ( onLoad ) onLoad( cached ); + for ( j = 0; j < radialSegments; j ++ ) { - scope.manager.itemEnd( url ); + v = j / radialSegments * 2 * Math.PI; - }, 0 ); + cx = - r * Math.cos( v ); // TODO: Hack: Negating it so it faces outside. + cy = r * Math.sin( v ); - return cached; + pos2.copy( pos ); + pos2.x += cx * normal.x + cy * binormal.x; + pos2.y += cx * normal.y + cy * binormal.y; + pos2.z += cx * normal.z + cy * binormal.z; - } + grid[ i ][ j ] = vert( pos2.x, pos2.y, pos2.z ); - var request = new XMLHttpRequest(); - request.open( 'GET', url, true ); + } - request.addEventListener( 'load', function ( event ) { + } - var response = event.target.response; - exports.Cache.add( url, response ); + // construct the mesh - if ( this.status === 200 ) { + for ( i = 0; i < segments; i ++ ) { - if ( onLoad ) onLoad( response ); + for ( j = 0; j < radialSegments; j ++ ) { - scope.manager.itemEnd( url ); + ip = ( closed ) ? ( i + 1 ) % segments : i + 1; + jp = ( j + 1 ) % radialSegments; - } else if ( this.status === 0 ) { - - // Some browsers return HTTP Status 0 when using non-http protocol - // e.g. 'file://' or 'data://'. Handle as success. - - console.warn( 'THREE.XHRLoader: HTTP Status 0 received.' ); - - if ( onLoad ) onLoad( response ); - - scope.manager.itemEnd( url ); - - } else { - - if ( onError ) onError( event ); - - scope.manager.itemError( url ); - - } - - }, false ); - - if ( onProgress !== undefined ) { + a = grid[ i ][ j ]; // *** NOT NECESSARILY PLANAR ! *** + b = grid[ ip ][ j ]; + c = grid[ ip ][ jp ]; + d = grid[ i ][ jp ]; - request.addEventListener( 'progress', function ( event ) { + uva = new Vector2( i / segments, j / radialSegments ); + uvb = new Vector2( ( i + 1 ) / segments, j / radialSegments ); + uvc = new Vector2( ( i + 1 ) / segments, ( j + 1 ) / radialSegments ); + uvd = new Vector2( i / segments, ( j + 1 ) / radialSegments ); - onProgress( event ); + this.faces.push( new Face3( a, b, d ) ); + this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); - }, false ); + this.faces.push( new Face3( b, c, d ) ); + this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); } - request.addEventListener( 'error', function ( event ) { - - if ( onError ) onError( event ); - - scope.manager.itemError( url ); - - }, false ); - - if ( this.responseType !== undefined ) request.responseType = this.responseType; - if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials; + } - if ( request.overrideMimeType ) request.overrideMimeType( 'text/plain' ); + this.computeFaceNormals(); + this.computeVertexNormals(); - request.send( null ); + } - scope.manager.itemStart( url ); + TubeGeometry.prototype = Object.create( Geometry.prototype ); + TubeGeometry.prototype.constructor = TubeGeometry; - return request; + TubeGeometry.NoTaper = function ( u ) { - }, + return 1; - setPath: function ( value ) { + }; - this.path = value; - return this; + TubeGeometry.SinusoidalTaper = function ( u ) { - }, + return Math.sin( Math.PI * u ); - setResponseType: function ( value ) { + }; - this.responseType = value; - return this; + // For computing of Frenet frames, exposing the tangents, normals and binormals the spline + TubeGeometry.FrenetFrames = function ( path, segments, closed ) { - }, + var normal = new Vector3(), - setWithCredentials: function ( value ) { + tangents = [], + normals = [], + binormals = [], - this.withCredentials = value; - return this; + vec = new Vector3(), + mat = new Matrix4(), - } + numpoints = segments + 1, + theta, + smallest, - } ); + tx, ty, tz, + i, u; - /** - * @author mrdoob / http://mrdoob.com/ - * - * Abstract Base class to block based textures loader (dds, pvr, ...) - */ - function CompressedTextureLoader( manager ) { + // expose internals + this.tangents = tangents; + this.normals = normals; + this.binormals = binormals; - this.manager = ( manager !== undefined ) ? manager : exports.DefaultLoadingManager; + // compute the tangent vectors for each segment on the path - // override in sub classes - this._parser = null; + for ( i = 0; i < numpoints; i ++ ) { - } + u = i / ( numpoints - 1 ); - Object.assign( CompressedTextureLoader.prototype, { + tangents[ i ] = path.getTangentAt( u ); + tangents[ i ].normalize(); - load: function ( url, onLoad, onProgress, onError ) { + } - var scope = this; + initialNormal3(); - var images = []; + /* + function initialNormal1(lastBinormal) { + // fixed start binormal. Has dangers of 0 vectors + normals[ 0 ] = new THREE.Vector3(); + binormals[ 0 ] = new THREE.Vector3(); + if (lastBinormal===undefined) lastBinormal = new THREE.Vector3( 0, 0, 1 ); + normals[ 0 ].crossVectors( lastBinormal, tangents[ 0 ] ).normalize(); + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize(); + } - var texture = new CompressedTexture(); - texture.image = images; + function initialNormal2() { - var loader = new XHRLoader( this.manager ); - loader.setPath( this.path ); - loader.setResponseType( 'arraybuffer' ); + // This uses the Frenet-Serret formula for deriving binormal + var t2 = path.getTangentAt( epsilon ); - function loadTexture( i ) { + normals[ 0 ] = new THREE.Vector3().subVectors( t2, tangents[ 0 ] ).normalize(); + binormals[ 0 ] = new THREE.Vector3().crossVectors( tangents[ 0 ], normals[ 0 ] ); - loader.load( url[ i ], function ( buffer ) { + normals[ 0 ].crossVectors( binormals[ 0 ], tangents[ 0 ] ).normalize(); // last binormal x tangent + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize(); - var texDatas = scope._parser( buffer, true ); + } + */ - images[ i ] = { - width: texDatas.width, - height: texDatas.height, - format: texDatas.format, - mipmaps: texDatas.mipmaps - }; + function initialNormal3() { - loaded += 1; + // select an initial normal vector perpendicular to the first tangent vector, + // and in the direction of the smallest tangent xyz component - if ( loaded === 6 ) { + normals[ 0 ] = new Vector3(); + binormals[ 0 ] = new Vector3(); + smallest = Number.MAX_VALUE; + tx = Math.abs( tangents[ 0 ].x ); + ty = Math.abs( tangents[ 0 ].y ); + tz = Math.abs( tangents[ 0 ].z ); - if ( texDatas.mipmapCount === 1 ) - texture.minFilter = LinearFilter; + if ( tx <= smallest ) { - texture.format = texDatas.format; - texture.needsUpdate = true; + smallest = tx; + normal.set( 1, 0, 0 ); - if ( onLoad ) onLoad( texture ); + } - } + if ( ty <= smallest ) { - }, onProgress, onError ); + smallest = ty; + normal.set( 0, 1, 0 ); } - if ( Array.isArray( url ) ) { - - var loaded = 0; + if ( tz <= smallest ) { - for ( var i = 0, il = url.length; i < il; ++ i ) { + normal.set( 0, 0, 1 ); - loadTexture( i ); + } - } + vec.crossVectors( tangents[ 0 ], normal ).normalize(); - } else { + normals[ 0 ].crossVectors( tangents[ 0 ], vec ); + binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); - // compressed cubemap texture stored in a single DDS file + } - loader.load( url, function ( buffer ) { - var texDatas = scope._parser( buffer, true ); + // compute the slowly-varying normal and binormal vectors for each segment on the path - if ( texDatas.isCubemap ) { + for ( i = 1; i < numpoints; i ++ ) { - var faces = texDatas.mipmaps.length / texDatas.mipmapCount; + normals[ i ] = normals[ i - 1 ].clone(); - for ( var f = 0; f < faces; f ++ ) { + binormals[ i ] = binormals[ i - 1 ].clone(); - images[ f ] = { mipmaps : [] }; + vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); - for ( var i = 0; i < texDatas.mipmapCount; i ++ ) { + if ( vec.length() > Number.EPSILON ) { - 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; + vec.normalize(); - } + theta = Math.acos( exports.Math.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors - } + normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); - } else { + } - texture.image.width = texDatas.width; - texture.image.height = texDatas.height; - texture.mipmaps = texDatas.mipmaps; + binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); - } + } - if ( texDatas.mipmapCount === 1 ) { - texture.minFilter = LinearFilter; + // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same - } + if ( closed ) { - texture.format = texDatas.format; - texture.needsUpdate = true; + theta = Math.acos( exports.Math.clamp( normals[ 0 ].dot( normals[ numpoints - 1 ] ), - 1, 1 ) ); + theta /= ( numpoints - 1 ); - if ( onLoad ) onLoad( texture ); + if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ numpoints - 1 ] ) ) > 0 ) { - }, onProgress, onError ); + theta = - theta; } - return texture; - - }, + for ( i = 1; i < numpoints; i ++ ) { - setPath: function ( value ) { + // twist a little... + normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); + binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); - this.path = value; - return this; + } } - } ); + }; /** - * @author Nikos M. / https://github.com/foo123/ + * @author Mugen87 / https://github.com/Mugen87 * - * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) + * see: http://www.blackpawn.com/texts/pqtorus/ */ + function TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) { - var DataTextureLoader = BinaryTextureLoader; - function BinaryTextureLoader( manager ) { - - this.manager = ( manager !== undefined ) ? manager : exports.DefaultLoadingManager; + BufferGeometry.call( this ); - // override in sub classes - this._parser = null; + this.type = 'TorusKnotBufferGeometry'; - } + this.parameters = { + radius: radius, + tube: tube, + tubularSegments: tubularSegments, + radialSegments: radialSegments, + p: p, + q: q + }; - Object.assign( BinaryTextureLoader.prototype, { + radius = radius || 100; + tube = tube || 40; + tubularSegments = Math.floor( tubularSegments ) || 64; + radialSegments = Math.floor( radialSegments ) || 8; + p = p || 2; + q = q || 3; - load: function ( url, onLoad, onProgress, onError ) { + // used to calculate buffer length + var vertexCount = ( ( radialSegments + 1 ) * ( tubularSegments + 1 ) ); + var indexCount = radialSegments * tubularSegments * 2 * 3; - var scope = this; + // buffers + var indices = new BufferAttribute( new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ) , 1 ); + var vertices = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); + var normals = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); + var uvs = new BufferAttribute( new Float32Array( vertexCount * 2 ), 2 ); - var texture = new DataTexture(); + // helper variables + var i, j, index = 0, indexOffset = 0; - var loader = new XHRLoader( this.manager ); - loader.setResponseType( 'arraybuffer' ); + var vertex = new Vector3(); + var normal = new Vector3(); + var uv = new Vector2(); - loader.load( url, function ( buffer ) { + var P1 = new Vector3(); + var P2 = new Vector3(); - var texData = scope._parser( buffer ); + var B = new Vector3(); + var T = new Vector3(); + var N = new Vector3(); - if ( ! texData ) return; + // generate vertices, normals and uvs - if ( undefined !== texData.image ) { + for ( i = 0; i <= tubularSegments; ++ i ) { - texture.image = texData.image; + // the radian "u" is used to calculate the position on the torus curve of the current tubular segement - } else if ( undefined !== texData.data ) { + var u = i / tubularSegments * p * Math.PI * 2; - texture.image.width = texData.width; - texture.image.height = texData.height; - texture.image.data = texData.data; + // 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 - } + calculatePositionOnCurve( u, p, q, radius, P1 ); + calculatePositionOnCurve( u + 0.01, p, q, radius, P2 ); - texture.wrapS = undefined !== texData.wrapS ? texData.wrapS : ClampToEdgeWrapping; - texture.wrapT = undefined !== texData.wrapT ? texData.wrapT : ClampToEdgeWrapping; + // calculate orthonormal basis - texture.magFilter = undefined !== texData.magFilter ? texData.magFilter : LinearFilter; - texture.minFilter = undefined !== texData.minFilter ? texData.minFilter : LinearMipMapLinearFilter; + T.subVectors( P2, P1 ); + N.addVectors( P2, P1 ); + B.crossVectors( T, N ); + N.crossVectors( B, T ); - texture.anisotropy = undefined !== texData.anisotropy ? texData.anisotropy : 1; + // normalize B, N. T can be ignored, we don't use it - if ( undefined !== texData.format ) { + B.normalize(); + N.normalize(); - texture.format = texData.format; + for ( j = 0; j <= radialSegments; ++ j ) { - } - if ( undefined !== texData.type ) { + // 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. - texture.type = texData.type; + var v = j / radialSegments * Math.PI * 2; + var cx = - tube * Math.cos( v ); + var cy = tube * Math.sin( v ); - } + // 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 - if ( undefined !== texData.mipmaps ) { + 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 ); - texture.mipmaps = texData.mipmaps; + // 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 ); - if ( 1 === texData.mipmapCount ) { + // uv + uv.x = i / tubularSegments; + uv.y = j / radialSegments; + uvs.setXY( index, uv.x, uv.y ); - texture.minFilter = LinearFilter; + // increase index + index ++; - } + } - texture.needsUpdate = true; + } - if ( onLoad ) onLoad( texture, texData ); + // generate indices - }, onProgress, onError ); + for ( j = 1; j <= tubularSegments; j ++ ) { + for ( i = 1; i <= radialSegments; i ++ ) { - return texture; + // 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++; - } ); + // face two + indices.setX( indexOffset, b ); indexOffset++; + indices.setX( indexOffset, c ); indexOffset++; + indices.setX( indexOffset, d ); indexOffset++; - /** - * @author mrdoob / http://mrdoob.com/ - */ + } - function ImageLoader( manager ) { + } - this.manager = ( manager !== undefined ) ? manager : exports.DefaultLoadingManager; + // build geometry - } + this.setIndex( indices ); + this.addAttribute( 'position', vertices ); + this.addAttribute( 'normal', normals ); + this.addAttribute( 'uv', uvs ); - Object.assign( ImageLoader.prototype, { + // this function calculates the current position on the torus curve - load: function ( url, onLoad, onProgress, onError ) { + function calculatePositionOnCurve( u, p, q, radius, position ) { - var scope = this; + var cu = Math.cos( u ); + var su = Math.sin( u ); + var quOverP = q / p * u; + var cs = Math.cos( quOverP ); - var image = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'img' ); - image.onload = function () { + position.x = radius * ( 2 + cs ) * 0.5 * cu; + position.y = radius * ( 2 + cs ) * su * 0.5; + position.z = radius * Math.sin( quOverP ) * 0.5; - image.onload = null; + } - URL.revokeObjectURL( image.src ); + } - if ( onLoad ) onLoad( image ); + TorusKnotBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); + TorusKnotBufferGeometry.prototype.constructor = TorusKnotBufferGeometry; - scope.manager.itemEnd( url ); + /** + * @author oosmoxiecode + */ - }; + function TorusKnotGeometry( radius, tube, tubularSegments, radialSegments, p, q, heightScale ) { - if ( url.indexOf( 'data:' ) === 0 ) { + Geometry.call( this ); - image.src = url; + this.type = 'TorusKnotGeometry'; - } else { + this.parameters = { + radius: radius, + tube: tube, + tubularSegments: tubularSegments, + radialSegments: radialSegments, + p: p, + q: q + }; - var loader = new XHRLoader(); - loader.setPath( this.path ); - loader.setResponseType( 'blob' ); - loader.setWithCredentials( this.withCredentials ); - loader.load( url, function ( blob ) { + if( heightScale !== undefined ) console.warn( 'THREE.TorusKnotGeometry: heightScale has been deprecated. Use .scale( x, y, z ) instead.' ); - image.src = URL.createObjectURL( blob ); + this.fromBufferGeometry( new TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) ); + this.mergeVertices(); - }, onProgress, onError ); + } - } + TorusKnotGeometry.prototype = Object.create( Geometry.prototype ); + TorusKnotGeometry.prototype.constructor = TorusKnotGeometry; - scope.manager.itemStart( url ); + /** + * @author Mugen87 / https://github.com/Mugen87 + */ - return image; + function TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) { - }, + BufferGeometry.call( this ); - setCrossOrigin: function ( value ) { + this.type = 'TorusBufferGeometry'; - this.crossOrigin = value; - return this; + this.parameters = { + radius: radius, + tube: tube, + radialSegments: radialSegments, + tubularSegments: tubularSegments, + arc: arc + }; - }, + radius = radius || 100; + tube = tube || 40; + radialSegments = Math.floor( radialSegments ) || 8; + tubularSegments = Math.floor( tubularSegments ) || 6; + arc = arc || Math.PI * 2; - setWithCredentials: function ( value ) { + // used to calculate buffer length + var vertexCount = ( ( radialSegments + 1 ) * ( tubularSegments + 1 ) ); + var indexCount = radialSegments * tubularSegments * 2 * 3; - this.withCredentials = value; - return this; + // 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; - setPath: function ( value ) { + // helper variables + var center = new Vector3(); + var vertex = new Vector3(); + var normal = new Vector3(); - this.path = value; - return this; + var j, i; - } + // generate vertices, normals and uvs - } ); + for ( j = 0; j <= radialSegments; j ++ ) { - /** - * @author mrdoob / http://mrdoob.com/ - */ + for ( i = 0; i <= tubularSegments; i ++ ) { - function CubeTextureLoader( manager ) { + var u = i / tubularSegments * arc; + var v = j / radialSegments * Math.PI * 2; - this.manager = ( manager !== undefined ) ? manager : exports.DefaultLoadingManager; + // 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 ); - } + vertices[ vertexBufferOffset ] = vertex.x; + vertices[ vertexBufferOffset + 1 ] = vertex.y; + vertices[ vertexBufferOffset + 2 ] = vertex.z; - Object.assign( CubeTextureLoader.prototype, { + // this vector is used to calculate the normal + center.x = radius * Math.cos( u ); + center.y = radius * Math.sin( u ); - load: function ( urls, onLoad, onProgress, onError ) { + // normal + normal.subVectors( vertex, center ).normalize(); - var texture = new CubeTexture(); + normals[ vertexBufferOffset ] = normal.x; + normals[ vertexBufferOffset + 1 ] = normal.y; + normals[ vertexBufferOffset + 2 ] = normal.z; - var loader = new ImageLoader( this.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.setPath( this.path ); + // uv + uvs[ uvBufferOffset ] = i / tubularSegments; + uvs[ uvBufferOffset + 1 ] = j / radialSegments; - var loaded = 0; + // update offsets + vertexBufferOffset += 3; + uvBufferOffset += 2; - function loadTexture( i ) { + } - loader.load( urls[ i ], function ( image ) { + } - texture.images[ i ] = image; + // generate indices - loaded ++; + for ( j = 1; j <= radialSegments; j ++ ) { - if ( loaded === 6 ) { + for ( i = 1; i <= tubularSegments; i ++ ) { - texture.needsUpdate = true; + // 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; - if ( onLoad ) onLoad( texture ); + // face one + indices[ indexBufferOffset ] = a; + indices[ indexBufferOffset + 1 ] = b; + indices[ indexBufferOffset + 2 ] = d; - } + // face two + indices[ indexBufferOffset + 3 ] = b; + indices[ indexBufferOffset + 4 ] = c; + indices[ indexBufferOffset + 5 ] = d; - }, undefined, onError ); + // update offset + indexBufferOffset += 6; } - for ( var i = 0; i < urls.length; ++ i ) { + } - loadTexture( i ); + // build geometry + this.setIndex( new BufferAttribute( indices, 1 ) ); + this.addAttribute( 'position', new BufferAttribute( vertices, 3 ) ); + this.addAttribute( 'normal', new BufferAttribute( normals, 3 ) ); + this.addAttribute( 'uv', new BufferAttribute( uvs, 2 ) ); - } + } - return texture; + TorusBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); + TorusBufferGeometry.prototype.constructor = TorusBufferGeometry; - }, + /** + * @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 + */ - setCrossOrigin: function ( value ) { + function TorusGeometry( radius, tube, radialSegments, tubularSegments, arc ) { - this.crossOrigin = value; - return this; + Geometry.call( this ); - }, + this.type = 'TorusGeometry'; - setPath: function ( value ) { + this.parameters = { + radius: radius, + tube: tube, + radialSegments: radialSegments, + tubularSegments: tubularSegments, + arc: arc + }; - this.path = value; - return this; + this.fromBufferGeometry( new TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) ); - } + } - } ); + TorusGeometry.prototype = Object.create( Geometry.prototype ); + TorusGeometry.prototype.constructor = TorusGeometry; /** - * @author mrdoob / http://mrdoob.com/ + * @author zz85 / http://www.lab4games.net/zz85/blog */ - function TextureLoader( manager ) { + exports.ShapeUtils = { - this.manager = ( manager !== undefined ) ? manager : exports.DefaultLoadingManager; + // calculate area of the contour polygon - } + area: function ( contour ) { - Object.assign( TextureLoader.prototype, { + var n = contour.length; + var a = 0.0; - load: function ( url, onLoad, onProgress, onError ) { + for ( var p = n - 1, q = 0; q < n; p = q ++ ) { - var texture = new Texture(); + a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; - var loader = new ImageLoader( this.manager ); - loader.setCrossOrigin( this.crossOrigin ); - loader.setWithCredentials( this.withCredentials ); - 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; + return a * 0.5; - texture.format = isJPEG ? RGBFormat : RGBAFormat; - texture.image = image; - texture.needsUpdate = true; + }, - if ( onLoad !== undefined ) { + triangulate: ( function () { - onLoad( texture ); + /** + * 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 ) { - }, onProgress, onError ); + var p; + var ax, ay, bx, by; + var cx, cy, px, py; - return texture; + ax = contour[ verts[ u ] ].x; + ay = contour[ verts[ u ] ].y; - }, + bx = contour[ verts[ v ] ].x; + by = contour[ verts[ v ] ].y; - setCrossOrigin: function ( value ) { + cx = contour[ verts[ w ] ].x; + cy = contour[ verts[ w ] ].y; - this.crossOrigin = value; - return this; + 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; - setWithCredentials: function ( value ) { + aX = cx - bx; aY = cy - by; + bX = ax - cx; bY = ay - cy; + cX = bx - ax; cY = by - ay; - this.withCredentials = value; - return this; + for ( p = 0; p < n; p ++ ) { - }, + px = contour[ verts[ p ] ].x; + py = contour[ verts[ p ] ].y; - setPath: function ( value ) { + if ( ( ( px === ax ) && ( py === ay ) ) || + ( ( px === bx ) && ( py === by ) ) || + ( ( px === cx ) && ( py === cy ) ) ) continue; - this.path = value; - return this; + 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; - } ); + } - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ + return true; - function Light( color, intensity ) { + } - Object3D.call( this ); + // takes in an contour array and returns - this.type = 'Light'; + return function triangulate( contour, indices ) { - this.color = new Color( color ); - this.intensity = intensity !== undefined ? intensity : 1; + var n = contour.length; - this.receiveShadow = undefined; + if ( n < 3 ) return null; - } + var result = [], + verts = [], + vertIndices = []; - Light.prototype = Object.assign( Object.create( Object3D.prototype ), { + /* we want a counter-clockwise polygon in verts */ - constructor: Light, + var u, v, w; - isLight: true, + if ( exports.ShapeUtils.area( contour ) > 0.0 ) { - copy: function ( source ) { + for ( v = 0; v < n; v ++ ) verts[ v ] = v; - Object3D.prototype.copy.call( this, source ); + } else { - this.color.copy( source.color ); - this.intensity = source.intensity; + for ( v = 0; v < n; v ++ ) verts[ v ] = ( n - 1 ) - v; - return this; + } - }, + var nv = n; - toJSON: function ( meta ) { + /* remove nv - 2 vertices, creating 1 triangle every time */ - var data = Object3D.prototype.toJSON.call( this, meta ); + var count = 2 * nv; /* error detection */ - data.object.color = this.color.getHex(); - data.object.intensity = this.intensity; + for ( v = nv - 1; nv > 2; ) { - if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex(); + /* if we loop, it is probably a non-simple polygon */ - 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; + if ( ( count -- ) <= 0 ) { - if ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON(); + //** Triangulate: ERROR - probable bad polygon! - return data; + //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; - } ); + } - /** - * @author alteredq / http://alteredqualia.com/ - */ + /* three consecutive vertices in current polygon, */ - function HemisphereLight( skyColor, groundColor, intensity ) { + 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 */ - Light.call( this, skyColor, intensity ); + if ( snip( contour, u, v, w, nv, verts ) ) { - this.type = 'HemisphereLight'; + var a, b, c, s, t; - this.castShadow = undefined; + /* true names of the vertices */ - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); + a = verts[ u ]; + b = verts[ v ]; + c = verts[ w ]; - this.groundColor = new Color( groundColor ); + /* output Triangle */ - } + result.push( [ contour[ a ], + contour[ b ], + contour[ c ] ] ); - HemisphereLight.prototype = Object.assign( Object.create( Light.prototype ), { - constructor: HemisphereLight, + vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] ); - isHemisphereLight: true, + /* remove v from the remaining polygon */ - copy: function ( source ) { + for ( s = v, t = v + 1; t < nv; s ++, t ++ ) { - Light.prototype.copy.call( this, source ); + verts[ s ] = verts[ t ]; - this.groundColor.copy( source.groundColor ); + } - return this; + nv --; - } + /* reset error detection counter */ - } ); + count = 2 * nv; - /** - * @author mrdoob / http://mrdoob.com/ - */ + } - function LightShadow( camera ) { + } - this.camera = camera; + if ( indices ) return vertIndices; + return result; - this.bias = 0; - this.radius = 1; + } - this.mapSize = new Vector2( 512, 512 ); + } )(), - this.map = null; - this.matrix = new Matrix4(); + triangulateShape: function ( contour, holes ) { - } + function removeDupEndPts(points) { - Object.assign( LightShadow.prototype, { + var l = points.length; - copy: function ( source ) { + if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) { - this.camera = source.camera.clone(); + points.pop(); - this.bias = source.bias; - this.radius = source.radius; + } - this.mapSize.copy( source.mapSize ); + } - return this; + removeDupEndPts( contour ); + holes.forEach( removeDupEndPts ); - }, + function point_in_segment_2D_colin( inSegPt1, inSegPt2, inOtherPt ) { - clone: function () { + // inOtherPt needs to be collinear to the inSegment + if ( inSegPt1.x !== inSegPt2.x ) { - return new this.constructor().copy( this ); + if ( inSegPt1.x < inSegPt2.x ) { - }, + return ( ( inSegPt1.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt2.x ) ); - toJSON: function () { + } else { - var object = {}; + return ( ( inSegPt2.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt1.x ) ); - if ( this.bias !== 0 ) object.bias = this.bias; - if ( this.radius !== 1 ) object.radius = this.radius; - if ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray(); + } - object.camera = this.camera.toJSON( false ).object; - delete object.camera.matrix; + } else { - return object; + if ( inSegPt1.y < inSegPt2.y ) { - } + return ( ( inSegPt1.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt2.y ) ); - } ); + } else { - /** - * @author mrdoob / http://mrdoob.com/ - */ + return ( ( inSegPt2.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt1.y ) ); - function SpotLightShadow() { + } - LightShadow.call( this, new PerspectiveCamera( 50, 1, 0.5, 500 ) ); + } - } + } - SpotLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { + function intersect_segments_2D( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1, inSeg2Pt2, inExcludeAdjacentSegs ) { - constructor: SpotLightShadow, + var seg1dx = inSeg1Pt2.x - inSeg1Pt1.x, seg1dy = inSeg1Pt2.y - inSeg1Pt1.y; + var seg2dx = inSeg2Pt2.x - inSeg2Pt1.x, seg2dy = inSeg2Pt2.y - inSeg2Pt1.y; - isSpotLightShadow: true, + var seg1seg2dx = inSeg1Pt1.x - inSeg2Pt1.x; + var seg1seg2dy = inSeg1Pt1.y - inSeg2Pt1.y; - update: function ( light ) { + var limit = seg1dy * seg2dx - seg1dx * seg2dy; + var perpSeg1 = seg1dy * seg1seg2dx - seg1dx * seg1seg2dy; - var fov = exports.Math.RAD2DEG * 2 * light.angle; - var aspect = this.mapSize.width / this.mapSize.height; - var far = light.distance || 500; + if ( Math.abs( limit ) > Number.EPSILON ) { - var camera = this.camera; + // not parallel - if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) { + var perpSeg2; + if ( limit > 0 ) { - camera.fov = fov; - camera.aspect = aspect; - camera.far = far; - camera.updateProjectionMatrix(); + 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 []; - } ); + } - /** - * @author alteredq / http://alteredqualia.com/ - */ + // i.e. to reduce rounding errors + // intersection at endpoint of segment#1? + if ( perpSeg2 === 0 ) { - function SpotLight( color, intensity, distance, angle, penumbra, decay ) { + if ( ( inExcludeAdjacentSegs ) && + ( ( perpSeg1 === 0 ) || ( perpSeg1 === limit ) ) ) return []; + return [ inSeg1Pt1 ]; - Light.call( this, color, intensity ); + } + if ( perpSeg2 === limit ) { - this.type = 'SpotLight'; + if ( ( inExcludeAdjacentSegs ) && + ( ( perpSeg1 === 0 ) || ( perpSeg1 === limit ) ) ) return []; + return [ inSeg1Pt2 ]; - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); + } + // intersection at endpoint of segment#2? + if ( perpSeg1 === 0 ) return [ inSeg2Pt1 ]; + if ( perpSeg1 === limit ) return [ inSeg2Pt2 ]; - this.target = new Object3D(); + // return real intersection point + var factorSeg1 = perpSeg2 / limit; + return [ { x: inSeg1Pt1.x + factorSeg1 * seg1dx, + y: inSeg1Pt1.y + factorSeg1 * seg1dy } ]; - 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; - } - } ); + } else { - 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. + // parallel or collinear + if ( ( perpSeg1 !== 0 ) || + ( seg2dy * seg1seg2dx !== seg2dx * seg1seg2dy ) ) return []; - this.shadow = new SpotLightShadow(); + // 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 - SpotLight.prototype = Object.assign( Object.create( Light.prototype ), { + } + // segment#1 is a single point + if ( seg1Pt ) { - constructor: SpotLight, + if ( ! point_in_segment_2D_colin( inSeg2Pt1, inSeg2Pt2, inSeg1Pt1 ) ) return []; // but not in segment#2 + return [ inSeg1Pt1 ]; - isSpotLight: true, + } + // segment#2 is a single point + if ( seg2Pt ) { - copy: function ( source ) { + if ( ! point_in_segment_2D_colin( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1 ) ) return []; // but not in segment#1 + return [ inSeg2Pt1 ]; - Light.prototype.copy.call( this, source ); + } - this.distance = source.distance; - this.angle = source.angle; - this.penumbra = source.penumbra; - this.decay = source.decay; + // they are collinear segments, which might overlap + var seg1min, seg1max, seg1minVal, seg1maxVal; + var seg2min, seg2max, seg2minVal, seg2maxVal; + if ( seg1dx !== 0 ) { - this.target = source.target.clone(); + // the segments are NOT on a vertical line + if ( inSeg1Pt1.x < inSeg1Pt2.x ) { - this.shadow = source.shadow.clone(); + seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.x; + seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.x; - return this; + } else { - } + seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.x; + seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.x; - } ); + } + if ( inSeg2Pt1.x < inSeg2Pt2.x ) { - /** - * @author mrdoob / http://mrdoob.com/ - */ + seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.x; + seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.x; + } else { - function PointLight( color, intensity, distance, decay ) { + seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.x; + seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.x; - Light.call( this, color, intensity ); + } - this.type = 'PointLight'; + } else { - 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; - - }, - 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 ); - } - } ); + // the segments are on a vertical line + if ( inSeg1Pt1.y < inSeg1Pt2.y ) { - this.distance = ( distance !== undefined ) ? distance : 0; - this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. + seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.y; + seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.y; - this.shadow = new LightShadow( new PerspectiveCamera( 90, 1, 0.5, 500 ) ); + } else { - } + seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.y; + seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.y; - PointLight.prototype = Object.assign( Object.create( Light.prototype ), { + } + if ( inSeg2Pt1.y < inSeg2Pt2.y ) { - constructor: PointLight, + seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.y; + seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.y; - isPointLight: true, + } else { - copy: function ( source ) { + seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.y; + seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.y; - Light.prototype.copy.call( this, source ); + } - this.distance = source.distance; - this.decay = source.decay; + } + if ( seg1minVal <= seg2minVal ) { - this.shadow = source.shadow.clone(); + if ( seg1maxVal < seg2minVal ) return []; + if ( seg1maxVal === seg2minVal ) { - return this; + if ( inExcludeAdjacentSegs ) return []; + return [ seg2min ]; - } + } + if ( seg1maxVal <= seg2maxVal ) return [ seg2min, seg1max ]; + return [ seg2min, seg2max ]; - } ); + } else { - /** - * @author mrdoob / http://mrdoob.com/ - */ + if ( seg1minVal > seg2maxVal ) return []; + if ( seg1minVal === seg2maxVal ) { - function DirectionalLightShadow( light ) { + if ( inExcludeAdjacentSegs ) return []; + return [ seg1min ]; - LightShadow.call( this, new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) ); + } + if ( seg1maxVal <= seg2maxVal ) return [ seg1min, seg1max ]; + return [ seg1min, seg2max ]; - } + } - DirectionalLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { + } - constructor: DirectionalLightShadow + } - } ); + function isPointInsideAngle( inVertex, inLegFromPt, inLegToPt, inOtherPt ) { - /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ - */ + // The order of legs is important - function DirectionalLight( color, intensity ) { + // 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; - Light.call( this, color, intensity ); + // main angle >0: < 180 deg.; 0: 180 deg.; <0: > 180 deg. + var from2toAngle = legFromPtX * legToPtY - legFromPtY * legToPtX; + var from2otherAngle = legFromPtX * otherPtY - legFromPtY * otherPtX; - this.type = 'DirectionalLight'; + if ( Math.abs( from2toAngle ) > Number.EPSILON ) { - this.position.copy( Object3D.DefaultUp ); - this.updateMatrix(); + // angle != 180 deg. - this.target = new Object3D(); + var other2toAngle = otherPtX * legToPtY - otherPtY * legToPtX; + // console.log( "from2to: " + from2toAngle + ", from2other: " + from2otherAngle + ", other2to: " + other2toAngle ); - this.shadow = new DirectionalLightShadow(); + if ( from2toAngle > 0 ) { - } + // main angle < 180 deg. + return ( ( from2otherAngle >= 0 ) && ( other2toAngle >= 0 ) ); - DirectionalLight.prototype = Object.assign( Object.create( Light.prototype ), { + } else { - constructor: DirectionalLight, + // main angle > 180 deg. + return ( ( from2otherAngle >= 0 ) || ( other2toAngle >= 0 ) ); - isDirectionalLight: true, + } - copy: function ( source ) { + } else { - Light.prototype.copy.call( this, source ); + // angle == 180 deg. + // console.log( "from2to: 180 deg., from2other: " + from2otherAngle ); + return ( from2otherAngle > 0 ); - this.target = source.target.clone(); + } - this.shadow = source.shadow.clone(); + } - return this; - } + function removeHoles( contour, holes ) { - } ); + var shape = contour.concat(); // work on this shape + var hole; - /** - * @author mrdoob / http://mrdoob.com/ - */ + function isCutLineInsideAngles( inShapeIdx, inHoleIdx ) { - function AmbientLight( color, intensity ) { + // Check if hole point lies within angle around shape point + var lastShapeIdx = shape.length - 1; - Light.call( this, color, intensity ); + var prevShapeIdx = inShapeIdx - 1; + if ( prevShapeIdx < 0 ) prevShapeIdx = lastShapeIdx; - this.type = 'AmbientLight'; + var nextShapeIdx = inShapeIdx + 1; + if ( nextShapeIdx > lastShapeIdx ) nextShapeIdx = 0; - this.castShadow = undefined; + 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; - AmbientLight.prototype = Object.assign( Object.create( Light.prototype ), { + } - constructor: AmbientLight, + // Check if shape point lies within angle around hole point + var lastHoleIdx = hole.length - 1; - isAmbientLight: true, + var prevHoleIdx = inHoleIdx - 1; + if ( prevHoleIdx < 0 ) prevHoleIdx = lastHoleIdx; - } ); + var nextHoleIdx = inHoleIdx + 1; + if ( nextHoleIdx > lastHoleIdx ) nextHoleIdx = 0; - /** - * @author tschw - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - */ + insideAngle = isPointInsideAngle( hole[ inHoleIdx ], hole[ prevHoleIdx ], hole[ nextHoleIdx ], shape[ inShapeIdx ] ); + if ( ! insideAngle ) { - exports.AnimationUtils = { + // console.log( "Vertex (Hole): " + inHoleIdx + ", Point: " + shape[inShapeIdx].x + "/" + shape[inShapeIdx].y ); + return false; - // same as Array.prototype.slice, but also works on typed arrays - arraySlice: function( array, from, to ) { + } - if ( exports.AnimationUtils.isTypedArray( array ) ) { + return true; - return new array.constructor( array.subarray( from, to ) ); + } - } + function intersectsShapeEdge( inShapePt, inHolePt ) { - return array.slice( from, to ); + // 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; - // converts an array to a specific type - convertArray: function( array, type, forceClone ) { + } - if ( ! array || // let 'undefined' and 'null' pass - ! forceClone && array.constructor === type ) return array; + return false; - if ( typeof type.BYTES_PER_ELEMENT === 'number' ) { + } - return new type( array ); // create typed array + var indepHoles = []; - } + function intersectsHoleEdge( inShapePt, inHolePt ) { - return Array.prototype.slice.call( array ); // create Array + // 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 ++ ) { - isTypedArray: function( object ) { + nextIdx = hIdx + 1; nextIdx %= chkHole.length; + intersection = intersect_segments_2D( inShapePt, inHolePt, chkHole[ hIdx ], chkHole[ nextIdx ], true ); + if ( intersection.length > 0 ) return true; - return ArrayBuffer.isView( object ) && - ! ( object instanceof DataView ); + } - }, + } + return false; - // returns an array by which times and values can be sorted - getKeyframeOrder: function( times ) { + } - function compareTime( i, j ) { + var holeIndex, shapeIndex, + shapePt, holePt, + holeIdx, cutKey, failedCuts = [], + tmpShape1, tmpShape2, + tmpHole1, tmpHole2; - return times[ i ] - times[ j ]; + for ( var h = 0, hl = holes.length; h < hl; h ++ ) { - } + indepHoles.push( h ); - var n = times.length; - var result = new Array( n ); - for ( var i = 0; i !== n; ++ i ) result[ i ] = i; + } - result.sort( compareTime ); + var minShapeIndex = 0; + var counter = indepHoles.length * 2; + while ( indepHoles.length > 0 ) { - return result; + counter --; + if ( counter < 0 ) { - }, + console.log( "Infinite Loop! Holes left:" + indepHoles.length + ", Probably Hole outside Shape!" ); + break; - // uses the array previously returned by 'getKeyframeOrder' to sort data - sortedArray: function( values, stride, order ) { + } - var nValues = values.length; - var result = new values.constructor( nValues ); + // search for shape-vertex and hole-vertex, + // which can be connected without intersections + for ( shapeIndex = minShapeIndex; shapeIndex < shape.length; shapeIndex ++ ) { - for ( var i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) { + shapePt = shape[ shapeIndex ]; + holeIndex = - 1; - var srcOffset = order[ i ] * stride; + // search for hole which can be reached without intersections + for ( var h = 0; h < indepHoles.length; h ++ ) { - for ( var j = 0; j !== stride; ++ j ) { + holeIdx = indepHoles[ h ]; - result[ dstOffset ++ ] = values[ srcOffset + j ]; + // prevent multiple checks + cutKey = shapePt.x + ":" + shapePt.y + ":" + holeIdx; + if ( failedCuts[ cutKey ] !== undefined ) continue; - } - - } - - return result; - - }, - - // function for parsing AOS keyframe formats - flattenJSON: function( jsonKeys, times, values, valuePropertyName ) { - - var i = 1, key = jsonKeys[ 0 ]; + hole = holes[ holeIdx ]; + for ( var h2 = 0; h2 < hole.length; h2 ++ ) { - while ( key !== undefined && key[ valuePropertyName ] === undefined ) { + holePt = hole[ h2 ]; + if ( ! isCutLineInsideAngles( shapeIndex, h2 ) ) continue; + if ( intersectsShapeEdge( shapePt, holePt ) ) continue; + if ( intersectsHoleEdge( shapePt, holePt ) ) continue; - key = jsonKeys[ i ++ ]; + 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 ); - if ( key === undefined ) return; // no data + shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 ); - var value = key[ valuePropertyName ]; - if ( value === undefined ) return; // no data + minShapeIndex = shapeIndex; - if ( Array.isArray( value ) ) { + // Debug only, to show the selected cuts + // glob_CutLines.push( [ shapePt, holePt ] ); - do { + break; - value = key[ valuePropertyName ]; + } + if ( holeIndex >= 0 ) break; // hole-vertex found - if ( value !== undefined ) { + failedCuts[ cutKey ] = true; // remember failure - times.push( key.time ); - values.push.apply( values, value ); // push all elements + } + if ( holeIndex >= 0 ) break; // hole-vertex found } - key = jsonKeys[ i ++ ]; + } - } while ( key !== undefined ); + return shape; /* shape with no holes */ - } else if ( value.toArray !== undefined ) { - // ...assume THREE.Math-ish + } - do { - value = key[ valuePropertyName ]; + var i, il, f, face, + key, index, + allPointsMap = {}; - if ( value !== undefined ) { + // 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. - times.push( key.time ); - value.toArray( values, values.length ); + var allpoints = contour.concat(); - } + for ( var h = 0, hl = holes.length; h < hl; h ++ ) { - key = jsonKeys[ i ++ ]; + Array.prototype.push.apply( allpoints, holes[ h ] ); - } while ( key !== undefined ); + } - } else { - // otherwise push as-is + //console.log( "allpoints",allpoints, allpoints.length ); - do { + // prepare all points map - value = key[ valuePropertyName ]; + for ( i = 0, il = allpoints.length; i < il; i ++ ) { - if ( value !== undefined ) { + key = allpoints[ i ].x + ":" + allpoints[ i ].y; - times.push( key.time ); - values.push( value ); + if ( allPointsMap[ key ] !== undefined ) { - } + console.warn( "THREE.ShapeUtils: Duplicate point", key, i ); - key = jsonKeys[ i ++ ]; + } - } while ( key !== undefined ); + allPointsMap[ key ] = i; } - } - - }; - - /** - * Abstract base class of interpolants over parametric samples. - * - * The parameter domain is one dimensional, typically the time or a path - * along a curve defined by the data. - * - * The sample values can have any dimensionality and derived classes may - * apply special interpretations to the data. - * - * This class provides the interval seek in a Template Method, deferring - * the actual interpolation to derived classes. - * - * Time complexity is O(1) for linear access crossing at most two points - * and O(log N) for random access, where N is the number of positions. - * - * References: - * - * http://www.oodesign.com/template-method-pattern.html - * - * @author tschw - */ - - function Interpolant( - parameterPositions, sampleValues, sampleSize, resultBuffer ) { - - this.parameterPositions = parameterPositions; - this._cachedIndex = 0; + // remove holes by cutting paths to holes and adding them to the shape + var shapeWithoutHoles = removeHoles( contour, holes ); - this.resultBuffer = resultBuffer !== undefined ? - resultBuffer : new sampleValues.constructor( sampleSize ); - this.sampleValues = sampleValues; - this.valueSize = sampleSize; + var triangles = exports.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 - Interpolant.prototype = { + for ( i = 0, il = triangles.length; i < il; i ++ ) { - constructor: Interpolant, + face = triangles[ i ]; - evaluate: function( t ) { + for ( f = 0; f < 3; f ++ ) { - var pp = this.parameterPositions, - i1 = this._cachedIndex, + key = face[ f ].x + ":" + face[ f ].y; - t1 = pp[ i1 ], - t0 = pp[ i1 - 1 ]; + index = allPointsMap[ key ]; - validate_interval: { + if ( index !== undefined ) { - seek: { + face[ f ] = index; - var right; + } - linear_scan: { - //- See http://jsperf.com/comparison-to-undefined/3 - //- slower code: - //- - //- if ( t >= t1 || t1 === undefined ) { - forward_scan: if ( ! ( t < t1 ) ) { + } - for ( var giveUpAt = i1 + 2; ;) { + } - if ( t1 === undefined ) { + return triangles.concat(); - if ( t < t0 ) break forward_scan; + }, - // after end + isClockWise: function ( pts ) { - i1 = pp.length; - this._cachedIndex = i1; - return this.afterEnd_( i1 - 1, t, t0 ); + return exports.ShapeUtils.area( pts ) < 0; - } + }, - if ( i1 === giveUpAt ) break; // this loop + // Bezier Curves formulas obtained from + // http://en.wikipedia.org/wiki/B%C3%A9zier_curve - t0 = t1; - t1 = pp[ ++ i1 ]; + // Quad Bezier Functions - if ( t < t1 ) { + b2: ( function () { - // we have arrived at the sought interval - break seek; + function b2p0( t, p ) { - } + var k = 1 - t; + return k * k * p; - } + } - // prepare binary search on the right side of the index - right = pp.length; - break linear_scan; + function b2p1( t, p ) { - } + return 2 * ( 1 - t ) * t * p; - //- slower code: - //- if ( t < t0 || t0 === undefined ) { - if ( ! ( t >= t0 ) ) { + } - // looping? + function b2p2( t, p ) { - var t1global = pp[ 1 ]; + return t * t * p; - if ( t < t1global ) { + } - i1 = 2; // + 1, using the scan for the details - t0 = t1global; + return function b2( t, p0, p1, p2 ) { - } + return b2p0( t, p0 ) + b2p1( t, p1 ) + b2p2( t, p2 ); - // linear reverse scan + }; - for ( var giveUpAt = i1 - 2; ;) { + } )(), - if ( t0 === undefined ) { + // Cubic Bezier Functions - // before start + b3: ( function () { - this._cachedIndex = 0; - return this.beforeStart_( 0, t, t1 ); + function b3p0( t, p ) { - } + var k = 1 - t; + return k * k * k * p; - if ( i1 === giveUpAt ) break; // this loop + } - t1 = t0; - t0 = pp[ -- i1 - 1 ]; + function b3p1( t, p ) { - if ( t >= t0 ) { + var k = 1 - t; + return 3 * k * k * t * p; - // we have arrived at the sought interval - break seek; + } - } + function b3p2( t, p ) { - } + var k = 1 - t; + return 3 * k * t * t * p; - // prepare binary search on the left side of the index - right = i1; - i1 = 0; - break linear_scan; + } - } + function b3p3( t, p ) { - // the interval is valid + return t * t * t * p; - break validate_interval; + } - } // linear scan + return function b3( t, p0, p1, p2, p3 ) { - // binary search + return b3p0( t, p0 ) + b3p1( t, p1 ) + b3p2( t, p2 ) + b3p3( t, p3 ); - while ( i1 < right ) { + }; - var mid = ( i1 + right ) >>> 1; + } )() - if ( t < pp[ mid ] ) { + }; - right = mid; + /** + * @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 + * + * } + **/ - } else { + function ExtrudeGeometry( shapes, options ) { - i1 = mid + 1; + if ( typeof( shapes ) === "undefined" ) { - } + shapes = []; + return; - } + } - t1 = pp[ i1 ]; - t0 = pp[ i1 - 1 ]; + Geometry.call( this ); - // check boundary cases, again + this.type = 'ExtrudeGeometry'; - if ( t0 === undefined ) { + shapes = Array.isArray( shapes ) ? shapes : [ shapes ]; - this._cachedIndex = 0; - return this.beforeStart_( 0, t, t1 ); + this.addShapeList( shapes, options ); - } + this.computeFaceNormals(); - if ( t1 === undefined ) { + // can't really use automatic vertex normals + // as then front and back sides get smoothed too + // should do separate smoothing just for sides - i1 = pp.length; - this._cachedIndex = i1; - return this.afterEnd_( i1 - 1, t0, t ); + //this.computeVertexNormals(); - } + //console.log( "took", ( Date.now() - startTime ) ); - } // seek + } - this._cachedIndex = i1; + ExtrudeGeometry.prototype = Object.create( Geometry.prototype ); + ExtrudeGeometry.prototype.constructor = ExtrudeGeometry; - this.intervalChanged_( i1, t0, t1 ); + ExtrudeGeometry.prototype.addShapeList = function ( shapes, options ) { - } // validate_interval + var sl = shapes.length; - return this.interpolate_( i1, t0, t, t1 ); + for ( var s = 0; s < sl; s ++ ) { - }, + var shape = shapes[ s ]; + this.addShape( shape, options ); - settings: null, // optional, subclass-specific settings structure - // Note: The indirection allows central control of many interpolants. + } - // --- Protected interface + }; - DefaultSettings_: {}, + ExtrudeGeometry.prototype.addShape = function ( shape, options ) { - getSettings_: function() { + var amount = options.amount !== undefined ? options.amount : 100; - return this.settings || this.DefaultSettings_; + var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10 + var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8 + var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; - }, + var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false - copySampleValue_: function( index ) { + var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; - // copies a sample value to the result buffer + var steps = options.steps !== undefined ? options.steps : 1; - var result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, - offset = index * stride; + var extrudePath = options.extrudePath; + var extrudePts, extrudeByPath = false; - for ( var i = 0; i !== stride; ++ i ) { + // Use default WorldUVGenerator if no UV generators are specified. + var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : ExtrudeGeometry.WorldUVGenerator; - result[ i ] = values[ offset + i ]; + var splineTube, binormal, normal, position2; + if ( extrudePath ) { - } + extrudePts = extrudePath.getSpacedPoints( steps ); - return result; + extrudeByPath = true; + bevelEnabled = false; // bevels not supported for path extrusion - }, + // SETUP TNB variables - // Template methods for derived classes: + // Reuse TNB from TubeGeomtry for now. + // TODO1 - have a .isClosed in spline? - interpolate_: function( i1, t0, t, t1 ) { + splineTube = options.frames !== undefined ? options.frames : new TubeGeometry.FrenetFrames( extrudePath, steps, false ); - throw new Error( "call to abstract method" ); - // implementations shall return this.resultBuffer + // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); - }, + binormal = new Vector3(); + normal = new Vector3(); + position2 = new Vector3(); - intervalChanged_: function( i1, t0, t1 ) { + } - // empty + // Safeguards if bevels are not enabled + + if ( ! bevelEnabled ) { + + bevelSegments = 0; + bevelThickness = 0; + bevelSize = 0; } - }; + // Variables initialization - Object.assign( Interpolant.prototype, { + var ahole, h, hl; // looping of holes + var scope = this; - beforeStart_: //( 0, t, t0 ), returns this.resultBuffer - Interpolant.prototype.copySampleValue_, + var shapesOffset = this.vertices.length; - afterEnd_: //( N-1, tN-1, t ), returns this.resultBuffer - Interpolant.prototype.copySampleValue_ + var shapePoints = shape.extractPoints( curveSegments ); - } ); + var vertices = shapePoints.shape; + var holes = shapePoints.holes; - /** - * Fast and simple cubic spline interpolant. - * - * It was derived from a Hermitian construction setting the first derivative - * at each sample position to the linear slope between neighboring positions - * over their parameter interval. - * - * @author tschw - */ + var reverse = ! exports.ShapeUtils.isClockWise( vertices ); - function CubicInterpolant( - parameterPositions, sampleValues, sampleSize, resultBuffer ) { + if ( reverse ) { - Interpolant.call( - this, parameterPositions, sampleValues, sampleSize, resultBuffer ); + vertices = vertices.reverse(); - this._weightPrev = -0; - this._offsetPrev = -0; - this._weightNext = -0; - this._offsetNext = -0; + // Maybe we should also check if holes are in the opposite direction, just to be safe ... - } + for ( h = 0, hl = holes.length; h < hl; h ++ ) { - CubicInterpolant.prototype = - Object.assign( Object.create( Interpolant.prototype ), { + ahole = holes[ h ]; - constructor: CubicInterpolant, + if ( exports.ShapeUtils.isClockWise( ahole ) ) { - DefaultSettings_: { + holes[ h ] = ahole.reverse(); - endingStart: ZeroCurvatureEnding, - endingEnd: ZeroCurvatureEnding + } - }, + } - intervalChanged_: function( i1, t0, t1 ) { + reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)! - var pp = this.parameterPositions, - iPrev = i1 - 2, - iNext = i1 + 1, + } - tPrev = pp[ iPrev ], - tNext = pp[ iNext ]; - if ( tPrev === undefined ) { + var faces = exports.ShapeUtils.triangulateShape( vertices, holes ); - switch ( this.getSettings_().endingStart ) { + /* Vertices */ - case ZeroSlopeEnding: + var contour = vertices; // vertices has all points but contour has only points of circumference - // f'(t0) = 0 - iPrev = i1; - tPrev = 2 * t0 - t1; + for ( h = 0, hl = holes.length; h < hl; h ++ ) { - break; + ahole = holes[ h ]; - case WrapAroundEnding: + vertices = vertices.concat( ahole ); - // use the other end of the curve - iPrev = pp.length - 2; - tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ]; + } - break; - default: // ZeroCurvatureEnding + function scalePt2( pt, vec, size ) { - // f''(t0) = 0 a.k.a. Natural Spline - iPrev = i1; - tPrev = t1; + if ( ! vec ) console.error( "THREE.ExtrudeGeometry: vec does not exist" ); - } + return vec.clone().multiplyScalar( size ).add( pt ); - } + } - if ( tNext === undefined ) { + var b, bs, t, z, + vert, vlen = vertices.length, + face, flen = faces.length; - switch ( this.getSettings_().endingEnd ) { - case ZeroSlopeEnding: + // Find directions for point movement - // f'(tN) = 0 - iNext = i1; - tNext = 2 * t1 - t0; - break; + function getBevelVec( inPt, inPrev, inNext ) { - case WrapAroundEnding: + // 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. - // use the other end of the curve - iNext = 1; - tNext = t1 + pp[ 1 ] - pp[ 0 ]; + var v_trans_x, v_trans_y, shrink_by = 1; // resulting translation vector for inPt - break; + // good reading for geometry algorithms (here: line-line intersection) + // http://geomalgorithms.com/a05-_intersect-1.html - default: // ZeroCurvatureEnding + 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; - // f''(tN) = 0, a.k.a. Natural Spline - iNext = i1 - 1; - tNext = t0; + var v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); - } + // check for collinear edges + var collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); - } + if ( Math.abs( collinear0 ) > Number.EPSILON ) { - var halfDt = ( t1 - t0 ) * 0.5, - stride = this.valueSize; + // not collinear - this._weightPrev = halfDt / ( t0 - tPrev ); - this._weightNext = halfDt / ( tNext - t1 ); - this._offsetPrev = iPrev * stride; - this._offsetNext = iNext * stride; - - }, + // length of vectors for normalizing - interpolate_: function( i1, t0, t, t1 ) { + 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 result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, + // shift adjacent points by unit vectors to the left - o1 = i1 * stride, o0 = o1 - stride, - oP = this._offsetPrev, oN = this._offsetNext, - wP = this._weightPrev, wN = this._weightNext, + var ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); + var ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); - p = ( t - t0 ) / ( t1 - t0 ), - pp = p * p, - ppp = pp * p; + var ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); + var ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); - // evaluate polynomials + // scaling factor for v_prev to intersection point - var sP = - wP * ppp + 2 * wP * pp - wP * p; - var s0 = ( 1 + wP ) * ppp + (-1.5 - 2 * wP ) * pp + ( -0.5 + wP ) * p + 1; - var s1 = (-1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p; - var sN = wN * ppp - wN * pp; + 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 ); - // combine data linearly + // vector from inPt to intersection point - for ( var i = 0; i !== stride; ++ i ) { + v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x ); + v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y ); - result[ i ] = - sP * values[ oP + i ] + - s0 * values[ o0 + i ] + - s1 * values[ o1 + i ] + - sN * values[ oN + i ]; + // Don't normalize!, otherwise sharp corners become ugly + // but prevent crazy spikes + var v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y ); + if ( v_trans_lensq <= 2 ) { - } + return new Vector2( v_trans_x, v_trans_y ); - return result; + } else { - } + shrink_by = Math.sqrt( v_trans_lensq / 2 ); - } ); + } - /** - * @author tschw - */ + } else { - function LinearInterpolant( - parameterPositions, sampleValues, sampleSize, resultBuffer ) { + // handle special case of collinear edges - Interpolant.call( - this, parameterPositions, sampleValues, sampleSize, resultBuffer ); + var direction_eq = false; // assumes: opposite + if ( v_prev_x > Number.EPSILON ) { - } + if ( v_next_x > Number.EPSILON ) { - LinearInterpolant.prototype = - Object.assign( Object.create( Interpolant.prototype ), { + direction_eq = true; - constructor: LinearInterpolant, + } - interpolate_: function( i1, t0, t, t1 ) { + } else { - var result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, + if ( v_prev_x < - Number.EPSILON ) { - offset1 = i1 * stride, - offset0 = offset1 - stride, + if ( v_next_x < - Number.EPSILON ) { - weight1 = ( t - t0 ) / ( t1 - t0 ), - weight0 = 1 - weight1; + direction_eq = true; - for ( var i = 0; i !== stride; ++ i ) { + } - result[ i ] = - values[ offset0 + i ] * weight0 + - values[ offset1 + i ] * weight1; + } else { - } + if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) { - return result; + direction_eq = true; - } + } - } ); + } - /** - * - * Interpolant that evaluates to the sample value at the position preceeding - * the parameter. - * - * @author tschw - */ + } - function DiscreteInterpolant( - parameterPositions, sampleValues, sampleSize, resultBuffer ) { + if ( direction_eq ) { - Interpolant.call( - this, parameterPositions, sampleValues, sampleSize, resultBuffer ); + // console.log("Warning: lines are a straight sequence"); + v_trans_x = - v_prev_y; + v_trans_y = v_prev_x; + shrink_by = Math.sqrt( v_prev_lensq ); - } + } else { - DiscreteInterpolant.prototype = - Object.assign( Object.create( Interpolant.prototype ), { + // 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 ); - constructor: DiscreteInterpolant, + } - interpolate_: function( i1, t0, t, t1 ) { + } - return this.copySampleValue_( i1 - 1 ); + return new Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); } - } ); - var KeyframeTrackPrototype; + var contourMovements = []; - KeyframeTrackPrototype = { + for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { - TimeBufferType: Float32Array, - ValueBufferType: Float32Array, + if ( j === il ) j = 0; + if ( k === il ) k = 0; - DefaultInterpolation: InterpolateLinear, + // (j)---(i)---(k) + // console.log('i,j,k', i, j , k) - InterpolantFactoryMethodDiscrete: function( result ) { + contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); - return new DiscreteInterpolant( - this.times, this.values, this.getValueSize(), result ); + } - }, + var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat(); - InterpolantFactoryMethodLinear: function( result ) { + for ( h = 0, hl = holes.length; h < hl; h ++ ) { - return new LinearInterpolant( - this.times, this.values, this.getValueSize(), result ); + ahole = holes[ h ]; - }, + oneHoleMovements = []; - InterpolantFactoryMethodSmooth: function( result ) { + for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { - return new CubicInterpolant( - this.times, this.values, this.getValueSize(), result ); + if ( j === il ) j = 0; + if ( k === il ) k = 0; - }, + // (j)---(i)---(k) + oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); - setInterpolation: function( interpolation ) { + } - var factoryMethod; + holesMovements.push( oneHoleMovements ); + verticesMovements = verticesMovements.concat( oneHoleMovements ); - switch ( interpolation ) { + } - case InterpolateDiscrete: - factoryMethod = this.InterpolantFactoryMethodDiscrete; + // Loop bevelSegments, 1 for the front, 1 for the back - break; + for ( b = 0; b < bevelSegments; b ++ ) { - case InterpolateLinear: + //for ( b = bevelSegments; b > 0; b -- ) { - factoryMethod = this.InterpolantFactoryMethodLinear; + t = b / bevelSegments; + z = bevelThickness * Math.cos( t * Math.PI / 2 ); + bs = bevelSize * Math.sin( t * Math.PI / 2 ); - break; + // contract shape - case InterpolateSmooth: + for ( i = 0, il = contour.length; i < il; i ++ ) { - factoryMethod = this.InterpolantFactoryMethodSmooth; + vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); - break; + v( vert.x, vert.y, - z ); } - if ( factoryMethod === undefined ) { - - var message = "unsupported interpolation for " + - this.ValueTypeName + " keyframe track named " + this.name; - - if ( this.createInterpolant === undefined ) { + // expand holes - // fall back to default, unless the default itself is messed up - if ( interpolation !== this.DefaultInterpolation ) { + for ( h = 0, hl = holes.length; h < hl; h ++ ) { - this.setInterpolation( this.DefaultInterpolation ); + ahole = holes[ h ]; + oneHoleMovements = holesMovements[ h ]; - } else { + for ( i = 0, il = ahole.length; i < il; i ++ ) { - throw new Error( message ); // fatal, in this case + vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); - } + v( vert.x, vert.y, - z ); } - console.warn( message ); - return; - } - this.createInterpolant = factoryMethod; + } - }, + bs = bevelSize; - getInterpolation: function() { + // Back facing vertices - switch ( this.createInterpolant ) { + for ( i = 0; i < vlen; i ++ ) { - case this.InterpolantFactoryMethodDiscrete: + vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; - return InterpolateDiscrete; + if ( ! extrudeByPath ) { - case this.InterpolantFactoryMethodLinear: + v( vert.x, vert.y, 0 ); - return InterpolateLinear; + } else { - case this.InterpolantFactoryMethodSmooth: + // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); - return InterpolateSmooth; + normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x ); + binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y ); - } + position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal ); - }, + v( position2.x, position2.y, position2.z ); - getValueSize: function() { + } - return this.values.length / this.times.length; + } - }, + // Add stepped vertices... + // Including front facing vertices - // move all keyframes either forwards or backwards in time - shift: function( timeOffset ) { + var s; - if( timeOffset !== 0.0 ) { + for ( s = 1; s <= steps; s ++ ) { - var times = this.times; + for ( i = 0; i < vlen; i ++ ) { - for( var i = 0, n = times.length; i !== n; ++ i ) { + vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; - times[ i ] += timeOffset; + if ( ! extrudeByPath ) { - } - - } - - return this; - - }, + v( vert.x, vert.y, amount / steps * s ); - // scale all keyframe times by a factor (useful for frame <-> seconds conversions) - scale: function( timeScale ) { + } else { - if( timeScale !== 1.0 ) { + // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); - var times = this.times; + normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x ); + binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y ); - for( var i = 0, n = times.length; i !== n; ++ i ) { + position2.copy( extrudePts[ s ] ).add( normal ).add( binormal ); - times[ i ] *= timeScale; + v( position2.x, position2.y, position2.z ); } } - return this; - - }, + } - // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. - // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values - trim: function( startTime, endTime ) { - var times = this.times, - nKeys = times.length, - from = 0, - to = nKeys - 1; + // Add bevel segments planes - while ( from !== nKeys && times[ from ] < startTime ) ++ from; - while ( to !== -1 && times[ to ] > endTime ) -- to; + //for ( b = 1; b <= bevelSegments; b ++ ) { + for ( b = bevelSegments - 1; b >= 0; b -- ) { - ++ to; // inclusive -> exclusive bound + t = b / bevelSegments; + z = bevelThickness * Math.cos ( t * Math.PI / 2 ); + bs = bevelSize * Math.sin( t * Math.PI / 2 ); - if( from !== 0 || to !== nKeys ) { + // contract shape - // empty tracks are forbidden, so keep at least one keyframe - if ( from >= to ) to = Math.max( to , 1 ), from = to - 1; + for ( i = 0, il = contour.length; i < il; i ++ ) { - var stride = this.getValueSize(); - this.times = exports.AnimationUtils.arraySlice( times, from, to ); - this.values = exports.AnimationUtils. - arraySlice( this.values, from * stride, to * stride ); + vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); + v( vert.x, vert.y, amount + z ); } - return this; + // expand holes - }, + for ( h = 0, hl = holes.length; h < hl; h ++ ) { - // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable - validate: function() { + ahole = holes[ h ]; + oneHoleMovements = holesMovements[ h ]; - var valid = true; + for ( i = 0, il = ahole.length; i < il; i ++ ) { - var valueSize = this.getValueSize(); - if ( valueSize - Math.floor( valueSize ) !== 0 ) { + vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); - console.error( "invalid value size in track", this ); - valid = false; + if ( ! extrudeByPath ) { - } + v( vert.x, vert.y, amount + z ); - var times = this.times, - values = this.values, + } else { - nKeys = times.length; + v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); - if( nKeys === 0 ) { + } - console.error( "track is empty", this ); - valid = false; + } } - var prevTime = null; + } - for( var i = 0; i !== nKeys; i ++ ) { + /* Faces */ - var currTime = times[ i ]; + // Top and bottom faces - if ( typeof currTime === 'number' && isNaN( currTime ) ) { + buildLidFaces(); - console.error( "time is not a valid number", this, i, currTime ); - valid = false; - break; + // Sides faces - } + buildSideFaces(); - if( prevTime !== null && prevTime > currTime ) { - console.error( "out of order keys", this, i, currTime, prevTime ); - valid = false; - break; + ///// Internal functions - } + function buildLidFaces() { - prevTime = currTime; + if ( bevelEnabled ) { - } + var layer = 0; // steps + 1 + var offset = vlen * layer; - if ( values !== undefined ) { + // Bottom faces - if ( exports.AnimationUtils.isTypedArray( values ) ) { + for ( i = 0; i < flen; i ++ ) { - for ( var i = 0, n = values.length; i !== n; ++ i ) { + face = faces[ i ]; + f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset ); - var value = values[ i ]; + } - if ( isNaN( value ) ) { + layer = steps + bevelSegments * 2; + offset = vlen * layer; - console.error( "value is not a valid number", this, i, value ); - valid = false; - break; + // Top faces - } + for ( i = 0; i < flen; i ++ ) { - } + face = faces[ i ]; + f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset ); } - } - - return valid; - - }, - - // removes equivalent sequential keys as common in morph target sequences - // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) - optimize: function() { + } else { - var times = this.times, - values = this.values, - stride = this.getValueSize(), + // Bottom faces - smoothInterpolation = this.getInterpolation() === InterpolateSmooth, + for ( i = 0; i < flen; i ++ ) { - writeIndex = 1, - lastIndex = times.length - 1; + face = faces[ i ]; + f3( face[ 2 ], face[ 1 ], face[ 0 ] ); - for( var i = 1; i < lastIndex; ++ i ) { + } - var keep = false; + // Top faces - var time = times[ i ]; - var timeNext = times[ i + 1 ]; + for ( i = 0; i < flen; i ++ ) { - // remove adjacent keyframes scheduled at the same time + face = faces[ i ]; + f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); - if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) { + } - if ( ! smoothInterpolation ) { + } - // remove unnecessary keyframes same as their neighbors + } - var offset = i * stride, - offsetP = offset - stride, - offsetN = offset + stride; + // Create faces for the z-sides of the shape - for ( var j = 0; j !== stride; ++ j ) { + function buildSideFaces() { - var value = values[ offset + j ]; + var layeroffset = 0; + sidewalls( contour, layeroffset ); + layeroffset += contour.length; - if ( value !== values[ offsetP + j ] || - value !== values[ offsetN + j ] ) { + for ( h = 0, hl = holes.length; h < hl; h ++ ) { - keep = true; - break; + ahole = holes[ h ]; + sidewalls( ahole, layeroffset ); - } + //, true + layeroffset += ahole.length; - } + } - } else keep = true; + } - } + function sidewalls( contour, layeroffset ) { - // in-place compaction + var j, k; + i = contour.length; - if ( keep ) { + while ( -- i >= 0 ) { - if ( i !== writeIndex ) { + j = i; + k = i - 1; + if ( k < 0 ) k = contour.length - 1; - times[ writeIndex ] = times[ i ]; + //console.log('b', i,j, i-1, k,vertices.length); - var readOffset = i * stride, - writeOffset = writeIndex * stride; + var s = 0, sl = steps + bevelSegments * 2; - for ( var j = 0; j !== stride; ++ j ) + for ( s = 0; s < sl; s ++ ) { - values[ writeOffset + j ] = values[ readOffset + j ]; + 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; - ++ writeIndex; + f4( a, b, c, d, contour, s, sl, j, k ); } } - // flush last keyframe (compaction looks ahead) - - if ( lastIndex > 0 ) { + } - times[ writeIndex ] = times[ lastIndex ]; - for ( var readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) + function v( x, y, z ) { - values[ writeOffset + j ] = values[ readOffset + j ]; + scope.vertices.push( new Vector3( x, y, z ) ); - ++ writeIndex; + } - } + function f3( a, b, c ) { - if ( writeIndex !== times.length ) { + a += shapesOffset; + b += shapesOffset; + c += shapesOffset; - this.times = exports.AnimationUtils.arraySlice( times, 0, writeIndex ); - this.values = exports.AnimationUtils.arraySlice( values, 0, writeIndex * stride ); + scope.faces.push( new Face3( a, b, c, null, null, 0 ) ); - } + var uvs = uvgen.generateTopUV( scope, a, b, c ); - return this; + scope.faceVertexUvs[ 0 ].push( uvs ); } - } + function f4( a, b, c, d, wallContour, stepIndex, stepsLength, contourIndex1, contourIndex2 ) { - function KeyframeTrackConstructor( name, times, values, interpolation ) { + a += shapesOffset; + b += shapesOffset; + c += shapesOffset; + d += shapesOffset; - if( name === undefined ) throw new Error( "track name is undefined" ); + scope.faces.push( new Face3( a, b, d, null, null, 1 ) ); + scope.faces.push( new Face3( b, c, d, null, null, 1 ) ); - if( times === undefined || times.length === 0 ) { + var uvs = uvgen.generateSideWallUV( scope, a, b, c, d ); - throw new Error( "no keyframes in track named " + name ); + scope.faceVertexUvs[ 0 ].push( [ uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] ] ); + scope.faceVertexUvs[ 0 ].push( [ uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] ] ); } - this.name = name; + }; - this.times = exports.AnimationUtils.convertArray( times, this.TimeBufferType ); - this.values = exports.AnimationUtils.convertArray( values, this.ValueBufferType ); + ExtrudeGeometry.WorldUVGenerator = { - this.setInterpolation( interpolation || this.DefaultInterpolation ); + generateTopUV: function ( geometry, indexA, indexB, indexC ) { - this.validate(); - this.optimize(); + var vertices = geometry.vertices; - } + var a = vertices[ indexA ]; + var b = vertices[ indexB ]; + var c = vertices[ indexC ]; - /** - * - * A Track of vectored keyframe values. - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ + return [ + new Vector2( a.x, a.y ), + new Vector2( b.x, b.y ), + new Vector2( c.x, c.y ) + ]; - function VectorKeyframeTrack( name, times, values, interpolation ) { + }, - KeyframeTrackConstructor.call( this, name, times, values, interpolation ); + generateSideWallUV: function ( geometry, indexA, indexB, indexC, indexD ) { - } + var vertices = geometry.vertices; - VectorKeyframeTrack.prototype = - Object.assign( Object.create( KeyframeTrackPrototype ), { + var a = vertices[ indexA ]; + var b = vertices[ indexB ]; + var c = vertices[ indexC ]; + var d = vertices[ indexD ]; - constructor: VectorKeyframeTrack, + if ( Math.abs( a.y - b.y ) < 0.01 ) { - ValueTypeName: 'vector' + return [ + new Vector2( a.x, 1 - a.z ), + new Vector2( b.x, 1 - b.z ), + new Vector2( c.x, 1 - c.z ), + new Vector2( d.x, 1 - d.z ) + ]; - // ValueBufferType is inherited + } else { - // DefaultInterpolation is inherited + return [ + new Vector2( a.y, 1 - a.z ), + new Vector2( b.y, 1 - b.z ), + new Vector2( c.y, 1 - c.z ), + new Vector2( d.y, 1 - d.z ) + ]; - } ); + } + + } + }; /** - * Spherical linear unit quaternion interpolant. + * @author zz85 / http://www.lab4games.net/zz85/blog + * @author alteredq / http://alteredqualia.com/ * - * @author tschw + * 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 + * } */ - function QuaternionLinearInterpolant( - parameterPositions, sampleValues, sampleSize, resultBuffer ) { + function TextGeometry( text, parameters ) { - Interpolant.call( - this, parameterPositions, sampleValues, sampleSize, resultBuffer ); + parameters = parameters || {}; - } + var font = parameters.font; - QuaternionLinearInterpolant.prototype = - Object.assign( Object.create( Interpolant.prototype ), { + if ( (font && font.isFont) === false ) { - constructor: QuaternionLinearInterpolant, + console.error( 'THREE.TextGeometry: font parameter is not an instance of THREE.Font.' ); + return new Geometry(); - interpolate_: function( i1, t0, t, t1 ) { + } - var result = this.resultBuffer, - values = this.sampleValues, - stride = this.valueSize, + var shapes = font.generateShapes( text, parameters.size, parameters.curveSegments ); - offset = i1 * stride, + // translate parameters to ExtrudeGeometry API - alpha = ( t - t0 ) / ( t1 - t0 ); + parameters.amount = parameters.height !== undefined ? parameters.height : 50; - for ( var end = offset + stride; offset !== end; offset += 4 ) { + // defaults - Quaternion.slerpFlat( result, 0, - values, offset - stride, values, offset, alpha ); + if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10; + if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8; + if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false; - } + ExtrudeGeometry.call( this, shapes, parameters ); - return result; + this.type = 'TextGeometry'; - } + } - } ); + TextGeometry.prototype = Object.create( ExtrudeGeometry.prototype ); + TextGeometry.prototype.constructor = TextGeometry; /** - * - * A Track of quaternion keyframe values. - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw + * @author benaadams / https://twitter.com/ben_a_adams + * based on THREE.SphereGeometry */ - function QuaternionKeyframeTrack( name, times, values, interpolation ) { + function SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { - KeyframeTrackConstructor.call( this, name, times, values, interpolation ); + BufferGeometry.call( this ); - } + this.type = 'SphereBufferGeometry'; - QuaternionKeyframeTrack.prototype = - Object.assign( Object.create( KeyframeTrackPrototype ), { + this.parameters = { + radius: radius, + widthSegments: widthSegments, + heightSegments: heightSegments, + phiStart: phiStart, + phiLength: phiLength, + thetaStart: thetaStart, + thetaLength: thetaLength + }; - constructor: QuaternionKeyframeTrack, + radius = radius || 50; - ValueTypeName: 'quaternion', + widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 ); + heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 ); - // ValueBufferType is inherited + phiStart = phiStart !== undefined ? phiStart : 0; + phiLength = phiLength !== undefined ? phiLength : Math.PI * 2; - DefaultInterpolation: InterpolateLinear, + thetaStart = thetaStart !== undefined ? thetaStart : 0; + thetaLength = thetaLength !== undefined ? thetaLength : Math.PI; - InterpolantFactoryMethodLinear: function( result ) { + var thetaEnd = thetaStart + thetaLength; - return new QuaternionLinearInterpolant( - this.times, this.values, this.getValueSize(), result ); + var vertexCount = ( ( widthSegments + 1 ) * ( heightSegments + 1 ) ); - }, + var positions = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); + var normals = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); + var uvs = new BufferAttribute( new Float32Array( vertexCount * 2 ), 2 ); - InterpolantFactoryMethodSmooth: undefined // not yet implemented + var index = 0, vertices = [], normal = new Vector3(); - } ); + for ( var y = 0; y <= heightSegments; y ++ ) { - /** - * - * A Track of numeric keyframe values. - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ + var verticesRow = []; - function NumberKeyframeTrack( name, times, values, interpolation ) { + var v = y / heightSegments; - KeyframeTrackConstructor.call( this, name, times, values, interpolation ); + for ( var x = 0; x <= widthSegments; x ++ ) { - } + var u = x / widthSegments; - NumberKeyframeTrack.prototype = - Object.assign( Object.create( KeyframeTrackPrototype ), { + 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 ); - constructor: NumberKeyframeTrack, + normal.set( px, py, pz ).normalize(); - ValueTypeName: 'number', + positions.setXYZ( index, px, py, pz ); + normals.setXYZ( index, normal.x, normal.y, normal.z ); + uvs.setXY( index, u, 1 - v ); - // ValueBufferType is inherited + verticesRow.push( index ); - // DefaultInterpolation is inherited + index ++; - } ); + } - /** - * - * A Track that interpolates Strings - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ + vertices.push( verticesRow ); - function StringKeyframeTrack( name, times, values, interpolation ) { + } - KeyframeTrackConstructor.call( this, name, times, values, interpolation ); + var indices = []; - } + for ( var y = 0; y < heightSegments; y ++ ) { - StringKeyframeTrack.prototype = - Object.assign( Object.create( KeyframeTrackPrototype ), { + for ( var x = 0; x < widthSegments; x ++ ) { - constructor: StringKeyframeTrack, + var v1 = vertices[ y ][ x + 1 ]; + var v2 = vertices[ y ][ x ]; + var v3 = vertices[ y + 1 ][ x ]; + var v4 = vertices[ y + 1 ][ x + 1 ]; - ValueTypeName: 'string', - ValueBufferType: Array, + if ( y !== 0 || thetaStart > 0 ) indices.push( v1, v2, v4 ); + if ( y !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( v2, v3, v4 ); - DefaultInterpolation: InterpolateDiscrete, + } - InterpolantFactoryMethodLinear: undefined, + } - InterpolantFactoryMethodSmooth: undefined + this.setIndex( new ( positions.count > 65535 ? Uint32Attribute : Uint16Attribute )( indices, 1 ) ); + this.addAttribute( 'position', positions ); + this.addAttribute( 'normal', normals ); + this.addAttribute( 'uv', uvs ); - } ); + this.boundingSphere = new Sphere( new Vector3(), radius ); + + } + + SphereBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); + SphereBufferGeometry.prototype.constructor = SphereBufferGeometry; /** - * - * A Track of Boolean keyframe values. - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw + * @author mrdoob / http://mrdoob.com/ */ - function BooleanKeyframeTrack( name, times, values ) { + function SphereGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { - KeyframeTrackConstructor.call( this, name, times, values ); + Geometry.call( this ); - } + this.type = 'SphereGeometry'; - BooleanKeyframeTrack.prototype = - Object.assign( Object.create( KeyframeTrackPrototype ), { - - constructor: BooleanKeyframeTrack, - - ValueTypeName: 'bool', - ValueBufferType: Array, - - DefaultInterpolation: InterpolateDiscrete, + this.parameters = { + radius: radius, + widthSegments: widthSegments, + heightSegments: heightSegments, + phiStart: phiStart, + phiLength: phiLength, + thetaStart: thetaStart, + thetaLength: thetaLength + }; - InterpolantFactoryMethodLinear: undefined, - InterpolantFactoryMethodSmooth: undefined + this.fromBufferGeometry( new SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) ); - // Note: Actually this track could have a optimized / compressed - // representation of a single value and a custom interpolant that - // computes "firstValue ^ isOdd( index )". + } - } ); + SphereGeometry.prototype = Object.create( Geometry.prototype ); + SphereGeometry.prototype.constructor = SphereGeometry; /** - * - * A Track of keyframe values that represent color. - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw + * @author Mugen87 / https://github.com/Mugen87 */ - function ColorKeyframeTrack( name, times, values, interpolation ) { - - KeyframeTrackConstructor.call( this, name, times, values, interpolation ); - - } - - ColorKeyframeTrack.prototype = - Object.assign( Object.create( KeyframeTrackPrototype ), { + function RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { - constructor: ColorKeyframeTrack, + BufferGeometry.call( this ); - ValueTypeName: 'color' + this.type = 'RingBufferGeometry'; - // ValueBufferType is inherited + this.parameters = { + innerRadius: innerRadius, + outerRadius: outerRadius, + thetaSegments: thetaSegments, + phiSegments: phiSegments, + thetaStart: thetaStart, + thetaLength: thetaLength + }; - // DefaultInterpolation is inherited + innerRadius = innerRadius || 20; + outerRadius = outerRadius || 50; + thetaStart = thetaStart !== undefined ? thetaStart : 0; + thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; - // Note: Very basic implementation and nothing special yet. - // However, this is the place for color space parameterization. + thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8; + phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 1; - } ); + // these are used to calculate buffer length + var vertexCount = ( thetaSegments + 1 ) * ( phiSegments + 1 ); + var indexCount = thetaSegments * phiSegments * 2 * 3; - /** - * - * A timed sequence of keyframes for a specific property. - * - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - * @author tschw - */ + // buffers + var indices = new BufferAttribute( new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ) , 1 ); + var vertices = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); + var normals = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); + var uvs = new BufferAttribute( new Float32Array( vertexCount * 2 ), 2 ); - function KeyframeTrack( name, times, values, interpolation ) { + // some helper variables + var index = 0, indexOffset = 0, segment; + var radius = innerRadius; + var radiusStep = ( ( outerRadius - innerRadius ) / phiSegments ); + var vertex = new Vector3(); + var uv = new Vector2(); + var j, i; - KeyframeTrackConstructor.apply( this, arguments ); + // generate vertices, normals and uvs - } + // values are generate from the inside of the ring to the outside - KeyframeTrack.prototype = KeyframeTrackPrototype; - KeyframeTrackPrototype.constructor = KeyframeTrack; + for ( j = 0; j <= phiSegments; j ++ ) { - // Static methods: + for ( i = 0; i <= thetaSegments; i ++ ) { - Object.assign( KeyframeTrack, { + segment = thetaStart + i / thetaSegments * thetaLength; - // Serialization (in static context, because of constructor invocation - // and automatic invocation of .toJSON): + // vertex + vertex.x = radius * Math.cos( segment ); + vertex.y = radius * Math.sin( segment ); + vertices.setXYZ( index, vertex.x, vertex.y, vertex.z ); - parse: function( json ) { + // normal + normals.setXYZ( index, 0, 0, 1 ); - if( json.type === undefined ) { + // uv + uv.x = ( vertex.x / outerRadius + 1 ) / 2; + uv.y = ( vertex.y / outerRadius + 1 ) / 2; + uvs.setXY( index, uv.x, uv.y ); - throw new Error( "track type undefined, can not parse" ); + // increase index + index++; } - var trackType = KeyframeTrack._getTrackTypeForValueTypeName( json.type ); + // increase the radius for next row of vertices + radius += radiusStep; - if ( json.times === undefined ) { + } - var times = [], values = []; + // generate indices - exports.AnimationUtils.flattenJSON( json.keys, times, values, 'value' ); + for ( j = 0; j < phiSegments; j ++ ) { - json.times = times; - json.values = values; + var thetaSegmentLevel = j * ( thetaSegments + 1 ); - } + for ( i = 0; i < thetaSegments; i ++ ) { - // derived classes can define a static parse method - if ( trackType.parse !== undefined ) { + segment = i + thetaSegmentLevel; - return trackType.parse( json ); + // indices + var a = segment; + var b = segment + thetaSegments + 1; + var c = segment + thetaSegments + 2; + var d = segment + 1; - } else { + // face one + indices.setX( indexOffset, a ); indexOffset++; + indices.setX( indexOffset, b ); indexOffset++; + indices.setX( indexOffset, c ); indexOffset++; - // by default, we asssume a constructor compatible with the base - return new trackType( - json.name, json.times, json.values, json.interpolation ); + // face two + indices.setX( indexOffset, a ); indexOffset++; + indices.setX( indexOffset, c ); indexOffset++; + indices.setX( indexOffset, d ); indexOffset++; } - }, + } - toJSON: function( track ) { + // build geometry - var trackType = track.constructor; + this.setIndex( indices ); + this.addAttribute( 'position', vertices ); + this.addAttribute( 'normal', normals ); + this.addAttribute( 'uv', uvs ); - var json; + } - // derived classes can define a static toJSON method - if ( trackType.toJSON !== undefined ) { + RingBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); + RingBufferGeometry.prototype.constructor = RingBufferGeometry; - json = trackType.toJSON( track ); + /** + * @author Kaleb Murphy + */ - } else { + function RingGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { - // by default, we assume the data can be serialized as-is - json = { + Geometry.call( this ); - 'name': track.name, - 'times': exports.AnimationUtils.convertArray( track.times, Array ), - 'values': exports.AnimationUtils.convertArray( track.values, Array ) + this.type = 'RingGeometry'; - }; + this.parameters = { + innerRadius: innerRadius, + outerRadius: outerRadius, + thetaSegments: thetaSegments, + phiSegments: phiSegments, + thetaStart: thetaStart, + thetaLength: thetaLength + }; - var interpolation = track.getInterpolation(); + this.fromBufferGeometry( new RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) ); - if ( interpolation !== track.DefaultInterpolation ) { + } - json.interpolation = interpolation; + RingGeometry.prototype = Object.create( Geometry.prototype ); + RingGeometry.prototype.constructor = RingGeometry; - } + /** + * @author mrdoob / http://mrdoob.com/ + * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as + */ - } + function PlaneGeometry( width, height, widthSegments, heightSegments ) { - json.type = track.ValueTypeName; // mandatory + Geometry.call( this ); - return json; + this.type = 'PlaneGeometry'; - }, + this.parameters = { + width: width, + height: height, + widthSegments: widthSegments, + heightSegments: heightSegments + }; - _getTrackTypeForValueTypeName: function( typeName ) { + this.fromBufferGeometry( new PlaneBufferGeometry( width, height, widthSegments, heightSegments ) ); - switch( typeName.toLowerCase() ) { + } - case "scalar": - case "double": - case "float": - case "number": - case "integer": + PlaneGeometry.prototype = Object.create( Geometry.prototype ); + PlaneGeometry.prototype.constructor = PlaneGeometry; - return NumberKeyframeTrack; + /** + * @author Mugen87 / https://github.com/Mugen87 + */ - case "vector": - case "vector2": - case "vector3": - case "vector4": + // 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. - return VectorKeyframeTrack; + function LatheBufferGeometry( points, segments, phiStart, phiLength ) { - case "color": + BufferGeometry.call( this ); - return ColorKeyframeTrack; + this.type = 'LatheBufferGeometry'; - case "quaternion": + this.parameters = { + points: points, + segments: segments, + phiStart: phiStart, + phiLength: phiLength + }; - return QuaternionKeyframeTrack; - - case "bool": - case "boolean": + segments = Math.floor( segments ) || 12; + phiStart = phiStart || 0; + phiLength = phiLength || Math.PI * 2; - return BooleanKeyframeTrack; + // clamp phiLength so it's in range of [ 0, 2PI ] + phiLength = exports.Math.clamp( phiLength, 0, Math.PI * 2 ); - case "string": + // these are used to calculate buffer length + var vertexCount = ( segments + 1 ) * points.length; + var indexCount = segments * points.length * 2 * 3; - return StringKeyframeTrack; + // buffers + var indices = new BufferAttribute( new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ) , 1 ); + var vertices = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); + var uvs = new BufferAttribute( new Float32Array( vertexCount * 2 ), 2 ); - } + // helper variables + var index = 0, indexOffset = 0, base; + var inverseSegments = 1.0 / segments; + var vertex = new Vector3(); + var uv = new Vector2(); + var i, j; - throw new Error( "Unsupported typeName: " + typeName ); + // generate vertices and uvs - } + for ( i = 0; i <= segments; i ++ ) { - } ); + var phi = phiStart + i * inverseSegments * phiLength; - /** - * - * Reusable set of Tracks that represent an animation. - * - * @author Ben Houston / http://clara.io/ - * @author David Sarno / http://lighthaus.us/ - */ + var sin = Math.sin( phi ); + var cos = Math.cos( phi ); - function AnimationClip( name, duration, tracks ) { + for ( j = 0; j <= ( points.length - 1 ); j ++ ) { - this.name = name; - this.tracks = tracks; - this.duration = ( duration !== undefined ) ? duration : -1; + // 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 ); - this.uuid = exports.Math.generateUUID(); + // uv + uv.x = i / segments; + uv.y = j / ( points.length - 1 ); + uvs.setXY( index, uv.x, uv.y ); - // this means it should figure out its duration by scanning the tracks - if ( this.duration < 0 ) { + // increase index + index ++; - this.resetDuration(); + } } - this.optimize(); + // generate indices - } + for ( i = 0; i < segments; i ++ ) { - AnimationClip.prototype = { + for ( j = 0; j < ( points.length - 1 ); j ++ ) { - constructor: AnimationClip, + base = j + i * points.length; - resetDuration: function() { + // indices + var a = base; + var b = base + points.length; + var c = base + points.length + 1; + var d = base + 1; - var tracks = this.tracks, - duration = 0; + // face one + indices.setX( indexOffset, a ); indexOffset++; + indices.setX( indexOffset, b ); indexOffset++; + indices.setX( indexOffset, d ); indexOffset++; - for ( var i = 0, n = tracks.length; i !== n; ++ i ) { + // face two + indices.setX( indexOffset, b ); indexOffset++; + indices.setX( indexOffset, c ); indexOffset++; + indices.setX( indexOffset, d ); indexOffset++; - var track = this.tracks[ i ]; + } - duration = Math.max( - duration, track.times[ track.times.length - 1 ] ); + } - } + // build geometry - this.duration = duration; + this.setIndex( indices ); + this.addAttribute( 'position', vertices ); + this.addAttribute( 'uv', uvs ); - }, + // generate normals - trim: function() { + this.computeVertexNormals(); - for ( var i = 0; i < this.tracks.length; i ++ ) { + // 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). - this.tracks[ i ].trim( 0, this.duration ); + if( phiLength === Math.PI * 2 ) { - } + var normals = this.attributes.normal.array; + var n1 = new Vector3(); + var n2 = new Vector3(); + var n = new Vector3(); - return this; + // this is the buffer offset for the last line of vertices + base = segments * points.length * 3; - }, + for( i = 0, j = 0; i < points.length; i ++, j += 3 ) { - optimize: function() { + // 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 ]; - for ( var i = 0; i < this.tracks.length; i ++ ) { + // 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 ]; - this.tracks[ i ].optimize(); + // average normals + n.addVectors( n1, n2 ).normalize(); - } + // 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; - return this; + } // next row } - }; + } - // Static methods: + LatheBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); + LatheBufferGeometry.prototype.constructor = LatheBufferGeometry; - Object.assign( AnimationClip, { + /** + * @author astrodud / http://astrodud.isgreat.org/ + * @author zz85 / https://github.com/zz85 + * @author bhouston / http://clara.io + */ - parse: function( json ) { + // 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 tracks = [], - jsonTracks = json.tracks, - frameTime = 1.0 / ( json.fps || 1.0 ); + function LatheGeometry( points, segments, phiStart, phiLength ) { - for ( var i = 0, n = jsonTracks.length; i !== n; ++ i ) { + Geometry.call( this ); - tracks.push( KeyframeTrack.parse( jsonTracks[ i ] ).scale( frameTime ) ); + this.type = 'LatheGeometry'; - } + this.parameters = { + points: points, + segments: segments, + phiStart: phiStart, + phiLength: phiLength + }; - return new AnimationClip( json.name, json.duration, tracks ); + this.fromBufferGeometry( new LatheBufferGeometry( points, segments, phiStart, phiLength ) ); + this.mergeVertices(); - }, + } + LatheGeometry.prototype = Object.create( Geometry.prototype ); + LatheGeometry.prototype.constructor = LatheGeometry; - toJSON: function( clip ) { + /** + * @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 + * + * } + **/ - var tracks = [], - clipTracks = clip.tracks; + function ShapeGeometry( shapes, options ) { - var json = { + Geometry.call( this ); - 'name': clip.name, - 'duration': clip.duration, - 'tracks': tracks + this.type = 'ShapeGeometry'; - }; + if ( Array.isArray( shapes ) === false ) shapes = [ shapes ]; - for ( var i = 0, n = clipTracks.length; i !== n; ++ i ) { + this.addShapeList( shapes, options ); - tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) ); + this.computeFaceNormals(); - } + } - return json; + ShapeGeometry.prototype = Object.create( Geometry.prototype ); + ShapeGeometry.prototype.constructor = ShapeGeometry; - }, + /** + * Add an array of shapes to THREE.ShapeGeometry. + */ + ShapeGeometry.prototype.addShapeList = function ( shapes, options ) { + for ( var i = 0, l = shapes.length; i < l; i ++ ) { - CreateFromMorphTargetSequence: function( name, morphTargetSequence, fps, noLoop ) { + this.addShape( shapes[ i ], options ); - var numMorphTargets = morphTargetSequence.length; - var tracks = []; + } - for ( var i = 0; i < numMorphTargets; i ++ ) { + return this; - var times = []; - var values = []; + }; - times.push( - ( i + numMorphTargets - 1 ) % numMorphTargets, - i, - ( i + 1 ) % numMorphTargets ); + /** + * Adds a shape to THREE.ShapeGeometry, based on THREE.ExtrudeGeometry. + */ + ShapeGeometry.prototype.addShape = function ( shape, options ) { - values.push( 0, 1, 0 ); + if ( options === undefined ) options = {}; + var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; - var order = exports.AnimationUtils.getKeyframeOrder( times ); - times = exports.AnimationUtils.sortedArray( times, 1, order ); - values = exports.AnimationUtils.sortedArray( values, 1, order ); + var material = options.material; + var uvgen = options.UVGenerator === undefined ? ExtrudeGeometry.WorldUVGenerator : options.UVGenerator; - // if there is a key at the first frame, duplicate it as the - // last frame as well for perfect loop. - if ( ! noLoop && times[ 0 ] === 0 ) { + // - times.push( numMorphTargets ); - values.push( values[ 0 ] ); + var i, l, hole; - } + var shapesOffset = this.vertices.length; + var shapePoints = shape.extractPoints( curveSegments ); - tracks.push( - new NumberKeyframeTrack( - '.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']', - times, values - ).scale( 1.0 / fps ) ); - } - - return new AnimationClip( name, -1, tracks ); - - }, + var vertices = shapePoints.shape; + var holes = shapePoints.holes; - findByName: function( objectOrClipArray, name ) { + var reverse = ! exports.ShapeUtils.isClockWise( vertices ); - var clipArray = objectOrClipArray; + if ( reverse ) { - if ( ! Array.isArray( objectOrClipArray ) ) { + vertices = vertices.reverse(); - var o = objectOrClipArray; - clipArray = o.geometry && o.geometry.animations || o.animations; + // Maybe we should also check if holes are in the opposite direction, just to be safe... - } + for ( i = 0, l = holes.length; i < l; i ++ ) { - for ( var i = 0; i < clipArray.length; i ++ ) { + hole = holes[ i ]; - if ( clipArray[ i ].name === name ) { + if ( exports.ShapeUtils.isClockWise( hole ) ) { - return clipArray[ i ]; + holes[ i ] = hole.reverse(); } + } - return null; + reverse = false; - }, + } - CreateClipsFromMorphTargetSequences: function( morphTargets, fps, noLoop ) { + var faces = exports.ShapeUtils.triangulateShape( vertices, holes ); - var animationToMorphTargets = {}; + // Vertices - // tested with https://regex101.com/ on trick sequences - // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 - var pattern = /^([\w-]*?)([\d]+)$/; + for ( i = 0, l = holes.length; i < l; i ++ ) { - // sort morph target names into animation groups based - // patterns like Walk_001, Walk_002, Run_001, Run_002 - for ( var i = 0, il = morphTargets.length; i < il; i ++ ) { + hole = holes[ i ]; + vertices = vertices.concat( hole ); - var morphTarget = morphTargets[ i ]; - var parts = morphTarget.name.match( pattern ); + } - if ( parts && parts.length > 1 ) { + // - var name = parts[ 1 ]; + var vert, vlen = vertices.length; + var face, flen = faces.length; - var animationMorphTargets = animationToMorphTargets[ name ]; - if ( ! animationMorphTargets ) { + for ( i = 0; i < vlen; i ++ ) { - animationToMorphTargets[ name ] = animationMorphTargets = []; + vert = vertices[ i ]; - } + this.vertices.push( new Vector3( vert.x, vert.y, 0 ) ); - animationMorphTargets.push( morphTarget ); + } - } + for ( i = 0; i < flen; i ++ ) { - } + face = faces[ i ]; - var clips = []; + var a = face[ 0 ] + shapesOffset; + var b = face[ 1 ] + shapesOffset; + var c = face[ 2 ] + shapesOffset; - for ( var name in animationToMorphTargets ) { + this.faces.push( new Face3( a, b, c, null, null, material ) ); + this.faceVertexUvs[ 0 ].push( uvgen.generateTopUV( this, a, b, c ) ); - clips.push( AnimationClip.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) ); + } - } + }; - return clips; + /** + * @author WestLangley / http://github.com/WestLangley + */ - }, + function EdgesGeometry( geometry, thresholdAngle ) { - // parse the animation.hierarchy format - parseAnimation: function( animation, bones ) { + BufferGeometry.call( this ); - if ( ! animation ) { + thresholdAngle = ( thresholdAngle !== undefined ) ? thresholdAngle : 1; - console.error( " no animation in JSONLoader data" ); - return null; + var thresholdDot = Math.cos( exports.Math.DEG2RAD * thresholdAngle ); - } + var edge = [ 0, 0 ], hash = {}; - var addNonemptyTrack = function( - trackType, trackName, animationKeys, propertyName, destTracks ) { + function sortFunction( a, b ) { - // only return track if there are actually keys. - if ( animationKeys.length !== 0 ) { + return a - b; - var times = []; - var values = []; + } - exports.AnimationUtils.flattenJSON( - animationKeys, times, values, propertyName ); + var keys = [ 'a', 'b', 'c' ]; - // empty keys are filtered out, so check again - if ( times.length !== 0 ) { + var geometry2; - destTracks.push( new trackType( trackName, times, values ) ); + if ( (geometry && geometry.isBufferGeometry) ) { - } + geometry2 = new Geometry(); + geometry2.fromBufferGeometry( geometry ); - } + } else { - }; + geometry2 = geometry.clone(); - var tracks = []; + } - var clipName = animation.name || 'default'; - // automatic length determination in AnimationClip. - var duration = animation.length || -1; - var fps = animation.fps || 30; + geometry2.mergeVertices(); + geometry2.computeFaceNormals(); - var hierarchyTracks = animation.hierarchy || []; + var vertices = geometry2.vertices; + var faces = geometry2.faces; - for ( var h = 0; h < hierarchyTracks.length; h ++ ) { + for ( var i = 0, l = faces.length; i < l; i ++ ) { - var animationKeys = hierarchyTracks[ h ].keys; + var face = faces[ i ]; - // skip empty tracks - if ( ! animationKeys || animationKeys.length === 0 ) continue; + for ( var j = 0; j < 3; j ++ ) { - // process morph targets in a way exactly compatible - // with AnimationHandler.init( animation ) - if ( animationKeys[0].morphTargets ) { + edge[ 0 ] = face[ keys[ j ] ]; + edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; + edge.sort( sortFunction ); - // figure out all morph targets used in this track - var morphTargetNames = {}; - for ( var k = 0; k < animationKeys.length; k ++ ) { + var key = edge.toString(); - if ( animationKeys[k].morphTargets ) { + if ( hash[ key ] === undefined ) { - for ( var m = 0; m < animationKeys[k].morphTargets.length; m ++ ) { + hash[ key ] = { vert1: edge[ 0 ], vert2: edge[ 1 ], face1: i, face2: undefined }; - morphTargetNames[ animationKeys[k].morphTargets[m] ] = -1; - } + } else { - } + hash[ key ].face2 = i; - } + } - // create a track for each morph target with all zero - // morphTargetInfluences except for the keys in which - // the morphTarget is named. - for ( var morphTargetName in morphTargetNames ) { + } - var times = []; - var values = []; + } - for ( var m = 0; - m !== animationKeys[k].morphTargets.length; ++ m ) { + var coords = []; - var animationKey = animationKeys[k]; + for ( var key in hash ) { - times.push( animationKey.time ); - values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 ); + var h = hash[ key ]; - } + if ( h.face2 === undefined || faces[ h.face1 ].normal.dot( faces[ h.face2 ].normal ) <= thresholdDot ) { - tracks.push( new NumberKeyframeTrack( - '.morphTargetInfluence[' + morphTargetName + ']', times, values ) ); + var vertex = vertices[ h.vert1 ]; + coords.push( vertex.x ); + coords.push( vertex.y ); + coords.push( vertex.z ); - } + vertex = vertices[ h.vert2 ]; + coords.push( vertex.x ); + coords.push( vertex.y ); + coords.push( vertex.z ); - duration = morphTargetNames.length * ( fps || 1.0 ); + } - } else { - // ...assume skeletal animation + } - var boneName = '.bones[' + bones[ h ].name + ']'; + this.addAttribute( 'position', new BufferAttribute( new Float32Array( coords ), 3 ) ); - addNonemptyTrack( - VectorKeyframeTrack, boneName + '.position', - animationKeys, 'pos', tracks ); + } - addNonemptyTrack( - QuaternionKeyframeTrack, boneName + '.quaternion', - animationKeys, 'rot', tracks ); + EdgesGeometry.prototype = Object.create( BufferGeometry.prototype ); + EdgesGeometry.prototype.constructor = EdgesGeometry; - addNonemptyTrack( - VectorKeyframeTrack, boneName + '.scale', - animationKeys, 'scl', tracks ); + /** + * @author Mugen87 / https://github.com/Mugen87 + */ - } + function CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { - } + BufferGeometry.call( this ); - if ( tracks.length === 0 ) { + this.type = 'CylinderBufferGeometry'; - return null; + this.parameters = { + radiusTop: radiusTop, + radiusBottom: radiusBottom, + height: height, + radialSegments: radialSegments, + heightSegments: heightSegments, + openEnded: openEnded, + thetaStart: thetaStart, + thetaLength: thetaLength + }; - } + var scope = this; - var clip = new AnimationClip( clipName, duration, tracks ); + radiusTop = radiusTop !== undefined ? radiusTop : 20; + radiusBottom = radiusBottom !== undefined ? radiusBottom : 20; + height = height !== undefined ? height : 100; - return clip; + 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 - /** - * @author mrdoob / http://mrdoob.com/ - */ + var nbCap = 0; - function MaterialLoader( manager ) { + if ( openEnded === false ) { - this.manager = ( manager !== undefined ) ? manager : exports.DefaultLoadingManager; - this.textures = {}; + if ( radiusTop > 0 ) nbCap ++; + if ( radiusBottom > 0 ) nbCap ++; - } + } - Object.assign( MaterialLoader.prototype, { + var vertexCount = calculateVertexCount(); + var indexCount = calculateIndexCount(); - load: function ( url, onLoad, onProgress, onError ) { + // buffers - var scope = this; - - var loader = new XHRLoader( scope.manager ); - loader.load( url, function ( text ) { + var indices = new BufferAttribute( new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ), 1 ); + var vertices = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); + var normals = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); + var uvs = new BufferAttribute( new Float32Array( vertexCount * 2 ), 2 ); - onLoad( scope.parse( JSON.parse( text ) ) ); + // helper variables - }, onProgress, onError ); + var index = 0, + indexOffset = 0, + indexArray = [], + halfHeight = height / 2; - }, + // group variables + var groupStart = 0; - setTextures: function ( value ) { + // generate geometry - this.textures = value; + generateTorso(); - }, + if ( openEnded === false ) { - parse: function ( json ) { + if ( radiusTop > 0 ) generateCap( true ); + if ( radiusBottom > 0 ) generateCap( false ); - var textures = this.textures; + } - function getTexture( name ) { + // build geometry - if ( textures[ name ] === undefined ) { + this.setIndex( indices ); + this.addAttribute( 'position', vertices ); + this.addAttribute( 'normal', normals ); + this.addAttribute( 'uv', uvs ); - console.warn( 'THREE.MaterialLoader: Undefined texture', name ); + // helper functions - } + function calculateVertexCount() { - return textures[ name ]; + var count = ( radialSegments + 1 ) * ( heightSegments + 1 ); - } + if ( openEnded === false ) { - var material = new Materials[ json.type ](); + count += ( ( radialSegments + 1 ) * nbCap ) + ( radialSegments * nbCap ); - 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.fog !== undefined ) material.fog = json.fog; - 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; - if ( json.wireframeLinecap !== undefined ) material.wireframeLinecap = json.wireframeLinecap; - if ( json.wireframeLinejoin !== undefined ) material.wireframeLinejoin = json.wireframeLinejoin; - if ( json.skinning !== undefined ) material.skinning = json.skinning; - if ( json.morphTargets !== undefined ) material.morphTargets = json.morphTargets; + } - // for PointsMaterial + return count; - if ( json.size !== undefined ) material.size = json.size; - if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation; + } - // maps + function calculateIndexCount() { - if ( json.map !== undefined ) material.map = getTexture( json.map ); + var count = radialSegments * heightSegments * 2 * 3; - if ( json.alphaMap !== undefined ) { + if ( openEnded === false ) { - material.alphaMap = getTexture( json.alphaMap ); - material.transparent = true; + count += radialSegments * nbCap * 3; } - if ( json.bumpMap !== undefined ) material.bumpMap = getTexture( json.bumpMap ); - if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale; - - if ( json.normalMap !== undefined ) material.normalMap = getTexture( json.normalMap ); - if ( json.normalScale !== undefined ) { + return count; - var normalScale = json.normalScale; + } - if ( Array.isArray( normalScale ) === false ) { + function generateTorso() { - // Blender exporter used to export a scalar. See #7459 + var x, y; + var normal = new Vector3(); + var vertex = new Vector3(); - normalScale = [ normalScale, normalScale ]; + var groupCount = 0; - } + // this will be used to calculate the normal + var slope = ( radiusBottom - radiusTop ) / height; - material.normalScale = new Vector2().fromArray( normalScale ); + // generate vertices, normals and uvs - } + for ( y = 0; y <= heightSegments; y ++ ) { - if ( json.displacementMap !== undefined ) material.displacementMap = getTexture( json.displacementMap ); - if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale; - if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias; + var indexRow = []; - if ( json.roughnessMap !== undefined ) material.roughnessMap = getTexture( json.roughnessMap ); - if ( json.metalnessMap !== undefined ) material.metalnessMap = getTexture( json.metalnessMap ); + var v = y / heightSegments; - if ( json.emissiveMap !== undefined ) material.emissiveMap = getTexture( json.emissiveMap ); - if ( json.emissiveIntensity !== undefined ) material.emissiveIntensity = json.emissiveIntensity; + // calculate the radius of the current row + var radius = v * ( radiusBottom - radiusTop ) + radiusTop; - if ( json.specularMap !== undefined ) material.specularMap = getTexture( json.specularMap ); + for ( x = 0; x <= radialSegments; x ++ ) { - if ( json.envMap !== undefined ) material.envMap = getTexture( json.envMap ); + var u = x / radialSegments; - if ( json.reflectivity !== undefined ) material.reflectivity = json.reflectivity; + var theta = u * thetaLength + thetaStart; - if ( json.lightMap !== undefined ) material.lightMap = getTexture( json.lightMap ); - if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity; + var sinTheta = Math.sin( theta ); + var cosTheta = Math.cos( theta ); - if ( json.aoMap !== undefined ) material.aoMap = getTexture( json.aoMap ); - if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity; + // vertex + vertex.x = radius * sinTheta; + vertex.y = - v * height + halfHeight; + vertex.z = radius * cosTheta; + vertices.setXYZ( index, vertex.x, vertex.y, vertex.z ); - // MultiMaterial + // normal + normal.set( sinTheta, slope, cosTheta ).normalize(); + normals.setXYZ( index, normal.x, normal.y, normal.z ); - if ( json.materials !== undefined ) { + // uv + uvs.setXY( index, u, 1 - v ); - for ( var i = 0, l = json.materials.length; i < l; i ++ ) { + // save index of vertex in respective row + indexRow.push( index ); - material.materials.push( this.parse( json.materials[ i ] ) ); + // increase index + index ++; } + // now save vertices of the row in our index array + indexArray.push( indexRow ); + } - return material; + // generate indices - } + for ( x = 0; x < radialSegments; x ++ ) { - } ); + for ( y = 0; y < heightSegments; y ++ ) { - /** - * @author mrdoob / http://mrdoob.com/ - */ + // 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 ]; - function BufferGeometryLoader( manager ) { + // face one + indices.setX( indexOffset, i1 ); indexOffset ++; + indices.setX( indexOffset, i2 ); indexOffset ++; + indices.setX( indexOffset, i4 ); indexOffset ++; - this.manager = ( manager !== undefined ) ? manager : exports.DefaultLoadingManager; + // face two + indices.setX( indexOffset, i2 ); indexOffset ++; + indices.setX( indexOffset, i3 ); indexOffset ++; + indices.setX( indexOffset, i4 ); indexOffset ++; - } + // update counters + groupCount += 6; - Object.assign( BufferGeometryLoader.prototype, { + } - load: function ( url, onLoad, onProgress, onError ) { + } - var scope = this; + // add a group to the geometry. this will ensure multi material support + scope.addGroup( groupStart, groupCount, 0 ); - var loader = new XHRLoader( scope.manager ); - loader.load( url, function ( text ) { + // calculate new start value for groups + groupStart += groupCount; - onLoad( scope.parse( JSON.parse( text ) ) ); + } - }, onProgress, onError ); + function generateCap( top ) { - }, + var x, centerIndexStart, centerIndexEnd; - parse: function ( json ) { + var uv = new Vector2(); + var vertex = new Vector3(); - var geometry = new BufferGeometry(); + var groupCount = 0; - var index = json.data.index; + var radius = ( top === true ) ? radiusTop : radiusBottom; + var sign = ( top === true ) ? 1 : - 1; - var TYPED_ARRAYS = { - 'Int8Array': Int8Array, - 'Uint8Array': Uint8Array, - 'Uint8ClampedArray': Uint8ClampedArray, - 'Int16Array': Int16Array, - 'Uint16Array': Uint16Array, - 'Int32Array': Int32Array, - 'Uint32Array': Uint32Array, - 'Float32Array': Float32Array, - 'Float64Array': Float64Array - }; + // save the index of the first center vertex + centerIndexStart = index; - if ( index !== undefined ) { + // 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 - var typedArray = new TYPED_ARRAYS[ index.type ]( index.array ); - geometry.setIndex( new BufferAttribute( typedArray, 1 ) ); + for ( x = 1; x <= radialSegments; x ++ ) { - } + // vertex + vertices.setXYZ( index, 0, halfHeight * sign, 0 ); - var attributes = json.data.attributes; + // normal + normals.setXYZ( index, 0, sign, 0 ); - for ( var key in attributes ) { + // uv + uv.x = 0.5; + uv.y = 0.5; - var attribute = attributes[ key ]; - var typedArray = new TYPED_ARRAYS[ attribute.type ]( attribute.array ); + uvs.setXY( index, uv.x, uv.y ); - geometry.addAttribute( key, new BufferAttribute( typedArray, attribute.itemSize, attribute.normalized ) ); + // increase index + index ++; } - var groups = json.data.groups || json.data.drawcalls || json.data.offsets; - - if ( groups !== undefined ) { + // save the index of the last center vertex + centerIndexEnd = index; - for ( var i = 0, n = groups.length; i !== n; ++ i ) { + // now we generate the surrounding vertices, normals and uvs - var group = groups[ i ]; + for ( x = 0; x <= radialSegments; x ++ ) { - geometry.addGroup( group.start, group.count, group.materialIndex ); + var u = x / radialSegments; + var theta = u * thetaLength + thetaStart; - } + var cosTheta = Math.cos( theta ); + var sinTheta = Math.sin( theta ); + + // vertex + vertex.x = radius * sinTheta; + vertex.y = halfHeight * sign; + vertex.z = radius * cosTheta; + vertices.setXYZ( index, vertex.x, vertex.y, vertex.z ); + + // normal + normals.setXYZ( index, 0, sign, 0 ); + + // uv + uv.x = ( cosTheta * 0.5 ) + 0.5; + uv.y = ( sinTheta * 0.5 * sign ) + 0.5; + uvs.setXY( index, uv.x, uv.y ); + + // increase index + index ++; } - var boundingSphere = json.data.boundingSphere; + // generate indices - if ( boundingSphere !== undefined ) { + for ( x = 0; x < radialSegments; x ++ ) { - var center = new Vector3(); + var c = centerIndexStart + x; + var i = centerIndexEnd + x; - if ( boundingSphere.center !== undefined ) { + if ( top === true ) { - center.fromArray( boundingSphere.center ); + // face top + indices.setX( indexOffset, i ); indexOffset ++; + indices.setX( indexOffset, i + 1 ); indexOffset ++; + indices.setX( indexOffset, c ); indexOffset ++; + + } else { + + // face bottom + indices.setX( indexOffset, i + 1 ); indexOffset ++; + indices.setX( indexOffset, i ); indexOffset ++; + indices.setX( indexOffset, c ); indexOffset ++; } - geometry.boundingSphere = new Sphere( center, boundingSphere.radius ); + // update counters + groupCount += 3; } - return geometry; + // 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; } - } ); + } + + CylinderBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); + CylinderBufferGeometry.prototype.constructor = CylinderBufferGeometry; /** - * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ */ - function Loader() { + function CylinderGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { - this.onLoadStart = function () {}; - this.onLoadProgress = function () {}; - this.onLoadComplete = function () {}; + Geometry.call( this ); - } + this.type = 'CylinderGeometry'; - Loader.prototype = { + this.parameters = { + radiusTop: radiusTop, + radiusBottom: radiusBottom, + height: height, + radialSegments: radialSegments, + heightSegments: heightSegments, + openEnded: openEnded, + thetaStart: thetaStart, + thetaLength: thetaLength + }; - constructor: Loader, + this.fromBufferGeometry( new CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) ); + this.mergeVertices(); - crossOrigin: undefined, + } - extractUrlBase: function ( url ) { + CylinderGeometry.prototype = Object.create( Geometry.prototype ); + CylinderGeometry.prototype.constructor = CylinderGeometry; - var parts = url.split( '/' ); + /** + * @author abelnation / http://github.com/abelnation + */ - if ( parts.length === 1 ) return './'; + function ConeGeometry( + radius, height, + radialSegments, heightSegments, + openEnded, thetaStart, thetaLength ) { - parts.pop(); + CylinderGeometry.call( this, + 0, radius, height, + radialSegments, heightSegments, + openEnded, thetaStart, thetaLength ); - return parts.join( '/' ) + '/'; + this.type = 'ConeGeometry'; - }, + this.parameters = { + radius: radius, + height: height, + radialSegments: radialSegments, + heightSegments: heightSegments, + openEnded: openEnded, + thetaStart: thetaStart, + thetaLength: thetaLength + }; - initMaterials: function ( materials, texturePath, crossOrigin ) { + } - var array = []; + ConeGeometry.prototype = Object.create( CylinderGeometry.prototype ); + ConeGeometry.prototype.constructor = ConeGeometry; - for ( var i = 0; i < materials.length; ++ i ) { + /* + * @author: abelnation / http://github.com/abelnation + */ - array[ i ] = this.createMaterial( materials[ i ], texturePath, crossOrigin ); + function ConeBufferGeometry( + radius, height, + radialSegments, heightSegments, + openEnded, thetaStart, thetaLength ) { - } + CylinderBufferGeometry.call( this, + 0, radius, height, + radialSegments, heightSegments, + openEnded, thetaStart, thetaLength ); - return array; + this.type = 'ConeBufferGeometry'; - }, + this.parameters = { + radius: radius, + height: height, + radialSegments: radialSegments, + heightSegments: heightSegments, + thetaStart: thetaStart, + thetaLength: thetaLength + }; - createMaterial: ( function () { + } - var color, textureLoader, materialLoader; + ConeBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); + ConeBufferGeometry.prototype.constructor = ConeBufferGeometry; - return function createMaterial( m, texturePath, crossOrigin ) { + /** + * @author benaadams / https://twitter.com/ben_a_adams + */ - if ( color === undefined ) color = new Color(); - if ( textureLoader === undefined ) textureLoader = new TextureLoader(); - if ( materialLoader === undefined ) materialLoader = new MaterialLoader(); + function CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) { - // convert from old material format + BufferGeometry.call( this ); - var textures = {}; + this.type = 'CircleBufferGeometry'; - function loadTexture( path, repeat, offset, wrap, anisotropy ) { + this.parameters = { + radius: radius, + segments: segments, + thetaStart: thetaStart, + thetaLength: thetaLength + }; - var fullPath = texturePath + path; - var loader = Loader.Handlers.get( fullPath ); + radius = radius || 50; + segments = segments !== undefined ? Math.max( 3, segments ) : 8; - var texture; + thetaStart = thetaStart !== undefined ? thetaStart : 0; + thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; - if ( loader !== null ) { + var vertices = segments + 2; - texture = loader.load( fullPath ); + var positions = new Float32Array( vertices * 3 ); + var normals = new Float32Array( vertices * 3 ); + var uvs = new Float32Array( vertices * 2 ); - } else { + // center data is already zero, but need to set a few extras + normals[ 2 ] = 1.0; + uvs[ 0 ] = 0.5; + uvs[ 1 ] = 0.5; - textureLoader.setCrossOrigin( crossOrigin ); - texture = textureLoader.load( fullPath ); + for ( var s = 0, i = 3, ii = 2 ; s <= segments; s ++, i += 3, ii += 2 ) { - } + var segment = thetaStart + s / segments * thetaLength; - if ( repeat !== undefined ) { + positions[ i ] = radius * Math.cos( segment ); + positions[ i + 1 ] = radius * Math.sin( segment ); - texture.repeat.fromArray( repeat ); + normals[ i + 2 ] = 1; // normal z - if ( repeat[ 0 ] !== 1 ) texture.wrapS = RepeatWrapping; - if ( repeat[ 1 ] !== 1 ) texture.wrapT = RepeatWrapping; + uvs[ ii ] = ( positions[ i ] / radius + 1 ) / 2; + uvs[ ii + 1 ] = ( positions[ i + 1 ] / radius + 1 ) / 2; - } + } - if ( offset !== undefined ) { + var indices = []; - texture.offset.fromArray( offset ); + for ( var i = 1; i <= segments; i ++ ) { - } + indices.push( i, i + 1, 0 ); - if ( wrap !== undefined ) { + } - if ( wrap[ 0 ] === 'repeat' ) texture.wrapS = RepeatWrapping; - if ( wrap[ 0 ] === 'mirror' ) texture.wrapS = MirroredRepeatWrapping; + this.setIndex( new BufferAttribute( new Uint16Array( indices ), 1 ) ); + this.addAttribute( 'position', new BufferAttribute( positions, 3 ) ); + this.addAttribute( 'normal', new BufferAttribute( normals, 3 ) ); + this.addAttribute( 'uv', new BufferAttribute( uvs, 2 ) ); - if ( wrap[ 1 ] === 'repeat' ) texture.wrapT = RepeatWrapping; - if ( wrap[ 1 ] === 'mirror' ) texture.wrapT = MirroredRepeatWrapping; + this.boundingSphere = new Sphere( new Vector3(), radius ); - } + } - if ( anisotropy !== undefined ) { + CircleBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); + CircleBufferGeometry.prototype.constructor = CircleBufferGeometry; - texture.anisotropy = anisotropy; + /** + * @author hughes + */ - } + function CircleGeometry( radius, segments, thetaStart, thetaLength ) { - var uuid = exports.Math.generateUUID(); + Geometry.call( this ); - textures[ uuid ] = texture; + this.type = 'CircleGeometry'; - return uuid; + this.parameters = { + radius: radius, + segments: segments, + thetaStart: thetaStart, + thetaLength: thetaLength + }; - } + this.fromBufferGeometry( new CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) ); - // + } - var json = { - uuid: exports.Math.generateUUID(), - type: 'MeshLambertMaterial' - }; + CircleGeometry.prototype = Object.create( Geometry.prototype ); + CircleGeometry.prototype.constructor = CircleGeometry; - for ( var name in m ) { + /** + * @author mrdoob / http://mrdoob.com/ + * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Cube.as + */ - var value = m[ name ]; + function BoxGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) { - switch ( name ) { - case 'DbgColor': - case 'DbgIndex': - case 'opticalDensity': - case 'illumination': - break; - case 'DbgName': - json.name = value; - break; - case 'blending': - json.blending = BlendingMode[ 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 = BackSide; - break; - case 'doubleSided': - json.side = 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 = VertexColors; - if ( value === 'face' ) json.vertexColors = FaceColors; - break; - default: - console.error( 'THREE.Loader.createMaterial: Unsupported', name, value ); - break; - } + Geometry.call( this ); + this.type = 'BoxGeometry'; + + this.parameters = { + width: width, + height: height, + depth: depth, + widthSegments: widthSegments, + heightSegments: heightSegments, + depthSegments: depthSegments + }; + + this.fromBufferGeometry( new BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) ); + this.mergeVertices(); + + } + + BoxGeometry.prototype = Object.create( Geometry.prototype ); + BoxGeometry.prototype.constructor = BoxGeometry; + + + + var Geometries = Object.freeze({ + WireframeGeometry: WireframeGeometry, + ParametricGeometry: ParametricGeometry, + TetrahedronGeometry: TetrahedronGeometry, + OctahedronGeometry: OctahedronGeometry, + IcosahedronGeometry: IcosahedronGeometry, + DodecahedronGeometry: DodecahedronGeometry, + PolyhedronGeometry: PolyhedronGeometry, + TubeGeometry: TubeGeometry, + TorusKnotGeometry: TorusKnotGeometry, + TorusKnotBufferGeometry: TorusKnotBufferGeometry, + TorusGeometry: TorusGeometry, + TorusBufferGeometry: TorusBufferGeometry, + TextGeometry: TextGeometry, + SphereBufferGeometry: SphereBufferGeometry, + SphereGeometry: SphereGeometry, + RingGeometry: RingGeometry, + RingBufferGeometry: RingBufferGeometry, + PlaneBufferGeometry: PlaneBufferGeometry, + PlaneGeometry: PlaneGeometry, + LatheGeometry: LatheGeometry, + LatheBufferGeometry: LatheBufferGeometry, + ShapeGeometry: ShapeGeometry, + ExtrudeGeometry: ExtrudeGeometry, + EdgesGeometry: EdgesGeometry, + ConeGeometry: ConeGeometry, + ConeBufferGeometry: ConeBufferGeometry, + CylinderGeometry: CylinderGeometry, + CylinderBufferGeometry: CylinderBufferGeometry, + CircleBufferGeometry: CircleBufferGeometry, + CircleGeometry: CircleGeometry, + BoxBufferGeometry: BoxBufferGeometry, + BoxGeometry: BoxGeometry + }); + + /** + * @author mrdoob / http://mrdoob.com/ + */ + + function ShadowMaterial() { + + ShaderMaterial.call( this, { + uniforms: exports.UniformsUtils.merge( [ + UniformsLib[ "lights" ], + { + opacity: { value: 1.0 } } + ] ), + vertexShader: ShaderChunk[ 'shadow_vert' ], + fragmentShader: ShaderChunk[ 'shadow_frag' ] + } ); - if ( json.type === 'MeshBasicMaterial' ) delete json.emissive; - if ( json.type !== 'MeshPhongMaterial' ) delete json.specular; + this.lights = true; + this.transparent = true; - if ( json.opacity < 1 ) json.transparent = true; + Object.defineProperties( this, { + opacity: { + enumerable: true, + get: function () { + return this.uniforms.opacity.value; + }, + set: function ( value ) { + this.uniforms.opacity.value = value; + } + } + } ); - materialLoader.setTextures( textures ); + } - return materialLoader.parse( json ); + ShadowMaterial.prototype = Object.create( ShaderMaterial.prototype ); + ShadowMaterial.prototype.constructor = ShadowMaterial; - }; + ShadowMaterial.prototype.isShadowMaterial = true; - } )() + /** + * @author mrdoob / http://mrdoob.com/ + */ - }; + function RawShaderMaterial( parameters ) { - Loader.Handlers = { + ShaderMaterial.call( this, parameters ); - handlers: [], + this.type = 'RawShaderMaterial'; - add: function ( regex, loader ) { + } - this.handlers.push( regex, loader ); + RawShaderMaterial.prototype = Object.create( ShaderMaterial.prototype ); + RawShaderMaterial.prototype.constructor = RawShaderMaterial; - }, + RawShaderMaterial.prototype.isRawShaderMaterial = true; - get: function ( file ) { + /** + * @author mrdoob / http://mrdoob.com/ + */ - var handlers = this.handlers; + function MultiMaterial( materials ) { - for ( var i = 0, l = handlers.length; i < l; i += 2 ) { + this.uuid = exports.Math.generateUUID(); - var regex = handlers[ i ]; - var loader = handlers[ i + 1 ]; + this.type = 'MultiMaterial'; - if ( regex.test( file ) ) { + this.materials = materials instanceof Array ? materials : []; - return loader; + this.visible = true; - } + } + + MultiMaterial.prototype = { + + constructor: MultiMaterial, + + isMultiMaterial: true, + + toJSON: function ( meta ) { + + var output = { + metadata: { + version: 4.2, + type: 'material', + generator: 'MaterialExporter' + }, + uuid: this.uuid, + type: this.type, + materials: [] + }; + + var materials = this.materials; + + for ( var i = 0, l = materials.length; i < l; i ++ ) { + + var material = materials[ i ].toJSON( meta ); + delete material.metadata; + + output.materials.push( material ); } - return null; + output.visible = this.visible; + + return output; + + }, + + clone: function () { + + var material = new this.constructor(); + + for ( var i = 0; i < this.materials.length; i ++ ) { + + material.materials.push( this.materials[ i ].clone() ); + + } + + material.visible = this.visible; + + return material; } }; /** - * @author mrdoob / http://mrdoob.com/ - * @author alteredq / http://alteredqualia.com/ + * @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: + * } */ - function JSONLoader( manager ) { + function MeshStandardMaterial( parameters ) { - if ( typeof manager === 'boolean' ) { + Material.call( this ); - console.warn( 'THREE.JSONLoader: showStatus parameter has been removed from constructor.' ); - manager = undefined; + this.defines = { 'STANDARD': '' }; - } + this.type = 'MeshStandardMaterial'; - this.manager = ( manager !== undefined ) ? manager : exports.DefaultLoadingManager; + this.color = new Color( 0xffffff ); // diffuse + this.roughness = 0.5; + this.metalness = 0.5; - this.withCredentials = false; + this.map = null; - } + this.lightMap = null; + this.lightMapIntensity = 1.0; - Object.assign( JSONLoader.prototype, { + this.aoMap = null; + this.aoMapIntensity = 1.0; - load: function( url, onLoad, onProgress, onError ) { + this.emissive = new Color( 0x000000 ); + this.emissiveIntensity = 1.0; + this.emissiveMap = null; - var scope = this; + this.bumpMap = null; + this.bumpScale = 1; - var texturePath = this.texturePath && ( typeof this.texturePath === "string" ) ? this.texturePath : Loader.prototype.extractUrlBase( url ); + this.normalMap = null; + this.normalScale = new Vector2( 1, 1 ); - var loader = new XHRLoader( this.manager ); - loader.setWithCredentials( this.withCredentials ); - loader.load( url, function ( text ) { + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - var json = JSON.parse( text ); - var metadata = json.metadata; + this.roughnessMap = null; - if ( metadata !== undefined ) { + this.metalnessMap = null; - var type = metadata.type; + this.alphaMap = null; - if ( type !== undefined ) { + this.envMap = null; + this.envMapIntensity = 1.0; - if ( type.toLowerCase() === 'object' ) { + this.refractionRatio = 0.98; - console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.ObjectLoader instead.' ); - return; + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; - } + this.skinning = false; + this.morphTargets = false; + this.morphNormals = false; - if ( type.toLowerCase() === 'scene' ) { + this.setValues( parameters ); - console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.SceneLoader instead.' ); - return; + } - } + MeshStandardMaterial.prototype = Object.create( Material.prototype ); + MeshStandardMaterial.prototype.constructor = MeshStandardMaterial; + + MeshStandardMaterial.prototype.isMeshStandardMaterial = true; + + MeshStandardMaterial.prototype.copy = function ( source ) { + + Material.prototype.copy.call( this, source ); + + this.defines = { 'STANDARD': '' }; + + this.color.copy( source.color ); + this.roughness = source.roughness; + this.metalness = source.metalness; + + this.map = source.map; + + 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; + + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; + + this.normalMap = source.normalMap; + this.normalScale.copy( source.normalScale ); - } + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; - } + this.roughnessMap = source.roughnessMap; - var object = scope.parse( json, texturePath ); - onLoad( object.geometry, object.materials ); + this.metalnessMap = source.metalnessMap; - }, onProgress, onError ); + this.alphaMap = source.alphaMap; - }, + this.envMap = source.envMap; + this.envMapIntensity = source.envMapIntensity; - setTexturePath: function ( value ) { + this.refractionRatio = source.refractionRatio; - this.texturePath = value; + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; - }, + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; + this.morphNormals = source.morphNormals; - parse: function ( json, texturePath ) { + return this; - var geometry = new Geometry(), - scale = ( json.scale !== undefined ) ? 1.0 / json.scale : 1.0; + }; - parseModel( scale ); + /** + * @author WestLangley / http://github.com/WestLangley + * + * parameters = { + * reflectivity: + * } + */ - parseSkin(); - parseMorphing( scale ); - parseAnimations(); + function MeshPhysicalMaterial( parameters ) { - geometry.computeFaceNormals(); - geometry.computeBoundingSphere(); + MeshStandardMaterial.call( this ); - function parseModel( scale ) { + this.defines = { 'PHYSICAL': '' }; - function isBitSet( value, position ) { + this.type = 'MeshPhysicalMaterial'; - return value & ( 1 << position ); + this.reflectivity = 0.5; // maps to F0 = 0.04 - } + this.clearCoat = 0.0; + this.clearCoatRoughness = 0.0; - var i, j, fi, + this.setValues( parameters ); - offset, zLength, + } - colorIndex, normalIndex, uvIndex, materialIndex, + MeshPhysicalMaterial.prototype = Object.create( MeshStandardMaterial.prototype ); + MeshPhysicalMaterial.prototype.constructor = MeshPhysicalMaterial; - type, - isQuad, - hasMaterial, - hasFaceVertexUv, - hasFaceNormal, hasFaceVertexNormal, - hasFaceColor, hasFaceVertexColor, + MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true; - vertex, face, faceA, faceB, hex, normal, + MeshPhysicalMaterial.prototype.copy = function ( source ) { - uvLayer, uv, u, v, + MeshStandardMaterial.prototype.copy.call( this, source ); - faces = json.faces, - vertices = json.vertices, - normals = json.normals, - colors = json.colors, + this.defines = { 'PHYSICAL': '' }; - nUvLayers = 0; + this.reflectivity = source.reflectivity; - if ( json.uvs !== undefined ) { + this.clearCoat = source.clearCoat; + this.clearCoatRoughness = source.clearCoatRoughness; - // disregard empty arrays + return this; - for ( i = 0; i < json.uvs.length; i ++ ) { + }; - if ( json.uvs[ i ].length ) nUvLayers ++; + /** + * @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: + * } + */ - } + function MeshPhongMaterial( parameters ) { - for ( i = 0; i < nUvLayers; i ++ ) { + Material.call( this ); - geometry.faceVertexUvs[ i ] = []; + this.type = 'MeshPhongMaterial'; - } + this.color = new Color( 0xffffff ); // diffuse + this.specular = new Color( 0x111111 ); + this.shininess = 30; - } + this.map = null; - offset = 0; - zLength = vertices.length; + this.lightMap = null; + this.lightMapIntensity = 1.0; - while ( offset < zLength ) { + this.aoMap = null; + this.aoMapIntensity = 1.0; - vertex = new Vector3(); + this.emissive = new Color( 0x000000 ); + this.emissiveIntensity = 1.0; + this.emissiveMap = null; - vertex.x = vertices[ offset ++ ] * scale; - vertex.y = vertices[ offset ++ ] * scale; - vertex.z = vertices[ offset ++ ] * scale; + this.bumpMap = null; + this.bumpScale = 1; - geometry.vertices.push( vertex ); + this.normalMap = null; + this.normalScale = new Vector2( 1, 1 ); - } + this.displacementMap = null; + this.displacementScale = 1; + this.displacementBias = 0; - offset = 0; - zLength = faces.length; + this.specularMap = null; - while ( offset < zLength ) { + this.alphaMap = null; - type = faces[ offset ++ ]; + this.envMap = null; + this.combine = MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; - 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 ); + this.skinning = false; + this.morphTargets = false; + this.morphNormals = false; - // console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor); + this.setValues( parameters ); - if ( isQuad ) { + } - faceA = new Face3(); - faceA.a = faces[ offset ]; - faceA.b = faces[ offset + 1 ]; - faceA.c = faces[ offset + 3 ]; + MeshPhongMaterial.prototype = Object.create( Material.prototype ); + MeshPhongMaterial.prototype.constructor = MeshPhongMaterial; - faceB = new Face3(); - faceB.a = faces[ offset + 1 ]; - faceB.b = faces[ offset + 2 ]; - faceB.c = faces[ offset + 3 ]; + MeshPhongMaterial.prototype.isMeshPhongMaterial = true; - offset += 4; + MeshPhongMaterial.prototype.copy = function ( source ) { - if ( hasMaterial ) { + Material.prototype.copy.call( this, source ); - materialIndex = faces[ offset ++ ]; - faceA.materialIndex = materialIndex; - faceB.materialIndex = materialIndex; + this.color.copy( source.color ); + this.specular.copy( source.specular ); + this.shininess = source.shininess; - } + this.map = source.map; - // to get face <=> uv index correspondence + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; - fi = geometry.faces.length; + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; - if ( hasFaceVertexUv ) { + this.emissive.copy( source.emissive ); + this.emissiveMap = source.emissiveMap; + this.emissiveIntensity = source.emissiveIntensity; - for ( i = 0; i < nUvLayers; i ++ ) { + this.bumpMap = source.bumpMap; + this.bumpScale = source.bumpScale; - uvLayer = json.uvs[ i ]; + this.normalMap = source.normalMap; + this.normalScale.copy( source.normalScale ); - geometry.faceVertexUvs[ i ][ fi ] = []; - geometry.faceVertexUvs[ i ][ fi + 1 ] = []; + this.displacementMap = source.displacementMap; + this.displacementScale = source.displacementScale; + this.displacementBias = source.displacementBias; - for ( j = 0; j < 4; j ++ ) { + this.specularMap = source.specularMap; - uvIndex = faces[ offset ++ ]; + this.alphaMap = source.alphaMap; - u = uvLayer[ uvIndex * 2 ]; - v = uvLayer[ uvIndex * 2 + 1 ]; + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; - uv = new Vector2( u, v ); + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; - if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv ); - if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv ); + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; + this.morphNormals = source.morphNormals; - } + return this; - } + }; - } + /** + * @author mrdoob / http://mrdoob.com/ + * + * parameters = { + * opacity: , + * + * wireframe: , + * wireframeLinewidth: + * } + */ - if ( hasFaceNormal ) { + function MeshNormalMaterial( parameters ) { - normalIndex = faces[ offset ++ ] * 3; + Material.call( this, parameters ); - faceA.normal.set( - normals[ normalIndex ++ ], - normals[ normalIndex ++ ], - normals[ normalIndex ] - ); + this.type = 'MeshNormalMaterial'; - faceB.normal.copy( faceA.normal ); + this.wireframe = false; + this.wireframeLinewidth = 1; - } + this.fog = false; + this.lights = false; + this.morphTargets = false; - if ( hasFaceVertexNormal ) { + this.setValues( parameters ); - for ( i = 0; i < 4; i ++ ) { + } - normalIndex = faces[ offset ++ ] * 3; + MeshNormalMaterial.prototype = Object.create( Material.prototype ); + MeshNormalMaterial.prototype.constructor = MeshNormalMaterial; - normal = new Vector3( - normals[ normalIndex ++ ], - normals[ normalIndex ++ ], - normals[ normalIndex ] - ); + MeshNormalMaterial.prototype.isMeshNormalMaterial = true; + MeshNormalMaterial.prototype.copy = function ( source ) { - if ( i !== 2 ) faceA.vertexNormals.push( normal ); - if ( i !== 0 ) faceB.vertexNormals.push( normal ); + Material.prototype.copy.call( this, source ); - } + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; - } + return this; + }; - if ( hasFaceColor ) { + /** + * @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: + * } + */ - colorIndex = faces[ offset ++ ]; - hex = colors[ colorIndex ]; + function MeshLambertMaterial( parameters ) { - faceA.color.setHex( hex ); - faceB.color.setHex( hex ); + Material.call( this ); - } + this.type = 'MeshLambertMaterial'; + this.color = new Color( 0xffffff ); // diffuse - if ( hasFaceVertexColor ) { + this.map = null; - for ( i = 0; i < 4; i ++ ) { + this.lightMap = null; + this.lightMapIntensity = 1.0; - colorIndex = faces[ offset ++ ]; - hex = colors[ colorIndex ]; + this.aoMap = null; + this.aoMapIntensity = 1.0; - if ( i !== 2 ) faceA.vertexColors.push( new Color( hex ) ); - if ( i !== 0 ) faceB.vertexColors.push( new Color( hex ) ); + this.emissive = new Color( 0x000000 ); + this.emissiveIntensity = 1.0; + this.emissiveMap = null; - } + this.specularMap = null; - } + this.alphaMap = null; - geometry.faces.push( faceA ); - geometry.faces.push( faceB ); + this.envMap = null; + this.combine = MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; - } else { + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; - face = new Face3(); - face.a = faces[ offset ++ ]; - face.b = faces[ offset ++ ]; - face.c = faces[ offset ++ ]; + this.skinning = false; + this.morphTargets = false; + this.morphNormals = false; - if ( hasMaterial ) { + this.setValues( parameters ); - materialIndex = faces[ offset ++ ]; - face.materialIndex = materialIndex; + } - } + MeshLambertMaterial.prototype = Object.create( Material.prototype ); + MeshLambertMaterial.prototype.constructor = MeshLambertMaterial; - // to get face <=> uv index correspondence + MeshLambertMaterial.prototype.isMeshLambertMaterial = true; - fi = geometry.faces.length; + MeshLambertMaterial.prototype.copy = function ( source ) { - if ( hasFaceVertexUv ) { + Material.prototype.copy.call( this, source ); - for ( i = 0; i < nUvLayers; i ++ ) { + this.color.copy( source.color ); - uvLayer = json.uvs[ i ]; + this.map = source.map; - geometry.faceVertexUvs[ i ][ fi ] = []; + this.lightMap = source.lightMap; + this.lightMapIntensity = source.lightMapIntensity; - for ( j = 0; j < 3; j ++ ) { + this.aoMap = source.aoMap; + this.aoMapIntensity = source.aoMapIntensity; - uvIndex = faces[ offset ++ ]; + this.emissive.copy( source.emissive ); + this.emissiveMap = source.emissiveMap; + this.emissiveIntensity = source.emissiveIntensity; - u = uvLayer[ uvIndex * 2 ]; - v = uvLayer[ uvIndex * 2 + 1 ]; + this.specularMap = source.specularMap; - uv = new Vector2( u, v ); + this.alphaMap = source.alphaMap; - geometry.faceVertexUvs[ i ][ fi ].push( uv ); + this.envMap = source.envMap; + this.combine = source.combine; + this.reflectivity = source.reflectivity; + this.refractionRatio = source.refractionRatio; - } + this.wireframe = source.wireframe; + this.wireframeLinewidth = source.wireframeLinewidth; + this.wireframeLinecap = source.wireframeLinecap; + this.wireframeLinejoin = source.wireframeLinejoin; - } + this.skinning = source.skinning; + this.morphTargets = source.morphTargets; + this.morphNormals = source.morphNormals; - } + return this; - if ( hasFaceNormal ) { + }; - normalIndex = faces[ offset ++ ] * 3; + /** + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * + * linewidth: , + * + * scale: , + * dashSize: , + * gapSize: + * } + */ - face.normal.set( - normals[ normalIndex ++ ], - normals[ normalIndex ++ ], - normals[ normalIndex ] - ); + function LineDashedMaterial( parameters ) { - } + Material.call( this ); - if ( hasFaceVertexNormal ) { + this.type = 'LineDashedMaterial'; - for ( i = 0; i < 3; i ++ ) { + this.color = new Color( 0xffffff ); - normalIndex = faces[ offset ++ ] * 3; + this.linewidth = 1; - normal = new Vector3( - normals[ normalIndex ++ ], - normals[ normalIndex ++ ], - normals[ normalIndex ] - ); + this.scale = 1; + this.dashSize = 3; + this.gapSize = 1; - face.vertexNormals.push( normal ); + this.lights = false; - } + this.setValues( parameters ); - } + } + LineDashedMaterial.prototype = Object.create( Material.prototype ); + LineDashedMaterial.prototype.constructor = LineDashedMaterial; - if ( hasFaceColor ) { + LineDashedMaterial.prototype.isLineDashedMaterial = true; - colorIndex = faces[ offset ++ ]; - face.color.setHex( colors[ colorIndex ] ); + LineDashedMaterial.prototype.copy = function ( source ) { - } + Material.prototype.copy.call( this, source ); + this.color.copy( source.color ); - if ( hasFaceVertexColor ) { + this.linewidth = source.linewidth; - for ( i = 0; i < 3; i ++ ) { + this.scale = source.scale; + this.dashSize = source.dashSize; + this.gapSize = source.gapSize; - colorIndex = faces[ offset ++ ]; - face.vertexColors.push( new Color( colors[ colorIndex ] ) ); + return this; - } + }; - } - geometry.faces.push( face ); - } + var Materials = Object.freeze({ + ShadowMaterial: ShadowMaterial, + SpriteMaterial: SpriteMaterial, + RawShaderMaterial: RawShaderMaterial, + ShaderMaterial: ShaderMaterial, + PointsMaterial: PointsMaterial, + MultiMaterial: MultiMaterial, + MeshPhysicalMaterial: MeshPhysicalMaterial, + MeshStandardMaterial: MeshStandardMaterial, + MeshPhongMaterial: MeshPhongMaterial, + MeshNormalMaterial: MeshNormalMaterial, + MeshLambertMaterial: MeshLambertMaterial, + MeshDepthMaterial: MeshDepthMaterial, + MeshBasicMaterial: MeshBasicMaterial, + LineDashedMaterial: LineDashedMaterial, + LineBasicMaterial: LineBasicMaterial, + Material: Material + }); - } + /** + * @author mrdoob / http://mrdoob.com/ + */ - } + exports.Cache = { - function parseSkin() { + enabled: false, - var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2; + files: {}, - if ( json.skinWeights ) { + add: function ( key, file ) { - for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) { + if ( this.enabled === false ) return; - 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; + // console.log( 'THREE.Cache', 'Adding key:', key ); - geometry.skinWeights.push( new Vector4( x, y, z, w ) ); + this.files[ key ] = file; - } + }, - } + get: function ( key ) { - if ( json.skinIndices ) { + if ( this.enabled === false ) return; - for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) { + // console.log( 'THREE.Cache', 'Checking key:', key ); - 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; + return this.files[ key ]; - geometry.skinIndices.push( new Vector4( a, b, c, d ) ); + }, - } + remove: function ( key ) { - } + delete this.files[ key ]; - geometry.bones = json.bones; + }, - if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) { + clear: function () { - console.warn( 'When skinning, number of vertices (' + geometry.vertices.length + '), skinIndices (' + - geometry.skinIndices.length + '), and skinWeights (' + geometry.skinWeights.length + ') should match.' ); + this.files = {}; - } + } - } + }; - function parseMorphing( scale ) { + /** + * @author mrdoob / http://mrdoob.com/ + */ - if ( json.morphTargets !== undefined ) { + function LoadingManager( onLoad, onProgress, onError ) { - for ( var i = 0, l = json.morphTargets.length; i < l; i ++ ) { + var scope = this; - geometry.morphTargets[ i ] = {}; - geometry.morphTargets[ i ].name = json.morphTargets[ i ].name; - geometry.morphTargets[ i ].vertices = []; + var isLoading = false, itemsLoaded = 0, itemsTotal = 0; - var dstVertices = geometry.morphTargets[ i ].vertices; - var srcVertices = json.morphTargets[ i ].vertices; + this.onStart = undefined; + this.onLoad = onLoad; + this.onProgress = onProgress; + this.onError = onError; - for ( var v = 0, vl = srcVertices.length; v < vl; v += 3 ) { + this.itemStart = function ( url ) { - var vertex = new Vector3(); - vertex.x = srcVertices[ v ] * scale; - vertex.y = srcVertices[ v + 1 ] * scale; - vertex.z = srcVertices[ v + 2 ] * scale; + itemsTotal ++; - dstVertices.push( vertex ); + if ( isLoading === false ) { - } + if ( scope.onStart !== undefined ) { - } + scope.onStart( url, itemsLoaded, itemsTotal ); } - if ( json.morphColors !== undefined && json.morphColors.length > 0 ) { + } - console.warn( 'THREE.JSONLoader: "morphColors" no longer supported. Using them as face colors.' ); + isLoading = true; - var faces = geometry.faces; - var morphColors = json.morphColors[ 0 ].colors; + }; - for ( var i = 0, l = faces.length; i < l; i ++ ) { + this.itemEnd = function ( url ) { - faces[ i ].color.fromArray( morphColors, i * 3 ); + itemsLoaded ++; - } + if ( scope.onProgress !== undefined ) { - } + scope.onProgress( url, itemsLoaded, itemsTotal ); } - function parseAnimations() { - - var outputAnimations = []; + if ( itemsLoaded === itemsTotal ) { - // parse old style Bone/Hierarchy animations - var animations = []; + isLoading = false; - if ( json.animation !== undefined ) { + if ( scope.onLoad !== undefined ) { - animations.push( json.animation ); + scope.onLoad(); } - if ( json.animations !== undefined ) { - - if ( json.animations.length ) { + } - animations = animations.concat( json.animations ); + }; - } else { + this.itemError = function ( url ) { - animations.push( json.animations ); + if ( scope.onError !== undefined ) { - } + scope.onError( url ); - } + } - for ( var i = 0; i < animations.length; i ++ ) { + }; - var clip = AnimationClip.parseAnimation( animations[ i ], geometry.bones ); - if ( clip ) outputAnimations.push( clip ); + } - } + exports.DefaultLoadingManager = new LoadingManager(); - // parse implicit morph animations - if ( geometry.morphTargets ) { + /** + * @author mrdoob / http://mrdoob.com/ + */ - // TODO: Figure out what an appropraite FPS is for morph target animations -- defaulting to 10, but really it is completely arbitrary. - var morphAnimationClips = AnimationClip.CreateClipsFromMorphTargetSequences( geometry.morphTargets, 10 ); - outputAnimations = outputAnimations.concat( morphAnimationClips ); + function XHRLoader( manager ) { - } + this.manager = ( manager !== undefined ) ? manager : exports.DefaultLoadingManager; - if ( outputAnimations.length > 0 ) geometry.animations = outputAnimations; + } - } + Object.assign( XHRLoader.prototype, { - if ( json.materials === undefined || json.materials.length === 0 ) { + load: function ( url, onLoad, onProgress, onError ) { - return { geometry: geometry }; + if ( this.path !== undefined ) url = this.path + url; - } else { + var scope = this; - var materials = Loader.prototype.initMaterials( json.materials, texturePath, this.crossOrigin ); + var cached = exports.Cache.get( url ); - return { geometry: geometry, materials: materials }; + if ( cached !== undefined ) { - } + scope.manager.itemStart( url ); - } + setTimeout( function () { - } ); + if ( onLoad ) onLoad( cached ); - /** - * @author mrdoob / http://mrdoob.com/ - */ + scope.manager.itemEnd( url ); - function WireframeGeometry( geometry ) { + }, 0 ); - BufferGeometry.call( this ); + return cached; - var edge = [ 0, 0 ], hash = {}; + } - function sortFunction( a, b ) { + var request = new XMLHttpRequest(); + request.open( 'GET', url, true ); - return a - b; + request.addEventListener( 'load', function ( event ) { - } + var response = event.target.response; - var keys = [ 'a', 'b', 'c' ]; + exports.Cache.add( url, response ); - if ( (geometry && geometry.isGeometry) ) { + if ( this.status === 200 ) { - var vertices = geometry.vertices; - var faces = geometry.faces; - var numEdges = 0; + if ( onLoad ) onLoad( response ); - // allocate maximal size - var edges = new Uint32Array( 6 * faces.length ); + scope.manager.itemEnd( url ); - for ( var i = 0, l = faces.length; i < l; i ++ ) { + } else if ( this.status === 0 ) { - var face = faces[ i ]; + // Some browsers return HTTP Status 0 when using non-http protocol + // e.g. 'file://' or 'data://'. Handle as success. - for ( var j = 0; j < 3; j ++ ) { + console.warn( 'THREE.XHRLoader: HTTP Status 0 received.' ); - edge[ 0 ] = face[ keys[ j ] ]; - edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; - edge.sort( sortFunction ); + if ( onLoad ) onLoad( response ); - var key = edge.toString(); + scope.manager.itemEnd( url ); - if ( hash[ key ] === undefined ) { + } else { - edges[ 2 * numEdges ] = edge[ 0 ]; - edges[ 2 * numEdges + 1 ] = edge[ 1 ]; - hash[ key ] = true; - numEdges ++; + if ( onError ) onError( event ); - } + scope.manager.itemError( url ); } - } - - var coords = new Float32Array( numEdges * 2 * 3 ); - - for ( var i = 0, l = numEdges; i < l; i ++ ) { + }, false ); - for ( var j = 0; j < 2; j ++ ) { + if ( onProgress !== undefined ) { - var vertex = vertices[ edges [ 2 * i + j ] ]; + request.addEventListener( 'progress', function ( event ) { - var index = 6 * i + 3 * j; - coords[ index + 0 ] = vertex.x; - coords[ index + 1 ] = vertex.y; - coords[ index + 2 ] = vertex.z; + onProgress( event ); - } + }, false ); } - this.addAttribute( 'position', new BufferAttribute( coords, 3 ) ); + request.addEventListener( 'error', function ( event ) { + + if ( onError ) onError( event ); - } else if ( (geometry && geometry.isBufferGeometry) ) { + scope.manager.itemError( url ); - if ( geometry.index !== null ) { + }, false ); - // Indexed BufferGeometry + if ( this.responseType !== undefined ) request.responseType = this.responseType; + if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials; - var indices = geometry.index.array; - var vertices = geometry.attributes.position; - var groups = geometry.groups; - var numEdges = 0; + if ( request.overrideMimeType ) request.overrideMimeType( 'text/plain' ); - if ( groups.length === 0 ) { + request.send( null ); - geometry.addGroup( 0, indices.length ); + scope.manager.itemStart( url ); - } + return request; - // allocate maximal size - var edges = new Uint32Array( 2 * indices.length ); + }, - for ( var o = 0, ol = groups.length; o < ol; ++ o ) { + setPath: function ( value ) { - var group = groups[ o ]; + this.path = value; + return this; - var start = group.start; - var count = group.count; + }, - for ( var i = start, il = start + count; i < il; i += 3 ) { + setResponseType: function ( value ) { - for ( var j = 0; j < 3; j ++ ) { + this.responseType = value; + return this; - edge[ 0 ] = indices[ i + j ]; - edge[ 1 ] = indices[ i + ( j + 1 ) % 3 ]; - edge.sort( sortFunction ); + }, - var key = edge.toString(); + setWithCredentials: function ( value ) { - if ( hash[ key ] === undefined ) { + this.withCredentials = value; + return this; - edges[ 2 * numEdges ] = edge[ 0 ]; - edges[ 2 * numEdges + 1 ] = edge[ 1 ]; - hash[ key ] = true; - numEdges ++; + } - } + } ); - } + /** + * @author mrdoob / http://mrdoob.com/ + * + * Abstract Base class to block based textures loader (dds, pvr, ...) + */ - } + function CompressedTextureLoader( manager ) { - } + this.manager = ( manager !== undefined ) ? manager : exports.DefaultLoadingManager; - var coords = new Float32Array( numEdges * 2 * 3 ); + // override in sub classes + this._parser = null; - for ( var i = 0, l = numEdges; i < l; i ++ ) { + } - for ( var j = 0; j < 2; j ++ ) { + Object.assign( CompressedTextureLoader.prototype, { - var index = 6 * i + 3 * j; - var index2 = edges[ 2 * i + j ]; + load: function ( url, onLoad, onProgress, onError ) { - coords[ index + 0 ] = vertices.getX( index2 ); - coords[ index + 1 ] = vertices.getY( index2 ); - coords[ index + 2 ] = vertices.getZ( index2 ); + var scope = this; - } + var images = []; - } + var texture = new CompressedTexture(); + texture.image = images; - this.addAttribute( 'position', new BufferAttribute( coords, 3 ) ); + var loader = new XHRLoader( this.manager ); + loader.setPath( this.path ); + loader.setResponseType( 'arraybuffer' ); - } else { + function loadTexture( i ) { - // non-indexed BufferGeometry + loader.load( url[ i ], function ( buffer ) { - var vertices = geometry.attributes.position.array; - var numEdges = vertices.length / 3; - var numTris = numEdges / 3; + var texDatas = scope._parser( buffer, true ); - var coords = new Float32Array( numEdges * 2 * 3 ); + images[ i ] = { + width: texDatas.width, + height: texDatas.height, + format: texDatas.format, + mipmaps: texDatas.mipmaps + }; - for ( var i = 0, l = numTris; i < l; i ++ ) { + loaded += 1; - for ( var j = 0; j < 3; j ++ ) { + if ( loaded === 6 ) { - var index = 18 * i + 6 * j; + if ( texDatas.mipmapCount === 1 ) + texture.minFilter = LinearFilter; - var index1 = 9 * i + 3 * j; - coords[ index + 0 ] = vertices[ index1 ]; - coords[ index + 1 ] = vertices[ index1 + 1 ]; - coords[ index + 2 ] = vertices[ index1 + 2 ]; + texture.format = texDatas.format; + texture.needsUpdate = true; - 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 ]; + if ( onLoad ) onLoad( texture ); } - } - - this.addAttribute( 'position', new BufferAttribute( coords, 3 ) ); + }, onProgress, onError ); } - } + if ( Array.isArray( url ) ) { - } + var loaded = 0; - WireframeGeometry.prototype = Object.create( BufferGeometry.prototype ); - WireframeGeometry.prototype.constructor = WireframeGeometry; + for ( var i = 0, il = url.length; i < il; ++ i ) { - /** - * @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 ); - * - */ + loadTexture( i ); - function ParametricGeometry( func, slices, stacks ) { + } - Geometry.call( this ); + } else { - this.type = 'ParametricGeometry'; + // compressed cubemap texture stored in a single DDS file - this.parameters = { - func: func, - slices: slices, - stacks: stacks - }; + loader.load( url, function ( buffer ) { - var verts = this.vertices; - var faces = this.faces; - var uvs = this.faceVertexUvs[ 0 ]; + var texDatas = scope._parser( buffer, true ); - var i, j, p; - var u, v; + if ( texDatas.isCubemap ) { - var sliceCount = slices + 1; + var faces = texDatas.mipmaps.length / texDatas.mipmapCount; - for ( i = 0; i <= stacks; i ++ ) { + for ( var f = 0; f < faces; f ++ ) { - v = i / stacks; + images[ f ] = { mipmaps : [] }; - for ( j = 0; j <= slices; j ++ ) { + for ( var i = 0; i < texDatas.mipmapCount; i ++ ) { - u = j / slices; + 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; - p = func( u, v ); - verts.push( p ); + } - } + } - } + } else { - var a, b, c, d; - var uva, uvb, uvc, uvd; + texture.image.width = texDatas.width; + texture.image.height = texDatas.height; + texture.mipmaps = texDatas.mipmaps; - for ( i = 0; i < stacks; i ++ ) { + } - for ( j = 0; j < slices; j ++ ) { + if ( texDatas.mipmapCount === 1 ) { - a = i * sliceCount + j; - b = i * sliceCount + j + 1; - c = ( i + 1 ) * sliceCount + j + 1; - d = ( i + 1 ) * sliceCount + j; + texture.minFilter = LinearFilter; - uva = new Vector2( j / slices, i / stacks ); - uvb = new Vector2( ( j + 1 ) / slices, i / stacks ); - uvc = new Vector2( ( j + 1 ) / slices, ( i + 1 ) / stacks ); - uvd = new Vector2( j / slices, ( i + 1 ) / stacks ); + } - faces.push( new Face3( a, b, d ) ); - uvs.push( [ uva, uvb, uvd ] ); + texture.format = texDatas.format; + texture.needsUpdate = true; - faces.push( new Face3( b, c, d ) ); - uvs.push( [ uvb.clone(), uvc, uvd.clone() ] ); + if ( onLoad ) onLoad( texture ); + + }, onProgress, onError ); } + return texture; + + }, + + setPath: function ( value ) { + + this.path = value; + return this; + } - // console.log(this); + } ); - // magic bullet - // var diff = this.mergeVertices(); - // console.log('removed ', diff, ' vertices by merging'); + /** + * @author Nikos M. / https://github.com/foo123/ + * + * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) + */ - this.computeFaceNormals(); - this.computeVertexNormals(); + var DataTextureLoader = BinaryTextureLoader; + function BinaryTextureLoader( manager ) { + + this.manager = ( manager !== undefined ) ? manager : exports.DefaultLoadingManager; + + // override in sub classes + this._parser = null; } - ParametricGeometry.prototype = Object.create( Geometry.prototype ); - ParametricGeometry.prototype.constructor = ParametricGeometry; + Object.assign( BinaryTextureLoader.prototype, { - /** - * @author clockworkgeek / https://github.com/clockworkgeek - * @author timothypratley / https://github.com/timothypratley - * @author WestLangley / http://github.com/WestLangley - */ + load: function ( url, onLoad, onProgress, onError ) { - function PolyhedronGeometry( vertices, indices, radius, detail ) { + var scope = this; - Geometry.call( this ); + var texture = new DataTexture(); - this.type = 'PolyhedronGeometry'; + var loader = new XHRLoader( this.manager ); + loader.setResponseType( 'arraybuffer' ); - this.parameters = { - vertices: vertices, - indices: indices, - radius: radius, - detail: detail - }; + loader.load( url, function ( buffer ) { - radius = radius || 1; - detail = detail || 0; + var texData = scope._parser( buffer ); - var that = this; + if ( ! texData ) return; - for ( var i = 0, l = vertices.length; i < l; i += 3 ) { + if ( undefined !== texData.image ) { - prepare( new Vector3( vertices[ i ], vertices[ i + 1 ], vertices[ i + 2 ] ) ); + texture.image = texData.image; - } + } else if ( undefined !== texData.data ) { - var p = this.vertices; + texture.image.width = texData.width; + texture.image.height = texData.height; + texture.image.data = texData.data; - var faces = []; + } - for ( var i = 0, j = 0, l = indices.length; i < l; i += 3, j ++ ) { + texture.wrapS = undefined !== texData.wrapS ? texData.wrapS : ClampToEdgeWrapping; + texture.wrapT = undefined !== texData.wrapT ? texData.wrapT : ClampToEdgeWrapping; - var v1 = p[ indices[ i ] ]; - var v2 = p[ indices[ i + 1 ] ]; - var v3 = p[ indices[ i + 2 ] ]; + texture.magFilter = undefined !== texData.magFilter ? texData.magFilter : LinearFilter; + texture.minFilter = undefined !== texData.minFilter ? texData.minFilter : LinearMipMapLinearFilter; - faces[ j ] = new Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] ); + texture.anisotropy = undefined !== texData.anisotropy ? texData.anisotropy : 1; - } + if ( undefined !== texData.format ) { - var centroid = new Vector3(); + texture.format = texData.format; - for ( var i = 0, l = faces.length; i < l; i ++ ) { + } + if ( undefined !== texData.type ) { - subdivide( faces[ i ], detail ); + texture.type = texData.type; - } + } + if ( undefined !== texData.mipmaps ) { - // Handle case when face straddles the seam + texture.mipmaps = texData.mipmaps; - for ( var i = 0, l = this.faceVertexUvs[ 0 ].length; i < l; i ++ ) { + } - var uvs = this.faceVertexUvs[ 0 ][ i ]; + if ( 1 === texData.mipmapCount ) { - var x0 = uvs[ 0 ].x; - var x1 = uvs[ 1 ].x; - var x2 = uvs[ 2 ].x; + texture.minFilter = LinearFilter; - var max = Math.max( x0, x1, x2 ); - var min = Math.min( x0, x1, x2 ); + } - if ( max > 0.9 && min < 0.1 ) { + texture.needsUpdate = true; - // 0.9 is somewhat arbitrary + if ( onLoad ) onLoad( texture, texData ); - if ( x0 < 0.2 ) uvs[ 0 ].x += 1; - if ( x1 < 0.2 ) uvs[ 1 ].x += 1; - if ( x2 < 0.2 ) uvs[ 2 ].x += 1; + }, onProgress, onError ); - } + + return texture; } + } ); - // Apply radius + /** + * @author mrdoob / http://mrdoob.com/ + */ - for ( var i = 0, l = this.vertices.length; i < l; i ++ ) { + function ImageLoader( manager ) { - this.vertices[ i ].multiplyScalar( radius ); + this.manager = ( manager !== undefined ) ? manager : exports.DefaultLoadingManager; - } + } + Object.assign( ImageLoader.prototype, { - // Merge vertices + load: function ( url, onLoad, onProgress, onError ) { - this.mergeVertices(); + var scope = this; - this.computeFaceNormals(); + var image = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'img' ); + image.onload = function () { - this.boundingSphere = new Sphere( new Vector3(), radius ); + image.onload = null; + URL.revokeObjectURL( image.src ); - // Project vector onto sphere's surface + if ( onLoad ) onLoad( image ); - function prepare( vector ) { + scope.manager.itemEnd( url ); - 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. + if ( url.indexOf( 'data:' ) === 0 ) { - var u = azimuth( vector ) / 2 / Math.PI + 0.5; - var v = inclination( vector ) / Math.PI + 0.5; - vertex.uv = new Vector2( u, 1 - v ); + image.src = url; - return vertex; + } else { - } + var loader = new XHRLoader(); + loader.setPath( this.path ); + loader.setResponseType( 'blob' ); + loader.setWithCredentials( this.withCredentials ); + loader.load( url, function ( blob ) { + image.src = URL.createObjectURL( blob ); - // Approximate a curved face with recursively sub-divided triangles. + }, onProgress, onError ); - function make( v1, v2, v3 ) { + } - var face = new Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] ); - that.faces.push( face ); + scope.manager.itemStart( url ); - centroid.copy( v1 ).add( v2 ).add( v3 ).divideScalar( 3 ); + return image; - var azi = azimuth( centroid ); + }, - that.faceVertexUvs[ 0 ].push( [ - correctUV( v1.uv, v1, azi ), - correctUV( v2.uv, v2, azi ), - correctUV( v3.uv, v3, azi ) - ] ); + setCrossOrigin: function ( value ) { - } + this.crossOrigin = value; + return this; + }, - // Analytically subdivide a face to the required detail level. + setWithCredentials: function ( value ) { - function subdivide( face, detail ) { + this.withCredentials = value; + return this; - 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. + setPath: function ( value ) { - for ( var i = 0 ; i <= cols; i ++ ) { + this.path = value; + return this; - v[ i ] = []; + } - var aj = prepare( a.clone().lerp( c, i / cols ) ); - var bj = prepare( b.clone().lerp( c, i / cols ) ); - var rows = cols - i; + } ); - for ( var j = 0; j <= rows; j ++ ) { + /** + * @author mrdoob / http://mrdoob.com/ + */ - if ( j === 0 && i === cols ) { + function CubeTextureLoader( manager ) { - v[ i ][ j ] = aj; + this.manager = ( manager !== undefined ) ? manager : exports.DefaultLoadingManager; - } else { + } - v[ i ][ j ] = prepare( aj.clone().lerp( bj, j / rows ) ); + Object.assign( CubeTextureLoader.prototype, { - } + load: function ( urls, onLoad, onProgress, onError ) { - } + var texture = new CubeTexture(); - } + var loader = new ImageLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.setPath( this.path ); - // Construct all of the faces. + var loaded = 0; - for ( var i = 0; i < cols ; i ++ ) { + function loadTexture( i ) { - for ( var j = 0; j < 2 * ( cols - i ) - 1; j ++ ) { + loader.load( urls[ i ], function ( image ) { - var k = Math.floor( j / 2 ); + texture.images[ i ] = image; - if ( j % 2 === 0 ) { + loaded ++; - make( - v[ i ][ k + 1 ], - v[ i + 1 ][ k ], - v[ i ][ k ] - ); + if ( loaded === 6 ) { - } else { + texture.needsUpdate = true; - make( - v[ i ][ k + 1 ], - v[ i + 1 ][ k + 1 ], - v[ i + 1 ][ k ] - ); + if ( onLoad ) onLoad( texture ); } - } + }, undefined, onError ); } - } + for ( var i = 0; i < urls.length; ++ i ) { + loadTexture( i ); - // Angle around the Y axis, counter-clockwise when looking from above. + } - function azimuth( vector ) { + return texture; - return Math.atan2( vector.z, - vector.x ); + }, - } + setCrossOrigin: function ( value ) { + this.crossOrigin = value; + return this; - // Angle above the XZ plane. + }, - function inclination( vector ) { + setPath: function ( value ) { - return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) ); + this.path = value; + return this; } + } ); - // Texture fixing helper. Spheres have some odd behaviours. - - function correctUV( uv, vector, azimuth ) { + /** + * @author mrdoob / http://mrdoob.com/ + */ - if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) uv = new Vector2( uv.x - 1, uv.y ); - if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) uv = new Vector2( azimuth / 2 / Math.PI + 0.5, uv.y ); - return uv.clone(); + function TextureLoader( manager ) { - } + this.manager = ( manager !== undefined ) ? manager : exports.DefaultLoadingManager; } - PolyhedronGeometry.prototype = Object.create( Geometry.prototype ); - PolyhedronGeometry.prototype.constructor = PolyhedronGeometry; + Object.assign( TextureLoader.prototype, { - /** - * @author timothypratley / https://github.com/timothypratley - */ + load: function ( url, onLoad, onProgress, onError ) { - function TetrahedronGeometry( radius, detail ) { + var texture = new Texture(); - var vertices = [ - 1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1 - ]; + var loader = new ImageLoader( this.manager ); + loader.setCrossOrigin( this.crossOrigin ); + loader.setWithCredentials( this.withCredentials ); + 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; + + texture.format = isJPEG ? RGBFormat : RGBAFormat; + texture.image = image; + texture.needsUpdate = true; + + if ( onLoad !== undefined ) { + + onLoad( texture ); - var indices = [ - 2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1 - ]; + } - PolyhedronGeometry.call( this, vertices, indices, radius, detail ); + }, onProgress, onError ); - this.type = 'TetrahedronGeometry'; + return texture; - this.parameters = { - radius: radius, - detail: detail - }; + }, - } + setCrossOrigin: function ( value ) { - TetrahedronGeometry.prototype = Object.create( PolyhedronGeometry.prototype ); - TetrahedronGeometry.prototype.constructor = TetrahedronGeometry; + this.crossOrigin = value; + return this; - /** - * @author timothypratley / https://github.com/timothypratley - */ + }, - function OctahedronGeometry( radius, detail ) { + setWithCredentials: function ( value ) { - var vertices = [ - 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1, 0, 0, 0, 1, 0, 0, - 1 - ]; + this.withCredentials = value; + return this; - 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 - ]; + }, - PolyhedronGeometry.call( this, vertices, indices, radius, detail ); + setPath: function ( value ) { - this.type = 'OctahedronGeometry'; + this.path = value; + return this; - this.parameters = { - radius: radius, - detail: detail - }; + } - } - OctahedronGeometry.prototype = Object.create( PolyhedronGeometry.prototype ); - OctahedronGeometry.prototype.constructor = OctahedronGeometry; + + } ); /** - * @author timothypratley / https://github.com/timothypratley + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ */ - function IcosahedronGeometry( radius, detail ) { + function Light( color, intensity ) { - var t = ( 1 + Math.sqrt( 5 ) ) / 2; + Object3D.call( this ); - 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 - ]; + this.type = 'Light'; - var indices = [ - 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11, - 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8, - 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9, - 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1 - ]; + this.color = new Color( color ); + this.intensity = intensity !== undefined ? intensity : 1; - PolyhedronGeometry.call( this, vertices, indices, radius, detail ); + this.receiveShadow = undefined; - this.type = 'IcosahedronGeometry'; + } - this.parameters = { - radius: radius, - detail: detail - }; + Light.prototype = Object.assign( Object.create( Object3D.prototype ), { - } + constructor: Light, - IcosahedronGeometry.prototype = Object.create( PolyhedronGeometry.prototype ); - IcosahedronGeometry.prototype.constructor = IcosahedronGeometry; + isLight: true, - /** - * @author Abe Pazos / https://hamoid.com - */ + copy: function ( source ) { - function DodecahedronGeometry( radius, detail ) { + Object3D.prototype.copy.call( this, source ); - var t = ( 1 + Math.sqrt( 5 ) ) / 2; - var r = 1 / t; + this.color.copy( source.color ); + this.intensity = source.intensity; - var vertices = [ + return this; - // (±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, + toJSON: function ( meta ) { - // (±1/φ, ±φ, 0) - - r, - t, 0, - r, t, 0, - r, - t, 0, r, t, 0, + var data = Object3D.prototype.toJSON.call( this, meta ); - // (±φ, 0, ±1/φ) - - t, 0, - r, t, 0, - r, - - t, 0, r, t, 0, r - ]; + data.object.color = this.color.getHex(); + data.object.intensity = this.intensity; - 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 - ]; + if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex(); - PolyhedronGeometry.call( this, vertices, indices, radius, detail ); + 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; - this.type = 'DodecahedronGeometry'; + if ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON(); - this.parameters = { - radius: radius, - detail: detail - }; + return data; - } + } - DodecahedronGeometry.prototype = Object.create( PolyhedronGeometry.prototype ); - DodecahedronGeometry.prototype.constructor = DodecahedronGeometry; + } ); /** - * @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 + * @author alteredq / http://alteredqualia.com/ */ - function TubeGeometry( path, segments, radius, radialSegments, closed, taper ) { - - Geometry.call( this ); + function HemisphereLight( skyColor, groundColor, intensity ) { - this.type = 'TubeGeometry'; + Light.call( this, skyColor, intensity ); - this.parameters = { - path: path, - segments: segments, - radius: radius, - radialSegments: radialSegments, - closed: closed, - taper: taper - }; + this.type = 'HemisphereLight'; - segments = segments || 64; - radius = radius || 1; - radialSegments = radialSegments || 8; - closed = closed || false; - taper = taper || TubeGeometry.NoTaper; + this.castShadow = undefined; - var grid = []; + this.position.copy( Object3D.DefaultUp ); + this.updateMatrix(); - var scope = this, + this.groundColor = new Color( groundColor ); - tangent, - normal, - binormal, + } - numpoints = segments + 1, + HemisphereLight.prototype = Object.assign( Object.create( Light.prototype ), { - u, v, r, + constructor: HemisphereLight, - cx, cy, - pos, pos2 = new Vector3(), - i, j, - ip, jp, - a, b, c, d, - uva, uvb, uvc, uvd; + isHemisphereLight: true, - var frames = new TubeGeometry.FrenetFrames( path, segments, closed ), - tangents = frames.tangents, - normals = frames.normals, - binormals = frames.binormals; + copy: function ( source ) { - // proxy internals - this.tangents = tangents; - this.normals = normals; - this.binormals = binormals; + Light.prototype.copy.call( this, source ); - function vert( x, y, z ) { + this.groundColor.copy( source.groundColor ); - return scope.vertices.push( new Vector3( x, y, z ) ) - 1; + return this; } - // construct the grid + } ); - for ( i = 0; i < numpoints; i ++ ) { + /** + * @author mrdoob / http://mrdoob.com/ + */ - grid[ i ] = []; + function LightShadow( camera ) { - u = i / ( numpoints - 1 ); + this.camera = camera; - pos = path.getPointAt( u ); + this.bias = 0; + this.radius = 1; - tangent = tangents[ i ]; - normal = normals[ i ]; - binormal = binormals[ i ]; + this.mapSize = new Vector2( 512, 512 ); - r = radius * taper( u ); + this.map = null; + this.matrix = new Matrix4(); - for ( j = 0; j < radialSegments; j ++ ) { + } - v = j / radialSegments * 2 * Math.PI; + Object.assign( LightShadow.prototype, { - cx = - r * Math.cos( v ); // TODO: Hack: Negating it so it faces outside. - cy = r * Math.sin( v ); + copy: function ( source ) { - 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; + this.camera = source.camera.clone(); - grid[ i ][ j ] = vert( pos2.x, pos2.y, pos2.z ); + this.bias = source.bias; + this.radius = source.radius; - } + this.mapSize.copy( source.mapSize ); + + return this; + + }, - } + clone: function () { + return new this.constructor().copy( this ); - // construct the mesh + }, - for ( i = 0; i < segments; i ++ ) { + toJSON: function () { - for ( j = 0; j < radialSegments; j ++ ) { + var object = {}; - ip = ( closed ) ? ( i + 1 ) % segments : i + 1; - jp = ( j + 1 ) % radialSegments; + if ( this.bias !== 0 ) object.bias = this.bias; + if ( this.radius !== 1 ) object.radius = this.radius; + if ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray(); - a = grid[ i ][ j ]; // *** NOT NECESSARILY PLANAR ! *** - b = grid[ ip ][ j ]; - c = grid[ ip ][ jp ]; - d = grid[ i ][ jp ]; + object.camera = this.camera.toJSON( false ).object; + delete object.camera.matrix; - uva = new Vector2( i / segments, j / radialSegments ); - uvb = new Vector2( ( i + 1 ) / segments, j / radialSegments ); - uvc = new Vector2( ( i + 1 ) / segments, ( j + 1 ) / radialSegments ); - uvd = new Vector2( i / segments, ( j + 1 ) / radialSegments ); + return object; - this.faces.push( new Face3( a, b, d ) ); - this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] ); + } - this.faces.push( new Face3( b, c, d ) ); - this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] ); + } ); - } + /** + * @author mrdoob / http://mrdoob.com/ + */ - } + function SpotLightShadow() { - this.computeFaceNormals(); - this.computeVertexNormals(); + LightShadow.call( this, new PerspectiveCamera( 50, 1, 0.5, 500 ) ); } - TubeGeometry.prototype = Object.create( Geometry.prototype ); - TubeGeometry.prototype.constructor = TubeGeometry; + SpotLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { - TubeGeometry.NoTaper = function ( u ) { + constructor: SpotLightShadow, - return 1; + isSpotLightShadow: true, - }; + update: function ( light ) { - TubeGeometry.SinusoidalTaper = function ( u ) { + var fov = exports.Math.RAD2DEG * 2 * light.angle; + var aspect = this.mapSize.width / this.mapSize.height; + var far = light.distance || 500; - return Math.sin( Math.PI * u ); + var camera = this.camera; - }; + if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) { - // For computing of Frenet frames, exposing the tangents, normals and binormals the spline - TubeGeometry.FrenetFrames = function ( path, segments, closed ) { + camera.fov = fov; + camera.aspect = aspect; + camera.far = far; + camera.updateProjectionMatrix(); - var normal = new Vector3(), + } - tangents = [], - normals = [], - binormals = [], + } - vec = new Vector3(), - mat = new Matrix4(), + } ); - numpoints = segments + 1, - theta, - smallest, + /** + * @author alteredq / http://alteredqualia.com/ + */ - tx, ty, tz, - i, u; + function SpotLight( color, intensity, distance, angle, penumbra, decay ) { + Light.call( this, color, intensity ); - // expose internals - this.tangents = tangents; - this.normals = normals; - this.binormals = binormals; + this.type = 'SpotLight'; - // compute the tangent vectors for each segment on the path + this.position.copy( Object3D.DefaultUp ); + this.updateMatrix(); - for ( i = 0; i < numpoints; i ++ ) { + this.target = new Object3D(); - u = i / ( numpoints - 1 ); + 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; + } + } ); - tangents[ i ] = path.getTangentAt( u ); - tangents[ i ].normalize(); + 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. - } + this.shadow = new SpotLightShadow(); - 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(); - } + SpotLight.prototype = Object.assign( Object.create( Light.prototype ), { - function initialNormal2() { + constructor: SpotLight, - // This uses the Frenet-Serret formula for deriving binormal - var t2 = path.getTangentAt( epsilon ); + isSpotLight: true, - normals[ 0 ] = new THREE.Vector3().subVectors( t2, tangents[ 0 ] ).normalize(); - binormals[ 0 ] = new THREE.Vector3().crossVectors( tangents[ 0 ], normals[ 0 ] ); + copy: function ( source ) { - normals[ 0 ].crossVectors( binormals[ 0 ], tangents[ 0 ] ).normalize(); // last binormal x tangent - binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize(); + Light.prototype.copy.call( this, source ); - } - */ + this.distance = source.distance; + this.angle = source.angle; + this.penumbra = source.penumbra; + this.decay = source.decay; - function initialNormal3() { + this.target = source.target.clone(); - // select an initial normal vector perpendicular to the first tangent vector, - // and in the direction of the smallest tangent xyz component + this.shadow = source.shadow.clone(); - normals[ 0 ] = new Vector3(); - binormals[ 0 ] = new Vector3(); - smallest = Number.MAX_VALUE; - tx = Math.abs( tangents[ 0 ].x ); - ty = Math.abs( tangents[ 0 ].y ); - tz = Math.abs( tangents[ 0 ].z ); + return this; - if ( tx <= smallest ) { + } - smallest = tx; - normal.set( 1, 0, 0 ); + } ); - } + /** + * @author mrdoob / http://mrdoob.com/ + */ - if ( ty <= smallest ) { - smallest = ty; - normal.set( 0, 1, 0 ); + function PointLight( color, intensity, distance, decay ) { - } + Light.call( this, color, intensity ); - if ( tz <= smallest ) { + this.type = 'PointLight'; - normal.set( 0, 0, 1 ); + 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; + }, + 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 ); } + } ); - vec.crossVectors( tangents[ 0 ], normal ).normalize(); - - normals[ 0 ].crossVectors( tangents[ 0 ], vec ); - binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ); - - } + this.distance = ( distance !== undefined ) ? distance : 0; + this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. + this.shadow = new LightShadow( new PerspectiveCamera( 90, 1, 0.5, 500 ) ); - // compute the slowly-varying normal and binormal vectors for each segment on the path + } - for ( i = 1; i < numpoints; i ++ ) { + PointLight.prototype = Object.assign( Object.create( Light.prototype ), { - normals[ i ] = normals[ i - 1 ].clone(); + constructor: PointLight, - binormals[ i ] = binormals[ i - 1 ].clone(); + isPointLight: true, - vec.crossVectors( tangents[ i - 1 ], tangents[ i ] ); + copy: function ( source ) { - if ( vec.length() > Number.EPSILON ) { + Light.prototype.copy.call( this, source ); - vec.normalize(); + this.distance = source.distance; + this.decay = source.decay; - theta = Math.acos( exports.Math.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors + this.shadow = source.shadow.clone(); - normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) ); + return this; - } + } - binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); + } ); - } + /** + * @author mrdoob / http://mrdoob.com/ + */ + function DirectionalLightShadow( light ) { - // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same + LightShadow.call( this, new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) ); - if ( closed ) { + } - theta = Math.acos( exports.Math.clamp( normals[ 0 ].dot( normals[ numpoints - 1 ] ), - 1, 1 ) ); - theta /= ( numpoints - 1 ); + DirectionalLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), { - if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ numpoints - 1 ] ) ) > 0 ) { + constructor: DirectionalLightShadow - theta = - theta; + } ); - } + /** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ - for ( i = 1; i < numpoints; i ++ ) { + function DirectionalLight( color, intensity ) { - // twist a little... - normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) ); - binormals[ i ].crossVectors( tangents[ i ], normals[ i ] ); + Light.call( this, color, intensity ); - } + this.type = 'DirectionalLight'; - } + this.position.copy( Object3D.DefaultUp ); + this.updateMatrix(); - }; + this.target = new Object3D(); - /** - * @author Mugen87 / https://github.com/Mugen87 - * - * see: http://www.blackpawn.com/texts/pqtorus/ - */ - function TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) { + this.shadow = new DirectionalLightShadow(); - BufferGeometry.call( this ); + } - this.type = 'TorusKnotBufferGeometry'; + DirectionalLight.prototype = Object.assign( Object.create( Light.prototype ), { - this.parameters = { - radius: radius, - tube: tube, - tubularSegments: tubularSegments, - radialSegments: radialSegments, - p: p, - q: q - }; + constructor: DirectionalLight, - radius = radius || 100; - tube = tube || 40; - tubularSegments = Math.floor( tubularSegments ) || 64; - radialSegments = Math.floor( radialSegments ) || 8; - p = p || 2; - q = q || 3; + isDirectionalLight: true, - // used to calculate buffer length - var vertexCount = ( ( radialSegments + 1 ) * ( tubularSegments + 1 ) ); - var indexCount = radialSegments * tubularSegments * 2 * 3; + copy: function ( source ) { - // buffers - var indices = new BufferAttribute( new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ) , 1 ); - var vertices = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); - var normals = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); - var uvs = new BufferAttribute( new Float32Array( vertexCount * 2 ), 2 ); + Light.prototype.copy.call( this, source ); - // helper variables - var i, j, index = 0, indexOffset = 0; + this.target = source.target.clone(); - var vertex = new Vector3(); - var normal = new Vector3(); - var uv = new Vector2(); + this.shadow = source.shadow.clone(); - var P1 = new Vector3(); - var P2 = new Vector3(); + return this; - var B = new Vector3(); - var T = new Vector3(); - var N = new Vector3(); + } - // generate vertices, normals and uvs + } ); - for ( i = 0; i <= tubularSegments; ++ i ) { + /** + * @author mrdoob / http://mrdoob.com/ + */ - // the radian "u" is used to calculate the position on the torus curve of the current tubular segement + function AmbientLight( color, intensity ) { - var u = i / tubularSegments * p * Math.PI * 2; + Light.call( this, color, intensity ); - // 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 + this.type = 'AmbientLight'; - calculatePositionOnCurve( u, p, q, radius, P1 ); - calculatePositionOnCurve( u + 0.01, p, q, radius, P2 ); + this.castShadow = undefined; - // calculate orthonormal basis + } - T.subVectors( P2, P1 ); - N.addVectors( P2, P1 ); - B.crossVectors( T, N ); - N.crossVectors( B, T ); + AmbientLight.prototype = Object.assign( Object.create( Light.prototype ), { - // normalize B, N. T can be ignored, we don't use it + constructor: AmbientLight, - B.normalize(); - N.normalize(); + isAmbientLight: true, - 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. + /** + * @author tschw + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ - var v = j / radialSegments * Math.PI * 2; - var cx = - tube * Math.cos( v ); - var cy = tube * Math.sin( v ); + exports.AnimationUtils = { - // 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 + // same as Array.prototype.slice, but also works on typed arrays + arraySlice: function( array, from, to ) { - 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 ); + if ( exports.AnimationUtils.isTypedArray( array ) ) { - // vertex - vertices.setXYZ( index, vertex.x, vertex.y, vertex.z ); + return new array.constructor( array.subarray( from, to ) ); - // 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 ); + } - // uv - uv.x = i / tubularSegments; - uv.y = j / radialSegments; - uvs.setXY( index, uv.x, uv.y ); + return array.slice( from, to ); - // increase index - index ++; + }, - } + // converts an array to a specific type + convertArray: function( array, type, forceClone ) { - } + if ( ! array || // let 'undefined' and 'null' pass + ! forceClone && array.constructor === type ) return array; - // generate indices + if ( typeof type.BYTES_PER_ELEMENT === 'number' ) { - for ( j = 1; j <= tubularSegments; j ++ ) { + return new type( array ); // create typed array - 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; + return Array.prototype.slice.call( array ); // create Array - // face one - indices.setX( indexOffset, a ); indexOffset++; - indices.setX( indexOffset, b ); indexOffset++; - indices.setX( indexOffset, d ); indexOffset++; + }, - // face two - indices.setX( indexOffset, b ); indexOffset++; - indices.setX( indexOffset, c ); indexOffset++; - indices.setX( indexOffset, d ); indexOffset++; + isTypedArray: function( object ) { - } + return ArrayBuffer.isView( object ) && + ! ( object instanceof DataView ); - } + }, - // build geometry + // returns an array by which times and values can be sorted + getKeyframeOrder: function( times ) { - this.setIndex( indices ); - this.addAttribute( 'position', vertices ); - this.addAttribute( 'normal', normals ); - this.addAttribute( 'uv', uvs ); + function compareTime( i, j ) { - // this function calculates the current position on the torus curve + return times[ i ] - times[ j ]; - 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 ); + var n = times.length; + var result = new Array( n ); + for ( var i = 0; i !== n; ++ i ) result[ i ] = i; - position.x = radius * ( 2 + cs ) * 0.5 * cu; - position.y = radius * ( 2 + cs ) * su * 0.5; - position.z = radius * Math.sin( quOverP ) * 0.5; + result.sort( compareTime ); - } + return result; - } + }, - TorusKnotBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - TorusKnotBufferGeometry.prototype.constructor = TorusKnotBufferGeometry; + // uses the array previously returned by 'getKeyframeOrder' to sort data + sortedArray: function( values, stride, order ) { - /** - * @author oosmoxiecode - */ + var nValues = values.length; + var result = new values.constructor( nValues ); - function TorusKnotGeometry( radius, tube, tubularSegments, radialSegments, p, q, heightScale ) { + for ( var i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) { - Geometry.call( this ); + var srcOffset = order[ i ] * stride; - this.type = 'TorusKnotGeometry'; + for ( var j = 0; j !== stride; ++ j ) { - this.parameters = { - radius: radius, - tube: tube, - tubularSegments: tubularSegments, - radialSegments: radialSegments, - p: p, - q: q - }; + result[ dstOffset ++ ] = values[ srcOffset + j ]; - if( heightScale !== undefined ) console.warn( 'THREE.TorusKnotGeometry: heightScale has been deprecated. Use .scale( x, y, z ) instead.' ); + } - this.fromBufferGeometry( new TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) ); - this.mergeVertices(); + } - } + return result; - TorusKnotGeometry.prototype = Object.create( Geometry.prototype ); - TorusKnotGeometry.prototype.constructor = TorusKnotGeometry; + }, - /** - * @author Mugen87 / https://github.com/Mugen87 - */ + // function for parsing AOS keyframe formats + flattenJSON: function( jsonKeys, times, values, valuePropertyName ) { - function TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) { + var i = 1, key = jsonKeys[ 0 ]; - BufferGeometry.call( this ); + while ( key !== undefined && key[ valuePropertyName ] === undefined ) { - this.type = 'TorusBufferGeometry'; + key = jsonKeys[ i ++ ]; - this.parameters = { - radius: radius, - tube: tube, - radialSegments: radialSegments, - tubularSegments: tubularSegments, - arc: arc - }; + } - radius = radius || 100; - tube = tube || 40; - radialSegments = Math.floor( radialSegments ) || 8; - tubularSegments = Math.floor( tubularSegments ) || 6; - arc = arc || Math.PI * 2; + if ( key === undefined ) return; // no data - // used to calculate buffer length - var vertexCount = ( ( radialSegments + 1 ) * ( tubularSegments + 1 ) ); - var indexCount = radialSegments * tubularSegments * 2 * 3; + var value = key[ valuePropertyName ]; + if ( value === undefined ) return; // no data - // 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 ); + if ( Array.isArray( value ) ) { - // offset variables - var vertexBufferOffset = 0; - var uvBufferOffset = 0; - var indexBufferOffset = 0; + do { - // helper variables - var center = new Vector3(); - var vertex = new Vector3(); - var normal = new Vector3(); + value = key[ valuePropertyName ]; - var j, i; + if ( value !== undefined ) { - // generate vertices, normals and uvs + times.push( key.time ); + values.push.apply( values, value ); // push all elements - for ( j = 0; j <= radialSegments; j ++ ) { + } - for ( i = 0; i <= tubularSegments; i ++ ) { + key = jsonKeys[ i ++ ]; - var u = i / tubularSegments * arc; - var v = j / radialSegments * Math.PI * 2; + } while ( key !== undefined ); - // 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 ); + } else if ( value.toArray !== undefined ) { + // ...assume THREE.Math-ish - vertices[ vertexBufferOffset ] = vertex.x; - vertices[ vertexBufferOffset + 1 ] = vertex.y; - vertices[ vertexBufferOffset + 2 ] = vertex.z; + do { - // this vector is used to calculate the normal - center.x = radius * Math.cos( u ); - center.y = radius * Math.sin( u ); + value = key[ valuePropertyName ]; - // normal - normal.subVectors( vertex, center ).normalize(); + if ( value !== undefined ) { - normals[ vertexBufferOffset ] = normal.x; - normals[ vertexBufferOffset + 1 ] = normal.y; - normals[ vertexBufferOffset + 2 ] = normal.z; + times.push( key.time ); + value.toArray( values, values.length ); - // uv - uvs[ uvBufferOffset ] = i / tubularSegments; - uvs[ uvBufferOffset + 1 ] = j / radialSegments; + } - // update offsets - vertexBufferOffset += 3; - uvBufferOffset += 2; + key = jsonKeys[ i ++ ]; - } + } while ( key !== undefined ); - } + } else { + // otherwise push as-is - // generate indices + do { - for ( j = 1; j <= radialSegments; j ++ ) { + value = key[ valuePropertyName ]; - for ( i = 1; i <= tubularSegments; i ++ ) { + if ( value !== undefined ) { - // 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; + times.push( key.time ); + values.push( value ); - // face one - indices[ indexBufferOffset ] = a; - indices[ indexBufferOffset + 1 ] = b; - indices[ indexBufferOffset + 2 ] = d; + } - // face two - indices[ indexBufferOffset + 3 ] = b; - indices[ indexBufferOffset + 4 ] = c; - indices[ indexBufferOffset + 5 ] = d; + key = jsonKeys[ i ++ ]; - // update offset - indexBufferOffset += 6; + } while ( key !== undefined ); } } - // build geometry - this.setIndex( new BufferAttribute( indices, 1 ) ); - this.addAttribute( 'position', new BufferAttribute( vertices, 3 ) ); - this.addAttribute( 'normal', new BufferAttribute( normals, 3 ) ); - this.addAttribute( 'uv', new BufferAttribute( uvs, 2 ) ); - - } - - TorusBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - TorusBufferGeometry.prototype.constructor = TorusBufferGeometry; + }; /** - * @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 + * Abstract base class of interpolants over parametric samples. + * + * The parameter domain is one dimensional, typically the time or a path + * along a curve defined by the data. + * + * The sample values can have any dimensionality and derived classes may + * apply special interpretations to the data. + * + * This class provides the interval seek in a Template Method, deferring + * the actual interpolation to derived classes. + * + * Time complexity is O(1) for linear access crossing at most two points + * and O(log N) for random access, where N is the number of positions. + * + * References: + * + * http://www.oodesign.com/template-method-pattern.html + * + * @author tschw */ - function TorusGeometry( radius, tube, radialSegments, tubularSegments, arc ) { + function Interpolant( + parameterPositions, sampleValues, sampleSize, resultBuffer ) { - Geometry.call( this ); + this.parameterPositions = parameterPositions; + this._cachedIndex = 0; - this.type = 'TorusGeometry'; + this.resultBuffer = resultBuffer !== undefined ? + resultBuffer : new sampleValues.constructor( sampleSize ); + this.sampleValues = sampleValues; + this.valueSize = sampleSize; - this.parameters = { - radius: radius, - tube: tube, - radialSegments: radialSegments, - tubularSegments: tubularSegments, - arc: arc - }; + } - this.fromBufferGeometry( new TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) ); + Interpolant.prototype = { - } + constructor: Interpolant, - TorusGeometry.prototype = Object.create( Geometry.prototype ); - TorusGeometry.prototype.constructor = TorusGeometry; + evaluate: function( t ) { - /** - * @author zz85 / http://www.lab4games.net/zz85/blog - */ + var pp = this.parameterPositions, + i1 = this._cachedIndex, - exports.ShapeUtils = { + t1 = pp[ i1 ], + t0 = pp[ i1 - 1 ]; - // calculate area of the contour polygon + validate_interval: { - area: function ( contour ) { + seek: { - var n = contour.length; - var a = 0.0; + var right; - for ( var p = n - 1, q = 0; q < n; p = q ++ ) { + linear_scan: { + //- See http://jsperf.com/comparison-to-undefined/3 + //- slower code: + //- + //- if ( t >= t1 || t1 === undefined ) { + forward_scan: if ( ! ( t < t1 ) ) { - a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; + for ( var giveUpAt = i1 + 2; ;) { - } + if ( t1 === undefined ) { - return a * 0.5; + if ( t < t0 ) break forward_scan; - }, + // after end - triangulate: ( function () { + i1 = pp.length; + this._cachedIndex = i1; + return this.afterEnd_( i1 - 1, t, t0 ); - /** - * 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 ) { + if ( i1 === giveUpAt ) break; // this loop - var p; - var ax, ay, bx, by; - var cx, cy, px, py; + t0 = t1; + t1 = pp[ ++ i1 ]; - ax = contour[ verts[ u ] ].x; - ay = contour[ verts[ u ] ].y; + if ( t < t1 ) { - bx = contour[ verts[ v ] ].x; - by = contour[ verts[ v ] ].y; + // we have arrived at the sought interval + break seek; - 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; + // prepare binary search on the right side of the index + right = pp.length; + break linear_scan; - aX = cx - bx; aY = cy - by; - bX = ax - cx; bY = ay - cy; - cX = bx - ax; cY = by - ay; + } - for ( p = 0; p < n; p ++ ) { + //- slower code: + //- if ( t < t0 || t0 === undefined ) { + if ( ! ( t >= t0 ) ) { - px = contour[ verts[ p ] ].x; - py = contour[ verts[ p ] ].y; + // looping? - if ( ( ( px === ax ) && ( py === ay ) ) || - ( ( px === bx ) && ( py === by ) ) || - ( ( px === cx ) && ( py === cy ) ) ) continue; + var t1global = pp[ 1 ]; - apx = px - ax; apy = py - ay; - bpx = px - bx; bpy = py - by; - cpx = px - cx; cpy = py - cy; + if ( t < t1global ) { - // see if p is inside triangle abc + i1 = 2; // + 1, using the scan for the details + t0 = t1global; - 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; + // linear reverse scan - } + for ( var giveUpAt = i1 - 2; ;) { - return true; + if ( t0 === undefined ) { - } + // before start - // takes in an contour array and returns + this._cachedIndex = 0; + return this.beforeStart_( 0, t, t1 ); - return function triangulate( contour, indices ) { + } - var n = contour.length; + if ( i1 === giveUpAt ) break; // this loop - if ( n < 3 ) return null; + t1 = t0; + t0 = pp[ -- i1 - 1 ]; - var result = [], - verts = [], - vertIndices = []; + if ( t >= t0 ) { - /* we want a counter-clockwise polygon in verts */ + // we have arrived at the sought interval + break seek; - var u, v, w; + } - if ( exports.ShapeUtils.area( contour ) > 0.0 ) { + } - for ( v = 0; v < n; v ++ ) verts[ v ] = v; + // prepare binary search on the left side of the index + right = i1; + i1 = 0; + break linear_scan; - } else { + } - for ( v = 0; v < n; v ++ ) verts[ v ] = ( n - 1 ) - v; + // the interval is valid - } + break validate_interval; - var nv = n; + } // linear scan - /* remove nv - 2 vertices, creating 1 triangle every time */ + // binary search - var count = 2 * nv; /* error detection */ + while ( i1 < right ) { - for ( v = nv - 1; nv > 2; ) { + var mid = ( i1 + right ) >>> 1; - /* if we loop, it is probably a non-simple polygon */ + if ( t < pp[ mid ] ) { - if ( ( count -- ) <= 0 ) { + right = mid; - //** Triangulate: ERROR - probable bad polygon! + } else { - //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()' ); + i1 = mid + 1; - if ( indices ) return vertIndices; - return result; + } } - /* three consecutive vertices in current polygon, */ + t1 = pp[ i1 ]; + t0 = pp[ i1 - 1 ]; - 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 */ + // check boundary cases, again - if ( snip( contour, u, v, w, nv, verts ) ) { + if ( t0 === undefined ) { - var a, b, c, s, t; + this._cachedIndex = 0; + return this.beforeStart_( 0, t, t1 ); - /* true names of the vertices */ + } - a = verts[ u ]; - b = verts[ v ]; - c = verts[ w ]; + if ( t1 === undefined ) { - /* output Triangle */ + i1 = pp.length; + this._cachedIndex = i1; + return this.afterEnd_( i1 - 1, t0, t ); - result.push( [ contour[ a ], - contour[ b ], - contour[ c ] ] ); + } + } // seek - vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] ); + this._cachedIndex = i1; - /* remove v from the remaining polygon */ + this.intervalChanged_( i1, t0, t1 ); - for ( s = v, t = v + 1; t < nv; s ++, t ++ ) { + } // validate_interval - verts[ s ] = verts[ t ]; + return this.interpolate_( i1, t0, t, t1 ); - } + }, - nv --; + settings: null, // optional, subclass-specific settings structure + // Note: The indirection allows central control of many interpolants. - /* reset error detection counter */ + // --- Protected interface - count = 2 * nv; + DefaultSettings_: {}, - } + getSettings_: function() { - } + return this.settings || this.DefaultSettings_; - if ( indices ) return vertIndices; - return result; + }, - } + copySampleValue_: function( index ) { - } )(), + // copies a sample value to the result buffer - triangulateShape: function ( contour, holes ) { + var result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, + offset = index * stride; - function removeDupEndPts(points) { + for ( var i = 0; i !== stride; ++ i ) { - var l = points.length; + result[ i ] = values[ offset + i ]; - if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) { + } - points.pop(); + return result; - } + }, - } + // Template methods for derived classes: - removeDupEndPts( contour ); - holes.forEach( removeDupEndPts ); + interpolate_: function( i1, t0, t, t1 ) { - function point_in_segment_2D_colin( inSegPt1, inSegPt2, inOtherPt ) { + throw new Error( "call to abstract method" ); + // implementations shall return this.resultBuffer - // inOtherPt needs to be collinear to the inSegment - if ( inSegPt1.x !== inSegPt2.x ) { + }, - if ( inSegPt1.x < inSegPt2.x ) { + intervalChanged_: function( i1, t0, t1 ) { - return ( ( inSegPt1.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt2.x ) ); + // empty - } else { + } - return ( ( inSegPt2.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt1.x ) ); + }; - } + Object.assign( Interpolant.prototype, { - } else { + beforeStart_: //( 0, t, t0 ), returns this.resultBuffer + Interpolant.prototype.copySampleValue_, - if ( inSegPt1.y < inSegPt2.y ) { + afterEnd_: //( N-1, tN-1, t ), returns this.resultBuffer + Interpolant.prototype.copySampleValue_ - return ( ( inSegPt1.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt2.y ) ); + } ); - } else { + /** + * Fast and simple cubic spline interpolant. + * + * It was derived from a Hermitian construction setting the first derivative + * at each sample position to the linear slope between neighboring positions + * over their parameter interval. + * + * @author tschw + */ - return ( ( inSegPt2.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt1.y ) ); + function CubicInterpolant( + parameterPositions, sampleValues, sampleSize, resultBuffer ) { - } + Interpolant.call( + this, parameterPositions, sampleValues, sampleSize, resultBuffer ); - } + this._weightPrev = -0; + this._offsetPrev = -0; + this._weightNext = -0; + this._offsetNext = -0; - } + } - function intersect_segments_2D( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1, inSeg2Pt2, inExcludeAdjacentSegs ) { + CubicInterpolant.prototype = + Object.assign( Object.create( Interpolant.prototype ), { - var seg1dx = inSeg1Pt2.x - inSeg1Pt1.x, seg1dy = inSeg1Pt2.y - inSeg1Pt1.y; - var seg2dx = inSeg2Pt2.x - inSeg2Pt1.x, seg2dy = inSeg2Pt2.y - inSeg2Pt1.y; + constructor: CubicInterpolant, - var seg1seg2dx = inSeg1Pt1.x - inSeg2Pt1.x; - var seg1seg2dy = inSeg1Pt1.y - inSeg2Pt1.y; + DefaultSettings_: { - var limit = seg1dy * seg2dx - seg1dx * seg2dy; - var perpSeg1 = seg1dy * seg1seg2dx - seg1dx * seg1seg2dy; + endingStart: ZeroCurvatureEnding, + endingEnd: ZeroCurvatureEnding - if ( Math.abs( limit ) > Number.EPSILON ) { + }, - // not parallel + intervalChanged_: function( i1, t0, t1 ) { - var perpSeg2; - if ( limit > 0 ) { + var pp = this.parameterPositions, + iPrev = i1 - 2, + iNext = i1 + 1, - if ( ( perpSeg1 < 0 ) || ( perpSeg1 > limit ) ) return []; - perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy; - if ( ( perpSeg2 < 0 ) || ( perpSeg2 > limit ) ) return []; + tPrev = pp[ iPrev ], + tNext = pp[ iNext ]; - } else { + if ( tPrev === undefined ) { - if ( ( perpSeg1 > 0 ) || ( perpSeg1 < limit ) ) return []; - perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy; - if ( ( perpSeg2 > 0 ) || ( perpSeg2 < limit ) ) return []; + switch ( this.getSettings_().endingStart ) { - } + case ZeroSlopeEnding: - // i.e. to reduce rounding errors - // intersection at endpoint of segment#1? - if ( perpSeg2 === 0 ) { + // f'(t0) = 0 + iPrev = i1; + tPrev = 2 * t0 - t1; - if ( ( inExcludeAdjacentSegs ) && - ( ( perpSeg1 === 0 ) || ( perpSeg1 === limit ) ) ) return []; - return [ inSeg1Pt1 ]; + break; - } - if ( perpSeg2 === limit ) { + case WrapAroundEnding: - if ( ( inExcludeAdjacentSegs ) && - ( ( perpSeg1 === 0 ) || ( perpSeg1 === limit ) ) ) return []; - return [ inSeg1Pt2 ]; + // use the other end of the curve + iPrev = pp.length - 2; + tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ]; - } - // intersection at endpoint of segment#2? - if ( perpSeg1 === 0 ) return [ inSeg2Pt1 ]; - if ( perpSeg1 === limit ) return [ inSeg2Pt2 ]; + break; - // return real intersection point - var factorSeg1 = perpSeg2 / limit; - return [ { x: inSeg1Pt1.x + factorSeg1 * seg1dx, - y: inSeg1Pt1.y + factorSeg1 * seg1dy } ]; + default: // ZeroCurvatureEnding - } else { + // f''(t0) = 0 a.k.a. Natural Spline + iPrev = i1; + tPrev = t1; - // 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 + if ( tNext === undefined ) { - } - // segment#1 is a single point - if ( seg1Pt ) { + switch ( this.getSettings_().endingEnd ) { - if ( ! point_in_segment_2D_colin( inSeg2Pt1, inSeg2Pt2, inSeg1Pt1 ) ) return []; // but not in segment#2 - return [ inSeg1Pt1 ]; + case ZeroSlopeEnding: - } - // segment#2 is a single point - if ( seg2Pt ) { + // f'(tN) = 0 + iNext = i1; + tNext = 2 * t1 - t0; - if ( ! point_in_segment_2D_colin( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1 ) ) return []; // but not in segment#1 - return [ inSeg2Pt1 ]; + break; - } + case WrapAroundEnding: - // they are collinear segments, which might overlap - var seg1min, seg1max, seg1minVal, seg1maxVal; - var seg2min, seg2max, seg2minVal, seg2maxVal; - if ( seg1dx !== 0 ) { + // use the other end of the curve + iNext = 1; + tNext = t1 + pp[ 1 ] - pp[ 0 ]; - // the segments are NOT on a vertical line - if ( inSeg1Pt1.x < inSeg1Pt2.x ) { + break; - seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.x; - seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.x; + default: // ZeroCurvatureEnding - } else { + // f''(tN) = 0, a.k.a. Natural Spline + iNext = i1 - 1; + tNext = t0; - 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; + var halfDt = ( t1 - t0 ) * 0.5, + stride = this.valueSize; - } else { + this._weightPrev = halfDt / ( t0 - tPrev ); + this._weightNext = halfDt / ( tNext - t1 ); + this._offsetPrev = iPrev * stride; + this._offsetNext = iNext * stride; - seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.x; - seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.x; + }, - } + interpolate_: function( i1, t0, t, t1 ) { - } else { + var result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, - // the segments are on a vertical line - if ( inSeg1Pt1.y < inSeg1Pt2.y ) { + o1 = i1 * stride, o0 = o1 - stride, + oP = this._offsetPrev, oN = this._offsetNext, + wP = this._weightPrev, wN = this._weightNext, - seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.y; - seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.y; + p = ( t - t0 ) / ( t1 - t0 ), + pp = p * p, + ppp = pp * p; - } else { + // evaluate polynomials - seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.y; - seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.y; + var sP = - wP * ppp + 2 * wP * pp - wP * p; + var s0 = ( 1 + wP ) * ppp + (-1.5 - 2 * wP ) * pp + ( -0.5 + wP ) * p + 1; + var s1 = (-1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p; + var sN = wN * ppp - wN * pp; - } - if ( inSeg2Pt1.y < inSeg2Pt2.y ) { + // combine data linearly - seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.y; - seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.y; + for ( var i = 0; i !== stride; ++ i ) { - } else { + result[ i ] = + sP * values[ oP + i ] + + s0 * values[ o0 + i ] + + s1 * values[ o1 + i ] + + sN * values[ oN + i ]; - seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.y; - seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.y; + } - } + return result; - } - if ( seg1minVal <= seg2minVal ) { + } - if ( seg1maxVal < seg2minVal ) return []; - if ( seg1maxVal === seg2minVal ) { + } ); - if ( inExcludeAdjacentSegs ) return []; - return [ seg2min ]; + /** + * @author tschw + */ - } - if ( seg1maxVal <= seg2maxVal ) return [ seg2min, seg1max ]; - return [ seg2min, seg2max ]; + function LinearInterpolant( + parameterPositions, sampleValues, sampleSize, resultBuffer ) { - } else { + Interpolant.call( + this, parameterPositions, sampleValues, sampleSize, resultBuffer ); - if ( seg1minVal > seg2maxVal ) return []; - if ( seg1minVal === seg2maxVal ) { + } - if ( inExcludeAdjacentSegs ) return []; - return [ seg1min ]; + LinearInterpolant.prototype = + Object.assign( Object.create( Interpolant.prototype ), { - } - if ( seg1maxVal <= seg2maxVal ) return [ seg1min, seg1max ]; - return [ seg1min, seg2max ]; + constructor: LinearInterpolant, - } + interpolate_: function( i1, t0, t, t1 ) { - } + var result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, - } + offset1 = i1 * stride, + offset0 = offset1 - stride, - function isPointInsideAngle( inVertex, inLegFromPt, inLegToPt, inOtherPt ) { + weight1 = ( t - t0 ) / ( t1 - t0 ), + weight0 = 1 - weight1; - // The order of legs is important + for ( var i = 0; i !== stride; ++ i ) { - // 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; + result[ i ] = + values[ offset0 + i ] * weight0 + + values[ offset1 + i ] * weight1; - // 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 ) { + return result; - // angle != 180 deg. + } - var other2toAngle = otherPtX * legToPtY - otherPtY * legToPtX; - // console.log( "from2to: " + from2toAngle + ", from2other: " + from2otherAngle + ", other2to: " + other2toAngle ); + } ); - if ( from2toAngle > 0 ) { + /** + * + * Interpolant that evaluates to the sample value at the position preceeding + * the parameter. + * + * @author tschw + */ - // main angle < 180 deg. - return ( ( from2otherAngle >= 0 ) && ( other2toAngle >= 0 ) ); + function DiscreteInterpolant( + parameterPositions, sampleValues, sampleSize, resultBuffer ) { - } else { + Interpolant.call( + this, parameterPositions, sampleValues, sampleSize, resultBuffer ); - // main angle > 180 deg. - return ( ( from2otherAngle >= 0 ) || ( other2toAngle >= 0 ) ); + } - } + DiscreteInterpolant.prototype = + Object.assign( Object.create( Interpolant.prototype ), { - } else { + constructor: DiscreteInterpolant, - // angle == 180 deg. - // console.log( "from2to: 180 deg., from2other: " + from2otherAngle ); - return ( from2otherAngle > 0 ); + interpolate_: function( i1, t0, t, t1 ) { - } + return this.copySampleValue_( i1 - 1 ); - } + } + } ); - function removeHoles( contour, holes ) { + var KeyframeTrackPrototype; - var shape = contour.concat(); // work on this shape - var hole; + KeyframeTrackPrototype = { - function isCutLineInsideAngles( inShapeIdx, inHoleIdx ) { + TimeBufferType: Float32Array, + ValueBufferType: Float32Array, - // Check if hole point lies within angle around shape point - var lastShapeIdx = shape.length - 1; + DefaultInterpolation: InterpolateLinear, - var prevShapeIdx = inShapeIdx - 1; - if ( prevShapeIdx < 0 ) prevShapeIdx = lastShapeIdx; + InterpolantFactoryMethodDiscrete: function( result ) { - var nextShapeIdx = inShapeIdx + 1; - if ( nextShapeIdx > lastShapeIdx ) nextShapeIdx = 0; + return new DiscreteInterpolant( + this.times, this.values, this.getValueSize(), result ); - 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; + InterpolantFactoryMethodLinear: function( result ) { - } + return new LinearInterpolant( + this.times, this.values, this.getValueSize(), result ); - // Check if shape point lies within angle around hole point - var lastHoleIdx = hole.length - 1; + }, - var prevHoleIdx = inHoleIdx - 1; - if ( prevHoleIdx < 0 ) prevHoleIdx = lastHoleIdx; + InterpolantFactoryMethodSmooth: function( result ) { - var nextHoleIdx = inHoleIdx + 1; - if ( nextHoleIdx > lastHoleIdx ) nextHoleIdx = 0; + return new CubicInterpolant( + this.times, this.values, this.getValueSize(), result ); - 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; + setInterpolation: function( interpolation ) { - } + var factoryMethod; - return true; + switch ( interpolation ) { - } + case InterpolateDiscrete: - function intersectsShapeEdge( inShapePt, inHolePt ) { + factoryMethod = this.InterpolantFactoryMethodDiscrete; - // checks for intersections with shape edges - var sIdx, nextIdx, intersection; - for ( sIdx = 0; sIdx < shape.length; sIdx ++ ) { + break; - nextIdx = sIdx + 1; nextIdx %= shape.length; - intersection = intersect_segments_2D( inShapePt, inHolePt, shape[ sIdx ], shape[ nextIdx ], true ); - if ( intersection.length > 0 ) return true; + case InterpolateLinear: - } + factoryMethod = this.InterpolantFactoryMethodLinear; - return false; + break; - } + case InterpolateSmooth: - var indepHoles = []; + factoryMethod = this.InterpolantFactoryMethodSmooth; - function intersectsHoleEdge( inShapePt, inHolePt ) { + break; - // 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 ++ ) { + if ( factoryMethod === undefined ) { - nextIdx = hIdx + 1; nextIdx %= chkHole.length; - intersection = intersect_segments_2D( inShapePt, inHolePt, chkHole[ hIdx ], chkHole[ nextIdx ], true ); - if ( intersection.length > 0 ) return true; + var message = "unsupported interpolation for " + + this.ValueTypeName + " keyframe track named " + this.name; - } + if ( this.createInterpolant === undefined ) { - } - return false; + // fall back to default, unless the default itself is messed up + if ( interpolation !== this.DefaultInterpolation ) { - } + this.setInterpolation( this.DefaultInterpolation ); - var holeIndex, shapeIndex, - shapePt, holePt, - holeIdx, cutKey, failedCuts = [], - tmpShape1, tmpShape2, - tmpHole1, tmpHole2; + } else { - for ( var h = 0, hl = holes.length; h < hl; h ++ ) { + throw new Error( message ); // fatal, in this case - indepHoles.push( h ); + } } - var minShapeIndex = 0; - var counter = indepHoles.length * 2; - while ( indepHoles.length > 0 ) { + console.warn( message ); + return; - counter --; - if ( counter < 0 ) { + } - console.log( "Infinite Loop! Holes left:" + indepHoles.length + ", Probably Hole outside Shape!" ); - break; + this.createInterpolant = factoryMethod; - } + }, - // search for shape-vertex and hole-vertex, - // which can be connected without intersections - for ( shapeIndex = minShapeIndex; shapeIndex < shape.length; shapeIndex ++ ) { + getInterpolation: function() { - shapePt = shape[ shapeIndex ]; - holeIndex = - 1; + switch ( this.createInterpolant ) { - // search for hole which can be reached without intersections - for ( var h = 0; h < indepHoles.length; h ++ ) { + case this.InterpolantFactoryMethodDiscrete: - holeIdx = indepHoles[ h ]; + return InterpolateDiscrete; - // prevent multiple checks - cutKey = shapePt.x + ":" + shapePt.y + ":" + holeIdx; - if ( failedCuts[ cutKey ] !== undefined ) continue; + case this.InterpolantFactoryMethodLinear: - hole = holes[ holeIdx ]; - for ( var h2 = 0; h2 < hole.length; h2 ++ ) { + return InterpolateLinear; - holePt = hole[ h2 ]; - if ( ! isCutLineInsideAngles( shapeIndex, h2 ) ) continue; - if ( intersectsShapeEdge( shapePt, holePt ) ) continue; - if ( intersectsHoleEdge( shapePt, holePt ) ) continue; + case this.InterpolantFactoryMethodSmooth: - holeIndex = h2; - indepHoles.splice( h, 1 ); + return InterpolateSmooth; - 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; + getValueSize: function() { - // Debug only, to show the selected cuts - // glob_CutLines.push( [ shapePt, holePt ] ); + return this.values.length / this.times.length; - break; + }, - } - if ( holeIndex >= 0 ) break; // hole-vertex found + // move all keyframes either forwards or backwards in time + shift: function( timeOffset ) { - failedCuts[ cutKey ] = true; // remember failure + if( timeOffset !== 0.0 ) { - } - if ( holeIndex >= 0 ) break; // hole-vertex found + var times = this.times; - } + for( var i = 0, n = times.length; i !== n; ++ i ) { - } + times[ i ] += timeOffset; - return shape; /* shape with no holes */ + } } + return this; + + }, - var i, il, f, face, - key, index, - allPointsMap = {}; + // scale all keyframe times by a factor (useful for frame <-> seconds conversions) + scale: function( timeScale ) { - // 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( timeScale !== 1.0 ) { - var allpoints = contour.concat(); + var times = this.times; - for ( var h = 0, hl = holes.length; h < hl; h ++ ) { + for( var i = 0, n = times.length; i !== n; ++ i ) { - Array.prototype.push.apply( allpoints, holes[ h ] ); + times[ i ] *= timeScale; + + } } - //console.log( "allpoints",allpoints, allpoints.length ); + return this; - // prepare all points map + }, - for ( i = 0, il = allpoints.length; i < il; i ++ ) { + // removes keyframes before and after animation without changing any values within the range [startTime, endTime]. + // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values + trim: function( startTime, endTime ) { - key = allpoints[ i ].x + ":" + allpoints[ i ].y; + var times = this.times, + nKeys = times.length, + from = 0, + to = nKeys - 1; - if ( allPointsMap[ key ] !== undefined ) { + while ( from !== nKeys && times[ from ] < startTime ) ++ from; + while ( to !== -1 && times[ to ] > endTime ) -- to; - console.warn( "THREE.ShapeUtils: Duplicate point", key, i ); + ++ to; // inclusive -> exclusive bound - } + if( from !== 0 || to !== nKeys ) { - allPointsMap[ key ] = i; + // empty tracks are forbidden, so keep at least one keyframe + if ( from >= to ) to = Math.max( to , 1 ), from = to - 1; - } + var stride = this.getValueSize(); + this.times = exports.AnimationUtils.arraySlice( times, from, to ); + this.values = exports.AnimationUtils. + arraySlice( this.values, from * stride, to * stride ); - // remove holes by cutting paths to holes and adding them to the shape - var shapeWithoutHoles = removeHoles( contour, holes ); + } - var triangles = exports.ShapeUtils.triangulate( shapeWithoutHoles, false ); // True returns indices for points of spooled shape - //console.log( "triangles",triangles, triangles.length ); + return this; - // check all face vertices against all points map + }, - for ( i = 0, il = triangles.length; i < il; i ++ ) { + // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable + validate: function() { - face = triangles[ i ]; + var valid = true; - for ( f = 0; f < 3; f ++ ) { + var valueSize = this.getValueSize(); + if ( valueSize - Math.floor( valueSize ) !== 0 ) { - key = face[ f ].x + ":" + face[ f ].y; + console.error( "invalid value size in track", this ); + valid = false; - index = allPointsMap[ key ]; + } - if ( index !== undefined ) { + var times = this.times, + values = this.values, - face[ f ] = index; + nKeys = times.length; - } + if( nKeys === 0 ) { - } + console.error( "track is empty", this ); + valid = false; } - return triangles.concat(); + var prevTime = null; - }, + for( var i = 0; i !== nKeys; i ++ ) { - isClockWise: function ( pts ) { + var currTime = times[ i ]; - return exports.ShapeUtils.area( pts ) < 0; + if ( typeof currTime === 'number' && isNaN( currTime ) ) { - }, + console.error( "time is not a valid number", this, i, currTime ); + valid = false; + break; - // Bezier Curves formulas obtained from - // http://en.wikipedia.org/wiki/B%C3%A9zier_curve + } - // Quad Bezier Functions + if( prevTime !== null && prevTime > currTime ) { - b2: ( function () { + console.error( "out of order keys", this, i, currTime, prevTime ); + valid = false; + break; - function b2p0( t, p ) { + } - var k = 1 - t; - return k * k * p; + prevTime = currTime; } - function b2p1( t, p ) { + if ( values !== undefined ) { - return 2 * ( 1 - t ) * t * p; + if ( exports.AnimationUtils.isTypedArray( values ) ) { - } + for ( var i = 0, n = values.length; i !== n; ++ i ) { - function b2p2( t, p ) { + var value = values[ i ]; - return t * t * p; + if ( isNaN( value ) ) { - } + console.error( "value is not a valid number", this, i, value ); + valid = false; + break; - return function b2( t, p0, p1, p2 ) { + } - return b2p0( t, p0 ) + b2p1( t, p1 ) + b2p2( t, p2 ); + } - }; + } - } )(), + } - // Cubic Bezier Functions + return valid; - b3: ( function () { + }, - function b3p0( t, p ) { + // removes equivalent sequential keys as common in morph target sequences + // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) + optimize: function() { - var k = 1 - t; - return k * k * k * p; + var times = this.times, + values = this.values, + stride = this.getValueSize(), - } + smoothInterpolation = this.getInterpolation() === InterpolateSmooth, - function b3p1( t, p ) { + writeIndex = 1, + lastIndex = times.length - 1; - var k = 1 - t; - return 3 * k * k * t * p; + for( var i = 1; i < lastIndex; ++ i ) { - } + var keep = false; - function b3p2( t, p ) { + var time = times[ i ]; + var timeNext = times[ i + 1 ]; - var k = 1 - t; - return 3 * k * t * t * p; + // remove adjacent keyframes scheduled at the same time - } + if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) { - function b3p3( t, p ) { + if ( ! smoothInterpolation ) { - return t * t * t * p; + // remove unnecessary keyframes same as their neighbors - } + var offset = i * stride, + offsetP = offset - stride, + offsetN = offset + stride; - return function b3( t, p0, p1, p2, p3 ) { + for ( var j = 0; j !== stride; ++ j ) { - return b3p0( t, p0 ) + b3p1( t, p1 ) + b3p2( t, p2 ) + b3p3( t, p3 ); + var value = values[ offset + j ]; - }; + if ( value !== values[ offsetP + j ] || + value !== values[ offsetN + j ] ) { + + keep = true; + break; + + } + + } - } )() + } else keep = true; - }; + } - /** - * @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 - * - * } - **/ + // in-place compaction - function ExtrudeGeometry( shapes, options ) { + if ( keep ) { - if ( typeof( shapes ) === "undefined" ) { + if ( i !== writeIndex ) { - shapes = []; - return; + times[ writeIndex ] = times[ i ]; - } + var readOffset = i * stride, + writeOffset = writeIndex * stride; - Geometry.call( this ); + for ( var j = 0; j !== stride; ++ j ) - this.type = 'ExtrudeGeometry'; + values[ writeOffset + j ] = values[ readOffset + j ]; - shapes = Array.isArray( shapes ) ? shapes : [ shapes ]; + } - this.addShapeList( shapes, options ); + ++ writeIndex; - 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(); + // flush last keyframe (compaction looks ahead) - //console.log( "took", ( Date.now() - startTime ) ); + if ( lastIndex > 0 ) { - } + times[ writeIndex ] = times[ lastIndex ]; - ExtrudeGeometry.prototype = Object.create( Geometry.prototype ); - ExtrudeGeometry.prototype.constructor = ExtrudeGeometry; + for ( var readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) - ExtrudeGeometry.prototype.addShapeList = function ( shapes, options ) { + values[ writeOffset + j ] = values[ readOffset + j ]; - var sl = shapes.length; + ++ writeIndex; - for ( var s = 0; s < sl; s ++ ) { + } - var shape = shapes[ s ]; - this.addShape( shape, options ); + if ( writeIndex !== times.length ) { - } + this.times = exports.AnimationUtils.arraySlice( times, 0, writeIndex ); + this.values = exports.AnimationUtils.arraySlice( values, 0, writeIndex * stride ); - }; + } - ExtrudeGeometry.prototype.addShape = function ( shape, options ) { + return this; - var amount = options.amount !== undefined ? options.amount : 100; + } - var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10 - var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8 - var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; + } - var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false + function KeyframeTrackConstructor( name, times, values, interpolation ) { - var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; + if( name === undefined ) throw new Error( "track name is undefined" ); - var steps = options.steps !== undefined ? options.steps : 1; + if( times === undefined || times.length === 0 ) { - var extrudePath = options.extrudePath; - var extrudePts, extrudeByPath = false; + throw new Error( "no keyframes in track named " + name ); - // Use default WorldUVGenerator if no UV generators are specified. - var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : ExtrudeGeometry.WorldUVGenerator; + } - var splineTube, binormal, normal, position2; - if ( extrudePath ) { + this.name = name; - extrudePts = extrudePath.getSpacedPoints( steps ); + this.times = exports.AnimationUtils.convertArray( times, this.TimeBufferType ); + this.values = exports.AnimationUtils.convertArray( values, this.ValueBufferType ); - extrudeByPath = true; - bevelEnabled = false; // bevels not supported for path extrusion + this.setInterpolation( interpolation || this.DefaultInterpolation ); - // SETUP TNB variables + this.validate(); + this.optimize(); - // Reuse TNB from TubeGeomtry for now. - // TODO1 - have a .isClosed in spline? + } - splineTube = options.frames !== undefined ? options.frames : new TubeGeometry.FrenetFrames( extrudePath, steps, false ); + /** + * + * A Track of vectored keyframe values. + * + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ - // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); + function VectorKeyframeTrack( name, times, values, interpolation ) { - binormal = new Vector3(); - normal = new Vector3(); - position2 = new Vector3(); + KeyframeTrackConstructor.call( this, name, times, values, interpolation ); - } + } - // Safeguards if bevels are not enabled + VectorKeyframeTrack.prototype = + Object.assign( Object.create( KeyframeTrackPrototype ), { - if ( ! bevelEnabled ) { + constructor: VectorKeyframeTrack, - bevelSegments = 0; - bevelThickness = 0; - bevelSize = 0; + ValueTypeName: 'vector' - } + // ValueBufferType is inherited - // Variables initialization + // DefaultInterpolation is inherited - var ahole, h, hl; // looping of holes - var scope = this; + } ); - var shapesOffset = this.vertices.length; + /** + * Spherical linear unit quaternion interpolant. + * + * @author tschw + */ - var shapePoints = shape.extractPoints( curveSegments ); + function QuaternionLinearInterpolant( + parameterPositions, sampleValues, sampleSize, resultBuffer ) { - var vertices = shapePoints.shape; - var holes = shapePoints.holes; + Interpolant.call( + this, parameterPositions, sampleValues, sampleSize, resultBuffer ); - var reverse = ! exports.ShapeUtils.isClockWise( vertices ); + } - if ( reverse ) { + QuaternionLinearInterpolant.prototype = + Object.assign( Object.create( Interpolant.prototype ), { - vertices = vertices.reverse(); + constructor: QuaternionLinearInterpolant, - // Maybe we should also check if holes are in the opposite direction, just to be safe ... + interpolate_: function( i1, t0, t, t1 ) { - for ( h = 0, hl = holes.length; h < hl; h ++ ) { + var result = this.resultBuffer, + values = this.sampleValues, + stride = this.valueSize, - ahole = holes[ h ]; + offset = i1 * stride, - if ( exports.ShapeUtils.isClockWise( ahole ) ) { + alpha = ( t - t0 ) / ( t1 - t0 ); - holes[ h ] = ahole.reverse(); + for ( var end = offset + stride; offset !== end; offset += 4 ) { - } + Quaternion.slerpFlat( result, 0, + values, offset - stride, values, offset, alpha ); } - reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)! + return result; } + } ); - var faces = exports.ShapeUtils.triangulateShape( vertices, holes ); + /** + * + * A Track of quaternion keyframe values. + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ - /* Vertices */ + function QuaternionKeyframeTrack( name, times, values, interpolation ) { - var contour = vertices; // vertices has all points but contour has only points of circumference + KeyframeTrackConstructor.call( this, name, times, values, interpolation ); - for ( h = 0, hl = holes.length; h < hl; h ++ ) { + } - ahole = holes[ h ]; + QuaternionKeyframeTrack.prototype = + Object.assign( Object.create( KeyframeTrackPrototype ), { - vertices = vertices.concat( ahole ); + constructor: QuaternionKeyframeTrack, - } + ValueTypeName: 'quaternion', + // ValueBufferType is inherited - function scalePt2( pt, vec, size ) { + DefaultInterpolation: InterpolateLinear, - if ( ! vec ) console.error( "THREE.ExtrudeGeometry: vec does not exist" ); + InterpolantFactoryMethodLinear: function( result ) { - return vec.clone().multiplyScalar( size ).add( pt ); + return new QuaternionLinearInterpolant( + this.times, this.values, this.getValueSize(), result ); - } + }, - var b, bs, t, z, - vert, vlen = vertices.length, - face, flen = faces.length; + InterpolantFactoryMethodSmooth: undefined // not yet implemented + } ); - // Find directions for point movement + /** + * + * A Track of numeric keyframe values. + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ + function NumberKeyframeTrack( name, times, values, interpolation ) { - function getBevelVec( inPt, inPrev, inNext ) { + KeyframeTrackConstructor.call( this, name, times, values, interpolation ); - // 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. + } - var v_trans_x, v_trans_y, shrink_by = 1; // resulting translation vector for inPt + NumberKeyframeTrack.prototype = + Object.assign( Object.create( KeyframeTrackPrototype ), { - // good reading for geometry algorithms (here: line-line intersection) - // http://geomalgorithms.com/a05-_intersect-1.html + constructor: NumberKeyframeTrack, - 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; + ValueTypeName: 'number', - var v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y ); + // ValueBufferType is inherited - // check for collinear edges - var collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x ); + // DefaultInterpolation is inherited + + } ); + + /** + * + * A Track that interpolates Strings + * + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ - if ( Math.abs( collinear0 ) > Number.EPSILON ) { + function StringKeyframeTrack( name, times, values, interpolation ) { - // not collinear + KeyframeTrackConstructor.call( this, name, times, values, interpolation ); - // length of vectors for normalizing + } - var v_prev_len = Math.sqrt( v_prev_lensq ); - var v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y ); + StringKeyframeTrack.prototype = + Object.assign( Object.create( KeyframeTrackPrototype ), { - // shift adjacent points by unit vectors to the left + constructor: StringKeyframeTrack, - var ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len ); - var ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len ); + ValueTypeName: 'string', + ValueBufferType: Array, - var ptNextShift_x = ( inNext.x - v_next_y / v_next_len ); - var ptNextShift_y = ( inNext.y + v_next_x / v_next_len ); + DefaultInterpolation: InterpolateDiscrete, - // scaling factor for v_prev to intersection point + InterpolantFactoryMethodLinear: undefined, - 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 ); + InterpolantFactoryMethodSmooth: undefined - // 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 ); + /** + * + * A Track of Boolean keyframe values. + * + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ - // 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 ) { + function BooleanKeyframeTrack( name, times, values ) { - return new Vector2( v_trans_x, v_trans_y ); + KeyframeTrackConstructor.call( this, name, times, values ); - } else { + } - shrink_by = Math.sqrt( v_trans_lensq / 2 ); + BooleanKeyframeTrack.prototype = + Object.assign( Object.create( KeyframeTrackPrototype ), { - } + constructor: BooleanKeyframeTrack, - } else { + ValueTypeName: 'bool', + ValueBufferType: Array, - // handle special case of collinear edges + DefaultInterpolation: InterpolateDiscrete, - var direction_eq = false; // assumes: opposite - if ( v_prev_x > Number.EPSILON ) { + InterpolantFactoryMethodLinear: undefined, + InterpolantFactoryMethodSmooth: undefined - if ( v_next_x > Number.EPSILON ) { + // Note: Actually this track could have a optimized / compressed + // representation of a single value and a custom interpolant that + // computes "firstValue ^ isOdd( index )". - direction_eq = true; + } ); - } + /** + * + * A Track of keyframe values that represent color. + * + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ - } else { + function ColorKeyframeTrack( name, times, values, interpolation ) { - if ( v_prev_x < - Number.EPSILON ) { + KeyframeTrackConstructor.call( this, name, times, values, interpolation ); - if ( v_next_x < - Number.EPSILON ) { + } - direction_eq = true; + ColorKeyframeTrack.prototype = + Object.assign( Object.create( KeyframeTrackPrototype ), { - } + constructor: ColorKeyframeTrack, - } else { + ValueTypeName: 'color' - if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) { + // ValueBufferType is inherited - direction_eq = true; + // DefaultInterpolation is inherited - } - } + // Note: Very basic implementation and nothing special yet. + // However, this is the place for color space parameterization. - } + } ); - if ( direction_eq ) { + /** + * + * A timed sequence of keyframes for a specific property. + * + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + * @author tschw + */ - // 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 ); + function KeyframeTrack( name, times, values, interpolation ) { - } else { + KeyframeTrackConstructor.apply( this, arguments ); - // 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 ); + } - } + KeyframeTrack.prototype = KeyframeTrackPrototype; + KeyframeTrackPrototype.constructor = KeyframeTrack; - } + // Static methods: - return new Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by ); + Object.assign( KeyframeTrack, { - } + // Serialization (in static context, because of constructor invocation + // and automatic invocation of .toJSON): + parse: function( json ) { - var contourMovements = []; + if( json.type === undefined ) { - for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { + throw new Error( "track type undefined, can not parse" ); - if ( j === il ) j = 0; - if ( k === il ) k = 0; + } - // (j)---(i)---(k) - // console.log('i,j,k', i, j , k) + var trackType = KeyframeTrack._getTrackTypeForValueTypeName( json.type ); - contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); + if ( json.times === undefined ) { - } + var times = [], values = []; - var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat(); + exports.AnimationUtils.flattenJSON( json.keys, times, values, 'value' ); - for ( h = 0, hl = holes.length; h < hl; h ++ ) { + json.times = times; + json.values = values; - ahole = holes[ h ]; + } - oneHoleMovements = []; + // derived classes can define a static parse method + if ( trackType.parse !== undefined ) { - for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { + return trackType.parse( json ); - if ( j === il ) j = 0; - if ( k === il ) k = 0; + } else { - // (j)---(i)---(k) - oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); + // by default, we asssume a constructor compatible with the base + return new trackType( + json.name, json.times, json.values, json.interpolation ); } - holesMovements.push( oneHoleMovements ); - verticesMovements = verticesMovements.concat( oneHoleMovements ); + }, - } + toJSON: function( track ) { + var trackType = track.constructor; - // Loop bevelSegments, 1 for the front, 1 for the back + var json; - for ( b = 0; b < bevelSegments; b ++ ) { + // derived classes can define a static toJSON method + if ( trackType.toJSON !== undefined ) { - //for ( b = bevelSegments; b > 0; b -- ) { + json = trackType.toJSON( track ); - t = b / bevelSegments; - z = bevelThickness * Math.cos( t * Math.PI / 2 ); - bs = bevelSize * Math.sin( t * Math.PI / 2 ); + } else { - // contract shape + // by default, we assume the data can be serialized as-is + json = { - for ( i = 0, il = contour.length; i < il; i ++ ) { + 'name': track.name, + 'times': exports.AnimationUtils.convertArray( track.times, Array ), + 'values': exports.AnimationUtils.convertArray( track.values, Array ) - vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); + }; - v( vert.x, vert.y, - z ); + var interpolation = track.getInterpolation(); - } + if ( interpolation !== track.DefaultInterpolation ) { - // expand holes + json.interpolation = interpolation; - for ( h = 0, hl = holes.length; h < hl; h ++ ) { + } - ahole = holes[ h ]; - oneHoleMovements = holesMovements[ h ]; + } - for ( i = 0, il = ahole.length; i < il; i ++ ) { + json.type = track.ValueTypeName; // mandatory - vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); + return json; - v( vert.x, vert.y, - z ); + }, - } + _getTrackTypeForValueTypeName: function( typeName ) { - } + switch( typeName.toLowerCase() ) { - } + case "scalar": + case "double": + case "float": + case "number": + case "integer": - bs = bevelSize; + return NumberKeyframeTrack; - // Back facing vertices + case "vector": + case "vector2": + case "vector3": + case "vector4": - for ( i = 0; i < vlen; i ++ ) { + return VectorKeyframeTrack; - vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; + case "color": - if ( ! extrudeByPath ) { + return ColorKeyframeTrack; - v( vert.x, vert.y, 0 ); + case "quaternion": - } else { + return QuaternionKeyframeTrack; - // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); + case "bool": + case "boolean": - normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x ); - binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y ); + return BooleanKeyframeTrack; - position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal ); + case "string": - v( position2.x, position2.y, position2.z ); + return StringKeyframeTrack; } + throw new Error( "Unsupported typeName: " + typeName ); + } - // Add stepped vertices... - // Including front facing vertices + } ); - var s; + /** + * + * Reusable set of Tracks that represent an animation. + * + * @author Ben Houston / http://clara.io/ + * @author David Sarno / http://lighthaus.us/ + */ - for ( s = 1; s <= steps; s ++ ) { + function AnimationClip( name, duration, tracks ) { - for ( i = 0; i < vlen; i ++ ) { + this.name = name; + this.tracks = tracks; + this.duration = ( duration !== undefined ) ? duration : -1; - vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; + this.uuid = exports.Math.generateUUID(); - if ( ! extrudeByPath ) { + // this means it should figure out its duration by scanning the tracks + if ( this.duration < 0 ) { - v( vert.x, vert.y, amount / steps * s ); + this.resetDuration(); - } else { + } - // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); + this.optimize(); - normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x ); - binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y ); + } - position2.copy( extrudePts[ s ] ).add( normal ).add( binormal ); + AnimationClip.prototype = { - v( position2.x, position2.y, position2.z ); + constructor: AnimationClip, - } + resetDuration: function() { - } + var tracks = this.tracks, + duration = 0; - } + for ( var i = 0, n = tracks.length; i !== n; ++ i ) { + var track = this.tracks[ i ]; - // Add bevel segments planes + duration = Math.max( + duration, track.times[ track.times.length - 1 ] ); - //for ( b = 1; b <= bevelSegments; b ++ ) { - for ( b = bevelSegments - 1; b >= 0; b -- ) { + } - t = b / bevelSegments; - z = bevelThickness * Math.cos ( t * Math.PI / 2 ); - bs = bevelSize * Math.sin( t * Math.PI / 2 ); + this.duration = duration; - // contract shape + }, - for ( i = 0, il = contour.length; i < il; i ++ ) { + trim: function() { - vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); - v( vert.x, vert.y, amount + z ); + for ( var i = 0; i < this.tracks.length; i ++ ) { - } + this.tracks[ i ].trim( 0, this.duration ); - // expand holes + } - for ( h = 0, hl = holes.length; h < hl; h ++ ) { + return this; - ahole = holes[ h ]; - oneHoleMovements = holesMovements[ h ]; + }, - for ( i = 0, il = ahole.length; i < il; i ++ ) { + optimize: function() { - vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); + for ( var i = 0; i < this.tracks.length; i ++ ) { - if ( ! extrudeByPath ) { + this.tracks[ i ].optimize(); - v( vert.x, vert.y, amount + z ); + } - } else { + return this; - v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); + } - } + }; - } + // Static methods: - } + Object.assign( AnimationClip, { - } + parse: function( json ) { - /* Faces */ + var tracks = [], + jsonTracks = json.tracks, + frameTime = 1.0 / ( json.fps || 1.0 ); - // Top and bottom faces + for ( var i = 0, n = jsonTracks.length; i !== n; ++ i ) { - buildLidFaces(); + tracks.push( KeyframeTrack.parse( jsonTracks[ i ] ).scale( frameTime ) ); - // Sides faces + } - buildSideFaces(); + return new AnimationClip( json.name, json.duration, tracks ); + }, - ///// Internal functions - function buildLidFaces() { + toJSON: function( clip ) { - if ( bevelEnabled ) { + var tracks = [], + clipTracks = clip.tracks; - var layer = 0; // steps + 1 - var offset = vlen * layer; + var json = { - // Bottom faces + 'name': clip.name, + 'duration': clip.duration, + 'tracks': tracks - for ( i = 0; i < flen; i ++ ) { + }; - face = faces[ i ]; - f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset ); + for ( var i = 0, n = clipTracks.length; i !== n; ++ i ) { - } + tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) ); - layer = steps + bevelSegments * 2; - offset = vlen * layer; + } - // Top faces + return json; - for ( i = 0; i < flen; i ++ ) { + }, - face = faces[ i ]; - f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset ); - } + CreateFromMorphTargetSequence: function( name, morphTargetSequence, fps, noLoop ) { - } else { + var numMorphTargets = morphTargetSequence.length; + var tracks = []; - // Bottom faces + for ( var i = 0; i < numMorphTargets; i ++ ) { - for ( i = 0; i < flen; i ++ ) { + var times = []; + var values = []; - face = faces[ i ]; - f3( face[ 2 ], face[ 1 ], face[ 0 ] ); + times.push( + ( i + numMorphTargets - 1 ) % numMorphTargets, + i, + ( i + 1 ) % numMorphTargets ); - } + values.push( 0, 1, 0 ); - // Top faces + var order = exports.AnimationUtils.getKeyframeOrder( times ); + times = exports.AnimationUtils.sortedArray( times, 1, order ); + values = exports.AnimationUtils.sortedArray( values, 1, order ); - for ( i = 0; i < flen; i ++ ) { + // if there is a key at the first frame, duplicate it as the + // last frame as well for perfect loop. + if ( ! noLoop && times[ 0 ] === 0 ) { - face = faces[ i ]; - f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps ); + times.push( numMorphTargets ); + values.push( values[ 0 ] ); } + tracks.push( + new NumberKeyframeTrack( + '.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']', + times, values + ).scale( 1.0 / fps ) ); } - } - - // Create faces for the z-sides of the shape + return new AnimationClip( name, -1, tracks ); - function buildSideFaces() { + }, - var layeroffset = 0; - sidewalls( contour, layeroffset ); - layeroffset += contour.length; + findByName: function( objectOrClipArray, name ) { - for ( h = 0, hl = holes.length; h < hl; h ++ ) { + var clipArray = objectOrClipArray; - ahole = holes[ h ]; - sidewalls( ahole, layeroffset ); + if ( ! Array.isArray( objectOrClipArray ) ) { - //, true - layeroffset += ahole.length; + var o = objectOrClipArray; + clipArray = o.geometry && o.geometry.animations || o.animations; } - } - - function sidewalls( contour, layeroffset ) { + for ( var i = 0; i < clipArray.length; i ++ ) { - var j, k; - i = contour.length; + if ( clipArray[ i ].name === name ) { - while ( -- i >= 0 ) { + return clipArray[ i ]; - j = i; - k = i - 1; - if ( k < 0 ) k = contour.length - 1; + } + } - //console.log('b', i,j, i-1, k,vertices.length); + return null; - var s = 0, sl = steps + bevelSegments * 2; + }, - for ( s = 0; s < sl; s ++ ) { + CreateClipsFromMorphTargetSequences: function( morphTargets, fps, noLoop ) { - var slen1 = vlen * s; - var slen2 = vlen * ( s + 1 ); + var animationToMorphTargets = {}; - var a = layeroffset + j + slen1, - b = layeroffset + k + slen1, - c = layeroffset + k + slen2, - d = layeroffset + j + slen2; + // tested with https://regex101.com/ on trick sequences + // such flamingo_flyA_003, flamingo_run1_003, crdeath0059 + var pattern = /^([\w-]*?)([\d]+)$/; - f4( a, b, c, d, contour, s, sl, j, k ); + // sort morph target names into animation groups based + // patterns like Walk_001, Walk_002, Run_001, Run_002 + for ( var i = 0, il = morphTargets.length; i < il; i ++ ) { - } + var morphTarget = morphTargets[ i ]; + var parts = morphTarget.name.match( pattern ); - } + if ( parts && parts.length > 1 ) { - } + var name = parts[ 1 ]; + var animationMorphTargets = animationToMorphTargets[ name ]; + if ( ! animationMorphTargets ) { - function v( x, y, z ) { + animationToMorphTargets[ name ] = animationMorphTargets = []; - scope.vertices.push( new Vector3( x, y, z ) ); + } - } + animationMorphTargets.push( morphTarget ); - function f3( a, b, c ) { + } - a += shapesOffset; - b += shapesOffset; - c += shapesOffset; + } - scope.faces.push( new Face3( a, b, c, null, null, 0 ) ); + var clips = []; - var uvs = uvgen.generateTopUV( scope, a, b, c ); + for ( var name in animationToMorphTargets ) { - scope.faceVertexUvs[ 0 ].push( uvs ); + clips.push( AnimationClip.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) ); - } + } - function f4( a, b, c, d, wallContour, stepIndex, stepsLength, contourIndex1, contourIndex2 ) { + return clips; - a += shapesOffset; - b += shapesOffset; - c += shapesOffset; - d += shapesOffset; + }, - scope.faces.push( new Face3( a, b, d, null, null, 1 ) ); - scope.faces.push( new Face3( b, c, d, null, null, 1 ) ); + // parse the animation.hierarchy format + parseAnimation: function( animation, bones ) { - var uvs = uvgen.generateSideWallUV( scope, a, b, c, d ); + if ( ! animation ) { - scope.faceVertexUvs[ 0 ].push( [ uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] ] ); - scope.faceVertexUvs[ 0 ].push( [ uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] ] ); + console.error( " no animation in JSONLoader data" ); + return null; - } + } - }; + var addNonemptyTrack = function( + trackType, trackName, animationKeys, propertyName, destTracks ) { - ExtrudeGeometry.WorldUVGenerator = { + // only return track if there are actually keys. + if ( animationKeys.length !== 0 ) { - generateTopUV: function ( geometry, indexA, indexB, indexC ) { + var times = []; + var values = []; - var vertices = geometry.vertices; + exports.AnimationUtils.flattenJSON( + animationKeys, times, values, propertyName ); - var a = vertices[ indexA ]; - var b = vertices[ indexB ]; - var c = vertices[ indexC ]; + // empty keys are filtered out, so check again + if ( times.length !== 0 ) { - return [ - new Vector2( a.x, a.y ), - new Vector2( b.x, b.y ), - new Vector2( c.x, c.y ) - ]; + destTracks.push( new trackType( trackName, times, values ) ); - }, + } - generateSideWallUV: function ( geometry, indexA, indexB, indexC, indexD ) { + } - var vertices = geometry.vertices; + }; - var a = vertices[ indexA ]; - var b = vertices[ indexB ]; - var c = vertices[ indexC ]; - var d = vertices[ indexD ]; + var tracks = []; - if ( Math.abs( a.y - b.y ) < 0.01 ) { + var clipName = animation.name || 'default'; + // automatic length determination in AnimationClip. + var duration = animation.length || -1; + var fps = animation.fps || 30; - return [ - new Vector2( a.x, 1 - a.z ), - new Vector2( b.x, 1 - b.z ), - new Vector2( c.x, 1 - c.z ), - new Vector2( d.x, 1 - d.z ) - ]; + var hierarchyTracks = animation.hierarchy || []; - } else { + for ( var h = 0; h < hierarchyTracks.length; h ++ ) { - return [ - new Vector2( a.y, 1 - a.z ), - new Vector2( b.y, 1 - b.z ), - new Vector2( c.y, 1 - c.z ), - new Vector2( d.y, 1 - d.z ) - ]; + var animationKeys = hierarchyTracks[ h ].keys; - } + // skip empty tracks + if ( ! animationKeys || animationKeys.length === 0 ) continue; - } - }; + // process morph targets in a way exactly compatible + // with AnimationHandler.init( animation ) + if ( animationKeys[0].morphTargets ) { - /** - * @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 - * } - */ + // figure out all morph targets used in this track + var morphTargetNames = {}; + for ( var k = 0; k < animationKeys.length; k ++ ) { - function TextGeometry( text, parameters ) { + if ( animationKeys[k].morphTargets ) { - parameters = parameters || {}; + for ( var m = 0; m < animationKeys[k].morphTargets.length; m ++ ) { - var font = parameters.font; + morphTargetNames[ animationKeys[k].morphTargets[m] ] = -1; + } - if ( (font && font.isFont) === false ) { + } - console.error( 'THREE.TextGeometry: font parameter is not an instance of THREE.Font.' ); - return new Geometry(); + } - } + // create a track for each morph target with all zero + // morphTargetInfluences except for the keys in which + // the morphTarget is named. + for ( var morphTargetName in morphTargetNames ) { - var shapes = font.generateShapes( text, parameters.size, parameters.curveSegments ); + var times = []; + var values = []; - // translate parameters to ExtrudeGeometry API + for ( var m = 0; + m !== animationKeys[k].morphTargets.length; ++ m ) { - parameters.amount = parameters.height !== undefined ? parameters.height : 50; + var animationKey = animationKeys[k]; - // defaults + times.push( animationKey.time ); + values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 ); - if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10; - if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8; - if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false; + } - ExtrudeGeometry.call( this, shapes, parameters ); + tracks.push( new NumberKeyframeTrack( + '.morphTargetInfluence[' + morphTargetName + ']', times, values ) ); - this.type = 'TextGeometry'; + } - } + duration = morphTargetNames.length * ( fps || 1.0 ); - TextGeometry.prototype = Object.create( ExtrudeGeometry.prototype ); - TextGeometry.prototype.constructor = TextGeometry; + } else { + // ...assume skeletal animation - /** - * @author benaadams / https://twitter.com/ben_a_adams - * based on THREE.SphereGeometry - */ + var boneName = '.bones[' + bones[ h ].name + ']'; - function SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { + addNonemptyTrack( + VectorKeyframeTrack, boneName + '.position', + animationKeys, 'pos', tracks ); - BufferGeometry.call( this ); + addNonemptyTrack( + QuaternionKeyframeTrack, boneName + '.quaternion', + animationKeys, 'rot', tracks ); - this.type = 'SphereBufferGeometry'; + addNonemptyTrack( + VectorKeyframeTrack, boneName + '.scale', + animationKeys, 'scl', tracks ); - this.parameters = { - radius: radius, - widthSegments: widthSegments, - heightSegments: heightSegments, - phiStart: phiStart, - phiLength: phiLength, - thetaStart: thetaStart, - thetaLength: thetaLength - }; + } - radius = radius || 50; + } - widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 ); - heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 ); + if ( tracks.length === 0 ) { - phiStart = phiStart !== undefined ? phiStart : 0; - phiLength = phiLength !== undefined ? phiLength : Math.PI * 2; + return null; - thetaStart = thetaStart !== undefined ? thetaStart : 0; - thetaLength = thetaLength !== undefined ? thetaLength : Math.PI; + } - var thetaEnd = thetaStart + thetaLength; + var clip = new AnimationClip( clipName, duration, tracks ); - var vertexCount = ( ( widthSegments + 1 ) * ( heightSegments + 1 ) ); + return clip; - var positions = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); - var normals = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); - var uvs = new BufferAttribute( new Float32Array( vertexCount * 2 ), 2 ); + } - var index = 0, vertices = [], normal = new Vector3(); + } ); - for ( var y = 0; y <= heightSegments; y ++ ) { + /** + * @author mrdoob / http://mrdoob.com/ + */ - var verticesRow = []; + function MaterialLoader( manager ) { - var v = y / heightSegments; + this.manager = ( manager !== undefined ) ? manager : exports.DefaultLoadingManager; + this.textures = {}; - for ( var x = 0; x <= widthSegments; x ++ ) { + } - var u = x / widthSegments; + Object.assign( MaterialLoader.prototype, { - 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 ); + load: function ( url, onLoad, onProgress, onError ) { - normal.set( px, py, pz ).normalize(); + var scope = this; - positions.setXYZ( index, px, py, pz ); - normals.setXYZ( index, normal.x, normal.y, normal.z ); - uvs.setXY( index, u, 1 - v ); + var loader = new XHRLoader( scope.manager ); + loader.load( url, function ( text ) { - verticesRow.push( index ); + onLoad( scope.parse( JSON.parse( text ) ) ); - index ++; + }, onProgress, onError ); - } + }, - vertices.push( verticesRow ); + setTextures: function ( value ) { - } + this.textures = value; - var indices = []; + }, - for ( var y = 0; y < heightSegments; y ++ ) { + parse: function ( json ) { - for ( var x = 0; x < widthSegments; x ++ ) { + var textures = this.textures; - var v1 = vertices[ y ][ x + 1 ]; - var v2 = vertices[ y ][ x ]; - var v3 = vertices[ y + 1 ][ x ]; - var v4 = vertices[ y + 1 ][ x + 1 ]; + function getTexture( name ) { - if ( y !== 0 || thetaStart > 0 ) indices.push( v1, v2, v4 ); - if ( y !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( v2, v3, v4 ); + if ( textures[ name ] === undefined ) { - } + console.warn( 'THREE.MaterialLoader: Undefined texture', name ); - } + } - this.setIndex( new ( positions.count > 65535 ? Uint32Attribute : Uint16Attribute )( indices, 1 ) ); - this.addAttribute( 'position', positions ); - this.addAttribute( 'normal', normals ); - this.addAttribute( 'uv', uvs ); + return textures[ name ]; - this.boundingSphere = new Sphere( new Vector3(), radius ); + } - } + var material = new Materials[ json.type ](); + + 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.fog !== undefined ) material.fog = json.fog; + 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; + if ( json.wireframeLinecap !== undefined ) material.wireframeLinecap = json.wireframeLinecap; + if ( json.wireframeLinejoin !== undefined ) material.wireframeLinejoin = json.wireframeLinejoin; + if ( json.skinning !== undefined ) material.skinning = json.skinning; + if ( json.morphTargets !== undefined ) material.morphTargets = json.morphTargets; - SphereBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - SphereBufferGeometry.prototype.constructor = SphereBufferGeometry; + // for PointsMaterial - /** - * @author mrdoob / http://mrdoob.com/ - */ + if ( json.size !== undefined ) material.size = json.size; + if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation; - function SphereGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { + // maps - Geometry.call( this ); + if ( json.map !== undefined ) material.map = getTexture( json.map ); - this.type = 'SphereGeometry'; + if ( json.alphaMap !== undefined ) { - this.parameters = { - radius: radius, - widthSegments: widthSegments, - heightSegments: heightSegments, - phiStart: phiStart, - phiLength: phiLength, - thetaStart: thetaStart, - thetaLength: thetaLength - }; + material.alphaMap = getTexture( json.alphaMap ); + material.transparent = true; - this.fromBufferGeometry( new SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) ); + } - } + if ( json.bumpMap !== undefined ) material.bumpMap = getTexture( json.bumpMap ); + if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale; - SphereGeometry.prototype = Object.create( Geometry.prototype ); - SphereGeometry.prototype.constructor = SphereGeometry; + if ( json.normalMap !== undefined ) material.normalMap = getTexture( json.normalMap ); + if ( json.normalScale !== undefined ) { - /** - * @author Mugen87 / https://github.com/Mugen87 - */ + var normalScale = json.normalScale; - function RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { + if ( Array.isArray( normalScale ) === false ) { - BufferGeometry.call( this ); + // Blender exporter used to export a scalar. See #7459 - this.type = 'RingBufferGeometry'; + normalScale = [ normalScale, normalScale ]; - this.parameters = { - innerRadius: innerRadius, - outerRadius: outerRadius, - thetaSegments: thetaSegments, - phiSegments: phiSegments, - thetaStart: thetaStart, - thetaLength: thetaLength - }; + } - innerRadius = innerRadius || 20; - outerRadius = outerRadius || 50; + material.normalScale = new Vector2().fromArray( normalScale ); - thetaStart = thetaStart !== undefined ? thetaStart : 0; - thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; + } - thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8; - phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 1; + if ( json.displacementMap !== undefined ) material.displacementMap = getTexture( json.displacementMap ); + if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale; + if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias; - // these are used to calculate buffer length - var vertexCount = ( thetaSegments + 1 ) * ( phiSegments + 1 ); - var indexCount = thetaSegments * phiSegments * 2 * 3; + if ( json.roughnessMap !== undefined ) material.roughnessMap = getTexture( json.roughnessMap ); + if ( json.metalnessMap !== undefined ) material.metalnessMap = getTexture( json.metalnessMap ); - // buffers - var indices = new BufferAttribute( new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ) , 1 ); - var vertices = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); - var normals = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); - var uvs = new BufferAttribute( new Float32Array( vertexCount * 2 ), 2 ); + if ( json.emissiveMap !== undefined ) material.emissiveMap = getTexture( json.emissiveMap ); + if ( json.emissiveIntensity !== undefined ) material.emissiveIntensity = json.emissiveIntensity; - // some helper variables - var index = 0, indexOffset = 0, segment; - var radius = innerRadius; - var radiusStep = ( ( outerRadius - innerRadius ) / phiSegments ); - var vertex = new Vector3(); - var uv = new Vector2(); - var j, i; + if ( json.specularMap !== undefined ) material.specularMap = getTexture( json.specularMap ); - // generate vertices, normals and uvs + if ( json.envMap !== undefined ) material.envMap = getTexture( json.envMap ); - // values are generate from the inside of the ring to the outside + if ( json.reflectivity !== undefined ) material.reflectivity = json.reflectivity; - for ( j = 0; j <= phiSegments; j ++ ) { + if ( json.lightMap !== undefined ) material.lightMap = getTexture( json.lightMap ); + if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity; - for ( i = 0; i <= thetaSegments; i ++ ) { + if ( json.aoMap !== undefined ) material.aoMap = getTexture( json.aoMap ); + if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity; - segment = thetaStart + i / thetaSegments * thetaLength; + // MultiMaterial - // vertex - vertex.x = radius * Math.cos( segment ); - vertex.y = radius * Math.sin( segment ); - vertices.setXYZ( index, vertex.x, vertex.y, vertex.z ); + if ( json.materials !== undefined ) { - // normal - normals.setXYZ( index, 0, 0, 1 ); + for ( var i = 0, l = json.materials.length; i < l; i ++ ) { - // uv - uv.x = ( vertex.x / outerRadius + 1 ) / 2; - uv.y = ( vertex.y / outerRadius + 1 ) / 2; - uvs.setXY( index, uv.x, uv.y ); + material.materials.push( this.parse( json.materials[ i ] ) ); - // increase index - index++; + } } - // increase the radius for next row of vertices - radius += radiusStep; + return material; } - // generate indices + } ); - for ( j = 0; j < phiSegments; j ++ ) { + /** + * @author mrdoob / http://mrdoob.com/ + */ - var thetaSegmentLevel = j * ( thetaSegments + 1 ); + function BufferGeometryLoader( manager ) { - for ( i = 0; i < thetaSegments; i ++ ) { + this.manager = ( manager !== undefined ) ? manager : exports.DefaultLoadingManager; - segment = i + thetaSegmentLevel; + } - // indices - var a = segment; - var b = segment + thetaSegments + 1; - var c = segment + thetaSegments + 2; - var d = segment + 1; + Object.assign( BufferGeometryLoader.prototype, { - // face one - indices.setX( indexOffset, a ); indexOffset++; - indices.setX( indexOffset, b ); indexOffset++; - indices.setX( indexOffset, c ); indexOffset++; + load: function ( url, onLoad, onProgress, onError ) { - // face two - indices.setX( indexOffset, a ); indexOffset++; - indices.setX( indexOffset, c ); indexOffset++; - indices.setX( indexOffset, d ); indexOffset++; + var scope = this; - } + var loader = new XHRLoader( scope.manager ); + loader.load( url, function ( text ) { - } + onLoad( scope.parse( JSON.parse( text ) ) ); - // build geometry + }, onProgress, onError ); - this.setIndex( indices ); - this.addAttribute( 'position', vertices ); - this.addAttribute( 'normal', normals ); - this.addAttribute( 'uv', uvs ); + }, - } + parse: function ( json ) { - RingBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - RingBufferGeometry.prototype.constructor = RingBufferGeometry; + var geometry = new BufferGeometry(); - /** - * @author Kaleb Murphy - */ + var index = json.data.index; - function RingGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) { + var TYPED_ARRAYS = { + 'Int8Array': Int8Array, + 'Uint8Array': Uint8Array, + 'Uint8ClampedArray': Uint8ClampedArray, + 'Int16Array': Int16Array, + 'Uint16Array': Uint16Array, + 'Int32Array': Int32Array, + 'Uint32Array': Uint32Array, + 'Float32Array': Float32Array, + 'Float64Array': Float64Array + }; - Geometry.call( this ); + if ( index !== undefined ) { - this.type = 'RingGeometry'; + var typedArray = new TYPED_ARRAYS[ index.type ]( index.array ); + geometry.setIndex( new BufferAttribute( typedArray, 1 ) ); - this.parameters = { - innerRadius: innerRadius, - outerRadius: outerRadius, - thetaSegments: thetaSegments, - phiSegments: phiSegments, - thetaStart: thetaStart, - thetaLength: thetaLength - }; + } - this.fromBufferGeometry( new RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) ); + var attributes = json.data.attributes; - } + for ( var key in attributes ) { - RingGeometry.prototype = Object.create( Geometry.prototype ); - RingGeometry.prototype.constructor = RingGeometry; + var attribute = attributes[ key ]; + var typedArray = new TYPED_ARRAYS[ attribute.type ]( attribute.array ); - /** - * @author mrdoob / http://mrdoob.com/ - * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as - */ + geometry.addAttribute( key, new BufferAttribute( typedArray, attribute.itemSize, attribute.normalized ) ); - function PlaneGeometry( width, height, widthSegments, heightSegments ) { + } - Geometry.call( this ); + var groups = json.data.groups || json.data.drawcalls || json.data.offsets; - this.type = 'PlaneGeometry'; + if ( groups !== undefined ) { - this.parameters = { - width: width, - height: height, - widthSegments: widthSegments, - heightSegments: heightSegments - }; + for ( var i = 0, n = groups.length; i !== n; ++ i ) { - this.fromBufferGeometry( new PlaneBufferGeometry( width, height, widthSegments, heightSegments ) ); + var group = groups[ i ]; - } + geometry.addGroup( group.start, group.count, group.materialIndex ); - PlaneGeometry.prototype = Object.create( Geometry.prototype ); - PlaneGeometry.prototype.constructor = PlaneGeometry; + } - /** - * @author Mugen87 / https://github.com/Mugen87 - */ + } - // 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 boundingSphere = json.data.boundingSphere; - function LatheBufferGeometry( points, segments, phiStart, phiLength ) { + if ( boundingSphere !== undefined ) { - BufferGeometry.call( this ); + var center = new Vector3(); - this.type = 'LatheBufferGeometry'; + if ( boundingSphere.center !== undefined ) { - this.parameters = { - points: points, - segments: segments, - phiStart: phiStart, - phiLength: phiLength - }; + center.fromArray( boundingSphere.center ); - segments = Math.floor( segments ) || 12; - phiStart = phiStart || 0; - phiLength = phiLength || Math.PI * 2; + } - // clamp phiLength so it's in range of [ 0, 2PI ] - phiLength = exports.Math.clamp( phiLength, 0, Math.PI * 2 ); + geometry.boundingSphere = new Sphere( center, boundingSphere.radius ); - // these are used to calculate buffer length - var vertexCount = ( segments + 1 ) * points.length; - var indexCount = segments * points.length * 2 * 3; + } - // buffers - var indices = new BufferAttribute( new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ) , 1 ); - var vertices = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); - var uvs = new BufferAttribute( new Float32Array( vertexCount * 2 ), 2 ); + return geometry; - // helper variables - var index = 0, indexOffset = 0, base; - var inverseSegments = 1.0 / segments; - var vertex = new Vector3(); - var uv = new Vector2(); - var i, j; + } - // generate vertices and uvs + } ); - for ( i = 0; i <= segments; i ++ ) { + /** + * @author alteredq / http://alteredqualia.com/ + */ - var phi = phiStart + i * inverseSegments * phiLength; + function Loader() { - var sin = Math.sin( phi ); - var cos = Math.cos( phi ); + this.onLoadStart = function () {}; + this.onLoadProgress = function () {}; + this.onLoadComplete = function () {}; - for ( j = 0; j <= ( points.length - 1 ); j ++ ) { + } - // 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 ); + Loader.prototype = { - // uv - uv.x = i / segments; - uv.y = j / ( points.length - 1 ); - uvs.setXY( index, uv.x, uv.y ); + constructor: Loader, - // increase index - index ++; + crossOrigin: undefined, - } + extractUrlBase: function ( url ) { - } + var parts = url.split( '/' ); - // generate indices + if ( parts.length === 1 ) return './'; - for ( i = 0; i < segments; i ++ ) { + parts.pop(); - for ( j = 0; j < ( points.length - 1 ); j ++ ) { + return parts.join( '/' ) + '/'; - base = j + i * points.length; + }, - // indices - var a = base; - var b = base + points.length; - var c = base + points.length + 1; - var d = base + 1; + initMaterials: function ( materials, texturePath, crossOrigin ) { - // face one - indices.setX( indexOffset, a ); indexOffset++; - indices.setX( indexOffset, b ); indexOffset++; - indices.setX( indexOffset, d ); indexOffset++; + var array = []; - // face two - indices.setX( indexOffset, b ); indexOffset++; - indices.setX( indexOffset, c ); indexOffset++; - indices.setX( indexOffset, d ); indexOffset++; + for ( var i = 0; i < materials.length; ++ i ) { + + array[ i ] = this.createMaterial( materials[ i ], texturePath, crossOrigin ); } - } + return array; - // build geometry + }, - this.setIndex( indices ); - this.addAttribute( 'position', vertices ); - this.addAttribute( 'uv', uvs ); + createMaterial: ( function () { - // generate normals + var color, textureLoader, materialLoader; - this.computeVertexNormals(); + return function createMaterial( m, texturePath, crossOrigin ) { - // 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 ( color === undefined ) color = new Color(); + if ( textureLoader === undefined ) textureLoader = new TextureLoader(); + if ( materialLoader === undefined ) materialLoader = new MaterialLoader(); - if( phiLength === Math.PI * 2 ) { + // convert from old material format - var normals = this.attributes.normal.array; - var n1 = new Vector3(); - var n2 = new Vector3(); - var n = new Vector3(); + var textures = {}; - // this is the buffer offset for the last line of vertices - base = segments * points.length * 3; + function loadTexture( path, repeat, offset, wrap, anisotropy ) { - for( i = 0, j = 0; i < points.length; i ++, j += 3 ) { + var fullPath = texturePath + path; + var loader = Loader.Handlers.get( fullPath ); - // 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 ]; + var texture; - // 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 ]; + if ( loader !== null ) { - // average normals - n.addVectors( n1, n2 ).normalize(); + texture = loader.load( fullPath ); - // 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; + } else { - } // next row + textureLoader.setCrossOrigin( crossOrigin ); + texture = textureLoader.load( fullPath ); - } + } - } + if ( repeat !== undefined ) { - LatheBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - LatheBufferGeometry.prototype.constructor = LatheBufferGeometry; + texture.repeat.fromArray( repeat ); - /** - * @author astrodud / http://astrodud.isgreat.org/ - * @author zz85 / https://github.com/zz85 - * @author bhouston / http://clara.io - */ + if ( repeat[ 0 ] !== 1 ) texture.wrapS = RepeatWrapping; + if ( repeat[ 1 ] !== 1 ) texture.wrapT = RepeatWrapping; - // 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. + } - function LatheGeometry( points, segments, phiStart, phiLength ) { + if ( offset !== undefined ) { - Geometry.call( this ); + texture.offset.fromArray( offset ); - this.type = 'LatheGeometry'; + } - this.parameters = { - points: points, - segments: segments, - phiStart: phiStart, - phiLength: phiLength - }; + if ( wrap !== undefined ) { - this.fromBufferGeometry( new LatheBufferGeometry( points, segments, phiStart, phiLength ) ); - this.mergeVertices(); + if ( wrap[ 0 ] === 'repeat' ) texture.wrapS = RepeatWrapping; + if ( wrap[ 0 ] === 'mirror' ) texture.wrapS = MirroredRepeatWrapping; - } + if ( wrap[ 1 ] === 'repeat' ) texture.wrapT = RepeatWrapping; + if ( wrap[ 1 ] === 'mirror' ) texture.wrapT = MirroredRepeatWrapping; - LatheGeometry.prototype = Object.create( Geometry.prototype ); - LatheGeometry.prototype.constructor = LatheGeometry; + } - /** - * @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 - * - * } - **/ + if ( anisotropy !== undefined ) { - function ShapeGeometry( shapes, options ) { + texture.anisotropy = anisotropy; - Geometry.call( this ); + } - this.type = 'ShapeGeometry'; + var uuid = exports.Math.generateUUID(); - if ( Array.isArray( shapes ) === false ) shapes = [ shapes ]; + textures[ uuid ] = texture; - this.addShapeList( shapes, options ); + return uuid; - this.computeFaceNormals(); + } + + // + + var json = { + uuid: exports.Math.generateUUID(), + type: 'MeshLambertMaterial' + }; + + for ( var name in m ) { + + var value = m[ name ]; + + switch ( name ) { + case 'DbgColor': + case 'DbgIndex': + case 'opticalDensity': + case 'illumination': + break; + case 'DbgName': + json.name = value; + break; + case 'blending': + json.blending = BlendingMode[ 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 = BackSide; + break; + case 'doubleSided': + json.side = 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 = VertexColors; + if ( value === 'face' ) json.vertexColors = FaceColors; + break; + default: + console.error( 'THREE.Loader.createMaterial: Unsupported', name, value ); + break; + } - } + } - ShapeGeometry.prototype = Object.create( Geometry.prototype ); - ShapeGeometry.prototype.constructor = ShapeGeometry; + if ( json.type === 'MeshBasicMaterial' ) delete json.emissive; + if ( json.type !== 'MeshPhongMaterial' ) delete json.specular; - /** - * Add an array of shapes to THREE.ShapeGeometry. - */ - ShapeGeometry.prototype.addShapeList = function ( shapes, options ) { + if ( json.opacity < 1 ) json.transparent = true; - for ( var i = 0, l = shapes.length; i < l; i ++ ) { + materialLoader.setTextures( textures ); - this.addShape( shapes[ i ], options ); + return materialLoader.parse( json ); - } + }; - return this; + } )() }; - /** - * Adds a shape to THREE.ShapeGeometry, based on THREE.ExtrudeGeometry. - */ - ShapeGeometry.prototype.addShape = function ( shape, options ) { - - if ( options === undefined ) options = {}; - var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; - - var material = options.material; - var uvgen = options.UVGenerator === undefined ? ExtrudeGeometry.WorldUVGenerator : options.UVGenerator; - - // - - var i, l, hole; + Loader.Handlers = { - var shapesOffset = this.vertices.length; - var shapePoints = shape.extractPoints( curveSegments ); + handlers: [], - var vertices = shapePoints.shape; - var holes = shapePoints.holes; + add: function ( regex, loader ) { - var reverse = ! exports.ShapeUtils.isClockWise( vertices ); + this.handlers.push( regex, loader ); - if ( reverse ) { + }, - vertices = vertices.reverse(); + get: function ( file ) { - // Maybe we should also check if holes are in the opposite direction, just to be safe... + var handlers = this.handlers; - for ( i = 0, l = holes.length; i < l; i ++ ) { + for ( var i = 0, l = handlers.length; i < l; i += 2 ) { - hole = holes[ i ]; + var regex = handlers[ i ]; + var loader = handlers[ i + 1 ]; - if ( exports.ShapeUtils.isClockWise( hole ) ) { + if ( regex.test( file ) ) { - holes[ i ] = hole.reverse(); + return loader; } } - reverse = false; - - } - - var faces = exports.ShapeUtils.triangulateShape( vertices, holes ); - - // Vertices - - for ( i = 0, l = holes.length; i < l; i ++ ) { - - hole = holes[ i ]; - vertices = vertices.concat( hole ); + return null; } - // - - var vert, vlen = vertices.length; - var face, flen = faces.length; - - for ( i = 0; i < vlen; i ++ ) { - - vert = vertices[ i ]; - - this.vertices.push( new Vector3( vert.x, vert.y, 0 ) ); - - } + }; - for ( i = 0; i < flen; i ++ ) { + /** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ - face = faces[ i ]; + function JSONLoader( manager ) { - var a = face[ 0 ] + shapesOffset; - var b = face[ 1 ] + shapesOffset; - var c = face[ 2 ] + shapesOffset; + if ( typeof manager === 'boolean' ) { - this.faces.push( new Face3( a, b, c, null, null, material ) ); - this.faceVertexUvs[ 0 ].push( uvgen.generateTopUV( this, a, b, c ) ); + console.warn( 'THREE.JSONLoader: showStatus parameter has been removed from constructor.' ); + manager = undefined; } - }; + this.manager = ( manager !== undefined ) ? manager : exports.DefaultLoadingManager; - /** - * @author WestLangley / http://github.com/WestLangley - */ + this.withCredentials = false; - function EdgesGeometry( geometry, thresholdAngle ) { + } - BufferGeometry.call( this ); + Object.assign( JSONLoader.prototype, { - thresholdAngle = ( thresholdAngle !== undefined ) ? thresholdAngle : 1; + load: function( url, onLoad, onProgress, onError ) { - var thresholdDot = Math.cos( exports.Math.DEG2RAD * thresholdAngle ); + var scope = this; - var edge = [ 0, 0 ], hash = {}; + var texturePath = this.texturePath && ( typeof this.texturePath === "string" ) ? this.texturePath : Loader.prototype.extractUrlBase( url ); - function sortFunction( a, b ) { + var loader = new XHRLoader( this.manager ); + loader.setWithCredentials( this.withCredentials ); + loader.load( url, function ( text ) { - return a - b; + var json = JSON.parse( text ); + var metadata = json.metadata; - } + if ( metadata !== undefined ) { - var keys = [ 'a', 'b', 'c' ]; + var type = metadata.type; - var geometry2; + if ( type !== undefined ) { - if ( (geometry && geometry.isBufferGeometry) ) { + if ( type.toLowerCase() === 'object' ) { - geometry2 = new Geometry(); - geometry2.fromBufferGeometry( geometry ); + console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.ObjectLoader instead.' ); + return; - } else { + } - geometry2 = geometry.clone(); + if ( type.toLowerCase() === 'scene' ) { - } + console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.SceneLoader instead.' ); + return; - geometry2.mergeVertices(); - geometry2.computeFaceNormals(); + } - var vertices = geometry2.vertices; - var faces = geometry2.faces; + } - for ( var i = 0, l = faces.length; i < l; i ++ ) { + } - var face = faces[ i ]; + var object = scope.parse( json, texturePath ); + onLoad( object.geometry, object.materials ); - for ( var j = 0; j < 3; j ++ ) { + }, onProgress, onError ); - edge[ 0 ] = face[ keys[ j ] ]; - edge[ 1 ] = face[ keys[ ( j + 1 ) % 3 ] ]; - edge.sort( sortFunction ); + }, - var key = edge.toString(); + setTexturePath: function ( value ) { - if ( hash[ key ] === undefined ) { + this.texturePath = value; - hash[ key ] = { vert1: edge[ 0 ], vert2: edge[ 1 ], face1: i, face2: undefined }; + }, - } else { + parse: function ( json, texturePath ) { - hash[ key ].face2 = i; + var geometry = new Geometry(), + scale = ( json.scale !== undefined ) ? 1.0 / json.scale : 1.0; - } + parseModel( scale ); - } + parseSkin(); + parseMorphing( scale ); + parseAnimations(); - } + geometry.computeFaceNormals(); + geometry.computeBoundingSphere(); - var coords = []; + function parseModel( scale ) { - for ( var key in hash ) { + function isBitSet( value, position ) { - var h = hash[ key ]; + return value & ( 1 << position ); - if ( h.face2 === undefined || faces[ h.face1 ].normal.dot( faces[ h.face2 ].normal ) <= thresholdDot ) { + } - var vertex = vertices[ h.vert1 ]; - coords.push( vertex.x ); - coords.push( vertex.y ); - coords.push( vertex.z ); + var i, j, fi, - vertex = vertices[ h.vert2 ]; - coords.push( vertex.x ); - coords.push( vertex.y ); - coords.push( vertex.z ); + offset, zLength, - } + colorIndex, normalIndex, uvIndex, materialIndex, - } + type, + isQuad, + hasMaterial, + hasFaceVertexUv, + hasFaceNormal, hasFaceVertexNormal, + hasFaceColor, hasFaceVertexColor, - this.addAttribute( 'position', new BufferAttribute( new Float32Array( coords ), 3 ) ); + vertex, face, faceA, faceB, hex, normal, - } + uvLayer, uv, u, v, - EdgesGeometry.prototype = Object.create( BufferGeometry.prototype ); - EdgesGeometry.prototype.constructor = EdgesGeometry; + faces = json.faces, + vertices = json.vertices, + normals = json.normals, + colors = json.colors, - /** - * @author Mugen87 / https://github.com/Mugen87 - */ + nUvLayers = 0; - function CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { + if ( json.uvs !== undefined ) { - BufferGeometry.call( this ); + // disregard empty arrays - this.type = 'CylinderBufferGeometry'; + for ( i = 0; i < json.uvs.length; i ++ ) { - this.parameters = { - radiusTop: radiusTop, - radiusBottom: radiusBottom, - height: height, - radialSegments: radialSegments, - heightSegments: heightSegments, - openEnded: openEnded, - thetaStart: thetaStart, - thetaLength: thetaLength - }; + if ( json.uvs[ i ].length ) nUvLayers ++; - var scope = this; + } - radiusTop = radiusTop !== undefined ? radiusTop : 20; - radiusBottom = radiusBottom !== undefined ? radiusBottom : 20; - height = height !== undefined ? height : 100; + for ( i = 0; i < nUvLayers; i ++ ) { - radialSegments = Math.floor( radialSegments ) || 8; - heightSegments = Math.floor( heightSegments ) || 1; + geometry.faceVertexUvs[ i ] = []; - 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; + offset = 0; + zLength = vertices.length; - if ( openEnded === false ) { + while ( offset < zLength ) { - if ( radiusTop > 0 ) nbCap ++; - if ( radiusBottom > 0 ) nbCap ++; + vertex = new Vector3(); - } + vertex.x = vertices[ offset ++ ] * scale; + vertex.y = vertices[ offset ++ ] * scale; + vertex.z = vertices[ offset ++ ] * scale; - var vertexCount = calculateVertexCount(); - var indexCount = calculateIndexCount(); + geometry.vertices.push( vertex ); - // buffers + } - var indices = new BufferAttribute( new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ), 1 ); - var vertices = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); - var normals = new BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); - var uvs = new BufferAttribute( new Float32Array( vertexCount * 2 ), 2 ); + offset = 0; + zLength = faces.length; - // helper variables + while ( offset < zLength ) { - var index = 0, - indexOffset = 0, - indexArray = [], - halfHeight = height / 2; + type = faces[ offset ++ ]; - // group variables - var groupStart = 0; - // generate geometry + isQuad = isBitSet( type, 0 ); + hasMaterial = isBitSet( type, 1 ); + hasFaceVertexUv = isBitSet( type, 3 ); + hasFaceNormal = isBitSet( type, 4 ); + hasFaceVertexNormal = isBitSet( type, 5 ); + hasFaceColor = isBitSet( type, 6 ); + hasFaceVertexColor = isBitSet( type, 7 ); - generateTorso(); + // console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor); - if ( openEnded === false ) { + if ( isQuad ) { - if ( radiusTop > 0 ) generateCap( true ); - if ( radiusBottom > 0 ) generateCap( false ); + faceA = new Face3(); + faceA.a = faces[ offset ]; + faceA.b = faces[ offset + 1 ]; + faceA.c = faces[ offset + 3 ]; - } + faceB = new Face3(); + faceB.a = faces[ offset + 1 ]; + faceB.b = faces[ offset + 2 ]; + faceB.c = faces[ offset + 3 ]; - // build geometry + offset += 4; - this.setIndex( indices ); - this.addAttribute( 'position', vertices ); - this.addAttribute( 'normal', normals ); - this.addAttribute( 'uv', uvs ); + if ( hasMaterial ) { - // helper functions + materialIndex = faces[ offset ++ ]; + faceA.materialIndex = materialIndex; + faceB.materialIndex = materialIndex; - function calculateVertexCount() { + } - var count = ( radialSegments + 1 ) * ( heightSegments + 1 ); + // to get face <=> uv index correspondence - if ( openEnded === false ) { + fi = geometry.faces.length; - count += ( ( radialSegments + 1 ) * nbCap ) + ( radialSegments * nbCap ); + if ( hasFaceVertexUv ) { - } + for ( i = 0; i < nUvLayers; i ++ ) { - return count; + uvLayer = json.uvs[ i ]; - } + geometry.faceVertexUvs[ i ][ fi ] = []; + geometry.faceVertexUvs[ i ][ fi + 1 ] = []; - function calculateIndexCount() { + for ( j = 0; j < 4; j ++ ) { - var count = radialSegments * heightSegments * 2 * 3; + uvIndex = faces[ offset ++ ]; - if ( openEnded === false ) { + u = uvLayer[ uvIndex * 2 ]; + v = uvLayer[ uvIndex * 2 + 1 ]; - count += radialSegments * nbCap * 3; + uv = new Vector2( u, v ); - } + if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv ); + if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv ); - return count; + } - } + } - function generateTorso() { + } - var x, y; - var normal = new Vector3(); - var vertex = new Vector3(); + if ( hasFaceNormal ) { - var groupCount = 0; + normalIndex = faces[ offset ++ ] * 3; - // this will be used to calculate the normal - var slope = ( radiusBottom - radiusTop ) / height; + faceA.normal.set( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); - // generate vertices, normals and uvs + faceB.normal.copy( faceA.normal ); - for ( y = 0; y <= heightSegments; y ++ ) { + } - var indexRow = []; + if ( hasFaceVertexNormal ) { - var v = y / heightSegments; + for ( i = 0; i < 4; i ++ ) { - // calculate the radius of the current row - var radius = v * ( radiusBottom - radiusTop ) + radiusTop; + normalIndex = faces[ offset ++ ] * 3; - for ( x = 0; x <= radialSegments; x ++ ) { + normal = new Vector3( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); - var u = x / radialSegments; - var theta = u * thetaLength + thetaStart; + if ( i !== 2 ) faceA.vertexNormals.push( normal ); + if ( i !== 0 ) faceB.vertexNormals.push( normal ); - var sinTheta = Math.sin( theta ); - var cosTheta = Math.cos( theta ); + } - // vertex - vertex.x = radius * sinTheta; - vertex.y = - v * height + halfHeight; - vertex.z = radius * cosTheta; - vertices.setXYZ( index, vertex.x, vertex.y, vertex.z ); + } - // normal - normal.set( sinTheta, slope, cosTheta ).normalize(); - normals.setXYZ( index, normal.x, normal.y, normal.z ); - // uv - uvs.setXY( index, u, 1 - v ); + if ( hasFaceColor ) { - // save index of vertex in respective row - indexRow.push( index ); + colorIndex = faces[ offset ++ ]; + hex = colors[ colorIndex ]; - // increase index - index ++; + faceA.color.setHex( hex ); + faceB.color.setHex( hex ); - } + } - // now save vertices of the row in our index array - indexArray.push( indexRow ); - } + if ( hasFaceVertexColor ) { - // generate indices + for ( i = 0; i < 4; i ++ ) { - for ( x = 0; x < radialSegments; x ++ ) { + colorIndex = faces[ offset ++ ]; + hex = colors[ colorIndex ]; - for ( y = 0; y < heightSegments; y ++ ) { + if ( i !== 2 ) faceA.vertexColors.push( new Color( hex ) ); + if ( i !== 0 ) faceB.vertexColors.push( new Color( hex ) ); - // 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 ++; + } - // face two - indices.setX( indexOffset, i2 ); indexOffset ++; - indices.setX( indexOffset, i3 ); indexOffset ++; - indices.setX( indexOffset, i4 ); indexOffset ++; + geometry.faces.push( faceA ); + geometry.faces.push( faceB ); - // update counters - groupCount += 6; + } else { - } + face = new Face3(); + face.a = faces[ offset ++ ]; + face.b = faces[ offset ++ ]; + face.c = faces[ offset ++ ]; - } + if ( hasMaterial ) { - // add a group to the geometry. this will ensure multi material support - scope.addGroup( groupStart, groupCount, 0 ); + materialIndex = faces[ offset ++ ]; + face.materialIndex = materialIndex; - // calculate new start value for groups - groupStart += groupCount; + } - } + // to get face <=> uv index correspondence - function generateCap( top ) { + fi = geometry.faces.length; - var x, centerIndexStart, centerIndexEnd; + if ( hasFaceVertexUv ) { - var uv = new Vector2(); - var vertex = new Vector3(); + for ( i = 0; i < nUvLayers; i ++ ) { - var groupCount = 0; + uvLayer = json.uvs[ i ]; - var radius = ( top === true ) ? radiusTop : radiusBottom; - var sign = ( top === true ) ? 1 : - 1; + geometry.faceVertexUvs[ i ][ fi ] = []; - // save the index of the first center vertex - centerIndexStart = index; + for ( j = 0; j < 3; j ++ ) { - // 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 + uvIndex = faces[ offset ++ ]; - for ( x = 1; x <= radialSegments; x ++ ) { + u = uvLayer[ uvIndex * 2 ]; + v = uvLayer[ uvIndex * 2 + 1 ]; - // vertex - vertices.setXYZ( index, 0, halfHeight * sign, 0 ); + uv = new Vector2( u, v ); - // normal - normals.setXYZ( index, 0, sign, 0 ); + geometry.faceVertexUvs[ i ][ fi ].push( uv ); - // uv - uv.x = 0.5; - uv.y = 0.5; + } - uvs.setXY( index, uv.x, uv.y ); + } - // increase index - index ++; + } - } + if ( hasFaceNormal ) { - // save the index of the last center vertex - centerIndexEnd = index; + normalIndex = faces[ offset ++ ] * 3; - // now we generate the surrounding vertices, normals and uvs + face.normal.set( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); - for ( x = 0; x <= radialSegments; x ++ ) { + } - var u = x / radialSegments; - var theta = u * thetaLength + thetaStart; + if ( hasFaceVertexNormal ) { - var cosTheta = Math.cos( theta ); - var sinTheta = Math.sin( theta ); + for ( i = 0; i < 3; i ++ ) { - // vertex - vertex.x = radius * sinTheta; - vertex.y = halfHeight * sign; - vertex.z = radius * cosTheta; - vertices.setXYZ( index, vertex.x, vertex.y, vertex.z ); + normalIndex = faces[ offset ++ ] * 3; - // normal - normals.setXYZ( index, 0, sign, 0 ); + normal = new Vector3( + normals[ normalIndex ++ ], + normals[ normalIndex ++ ], + normals[ normalIndex ] + ); - // uv - uv.x = ( cosTheta * 0.5 ) + 0.5; - uv.y = ( sinTheta * 0.5 * sign ) + 0.5; - uvs.setXY( index, uv.x, uv.y ); + face.vertexNormals.push( normal ); - // increase index - index ++; + } - } + } - // generate indices - for ( x = 0; x < radialSegments; x ++ ) { + if ( hasFaceColor ) { - var c = centerIndexStart + x; - var i = centerIndexEnd + x; + colorIndex = faces[ offset ++ ]; + face.color.setHex( colors[ colorIndex ] ); - if ( top === true ) { + } - // face top - indices.setX( indexOffset, i ); indexOffset ++; - indices.setX( indexOffset, i + 1 ); indexOffset ++; - indices.setX( indexOffset, c ); indexOffset ++; - } else { + if ( hasFaceVertexColor ) { - // face bottom - indices.setX( indexOffset, i + 1 ); indexOffset ++; - indices.setX( indexOffset, i ); indexOffset ++; - indices.setX( indexOffset, c ); indexOffset ++; + for ( i = 0; i < 3; i ++ ) { - } + colorIndex = faces[ offset ++ ]; + face.vertexColors.push( new Color( colors[ colorIndex ] ) ); - // update counters - groupCount += 3; + } - } + } - // add a group to the geometry. this will ensure multi material support - scope.addGroup( groupStart, groupCount, top === true ? 1 : 2 ); + geometry.faces.push( face ); - // calculate new start value for groups - groupStart += groupCount; + } - } + } - } + } - CylinderBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - CylinderBufferGeometry.prototype.constructor = CylinderBufferGeometry; + function parseSkin() { - /** - * @author mrdoob / http://mrdoob.com/ - */ + var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2; - function CylinderGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) { + if ( json.skinWeights ) { - Geometry.call( this ); + for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) { - this.type = 'CylinderGeometry'; + var x = json.skinWeights[ i ]; + var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0; + var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0; + var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0; - this.parameters = { - radiusTop: radiusTop, - radiusBottom: radiusBottom, - height: height, - radialSegments: radialSegments, - heightSegments: heightSegments, - openEnded: openEnded, - thetaStart: thetaStart, - thetaLength: thetaLength - }; + geometry.skinWeights.push( new Vector4( x, y, z, w ) ); - this.fromBufferGeometry( new CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) ); - this.mergeVertices(); + } - } + } - CylinderGeometry.prototype = Object.create( Geometry.prototype ); - CylinderGeometry.prototype.constructor = CylinderGeometry; + if ( json.skinIndices ) { - /** - * @author abelnation / http://github.com/abelnation - */ + for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) { - function ConeGeometry( - radius, height, - radialSegments, heightSegments, - openEnded, thetaStart, thetaLength ) { + 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; - CylinderGeometry.call( this, - 0, radius, height, - radialSegments, heightSegments, - openEnded, thetaStart, thetaLength ); + geometry.skinIndices.push( new Vector4( a, b, c, d ) ); - this.type = 'ConeGeometry'; + } - this.parameters = { - radius: radius, - height: height, - radialSegments: radialSegments, - heightSegments: heightSegments, - openEnded: openEnded, - thetaStart: thetaStart, - thetaLength: thetaLength - }; + } - } + geometry.bones = json.bones; - ConeGeometry.prototype = Object.create( CylinderGeometry.prototype ); - ConeGeometry.prototype.constructor = ConeGeometry; + if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) { - /* - * @author: abelnation / http://github.com/abelnation - */ + console.warn( 'When skinning, number of vertices (' + geometry.vertices.length + '), skinIndices (' + + geometry.skinIndices.length + '), and skinWeights (' + geometry.skinWeights.length + ') should match.' ); - function ConeBufferGeometry( - radius, height, - radialSegments, heightSegments, - openEnded, thetaStart, thetaLength ) { + } - CylinderBufferGeometry.call( this, - 0, radius, height, - radialSegments, heightSegments, - openEnded, thetaStart, thetaLength ); + } - this.type = 'ConeBufferGeometry'; + function parseMorphing( scale ) { - this.parameters = { - radius: radius, - height: height, - radialSegments: radialSegments, - heightSegments: heightSegments, - thetaStart: thetaStart, - thetaLength: thetaLength - }; + if ( json.morphTargets !== undefined ) { - } + for ( var i = 0, l = json.morphTargets.length; i < l; i ++ ) { - ConeBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - ConeBufferGeometry.prototype.constructor = ConeBufferGeometry; + geometry.morphTargets[ i ] = {}; + geometry.morphTargets[ i ].name = json.morphTargets[ i ].name; + geometry.morphTargets[ i ].vertices = []; - /** - * @author benaadams / https://twitter.com/ben_a_adams - */ + var dstVertices = geometry.morphTargets[ i ].vertices; + var srcVertices = json.morphTargets[ i ].vertices; - function CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) { + for ( var v = 0, vl = srcVertices.length; v < vl; v += 3 ) { - BufferGeometry.call( this ); + var vertex = new Vector3(); + vertex.x = srcVertices[ v ] * scale; + vertex.y = srcVertices[ v + 1 ] * scale; + vertex.z = srcVertices[ v + 2 ] * scale; - this.type = 'CircleBufferGeometry'; + dstVertices.push( vertex ); - this.parameters = { - radius: radius, - segments: segments, - thetaStart: thetaStart, - thetaLength: thetaLength - }; + } - radius = radius || 50; - segments = segments !== undefined ? Math.max( 3, segments ) : 8; + } - thetaStart = thetaStart !== undefined ? thetaStart : 0; - thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; + } - var vertices = segments + 2; + if ( json.morphColors !== undefined && json.morphColors.length > 0 ) { - var positions = new Float32Array( vertices * 3 ); - var normals = new Float32Array( vertices * 3 ); - var uvs = new Float32Array( vertices * 2 ); + console.warn( 'THREE.JSONLoader: "morphColors" no longer supported. Using them as face colors.' ); - // center data is already zero, but need to set a few extras - normals[ 2 ] = 1.0; - uvs[ 0 ] = 0.5; - uvs[ 1 ] = 0.5; + var faces = geometry.faces; + var morphColors = json.morphColors[ 0 ].colors; - for ( var s = 0, i = 3, ii = 2 ; s <= segments; s ++, i += 3, ii += 2 ) { + for ( var i = 0, l = faces.length; i < l; i ++ ) { - var segment = thetaStart + s / segments * thetaLength; + faces[ i ].color.fromArray( morphColors, i * 3 ); - 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; + } - } + function parseAnimations() { - var indices = []; + var outputAnimations = []; - for ( var i = 1; i <= segments; i ++ ) { + // parse old style Bone/Hierarchy animations + var animations = []; - indices.push( i, i + 1, 0 ); + if ( json.animation !== undefined ) { - } + animations.push( json.animation ); - this.setIndex( new BufferAttribute( new Uint16Array( indices ), 1 ) ); - this.addAttribute( 'position', new BufferAttribute( positions, 3 ) ); - this.addAttribute( 'normal', new BufferAttribute( normals, 3 ) ); - this.addAttribute( 'uv', new BufferAttribute( uvs, 2 ) ); + } - this.boundingSphere = new Sphere( new Vector3(), radius ); + if ( json.animations !== undefined ) { - } + if ( json.animations.length ) { - CircleBufferGeometry.prototype = Object.create( BufferGeometry.prototype ); - CircleBufferGeometry.prototype.constructor = CircleBufferGeometry; + animations = animations.concat( json.animations ); - /** - * @author hughes - */ + } else { - function CircleGeometry( radius, segments, thetaStart, thetaLength ) { + animations.push( json.animations ); - Geometry.call( this ); + } - this.type = 'CircleGeometry'; + } - this.parameters = { - radius: radius, - segments: segments, - thetaStart: thetaStart, - thetaLength: thetaLength - }; + for ( var i = 0; i < animations.length; i ++ ) { - this.fromBufferGeometry( new CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) ); + var clip = AnimationClip.parseAnimation( animations[ i ], geometry.bones ); + if ( clip ) outputAnimations.push( clip ); - } + } - CircleGeometry.prototype = Object.create( Geometry.prototype ); - CircleGeometry.prototype.constructor = CircleGeometry; + // parse implicit morph animations + if ( geometry.morphTargets ) { - /** - * @author mrdoob / http://mrdoob.com/ - * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Cube.as - */ + // TODO: Figure out what an appropraite FPS is for morph target animations -- defaulting to 10, but really it is completely arbitrary. + var morphAnimationClips = AnimationClip.CreateClipsFromMorphTargetSequences( geometry.morphTargets, 10 ); + outputAnimations = outputAnimations.concat( morphAnimationClips ); - function BoxGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) { + } - Geometry.call( this ); + if ( outputAnimations.length > 0 ) geometry.animations = outputAnimations; - this.type = 'BoxGeometry'; + } - this.parameters = { - width: width, - height: height, - depth: depth, - widthSegments: widthSegments, - heightSegments: heightSegments, - depthSegments: depthSegments - }; + if ( json.materials === undefined || json.materials.length === 0 ) { - this.fromBufferGeometry( new BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) ); - this.mergeVertices(); + return { geometry: geometry }; - } + } else { - BoxGeometry.prototype = Object.create( Geometry.prototype ); - BoxGeometry.prototype.constructor = BoxGeometry; + var materials = Loader.prototype.initMaterials( json.materials, texturePath, this.crossOrigin ); + return { geometry: geometry, materials: materials }; + } - var Geometries = Object.freeze({ - WireframeGeometry: WireframeGeometry, - ParametricGeometry: ParametricGeometry, - TetrahedronGeometry: TetrahedronGeometry, - OctahedronGeometry: OctahedronGeometry, - IcosahedronGeometry: IcosahedronGeometry, - DodecahedronGeometry: DodecahedronGeometry, - PolyhedronGeometry: PolyhedronGeometry, - TubeGeometry: TubeGeometry, - TorusKnotGeometry: TorusKnotGeometry, - TorusKnotBufferGeometry: TorusKnotBufferGeometry, - TorusGeometry: TorusGeometry, - TorusBufferGeometry: TorusBufferGeometry, - TextGeometry: TextGeometry, - SphereBufferGeometry: SphereBufferGeometry, - SphereGeometry: SphereGeometry, - RingGeometry: RingGeometry, - RingBufferGeometry: RingBufferGeometry, - PlaneBufferGeometry: PlaneBufferGeometry, - PlaneGeometry: PlaneGeometry, - LatheGeometry: LatheGeometry, - LatheBufferGeometry: LatheBufferGeometry, - ShapeGeometry: ShapeGeometry, - ExtrudeGeometry: ExtrudeGeometry, - EdgesGeometry: EdgesGeometry, - ConeGeometry: ConeGeometry, - ConeBufferGeometry: ConeBufferGeometry, - CylinderGeometry: CylinderGeometry, - CylinderBufferGeometry: CylinderBufferGeometry, - CircleBufferGeometry: CircleBufferGeometry, - CircleGeometry: CircleGeometry, - BoxBufferGeometry: BoxBufferGeometry, - BoxGeometry: BoxGeometry - }); + } + + } ); /** * @author mrdoob / http://mrdoob.com/ @@ -34093,22 +34093,6 @@ constructor: Shape, - // Convenience method to return ExtrudeGeometry - - extrude: function ( options ) { - - return new ExtrudeGeometry( this, options ); - - }, - - // Convenience method to return ShapeGeometry - - makeGeometry: function ( options ) { - - return new ShapeGeometry( this, options ); - - }, - getPointsHoles: function ( divisions ) { var holesPts = []; @@ -40782,6 +40766,13 @@ } } ); + Object.assign( Line3.prototype, { + center: function ( optionalTarget ) { + console.warn( 'THREE.Line3: .center() has been renamed to .getCenter().' ); + return this.getCenter( optionalTarget ); + } + } ); + Object.assign( Matrix3.prototype, { multiplyVector3: function ( vector ) { console.warn( 'THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' ); @@ -40868,6 +40859,17 @@ } } ); + Object.assign( Shape.prototype, { + extrude: function ( options ) { + console.warn( 'THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead.' ); + return new ExtrudeGeometry( this, options ); + }, + makeGeometry: function ( options ) { + console.warn( 'THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead.' ); + return new ShapeGeometry( this, options ); + } + } ); + Object.assign( Vector3.prototype, { setEulerFromRotationMatrix: function () { console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' ); @@ -41646,22 +41648,6 @@ exports.Font = Font; exports.CurvePath = CurvePath; exports.Curve = Curve; - exports.ShadowMaterial = ShadowMaterial; - exports.SpriteMaterial = SpriteMaterial; - exports.RawShaderMaterial = RawShaderMaterial; - exports.ShaderMaterial = ShaderMaterial; - exports.PointsMaterial = PointsMaterial; - exports.MultiMaterial = MultiMaterial; - exports.MeshPhysicalMaterial = MeshPhysicalMaterial; - exports.MeshStandardMaterial = MeshStandardMaterial; - exports.MeshPhongMaterial = MeshPhongMaterial; - exports.MeshNormalMaterial = MeshNormalMaterial; - exports.MeshLambertMaterial = MeshLambertMaterial; - exports.MeshDepthMaterial = MeshDepthMaterial; - exports.MeshBasicMaterial = MeshBasicMaterial; - exports.LineDashedMaterial = LineDashedMaterial; - exports.LineBasicMaterial = LineBasicMaterial; - exports.Material = Material; exports.WireframeGeometry = WireframeGeometry; exports.ParametricGeometry = ParametricGeometry; exports.TetrahedronGeometry = TetrahedronGeometry; @@ -41694,6 +41680,22 @@ exports.CircleGeometry = CircleGeometry; exports.BoxBufferGeometry = BoxBufferGeometry; exports.BoxGeometry = BoxGeometry; + exports.ShadowMaterial = ShadowMaterial; + exports.SpriteMaterial = SpriteMaterial; + exports.RawShaderMaterial = RawShaderMaterial; + exports.ShaderMaterial = ShaderMaterial; + exports.PointsMaterial = PointsMaterial; + exports.MultiMaterial = MultiMaterial; + exports.MeshPhysicalMaterial = MeshPhysicalMaterial; + exports.MeshStandardMaterial = MeshStandardMaterial; + exports.MeshPhongMaterial = MeshPhongMaterial; + exports.MeshNormalMaterial = MeshNormalMaterial; + exports.MeshLambertMaterial = MeshLambertMaterial; + exports.MeshDepthMaterial = MeshDepthMaterial; + exports.MeshBasicMaterial = MeshBasicMaterial; + exports.LineDashedMaterial = LineDashedMaterial; + exports.LineBasicMaterial = LineBasicMaterial; + exports.Material = Material; exports.REVISION = REVISION; exports.MOUSE = MOUSE; exports.CullFaceNone = CullFaceNone; diff --git a/build/three.min.js b/build/three.min.js index bfae15dd9a7ddad30158ee915f8cb41959924238..121572b8536cd8687429685407f9bbb3aa4a22df 100644 --- a/build/three.min.js +++ b/build/three.min.js @@ -1,60 +1,60 @@ // threejs.org/license -(function(h,na){"object"===typeof exports&&"undefined"!==typeof module?na(exports):"function"===typeof define&&define.amd?define(["exports"],na):na(h.THREE=h.THREE||{})})(this,function(h){function na(){}function B(a,b){this.x=a||0;this.y=b||0}function Z(a,b,c,d,e,f,g,k,l,n){Object.defineProperty(this,"id",{value:Sd++});this.uuid=h.Math.generateUUID();this.sourceFile=this.name="";this.image=void 0!==a?a:Z.DEFAULT_IMAGE;this.mipmaps=[];this.mapping=void 0!==b?b:Z.DEFAULT_MAPPING;this.wrapS=void 0!== -c?c:1001;this.wrapT=void 0!==d?d:1001;this.magFilter=void 0!==e?e:1006;this.minFilter=void 0!==f?f:1008;this.anisotropy=void 0!==l?l:1;this.format=void 0!==g?g:1023;this.type=void 0!==k?k:1009;this.offset=new B(0,0);this.repeat=new B(1,1);this.generateMipmaps=!0;this.premultiplyAlpha=!1;this.flipY=!0;this.unpackAlignment=4;this.encoding=void 0!==n?n:3E3;this.version=0;this.onUpdate=null}function da(a,b,c,d){this.x=a||0;this.y=b||0;this.z=c||0;this.w=void 0!==d?d:1}function yb(a,b,c){this.uuid=h.Math.generateUUID(); -this.width=a;this.height=b;this.scissor=new da(0,0,a,b);this.scissorTest=!1;this.viewport=new da(0,0,a,b);c=c||{};void 0===c.minFilter&&(c.minFilter=1006);this.texture=new Z(void 0,void 0,c.wrapS,c.wrapT,c.magFilter,c.minFilter,c.format,c.type,c.anisotropy,c.encoding);this.depthBuffer=void 0!==c.depthBuffer?c.depthBuffer:!0;this.stencilBuffer=void 0!==c.stencilBuffer?c.stencilBuffer:!0;this.depthTexture=void 0!==c.depthTexture?c.depthTexture:null}function zb(a,b,c){yb.call(this,a,b,c);this.activeMipMapLevel= -this.activeCubeFace=0}function oa(a,b,c,d){this._x=a||0;this._y=b||0;this._z=c||0;this._w=void 0!==d?d:1}function q(a,b,c){this.x=a||0;this.y=b||0;this.z=c||0}function Q(){this.elements=new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]);0= +(function(h,na){"object"===typeof exports&&"undefined"!==typeof module?na(exports):"function"===typeof define&&define.amd?define(["exports"],na):na(h.THREE=h.THREE||{})})(this,function(h){function na(){}function C(a,b){this.x=a||0;this.y=b||0}function Z(a,b,c,d,e,f,g,k,l,n){Object.defineProperty(this,"id",{value:Sd++});this.uuid=h.Math.generateUUID();this.sourceFile=this.name="";this.image=void 0!==a?a:Z.DEFAULT_IMAGE;this.mipmaps=[];this.mapping=void 0!==b?b:Z.DEFAULT_MAPPING;this.wrapS=void 0!== +c?c:1001;this.wrapT=void 0!==d?d:1001;this.magFilter=void 0!==e?e:1006;this.minFilter=void 0!==f?f:1008;this.anisotropy=void 0!==l?l:1;this.format=void 0!==g?g:1023;this.type=void 0!==k?k:1009;this.offset=new C(0,0);this.repeat=new C(1,1);this.generateMipmaps=!0;this.premultiplyAlpha=!1;this.flipY=!0;this.unpackAlignment=4;this.encoding=void 0!==n?n:3E3;this.version=0;this.onUpdate=null}function da(a,b,c,d){this.x=a||0;this.y=b||0;this.z=c||0;this.w=void 0!==d?d:1}function Ab(a,b,c){this.uuid=h.Math.generateUUID(); +this.width=a;this.height=b;this.scissor=new da(0,0,a,b);this.scissorTest=!1;this.viewport=new da(0,0,a,b);c=c||{};void 0===c.minFilter&&(c.minFilter=1006);this.texture=new Z(void 0,void 0,c.wrapS,c.wrapT,c.magFilter,c.minFilter,c.format,c.type,c.anisotropy,c.encoding);this.depthBuffer=void 0!==c.depthBuffer?c.depthBuffer:!0;this.stencilBuffer=void 0!==c.stencilBuffer?c.stencilBuffer:!0;this.depthTexture=void 0!==c.depthTexture?c.depthTexture:null}function Bb(a,b,c){Ab.call(this,a,b,c);this.activeMipMapLevel= +this.activeCubeFace=0}function oa(a,b,c,d){this._x=a||0;this._y=b||0;this._z=c||0;this._w=void 0!==d?d:1}function q(a,b,c){this.x=a||0;this.y=b||0;this.z=c||0}function P(){this.elements=new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]);0= d||0 0 ) {\nfloat depth = gl_FragCoord.z / gl_FragCoord.w;\nfloat fogFactor = 0.0;\nif ( fogType == 1 ) {\nfogFactor = smoothstep( fogNear, fogFar, depth );\n} else {\nconst float LOG2 = 1.442695;\nfogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );\nfogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );\n}\ngl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );\n}\n}"].join("\n")); -y.compileShader(O);y.compileShader(P);y.attachShader(M,O);y.attachShader(M,P);y.linkProgram(M);K=M;w=y.getAttribLocation(K,"position");x=y.getAttribLocation(K,"uv");c=y.getUniformLocation(K,"uvOffset");d=y.getUniformLocation(K,"uvScale");e=y.getUniformLocation(K,"rotation");f=y.getUniformLocation(K,"scale");g=y.getUniformLocation(K,"color");k=y.getUniformLocation(K,"map");l=y.getUniformLocation(K,"opacity");n=y.getUniformLocation(K,"modelViewMatrix");r=y.getUniformLocation(K,"projectionMatrix");p= -y.getUniformLocation(K,"fogType");m=y.getUniformLocation(K,"fogDensity");h=y.getUniformLocation(K,"fogNear");u=y.getUniformLocation(K,"fogFar");v=y.getUniformLocation(K,"fogColor");z=y.getUniformLocation(K,"alphaTest");M=document.createElementNS("http://www.w3.org/1999/xhtml","canvas");M.width=8;M.height=8;O=M.getContext("2d");O.fillStyle="white";O.fillRect(0,0,8,8);Na=new Z(M);Na.needsUpdate=!0}y.useProgram(K);F.initAttributes();F.enableAttribute(w);F.enableAttribute(x);F.disableUnusedAttributes(); -F.disable(y.CULL_FACE);F.enable(y.BLEND);y.bindBuffer(y.ARRAY_BUFFER,G);y.vertexAttribPointer(w,2,y.FLOAT,!1,16,0);y.vertexAttribPointer(x,2,y.FLOAT,!1,16,8);y.bindBuffer(y.ELEMENT_ARRAY_BUFFER,E);y.uniformMatrix4fv(r,!1,Fa.projectionMatrix.elements);F.activeTexture(y.TEXTURE0);y.uniform1i(k,0);O=M=0;(P=q.fog)?(y.uniform3f(v,P.color.r,P.color.g,P.color.b),P&&P.isFog?(y.uniform1f(h,P.near),y.uniform1f(u,P.far),y.uniform1i(p,1),O=M=1):P&&P.isFogExp2&&(y.uniform1f(m,P.density),y.uniform1i(p,2),O=M=2)): -(y.uniform1i(p,0),O=M=0);for(var P=0,R=b.length;P 0 ) {\nfloat depth = gl_FragCoord.z / gl_FragCoord.w;\nfloat fogFactor = 0.0;\nif ( fogType == 1 ) {\nfogFactor = smoothstep( fogNear, fogFar, depth );\n} else {\nconst float LOG2 = 1.442695;\nfogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );\nfogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );\n}\ngl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );\n}\n}"].join("\n")); +z.compileShader(N);z.compileShader(O);z.attachShader(M,N);z.attachShader(M,O);z.linkProgram(M);K=M;w=z.getAttribLocation(K,"position");x=z.getAttribLocation(K,"uv");c=z.getUniformLocation(K,"uvOffset");d=z.getUniformLocation(K,"uvScale");e=z.getUniformLocation(K,"rotation");f=z.getUniformLocation(K,"scale");g=z.getUniformLocation(K,"color");k=z.getUniformLocation(K,"map");l=z.getUniformLocation(K,"opacity");n=z.getUniformLocation(K,"modelViewMatrix");r=z.getUniformLocation(K,"projectionMatrix");p= +z.getUniformLocation(K,"fogType");m=z.getUniformLocation(K,"fogDensity");h=z.getUniformLocation(K,"fogNear");u=z.getUniformLocation(K,"fogFar");v=z.getUniformLocation(K,"fogColor");A=z.getUniformLocation(K,"alphaTest");M=document.createElementNS("http://www.w3.org/1999/xhtml","canvas");M.width=8;M.height=8;N=M.getContext("2d");N.fillStyle="white";N.fillRect(0,0,8,8);Na=new Z(M);Na.needsUpdate=!0}z.useProgram(K);F.initAttributes();F.enableAttribute(w);F.enableAttribute(x);F.disableUnusedAttributes(); +F.disable(z.CULL_FACE);F.enable(z.BLEND);z.bindBuffer(z.ARRAY_BUFFER,G);z.vertexAttribPointer(w,2,z.FLOAT,!1,16,0);z.vertexAttribPointer(x,2,z.FLOAT,!1,16,8);z.bindBuffer(z.ELEMENT_ARRAY_BUFFER,E);z.uniformMatrix4fv(r,!1,Fa.projectionMatrix.elements);F.activeTexture(z.TEXTURE0);z.uniform1i(k,0);N=M=0;(O=q.fog)?(z.uniform3f(v,O.color.r,O.color.g,O.color.b),O&&O.isFog?(z.uniform1f(h,O.near),z.uniform1f(u,O.far),z.uniform1i(p,1),N=M=1):O&&O.isFogExp2&&(z.uniform1f(m,O.density),z.uniform1i(p,2),N=M=2)): +(z.uniform1i(p,0),N=M=0);for(var O=0,Q=b.length;Oc){var d=b;b=c;c=d}d=a[b];return void 0===d?(a[b]=[c],!0):-1===d.indexOf(c)?(d.push(c),!0):!1}var f=new ff(a,b,c);return{getAttributeBuffer:function(a){return a.isInterleavedBufferAttribute?b.get(a.data).__webglBuffer:b.get(a).__webglBuffer},getWireframeAttribute:function(c){var f=b.get(c); -if(void 0!==f.wireframe)return f.wireframe;var l=[],n=c.index,h=c.attributes;c=h.position;if(null!==n)for(var h={},n=n.array,p=0,m=n.length;pb||a.height>b){var c=b/Math.max(a.width,a.height),d=document.createElementNS("http://www.w3.org/1999/xhtml","canvas");d.width=Math.floor(a.width*c);d.height=Math.floor(a.height*c);d.getContext("2d").drawImage(a,0,0,a.width, a.height,0,0,d.width,d.height);console.warn("THREE.WebGLRenderer: image is too big ("+a.width+"x"+a.height+"). Resized to "+d.width+"x"+d.height,a);return d}return a}function l(a){return h.Math.isPowerOfTwo(a.width)&&h.Math.isPowerOfTwo(a.height)}function n(b){return 1003===b||1004===b||1005===b?a.NEAREST:a.LINEAR}function r(b){b=b.target;b.removeEventListener("dispose",r);a:{var c=d.get(b);if(b.image&&c.__image__webglTextureCube)a.deleteTexture(c.__image__webglTextureCube);else{if(void 0===c.__webglInit)break a; -a.deleteTexture(c.__webglTexture)}d["delete"](b)}z.textures--}function p(b){b=b.target;b.removeEventListener("dispose",p);var c=d.get(b),e=d.get(b.texture);if(b){void 0!==e.__webglTexture&&a.deleteTexture(e.__webglTexture);b.depthTexture&&b.depthTexture.dispose();if(b&&b.isWebGLRenderTargetCube)for(e=0;6>e;e++)a.deleteFramebuffer(c.__webglFramebuffer[e]),c.__webglDepthbuffer&&a.deleteRenderbuffer(c.__webglDepthbuffer[e]);else a.deleteFramebuffer(c.__webglFramebuffer),c.__webglDepthbuffer&&a.deleteRenderbuffer(c.__webglDepthbuffer); -d["delete"](b.texture);d["delete"](b)}z.textures--}function m(b,g){var n=d.get(b);if(0e;e++)a.deleteFramebuffer(c.__webglFramebuffer[e]),c.__webglDepthbuffer&&a.deleteRenderbuffer(c.__webglDepthbuffer[e]);else a.deleteFramebuffer(c.__webglFramebuffer),c.__webglDepthbuffer&&a.deleteRenderbuffer(c.__webglDepthbuffer); +d["delete"](b.texture);d["delete"](b)}A.textures--}function m(b,g){var n=d.get(b);if(0u;u++)p[u]=h||m?m?b.image[u].image: -b.image[u]:k(b.image[u],e.maxCubemapSize);var v=l(p[0]),q=f(b.format),w=f(b.type);t(a.TEXTURE_CUBE_MAP,b,v);for(u=0;6>u;u++)if(h)for(var B,D=p[u].mipmaps,C=0,M=D.length;Cn;n++)e.__webglFramebuffer[n]=a.createFramebuffer()}else e.__webglFramebuffer=a.createFramebuffer();if(g){c.bindTexture(a.TEXTURE_CUBE_MAP,f.__webglTexture);t(a.TEXTURE_CUBE_MAP,b.texture,k);for(n=0;6>n;n++)u(e.__webglFramebuffer[n],b,a.COLOR_ATTACHMENT0, +a.DEPTH_COMPONENT16,c.width,c.height),a.framebufferRenderbuffer(a.FRAMEBUFFER,a.DEPTH_ATTACHMENT,a.RENDERBUFFER,b)):c.depthBuffer&&c.stencilBuffer?(a.renderbufferStorage(a.RENDERBUFFER,a.DEPTH_STENCIL,c.width,c.height),a.framebufferRenderbuffer(a.FRAMEBUFFER,a.DEPTH_STENCIL_ATTACHMENT,a.RENDERBUFFER,b)):a.renderbufferStorage(a.RENDERBUFFER,a.RGBA4,c.width,c.height);a.bindRenderbuffer(a.RENDERBUFFER,null)}var A=g.memory,w="undefined"!==typeof WebGL2RenderingContext&&a instanceof WebGL2RenderingContext; +this.setTexture2D=m;this.setTextureCube=function(b,g){var n=d.get(b);if(6===b.image.length)if(0u;u++)p[u]=h||m?m?b.image[u].image: +b.image[u]:k(b.image[u],e.maxCubemapSize);var v=l(p[0]),q=f(b.format),w=f(b.type);t(a.TEXTURE_CUBE_MAP,b,v);for(u=0;6>u;u++)if(h)for(var C,D=p[u].mipmaps,y=0,M=D.length;yn;n++)e.__webglFramebuffer[n]=a.createFramebuffer()}else e.__webglFramebuffer=a.createFramebuffer();if(g){c.bindTexture(a.TEXTURE_CUBE_MAP,f.__webglTexture);t(a.TEXTURE_CUBE_MAP,b.texture,k);for(n=0;6>n;n++)u(e.__webglFramebuffer[n],b,a.COLOR_ATTACHMENT0, a.TEXTURE_CUBE_MAP_POSITIVE_X+n);b.texture.generateMipmaps&&k&&a.generateMipmap(a.TEXTURE_CUBE_MAP);c.bindTexture(a.TEXTURE_CUBE_MAP,null)}else c.bindTexture(a.TEXTURE_2D,f.__webglTexture),t(a.TEXTURE_2D,b.texture,k),u(e.__webglFramebuffer,b,a.COLOR_ATTACHMENT0,a.TEXTURE_2D),b.texture.generateMipmaps&&k&&a.generateMipmap(a.TEXTURE_2D),c.bindTexture(a.TEXTURE_2D,null);if(b.depthBuffer){e=d.get(b);f=b&&b.isWebGLRenderTargetCube;if(b.depthTexture){if(f)throw Error("target.depthTexture not supported in Cube render targets"); if(b&&b.isWebGLRenderTargetCube)throw Error("Depth Texture with cube render targets is not supported!");a.bindFramebuffer(a.FRAMEBUFFER,e.__webglFramebuffer);if(!b.depthTexture||!b.depthTexture.isDepthTexture)throw Error("renderTarget.depthTexture must be an instance of THREE.DepthTexture");d.get(b.depthTexture).__webglTexture&&b.depthTexture.image.width===b.width&&b.depthTexture.image.height===b.height||(b.depthTexture.image.width=b.width,b.depthTexture.image.height=b.height,b.depthTexture.needsUpdate= !0);m(b.depthTexture,0);e=d.get(b.depthTexture).__webglTexture;if(1026===b.depthTexture.format)a.framebufferTexture2D(a.FRAMEBUFFER,a.DEPTH_ATTACHMENT,a.TEXTURE_2D,e,0);else if(1027===b.depthTexture.format)a.framebufferTexture2D(a.FRAMEBUFFER,a.DEPTH_STENCIL_ATTACHMENT,a.TEXTURE_2D,e,0);else throw Error("Unknown depthTexture format");}else if(f)for(e.__webglDepthbuffer=[],f=0;6>f;f++)a.bindFramebuffer(a.FRAMEBUFFER,e.__webglFramebuffer[f]),e.__webglDepthbuffer[f]=a.createRenderbuffer(),v(e.__webglDepthbuffer[f], b);else a.bindFramebuffer(a.FRAMEBUFFER,e.__webglFramebuffer),e.__webglDepthbuffer=a.createRenderbuffer(),v(e.__webglDepthbuffer,b);a.bindFramebuffer(a.FRAMEBUFFER,null)}};this.updateRenderTargetMipmap=function(b){var e=b.texture;e.generateMipmaps&&l(b)&&1003!==e.minFilter&&1006!==e.minFilter&&(b=b&&b.isWebGLRenderTargetCube?a.TEXTURE_CUBE_MAP:a.TEXTURE_2D,e=d.get(e).__webglTexture,c.bindTexture(b,e),a.generateMipmap(b),c.bindTexture(b,null))}}function jf(){var a={};return{get:function(b){b=b.uuid; var c=a[b];void 0===c&&(c={},a[b]=c);return c},"delete":function(b){delete a[b.uuid]},clear:function(){a={}}}}function kf(a,b,c){function d(b,c,d){var e=new Uint8Array(4),f=a.createTexture();a.bindTexture(b,f);a.texParameteri(b,a.TEXTURE_MIN_FILTER,a.NEAREST);a.texParameteri(b,a.TEXTURE_MAG_FILTER,a.NEAREST);for(b=0;b=ja.maxTextures&&console.warn("WebGLRenderer: trying to use "+a+" texture units while this GPU supports only "+ja.maxTextures);Z+=1; return a};this.setTexture2D=function(){var a=!1;return function(b,c){b&&b.isWebGLRenderTarget&&(a||(console.warn("THREE.WebGLRenderer.setTexture2D: don't use render targets as textures. Use their .texture property instead."),a=!0),b=b.texture);sa.setTexture2D(b,c)}}();this.setTexture=function(){var a=!1;return function(b,c){a||(console.warn("THREE.WebGLRenderer: .setTexture is deprecated, use setTexture2D instead."),a=!0);sa.setTexture2D(b,c)}}();this.setTextureCube=function(){var a=!1;return function(b, c){b&&b.isWebGLRenderTargetCube&&(a||(console.warn("THREE.WebGLRenderer.setTextureCube: don't use cube render targets as textures. Use their .texture property instead."),a=!0),b=b.texture);b&&b.isCubeTexture||Array.isArray(b.image)&&6===b.image.length?sa.setTextureCube(b,c):sa.setTextureCubeDynamic(b,c)}}();this.getCurrentRenderTarget=function(){return V};this.setRenderTarget=function(a){(V=a)&&void 0===fa.get(a).__webglFramebuffer&&sa.setupRenderTarget(a);var b=a&&a.isWebGLRenderTargetCube,c;a?(c= -fa.get(a),c=b?c.__webglFramebuffer[a.activeCubeFace]:c.__webglFramebuffer,Y.copy(a.scissor),cb=a.scissorTest,Wa.copy(a.viewport)):(c=null,Y.copy(ha).multiplyScalar(Pa),cb=la,Wa.copy(ga).multiplyScalar(Pa));W!==c&&(A.bindFramebuffer(A.FRAMEBUFFER,c),W=c);X.scissor(Y);X.setScissorTest(cb);X.viewport(Wa);b&&(b=fa.get(a.texture),A.framebufferTexture2D(A.FRAMEBUFFER,A.COLOR_ATTACHMENT0,A.TEXTURE_CUBE_MAP_POSITIVE_X+a.activeCubeFace,b.__webglTexture,a.activeMipMapLevel))};this.readRenderTargetPixels=function(a, -b,c,d,e,f){if(!1===(a&&a.isWebGLRenderTarget))console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.");else{var g=fa.get(a).__webglFramebuffer;if(g){var k=!1;g!==W&&(A.bindFramebuffer(A.FRAMEBUFFER,g),k=!0);try{var l=a.texture,n=l.format,h=l.type;1023!==n&&w(n)!==A.getParameter(A.IMPLEMENTATION_COLOR_READ_FORMAT)?console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format."):1009===h||w(h)=== -A.getParameter(A.IMPLEMENTATION_COLOR_READ_TYPE)||1015===h&&(ia.get("OES_texture_float")||ia.get("WEBGL_color_buffer_float"))||1016===h&&ia.get("EXT_color_buffer_half_float")?A.checkFramebufferStatus(A.FRAMEBUFFER)===A.FRAMEBUFFER_COMPLETE?0<=b&&b<=a.width-d&&0<=c&&c<=a.height-e&&A.readPixels(b,c,d,e,w(n),w(h),f):console.error("THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete."):console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.")}finally{k&& -A.bindFramebuffer(A.FRAMEBUFFER,W)}}}}}function Eb(a,b){this.name="";this.color=new I(a);this.density=void 0!==b?b:2.5E-4}function Fb(a,b,c){this.name="";this.color=new I(a);this.near=void 0!==b?b:1;this.far=void 0!==c?c:1E3}function fb(){D.call(this);this.type="Scene";this.overrideMaterial=this.fog=this.background=null;this.autoUpdate=!0}function sd(a,b,c,d,e){D.call(this);this.lensFlares=[];this.positionScreen=new q;this.customUpdateCallback=void 0;void 0!==a&&this.add(a,b,c,d,e)}function gb(a){T.call(this); -this.type="SpriteMaterial";this.color=new I(16777215);this.map=null;this.rotation=0;this.lights=this.fog=!1;this.setValues(a)}function gc(a){D.call(this);this.type="Sprite";this.material=void 0!==a?a:new gb}function hc(){D.call(this);this.type="LOD";Object.defineProperties(this,{levels:{enumerable:!0,value:[]}})}function hb(a,b,c,d,e,f,g,k,l,n,h,p){Z.call(this,null,f,g,k,l,n,d,e,h,p);this.image={data:a,width:b,height:c};this.magFilter=void 0!==l?l:1003;this.minFilter=void 0!==n?n:1003;this.generateMipmaps= -this.flipY=!1}function Nc(a,b,c){this.useVertexTexture=void 0!==c?c:!0;this.identityMatrix=new Q;a=a||[];this.bones=a.slice(0);this.useVertexTexture?(a=Math.sqrt(4*this.bones.length),a=h.Math.nextPowerOfTwo(Math.ceil(a)),this.boneTextureHeight=this.boneTextureWidth=a=Math.max(a,4),this.boneMatrices=new Float32Array(this.boneTextureWidth*this.boneTextureHeight*4),this.boneTexture=new hb(this.boneMatrices,this.boneTextureWidth,this.boneTextureHeight,1023,1015)):this.boneMatrices=new Float32Array(16* -this.bones.length);if(void 0===b)this.calculateInverses();else if(this.bones.length===b.length)this.boneInverses=b.slice(0);else for(console.warn("THREE.Skeleton bonInverses is the wrong length."),this.boneInverses=[],b=0,a=this.bones.length;b=a.HAVE_CURRENT_DATA&&(h.needsUpdate=!0)}Z.call(this,a,b,c,d,e,f,g,k,l);this.generateMipmaps= -!1;var h=this;n()}function Hb(a,b,c,d,e,f,g,k,l,n,h,p){Z.call(this,null,f,g,k,l,n,d,e,h,p);this.image={width:b,height:c};this.mipmaps=a;this.generateMipmaps=this.flipY=!1}function Rc(a,b,c,d,e,f,g,k,l){Z.call(this,a,b,c,d,e,f,g,k,l);this.needsUpdate=!0}function jc(a,b,c,d,e,f,g,k,l,n){n=void 0!==n?n:1026;if(1026!==n&&1027!==n)throw Error("DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat");Z.call(this,null,d,e,f,g,k,n,c,l);this.image={width:a,height:b};this.type=void 0!== -c?c:1012;this.magFilter=void 0!==g?g:1003;this.minFilter=void 0!==k?k:1003;this.generateMipmaps=this.flipY=!1}function Ib(){Ea.call(this,{uniforms:h.UniformsUtils.merge([U.lights,{opacity:{value:1}}]),vertexShader:Y.shadow_vert,fragmentShader:Y.shadow_frag});this.transparent=this.lights=!0;Object.defineProperties(this,{opacity:{enumerable:!0,get:function(){return this.uniforms.opacity.value},set:function(a){this.uniforms.opacity.value=a}}})}function Jb(a){Ea.call(this,a);this.type="RawShaderMaterial"} -function kc(a){this.uuid=h.Math.generateUUID();this.type="MultiMaterial";this.materials=a instanceof Array?a:[];this.visible=!0}function La(a){T.call(this);this.defines={STANDARD:""};this.type="MeshStandardMaterial";this.color=new I(16777215);this.metalness=this.roughness=.5;this.lightMap=this.map=null;this.lightMapIntensity=1;this.aoMap=null;this.aoMapIntensity=1;this.emissive=new I(0);this.emissiveIntensity=1;this.bumpMap=this.emissiveMap=null;this.bumpScale=1;this.normalMap=null;this.normalScale= -new B(1,1);this.displacementMap=null;this.displacementScale=1;this.displacementBias=0;this.envMap=this.alphaMap=this.metalnessMap=this.roughnessMap=null;this.envMapIntensity=1;this.refractionRatio=.98;this.wireframe=!1;this.wireframeLinewidth=1;this.wireframeLinejoin=this.wireframeLinecap="round";this.morphNormals=this.morphTargets=this.skinning=!1;this.setValues(a)}function ib(a){La.call(this);this.defines={PHYSICAL:""};this.type="MeshPhysicalMaterial";this.reflectivity=.5;this.clearCoatRoughness= -this.clearCoat=0;this.setValues(a)}function Za(a){T.call(this);this.type="MeshPhongMaterial";this.color=new I(16777215);this.specular=new I(1118481);this.shininess=30;this.lightMap=this.map=null;this.lightMapIntensity=1;this.aoMap=null;this.aoMapIntensity=1;this.emissive=new I(0);this.emissiveIntensity=1;this.bumpMap=this.emissiveMap=null;this.bumpScale=1;this.normalMap=null;this.normalScale=new B(1,1);this.displacementMap=null;this.displacementScale=1;this.displacementBias=0;this.envMap=this.alphaMap= -this.specularMap=null;this.combine=0;this.reflectivity=1;this.refractionRatio=.98;this.wireframe=!1;this.wireframeLinewidth=1;this.wireframeLinejoin=this.wireframeLinecap="round";this.morphNormals=this.morphTargets=this.skinning=!1;this.setValues(a)}function jb(a){T.call(this,a);this.type="MeshNormalMaterial";this.wireframe=!1;this.wireframeLinewidth=1;this.morphTargets=this.lights=this.fog=!1;this.setValues(a)}function kb(a){T.call(this);this.type="MeshLambertMaterial";this.color=new I(16777215); -this.lightMap=this.map=null;this.lightMapIntensity=1;this.aoMap=null;this.aoMapIntensity=1;this.emissive=new I(0);this.emissiveIntensity=1;this.envMap=this.alphaMap=this.specularMap=this.emissiveMap=null;this.combine=0;this.reflectivity=1;this.refractionRatio=.98;this.wireframe=!1;this.wireframeLinewidth=1;this.wireframeLinejoin=this.wireframeLinecap="round";this.morphNormals=this.morphTargets=this.skinning=!1;this.setValues(a)}function lb(a){T.call(this);this.type="LineDashedMaterial";this.color= -new I(16777215);this.scale=this.linewidth=1;this.dashSize=3;this.gapSize=1;this.lights=!1;this.setValues(a)}function td(a,b,c){var d=this,e=!1,f=0,g=0;this.onStart=void 0;this.onLoad=a;this.onProgress=b;this.onError=c;this.itemStart=function(a){g++;if(!1===e&&void 0!==d.onStart)d.onStart(a,f,g);e=!0};this.itemEnd=function(a){f++;if(void 0!==d.onProgress)d.onProgress(a,f,g);if(f===g&&(e=!1,void 0!==d.onLoad))d.onLoad()};this.itemError=function(a){if(void 0!==d.onError)d.onError(a)}}function wa(a){this.manager= -void 0!==a?a:h.DefaultLoadingManager}function ke(a){this.manager=void 0!==a?a:h.DefaultLoadingManager;this._parser=null}function ud(a){this.manager=void 0!==a?a:h.DefaultLoadingManager;this._parser=null}function lc(a){this.manager=void 0!==a?a:h.DefaultLoadingManager}function vd(a){this.manager=void 0!==a?a:h.DefaultLoadingManager}function Sc(a){this.manager=void 0!==a?a:h.DefaultLoadingManager}function ca(a,b){D.call(this);this.type="Light";this.color=new I(a);this.intensity=void 0!==b?b:1;this.receiveShadow= -void 0}function Tc(a,b,c){ca.call(this,a,c);this.type="HemisphereLight";this.castShadow=void 0;this.position.copy(D.DefaultUp);this.updateMatrix();this.groundColor=new I(b)}function mb(a){this.camera=a;this.bias=0;this.radius=1;this.mapSize=new B(512,512);this.map=null;this.matrix=new Q}function Uc(){mb.call(this,new Ca(50,1,.5,500))}function Vc(a,b,c,d,e,f){ca.call(this,a,b);this.type="SpotLight";this.position.copy(D.DefaultUp);this.updateMatrix();this.target=new D;Object.defineProperty(this,"power", -{get:function(){return this.intensity*Math.PI},set:function(a){this.intensity=a/Math.PI}});this.distance=void 0!==c?c:0;this.angle=void 0!==d?d:Math.PI/3;this.penumbra=void 0!==e?e:0;this.decay=void 0!==f?f:1;this.shadow=new Uc}function Wc(a,b,c,d){ca.call(this,a,b);this.type="PointLight";Object.defineProperty(this,"power",{get:function(){return 4*this.intensity*Math.PI},set:function(a){this.intensity=a/(4*Math.PI)}});this.distance=void 0!==c?c:0;this.decay=void 0!==d?d:1;this.shadow=new mb(new Ca(90, -1,.5,500))}function Xc(a){mb.call(this,new Db(-5,5,5,-5,.5,500))}function Yc(a,b){ca.call(this,a,b);this.type="DirectionalLight";this.position.copy(D.DefaultUp);this.updateMatrix();this.target=new D;this.shadow=new Xc}function Zc(a,b){ca.call(this,a,b);this.type="AmbientLight";this.castShadow=void 0}function ja(a,b,c,d){this.parameterPositions=a;this._cachedIndex=0;this.resultBuffer=void 0!==d?d:new b.constructor(c);this.sampleValues=b;this.valueSize=c}function $c(a,b,c,d){ja.call(this,a,b,c,d);this._offsetNext= -this._weightNext=this._offsetPrev=this._weightPrev=-0}function mc(a,b,c,d){ja.call(this,a,b,c,d)}function ad(a,b,c,d){ja.call(this,a,b,c,d)}function nb(a,b,c,d){if(void 0===a)throw Error("track name is undefined");if(void 0===b||0===b.length)throw Error("no keyframes in track named "+a);this.name=a;this.times=h.AnimationUtils.convertArray(b,this.TimeBufferType);this.values=h.AnimationUtils.convertArray(c,this.ValueBufferType);this.setInterpolation(d||this.DefaultInterpolation);this.validate();this.optimize()} -function Kb(a,b,c,d){nb.call(this,a,b,c,d)}function bd(a,b,c,d){ja.call(this,a,b,c,d)}function nc(a,b,c,d){nb.call(this,a,b,c,d)}function Lb(a,b,c,d){nb.call(this,a,b,c,d)}function cd(a,b,c,d){nb.call(this,a,b,c,d)}function dd(a,b,c){nb.call(this,a,b,c)}function ed(a,b,c,d){nb.call(this,a,b,c,d)}function ob(a,b,c,d){nb.apply(this,arguments)}function sa(a,b,c){this.name=a;this.tracks=c;this.duration=void 0!==b?b:-1;this.uuid=h.Math.generateUUID();0>this.duration&&this.resetDuration();this.optimize()} -function fd(a){this.manager=void 0!==a?a:h.DefaultLoadingManager;this.textures={}}function wd(a){this.manager=void 0!==a?a:h.DefaultLoadingManager}function pb(){this.onLoadStart=function(){};this.onLoadProgress=function(){};this.onLoadComplete=function(){}}function xd(a){"boolean"===typeof a&&(console.warn("THREE.JSONLoader: showStatus parameter has been removed from constructor."),a=void 0);this.manager=void 0!==a?a:h.DefaultLoadingManager;this.withCredentials=!1}function Mb(a){function b(a,b){return a- -b}H.call(this);var c=[0,0],d={},e=["a","b","c"];if(a&&a.isGeometry){var f=a.vertices,g=a.faces,k=0,l=new Uint32Array(6*g.length);a=0;for(var n=g.length;ap;p++){c[0]=h[e[p]];c[1]=h[e[(p+1)%3]];c.sort(b);var m=c.toString();void 0===d[m]&&(l[2*k]=c[0],l[2*k+1]=c[1],d[m]=!0,k++)}c=new Float32Array(6*k);a=0;for(n=k;ap;p++)d=f[l[2*a+p]],k=6*a+3*p,c[k+0]=d.x,c[k+1]=d.y,c[k+2]=d.z;this.addAttribute("position",new C(c,3))}else if(a&&a.isBufferGeometry){if(null!== -a.index){n=a.index.array;f=a.attributes.position;e=a.groups;k=0;0===e.length&&a.addGroup(0,n.length);l=new Uint32Array(2*n.length);g=0;for(h=e.length;gp;p++)c[0]=n[a+p],c[1]=n[a+(p+1)%3],c.sort(b),m=c.toString(),void 0===d[m]&&(l[2*k]=c[0],l[2*k+1]=c[1],d[m]=!0,k++)}c=new Float32Array(6*k);a=0;for(n=k;ap;p++)k=6*a+3*p,d=l[2*a+p],c[k+0]=f.getX(d),c[k+1]=f.getY(d),c[k+2]=f.getZ(d)}else for(f=a.attributes.position.array, -k=f.length/3,l=k/3,c=new Float32Array(6*k),a=0,n=l;ap;p++)k=18*a+6*p,l=9*a+3*p,c[k+0]=f[l],c[k+1]=f[l+1],c[k+2]=f[l+2],d=9*a+(p+1)%3*3,c[k+3]=f[d],c[k+4]=f[d+1],c[k+5]=f[d+2];this.addAttribute("position",new C(c,3))}}function oc(a,b,c){S.call(this);this.type="ParametricGeometry";this.parameters={func:a,slices:b,stacks:c};var d=this.vertices,e=this.faces,f=this.faceVertexUvs[0],g,k,l,n,h=b+1;for(g=0;g<=c;g++)for(n=g/c,k=0;k<=b;k++)l=k/b,l=a(l,n),d.push(l);var p,m,t,u;for(g=0;gc&&1===a.x&&(a=new B(a.x-1,a.y));0===b.x&&0===b.z&&(a=new B(c/2/Math.PI+.5,a.y));return a.clone()}S.call(this);this.type="PolyhedronGeometry";this.parameters={vertices:a,indices:b,radius:c,detail:d};c=c||1;d=d||0;for(var l=this,n=0,h=a.length;nm&&(.2>d&&(b[0].x+=1),.2>a&&(b[1].x+=1),.2>p&&(b[2].x+=1));n=0;for(h=this.vertices.length;nm;m++){e[0]=p[g[m]];e[1]=p[g[(m+1)%3]];e.sort(c);var t=e.toString();void 0===f[t]? -f[t]={vert1:e[0],vert2:e[1],face1:n,face2:void 0}:f[t].face2=n}e=[];for(t in f)if(g=f[t],void 0===g.face2||k[g.face1].normal.dot(k[g.face2].normal)<=d)n=l[g.vert1],e.push(n.x),e.push(n.y),e.push(n.z),n=l[g.vert2],e.push(n.x),e.push(n.y),e.push(n.z);this.addAttribute("position",new C(new Float32Array(e),3))}function ab(a,b,c,d,e,f,g,k){function l(c){var e,f,l,h=new B,m=new q,p=0,r=!0===c?a:b,N=!0===c?1:-1;f=w;for(e=1;e<=d;e++)u.setXYZ(w,0,y*N,0),v.setXYZ(w,0,N,0),h.x=.5,h.y=.5,z.setXY(w,h.x,h.y),w++; -l=w;for(e=0;e<=d;e++){var C=e/d*k+g,D=Math.cos(C),C=Math.sin(C);m.x=r*C;m.y=y*N;m.z=r*D;u.setXYZ(w,m.x,m.y,m.z);v.setXYZ(w,0,N,0);h.x=.5*D+.5;h.y=.5*C*N+.5;z.setXY(w,h.x,h.y);w++}for(e=0;ec;c++,d++){var e=c/32*Math.PI*2,f=d/32*Math.PI*2;b.push(Math.cos(e),Math.sin(e),1,Math.cos(f),Math.sin(f),1)}a.addAttribute("position",new ka(b,3));b=new ga({fog:!1});this.cone=new ba(a,b);this.add(this.cone);this.update()}function $b(a){this.bones=this.getBoneList(a);for(var b=new S,c=0;cd;d++)c.faces[d].color=this.colors[4>d?0:1];d=new Ja({vertexColors:1,wireframe:!0});this.lightSphere=new va(c,d);this.add(this.lightSphere);this.update()}function Gc(a,b,c,d){b=b||1;c=new I(void 0!==c?c:4473924);d=new I(void 0!== -d?d:8947848);for(var e=b/2,f=2*a/b,g=[],k=[],l=0,n=0,h=-a;l<=b;l++,h+=f){g.push(-a,0,h,a,0,h);g.push(h,0,-a,h,0,a);var p=l===e?c:d;p.toArray(k,n);n+=3;p.toArray(k,n);n+=3;p.toArray(k,n);n+=3;p.toArray(k,n);n+=3}a=new H;a.addAttribute("position",new ka(g,3));a.addAttribute("color",new ka(k,3));g=new ga({vertexColors:2});ba.call(this,a,g)}function Hc(a,b,c,d){this.object=a;this.size=void 0!==b?b:1;a=void 0!==c?c:16776960;d=void 0!==d?d:1;b=0;(c=this.object.geometry)&&c.isGeometry?b=c.faces.length:console.warn("THREE.FaceNormalsHelper: only THREE.Geometry is supported. Use THREE.VertexNormalsHelper, instead."); -c=new H;b=new ka(6*b,3);c.addAttribute("position",b);ba.call(this,c,new ga({color:a,linewidth:d}));this.matrixAutoUpdate=!1;this.update()}function jd(a,b,c){b=void 0!==b?b:16777215;ba.call(this,new Sb(a.geometry,c),new ga({color:b}));this.matrix=a.matrixWorld;this.matrixAutoUpdate=!1}function cc(a,b){D.call(this);this.light=a;this.light.updateMatrixWorld();this.matrix=a.matrixWorld;this.matrixAutoUpdate=!1;void 0===b&&(b=1);var c=new H;c.addAttribute("position",new ka([-b,b,0,b,b,0,b,-b,0,-b,-b,0, --b,b,0],3));var d=new ga({fog:!1});this.add(new Qa(c,d));c=new H;c.addAttribute("position",new ka([0,0,0,0,0,1],3));this.add(new Qa(c,d));this.update()}function Ic(a){function b(a,b,d){c(a,d);c(b,d)}function c(a,b){d.vertices.push(new q);d.colors.push(new I(b));void 0===f[a]&&(f[a]=[]);f[a].push(d.vertices.length-1)}var d=new S,e=new ga({color:16777215,vertexColors:1}),f={};b("n1","n2",16755200);b("n2","n4",16755200);b("n4","n3",16755200);b("n3","n1",16755200);b("f1","f2",16755200);b("f2","f4",16755200); +fa.get(a),c=b?c.__webglFramebuffer[a.activeCubeFace]:c.__webglFramebuffer,Y.copy(a.scissor),cb=a.scissorTest,Wa.copy(a.viewport)):(c=null,Y.copy(ha).multiplyScalar(Pa),cb=ka,Wa.copy(ga).multiplyScalar(Pa));W!==c&&(B.bindFramebuffer(B.FRAMEBUFFER,c),W=c);X.scissor(Y);X.setScissorTest(cb);X.viewport(Wa);b&&(b=fa.get(a.texture),B.framebufferTexture2D(B.FRAMEBUFFER,B.COLOR_ATTACHMENT0,B.TEXTURE_CUBE_MAP_POSITIVE_X+a.activeCubeFace,b.__webglTexture,a.activeMipMapLevel))};this.readRenderTargetPixels=function(a, +b,c,d,e,f){if(!1===(a&&a.isWebGLRenderTarget))console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.");else{var g=fa.get(a).__webglFramebuffer;if(g){var k=!1;g!==W&&(B.bindFramebuffer(B.FRAMEBUFFER,g),k=!0);try{var l=a.texture,n=l.format,h=l.type;1023!==n&&w(n)!==B.getParameter(B.IMPLEMENTATION_COLOR_READ_FORMAT)?console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format."):1009===h||w(h)=== +B.getParameter(B.IMPLEMENTATION_COLOR_READ_TYPE)||1015===h&&(ia.get("OES_texture_float")||ia.get("WEBGL_color_buffer_float"))||1016===h&&ia.get("EXT_color_buffer_half_float")?B.checkFramebufferStatus(B.FRAMEBUFFER)===B.FRAMEBUFFER_COMPLETE?0<=b&&b<=a.width-d&&0<=c&&c<=a.height-e&&B.readPixels(b,c,d,e,w(n),w(h),f):console.error("THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete."):console.error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.")}finally{k&& +B.bindFramebuffer(B.FRAMEBUFFER,W)}}}}}function Fb(a,b){this.name="";this.color=new I(a);this.density=void 0!==b?b:2.5E-4}function Gb(a,b,c){this.name="";this.color=new I(a);this.near=void 0!==b?b:1;this.far=void 0!==c?c:1E3}function gb(){D.call(this);this.type="Scene";this.overrideMaterial=this.fog=this.background=null;this.autoUpdate=!0}function sd(a,b,c,d,e){D.call(this);this.lensFlares=[];this.positionScreen=new q;this.customUpdateCallback=void 0;void 0!==a&&this.add(a,b,c,d,e)}function hb(a){S.call(this); +this.type="SpriteMaterial";this.color=new I(16777215);this.map=null;this.rotation=0;this.lights=this.fog=!1;this.setValues(a)}function gc(a){D.call(this);this.type="Sprite";this.material=void 0!==a?a:new hb}function hc(){D.call(this);this.type="LOD";Object.defineProperties(this,{levels:{enumerable:!0,value:[]}})}function ib(a,b,c,d,e,f,g,k,l,n,h,p){Z.call(this,null,f,g,k,l,n,d,e,h,p);this.image={data:a,width:b,height:c};this.magFilter=void 0!==l?l:1003;this.minFilter=void 0!==n?n:1003;this.generateMipmaps= +this.flipY=!1}function Nc(a,b,c){this.useVertexTexture=void 0!==c?c:!0;this.identityMatrix=new P;a=a||[];this.bones=a.slice(0);this.useVertexTexture?(a=Math.sqrt(4*this.bones.length),a=h.Math.nextPowerOfTwo(Math.ceil(a)),this.boneTextureHeight=this.boneTextureWidth=a=Math.max(a,4),this.boneMatrices=new Float32Array(this.boneTextureWidth*this.boneTextureHeight*4),this.boneTexture=new ib(this.boneMatrices,this.boneTextureWidth,this.boneTextureHeight,1023,1015)):this.boneMatrices=new Float32Array(16* +this.bones.length);if(void 0===b)this.calculateInverses();else if(this.bones.length===b.length)this.boneInverses=b.slice(0);else for(console.warn("THREE.Skeleton bonInverses is the wrong length."),this.boneInverses=[],b=0,a=this.bones.length;b=a.HAVE_CURRENT_DATA&&(h.needsUpdate=!0)}Z.call(this,a,b,c,d,e,f,g,k,l);this.generateMipmaps= +!1;var h=this;n()}function Ib(a,b,c,d,e,f,g,k,l,n,h,p){Z.call(this,null,f,g,k,l,n,d,e,h,p);this.image={width:b,height:c};this.mipmaps=a;this.generateMipmaps=this.flipY=!1}function Rc(a,b,c,d,e,f,g,k,l){Z.call(this,a,b,c,d,e,f,g,k,l);this.needsUpdate=!0}function jc(a,b,c,d,e,f,g,k,l,n){n=void 0!==n?n:1026;if(1026!==n&&1027!==n)throw Error("DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat");Z.call(this,null,d,e,f,g,k,n,c,l);this.image={width:a,height:b};this.type=void 0!== +c?c:1012;this.magFilter=void 0!==g?g:1003;this.minFilter=void 0!==k?k:1003;this.generateMipmaps=this.flipY=!1}function Jb(a){function b(a,b){return a-b}H.call(this);var c=[0,0],d={},e=["a","b","c"];if(a&&a.isGeometry){var f=a.vertices,g=a.faces,k=0,l=new Uint32Array(6*g.length);a=0;for(var n=g.length;ap;p++){c[0]=h[e[p]];c[1]=h[e[(p+1)%3]];c.sort(b);var m=c.toString();void 0===d[m]&&(l[2*k]=c[0],l[2*k+1]=c[1],d[m]=!0,k++)}c=new Float32Array(6*k);a=0;for(n=k;ap;p++)d=f[l[2*a+p]],k=6*a+3*p,c[k+0]=d.x,c[k+1]=d.y,c[k+2]=d.z;this.addAttribute("position",new y(c,3))}else if(a&&a.isBufferGeometry){if(null!==a.index){n=a.index.array;f=a.attributes.position;e=a.groups;k=0;0===e.length&&a.addGroup(0,n.length);l=new Uint32Array(2*n.length);g=0;for(h=e.length;gp;p++)c[0]=n[a+p],c[1]=n[a+(p+1)%3],c.sort(b),m=c.toString(),void 0===d[m]&&(l[2*k]=c[0],l[2*k+1]=c[1],d[m]=!0,k++)}c=new Float32Array(6* +k);a=0;for(n=k;ap;p++)k=6*a+3*p,d=l[2*a+p],c[k+0]=f.getX(d),c[k+1]=f.getY(d),c[k+2]=f.getZ(d)}else for(f=a.attributes.position.array,k=f.length/3,l=k/3,c=new Float32Array(6*k),a=0,n=l;ap;p++)k=18*a+6*p,l=9*a+3*p,c[k+0]=f[l],c[k+1]=f[l+1],c[k+2]=f[l+2],d=9*a+(p+1)%3*3,c[k+3]=f[d],c[k+4]=f[d+1],c[k+5]=f[d+2];this.addAttribute("position",new y(c,3))}}function kc(a,b,c){R.call(this);this.type="ParametricGeometry";this.parameters={func:a,slices:b,stacks:c};var d=this.vertices, +e=this.faces,f=this.faceVertexUvs[0],g,k,l,n,h=b+1;for(g=0;g<=c;g++)for(n=g/c,k=0;k<=b;k++)l=k/b,l=a(l,n),d.push(l);var p,m,t,u;for(g=0;gc&&1===a.x&&(a=new C(a.x-1,a.y));0===b.x&&0===b.z&&(a=new C(c/2/Math.PI+.5,a.y));return a.clone()}R.call(this);this.type="PolyhedronGeometry";this.parameters={vertices:a,indices:b,radius:c,detail:d};c=c||1;d=d||0;for(var l=this,n= +0,h=a.length;nm&&(.2>d&&(b[0].x+=1),.2>a&&(b[1].x+=1),.2>p&&(b[2].x+=1));n=0;for(h=this.vertices.length;n< +h;n++)this.vertices[n].multiplyScalar(c);this.mergeVertices();this.computeFaceNormals();this.boundingSphere=new Aa(new q,c)}function lc(a,b){sa.call(this,[1,1,1,-1,-1,1,-1,1,-1,1,-1,-1],[2,1,0,0,3,2,1,3,0,2,3,1],a,b);this.type="TetrahedronGeometry";this.parameters={radius:a,detail:b}}function mc(a,b){sa.call(this,[1,0,0,-1,0,0,0,1,0,0,-1,0,0,0,1,0,0,-1],[0,2,4,0,4,3,0,3,5,0,5,2,1,2,5,1,5,3,1,3,4,1,4,2],a,b);this.type="OctahedronGeometry";this.parameters={radius:a,detail:b}}function nc(a,b){var c= +(1+Math.sqrt(5))/2;sa.call(this,[-1,c,0,1,c,0,-1,-c,0,1,-c,0,0,-1,c,0,1,c,0,-1,-c,0,1,-c,c,0,-1,c,0,1,-c,0,-1,-c,0,1],[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],a,b);this.type="IcosahedronGeometry";this.parameters={radius:a,detail:b}}function oc(a,b){var c=(1+Math.sqrt(5))/2,d=1/c;sa.call(this,[-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,-d,-c,0,-d,c,0,d,-c,0,d,c,-d,-c,0,-d,c,0,d,-c,0,d,c,0, +-c,0,-d,c,0,-d,-c,0,d,c,0,d],[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],a,b);this.type="DodecahedronGeometry";this.parameters={radius:a,detail:b}}function wa(a,b,c,d,e,f){R.call(this);this.type="TubeGeometry";this.parameters={path:a,segments:b,radius:c,radialSegments:d,closed:e, +taper:f};b=b||64;c=c||1;d=d||8;e=e||!1;f=f||wa.NoTaper;var g=[],k,l,n=b+1,h,p,m,t,u,v=new q,A,w,x;A=new wa.FrenetFrames(a,b,e);w=A.normals;x=A.binormals;this.tangents=A.tangents;this.normals=w;this.binormals=x;for(A=0;Am;m++){e[0]=p[g[m]];e[1]=p[g[(m+1)%3]];e.sort(c);var t=e.toString();void 0===f[t]?f[t]={vert1:e[0],vert2:e[1],face1:n,face2:void 0}:f[t].face2=n}e=[];for(t in f)if(g=f[t],void 0===g.face2||k[g.face1].normal.dot(k[g.face2].normal)<=d)n=l[g.vert1],e.push(n.x),e.push(n.y),e.push(n.z),n=l[g.vert2],e.push(n.x),e.push(n.y),e.push(n.z);this.addAttribute("position",new y(new Float32Array(e),3))}function $a(a,b,c,d,e,f,g,k){function l(c){var e, +f,l,h=new C,m=new q,p=0,r=!0===c?a:b,T=!0===c?1:-1;f=w;for(e=1;e<=d;e++)u.setXYZ(w,0,z*T,0),v.setXYZ(w,0,T,0),h.x=.5,h.y=.5,A.setXY(w,h.x,h.y),w++;l=w;for(e=0;e<=d;e++){var D=e/d*k+g,y=Math.cos(D),D=Math.sin(D);m.x=r*D;m.y=z*T;m.z=r*y;u.setXYZ(w,m.x,m.y,m.z);v.setXYZ(w,0,T,0);h.x=.5*y+.5;h.y=.5*D*T+.5;A.setXY(w,h.x,h.y);w++}for(e=0;ethis.duration&&this.resetDuration();this.optimize()}function fd(a){this.manager=void 0!==a?a:h.DefaultLoadingManager;this.textures={}}function wd(a){this.manager=void 0!==a?a:h.DefaultLoadingManager}function tb(){this.onLoadStart=function(){};this.onLoadProgress= +function(){};this.onLoadComplete=function(){}}function xd(a){"boolean"===typeof a&&(console.warn("THREE.JSONLoader: showStatus parameter has been removed from constructor."),a=void 0);this.manager=void 0!==a?a:h.DefaultLoadingManager;this.withCredentials=!1}function le(a){this.manager=void 0!==a?a:h.DefaultLoadingManager;this.texturePath=""}function qa(){}function Ma(a,b){this.v1=a;this.v2=b}function Cc(){this.curves=[];this.autoClose=!1}function Sa(a,b,c,d,e,f,g,k){this.aX=a;this.aY=b;this.xRadius= +c;this.yRadius=d;this.aStartAngle=e;this.aEndAngle=f;this.aClockwise=g;this.aRotation=k||0}function ub(a){this.points=void 0===a?[]:a}function vb(a,b,c,d){this.v0=a;this.v1=b;this.v2=c;this.v3=d}function wb(a,b,c){this.v0=a;this.v1=b;this.v2=c}function xb(){Dc.apply(this,arguments);this.holes=[]}function Dc(a){Cc.call(this);this.currentPoint=new C;a&&this.fromPoints(a)}function yd(){this.subPaths=[];this.currentPath=null}function zd(a){this.data=a}function me(a){this.manager=void 0!==a?a:h.DefaultLoadingManager} +function Ad(){void 0===Bd&&(Bd=new (window.AudioContext||window.webkitAudioContext));return Bd}function Cd(a){this.manager=void 0!==a?a:h.DefaultLoadingManager}function ne(){this.type="StereoCamera";this.aspect=1;this.eyeSep=.064;this.cameraL=new Ca;this.cameraL.layers.enable(1);this.cameraL.matrixAutoUpdate=!1;this.cameraR=new Ca;this.cameraR.layers.enable(2);this.cameraR.matrixAutoUpdate=!1}function gd(a,b,c){D.call(this);this.type="CubeCamera";var d=new Ca(90,1,a,b);d.up.set(0,-1,0);d.lookAt(new q(1, +0,0));this.add(d);var e=new Ca(90,1,a,b);e.up.set(0,-1,0);e.lookAt(new q(-1,0,0));this.add(e);var f=new Ca(90,1,a,b);f.up.set(0,0,1);f.lookAt(new q(0,1,0));this.add(f);var g=new Ca(90,1,a,b);g.up.set(0,0,-1);g.lookAt(new q(0,-1,0));this.add(g);var k=new Ca(90,1,a,b);k.up.set(0,-1,0);k.lookAt(new q(0,0,1));this.add(k);var l=new Ca(90,1,a,b);l.up.set(0,-1,0);l.lookAt(new q(0,0,-1));this.add(l);this.renderTarget=new Bb(c,c,{format:1022,magFilter:1006,minFilter:1006});this.updateCubeMap=function(a,b){null=== +this.parent&&this.updateMatrixWorld();var c=this.renderTarget,h=c.texture.generateMipmaps;c.texture.generateMipmaps=!1;c.activeCubeFace=0;a.render(b,d,c);c.activeCubeFace=1;a.render(b,e,c);c.activeCubeFace=2;a.render(b,f,c);c.activeCubeFace=3;a.render(b,g,c);c.activeCubeFace=4;a.render(b,k,c);c.texture.generateMipmaps=h;c.activeCubeFace=5;a.render(b,l,c);a.setRenderTarget(null)}}function Dd(){D.call(this);this.type="AudioListener";this.context=Ad();this.gain=this.context.createGain();this.gain.connect(this.context.destination); +this.filter=null}function Vb(a){D.call(this);this.type="Audio";this.context=a.context;this.source=this.context.createBufferSource();this.source.onended=this.onEnded.bind(this);this.gain=this.context.createGain();this.gain.connect(a.getInput());this.autoplay=!1;this.startTime=0;this.playbackRate=1;this.isPlaying=!1;this.hasPlaybackControl=!0;this.sourceType="empty";this.filters=[]}function Ed(a){Vb.call(this,a);this.panner=this.context.createPanner();this.panner.connect(this.gain)}function Fd(a,b){this.analyser= +a.context.createAnalyser();this.analyser.fftSize=void 0!==b?b:2048;this.data=new Uint8Array(this.analyser.frequencyBinCount);a.getOutput().connect(this.analyser)}function hd(a,b,c){this.binding=a;this.valueSize=c;a=Float64Array;switch(b){case "quaternion":b=this._slerp;break;case "string":case "bool":a=Array;b=this._select;break;default:b=this._lerp}this.buffer=new a(4*c);this._mixBufferRegion=b;this.referenceCount=this.useCount=this.cumulativeWeight=0}function ha(a,b,c){this.path=b;this.parsedPath= +c||ha.parseTrackName(b);this.node=ha.findNode(a,this.parsedPath.nodeName)||a;this.rootNode=a}function Gd(a){this.uuid=h.Math.generateUUID();this._objects=Array.prototype.slice.call(arguments);this.nCachedObjects_=0;var b={};this._indicesByUUID=b;for(var c=0,d=arguments.length;c!==d;++c)b[arguments[c].uuid]=c;this._paths=[];this._parsedPaths=[];this._bindings=[];this._bindingsIndicesByPath={};var e=this;this.stats={objects:{get total(){return e._objects.length},get inUse(){return this.total-e.nCachedObjects_}}, +get bindingsPerObject(){return e._bindings.length}}}function Hd(a,b,c){this._mixer=a;this._clip=b;this._localRoot=c||null;a=b.tracks;b=a.length;c=Array(b);for(var d={endingStart:2400,endingEnd:2400},e=0;e!==b;++e){var f=a[e].createInterpolant(null);c[e]=f;f.settings=d}this._interpolantSettings=d;this._interpolants=c;this._propertyBindings=Array(b);this._weightInterpolant=this._timeScaleInterpolant=this._byClipCacheIndex=this._cacheIndex=null;this.loop=2201;this._loopCount=-1;this._startTime=null; +this.time=0;this._effectiveWeight=this.weight=this._effectiveTimeScale=this.timeScale=1;this.repetitions=Infinity;this.paused=!1;this.enabled=!0;this.clampWhenFinished=!1;this.zeroSlopeAtEnd=this.zeroSlopeAtStart=!0}function Id(a){this._root=a;this._initMemoryManager();this.time=this._accuIndex=0;this.timeScale=1}function Jd(a,b){"string"===typeof a&&(console.warn("THREE.Uniform: Type parameter is no longer needed."),a=b);this.value=a;this.dynamic=!1}function yb(){H.call(this);this.type="InstancedBufferGeometry"; +this.maxInstancedCount=void 0}function Kd(a,b,c,d){this.uuid=h.Math.generateUUID();this.data=a;this.itemSize=b;this.offset=c;this.normalized=!0===d}function Wb(a,b){this.uuid=h.Math.generateUUID();this.array=a;this.stride=b;this.count=a.length/b;this.dynamic=!1;this.updateRange={offset:0,count:-1};this.version=0}function Xb(a,b,c){Wb.call(this,a,b);this.meshPerAttribute=c||1}function Yb(a,b,c){y.call(this,a,b);this.meshPerAttribute=c||1}function Ld(a,b,c,d){this.ray=new Xa(a,b);this.near=c||0;this.far= +d||Infinity;this.params={Mesh:{},Line:{},LOD:{},Points:{threshold:1},Sprite:{}};Object.defineProperties(this.params,{PointCloud:{get:function(){console.warn("THREE.Raycaster: params.PointCloud has been renamed to params.Points.");return this.Points}}})}function oe(a,b){return a.distance-b.distance}function Md(a,b,c,d){if(!1!==a.visible&&(a.raycast(b,c),!0===d)){a=a.children;d=0;for(var e=a.length;dc;c++,d++){var e=c/32*Math.PI*2,f=d/32*Math.PI*2;b.push(Math.cos(e),Math.sin(e),1,Math.cos(f),Math.sin(f),1)}a.addAttribute("position",new la(b,3));b=new ga({fog:!1});this.cone=new ba(a,b);this.add(this.cone);this.update()}function $b(a){this.bones=this.getBoneList(a);for(var b=new R,c=0;cd;d++)c.faces[d].color=this.colors[4>d?0:1];d=new Ja({vertexColors:1,wireframe:!0});this.lightSphere=new va(c,d);this.add(this.lightSphere);this.update()}function Gc(a,b,c,d){b=b||1;c=new I(void 0!==c?c:4473924);d=new I(void 0!==d?d:8947848);for(var e=b/2,f=2*a/b,g=[],k=[],l=0,n=0,h=-a;l<=b;l++, +h+=f){g.push(-a,0,h,a,0,h);g.push(h,0,-a,h,0,a);var p=l===e?c:d;p.toArray(k,n);n+=3;p.toArray(k,n);n+=3;p.toArray(k,n);n+=3;p.toArray(k,n);n+=3}a=new H;a.addAttribute("position",new la(g,3));a.addAttribute("color",new la(k,3));g=new ga({vertexColors:2});ba.call(this,a,g)}function Hc(a,b,c,d){this.object=a;this.size=void 0!==b?b:1;a=void 0!==c?c:16776960;d=void 0!==d?d:1;b=0;(c=this.object.geometry)&&c.isGeometry?b=c.faces.length:console.warn("THREE.FaceNormalsHelper: only THREE.Geometry is supported. Use THREE.VertexNormalsHelper, instead."); +c=new H;b=new la(6*b,3);c.addAttribute("position",b);ba.call(this,c,new ga({color:a,linewidth:d}));this.matrixAutoUpdate=!1;this.update()}function jd(a,b,c){b=void 0!==b?b:16777215;ba.call(this,new Pb(a.geometry,c),new ga({color:b}));this.matrix=a.matrixWorld;this.matrixAutoUpdate=!1}function cc(a,b){D.call(this);this.light=a;this.light.updateMatrixWorld();this.matrix=a.matrixWorld;this.matrixAutoUpdate=!1;void 0===b&&(b=1);var c=new H;c.addAttribute("position",new la([-b,b,0,b,b,0,b,-b,0,-b,-b,0, +-b,b,0],3));var d=new ga({fog:!1});this.add(new Qa(c,d));c=new H;c.addAttribute("position",new la([0,0,0,0,0,1],3));this.add(new Qa(c,d));this.update()}function Ic(a){function b(a,b,d){c(a,d);c(b,d)}function c(a,b){d.vertices.push(new q);d.colors.push(new I(b));void 0===f[a]&&(f[a]=[]);f[a].push(d.vertices.length-1)}var d=new R,e=new ga({color:16777215,vertexColors:1}),f={};b("n1","n2",16755200);b("n2","n4",16755200);b("n4","n3",16755200);b("n3","n1",16755200);b("f1","f2",16755200);b("f2","f4",16755200); b("f4","f3",16755200);b("f3","f1",16755200);b("n1","f1",16755200);b("n2","f2",16755200);b("n3","f3",16755200);b("n4","f4",16755200);b("p","n1",16711680);b("p","n2",16711680);b("p","n3",16711680);b("p","n4",16711680);b("u1","u2",43775);b("u2","u3",43775);b("u3","u1",43775);b("c","t",16777215);b("p","c",3355443);b("cn1","cn2",3355443);b("cn3","cn4",3355443);b("cf1","cf2",3355443);b("cf3","cf4",3355443);ba.call(this,d,e);this.camera=a;this.camera.updateProjectionMatrix&&this.camera.updateProjectionMatrix(); -this.matrix=a.matrixWorld;this.matrixAutoUpdate=!1;this.pointMap=f;this.update()}function Jc(a,b){var c=void 0!==b?b:8947848;this.object=a;this.box=new Ia;va.call(this,new sb(1,1,1),new Ja({color:c,wireframe:!0}))}function Kc(a,b){void 0===b&&(b=16776960);var c=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]),d=new Float32Array(24),e=new H;e.setIndex(new C(c,1));e.addAttribute("position",new C(d,3));ba.call(this,e,new ga({color:b}));void 0!==a&&this.update(a)}function xb(a,b,c,d, +this.matrix=a.matrixWorld;this.matrixAutoUpdate=!1;this.pointMap=f;this.update()}function Jc(a,b){var c=void 0!==b?b:8947848;this.object=a;this.box=new Ia;va.call(this,new lb(1,1,1),new Ja({color:c,wireframe:!0}))}function Kc(a,b){void 0===b&&(b=16776960);var c=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]),d=new Float32Array(24),e=new H;e.setIndex(new y(c,1));e.addAttribute("position",new y(d,3));ba.call(this,e,new ga({color:b}));void 0!==a&&this.update(a)}function zb(a,b,c,d, e,f){D.call(this);void 0===d&&(d=16776960);void 0===c&&(c=1);void 0===e&&(e=.2*c);void 0===f&&(f=.2*e);this.position.copy(b);this.line=new Qa(pe,new ga({color:d}));this.line.matrixAutoUpdate=!1;this.add(this.line);this.cone=new va(qe,new Ja({color:d}));this.cone.matrixAutoUpdate=!1;this.add(this.cone);this.setDirection(a);this.setLength(c,e,f)}function kd(a){a=a||1;var b=new Float32Array([0,0,0,a,0,0,0,0,0,0,a,0,0,0,0,0,0,a]),c=new Float32Array([1,0,0,1,.6,0,0,1,0,.6,1,0,0,0,1,0,.6,1]);a=new H;a.addAttribute("position", -new C(b,3));a.addAttribute("color",new C(c,3));b=new ga({vertexColors:2});ba.call(this,a,b)}function re(a){console.warn("THREE.ClosedSplineCurve3 has been deprecated. Please use THREE.CatmullRomCurve3.");h.CatmullRomCurve3.call(this,a);this.type="catmullrom";this.closed=!0}function ld(a,b,c,d,e,f){Sa.call(this,a,b,c,c,d,e,f)}void 0===Number.EPSILON&&(Number.EPSILON=Math.pow(2,-52));void 0===Math.sign&&(Math.sign=function(a){return 0>a?-1:0a?-1:0e;e++)8===e||13===e||18===e||23===e?b[e]="-":14===e?b[e]="4":(2>=c&&(c=33554432+16777216*Math.random()|0),d=c&15,c>>=4,b[e]=a[19===e?d& 3|8:d]);return b.join("")}}(),clamp:function(a,b,c){return Math.max(b,Math.min(c,a))},euclideanModulo:function(a,b){return(a%b+b)%b},mapLinear:function(a,b,c,d,e){return d+(a-b)*(e-d)/(c-b)},smoothstep:function(a,b,c){if(a<=b)return 0;if(a>=c)return 1;a=(a-b)/(c-b);return a*a*(3-2*a)},smootherstep:function(a,b,c){if(a<=b)return 0;if(a>=c)return 1;a=(a-b)/(c-b);return a*a*a*(a*(6*a-15)+10)},random16:function(){console.warn("THREE.Math.random16() has been deprecated. Use Math.random() instead.");return Math.random()}, -randInt:function(a,b){return a+Math.floor(Math.random()*(b-a+1))},randFloat:function(a,b){return a+Math.random()*(b-a)},randFloatSpread:function(a){return a*(.5-Math.random())},degToRad:function(a){return a*h.Math.DEG2RAD},radToDeg:function(a){return a*h.Math.RAD2DEG},isPowerOfTwo:function(a){return 0===(a&a-1)&&0!==a},nearestPowerOfTwo:function(a){return Math.pow(2,Math.round(Math.log(a)/Math.LN2))},nextPowerOfTwo:function(a){a--;a|=a>>1;a|=a>>2;a|=a>>4;a|=a>>8;a|=a>>16;a++;return a}};B.prototype= -{constructor:B,isVector2:!0,get width(){return this.x},set width(a){this.x=a},get height(){return this.y},set height(a){this.y=a},set:function(a,b){this.x=a;this.y=b;return this},setScalar:function(a){this.y=this.x=a;return this},setX:function(a){this.x=a;return this},setY:function(a){this.y=a;return this},setComponent:function(a,b){switch(a){case 0:this.x=b;break;case 1:this.y=b;break;default:throw Error("index is out of range: "+a);}},getComponent:function(a){switch(a){case 0:return this.x;case 1:return this.y; +randInt:function(a,b){return a+Math.floor(Math.random()*(b-a+1))},randFloat:function(a,b){return a+Math.random()*(b-a)},randFloatSpread:function(a){return a*(.5-Math.random())},degToRad:function(a){return a*h.Math.DEG2RAD},radToDeg:function(a){return a*h.Math.RAD2DEG},isPowerOfTwo:function(a){return 0===(a&a-1)&&0!==a},nearestPowerOfTwo:function(a){return Math.pow(2,Math.round(Math.log(a)/Math.LN2))},nextPowerOfTwo:function(a){a--;a|=a>>1;a|=a>>2;a|=a>>4;a|=a>>8;a|=a>>16;a++;return a}};C.prototype= +{constructor:C,isVector2:!0,get width(){return this.x},set width(a){this.x=a},get height(){return this.y},set height(a){this.y=a},set:function(a,b){this.x=a;this.y=b;return this},setScalar:function(a){this.y=this.x=a;return this},setX:function(a){this.x=a;return this},setY:function(a){this.y=a;return this},setComponent:function(a,b){switch(a){case 0:this.x=b;break;case 1:this.y=b;break;default:throw Error("index is out of range: "+a);}},getComponent:function(a){switch(a){case 0:return this.x;case 1:return this.y; default:throw Error("index is out of range: "+a);}},clone:function(){return new this.constructor(this.x,this.y)},copy:function(a){this.x=a.x;this.y=a.y;return this},add:function(a,b){if(void 0!==b)return console.warn("THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead."),this.addVectors(a,b);this.x+=a.x;this.y+=a.y;return this},addScalar:function(a){this.x+=a;this.y+=a;return this},addVectors:function(a,b){this.x=a.x+b.x;this.y=a.y+b.y;return this},addScaledVector:function(a, b){this.x+=a.x*b;this.y+=a.y*b;return this},sub:function(a,b){if(void 0!==b)return console.warn("THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead."),this.subVectors(a,b);this.x-=a.x;this.y-=a.y;return this},subScalar:function(a){this.x-=a;this.y-=a;return this},subVectors:function(a,b){this.x=a.x-b.x;this.y=a.y-b.y;return this},multiply:function(a){this.x*=a.x;this.y*=a.y;return this},multiplyScalar:function(a){isFinite(a)?(this.x*=a,this.y*=a):this.y=this.x=0; -return this},divide:function(a){this.x/=a.x;this.y/=a.y;return this},divideScalar:function(a){return this.multiplyScalar(1/a)},min:function(a){this.x=Math.min(this.x,a.x);this.y=Math.min(this.y,a.y);return this},max:function(a){this.x=Math.max(this.x,a.x);this.y=Math.max(this.y,a.y);return this},clamp:function(a,b){this.x=Math.max(a.x,Math.min(b.x,this.x));this.y=Math.max(a.y,Math.min(b.y,this.y));return this},clampScalar:function(){var a,b;return function(c,d){void 0===a&&(a=new B,b=new B);a.set(c, +return this},divide:function(a){this.x/=a.x;this.y/=a.y;return this},divideScalar:function(a){return this.multiplyScalar(1/a)},min:function(a){this.x=Math.min(this.x,a.x);this.y=Math.min(this.y,a.y);return this},max:function(a){this.x=Math.max(this.x,a.x);this.y=Math.max(this.y,a.y);return this},clamp:function(a,b){this.x=Math.max(a.x,Math.min(b.x,this.x));this.y=Math.max(a.y,Math.min(b.y,this.y));return this},clampScalar:function(){var a,b;return function(c,d){void 0===a&&(a=new C,b=new C);a.set(c, c);b.set(d,d);return this.clamp(a,b)}}(),clampLength:function(a,b){var c=this.length();return this.multiplyScalar(Math.max(a,Math.min(b,c))/c)},floor:function(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);return this},ceil:function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);return this},round:function(){this.x=Math.round(this.x);this.y=Math.round(this.y);return this},roundToZero:function(){this.x=0>this.x?Math.ceil(this.x):Math.floor(this.x);this.y=0>this.y?Math.ceil(this.y):Math.floor(this.y); return this},negate:function(){this.x=-this.x;this.y=-this.y;return this},dot:function(a){return this.x*a.x+this.y*a.y},lengthSq:function(){return this.x*this.x+this.y*this.y},length:function(){return Math.sqrt(this.x*this.x+this.y*this.y)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)},normalize:function(){return this.divideScalar(this.length())},angle:function(){var a=Math.atan2(this.y,this.x);0>a&&(a+=2*Math.PI);return a},distanceTo:function(a){return Math.sqrt(this.distanceToSquared(a))}, distanceToSquared:function(a){var b=this.x-a.x;a=this.y-a.y;return b*b+a*a},distanceToManhattan:function(a){return Math.abs(this.x-a.x)+Math.abs(this.y-a.y)},setLength:function(a){return this.multiplyScalar(a/this.length())},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+=(a.y-this.y)*b;return this},lerpVectors:function(a,b,c){return this.subVectors(b,a).multiplyScalar(c).add(a)},equals:function(a){return a.x===this.x&&a.y===this.y},fromArray:function(a,b){void 0===b&&(b=0);this.x=a[b];this.y=a[b+ @@ -287,9 +287,9 @@ Math.min(this.x,a.x);this.y=Math.min(this.y,a.y);this.z=Math.min(this.z,a.z);thi d){void 0===a&&(a=new da,b=new da);a.set(c,c,c,c);b.set(d,d,d,d);return this.clamp(a,b)}}(),floor:function(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);this.z=Math.floor(this.z);this.w=Math.floor(this.w);return this},ceil:function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);this.z=Math.ceil(this.z);this.w=Math.ceil(this.w);return this},round:function(){this.x=Math.round(this.x);this.y=Math.round(this.y);this.z=Math.round(this.z);this.w=Math.round(this.w);return this},roundToZero:function(){this.x= 0>this.x?Math.ceil(this.x):Math.floor(this.x);this.y=0>this.y?Math.ceil(this.y):Math.floor(this.y);this.z=0>this.z?Math.ceil(this.z):Math.floor(this.z);this.w=0>this.w?Math.ceil(this.w):Math.floor(this.w);return this},negate:function(){this.x=-this.x;this.y=-this.y;this.z=-this.z;this.w=-this.w;return this},dot:function(a){return this.x*a.x+this.y*a.y+this.z*a.z+this.w*a.w},lengthSq:function(){return this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w},length:function(){return Math.sqrt(this.x* this.x+this.y*this.y+this.z*this.z+this.w*this.w)},lengthManhattan:function(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)+Math.abs(this.w)},normalize:function(){return this.divideScalar(this.length())},setLength:function(a){return this.multiplyScalar(a/this.length())},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+=(a.y-this.y)*b;this.z+=(a.z-this.z)*b;this.w+=(a.w-this.w)*b;return this},lerpVectors:function(a,b,c){return this.subVectors(b,a).multiplyScalar(c).add(a)},equals:function(a){return a.x=== -this.x&&a.y===this.y&&a.z===this.z&&a.w===this.w},fromArray:function(a,b){void 0===b&&(b=0);this.x=a[b];this.y=a[b+1];this.z=a[b+2];this.w=a[b+3];return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);a[b]=this.x;a[b+1]=this.y;a[b+2]=this.z;a[b+3]=this.w;return a},fromAttribute:function(a,b,c){void 0===c&&(c=0);b=b*a.itemSize+c;this.x=a.array[b];this.y=a.array[b+1];this.z=a.array[b+2];this.w=a.array[b+3];return this}};Object.assign(yb.prototype,na.prototype,{isWebGLRenderTarget:!0, +this.x&&a.y===this.y&&a.z===this.z&&a.w===this.w},fromArray:function(a,b){void 0===b&&(b=0);this.x=a[b];this.y=a[b+1];this.z=a[b+2];this.w=a[b+3];return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);a[b]=this.x;a[b+1]=this.y;a[b+2]=this.z;a[b+3]=this.w;return a},fromAttribute:function(a,b,c){void 0===c&&(c=0);b=b*a.itemSize+c;this.x=a.array[b];this.y=a.array[b+1];this.z=a.array[b+2];this.w=a.array[b+3];return this}};Object.assign(Ab.prototype,na.prototype,{isWebGLRenderTarget:!0, setSize:function(a,b){if(this.width!==a||this.height!==b)this.width=a,this.height=b,this.dispose();this.viewport.set(0,0,a,b);this.scissor.set(0,0,a,b)},clone:function(){return(new this.constructor).copy(this)},copy:function(a){this.width=a.width;this.height=a.height;this.viewport.copy(a.viewport);this.texture=a.texture.clone();this.depthBuffer=a.depthBuffer;this.stencilBuffer=a.stencilBuffer;this.depthTexture=a.depthTexture;return this},dispose:function(){this.dispatchEvent({type:"dispose"})}}); -zb.prototype=Object.create(yb.prototype);zb.prototype.constructor=zb;zb.prototype.isWebGLRenderTargetCube=!0;oa.prototype={constructor:oa,get x(){return this._x},set x(a){this._x=a;this.onChangeCallback()},get y(){return this._y},set y(a){this._y=a;this.onChangeCallback()},get z(){return this._z},set z(a){this._z=a;this.onChangeCallback()},get w(){return this._w},set w(a){this._w=a;this.onChangeCallback()},set:function(a,b,c,d){this._x=a;this._y=b;this._z=c;this._w=d;this.onChangeCallback();return this}, +Bb.prototype=Object.create(Ab.prototype);Bb.prototype.constructor=Bb;Bb.prototype.isWebGLRenderTargetCube=!0;oa.prototype={constructor:oa,get x(){return this._x},set x(a){this._x=a;this.onChangeCallback()},get y(){return this._y},set y(a){this._y=a;this.onChangeCallback()},get z(){return this._z},set z(a){this._z=a;this.onChangeCallback()},get w(){return this._w},set w(a){this._w=a;this.onChangeCallback()},set:function(a,b,c,d){this._x=a;this._y=b;this._z=c;this._w=d;this.onChangeCallback();return this}, clone:function(){return new this.constructor(this._x,this._y,this._z,this._w)},copy:function(a){this._x=a.x;this._y=a.y;this._z=a.z;this._w=a.w;this.onChangeCallback();return this},setFromEuler:function(a,b){if(!1===(a&&a.isEuler))throw Error("THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.");var c=Math.cos(a._x/2),d=Math.cos(a._y/2),e=Math.cos(a._z/2),f=Math.sin(a._x/2),g=Math.sin(a._y/2),k=Math.sin(a._z/2),l=a.order;"XYZ"===l?(this._x=f*d*e+c*g*k, this._y=c*g*e-f*d*k,this._z=c*d*k+f*g*e,this._w=c*d*e-f*g*k):"YXZ"===l?(this._x=f*d*e+c*g*k,this._y=c*g*e-f*d*k,this._z=c*d*k-f*g*e,this._w=c*d*e+f*g*k):"ZXY"===l?(this._x=f*d*e-c*g*k,this._y=c*g*e+f*d*k,this._z=c*d*k+f*g*e,this._w=c*d*e-f*g*k):"ZYX"===l?(this._x=f*d*e-c*g*k,this._y=c*g*e+f*d*k,this._z=c*d*k-f*g*e,this._w=c*d*e+f*g*k):"YZX"===l?(this._x=f*d*e+c*g*k,this._y=c*g*e+f*d*k,this._z=c*d*k-f*g*e,this._w=c*d*e-f*g*k):"XZY"===l&&(this._x=f*d*e-c*g*k,this._y=c*g*e-f*d*k,this._z=c*d*k+f*g*e, this._w=c*d*e+f*g*k);if(!1!==b)this.onChangeCallback();return this},setFromAxisAngle:function(a,b){var c=b/2,d=Math.sin(c);this._x=a.x*d;this._y=a.y*d;this._z=a.z*d;this._w=Math.cos(c);this.onChangeCallback();return this},setFromRotationMatrix:function(a){var b=a.elements,c=b[0];a=b[4];var d=b[8],e=b[1],f=b[5],g=b[9],k=b[2],l=b[6],b=b[10],n=c+f+b;0f&&c>b?(c=2*Math.sqrt(1+c-f-b),this._w=(l-g)/c,this._x=.25*c,this._y= @@ -305,7 +305,7 @@ this.addVectors(a,b);this.x+=a.x;this.y+=a.y;this.z+=a.z;return this},addScalar: return this},subScalar:function(a){this.x-=a;this.y-=a;this.z-=a;return this},subVectors:function(a,b){this.x=a.x-b.x;this.y=a.y-b.y;this.z=a.z-b.z;return this},multiply:function(a,b){if(void 0!==b)return console.warn("THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead."),this.multiplyVectors(a,b);this.x*=a.x;this.y*=a.y;this.z*=a.z;return this},multiplyScalar:function(a){isFinite(a)?(this.x*=a,this.y*=a,this.z*=a):this.z=this.y=this.x=0;return this},multiplyVectors:function(a, b){this.x=a.x*b.x;this.y=a.y*b.y;this.z=a.z*b.z;return this},applyEuler:function(){var a;return function(b){!1===(b&&b.isEuler)&&console.error("THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.");void 0===a&&(a=new oa);return this.applyQuaternion(a.setFromEuler(b))}}(),applyAxisAngle:function(){var a;return function(b,c){void 0===a&&(a=new oa);return this.applyQuaternion(a.setFromAxisAngle(b,c))}}(),applyMatrix3:function(a){var b=this.x,c=this.y,d=this.z; a=a.elements;this.x=a[0]*b+a[3]*c+a[6]*d;this.y=a[1]*b+a[4]*c+a[7]*d;this.z=a[2]*b+a[5]*c+a[8]*d;return this},applyMatrix4:function(a){var b=this.x,c=this.y,d=this.z;a=a.elements;this.x=a[0]*b+a[4]*c+a[8]*d+a[12];this.y=a[1]*b+a[5]*c+a[9]*d+a[13];this.z=a[2]*b+a[6]*c+a[10]*d+a[14];return this},applyProjection:function(a){var b=this.x,c=this.y,d=this.z;a=a.elements;var e=1/(a[3]*b+a[7]*c+a[11]*d+a[15]);this.x=(a[0]*b+a[4]*c+a[8]*d+a[12])*e;this.y=(a[1]*b+a[5]*c+a[9]*d+a[13])*e;this.z=(a[2]*b+a[6]* -c+a[10]*d+a[14])*e;return this},applyQuaternion:function(a){var b=this.x,c=this.y,d=this.z,e=a.x,f=a.y,g=a.z;a=a.w;var k=a*b+f*d-g*c,l=a*c+g*b-e*d,n=a*d+e*c-f*b,b=-e*b-f*c-g*d;this.x=k*a+b*-e+l*-g-n*-f;this.y=l*a+b*-f+n*-e-k*-g;this.z=n*a+b*-g+k*-f-l*-e;return this},project:function(){var a;return function(b){void 0===a&&(a=new Q);a.multiplyMatrices(b.projectionMatrix,a.getInverse(b.matrixWorld));return this.applyProjection(a)}}(),unproject:function(){var a;return function(b){void 0===a&&(a=new Q); +c+a[10]*d+a[14])*e;return this},applyQuaternion:function(a){var b=this.x,c=this.y,d=this.z,e=a.x,f=a.y,g=a.z;a=a.w;var k=a*b+f*d-g*c,l=a*c+g*b-e*d,n=a*d+e*c-f*b,b=-e*b-f*c-g*d;this.x=k*a+b*-e+l*-g-n*-f;this.y=l*a+b*-f+n*-e-k*-g;this.z=n*a+b*-g+k*-f-l*-e;return this},project:function(){var a;return function(b){void 0===a&&(a=new P);a.multiplyMatrices(b.projectionMatrix,a.getInverse(b.matrixWorld));return this.applyProjection(a)}}(),unproject:function(){var a;return function(b){void 0===a&&(a=new P); a.multiplyMatrices(b.matrixWorld,a.getInverse(b.projectionMatrix));return this.applyProjection(a)}}(),transformDirection:function(a){var b=this.x,c=this.y,d=this.z;a=a.elements;this.x=a[0]*b+a[4]*c+a[8]*d;this.y=a[1]*b+a[5]*c+a[9]*d;this.z=a[2]*b+a[6]*c+a[10]*d;return this.normalize()},divide:function(a){this.x/=a.x;this.y/=a.y;this.z/=a.z;return this},divideScalar:function(a){return this.multiplyScalar(1/a)},min:function(a){this.x=Math.min(this.x,a.x);this.y=Math.min(this.y,a.y);this.z=Math.min(this.z, a.z);return this},max:function(a){this.x=Math.max(this.x,a.x);this.y=Math.max(this.y,a.y);this.z=Math.max(this.z,a.z);return this},clamp:function(a,b){this.x=Math.max(a.x,Math.min(b.x,this.x));this.y=Math.max(a.y,Math.min(b.y,this.y));this.z=Math.max(a.z,Math.min(b.z,this.z));return this},clampScalar:function(){var a,b;return function(c,d){void 0===a&&(a=new q,b=new q);a.set(c,c,c);b.set(d,d,d);return this.clamp(a,b)}}(),clampLength:function(a,b){var c=this.length();return this.multiplyScalar(Math.max(a, Math.min(b,c))/c)},floor:function(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);this.z=Math.floor(this.z);return this},ceil:function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);this.z=Math.ceil(this.z);return this},round:function(){this.x=Math.round(this.x);this.y=Math.round(this.y);this.z=Math.round(this.z);return this},roundToZero:function(){this.x=0>this.x?Math.ceil(this.x):Math.floor(this.x);this.y=0>this.y?Math.ceil(this.y):Math.floor(this.y);this.z=0>this.z?Math.ceil(this.z): @@ -314,23 +314,23 @@ this.length())},lerp:function(a,b){this.x+=(a.x-this.x)*b;this.y+=(a.y-this.y)*b a.x,d=a.y,e=a.z,f=b.x,g=b.y,k=b.z;this.x=d*k-e*g;this.y=e*f-c*k;this.z=c*g-d*f;return this},projectOnVector:function(a){var b=a.dot(this)/a.lengthSq();return this.copy(a).multiplyScalar(b)},projectOnPlane:function(){var a;return function(b){void 0===a&&(a=new q);a.copy(this).projectOnVector(b);return this.sub(a)}}(),reflect:function(){var a;return function(b){void 0===a&&(a=new q);return this.sub(a.copy(b).multiplyScalar(2*this.dot(b)))}}(),angleTo:function(a){a=this.dot(a)/Math.sqrt(this.lengthSq()* a.lengthSq());return Math.acos(h.Math.clamp(a,-1,1))},distanceTo:function(a){return Math.sqrt(this.distanceToSquared(a))},distanceToSquared:function(a){var b=this.x-a.x,c=this.y-a.y;a=this.z-a.z;return b*b+c*c+a*a},distanceToManhattan:function(a){return Math.abs(this.x-a.x)+Math.abs(this.y-a.y)+Math.abs(this.z-a.z)},setFromSpherical:function(a){var b=Math.sin(a.phi)*a.radius;this.x=b*Math.sin(a.theta);this.y=Math.cos(a.phi)*a.radius;this.z=b*Math.cos(a.theta);return this},setFromMatrixPosition:function(a){return this.setFromMatrixColumn(a, 3)},setFromMatrixScale:function(a){var b=this.setFromMatrixColumn(a,0).length(),c=this.setFromMatrixColumn(a,1).length();a=this.setFromMatrixColumn(a,2).length();this.x=b;this.y=c;this.z=a;return this},setFromMatrixColumn:function(a,b){if("number"===typeof a){console.warn("THREE.Vector3: setFromMatrixColumn now expects ( matrix, index ).");var c=a;a=b;b=c}return this.fromArray(a.elements,4*b)},equals:function(a){return a.x===this.x&&a.y===this.y&&a.z===this.z},fromArray:function(a,b){void 0===b&& -(b=0);this.x=a[b];this.y=a[b+1];this.z=a[b+2];return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);a[b]=this.x;a[b+1]=this.y;a[b+2]=this.z;return a},fromAttribute:function(a,b,c){void 0===c&&(c=0);b=b*a.itemSize+c;this.x=a.array[b];this.y=a.array[b+1];this.z=a.array[b+2];return this}};Q.prototype={constructor:Q,isMatrix4:!0,set:function(a,b,c,d,e,f,g,k,l,n,h,p,m,t,u,v){var q=this.elements;q[0]=a;q[4]=b;q[8]=c;q[12]=d;q[1]=e;q[5]=f;q[9]=g;q[13]=k;q[2]=l;q[6]=n;q[10]=h;q[14]=p;q[3]= -m;q[7]=t;q[11]=u;q[15]=v;return this},identity:function(){this.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1);return this},clone:function(){return(new Q).fromArray(this.elements)},copy:function(a){this.elements.set(a.elements);return this},copyPosition:function(a){var b=this.elements;a=a.elements;b[12]=a[12];b[13]=a[13];b[14]=a[14];return this},extractBasis:function(a,b,c){a.setFromMatrixColumn(this,0);b.setFromMatrixColumn(this,1);c.setFromMatrixColumn(this,2);return this},makeBasis:function(a,b,c){this.set(a.x, +(b=0);this.x=a[b];this.y=a[b+1];this.z=a[b+2];return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);a[b]=this.x;a[b+1]=this.y;a[b+2]=this.z;return a},fromAttribute:function(a,b,c){void 0===c&&(c=0);b=b*a.itemSize+c;this.x=a.array[b];this.y=a.array[b+1];this.z=a.array[b+2];return this}};P.prototype={constructor:P,isMatrix4:!0,set:function(a,b,c,d,e,f,g,k,l,n,h,p,m,t,u,v){var q=this.elements;q[0]=a;q[4]=b;q[8]=c;q[12]=d;q[1]=e;q[5]=f;q[9]=g;q[13]=k;q[2]=l;q[6]=n;q[10]=h;q[14]=p;q[3]= +m;q[7]=t;q[11]=u;q[15]=v;return this},identity:function(){this.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1);return this},clone:function(){return(new P).fromArray(this.elements)},copy:function(a){this.elements.set(a.elements);return this},copyPosition:function(a){var b=this.elements;a=a.elements;b[12]=a[12];b[13]=a[13];b[14]=a[14];return this},extractBasis:function(a,b,c){a.setFromMatrixColumn(this,0);b.setFromMatrixColumn(this,1);c.setFromMatrixColumn(this,2);return this},makeBasis:function(a,b,c){this.set(a.x, b.x,c.x,0,a.y,b.y,c.y,0,a.z,b.z,c.z,0,0,0,0,1);return this},extractRotation:function(){var a;return function(b){void 0===a&&(a=new q);var c=this.elements,d=b.elements,e=1/a.setFromMatrixColumn(b,0).length(),f=1/a.setFromMatrixColumn(b,1).length();b=1/a.setFromMatrixColumn(b,2).length();c[0]=d[0]*e;c[1]=d[1]*e;c[2]=d[2]*e;c[4]=d[4]*f;c[5]=d[5]*f;c[6]=d[6]*f;c[8]=d[8]*b;c[9]=d[9]*b;c[10]=d[10]*b;return this}}(),makeRotationFromEuler:function(a){!1===(a&&a.isEuler)&&console.error("THREE.Matrix: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order."); var b=this.elements,c=a.x,d=a.y,e=a.z,f=Math.cos(c),c=Math.sin(c),g=Math.cos(d),d=Math.sin(d),k=Math.cos(e),e=Math.sin(e);if("XYZ"===a.order){a=f*k;var l=f*e,h=c*k,r=c*e;b[0]=g*k;b[4]=-g*e;b[8]=d;b[1]=l+h*d;b[5]=a-r*d;b[9]=-c*g;b[2]=r-a*d;b[6]=h+l*d;b[10]=f*g}else"YXZ"===a.order?(a=g*k,l=g*e,h=d*k,r=d*e,b[0]=a+r*c,b[4]=h*c-l,b[8]=f*d,b[1]=f*e,b[5]=f*k,b[9]=-c,b[2]=l*c-h,b[6]=r+a*c,b[10]=f*g):"ZXY"===a.order?(a=g*k,l=g*e,h=d*k,r=d*e,b[0]=a-r*c,b[4]=-f*e,b[8]=h+l*c,b[1]=l+h*c,b[5]=f*k,b[9]=r-a*c,b[2]= -f*d,b[6]=c,b[10]=f*g):"ZYX"===a.order?(a=f*k,l=f*e,h=c*k,r=c*e,b[0]=g*k,b[4]=h*d-l,b[8]=a*d+r,b[1]=g*e,b[5]=r*d+a,b[9]=l*d-h,b[2]=-d,b[6]=c*g,b[10]=f*g):"YZX"===a.order?(a=f*g,l=f*d,h=c*g,r=c*d,b[0]=g*k,b[4]=r-a*e,b[8]=h*e+l,b[1]=e,b[5]=f*k,b[9]=-c*k,b[2]=-d*k,b[6]=l*e+h,b[10]=a-r*e):"XZY"===a.order&&(a=f*g,l=f*d,h=c*g,r=c*d,b[0]=g*k,b[4]=-e,b[8]=d*k,b[1]=a*e+r,b[5]=f*k,b[9]=l*e-h,b[2]=h*e-l,b[6]=c*k,b[10]=r*e+a);b[3]=0;b[7]=0;b[11]=0;b[12]=0;b[13]=0;b[14]=0;b[15]=1;return this},makeRotationFromQuaternion:function(a){var b= this.elements,c=a.x,d=a.y,e=a.z,f=a.w,g=c+c,k=d+d,l=e+e;a=c*g;var h=c*k,c=c*l,r=d*k,d=d*l,e=e*l,g=f*g,k=f*k,f=f*l;b[0]=1-(r+e);b[4]=h-f;b[8]=c+k;b[1]=h+f;b[5]=1-(a+e);b[9]=d-g;b[2]=c-k;b[6]=d+g;b[10]=1-(a+r);b[3]=0;b[7]=0;b[11]=0;b[12]=0;b[13]=0;b[14]=0;b[15]=1;return this},lookAt:function(){var a,b,c;return function(d,e,f){void 0===a&&(a=new q,b=new q,c=new q);var g=this.elements;c.subVectors(d,e).normalize();0===c.lengthSq()&&(c.z=1);a.crossVectors(f,c).normalize();0===a.lengthSq()&&(c.z+=1E-4, a.crossVectors(f,c).normalize());b.crossVectors(c,a);g[0]=a.x;g[4]=b.x;g[8]=c.x;g[1]=a.y;g[5]=b.y;g[9]=c.y;g[2]=a.z;g[6]=b.z;g[10]=c.z;return this}}(),multiply:function(a,b){return void 0!==b?(console.warn("THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead."),this.multiplyMatrices(a,b)):this.multiplyMatrices(this,a)},premultiply:function(a){return this.multiplyMatrices(a,this)},multiplyMatrices:function(a,b){var c=a.elements,d=b.elements,e=this.elements, -f=c[0],g=c[4],k=c[8],l=c[12],h=c[1],r=c[5],p=c[9],m=c[13],t=c[2],u=c[6],v=c[10],q=c[14],w=c[3],x=c[7],N=c[11],c=c[15],y=d[0],F=d[4],G=d[8],E=d[12],K=d[1],B=d[5],L=d[9],C=d[13],D=d[2],H=d[6],I=d[10],M=d[14],O=d[3],P=d[7],R=d[11],d=d[15];e[0]=f*y+g*K+k*D+l*O;e[4]=f*F+g*B+k*H+l*P;e[8]=f*G+g*L+k*I+l*R;e[12]=f*E+g*C+k*M+l*d;e[1]=h*y+r*K+p*D+m*O;e[5]=h*F+r*B+p*H+m*P;e[9]=h*G+r*L+p*I+m*R;e[13]=h*E+r*C+p*M+m*d;e[2]=t*y+u*K+v*D+q*O;e[6]=t*F+u*B+v*H+q*P;e[10]=t*G+u*L+v*I+q*R;e[14]=t*E+u*C+v*M+q*d;e[3]=w*y+ -x*K+N*D+c*O;e[7]=w*F+x*B+N*H+c*P;e[11]=w*G+x*L+N*I+c*R;e[15]=w*E+x*C+N*M+c*d;return this},multiplyToArray:function(a,b,c){var d=this.elements;this.multiplyMatrices(a,b);c[0]=d[0];c[1]=d[1];c[2]=d[2];c[3]=d[3];c[4]=d[4];c[5]=d[5];c[6]=d[6];c[7]=d[7];c[8]=d[8];c[9]=d[9];c[10]=d[10];c[11]=d[11];c[12]=d[12];c[13]=d[13];c[14]=d[14];c[15]=d[15];return this},multiplyScalar:function(a){var b=this.elements;b[0]*=a;b[4]*=a;b[8]*=a;b[12]*=a;b[1]*=a;b[5]*=a;b[9]*=a;b[13]*=a;b[2]*=a;b[6]*=a;b[10]*=a;b[14]*=a; +f=c[0],g=c[4],k=c[8],l=c[12],h=c[1],r=c[5],p=c[9],m=c[13],t=c[2],u=c[6],v=c[10],q=c[14],w=c[3],x=c[7],T=c[11],c=c[15],z=d[0],F=d[4],G=d[8],E=d[12],K=d[1],C=d[5],L=d[9],D=d[13],y=d[2],H=d[6],I=d[10],M=d[14],N=d[3],O=d[7],Q=d[11],d=d[15];e[0]=f*z+g*K+k*y+l*N;e[4]=f*F+g*C+k*H+l*O;e[8]=f*G+g*L+k*I+l*Q;e[12]=f*E+g*D+k*M+l*d;e[1]=h*z+r*K+p*y+m*N;e[5]=h*F+r*C+p*H+m*O;e[9]=h*G+r*L+p*I+m*Q;e[13]=h*E+r*D+p*M+m*d;e[2]=t*z+u*K+v*y+q*N;e[6]=t*F+u*C+v*H+q*O;e[10]=t*G+u*L+v*I+q*Q;e[14]=t*E+u*D+v*M+q*d;e[3]=w*z+ +x*K+T*y+c*N;e[7]=w*F+x*C+T*H+c*O;e[11]=w*G+x*L+T*I+c*Q;e[15]=w*E+x*D+T*M+c*d;return this},multiplyToArray:function(a,b,c){var d=this.elements;this.multiplyMatrices(a,b);c[0]=d[0];c[1]=d[1];c[2]=d[2];c[3]=d[3];c[4]=d[4];c[5]=d[5];c[6]=d[6];c[7]=d[7];c[8]=d[8];c[9]=d[9];c[10]=d[10];c[11]=d[11];c[12]=d[12];c[13]=d[13];c[14]=d[14];c[15]=d[15];return this},multiplyScalar:function(a){var b=this.elements;b[0]*=a;b[4]*=a;b[8]*=a;b[12]*=a;b[1]*=a;b[5]*=a;b[9]*=a;b[13]*=a;b[2]*=a;b[6]*=a;b[10]*=a;b[14]*=a; b[3]*=a;b[7]*=a;b[11]*=a;b[15]*=a;return this},applyToVector3Array:function(){var a;return function(b,c,d){void 0===a&&(a=new q);void 0===c&&(c=0);void 0===d&&(d=b.length);for(var e=0;ethis.determinant()&&(g= +g-d*k,l*k+d*g,0,l*g+d*k,h*g+c,h*k-d*f,0,l*k-d*g,h*k+d*f,e*k*k+c,0,0,0,0,1);return this},makeScale:function(a,b,c){this.set(a,0,0,0,0,b,0,0,0,0,c,0,0,0,0,1);return this},compose:function(a,b,c){this.makeRotationFromQuaternion(b);this.scale(c);this.setPosition(a);return this},decompose:function(){var a,b;return function(c,d,e){void 0===a&&(a=new q,b=new P);var f=this.elements,g=a.set(f[0],f[1],f[2]).length(),k=a.set(f[4],f[5],f[6]).length(),l=a.set(f[8],f[9],f[10]).length();0>this.determinant()&&(g= -g);c.x=f[12];c.y=f[13];c.z=f[14];b.elements.set(this.elements);c=1/g;var f=1/k,h=1/l;b.elements[0]*=c;b.elements[1]*=c;b.elements[2]*=c;b.elements[4]*=f;b.elements[5]*=f;b.elements[6]*=f;b.elements[8]*=h;b.elements[9]*=h;b.elements[10]*=h;d.setFromRotationMatrix(b);e.x=g;e.y=k;e.z=l;return this}}(),makeFrustum:function(a,b,c,d,e,f){var g=this.elements;g[0]=2*e/(b-a);g[4]=0;g[8]=(b+a)/(b-a);g[12]=0;g[1]=0;g[5]=2*e/(d-c);g[9]=(d+c)/(d-c);g[13]=0;g[2]=0;g[6]=0;g[10]=-(f+e)/(f-e);g[14]=-2*f*e/(f-e); g[3]=0;g[7]=0;g[11]=-1;g[15]=0;return this},makePerspective:function(a,b,c,d){a=c*Math.tan(h.Math.DEG2RAD*a*.5);var e=-a;return this.makeFrustum(e*b,a*b,e,a,c,d)},makeOrthographic:function(a,b,c,d,e,f){var g=this.elements,k=1/(b-a),l=1/(c-d),h=1/(f-e);g[0]=2*k;g[4]=0;g[8]=0;g[12]=-((b+a)*k);g[1]=0;g[5]=2*l;g[9]=0;g[13]=-((c+d)*l);g[2]=0;g[6]=0;g[10]=-2*h;g[14]=-((f+e)*h);g[3]=0;g[7]=0;g[11]=0;g[15]=1;return this},equals:function(a){var b=this.elements;a=a.elements;for(var c=0;16>c;c++)if(b[c]!==a[c])return!1; return!0},fromArray:function(a,b){void 0===b&&(b=0);for(var c=0;16>c;c++)this.elements[c]=a[c+b];return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);var c=this.elements;a[b]=c[0];a[b+1]=c[1];a[b+2]=c[2];a[b+3]=c[3];a[b+4]=c[4];a[b+5]=c[5];a[b+6]=c[6];a[b+7]=c[7];a[b+8]=c[8];a[b+9]=c[9];a[b+10]=c[10];a[b+11]=c[11];a[b+12]=c[12];a[b+13]=c[13];a[b+14]=c[14];a[b+15]=c[15];return a}};Ua.prototype=Object.create(Z.prototype);Ua.prototype.constructor=Ua;Ua.prototype.isCubeTexture=!0;Object.defineProperty(Ua.prototype, @@ -404,18 +404,18 @@ dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,f lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912, mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327, seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};var U={common:{diffuse:{value:new I(15658734)},opacity:{value:1},map:{value:null},offsetRepeat:{value:new da(0,0,1,1)},specularMap:{value:null},alphaMap:{value:null}, -envMap:{value:null},flipEnvMap:{value:-1},reflectivity:{value:1},refractionRatio:{value:.98}},aomap:{aoMap:{value:null},aoMapIntensity:{value:1}},lightmap:{lightMap:{value:null},lightMapIntensity:{value:1}},emissivemap:{emissiveMap:{value:null}},bumpmap:{bumpMap:{value:null},bumpScale:{value:1}},normalmap:{normalMap:{value:null},normalScale:{value:new B(1,1)}},displacementmap:{displacementMap:{value:null},displacementScale:{value:1},displacementBias:{value:0}},roughnessmap:{roughnessMap:{value:null}}, +envMap:{value:null},flipEnvMap:{value:-1},reflectivity:{value:1},refractionRatio:{value:.98}},aomap:{aoMap:{value:null},aoMapIntensity:{value:1}},lightmap:{lightMap:{value:null},lightMapIntensity:{value:1}},emissivemap:{emissiveMap:{value:null}},bumpmap:{bumpMap:{value:null},bumpScale:{value:1}},normalmap:{normalMap:{value:null},normalScale:{value:new C(1,1)}},displacementmap:{displacementMap:{value:null},displacementScale:{value:1},displacementBias:{value:0}},roughnessmap:{roughnessMap:{value:null}}, metalnessmap:{metalnessMap:{value:null}},fog:{fogDensity:{value:2.5E-4},fogNear:{value:1},fogFar:{value:2E3},fogColor:{value:new I(16777215)}},lights:{ambientLightColor:{value:[]},directionalLights:{value:[],properties:{direction:{},color:{},shadow:{},shadowBias:{},shadowRadius:{},shadowMapSize:{}}},directionalShadowMap:{value:[]},directionalShadowMatrix:{value:[]},spotLights:{value:[],properties:{color:{},position:{},direction:{},distance:{},coneCos:{},penumbraCos:{},decay:{},shadow:{},shadowBias:{}, shadowRadius:{},shadowMapSize:{}}},spotShadowMap:{value:[]},spotShadowMatrix:{value:[]},pointLights:{value:[],properties:{color:{},position:{},decay:{},distance:{},shadow:{},shadowBias:{},shadowRadius:{},shadowMapSize:{}}},pointShadowMap:{value:[]},pointShadowMatrix:{value:[]},hemisphereLights:{value:[],properties:{direction:{},skyColor:{},groundColor:{}}}},points:{diffuse:{value:new I(15658734)},opacity:{value:1},size:{value:1},scale:{value:1},map:{value:null},offsetRepeat:{value:new da(0,0,1,1)}}}, -Bb={basic:{uniforms:h.UniformsUtils.merge([U.common,U.aomap,U.fog]),vertexShader:Y.meshbasic_vert,fragmentShader:Y.meshbasic_frag},lambert:{uniforms:h.UniformsUtils.merge([U.common,U.aomap,U.lightmap,U.emissivemap,U.fog,U.lights,{emissive:{value:new I(0)}}]),vertexShader:Y.meshlambert_vert,fragmentShader:Y.meshlambert_frag},phong:{uniforms:h.UniformsUtils.merge([U.common,U.aomap,U.lightmap,U.emissivemap,U.bumpmap,U.normalmap,U.displacementmap,U.fog,U.lights,{emissive:{value:new I(0)},specular:{value:new I(1118481)}, +Db={basic:{uniforms:h.UniformsUtils.merge([U.common,U.aomap,U.fog]),vertexShader:Y.meshbasic_vert,fragmentShader:Y.meshbasic_frag},lambert:{uniforms:h.UniformsUtils.merge([U.common,U.aomap,U.lightmap,U.emissivemap,U.fog,U.lights,{emissive:{value:new I(0)}}]),vertexShader:Y.meshlambert_vert,fragmentShader:Y.meshlambert_frag},phong:{uniforms:h.UniformsUtils.merge([U.common,U.aomap,U.lightmap,U.emissivemap,U.bumpmap,U.normalmap,U.displacementmap,U.fog,U.lights,{emissive:{value:new I(0)},specular:{value:new I(1118481)}, shininess:{value:30}}]),vertexShader:Y.meshphong_vert,fragmentShader:Y.meshphong_frag},standard:{uniforms:h.UniformsUtils.merge([U.common,U.aomap,U.lightmap,U.emissivemap,U.bumpmap,U.normalmap,U.displacementmap,U.roughnessmap,U.metalnessmap,U.fog,U.lights,{emissive:{value:new I(0)},roughness:{value:.5},metalness:{value:0},envMapIntensity:{value:1}}]),vertexShader:Y.meshphysical_vert,fragmentShader:Y.meshphysical_frag},points:{uniforms:h.UniformsUtils.merge([U.points,U.fog]),vertexShader:Y.points_vert, fragmentShader:Y.points_frag},dashed:{uniforms:h.UniformsUtils.merge([U.common,U.fog,{scale:{value:1},dashSize:{value:1},totalSize:{value:2}}]),vertexShader:Y.linedashed_vert,fragmentShader:Y.linedashed_frag},depth:{uniforms:h.UniformsUtils.merge([U.common,U.displacementmap]),vertexShader:Y.depth_vert,fragmentShader:Y.depth_frag},normal:{uniforms:{opacity:{value:1}},vertexShader:Y.normal_vert,fragmentShader:Y.normal_frag},cube:{uniforms:{tCube:{value:null},tFlip:{value:-1},opacity:{value:1}},vertexShader:Y.cube_vert, -fragmentShader:Y.cube_frag},equirect:{uniforms:{tEquirect:{value:null},tFlip:{value:-1}},vertexShader:Y.equirect_vert,fragmentShader:Y.equirect_frag},distanceRGBA:{uniforms:{lightPos:{value:new q}},vertexShader:Y.distanceRGBA_vert,fragmentShader:Y.distanceRGBA_frag}};Bb.physical={uniforms:h.UniformsUtils.merge([Bb.standard.uniforms,{clearCoat:{value:0},clearCoatRoughness:{value:0}}]),vertexShader:Y.meshphysical_vert,fragmentShader:Y.meshphysical_frag};dc.prototype={constructor:dc,set:function(a,b){this.min.copy(a); -this.max.copy(b);return this},setFromPoints:function(a){this.makeEmpty();for(var b=0,c=a.length;bthis.max.x||a.ythis.max.y?!1:!0},containsBox:function(a){return this.min.x<=a.min.x&&a.max.x<=this.max.x&&this.min.y<=a.min.y&&a.max.y<=this.max.y?!0:!1},getParameter:function(a,b){return(b||new B).set((a.x-this.min.x)/(this.max.x-this.min.x),(a.y-this.min.y)/(this.max.y-this.min.y))},intersectsBox:function(a){return a.max.xthis.max.x||a.max.ythis.max.y? -!1:!0},clampPoint:function(a,b){return(b||new B).copy(a).clamp(this.min,this.max)},distanceToPoint:function(){var a=new B;return function(b){return a.copy(b).clamp(this.min,this.max).sub(b).length()}}(),intersect:function(a){this.min.max(a.min);this.max.min(a.max);return this},union:function(a){this.min.min(a.min);this.max.max(a.max);return this},translate:function(a){this.min.add(a);this.max.add(a);return this},equals:function(a){return a.min.equals(this.min)&&a.max.equals(this.max)}};T.prototype= -{constructor:T,isMaterial:!0,get needsUpdate(){return this._needsUpdate},set needsUpdate(a){!0===a&&this.update();this._needsUpdate=a},setValues:function(a){if(void 0!==a)for(var b in a){var c=a[b];if(void 0===c)console.warn("THREE.Material: '"+b+"' parameter is undefined.");else{var d=this[b];void 0===d?console.warn("THREE."+this.type+": '"+b+"' is not a property of this material."):d&&d.isColor?d.set(c):d&&d.isVector3&&c&&c.isVector3?d.copy(c):this[b]="overdraw"===b?Number(c):c}}},toJSON:function(a){function b(a){var b= +fragmentShader:Y.cube_frag},equirect:{uniforms:{tEquirect:{value:null},tFlip:{value:-1}},vertexShader:Y.equirect_vert,fragmentShader:Y.equirect_frag},distanceRGBA:{uniforms:{lightPos:{value:new q}},vertexShader:Y.distanceRGBA_vert,fragmentShader:Y.distanceRGBA_frag}};Db.physical={uniforms:h.UniformsUtils.merge([Db.standard.uniforms,{clearCoat:{value:0},clearCoatRoughness:{value:0}}]),vertexShader:Y.meshphysical_vert,fragmentShader:Y.meshphysical_frag};dc.prototype={constructor:dc,set:function(a,b){this.min.copy(a); +this.max.copy(b);return this},setFromPoints:function(a){this.makeEmpty();for(var b=0,c=a.length;bthis.max.x||a.ythis.max.y?!1:!0},containsBox:function(a){return this.min.x<=a.min.x&&a.max.x<=this.max.x&&this.min.y<=a.min.y&&a.max.y<=this.max.y?!0:!1},getParameter:function(a,b){return(b||new C).set((a.x-this.min.x)/(this.max.x-this.min.x),(a.y-this.min.y)/(this.max.y-this.min.y))},intersectsBox:function(a){return a.max.xthis.max.x||a.max.ythis.max.y? +!1:!0},clampPoint:function(a,b){return(b||new C).copy(a).clamp(this.min,this.max)},distanceToPoint:function(){var a=new C;return function(b){return a.copy(b).clamp(this.min,this.max).sub(b).length()}}(),intersect:function(a){this.min.max(a.min);this.max.min(a.max);return this},union:function(a){this.min.min(a.min);this.max.max(a.max);return this},translate:function(a){this.min.add(a);this.max.add(a);return this},equals:function(a){return a.min.equals(this.min)&&a.max.equals(this.max)}};S.prototype= +{constructor:S,isMaterial:!0,get needsUpdate(){return this._needsUpdate},set needsUpdate(a){!0===a&&this.update();this._needsUpdate=a},setValues:function(a){if(void 0!==a)for(var b in a){var c=a[b];if(void 0===c)console.warn("THREE.Material: '"+b+"' parameter is undefined.");else{var d=this[b];void 0===d?console.warn("THREE."+this.type+": '"+b+"' is not a property of this material."):d&&d.isColor?d.set(c):d&&d.isVector3&&c&&c.isVector3?d.copy(c):this[b]="overdraw"===b?Number(c):c}}},toJSON:function(a){function b(a){var b= [],c;for(c in a){var d=a[c];delete d.metadata;b.push(d)}return b}var c=void 0===a;c&&(a={textures:{},images:{}});var d={metadata:{version:4.4,type:"Material",generator:"Material.toJSON"}};d.uuid=this.uuid;d.type=this.type;""!==this.name&&(d.name=this.name);this.color&&this.color.isColor&&(d.color=this.color.getHex());void 0!==this.roughness&&(d.roughness=this.roughness);void 0!==this.metalness&&(d.metalness=this.metalness);this.emissive&&this.emissive.isColor&&(d.emissive=this.emissive.getHex()); this.specular&&this.specular.isColor&&(d.specular=this.specular.getHex());void 0!==this.shininess&&(d.shininess=this.shininess);this.map&&this.map.isTexture&&(d.map=this.map.toJSON(a).uuid);this.alphaMap&&this.alphaMap.isTexture&&(d.alphaMap=this.alphaMap.toJSON(a).uuid);this.lightMap&&this.lightMap.isTexture&&(d.lightMap=this.lightMap.toJSON(a).uuid);this.bumpMap&&this.bumpMap.isTexture&&(d.bumpMap=this.bumpMap.toJSON(a).uuid,d.bumpScale=this.bumpScale);this.normalMap&&this.normalMap.isTexture&& (d.normalMap=this.normalMap.toJSON(a).uuid,d.normalScale=this.normalScale.toArray());this.displacementMap&&this.displacementMap.isTexture&&(d.displacementMap=this.displacementMap.toJSON(a).uuid,d.displacementScale=this.displacementScale,d.displacementBias=this.displacementBias);this.roughnessMap&&this.roughnessMap.isTexture&&(d.roughnessMap=this.roughnessMap.toJSON(a).uuid);this.metalnessMap&&this.metalnessMap.isTexture&&(d.metalnessMap=this.metalnessMap.toJSON(a).uuid);this.emissiveMap&&this.emissiveMap.isTexture&& @@ -423,9 +423,9 @@ this.specular&&this.specular.isColor&&(d.specular=this.specular.getHex());void 0 (d.vertexColors=this.vertexColors);1>this.opacity&&(d.opacity=this.opacity);!0===this.transparent&&(d.transparent=this.transparent);d.depthFunc=this.depthFunc;d.depthTest=this.depthTest;d.depthWrite=this.depthWrite;0e&&(e=h);r>f&&(f=r);p>g&&(g=p)}this.min.set(b,c,d);this.max.set(e, f,g)},setFromPoints:function(a){this.makeEmpty();for(var b=0,c=a.length;bf||1b&&0a&&0c;c++)b[c].copy(a.planes[c]);return this},setFromMatrix:function(a){var b=this.planes,c=a.elements;a=c[0];var d=c[1],e=c[2],f=c[3],g=c[4],k=c[5],l=c[6],h=c[7],r=c[8],p=c[9],m=c[10],t=c[11],u=c[12],q=c[13],z=c[14],c=c[15];b[0].setComponents(f-a,h-g,t-r,c-u).normalize();b[1].setComponents(f+a,h+g,t+r,c+u).normalize();b[2].setComponents(f+d,h+k,t+p,c+q).normalize();b[3].setComponents(f-d,h-k,t-p,c-q).normalize();b[4].setComponents(f-e,h-l,t-m,c-z).normalize();b[5].setComponents(f+e, -h+l,t+m,c+z).normalize();return this},intersectsObject:function(){var a=new Aa;return function(b){var c=b.geometry;null===c.boundingSphere&&c.computeBoundingSphere();a.copy(c.boundingSphere).applyMatrix4(b.matrixWorld);return this.intersectsSphere(a)}}(),intersectsSprite:function(){var a=new Aa;return function(b){a.center.set(0,0,0);a.radius=.7071067811865476;a.applyMatrix4(b.matrixWorld);return this.intersectsSphere(a)}}(),intersectsSphere:function(a){var b=this.planes,c=a.center;a=-a.radius;for(var d= +this.planes,c=0;6>c;c++)b[c].copy(a.planes[c]);return this},setFromMatrix:function(a){var b=this.planes,c=a.elements;a=c[0];var d=c[1],e=c[2],f=c[3],g=c[4],k=c[5],l=c[6],h=c[7],r=c[8],p=c[9],m=c[10],t=c[11],u=c[12],q=c[13],A=c[14],c=c[15];b[0].setComponents(f-a,h-g,t-r,c-u).normalize();b[1].setComponents(f+a,h+g,t+r,c+u).normalize();b[2].setComponents(f+d,h+k,t+p,c+q).normalize();b[3].setComponents(f-d,h-k,t-p,c-q).normalize();b[4].setComponents(f-e,h-l,t-m,c-A).normalize();b[5].setComponents(f+e, +h+l,t+m,c+A).normalize();return this},intersectsObject:function(){var a=new Aa;return function(b){var c=b.geometry;null===c.boundingSphere&&c.computeBoundingSphere();a.copy(c.boundingSphere).applyMatrix4(b.matrixWorld);return this.intersectsSphere(a)}}(),intersectsSprite:function(){var a=new Aa;return function(b){a.center.set(0,0,0);a.radius=.7071067811865476;a.applyMatrix4(b.matrixWorld);return this.intersectsSphere(a)}}(),intersectsSphere:function(a){var b=this.planes,c=a.center;a=-a.radius;for(var d= 0;6>d;d++)if(b[d].distanceToPoint(c)e;e++){var f=d[e];a.x=0g&&0>f)return!1}return!0}}(),containsPoint:function(a){for(var b=this.planes,c=0;6> -c;c++)if(0>b[c].distanceToPoint(a))return!1;return!0}};C.prototype={constructor:C,isBufferAttribute:!0,set needsUpdate(a){!0===a&&this.version++},setDynamic:function(a){this.dynamic=a;return this},copy:function(a){this.array=new a.array.constructor(a.array);this.itemSize=a.itemSize;this.count=a.count;this.normalized=a.normalized;this.dynamic=a.dynamic;return this},copyAt:function(a,b,c){a*=this.itemSize;c*=b.itemSize;for(var d=0,e=this.itemSize;dMath.abs(g)?(this._x=Math.atan2(-n,e),this._z=Math.atan2(-f,a)):(this._x=Math.atan2(p,l),this._z=0)):"YXZ"===b?(this._x=Math.asin(-d(n,-1,1)),.99999>Math.abs(n)?(this._y=Math.atan2(g,e),this._z=Math.atan2(k,l)):(this._y=Math.atan2(-r,a),this._z=0)):"ZXY"===b?(this._x=Math.asin(d(p,-1,1)), -.99999>Math.abs(p)?(this._y=Math.atan2(-r,e),this._z=Math.atan2(-f,l)):(this._y=0,this._z=Math.atan2(k,a))):"ZYX"===b?(this._y=Math.asin(-d(r,-1,1)),.99999>Math.abs(r)?(this._x=Math.atan2(p,e),this._z=Math.atan2(k,a)):(this._x=0,this._z=Math.atan2(-f,l))):"YZX"===b?(this._z=Math.asin(d(k,-1,1)),.99999>Math.abs(k)?(this._x=Math.atan2(-n,l),this._y=Math.atan2(-r,a)):(this._x=0,this._y=Math.atan2(g,e))):"XZY"===b?(this._z=Math.asin(-d(f,-1,1)),.99999>Math.abs(f)?(this._x=Math.atan2(p,l),this._y=Math.atan2(g, -a)):(this._x=Math.atan2(-n,e),this._y=0)):console.warn("THREE.Euler: .setFromRotationMatrix() given unsupported order: "+b);this._order=b;if(!1!==c)this.onChangeCallback();return this},setFromQuaternion:function(){var a;return function(b,c,d){void 0===a&&(a=new Q);a.makeRotationFromQuaternion(b);return this.setFromRotationMatrix(a,c,d)}}(),setFromVector3:function(a,b){return this.set(a.x,a.y,a.z,b||this._order)},reorder:function(){var a=new oa;return function(b){a.setFromEuler(this);return this.setFromQuaternion(a, -b)}}(),equals:function(a){return a._x===this._x&&a._y===this._y&&a._z===this._z&&a._order===this._order},fromArray:function(a){this._x=a[0];this._y=a[1];this._z=a[2];void 0!==a[3]&&(this._order=a[3]);this.onChangeCallback();return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);a[b]=this._x;a[b+1]=this._y;a[b+2]=this._z;a[b+3]=this._order;return a},toVector3:function(a){return a?a.set(this._x,this._y,this._z):new q(this._x,this._y,this._z)},onChange:function(a){this.onChangeCallback= -a;return this},onChangeCallback:function(){}};Lc.prototype={constructor:Lc,set:function(a){this.mask=1<d;d++)if(e[d]===e[(d+1)%3]){a.push(f);break}for(f=a.length-1;0<=f;f--)for(e=a[f],this.faces.splice(e,1),c=0, -g=this.faceVertexUvs.length;cb[c].distanceToPoint(a))return!1;return!0}};Xa.prototype={constructor:Xa,set:function(a,b){this.origin.copy(a);this.direction.copy(b);return this},clone:function(){return(new this.constructor).copy(this)},copy:function(a){this.origin.copy(a.origin);this.direction.copy(a.direction);return this},at:function(a,b){return(b||new q).copy(this.direction).multiplyScalar(a).add(this.origin)},lookAt:function(a){this.direction.copy(a).sub(this.origin).normalize();return this},recast:function(){var a= +new q;return function(b){this.origin.copy(this.at(b,a));return this}}(),closestPointToPoint:function(a,b){var c=b||new q;c.subVectors(a,this.origin);var d=c.dot(this.direction);return 0>d?c.copy(this.origin):c.copy(this.direction).multiplyScalar(d).add(this.origin)},distanceToPoint:function(a){return Math.sqrt(this.distanceSqToPoint(a))},distanceSqToPoint:function(){var a=new q;return function(b){var c=a.subVectors(b,this.origin).dot(this.direction);if(0>c)return this.origin.distanceToSquared(b); +a.copy(this.direction).multiplyScalar(c).add(this.origin);return a.distanceToSquared(b)}}(),distanceSqToSegment:function(){var a=new q,b=new q,c=new q;return function(d,e,f,g){a.copy(d).add(e).multiplyScalar(.5);b.copy(e).sub(d).normalize();c.copy(this.origin).sub(a);var k=.5*d.distanceTo(e),l=-this.direction.dot(b),h=c.dot(this.direction),r=-c.dot(b),p=c.lengthSq(),m=Math.abs(1-l*l),t;0=-t?e<=t?(k=1/m,d*=k,e*=k,l=d*(d+l*e+2*h)+e*(l*d+e+2*r)+p):(e=k,d=Math.max(0,-(l* +e+h)),l=-d*d+e*(e+2*r)+p):(e=-k,d=Math.max(0,-(l*e+h)),l=-d*d+e*(e+2*r)+p):e<=-t?(d=Math.max(0,-(-l*k+h)),e=0f)return null;f=Math.sqrt(f-e);e=d-f;d+=f;return 0>e&&0>d?null:0>e?this.at(d,c):this.at(e,c)}}(),intersectsSphere:function(a){return this.distanceToPoint(a.center)<=a.radius},distanceToPlane:function(a){var b=a.normal.dot(this.direction);if(0===b)return 0===a.distanceToPoint(this.origin)?0:null;a=-(this.origin.dot(a.normal)+a.constant)/b;return 0<=a?a:null},intersectPlane:function(a, +b){var c=this.distanceToPlane(a);return null===c?null:this.at(c,b)},intersectsPlane:function(a){var b=a.distanceToPoint(this.origin);return 0===b||0>a.normal.dot(this.direction)*b?!0:!1},intersectBox:function(a,b){var c,d,e,f,g;d=1/this.direction.x;f=1/this.direction.y;g=1/this.direction.z;var k=this.origin;0<=d?(c=(a.min.x-k.x)*d,d*=a.max.x-k.x):(c=(a.max.x-k.x)*d,d*=a.min.x-k.x);0<=f?(e=(a.min.y-k.y)*f,f*=a.max.y-k.y):(e=(a.max.y-k.y)*f,f*=a.min.y-k.y);if(c>f||e>d)return null;if(e>c||c!==c)c=e; +if(fg||e>d)return null;if(e>c||c!==c)c=e;if(gd?null:this.at(0<=c?c:d,b)},intersectsBox:function(){var a=new q;return function(b){return null!==this.intersectBox(b,a)}}(),intersectTriangle:function(){var a=new q,b=new q,c=new q,d=new q;return function(e,f,g,k,l){b.subVectors(f,e);c.subVectors(g,e);d.crossVectors(b,c);f=this.direction.dot(d);if(0f)k= +-1,f=-f;else return null;a.subVectors(this.origin,e);e=k*this.direction.dot(c.crossVectors(a,c));if(0>e)return null;g=k*this.direction.dot(b.cross(a));if(0>g||e+g>f)return null;e=-k*a.dot(d);return 0>e?null:this.at(e/f,l)}}(),applyMatrix4:function(a){this.direction.add(this.origin).applyMatrix4(a);this.origin.applyMatrix4(a);this.direction.sub(this.origin);this.direction.normalize();return this},equals:function(a){return a.origin.equals(this.origin)&&a.direction.equals(this.direction)}};Ya.RotationOrders= +"XYZ YZX ZXY XZY YXZ ZYX".split(" ");Ya.DefaultOrder="XYZ";Ya.prototype={constructor:Ya,isEuler:!0,get x(){return this._x},set x(a){this._x=a;this.onChangeCallback()},get y(){return this._y},set y(a){this._y=a;this.onChangeCallback()},get z(){return this._z},set z(a){this._z=a;this.onChangeCallback()},get order(){return this._order},set order(a){this._order=a;this.onChangeCallback()},set:function(a,b,c,d){this._x=a;this._y=b;this._z=c;this._order=d||this._order;this.onChangeCallback();return this}, +clone:function(){return new this.constructor(this._x,this._y,this._z,this._order)},copy:function(a){this._x=a._x;this._y=a._y;this._z=a._z;this._order=a._order;this.onChangeCallback();return this},setFromRotationMatrix:function(a,b,c){var d=h.Math.clamp,e=a.elements;a=e[0];var f=e[4],g=e[8],k=e[1],l=e[5],n=e[9],r=e[2],p=e[6],e=e[10];b=b||this._order;"XYZ"===b?(this._y=Math.asin(d(g,-1,1)),.99999>Math.abs(g)?(this._x=Math.atan2(-n,e),this._z=Math.atan2(-f,a)):(this._x=Math.atan2(p,l),this._z=0)):"YXZ"=== +b?(this._x=Math.asin(-d(n,-1,1)),.99999>Math.abs(n)?(this._y=Math.atan2(g,e),this._z=Math.atan2(k,l)):(this._y=Math.atan2(-r,a),this._z=0)):"ZXY"===b?(this._x=Math.asin(d(p,-1,1)),.99999>Math.abs(p)?(this._y=Math.atan2(-r,e),this._z=Math.atan2(-f,l)):(this._y=0,this._z=Math.atan2(k,a))):"ZYX"===b?(this._y=Math.asin(-d(r,-1,1)),.99999>Math.abs(r)?(this._x=Math.atan2(p,e),this._z=Math.atan2(k,a)):(this._x=0,this._z=Math.atan2(-f,l))):"YZX"===b?(this._z=Math.asin(d(k,-1,1)),.99999>Math.abs(k)?(this._x= +Math.atan2(-n,l),this._y=Math.atan2(-r,a)):(this._x=0,this._y=Math.atan2(g,e))):"XZY"===b?(this._z=Math.asin(-d(f,-1,1)),.99999>Math.abs(f)?(this._x=Math.atan2(p,l),this._y=Math.atan2(g,a)):(this._x=Math.atan2(-n,e),this._y=0)):console.warn("THREE.Euler: .setFromRotationMatrix() given unsupported order: "+b);this._order=b;if(!1!==c)this.onChangeCallback();return this},setFromQuaternion:function(){var a;return function(b,c,d){void 0===a&&(a=new P);a.makeRotationFromQuaternion(b);return this.setFromRotationMatrix(a, +c,d)}}(),setFromVector3:function(a,b){return this.set(a.x,a.y,a.z,b||this._order)},reorder:function(){var a=new oa;return function(b){a.setFromEuler(this);return this.setFromQuaternion(a,b)}}(),equals:function(a){return a._x===this._x&&a._y===this._y&&a._z===this._z&&a._order===this._order},fromArray:function(a){this._x=a[0];this._y=a[1];this._z=a[2];void 0!==a[3]&&(this._order=a[3]);this.onChangeCallback();return this},toArray:function(a,b){void 0===a&&(a=[]);void 0===b&&(b=0);a[b]=this._x;a[b+1]= +this._y;a[b+2]=this._z;a[b+3]=this._order;return a},toVector3:function(a){return a?a.set(this._x,this._y,this._z):new q(this._x,this._y,this._z)},onChange:function(a){this.onChangeCallback=a;return this},onChangeCallback:function(){}};Lc.prototype={constructor:Lc,set:function(a){this.mask=1<=b.x+b.y}}();Ga.prototype={constructor:Ga,set:function(a,b,c){this.a.copy(a);this.b.copy(b);this.c.copy(c);return this},setFromPointsAndIndices:function(a,b,c,d){this.a.copy(a[b]);this.b.copy(a[c]);this.c.copy(a[d]);return this},clone:function(){return(new this.constructor).copy(this)},copy:function(a){this.a.copy(a.a);this.b.copy(a.b);this.c.copy(a.c);return this},area:function(){var a=new q,b=new q;return function(){a.subVectors(this.c, +this.b);b.subVectors(this.a,this.b);return.5*a.cross(b).length()}}(),midpoint:function(a){return(a||new q).addVectors(this.a,this.b).add(this.c).multiplyScalar(1/3)},normal:function(a){return Ga.normal(this.a,this.b,this.c,a)},plane:function(a){return(a||new fa).setFromCoplanarPoints(this.a,this.b,this.c)},barycoordFromPoint:function(a,b){return Ga.barycoordFromPoint(a,this.a,this.b,this.c,b)},containsPoint:function(a){return Ga.containsPoint(a,this.a,this.b,this.c)},closestPointToPoint:function(){var a, +b,c,d;return function(e,f){void 0===a&&(a=new fa,b=[new db,new db,new db],c=new q,d=new q);var g=f||new q,k=Infinity;a.setFromCoplanarPoints(this.a,this.b,this.c);a.projectPoint(e,c);if(!0===this.containsPoint(c))g.copy(c);else{b[0].set(this.a,this.b);b[1].set(this.b,this.c);b[2].set(this.c,this.a);for(var l=0;ld;d++)if(e[d]===e[(d+1)%3]){a.push(f);break}for(f=a.length-1;0<=f;f--)for(e=a[f],this.faces.splice(e,1),c=0,g=this.faceVertexUvs.length;cd?c.copy(this.origin):c.copy(this.direction).multiplyScalar(d).add(this.origin)},distanceToPoint:function(a){return Math.sqrt(this.distanceSqToPoint(a))},distanceSqToPoint:function(){var a=new q;return function(b){var c=a.subVectors(b,this.origin).dot(this.direction);if(0>c)return this.origin.distanceToSquared(b); -a.copy(this.direction).multiplyScalar(c).add(this.origin);return a.distanceToSquared(b)}}(),distanceSqToSegment:function(){var a=new q,b=new q,c=new q;return function(d,e,f,g){a.copy(d).add(e).multiplyScalar(.5);b.copy(e).sub(d).normalize();c.copy(this.origin).sub(a);var k=.5*d.distanceTo(e),l=-this.direction.dot(b),h=c.dot(this.direction),r=-c.dot(b),p=c.lengthSq(),m=Math.abs(1-l*l),t;0=-t?e<=t?(k=1/m,d*=k,e*=k,l=d*(d+l*e+2*h)+e*(l*d+e+2*r)+p):(e=k,d=Math.max(0,-(l* -e+h)),l=-d*d+e*(e+2*r)+p):(e=-k,d=Math.max(0,-(l*e+h)),l=-d*d+e*(e+2*r)+p):e<=-t?(d=Math.max(0,-(-l*k+h)),e=0f)return null;f=Math.sqrt(f-e);e=d-f;d+=f;return 0>e&&0>d?null:0>e?this.at(d,c):this.at(e,c)}}(),intersectsSphere:function(a){return this.distanceToPoint(a.center)<=a.radius},distanceToPlane:function(a){var b=a.normal.dot(this.direction);if(0===b)return 0===a.distanceToPoint(this.origin)?0:null;a=-(this.origin.dot(a.normal)+a.constant)/b;return 0<=a?a:null},intersectPlane:function(a, -b){var c=this.distanceToPlane(a);return null===c?null:this.at(c,b)},intersectsPlane:function(a){var b=a.distanceToPoint(this.origin);return 0===b||0>a.normal.dot(this.direction)*b?!0:!1},intersectBox:function(a,b){var c,d,e,f,g;d=1/this.direction.x;f=1/this.direction.y;g=1/this.direction.z;var k=this.origin;0<=d?(c=(a.min.x-k.x)*d,d*=a.max.x-k.x):(c=(a.max.x-k.x)*d,d*=a.min.x-k.x);0<=f?(e=(a.min.y-k.y)*f,f*=a.max.y-k.y):(e=(a.max.y-k.y)*f,f*=a.min.y-k.y);if(c>f||e>d)return null;if(e>c||c!==c)c=e; -if(fg||e>d)return null;if(e>c||c!==c)c=e;if(gd?null:this.at(0<=c?c:d,b)},intersectsBox:function(){var a=new q;return function(b){return null!==this.intersectBox(b,a)}}(),intersectTriangle:function(){var a=new q,b=new q,c=new q,d=new q;return function(e,f,g,k,h){b.subVectors(f,e);c.subVectors(g,e);d.crossVectors(b,c);f=this.direction.dot(d);if(0f)k= --1,f=-f;else return null;a.subVectors(this.origin,e);e=k*this.direction.dot(c.crossVectors(a,c));if(0>e)return null;g=k*this.direction.dot(b.cross(a));if(0>g||e+g>f)return null;e=-k*a.dot(d);return 0>e?null:this.at(e/f,h)}}(),applyMatrix4:function(a){this.direction.add(this.origin).applyMatrix4(a);this.origin.applyMatrix4(a);this.direction.sub(this.origin);this.direction.normalize();return this},equals:function(a){return a.origin.equals(this.origin)&&a.direction.equals(this.direction)}};Cb.prototype= -{constructor:Cb,set:function(a,b){this.start.copy(a);this.end.copy(b);return this},clone:function(){return(new this.constructor).copy(this)},copy:function(a){this.start.copy(a.start);this.end.copy(a.end);return this},center:function(a){return(a||new q).addVectors(this.start,this.end).multiplyScalar(.5)},delta:function(a){return(a||new q).subVectors(this.end,this.start)},distanceSq:function(){return this.start.distanceToSquared(this.end)},distance:function(){return this.start.distanceTo(this.end)}, -at:function(a,b){var c=b||new q;return this.delta(c).multiplyScalar(a).add(this.start)},closestPointToPointParameter:function(){var a=new q,b=new q;return function(c,d){a.subVectors(c,this.start);b.subVectors(this.end,this.start);var e=b.dot(b),e=b.dot(a)/e;d&&(e=h.Math.clamp(e,0,1));return e}}(),closestPointToPoint:function(a,b,c){a=this.closestPointToPointParameter(a,b);c=c||new q;return this.delta(c).multiplyScalar(a).add(this.start)},applyMatrix4:function(a){this.start.applyMatrix4(a);this.end.applyMatrix4(a); -return this},equals:function(a){return a.start.equals(this.start)&&a.end.equals(this.end)}};Ga.normal=function(){var a=new q;return function(b,c,d,e){e=e||new q;e.subVectors(d,c);a.subVectors(b,c);e.cross(a);b=e.lengthSq();return 0=b.x+b.y}}();Ga.prototype={constructor:Ga,set:function(a,b,c){this.a.copy(a);this.b.copy(b);this.c.copy(c);return this},setFromPointsAndIndices:function(a,b,c,d){this.a.copy(a[b]);this.b.copy(a[c]);this.c.copy(a[d]);return this},clone:function(){return(new this.constructor).copy(this)}, -copy:function(a){this.a.copy(a.a);this.b.copy(a.b);this.c.copy(a.c);return this},area:function(){var a=new q,b=new q;return function(){a.subVectors(this.c,this.b);b.subVectors(this.a,this.b);return.5*a.cross(b).length()}}(),midpoint:function(a){return(a||new q).addVectors(this.a,this.b).add(this.c).multiplyScalar(1/3)},normal:function(a){return Ga.normal(this.a,this.b,this.c,a)},plane:function(a){return(a||new fa).setFromCoplanarPoints(this.a,this.b,this.c)},barycoordFromPoint:function(a,b){return Ga.barycoordFromPoint(a, -this.a,this.b,this.c,b)},containsPoint:function(a){return Ga.containsPoint(a,this.a,this.b,this.c)},closestPointToPoint:function(){var a,b,c,d;return function(e,f){void 0===a&&(a=new fa,b=[new Cb,new Cb,new Cb],c=new q,d=new q);var g=f||new q,k=Infinity;a.setFromCoplanarPoints(this.a,this.b,this.c);a.projectPoint(e,c);if(!0===this.containsPoint(c))g.copy(c);else{b[0].set(this.a,this.b);b[1].set(this.b,this.c);b[2].set(this.c,this.a);for(var h=0;hb.far?null:{distance:c,point:w.clone(),object:a}}function c(c,d,e,f,n,p,r,q){g.fromArray(f,3*p);k.fromArray(f,3*r);h.fromArray(f,3*q);if(c=b(c,d,e,g,k,h,z))n&&(m.fromArray(n,2*p),t.fromArray(n,2*r),u.fromArray(n,2*q),c.uv=a(z,g,k,h,m,t,u)),c.face=new la(p,r,q,Ga.normal(g,k,h)),c.faceIndex=p;return c}var d=new Q,e=new Ya,f=new Aa,g=new q,k=new q,h=new q, -n=new q,r=new q,p=new q,m=new B,t=new B,u=new B,v=new q,z=new q,w=new q;return function(q,v){var w=this.geometry,F=this.material,G=this.matrixWorld;if(void 0!==F&&(null===w.boundingSphere&&w.computeBoundingSphere(),f.copy(w.boundingSphere),f.applyMatrix4(G),!1!==q.ray.intersectsSphere(f)&&(d.getInverse(G),e.copy(q.ray).applyMatrix4(d),null===w.boundingBox||!1!==e.intersectsBox(w.boundingBox)))){var E,K;if(w&&w.isBufferGeometry){var B,C,F=w.index,G=w.attributes,w=G.position.array;void 0!==G.uv&&(E= -G.uv.array);if(null!==F)for(var G=F.array,D=0,H=G.length;Dthis.scale.x*this.scale.y/4||c.push({distance:Math.sqrt(d),point:this.position,face:null,object:this})}}(),clone:function(){return(new this.constructor(this.material)).copy(this)}});hc.prototype=Object.assign(Object.create(D.prototype),{constructor:hc,copy:function(a){D.prototype.copy.call(this,a,!1);a=a.levels;for(var b=0,c=a.length;b=d[e].distance)d[e-1].object.visible=!1,d[e].object.visible=!0;else break;for(;ef||(r.applyMatrix4(this.matrixWorld),v=d.ray.origin.distanceTo(r),vd.far||e.push({distance:v,point:k.clone().applyMatrix4(this.matrixWorld), -index:g,face:null,faceIndex:null,object:this}))}else for(g=0,u=t.length/3-1;gf||(r.applyMatrix4(this.matrixWorld),v=d.ray.origin.distanceTo(r),vd.far||e.push({distance:v,point:k.clone().applyMatrix4(this.matrixWorld),index:g,face:null,faceIndex:null,object:this}))}else if(g&&g.isGeometry)for(h=g.vertices,n=h.length,g=0;gf||(r.applyMatrix4(this.matrixWorld), -v=d.ray.origin.distanceTo(r),vd.far||e.push({distance:v,point:k.clone().applyMatrix4(this.matrixWorld),index:g,face:null,faceIndex:null,object:this}))}}}(),clone:function(){return(new this.constructor(this.geometry,this.material)).copy(this)}});ba.prototype=Object.assign(Object.create(Qa.prototype),{constructor:ba,isLineSegments:!0});Ka.prototype=Object.create(T.prototype);Ka.prototype.constructor=Ka;Ka.prototype.isPointsMaterial=!0;Ka.prototype.copy=function(a){T.prototype.copy.call(this, -a);this.color.copy(a.color);this.map=a.map;this.size=a.size;this.sizeAttenuation=a.sizeAttenuation;return this};Gb.prototype=Object.assign(Object.create(D.prototype),{constructor:Gb,isPoints:!0,raycast:function(){var a=new Q,b=new Ya,c=new Aa;return function(d,e){function f(a,c){var f=b.distanceSqToPoint(a);if(fd.far||e.push({distance:m,distanceToRay:Math.sqrt(f),point:k.clone(),index:c,face:null,object:g})}} -var g=this,k=this.geometry,h=this.matrixWorld,n=d.params.Points.threshold;null===k.boundingSphere&&k.computeBoundingSphere();c.copy(k.boundingSphere);c.applyMatrix4(h);if(!1!==d.ray.intersectsSphere(c)){a.getInverse(h);b.copy(d.ray).applyMatrix4(a);var n=n/((this.scale.x+this.scale.y+this.scale.z)/3),r=n*n,n=new q;if(k&&k.isBufferGeometry){var p=k.index,k=k.attributes.position.array;if(null!==p)for(var m=p.array,p=0,t=m.length;p=e)break a;else{f=b[1];a=e)break b}d=c;c=0}}for(;c>>1,ab;)--f;++f;if(0!==e||f!==d)e>=f&&(f=Math.max(f, -1),e=f-1),d=this.getValueSize(),this.times=h.AnimationUtils.arraySlice(c,e,f),this.values=h.AnimationUtils.arraySlice(this.values,e*d,f*d);return this},validate:function(){var a=!0,b=this.getValueSize();0!==b-Math.floor(b)&&(console.error("invalid value size in track",this),a=!1);var c=this.times,b=this.values,d=c.length;0===d&&(console.error("track is empty",this),a=!1);for(var e=null,f=0;f!==d;f++){var g=c[f];if("number"===typeof g&&isNaN(g)){console.error("time is not a valid number",this,f,g); -a=!1;break}if(null!==e&&e>g){console.error("out of order keys",this,f,g,e);a=!1;break}e=g}if(void 0!==b&&h.AnimationUtils.isTypedArray(b))for(f=0,c=b.length;f!==c;++f)if(d=b[f],isNaN(d)){console.error("value is not a valid number",this,f,d);a=!1;break}return a},optimize:function(){for(var a=this.times,b=this.values,c=this.getValueSize(),d=2302===this.getInterpolation(),e=1,f=a.length-1,g=1;gl.opacity&&(l.transparent=!0);c.setTextures(k);return c.parse(l)}}()};pb.Handlers={handlers:[],add:function(a,b){this.handlers.push(a,b)},get:function(a){for(var b=this.handlers,c=0,d=b.length;cg;g++)m=x[h++],w=z[2*m],m=z[2*m+1],w=new B(w,m),2!==g&&c.faceVertexUvs[d][k].push(w),0!==g&&c.faceVertexUvs[d][k+1].push(w);p&&(p=3*x[h++],t.normal.set(N[p++],N[p++],N[p]),v.normal.copy(t.normal));if(u)for(d=0;4>d;d++)p=3*x[h++],u=new q(N[p++],N[p++],N[p]),2!==d&&t.vertexNormals.push(u),0!==d&&v.vertexNormals.push(u);r&&(r=x[h++],r=y[r],t.color.setHex(r),v.color.setHex(r));if(b)for(d= -0;4>d;d++)r=x[h++],r=y[r],2!==d&&t.vertexColors.push(new I(r)),0!==d&&v.vertexColors.push(new I(r));c.faces.push(t);c.faces.push(v)}else{t=new la;t.a=x[h++];t.b=x[h++];t.c=x[h++];k&&(k=x[h++],t.materialIndex=k);k=c.faces.length;if(d)for(d=0;dg;g++)m=x[h++],w=z[2*m],m=z[2*m+1],w=new B(w,m),c.faceVertexUvs[d][k].push(w);p&&(p=3*x[h++],t.normal.set(N[p++],N[p++],N[p]));if(u)for(d=0;3>d;d++)p=3*x[h++],u=new q(N[p++],N[p++],N[p]),t.vertexNormals.push(u); -r&&(r=x[h++],t.color.setHex(y[r]));if(b)for(d=0;3>d;d++)r=x[h++],t.vertexColors.push(new I(y[r]));c.faces.push(t)}})(d);(function(){var b=void 0!==a.influencesPerVertex?a.influencesPerVertex:2;if(a.skinWeights)for(var d=0,g=a.skinWeights.length;dNumber.EPSILON&&(k.normalize(),d=Math.acos(h.Math.clamp(e[n-1].dot(e[n]),-1,1)),f[n].applyMatrix4(l.makeRotationAxis(k,d))),g[n].crossVectors(e[n],f[n]);if(c)for(d=Math.acos(h.Math.clamp(f[0].dot(f[b-1]),-1,1)),d/=b-1,0c)return null;var d=[],e=[],f=[],g,k,l;if(0=n--){console.warn("THREE.ShapeUtils: Unable to triangulate polygon! in triangulate()");break}g=k;c<=g&&(g=0);k=g+1;c<=k&&(k=0);l=k+1;c<=l&&(l=0);var r;a:{var p,m,t,u,q,z,w,x;p=a[e[g]].x;m=a[e[g]].y;t=a[e[k]].x;u=a[e[k]].y;q=a[e[l]].x;z=a[e[l]].y;if(Number.EPSILON>(t-p)*(z-m)-(u-m)*(q-p))r=!1;else{var B,y,F,G,E,K,C,D,H,I;B=q-t;y=z-u;F=p-q;G=m-z;E=t-p;K=u-m;for(r=0;r=-Number.EPSILON&&D>=-Number.EPSILON&&C>=-Number.EPSILON)){r=!1;break a}r=!0}}if(r){d.push([a[e[g]],a[e[k]],a[e[l]]]);f.push([e[g],e[k],e[l]]);g=k;for(l=k+1;lNumber.EPSILON){if(0r||r>p)return[];h=l*n-h*m;if(0>h||h>p)return[]}else{if(0c?[]:h===c?f?[]:[g]:a<=c?[g,k]:[g,l]}function f(a,b,c,d){var e=b.x-a.x,f=b.y-a.y;b=c.x-a.x;c=c.y-a.y;var g=d.x-a.x;d=d.y-a.y;a=e*c-f*b;e=e*d-f*g;return Math.abs(a)> -Number.EPSILON?(b=g*c-d*b,0e&&(e=d);var g=a+1;g>d&&(g=0);d=f(k[a],k[e],k[g],h[b]);if(!d)return!1;d=h.length-1;e=b-1;0>e&&(e=d);g=b+1;g>d&&(g=0);return(d=f(h[b],h[e],h[g],k[a]))?!0: -!1}function d(a,b){var c,f;for(c=0;cM){console.log("Infinite Loop! Holes left:"+l.length+", Probably Hole outside Shape!");break}for(m= -C;ml;l++)r=n[l].x+":"+n[l].y,r=p[r],void 0!==r&&(n[l]=r);return m.concat()},isClockWise:function(a){return 0> -h.ShapeUtils.area(a)},b2:function(){return function(a,b,c,d){var e=1-a;return e*e*b+2*(1-a)*a*c+a*a*d}}(),b3:function(){return function(a,b,c,d,e){var f=1-a,g=1-a;return f*f*f*b+3*g*g*a*c+3*(1-a)*a*a*d+a*a*a*e}}()};ta.prototype=Object.create(S.prototype);ta.prototype.constructor=ta;ta.prototype.addShapeList=function(a,b){for(var c=a.length,d=0;dNumber.EPSILON){var h=Math.sqrt(k),l=Math.sqrt(d*d+g*g),k=b.x-f/h;b=b.y+e/h;g=((c.x-g/l-k)*g-(c.y+d/l-b)*d)/(e*g-f*d);d=k+e*g-a.x;e=b+f*g-a.y;f=d*d+e*e;if(2>=f)return new B(d,e);f=Math.sqrt(f/2)}else a=!1,e>Number.EPSILON?d>Number.EPSILON&&(a=!0):e<-Number.EPSILON?d<-Number.EPSILON&&(a=!0):Math.sign(f)===Math.sign(g)&&(a=!0),a?(d=-f,f=Math.sqrt(k)):(d=e,e=f,f=Math.sqrt(k/2));return new B(d/f,e/f)} -function e(a,b){var c,d;for(J=a.length;0<=--J;){c=J;d=J-1;0>d&&(d=a.length-1);var e,f=t+2*r;for(e=0;eMath.abs(b.y-c.y)?[new B(b.x,1-b.z),new B(c.x,1-c.z),new B(d.x,1-d.z),new B(e.x,1-e.z)]:[new B(b.y,1-b.z),new B(c.y,1-c.z),new B(d.y,1-d.z),new B(e.y,1-e.z)]}};vc.prototype=Object.create(ta.prototype);vc.prototype.constructor=vc;qb.prototype=Object.create(H.prototype);qb.prototype.constructor=qb;Pb.prototype=Object.create(S.prototype);Pb.prototype.constructor=Pb;Qb.prototype= -Object.create(H.prototype);Qb.prototype.constructor=Qb;wc.prototype=Object.create(S.prototype);wc.prototype.constructor=wc;xc.prototype=Object.create(S.prototype);xc.prototype.constructor=xc;Rb.prototype=Object.create(H.prototype);Rb.prototype.constructor=Rb;yc.prototype=Object.create(S.prototype);yc.prototype.constructor=yc;$a.prototype=Object.create(S.prototype);$a.prototype.constructor=$a;$a.prototype.addShapeList=function(a,b){for(var c=0,d=a.length;ch)g=d+1;else if(0b&&(b=0);1=b)return b=c[a]-b,a=this.curves[a],c=a.getLength(),a.getPointAt(0=== -c?0:1-b/c);a++}return null},getLength:function(){var a=this.getCurveLengths();return a[a.length-1]},updateArcLengths:function(){this.needsUpdate=!0;this.cacheLengths=null;this.getLengths()},getCurveLengths:function(){if(this.cacheLengths&&this.cacheLengths.length===this.curves.length)return this.cacheLengths;for(var a=[],b=0,c=0,d=this.curves.length;cc;)c+=b;for(;c>b;)c-=b;cb.length-2?b.length-1:c+1],b=b[c>b.length-3?b.length-1:c+2],c=h.CurveUtils.interpolate;return new B(c(d.x,e.x,f.x,b.x,a),c(d.y,e.y,f.y,b.y, -a))};ub.prototype=Object.create(qa.prototype);ub.prototype.constructor=ub;ub.prototype.getPoint=function(a){var b=h.ShapeUtils.b3;return new B(b(a,this.v0.x,this.v1.x,this.v2.x,this.v3.x),b(a,this.v0.y,this.v1.y,this.v2.y,this.v3.y))};ub.prototype.getTangent=function(a){var b=h.CurveUtils.tangentCubicBezier;return(new B(b(a,this.v0.x,this.v1.x,this.v2.x,this.v3.x),b(a,this.v0.y,this.v1.y,this.v2.y,this.v3.y))).normalize()};vb.prototype=Object.create(qa.prototype);vb.prototype.constructor=vb;vb.prototype.getPoint= -function(a){var b=h.ShapeUtils.b2;return new B(b(a,this.v0.x,this.v1.x,this.v2.x),b(a,this.v0.y,this.v1.y,this.v2.y))};vb.prototype.getTangent=function(a){var b=h.CurveUtils.tangentQuadraticBezier;return(new B(b(a,this.v0.x,this.v1.x,this.v2.x),b(a,this.v0.y,this.v1.y,this.v2.y))).normalize()};var Rd=Object.assign(Object.create(Cc.prototype),{fromPoints:function(a){this.moveTo(a[0].x,a[0].y);for(var b=1,c=a.length;bNumber.EPSILON){if(0>l&&(g=b[f],h=-h,k=b[e],l=-l),!(a.yk.y))if(a.y===g.y){if(a.x===g.x)return!0}else{e=l*(a.x-g.x)-h*(a.y-g.y);if(0===e)return!0;0>e||(d=!d)}}else if(a.y===g.y&&(k.x<=a.x&&a.x<=g.x||g.x<=a.x&&a.x<=k.x))return!0}return d}var e=h.ShapeUtils.isClockWise,f=this.subPaths;if(0===f.length)return[];if(!0===b)return c(f);var g,k,l,n=[];if(1===f.length)return k=f[0],l=new Ub,l.curves=k.curves,n.push(l), -n;var r=!e(f[0].getPoints()),r=a?!r:r;l=[];var p=[],m=[],q=0,u;p[q]=void 0;m[q]=[];for(var v=0,z=f.length;vb.far?null:{distance:c,point:w.clone(),object:a}}function c(c,d,e,f,n,p,r,q){g.fromArray(f,3*p);k.fromArray(f,3*r);h.fromArray(f, +3*q);if(c=b(c,d,e,g,k,h,A))n&&(m.fromArray(n,2*p),t.fromArray(n,2*r),u.fromArray(n,2*q),c.uv=a(A,g,k,h,m,t,u)),c.face=new ka(p,r,q,Ga.normal(g,k,h)),c.faceIndex=p;return c}var d=new P,e=new Xa,f=new Aa,g=new q,k=new q,h=new q,n=new q,r=new q,p=new q,m=new C,t=new C,u=new C,v=new q,A=new q,w=new q;return function(q,v){var w=this.geometry,F=this.material,G=this.matrixWorld;if(void 0!==F&&(null===w.boundingSphere&&w.computeBoundingSphere(),f.copy(w.boundingSphere),f.applyMatrix4(G),!1!==q.ray.intersectsSphere(f)&& +(d.getInverse(G),e.copy(q.ray).applyMatrix4(d),null===w.boundingBox||!1!==e.intersectsBox(w.boundingBox)))){var E,K;if(w&&w.isBufferGeometry){var C,D,F=w.index,G=w.attributes,w=G.position.array;void 0!==G.uv&&(E=G.uv.array);if(null!==F)for(var G=F.array,y=0,H=G.length;ythis.scale.x*this.scale.y/ +4||c.push({distance:Math.sqrt(d),point:this.position,face:null,object:this})}}(),clone:function(){return(new this.constructor(this.material)).copy(this)}});hc.prototype=Object.assign(Object.create(D.prototype),{constructor:hc,copy:function(a){D.prototype.copy.call(this,a,!1);a=a.levels;for(var b=0,c=a.length;b=d[e].distance)d[e-1].object.visible=!1,d[e].object.visible=!0;else break;for(;ef||(r.applyMatrix4(this.matrixWorld),v=d.ray.origin.distanceTo(r),vd.far||e.push({distance:v,point:k.clone().applyMatrix4(this.matrixWorld),index:g,face:null,faceIndex:null,object:this}))}else for(g=0,u=t.length/3-1;gf||(r.applyMatrix4(this.matrixWorld), +v=d.ray.origin.distanceTo(r),vd.far||e.push({distance:v,point:k.clone().applyMatrix4(this.matrixWorld),index:g,face:null,faceIndex:null,object:this}))}else if(g&&g.isGeometry)for(h=g.vertices,n=h.length,g=0;gf||(r.applyMatrix4(this.matrixWorld),v=d.ray.origin.distanceTo(r),vd.far||e.push({distance:v,point:k.clone().applyMatrix4(this.matrixWorld),index:g,face:null,faceIndex:null,object:this}))}}}(),clone:function(){return(new this.constructor(this.geometry, +this.material)).copy(this)}});ba.prototype=Object.assign(Object.create(Qa.prototype),{constructor:ba,isLineSegments:!0});Ka.prototype=Object.create(S.prototype);Ka.prototype.constructor=Ka;Ka.prototype.isPointsMaterial=!0;Ka.prototype.copy=function(a){S.prototype.copy.call(this,a);this.color.copy(a.color);this.map=a.map;this.size=a.size;this.sizeAttenuation=a.sizeAttenuation;return this};Hb.prototype=Object.assign(Object.create(D.prototype),{constructor:Hb,isPoints:!0,raycast:function(){var a=new P, +b=new Xa,c=new Aa;return function(d,e){function f(a,c){var f=b.distanceSqToPoint(a);if(fd.far||e.push({distance:m,distanceToRay:Math.sqrt(f),point:k.clone(),index:c,face:null,object:g})}}var g=this,k=this.geometry,h=this.matrixWorld,n=d.params.Points.threshold;null===k.boundingSphere&&k.computeBoundingSphere();c.copy(k.boundingSphere);c.applyMatrix4(h);if(!1!==d.ray.intersectsSphere(c)){a.getInverse(h); +b.copy(d.ray).applyMatrix4(a);var n=n/((this.scale.x+this.scale.y+this.scale.z)/3),r=n*n,n=new q;if(k&&k.isBufferGeometry){var p=k.index,k=k.attributes.position.array;if(null!==p)for(var m=p.array,p=0,t=m.length;pNumber.EPSILON&&(k.normalize(),d=Math.acos(h.Math.clamp(e[n-1].dot(e[n]),-1,1)),f[n].applyMatrix4(l.makeRotationAxis(k,d))),g[n].crossVectors(e[n],f[n]);if(c)for(d=Math.acos(h.Math.clamp(f[0].dot(f[b-1]),-1,1)),d/=b-1,0c)return null;var d=[],e=[],f=[],g,k,l;if(0=n--){console.warn("THREE.ShapeUtils: Unable to triangulate polygon! in triangulate()"); +break}g=k;c<=g&&(g=0);k=g+1;c<=k&&(k=0);l=k+1;c<=l&&(l=0);var r;a:{var p,m,t,u,q,A,w,x;p=a[e[g]].x;m=a[e[g]].y;t=a[e[k]].x;u=a[e[k]].y;q=a[e[l]].x;A=a[e[l]].y;if(Number.EPSILON>(t-p)*(A-m)-(u-m)*(q-p))r=!1;else{var C,z,F,G,E,K,D,y,H,I;C=q-t;z=A-u;F=p-q;G=m-A;E=t-p;K=u-m;for(r=0;r=-Number.EPSILON&&y>=-Number.EPSILON&&D>=-Number.EPSILON)){r=!1;break a}r=!0}}if(r){d.push([a[e[g]], +a[e[k]],a[e[l]]]);f.push([e[g],e[k],e[l]]);g=k;for(l=k+1;lNumber.EPSILON){if(0r||r>p)return[];h=l*m-h* +n;if(0>h||h>p)return[]}else{if(0c?[]:h===c?f?[]:[g]:a<=c?[g,k]:[g,l]}function f(a,b,c,d){var e=b.x-a.x,f=b.y-a.y;b=c.x-a.x;c=c.y-a.y;var g=d.x-a.x;d=d.y-a.y;a=e*c-f*b;e=e*d-f*g;return Math.abs(a)>Number.EPSILON?(b=g*c-d*b,0e&&(e=d);var g=a+1;g>d&&(g=0);d=f(k[a],k[e],k[g],h[b]);if(!d)return!1;d=h.length-1;e=b-1;0>e&&(e=d);g=b+1;g>d&&(g=0);return(d=f(h[b],h[e],h[g],k[a]))?!0:!1}function d(a,b){var c,f;for(c=0;cM){console.log("Infinite Loop! Holes left:"+l.length+", Probably Hole outside Shape!");break}for(n=D;nl;l++)r=n[l].x+":"+n[l].y,r=p[r],void 0!==r&&(n[l]=r);return m.concat()},isClockWise:function(a){return 0>h.ShapeUtils.area(a)},b2:function(){return function(a,b,c,d){var e=1-a;return e*e*b+2*(1-a)*a*c+a*a*d}}(),b3:function(){return function(a,b,c,d,e){var f=1- +a,g=1-a;return f*f*f*b+3*g*g*a*c+3*(1-a)*a*a*d+a*a*a*e}}()};pa.prototype=Object.create(R.prototype);pa.prototype.constructor=pa;pa.prototype.addShapeList=function(a,b){for(var c=a.length,d=0;dNumber.EPSILON){var h= +Math.sqrt(k),l=Math.sqrt(d*d+g*g),k=b.x-f/h;b=b.y+e/h;g=((c.x-g/l-k)*g-(c.y+d/l-b)*d)/(e*g-f*d);d=k+e*g-a.x;e=b+f*g-a.y;f=d*d+e*e;if(2>=f)return new C(d,e);f=Math.sqrt(f/2)}else a=!1,e>Number.EPSILON?d>Number.EPSILON&&(a=!0):e<-Number.EPSILON?d<-Number.EPSILON&&(a=!0):Math.sign(f)===Math.sign(g)&&(a=!0),a?(d=-f,f=Math.sqrt(k)):(d=e,e=f,f=Math.sqrt(k/2));return new C(d/f,e/f)}function e(a,b){var c,d;for(J=a.length;0<=--J;){c=J;d=J-1;0>d&&(d=a.length-1);var e,f=t+2*r;for(e=0;eMath.abs(b.y-c.y)?[new C(b.x,1-b.z),new C(c.x,1-c.z),new C(d.x,1-d.z),new C(e.x,1-e.z)]:[new C(b.y,1-b.z),new C(c.y,1-c.z),new C(d.y,1-d.z),new C(e.y,1-e.z)]}};rc.prototype=Object.create(pa.prototype);rc.prototype.constructor=rc;jb.prototype=Object.create(H.prototype);jb.prototype.constructor=jb;Mb.prototype=Object.create(R.prototype);Mb.prototype.constructor=Mb;Nb.prototype=Object.create(H.prototype);Nb.prototype.constructor=Nb;sc.prototype=Object.create(R.prototype);sc.prototype.constructor= +sc;tc.prototype=Object.create(R.prototype);tc.prototype.constructor=tc;Ob.prototype=Object.create(H.prototype);Ob.prototype.constructor=Ob;uc.prototype=Object.create(R.prototype);uc.prototype.constructor=uc;Za.prototype=Object.create(R.prototype);Za.prototype.constructor=Za;Za.prototype.addShapeList=function(a,b){for(var c=0,d=a.length;c=e)break a;else{f=b[1];a=e)break b}d=c;c=0}}for(;c>>1,ab;)--f;++f;if(0!==e||f!==d)e>=f&&(f=Math.max(f,1),e=f-1),d=this.getValueSize(),this.times=h.AnimationUtils.arraySlice(c,e,f),this.values=h.AnimationUtils.arraySlice(this.values,e*d,f*d);return this},validate:function(){var a=!0,b=this.getValueSize();0!==b-Math.floor(b)&&(console.error("invalid value size in track",this),a=!1);var c=this.times,b=this.values, +d=c.length;0===d&&(console.error("track is empty",this),a=!1);for(var e=null,f=0;f!==d;f++){var g=c[f];if("number"===typeof g&&isNaN(g)){console.error("time is not a valid number",this,f,g);a=!1;break}if(null!==e&&e>g){console.error("out of order keys",this,f,g,e);a=!1;break}e=g}if(void 0!==b&&h.AnimationUtils.isTypedArray(b))for(f=0,c=b.length;f!==c;++f)if(d=b[f],isNaN(d)){console.error("value is not a valid number",this,f,d);a=!1;break}return a},optimize:function(){for(var a=this.times,b=this.values, +c=this.getValueSize(),d=2302===this.getInterpolation(),e=1,f=a.length-1,g=1;gl.opacity&&(l.transparent=!0);c.setTextures(k);return c.parse(l)}}()};tb.Handlers={handlers:[],add:function(a,b){this.handlers.push(a,b)},get:function(a){for(var b=this.handlers,c=0,d=b.length;cg;g++)m=x[h++],w=A[2*m],m=A[2*m+1],w=new C(w,m),2!==g&&c.faceVertexUvs[d][k].push(w),0!==g&&c.faceVertexUvs[d][k+1].push(w);p&&(p=3*x[h++],t.normal.set(y[p++],y[p++],y[p]),v.normal.copy(t.normal));if(u)for(d=0;4> +d;d++)p=3*x[h++],u=new q(y[p++],y[p++],y[p]),2!==d&&t.vertexNormals.push(u),0!==d&&v.vertexNormals.push(u);r&&(r=x[h++],r=z[r],t.color.setHex(r),v.color.setHex(r));if(b)for(d=0;4>d;d++)r=x[h++],r=z[r],2!==d&&t.vertexColors.push(new I(r)),0!==d&&v.vertexColors.push(new I(r));c.faces.push(t);c.faces.push(v)}else{t=new ka;t.a=x[h++];t.b=x[h++];t.c=x[h++];k&&(k=x[h++],t.materialIndex=k);k=c.faces.length;if(d)for(d=0;dg;g++)m=x[h++],w=A[2*m],m=A[2*m+ +1],w=new C(w,m),c.faceVertexUvs[d][k].push(w);p&&(p=3*x[h++],t.normal.set(y[p++],y[p++],y[p]));if(u)for(d=0;3>d;d++)p=3*x[h++],u=new q(y[p++],y[p++],y[p]),t.vertexNormals.push(u);r&&(r=x[h++],t.color.setHex(z[r]));if(b)for(d=0;3>d;d++)r=x[h++],t.vertexColors.push(new I(z[r]));c.faces.push(t)}})(d);(function(){var b=void 0!==a.influencesPerVertex?a.influencesPerVertex:2;if(a.skinWeights)for(var d=0,g=a.skinWeights.length;dh)g=d+1; +else if(0b&&(b=0);1=b)return b=c[a]-b,a=this.curves[a],c=a.getLength(),a.getPointAt(0===c?0:1-b/c);a++}return null},getLength:function(){var a=this.getCurveLengths();return a[a.length-1]},updateArcLengths:function(){this.needsUpdate=!0;this.cacheLengths=null;this.getLengths()},getCurveLengths:function(){if(this.cacheLengths&&this.cacheLengths.length===this.curves.length)return this.cacheLengths; +for(var a=[],b=0,c=0,d=this.curves.length;cc;)c+=b;for(;c>b;)c-=b;cb.length-2?b.length-1:c+1],b=b[c>b.length-3?b.length-1:c+2],c=h.CurveUtils.interpolate;return new C(c(d.x,e.x,f.x,b.x,a),c(d.y,e.y,f.y,b.y,a))};vb.prototype=Object.create(qa.prototype);vb.prototype.constructor=vb;vb.prototype.getPoint=function(a){var b=h.ShapeUtils.b3;return new C(b(a,this.v0.x,this.v1.x,this.v2.x,this.v3.x),b(a,this.v0.y,this.v1.y,this.v2.y,this.v3.y))};vb.prototype.getTangent=function(a){var b=h.CurveUtils.tangentCubicBezier;return(new C(b(a, +this.v0.x,this.v1.x,this.v2.x,this.v3.x),b(a,this.v0.y,this.v1.y,this.v2.y,this.v3.y))).normalize()};wb.prototype=Object.create(qa.prototype);wb.prototype.constructor=wb;wb.prototype.getPoint=function(a){var b=h.ShapeUtils.b2;return new C(b(a,this.v0.x,this.v1.x,this.v2.x),b(a,this.v0.y,this.v1.y,this.v2.y))};wb.prototype.getTangent=function(a){var b=h.CurveUtils.tangentQuadraticBezier;return(new C(b(a,this.v0.x,this.v1.x,this.v2.x),b(a,this.v0.y,this.v1.y,this.v2.y))).normalize()};var Rd=Object.assign(Object.create(Cc.prototype), +{fromPoints:function(a){this.moveTo(a[0].x,a[0].y);for(var b=1,c=a.length;bNumber.EPSILON){if(0>l&&(g=b[f],h=-h,k=b[e],l=-l),!(a.yk.y))if(a.y===g.y){if(a.x===g.x)return!0}else{e=l*(a.x-g.x)-h*(a.y-g.y);if(0===e)return!0;0>e||(d=!d)}}else if(a.y===g.y&&(k.x<=a.x&&a.x<=g.x||g.x<=a.x&&a.x<=k.x))return!0}return d}var e=h.ShapeUtils.isClockWise,f=this.subPaths;if(0===f.length)return[];if(!0===b)return c(f); +var g,k,l,n=[];if(1===f.length)return k=f[0],l=new xb,l.curves=k.curves,n.push(l),n;var r=!e(f[0].getPoints()),r=a?!r:r;l=[];var p=[],m=[],q=0,u;p[q]=void 0;m[q]=[];for(var v=0,A=f.length;v=c){var p=c++,m=b[p];d[m.uuid]=q;b[q]=m;d[n]=p;b[p]=h;h=0;for(n=f;h!==n;++h){var m=e[h],t=m[q];m[q]=m[p];m[p]=t}}}this.nCachedObjects_=c},uncache:function(a){for(var b=this._objects,c=b.length,d=this.nCachedObjects_,e=this._indicesByUUID,f=this._bindings,g=f.length,k=0,h=arguments.length;k!==h;++k){var n=arguments[k].uuid,q=e[n];if(void 0!== q)if(delete e[n],qc.parameterPositions[1]&&(this.stopWarping(),0===b?this.paused=!0:this.timeScale=b)}}return this._effectiveTimeScale=b},_updateTime:function(a){var b=this.time+a;if(0===a)return b;var c=this._clip.duration,d=this.loop,e=this._loopCount;if(2200===d)a:{if(-1===e&&(this.loopCount= 0,this._setEndings(!0,!0,!1)),b>=c)b=c;else if(0>b)b=0;else break a;this.clampWhenFinished?this.paused=!0:this.enabled=!1;this._mixer.dispatchEvent({type:"finished",action:this,direction:0>a?-1:1})}else{d=2202===d;-1===e&&(0<=a?(e=0,this._setEndings(!0,0===this.repetitions,d)):this._setEndings(0===this.repetitions,!0,d));if(b>=c||0>b){var f=Math.floor(b/c),b=b-c*f,e=e+Math.abs(f),g=this.repetitions-e;0>g?(this.clampWhenFinished?this.paused=!0:this.enabled=!1,b=0a,this._setEndings(a,!a,d)):this._setEndings(!1,!1,d),this._loopCount=e,this._mixer.dispatchEvent({type:"loop",action:this,loopDelta:f}))}if(d&&1===(e&1))return this.time=b,c-b}return this.time=b},_setEndings:function(a,b,c){var d=this._interpolantSettings;c?(d.endingStart=2401,d.endingEnd=2401):(d.endingStart=a?this.zeroSlopeAtStart?2401:2400:2402,d.endingEnd=b?this.zeroSlopeAtEnd?2401:2400:2402)},_scheduleFading:function(a,b,c){var d=this._mixer,e=d.time, -f=this._weightInterpolant;null===f&&(this._weightInterpolant=f=d._lendControlInterpolant());d=f.parameterPositions;f=f.sampleValues;d[0]=e;f[0]=b;d[1]=e+a;f[1]=c;return this}};Object.assign(Id.prototype,na.prototype,{clipAction:function(a,b){var c=b||this._root,d=c.uuid,e="string"===typeof a?sa.findByName(c,a):a,c=null!==e?e.uuid:a,f=this._actionsByClip[c],g=null;if(void 0!==f){g=f.actionByRoot[d];if(void 0!==g)return g;g=f.knownActions[0];null===e&&(e=g._clip)}if(null===e)return null;e=new Hd(this, -e,b);this._bindAction(e,g);this._addInactiveAction(e,c,d);return e},existingAction:function(a,b){var c=b||this._root,d=c.uuid,c="string"===typeof a?sa.findByName(c,a):a,c=this._actionsByClip[c?c.uuid:a];return void 0!==c?c.actionByRoot[d]||null:null},stopAllAction:function(){for(var a=this._actions,b=this._nActiveActions,c=this._bindings,d=this._nActiveBindings,e=this._nActiveBindings=this._nActiveActions=0;e!==b;++e)a[e].reset();for(e=0;e!==d;++e)c[e].useCount=0;return this},update:function(a){a*= +f=this._weightInterpolant;null===f&&(this._weightInterpolant=f=d._lendControlInterpolant());d=f.parameterPositions;f=f.sampleValues;d[0]=e;f[0]=b;d[1]=e+a;f[1]=c;return this}};Object.assign(Id.prototype,na.prototype,{clipAction:function(a,b){var c=b||this._root,d=c.uuid,e="string"===typeof a?ta.findByName(c,a):a,c=null!==e?e.uuid:a,f=this._actionsByClip[c],g=null;if(void 0!==f){g=f.actionByRoot[d];if(void 0!==g)return g;g=f.knownActions[0];null===e&&(e=g._clip)}if(null===e)return null;e=new Hd(this, +e,b);this._bindAction(e,g);this._addInactiveAction(e,c,d);return e},existingAction:function(a,b){var c=b||this._root,d=c.uuid,c="string"===typeof a?ta.findByName(c,a):a,c=this._actionsByClip[c?c.uuid:a];return void 0!==c?c.actionByRoot[d]||null:null},stopAllAction:function(){for(var a=this._actions,b=this._nActiveActions,c=this._bindings,d=this._nActiveBindings,e=this._nActiveBindings=this._nActiveActions=0;e!==b;++e)a[e].reset();for(e=0;e!==d;++e)c[e].useCount=0;return this},update:function(a){a*= this.timeScale;for(var b=this._actions,c=this._nActiveActions,d=this.time+=a,e=Math.sign(a),f=this._accuIndex^=1,g=0;g!==c;++g){var k=b[g];k.enabled&&k._update(d,a,e,f)}a=this._bindings;b=this._nActiveBindings;for(g=0;g!==b;++g)a[g].apply(f);return this},getRoot:function(){return this._root},uncacheClip:function(a){var b=this._actions;a=a.uuid;var c=this._actionsByClip,d=c[a];if(void 0!==d){for(var d=d.knownActions,e=0,f=d.length;e!==f;++e){var g=d[e];this._deactivateAction(g);var k=g._cacheIndex, h=b[b.length-1];g._cacheIndex=null;g._byClipCacheIndex=null;h._cacheIndex=k;b[k]=h;b.pop();this._removeInactiveBindingsForAction(g)}delete c[a]}},uncacheRoot:function(a){a=a.uuid;var b=this._actionsByClip,c;for(c in b){var d=b[c].actionByRoot[a];void 0!==d&&(this._deactivateAction(d),this._removeInactiveAction(d))}c=this._bindingsByRootAndName[a];if(void 0!==c)for(var e in c)a=c[e],a.restoreOriginalState(),this._removeInactiveBinding(a)},uncacheAction:function(a,b){var c=this.existingAction(a,b); null!==c&&(this._deactivateAction(c),this._removeInactiveAction(c))}});Object.assign(Id.prototype,{_bindAction:function(a,b){var c=a._localRoot||this._root,d=a._clip.tracks,e=d.length,f=a._propertyBindings,g=a._interpolants,k=c.uuid,h=this._bindingsByRootAndName,n=h[k];void 0===n&&(n={},h[k]=n);for(h=0;h!==e;++h){var q=d[h],p=q.name,m=n[p];if(void 0===m){m=f[h];if(void 0!==m){null===m._cacheIndex&&(++m.referenceCount,this._addInactiveBinding(m,k,p));continue}m=new hd(ha.create(c,p,b&&b._propertyBindings[h].binding.parsedPath), @@ -742,13 +742,13 @@ get inUse(){return a._nActiveActions}},bindings:{get total(){return a._bindings. f.knownActions,a._byClipCacheIndex=b.length,b.push(a));a._cacheIndex=d.length;d.push(a);f.actionByRoot[c]=a},_removeInactiveAction:function(a){var b=this._actions,c=b[b.length-1],d=a._cacheIndex;c._cacheIndex=d;b[d]=c;b.pop();a._cacheIndex=null;var c=a._clip.uuid,d=this._actionsByClip,e=d[c],f=e.knownActions,g=f[f.length-1],k=a._byClipCacheIndex;g._byClipCacheIndex=k;f[k]=g;f.pop();a._byClipCacheIndex=null;delete e.actionByRoot[(b._localRoot||this._root).uuid];0===f.length&&delete d[c];this._removeInactiveBindingsForAction(a)}, _removeInactiveBindingsForAction:function(a){a=a._propertyBindings;for(var b=0,c=a.length;b!==c;++b){var d=a[b];0===--d.referenceCount&&this._removeInactiveBinding(d)}},_lendAction:function(a){var b=this._actions,c=a._cacheIndex,d=this._nActiveActions++,e=b[d];a._cacheIndex=d;b[d]=a;e._cacheIndex=c;b[c]=e},_takeBackAction:function(a){var b=this._actions,c=a._cacheIndex,d=--this._nActiveActions,e=b[d];a._cacheIndex=d;b[d]=a;e._cacheIndex=c;b[c]=e},_addInactiveBinding:function(a,b,c){var d=this._bindingsByRootAndName, e=d[b],f=this._bindings;void 0===e&&(e={},d[b]=e);e[c]=a;a._cacheIndex=f.length;f.push(a)},_removeInactiveBinding:function(a){var b=this._bindings,c=a.binding,d=c.rootNode.uuid,c=c.path,e=this._bindingsByRootAndName,f=e[d],g=b[b.length-1];a=a._cacheIndex;g._cacheIndex=a;b[a]=g;b.pop();delete f[c];a:{for(var k in f)break a;delete e[d]}},_lendBinding:function(a){var b=this._bindings,c=a._cacheIndex,d=this._nActiveBindings++,e=b[d];a._cacheIndex=d;b[d]=a;e._cacheIndex=c;b[c]=e},_takeBackBinding:function(a){var b= -this._bindings,c=a._cacheIndex,d=--this._nActiveBindings,e=b[d];a._cacheIndex=d;b[d]=a;e._cacheIndex=c;b[c]=e},_lendControlInterpolant:function(){var a=this._controlInterpolants,b=this._nActiveControlInterpolants++,c=a[b];void 0===c&&(c=new mc(new Float32Array(2),new Float32Array(2),1,this._controlInterpolantsResultBuffer),c.__cacheIndex=b,a[b]=c);return c},_takeBackControlInterpolant:function(a){var b=this._controlInterpolants,c=a.__cacheIndex,d=--this._nActiveControlInterpolants,e=b[d];a.__cacheIndex= -d;b[d]=a;e.__cacheIndex=c;b[c]=e},_controlInterpolantsResultBuffer:new Float32Array(1)});Jd.prototype={constructor:Jd,onUpdate:function(a){this.dynamic=!0;this.onUpdateCallback=a;return this}};wb.prototype=Object.create(H.prototype);wb.prototype.constructor=wb;wb.prototype.isInstancedBufferGeometry=!0;wb.prototype.addGroup=function(a,b,c){this.groups.push({start:a,count:b,instances:c})};wb.prototype.copy=function(a){var b=a.index;null!==b&&this.setIndex(b.clone());var b=a.attributes,c;for(c in b)this.addAttribute(c, +this._bindings,c=a._cacheIndex,d=--this._nActiveBindings,e=b[d];a._cacheIndex=d;b[d]=a;e._cacheIndex=c;b[c]=e},_lendControlInterpolant:function(){var a=this._controlInterpolants,b=this._nActiveControlInterpolants++,c=a[b];void 0===c&&(c=new Ac(new Float32Array(2),new Float32Array(2),1,this._controlInterpolantsResultBuffer),c.__cacheIndex=b,a[b]=c);return c},_takeBackControlInterpolant:function(a){var b=this._controlInterpolants,c=a.__cacheIndex,d=--this._nActiveControlInterpolants,e=b[d];a.__cacheIndex= +d;b[d]=a;e.__cacheIndex=c;b[c]=e},_controlInterpolantsResultBuffer:new Float32Array(1)});Jd.prototype={constructor:Jd,onUpdate:function(a){this.dynamic=!0;this.onUpdateCallback=a;return this}};yb.prototype=Object.create(H.prototype);yb.prototype.constructor=yb;yb.prototype.isInstancedBufferGeometry=!0;yb.prototype.addGroup=function(a,b,c){this.groups.push({start:a,count:b,instances:c})};yb.prototype.copy=function(a){var b=a.index;null!==b&&this.setIndex(b.clone());var b=a.attributes,c;for(c in b)this.addAttribute(c, b[c].clone());a=a.groups;c=0;for(b=a.length;cc.y?this.quaternion.set(1,0,0,0):(a.set(c.z,0,-c.x).normalize(),b=Math.acos(c.y),this.quaternion.setFromAxisAngle(a,b))}}();xb.prototype.setLength=function(a,b,c){void 0===b&&(b=.2*a);void 0===c&&(c=.2*b);this.line.scale.set(1,Math.max(0,a-b), -1);this.line.updateMatrix();this.cone.scale.set(c,b,c);this.cone.position.y=a;this.cone.updateMatrix()};xb.prototype.setColor=function(a){this.line.material.color.copy(a);this.cone.material.color.copy(a)};kd.prototype=Object.create(ba.prototype);kd.prototype.constructor=kd;h.CatmullRomCurve3=function(){function a(){}var b=new q,c=new a,d=new a,e=new a;a.prototype.init=function(a,b,c,d){this.c0=a;this.c1=c;this.c2=-3*a+3*b-2*c-d;this.c3=2*a-2*b+c+d};a.prototype.initNonuniformCatmullRom=function(a, +new la([0,0,0,0,1,0],3));var qe=new $a(0,.5,1,5,1);qe.translate(0,-.5,0);zb.prototype=Object.create(D.prototype);zb.prototype.constructor=zb;zb.prototype.setDirection=function(){var a=new q,b;return function(c){.99999c.y?this.quaternion.set(1,0,0,0):(a.set(c.z,0,-c.x).normalize(),b=Math.acos(c.y),this.quaternion.setFromAxisAngle(a,b))}}();zb.prototype.setLength=function(a,b,c){void 0===b&&(b=.2*a);void 0===c&&(c=.2*b);this.line.scale.set(1,Math.max(0,a-b), +1);this.line.updateMatrix();this.cone.scale.set(c,b,c);this.cone.position.y=a;this.cone.updateMatrix()};zb.prototype.setColor=function(a){this.line.material.color.copy(a);this.cone.material.color.copy(a)};kd.prototype=Object.create(ba.prototype);kd.prototype.constructor=kd;h.CatmullRomCurve3=function(){function a(){}var b=new q,c=new a,d=new a,e=new a;a.prototype.init=function(a,b,c,d){this.c0=a;this.c1=c;this.c2=-3*a+3*b-2*c-d;this.c3=2*a-2*b+c+d};a.prototype.initNonuniformCatmullRom=function(a, b,c,d,e,h,p){this.init(b,c,((b-a)/e-(c-a)/(e+h)+(c-b)/h)*h,((c-b)/h-(d-b)/(h+p)+(d-c)/p)*h)};a.prototype.initCatmullRom=function(a,b,c,d,e){this.init(b,c,e*(c-a),e*(d-b))};a.prototype.calc=function(a){var b=a*a;return this.c0+this.c1*a+this.c2*b+this.c3*b*a};return qa.create(function(a){this.points=a||[];this.closed=!1},function(a){var g=this.points,h,l;l=g.length;2>l&&console.log("duh, you need at least 2 points");a*=l-(this.closed?0:1);h=Math.floor(a);a-=h;this.closed?h+=0h&&(h=1);1E-4>l&&(l=h);1E-4>m&&(m=h);c.initNonuniformCatmullRom(n.x, r.x,p.x,g.x,l,h,m);d.initNonuniformCatmullRom(n.y,r.y,p.y,g.y,l,h,m);e.initNonuniformCatmullRom(n.z,r.z,p.z,g.z,l,h,m)}else"catmullrom"===this.type&&(l=void 0!==this.tension?this.tension:.5,c.initCatmullRom(n.x,r.x,p.x,g.x,l),d.initCatmullRom(n.y,r.y,p.y,g.y,l),e.initCatmullRom(n.z,r.z,p.z,g.z,l));return new q(c.calc(a),d.calc(a),e.calc(a))})}();re.prototype=Object.create(h.CatmullRomCurve3.prototype);var qf=qa.create(function(a){console.warn("THREE.SplineCurve3 will be deprecated. Please use THREE.CatmullRomCurve3"); this.points=void 0===a?[]:a},function(a){var b=this.points;a*=b.length-1;var c=Math.floor(a);a-=c;var d=b[0==c?c:c-1],e=b[c],f=b[c>b.length-2?b.length-1:c+1],b=b[c>b.length-3?b.length-1:c+2],c=h.CurveUtils.interpolate;return new q(c(d.x,e.x,f.x,b.x,a),c(d.y,e.y,f.y,b.y,a),c(d.z,e.z,f.z,b.z,a))});h.CubicBezierCurve3=qa.create(function(a,b,c,d){this.v0=a;this.v1=b;this.v2=c;this.v3=d},function(a){var b=h.ShapeUtils.b3;return new q(b(a,this.v0.x,this.v1.x,this.v2.x,this.v3.x),b(a,this.v0.y,this.v1.y, this.v2.y,this.v3.y),b(a,this.v0.z,this.v1.z,this.v2.z,this.v3.z))});h.QuadraticBezierCurve3=qa.create(function(a,b,c){this.v0=a;this.v1=b;this.v2=c},function(a){var b=h.ShapeUtils.b2;return new q(b(a,this.v0.x,this.v1.x,this.v2.x),b(a,this.v0.y,this.v1.y,this.v2.y),b(a,this.v0.z,this.v1.z,this.v2.z))});h.LineCurve3=qa.create(function(a,b){this.v1=a;this.v2=b},function(a){if(1===a)return this.v2.clone();var b=new q;b.subVectors(this.v2,this.v1);b.multiplyScalar(a);b.add(this.v1);return b});ld.prototype= -Object.create(Sa.prototype);ld.prototype.constructor=ld;h.SceneUtils={createMultiMaterialObject:function(a,b){for(var c=new ic,d=0,e=b.length;dthis.points.length-2?this.points.length-1:f+1; c[3]=f>this.points.length-3?this.points.length-1:f+2;n=this.points[c[0]];r=this.points[c[1]];p=this.points[c[2]];m=this.points[c[3]];h=g*g;l=g*h;d.x=b(n.x,r.x,p.x,m.x,g,h,l);d.y=b(n.y,r.y,p.y,m.y,g,h,l);d.z=b(n.z,r.z,p.z,m.z,g,h,l);return d};this.getControlPointsArray=function(){var a,b,c=this.points.length,d=[];for(a=0;a