Browse Source

Merge branch 'dev' into triangulation

Juergen Ahting 11 years ago
parent
commit
b8df097924

+ 1 - 1
README.md

@@ -62,7 +62,7 @@ This code creates a scene, then creates a camera, adds the camera and cube to th
 
 
 </script>
 </script>
 ```
 ```
-If everything went well you should see [this](http://jsfiddle.net/ksRyQ/).
+If everything went well you should see [this](http://jsfiddle.net/HptjJ/).
 
 
 ### Change log ###
 ### Change log ###
 
 

+ 85 - 410
build/three.js

@@ -6242,7 +6242,11 @@ THREE.Math = {
 
 
 		};
 		};
 
 
-	}()
+	}(),
+
+	isPowerOfTwo: function ( value ) {
+		return ( value & ( value - 1 ) ) === 0 && value !== 0;
+	}
 
 
 };
 };
 
 
@@ -9224,7 +9228,7 @@ THREE.BufferGeometry.prototype = {
 
 
 	constructor: THREE.BufferGeometry,
 	constructor: THREE.BufferGeometry,
 
 
-	addAttribute: function( name, type, numItems, itemSize ) {
+	addAttribute: function ( name, type, numItems, itemSize ) {
 
 
 		this.attributes[ name ] = {
 		this.attributes[ name ] = {
 
 
@@ -22124,6 +22128,45 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 	};
 	};
 
 
+	// used by renderBufferDirect for THREE.Line
+	function setupLinesVertexAttributes( material, programAttributes, geometryAttributes, startIndex ) {
+
+		var attributeItem, attributeName, attributePointer, attributeSize;
+
+		for ( attributeName in programAttributes ) {
+
+			attributePointer = programAttributes[ attributeName ];
+			attributeItem = geometryAttributes[ attributeName ];
+			
+			if ( attributePointer >= 0 ) {
+
+				if ( attributeItem ) {
+
+					attributeSize = attributeItem.itemSize;
+					_gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer );
+					enableAttribute( attributePointer );
+					_gl.vertexAttribPointer( attributePointer, attributeSize, _gl.FLOAT, false, 0, startIndex * attributeSize * 4 ); // 4 bytes per Float32
+
+				} else if ( material.defaultAttributeValues ) {
+
+					if ( material.defaultAttributeValues[ attributeName ].length === 2 ) {
+
+						_gl.vertexAttrib2fv( attributePointer, material.defaultAttributeValues[ attributeName ] );
+
+					} else if ( material.defaultAttributeValues[ attributeName ].length === 3 ) {
+
+						_gl.vertexAttrib3fv( attributePointer, material.defaultAttributeValues[ attributeName ] );
+
+					}
+
+				}
+
+			}
+
+		}
+
+	}
+
 	function setDirectBuffers ( geometry, hint, dispose ) {
 	function setDirectBuffers ( geometry, hint, dispose ) {
 
 
 		var attributes = geometry.attributes;
 		var attributes = geometry.attributes;
@@ -22463,54 +22506,65 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 		} else if ( object instanceof THREE.Line ) {
 		} else if ( object instanceof THREE.Line ) {
 
 
-			if ( updateBuffers ) {
+			var primitives = ( object.type === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES;
 
 
-				for ( attributeName in programAttributes ) {
+			setLineWidth( material.linewidth );
 
 
-					attributePointer = programAttributes[ attributeName ];
-					attributeItem = geometryAttributes[ attributeName ];
-					
-					if ( attributePointer >= 0 ) {
+			var index = geometryAttributes[ "index" ];
 
 
-						if ( attributeItem ) {
+			// indexed lines
+			
+			if ( index ) {
 
 
-							attributeSize = attributeItem.itemSize;
-							_gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer );
-							enableAttribute( attributePointer );
-							_gl.vertexAttribPointer( attributePointer, attributeSize, _gl.FLOAT, false, 0, 0 );
+				var offsets = geometry.offsets;
 
 
-						} else if ( material.defaultAttributeValues && material.defaultAttributeValues[ attributeName ] ) {
+				// if there is more than 1 chunk
+				// must set attribute pointers to use new offsets for each chunk
+				// even if geometry and materials didn't change
 
 
-							if ( material.defaultAttributeValues[ attributeName ].length === 2 ) {
+				if ( offsets.length > 1 ) updateBuffers = true;
 
 
-								_gl.vertexAttrib2fv( attributePointer, material.defaultAttributeValues[ attributeName ] );
+				for ( var i = 0, il = offsets.length; i < il; i ++ ) {
 
 
-							} else if ( material.defaultAttributeValues[ attributeName ].length === 3 ) {
+					var startIndex = offsets[ i ].index;
 
 
-								_gl.vertexAttrib3fv( attributePointer, material.defaultAttributeValues[ attributeName ] );
+					if ( updateBuffers ) {
 
 
-							}
+						setupLinesVertexAttributes(material, programAttributes, geometryAttributes, startIndex);
 
 
-						}
+						// indices
+						_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer );
 
 
 					}
 					}
 
 
+					// render indexed lines
+
+					_gl.drawElements( _gl.LINES, offsets[ i ].count, _gl.UNSIGNED_SHORT, offsets[ i ].start * 2 ); // 2 bytes per Uint16Array
+
+					_this.info.render.calls ++;
+					_this.info.render.vertices += offsets[ i ].count; // not really true, here vertices can be shared
+
 				}
 				}
 
 
 			}
 			}
 
 
-			// render lines
+			// non-indexed lines
 
 
-			var primitives = ( object.type === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES;
+			else {
 
 
-			setLineWidth( material.linewidth );
+				if ( updateBuffers ) {
 
 
-			var position = geometryAttributes[ "position" ];
+					setupLinesVertexAttributes(material, programAttributes, geometryAttributes, 0);
+				}
+
+				var position = geometryAttributes[ "position" ];
+
+				_gl.drawArrays( primitives, 0, position.numItems / 3 );
+				_this.info.render.calls ++;
+				_this.info.render.points += position.numItems;
+			}
 
 
-			_gl.drawArrays( primitives, 0, position.numItems / 3 );
 
 
-			_this.info.render.calls ++;
-			_this.info.render.points += position.numItems;
 
 
 		}
 		}
 
 
@@ -25564,13 +25618,6 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 	// Textures
 	// Textures
 
 
-
-	function isPowerOfTwo ( value ) {
-
-		return ( value & ( value - 1 ) ) === 0;
-
-	};
-
 	function setTextureParameters ( textureType, texture, isImagePowerOfTwo ) {
 	function setTextureParameters ( textureType, texture, isImagePowerOfTwo ) {
 
 
 		if ( isImagePowerOfTwo ) {
 		if ( isImagePowerOfTwo ) {
@@ -25628,7 +25675,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 			_gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment );
 			_gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment );
 
 
 			var image = texture.image,
 			var image = texture.image,
-			isImagePowerOfTwo = isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ),
+			isImagePowerOfTwo = THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height ),
 			glFormat = paramThreeToGL( texture.format ),
 			glFormat = paramThreeToGL( texture.format ),
 			glType = paramThreeToGL( texture.type );
 			glType = paramThreeToGL( texture.type );
 
 
@@ -25778,7 +25825,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 				}
 				}
 
 
 				var image = cubeImage[ 0 ],
 				var image = cubeImage[ 0 ],
-				isImagePowerOfTwo = isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ),
+				isImagePowerOfTwo = THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height ),
 				glFormat = paramThreeToGL( texture.format ),
 				glFormat = paramThreeToGL( texture.format ),
 				glType = paramThreeToGL( texture.type );
 				glType = paramThreeToGL( texture.type );
 
 
@@ -25891,7 +25938,7 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 			// Setup texture, create render and frame buffers
 			// Setup texture, create render and frame buffers
 
 
-			var isTargetPowerOfTwo = isPowerOfTwo( renderTarget.width ) && isPowerOfTwo( renderTarget.height ),
+			var isTargetPowerOfTwo = THREE.Math.isPowerOfTwo( renderTarget.width ) && THREE.Math.isPowerOfTwo( renderTarget.height ),
 				glFormat = paramThreeToGL( renderTarget.format ),
 				glFormat = paramThreeToGL( renderTarget.format ),
 				glType = paramThreeToGL( renderTarget.type );
 				glType = paramThreeToGL( renderTarget.type );
 
 
@@ -27993,10 +28040,6 @@ THREE.FontUtils.generateShapes = function( text, parameters ) {
 			px = contour[ verts[ p ] ].x
 			px = contour[ verts[ p ] ].x
 			py = contour[ verts[ p ] ].y
 			py = contour[ verts[ p ] ].y
 
 
-			if ( ( (px === ax) && (py === ay) ) ||
-				 ( (px === bx) && (py === by) ) ||
-				 ( (px === cx) && (py === cy) ) )	continue;
-
 			apx = px - ax;  apy = py - ay;
 			apx = px - ax;  apy = py - ay;
 			bpx = px - bx;  bpy = py - by;
 			bpx = px - bx;  bpy = py - by;
 			cpx = px - cx;  cpy = py - cy;
 			cpx = px - cx;  cpy = py - cy;
@@ -29632,7 +29675,7 @@ THREE.Shape.Utils = {
 
 
 	},
 	},
 
 
-	triangulateShape_OLD: function ( contour, holes ) {
+	triangulateShape: function ( contour, holes ) {
 
 
 		var shapeWithoutHoles = THREE.Shape.Utils.removeHoles( contour, holes );
 		var shapeWithoutHoles = THREE.Shape.Utils.removeHoles( contour, holes );
 
 
@@ -29716,374 +29759,6 @@ THREE.Shape.Utils = {
 
 
 	}, // end triangulate shapes
 	}, // end triangulate shapes
 
 
-	/*
-	 *		Modified Triangulation.
-	 *
-	 *		basically rewritten 'removeHoles':
-	 *		- doesn't cut out an area anymore, but slices from shape to hole by adding two edges
-	 *		  - ATTENTION: this requires small change to 'THREE.FontUtils.snip' to account for duplicate coordinates
-	 *		- checks whether such a cut line lies inside the shape doesn't intersect any other edge (shape and holes)
-	 */
-	triangulateShape: function ( contour, holes ) {
-
-		function point_in_segment_2D( inSegPt1, inSegPt2, inOtherPt ) {
-			// inOtherPt needs to be colinear to the inSegment
-			if ( inSegPt1.x != inSegPt2.x ) {
-				if ( inSegPt1.x < inSegPt2.x ) {
-					return	( ( inSegPt1.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt2.x ) );
-				} else {
-					return	( ( inSegPt2.x <= inOtherPt.x ) && ( inOtherPt.x <= inSegPt1.x ) );
-				}
-			} else {
-				if ( inSegPt1.y < inSegPt2.y ) {
-					return	( ( inSegPt1.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt2.y ) );
-				} else {
-					return	( ( inSegPt2.y <= inOtherPt.y ) && ( inOtherPt.y <= inSegPt1.y ) );
-				}
-			}
-		}
-
-		function intersect_segments_2D( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1, inSeg2Pt2, inExcludeAdjacentSegs ) {
-			var EPSILON = 0.0000000001;
-
-			var seg1dx = inSeg1Pt2.x - inSeg1Pt1.x,   seg1dy = inSeg1Pt2.y - inSeg1Pt1.y;
-			var seg2dx = inSeg2Pt2.x - inSeg2Pt1.x,   seg2dy = inSeg2Pt2.y - inSeg2Pt1.y;
-
-			var seg1seg2dx = inSeg1Pt1.x - inSeg2Pt1.x;
-			var seg1seg2dy = inSeg1Pt1.y - inSeg2Pt1.y;
-
-			var limit		= seg1dy * seg2dx - seg1dx * seg2dy;
-			var perpSeg1	= seg1dy * seg1seg2dx - seg1dx * seg1seg2dy;
-
-			if ( Math.abs(limit) > EPSILON ) {			// not parallel
-
-				var perpSeg2;
-				if ( limit > 0 ) {
-					if ( ( perpSeg1 < 0 ) || ( perpSeg1 > limit ) ) 		return [];
-					perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy;
-					if ( ( perpSeg2 < 0 ) || ( perpSeg2 > limit ) ) 		return [];
-				} else {
-					if ( ( perpSeg1 > 0 ) || ( perpSeg1 < limit ) ) 		return [];
-					perpSeg2 = seg2dy * seg1seg2dx - seg2dx * seg1seg2dy;
-					if ( ( perpSeg2 > 0 ) || ( perpSeg2 < limit ) ) 		return [];
-				}
-
-				// intersection at endpoint ?
-				if ( perpSeg2 == 0 ) {
-					if ( ( inExcludeAdjacentSegs ) &&
-						 ( ( perpSeg1 == 0 ) || ( perpSeg1 == limit ) ) )		return [];
-					return  [ inSeg1Pt1 ];
-				}
-				if ( perpSeg2 == limit ) {
-					if ( ( inExcludeAdjacentSegs ) &&
-						 ( ( perpSeg1 == 0 ) || ( perpSeg1 == limit ) ) )		return [];
-					return  [ inSeg1Pt2 ];
-				}
-
-				// return real intersection point
-				var factorSeg1 = perpSeg2 / limit;
-				return	[ { x: inSeg1Pt1.x + factorSeg1 * seg1dx,
-							y: inSeg1Pt1.y + factorSeg1 * seg1dy } ];
-
-			} else {		// parallel or colinear
-				if ( perpSeg1 != 0 )							 			return [];
-
-				// they are collinear or degenerate
-				var seg1Pt = ( (seg1dx == 0) && (seg1dy == 0) );	// segment1 ist just a point?
-				var seg2Pt = ( (seg2dx == 0) && (seg2dy == 0) );	// segment2 ist just a point?
-				// both segments are points
-				if ( seg1Pt && seg2Pt ) {
-					if ( (inSeg1Pt1.x != inSeg2Pt1.x) ||
-						 (inSeg1Pt1.y != inSeg2Pt1.y) )		return [];   	// they are distinct  points
-					return  [ inSeg1Pt1 ];                 					// they are the same point
-				}
-				// segment#1  is a single point
-				if ( seg1Pt ) {
-					if (! point_in_segment_2D( inSeg2Pt1, inSeg2Pt2, inSeg1Pt1 ) )		return [];		// but not in segment#2
-					return  [ inSeg1Pt1 ];
-				}
-				// segment#2  is a single point
-				if ( seg2Pt ) {
-					if (! point_in_segment_2D( inSeg1Pt1, inSeg1Pt2, inSeg2Pt1 ) )		return [];		// but not in segment#1
-					return  [ inSeg2Pt1 ];
-				}
-
-				// they are collinear segments, which might overlap
-				var seg1min, seg1max, seg1minVal, seg1maxVal;
-				var seg2min, seg2max, seg2minVal, seg2maxVal;
-				if (seg1dx != 0) {		// the segments are NOT on a vertical line
-					if ( inSeg1Pt1.x < inSeg1Pt2.x ) {
-						seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.x;
-						seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.x;
-					} else {
-						seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.x;
-						seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.x;
-					}
-					if ( inSeg2Pt1.x < inSeg2Pt2.x ) {
-						seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.x;
-						seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.x;
-					} else {
-						seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.x;
-						seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.x;
-					}
-				} else {				// the segments are on a vertical line
-					if ( inSeg1Pt1.y < inSeg1Pt2.y ) {
-						seg1min = inSeg1Pt1; seg1minVal = inSeg1Pt1.y;
-						seg1max = inSeg1Pt2; seg1maxVal = inSeg1Pt2.y;
-					} else {
-						seg1min = inSeg1Pt2; seg1minVal = inSeg1Pt2.y;
-						seg1max = inSeg1Pt1; seg1maxVal = inSeg1Pt1.y;
-					}
-					if ( inSeg2Pt1.y < inSeg2Pt2.y ) {
-						seg2min = inSeg2Pt1; seg2minVal = inSeg2Pt1.y;
-						seg2max = inSeg2Pt2; seg2maxVal = inSeg2Pt2.y;
-					} else {
-						seg2min = inSeg2Pt2; seg2minVal = inSeg2Pt2.y;
-						seg2max = inSeg2Pt1; seg2maxVal = inSeg2Pt1.y;
-					}
-				}
-				if ( seg1minVal <= seg2minVal ) {
-					if ( seg1maxVal <  seg2minVal )	return [];
-					if ( seg1maxVal == seg2minVal )	{
-						if ( inExcludeAdjacentSegs )		return [];
-						return [ seg2min ];
-					}
-					if ( seg1maxVal <= seg2maxVal )	return [ seg2min, seg1max ];
-					return	[ seg2min, seg2max ];
-				} else {
-					if ( seg1minVal >  seg2maxVal )	return [];
-					if ( seg1minVal == seg2maxVal )	{
-						if ( inExcludeAdjacentSegs )		return [];
-						return [ seg1min ];
-					}
-					if ( seg1maxVal <= seg2maxVal )	return [ seg1min, seg1max ];
-					return	[ seg1min, seg2max ];
-				}
-			}
-		}
-
-		function isPointInsideAngle( inVertex, inLegFromPt, inLegToPt, inOtherPt ) {
-			// The order of legs is important
-
-			var EPSILON = 0.0000000001;
-
-			// translation of all points, so that Vertex is at (0,0)
-			var legFromPtX	= inLegFromPt.x - inVertex.x,  legFromPtY	= inLegFromPt.y - inVertex.y;
-			var legToPtX	= inLegToPt.x	- inVertex.x,  legToPtY		= inLegToPt.y	- inVertex.y;
-			var otherPtX	= inOtherPt.x	- inVertex.x,  otherPtY		= inOtherPt.y	- inVertex.y;
-
-			// main angle >0: < 180 deg.; 0: 180 deg.; <0: > 180 deg.
-			var from2toAngle	= legFromPtX * legToPtY - legFromPtY * legToPtX;
-			var from2otherAngle	= legFromPtX * otherPtY - legFromPtY * otherPtX;
-
-			if ( Math.abs(from2toAngle) > EPSILON ) {			// angle != 180 deg.
-
-				var other2toAngle		= otherPtX * legToPtY - otherPtY * legToPtX;
-				// console.log( "from2to: " + from2toAngle + ", from2other: " + from2otherAngle + ", other2to: " + other2toAngle );
-
-				if ( from2toAngle > 0 ) {				// main angle < 180 deg.
-					return	( ( from2otherAngle >= 0 ) && ( other2toAngle >= 0 ) );
-				} else {								// main angle > 180 deg.
-					return	( ( from2otherAngle >= 0 ) || ( other2toAngle >= 0 ) );
-				}
-			} else {										// angle == 180 deg.
-				// console.log( "from2to: 180 deg., from2other: " + from2otherAngle  );
-				return	( from2otherAngle > 0 );
-			}
-		}
-
-
-		function removeHoles( contour, holes ) {
-
-			var shape = contour.concat(); // work on this shape
-			var hole;
-
-			function isCutLineInsideAngles( inShapeIdx, inHoleIdx ) {
-				// Check if hole point lies within angle around shape point
-				var lastShapeIdx = shape.length - 1;
-
-				var prevShapeIdx = inShapeIdx - 1;
-				if ( prevShapeIdx < 0 )			prevShapeIdx = lastShapeIdx;
-
-				var nextShapeIdx = inShapeIdx + 1;
-				if ( nextShapeIdx > lastShapeIdx )	nextShapeIdx = 0;
-
-				var insideAngle = isPointInsideAngle( shape[inShapeIdx], shape[ prevShapeIdx ], shape[ nextShapeIdx ], hole[inHoleIdx] );
-				if (! insideAngle ) {
-					// console.log( "Vertex (Shape): " + inShapeIdx + ", Point: " + hole[inHoleIdx].x + "/" + hole[inHoleIdx].y );
-					return	false;
-				}
-
-				// Check if shape point lies within angle around hole point
-				var lastHoleIdx = hole.length - 1;
-
-				var prevHoleIdx = inHoleIdx - 1;
-				if ( prevHoleIdx < 0 )			prevHoleIdx = lastHoleIdx;
-
-				var nextHoleIdx = inHoleIdx + 1;
-				if ( nextHoleIdx > lastHoleIdx )	nextHoleIdx = 0;
-
-				insideAngle = isPointInsideAngle( hole[inHoleIdx], hole[ prevHoleIdx ], hole[ nextHoleIdx ], shape[inShapeIdx] );
-				if (! insideAngle ) {
-					// console.log( "Vertex (Hole): " + inHoleIdx + ", Point: " + shape[inShapeIdx].x + "/" + shape[inShapeIdx].y );
-					return	false;
-				}
-
-				return	true;
-			}
-
-			function intersectsShapeEdge( inShapePt, inHolePt ) {
-				// checks for intersections with shape edges
-				var sIdx, nextIdx, intersection;
-				for ( sIdx = 0; sIdx < shape.length; sIdx++ ) {
-					nextIdx = sIdx+1; nextIdx %= shape.length;
-					intersection = intersect_segments_2D( inShapePt, inHolePt, shape[sIdx], shape[nextIdx], true );
-					if ( intersection.length > 0 )		return	true;
-				}
-
-				return	false;
-			}
-
-			var indepHoles = [];
-
-			function intersectsHoleEdge( inShapePt, inHolePt ) {
-				// checks for intersections with hole edges
-				var ihIdx, chkHole,
-					hIdx, nextIdx, intersection;
-				for ( ihIdx = 0; ihIdx < indepHoles.length; ihIdx++ ) {
-					chkHole = holes[indepHoles[ihIdx]];
-					for ( hIdx = 0; hIdx < chkHole.length; hIdx++ ) {
-						nextIdx = hIdx+1; nextIdx %= chkHole.length;
-						intersection = intersect_segments_2D( inShapePt, inHolePt, chkHole[hIdx], chkHole[nextIdx], true );
-						if ( intersection.length > 0 )		return	true;
-					}
-				}
-				return	false;
-			}
-
-			var holeIndex, shapeIndex,
-				shapePt, h, h2, holePt,
-				holeIdx, cutKey, failedCuts = [],
-				tmpShape1, tmpShape2,
-				tmpHole1, tmpHole2;
-
-			for (h in holes) { indepHoles.push( h ); }
-
-			var counter = indepHoles.length * 2;
-			while ( indepHoles.length > 0 ) {
-				counter --;
-				if ( counter < 0 ) {
-					console.log( "Infinite Loop! Holes left:" + indepHoles.length + ", Probably Hole outside Shape!" );
-					break;
-				}
-
-				// search for shape-vertex and hole-vertex,
-				// which can be connected without intersections
-				for ( shapeIndex = 0; shapeIndex < shape.length; shapeIndex++ ) {
-
-					shapePt = shape[ shapeIndex ];
-					holeIndex	= -1;
-
-					// search for hole which can be reached without intersections
-					for ( h = 0; h < indepHoles.length; h++ ) {
-						holeIdx = indepHoles[h];
-
-						// prevent multiple checks
-						cutKey = shapePt.x + ":" + shapePt.y + ":" + holeIdx;
-						if ( failedCuts[cutKey] !== undefined )			continue;
-
-						hole = holes[holeIdx];
-						for ( h2 = 0; h2 < hole.length; h2 ++ ) {
-							holePt = hole[ h2 ];
-							if (! isCutLineInsideAngles( shapeIndex, h2 ) )		continue;
-							if ( intersectsShapeEdge( shapePt, holePt ) )		continue;
-							if ( intersectsHoleEdge( shapePt, holePt ) )		continue;
-
-							holeIndex = h2;
-							indepHoles.splice(h,1);
-
-							tmpShape1 = shape.slice( 0, shapeIndex+1 );
-							tmpShape2 = shape.slice( shapeIndex );
-							tmpHole1 = hole.slice( holeIndex );
-							tmpHole2 = hole.slice( 0, holeIndex+1 );
-
-							shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 );
-
-							// Debug only, to show the selected cuts
-							// glob_CutLines.push( [ shapePt, holePt ] );
-
-							break;
-						}
-						if ( holeIndex >= 0 )	break;		// hole-vertex found
-
-						failedCuts[cutKey] = true;			// remember failure
-					}
-					if ( holeIndex >= 0 )	break;		// hole-vertex found
-				}
-			}
-
-			return shape; 			/* shape with no holes */
-		}
-
-
-		var i, il, f, face,
-			key, index,
-			allPointsMap = {};
-
-		// To maintain reference to old shape, one must match coordinates, or offset the indices from original arrays. It's probably easier to do the first.
-
-		var allpoints = contour.concat();
-		for (var h in holes) { Array.prototype.push.apply( allpoints, holes[h] ); }
-		//console.log( "allpoints",allpoints, allpoints.length );
-
-		// prepare all points map
-
-		for ( i = 0, il = allpoints.length; i < il; i ++ ) {
-
-			key = allpoints[ i ].x + ":" + allpoints[ i ].y;
-
-			if ( allPointsMap[ key ] !== undefined ) {
-
-				console.log( "Duplicate point", key );
-
-			}
-
-			allPointsMap[ key ] = i;
-
-		}
-
-		// remove holes by cutting paths to holes and adding them to the shape
-		var shapeWithoutHoles = removeHoles( contour, holes );
-
-		var triangles = THREE.FontUtils.Triangulate( shapeWithoutHoles, false ); // True returns indices for points of spooled shape
-		//console.log( "triangles",triangles, triangles.length );
-
-		// check all face vertices against all points map
-
-		for ( i = 0, il = triangles.length; i < il; i ++ ) {
-
-			face = triangles[ i ];
-
-			for ( f = 0; f < 3; f ++ ) {
-
-				key = face[ f ].x + ":" + face[ f ].y;
-
-				index = allPointsMap[ key ];
-
-				if ( index !== undefined ) {
-
-					face[ f ] = index;
-
-				}
-
-			}
-
-		}
-
-		return triangles.concat();
-
-	}, // end triangulate shapes
-
 	/*
 	/*
 	triangulate2 : function( pts, holes ) {
 	triangulate2 : function( pts, holes ) {
 
 

File diff suppressed because it is too large
+ 333 - 338
build/three.min.js


+ 47 - 27
examples/js/controls/OrbitControls.js

@@ -34,6 +34,7 @@ THREE.OrbitControls = function ( object, domElement ) {
 	// "target" sets the location of focus, where the control orbits around
 	// "target" sets the location of focus, where the control orbits around
 	// and where it pans with respect to.
 	// and where it pans with respect to.
 	this.target = new THREE.Vector3();
 	this.target = new THREE.Vector3();
+
 	// center is old, deprecated; use "target" instead
 	// center is old, deprecated; use "target" instead
 	this.center = this.target;
 	this.center = this.target;
 
 
@@ -41,6 +42,7 @@ THREE.OrbitControls = function ( object, domElement ) {
 	// backwards compatibility
 	// backwards compatibility
 	this.noZoom = false;
 	this.noZoom = false;
 	this.zoomSpeed = 1.0;
 	this.zoomSpeed = 1.0;
+
 	// Limits to how far you can dolly in and out
 	// Limits to how far you can dolly in and out
 	this.minDistance = 0;
 	this.minDistance = 0;
 	this.maxDistance = Infinity;
 	this.maxDistance = Infinity;
@@ -64,6 +66,7 @@ THREE.OrbitControls = function ( object, domElement ) {
 
 
 	// Set to true to disable use of the keys
 	// Set to true to disable use of the keys
 	this.noKeys = false;
 	this.noKeys = false;
+
 	// The four arrow keys
 	// The four arrow keys
 	this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
 	this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
 
 
@@ -81,6 +84,9 @@ THREE.OrbitControls = function ( object, domElement ) {
 	var panStart = new THREE.Vector2();
 	var panStart = new THREE.Vector2();
 	var panEnd = new THREE.Vector2();
 	var panEnd = new THREE.Vector2();
 	var panDelta = new THREE.Vector2();
 	var panDelta = new THREE.Vector2();
+	var panOffset = new THREE.Vector3();
+
+	var offset = new THREE.Vector3();
 
 
 	var dollyStart = new THREE.Vector2();
 	var dollyStart = new THREE.Vector2();
 	var dollyEnd = new THREE.Vector2();
 	var dollyEnd = new THREE.Vector2();
@@ -94,13 +100,13 @@ THREE.OrbitControls = function ( object, domElement ) {
 	var lastPosition = new THREE.Vector3();
 	var lastPosition = new THREE.Vector3();
 
 
 	var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 };
 	var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 };
+
 	var state = STATE.NONE;
 	var state = STATE.NONE;
 
 
 	// events
 	// events
 
 
 	var changeEvent = { type: 'change' };
 	var changeEvent = { type: 'change' };
 
 
-
 	this.rotateLeft = function ( angle ) {
 	this.rotateLeft = function ( angle ) {
 
 
 		if ( angle === undefined ) {
 		if ( angle === undefined ) {
@@ -128,11 +134,11 @@ THREE.OrbitControls = function ( object, domElement ) {
 	// pass in distance in world space to move left
 	// pass in distance in world space to move left
 	this.panLeft = function ( distance ) {
 	this.panLeft = function ( distance ) {
 
 
-		var panOffset = new THREE.Vector3();
 		var te = this.object.matrix.elements;
 		var te = this.object.matrix.elements;
+
 		// get X column of matrix
 		// get X column of matrix
-		panOffset.set( te[0], te[1], te[2] );
-		panOffset.multiplyScalar(-distance);
+		panOffset.set( te[ 0 ], te[ 1 ], te[ 2 ] );
+		panOffset.multiplyScalar( - distance );
 		
 		
 		pan.add( panOffset );
 		pan.add( panOffset );
 
 
@@ -141,18 +147,19 @@ THREE.OrbitControls = function ( object, domElement ) {
 	// pass in distance in world space to move up
 	// pass in distance in world space to move up
 	this.panUp = function ( distance ) {
 	this.panUp = function ( distance ) {
 
 
-		var panOffset = new THREE.Vector3();
 		var te = this.object.matrix.elements;
 		var te = this.object.matrix.elements;
+
 		// get Y column of matrix
 		// get Y column of matrix
-		panOffset.set( te[4], te[5], te[6] );
-		panOffset.multiplyScalar(distance);
+		panOffset.set( te[ 4 ], te[ 5 ], te[ 6 ] );
+		panOffset.multiplyScalar( distance );
 		
 		
 		pan.add( panOffset );
 		pan.add( panOffset );
+
 	};
 	};
 	
 	
-	// main entry point; pass in Vector2 of change desired in pixel space,
+	// pass in x,y of change desired in pixel space,
 	// right and down are positive
 	// right and down are positive
-	this.pan = function ( delta ) {
+	this.pan = function ( deltaX, deltaY ) {
 
 
 		var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
 		var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
 
 
@@ -164,20 +171,21 @@ THREE.OrbitControls = function ( object, domElement ) {
 			var targetDistance = offset.length();
 			var targetDistance = offset.length();
 
 
 			// half of the fov is center to top of screen
 			// half of the fov is center to top of screen
-			targetDistance *= Math.tan( (scope.object.fov/2) * Math.PI / 180.0 );
+			targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 );
+
 			// we actually don't use screenWidth, since perspective camera is fixed to screen height
 			// we actually don't use screenWidth, since perspective camera is fixed to screen height
-			scope.panLeft( 2 * delta.x * targetDistance / element.clientHeight );
-			scope.panUp( 2 * delta.y * targetDistance / element.clientHeight );
+			scope.panLeft( 2 * deltaX * targetDistance / element.clientHeight );
+			scope.panUp( 2 * deltaY * targetDistance / element.clientHeight );
 
 
 		} else if ( scope.object.top !== undefined ) {
 		} else if ( scope.object.top !== undefined ) {
 
 
 			// orthographic
 			// orthographic
-			scope.panLeft( delta.x * (scope.object.right - scope.object.left) / element.clientWidth );
-			scope.panUp( delta.y * (scope.object.top - scope.object.bottom) / element.clientHeight );
+			scope.panLeft( deltaX * (scope.object.right - scope.object.left) / element.clientWidth );
+			scope.panUp( deltaY * (scope.object.top - scope.object.bottom) / element.clientHeight );
 
 
 		} else {
 		} else {
 
 
-			// camera neither orthographic or perspective - warn user
+			// camera neither orthographic or perspective
 			console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
 			console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
 
 
 		}
 		}
@@ -211,7 +219,8 @@ THREE.OrbitControls = function ( object, domElement ) {
 	this.update = function () {
 	this.update = function () {
 
 
 		var position = this.object.position;
 		var position = this.object.position;
-		var offset = position.clone().sub( this.target );
+
+		offset.copy( position ).sub( this.target );
 
 
 		// angle from z-axis around y-axis
 		// angle from z-axis around y-axis
 
 
@@ -255,7 +264,7 @@ THREE.OrbitControls = function ( object, domElement ) {
 		thetaDelta = 0;
 		thetaDelta = 0;
 		phiDelta = 0;
 		phiDelta = 0;
 		scale = 1;
 		scale = 1;
-		pan.set(0,0,0);
+		pan.set( 0, 0, 0 );
 
 
 		if ( lastPosition.distanceTo( this.object.position ) > 0 ) {
 		if ( lastPosition.distanceTo( this.object.position ) > 0 ) {
 
 
@@ -308,7 +317,6 @@ THREE.OrbitControls = function ( object, domElement ) {
 
 
 		}
 		}
 
 
-		// Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be
 		scope.domElement.addEventListener( 'mousemove', onMouseMove, false );
 		scope.domElement.addEventListener( 'mousemove', onMouseMove, false );
 		scope.domElement.addEventListener( 'mouseup', onMouseUp, false );
 		scope.domElement.addEventListener( 'mouseup', onMouseUp, false );
 
 
@@ -331,6 +339,7 @@ THREE.OrbitControls = function ( object, domElement ) {
 
 
 			// rotating across whole screen goes 360 degrees around
 			// rotating across whole screen goes 360 degrees around
 			scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
 			scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
+
 			// rotating up and down along whole screen attempts to go 360, but limited to 180
 			// rotating up and down along whole screen attempts to go 360, but limited to 180
 			scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
 			scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
 
 
@@ -362,13 +371,12 @@ THREE.OrbitControls = function ( object, domElement ) {
 			panEnd.set( event.clientX, event.clientY );
 			panEnd.set( event.clientX, event.clientY );
 			panDelta.subVectors( panEnd, panStart );
 			panDelta.subVectors( panEnd, panStart );
 			
 			
-			scope.pan( panDelta );
+			scope.pan( panDelta.x, panDelta.y );
 
 
 			panStart.copy( panEnd );
 			panStart.copy( panEnd );
 
 
 		}
 		}
 
 
-		// Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be
 		scope.update();
 		scope.update();
 
 
 	}
 	}
@@ -377,7 +385,6 @@ THREE.OrbitControls = function ( object, domElement ) {
 
 
 		if ( scope.enabled === false ) return;
 		if ( scope.enabled === false ) return;
 
 
-		// Greggman fix: https://github.com/greggman/three.js/commit/fde9f9917d6d8381f06bf22cdff766029d1761be
 		scope.domElement.removeEventListener( 'mousemove', onMouseMove, false );
 		scope.domElement.removeEventListener( 'mousemove', onMouseMove, false );
 		scope.domElement.removeEventListener( 'mouseup', onMouseUp, false );
 		scope.domElement.removeEventListener( 'mouseup', onMouseUp, false );
 
 
@@ -424,26 +431,29 @@ THREE.OrbitControls = function ( object, domElement ) {
 		switch ( event.keyCode ) {
 		switch ( event.keyCode ) {
 
 
 			case scope.keys.UP:
 			case scope.keys.UP:
-				scope.pan( new THREE.Vector2( 0, scope.keyPanSpeed ) );
+				scope.pan( 0, scope.keyPanSpeed );
 				scope.update();
 				scope.update();
 				break;
 				break;
+
 			case scope.keys.BOTTOM:
 			case scope.keys.BOTTOM:
-				scope.pan( new THREE.Vector2( 0, -scope.keyPanSpeed ) );
+				scope.pan( 0, - scope.keyPanSpeed );
 				scope.update();
 				scope.update();
 				break;
 				break;
+
 			case scope.keys.LEFT:
 			case scope.keys.LEFT:
-				scope.pan( new THREE.Vector2( scope.keyPanSpeed, 0 ) );
+				scope.pan( scope.keyPanSpeed, 0 );
 				scope.update();
 				scope.update();
 				break;
 				break;
+
 			case scope.keys.RIGHT:
 			case scope.keys.RIGHT:
-				scope.pan( new THREE.Vector2( -scope.keyPanSpeed, 0 ) );
+				scope.pan( - scope.keyPanSpeed, 0 );
 				scope.update();
 				scope.update();
 				break;
 				break;
 
 
 		}
 		}
 
 
 	}
 	}
-	
+
 	function touchstart( event ) {
 	function touchstart( event ) {
 
 
 		if ( scope.enabled === false ) return;
 		if ( scope.enabled === false ) return;
@@ -451,6 +461,7 @@ THREE.OrbitControls = function ( object, domElement ) {
 		switch ( event.touches.length ) {
 		switch ( event.touches.length ) {
 
 
 			case 1:	// one-fingered touch: rotate
 			case 1:	// one-fingered touch: rotate
+
 				if ( scope.noRotate === true ) return;
 				if ( scope.noRotate === true ) return;
 
 
 				state = STATE.TOUCH_ROTATE;
 				state = STATE.TOUCH_ROTATE;
@@ -459,6 +470,7 @@ THREE.OrbitControls = function ( object, domElement ) {
 				break;
 				break;
 
 
 			case 2:	// two-fingered touch: dolly
 			case 2:	// two-fingered touch: dolly
+
 				if ( scope.noZoom === true ) return;
 				if ( scope.noZoom === true ) return;
 
 
 				state = STATE.TOUCH_DOLLY;
 				state = STATE.TOUCH_DOLLY;
@@ -470,6 +482,7 @@ THREE.OrbitControls = function ( object, domElement ) {
 				break;
 				break;
 
 
 			case 3: // three-fingered touch: pan
 			case 3: // three-fingered touch: pan
+
 				if ( scope.noPan === true ) return;
 				if ( scope.noPan === true ) return;
 
 
 				state = STATE.TOUCH_PAN;
 				state = STATE.TOUCH_PAN;
@@ -478,9 +491,11 @@ THREE.OrbitControls = function ( object, domElement ) {
 				break;
 				break;
 
 
 			default:
 			default:
+
 				state = STATE.NONE;
 				state = STATE.NONE;
 
 
 		}
 		}
+
 	}
 	}
 
 
 	function touchmove( event ) {
 	function touchmove( event ) {
@@ -495,6 +510,7 @@ THREE.OrbitControls = function ( object, domElement ) {
 		switch ( event.touches.length ) {
 		switch ( event.touches.length ) {
 
 
 			case 1: // one-fingered touch: rotate
 			case 1: // one-fingered touch: rotate
+
 				if ( scope.noRotate === true ) return;
 				if ( scope.noRotate === true ) return;
 				if ( state !== STATE.TOUCH_ROTATE ) return;
 				if ( state !== STATE.TOUCH_ROTATE ) return;
 
 
@@ -512,6 +528,7 @@ THREE.OrbitControls = function ( object, domElement ) {
 				break;
 				break;
 
 
 			case 2: // two-fingered touch: dolly
 			case 2: // two-fingered touch: dolly
+
 				if ( scope.noZoom === true ) return;
 				if ( scope.noZoom === true ) return;
 				if ( state !== STATE.TOUCH_DOLLY ) return;
 				if ( state !== STATE.TOUCH_DOLLY ) return;
 
 
@@ -538,13 +555,14 @@ THREE.OrbitControls = function ( object, domElement ) {
 				break;
 				break;
 
 
 			case 3: // three-fingered touch: pan
 			case 3: // three-fingered touch: pan
+
 				if ( scope.noPan === true ) return;
 				if ( scope.noPan === true ) return;
 				if ( state !== STATE.TOUCH_PAN ) return;
 				if ( state !== STATE.TOUCH_PAN ) return;
 
 
 				panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
 				panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
 				panDelta.subVectors( panEnd, panStart );
 				panDelta.subVectors( panEnd, panStart );
 				
 				
-				scope.pan( panDelta );
+				scope.pan( panDelta.x, panDelta.y );
 
 
 				panStart.copy( panEnd );
 				panStart.copy( panEnd );
 
 
@@ -552,6 +570,7 @@ THREE.OrbitControls = function ( object, domElement ) {
 				break;
 				break;
 
 
 			default:
 			default:
+
 				state = STATE.NONE;
 				state = STATE.NONE;
 
 
 		}
 		}
@@ -563,6 +582,7 @@ THREE.OrbitControls = function ( object, domElement ) {
 		if ( scope.enabled === false ) return;
 		if ( scope.enabled === false ) return;
 
 
 		state = STATE.NONE;
 		state = STATE.NONE;
+
 	}
 	}
 
 
 	this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
 	this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );

+ 1 - 1
examples/js/loaders/MTLLoader.js

@@ -395,7 +395,7 @@ THREE.MTLLoader.MaterialCreator.prototype = {
 
 
 THREE.MTLLoader.ensurePowerOfTwo_ = function ( image ) {
 THREE.MTLLoader.ensurePowerOfTwo_ = function ( image ) {
 
 
-	if ( ! THREE.Math.isPowerOfTwo_( image.width ) || ! THREE.Math.isPowerOfTwo_( image.height ) ) {
+	if ( ! THREE.Math.isPowerOfTwo( image.width ) || ! THREE.Math.isPowerOfTwo( image.height ) ) {
 
 
 		var canvas = document.createElement( "canvas" );
 		var canvas = document.createElement( "canvas" );
 		canvas.width = THREE.MTLLoader.nextHighestPowerOfTwo_( image.width );
 		canvas.width = THREE.MTLLoader.nextHighestPowerOfTwo_( image.width );

+ 261 - 0
examples/webgl_buffergeometry_lines_indexed.html

@@ -0,0 +1,261 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - buffergeometry</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			body {
+				color: #cccccc;
+				font-family:Monospace;
+				font-size:13px;
+				text-align:center;
+
+				background-color: #000000;
+				margin: 0px;
+				overflow: hidden;
+			}
+
+			#info {
+				position: absolute;
+				top: 0px; width: 100%;
+				padding: 5px;
+			}
+
+			a {
+
+				color: #0080ff;
+			}
+
+		</style>
+	</head>
+	<body>
+
+		<div id="container"></div>
+		<div id="info"><a href="http://threejs.org" target="_blank">three.js</a> webgl - buffergeometry</div>
+
+		<script src="../build/three.min.js"></script>
+
+		<script src="js/Detector.js"></script>
+		<script src="js/libs/stats.min.js"></script>
+
+		<script>
+
+			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
+
+			var container, stats;
+
+			var camera, scene, renderer;
+
+			var mesh, parent_node;
+
+			init();
+			animate();
+
+			function init() {
+
+				container = document.getElementById( 'container' );
+
+				camera = new THREE.PerspectiveCamera( 27, window.innerWidth / window.innerHeight, 1, 10000 );
+				camera.position.z = 9000;
+
+				scene = new THREE.Scene();
+
+				var geometry = new THREE.BufferGeometry();
+				var material = new THREE.LineBasicMaterial({ vertexColors: true });
+
+				var positions = [];
+				var next_positions_index = 0;
+				var colors = [];
+				var indices_array = [];
+
+				// --------------------------------
+				var iteration_count = 4;
+				var rangle = 60 * Math.PI / 180.0;
+
+				function add_vertex(v) {
+
+					if (next_positions_index == 0xffff) throw new Error("Too many points");
+
+					positions.push(v.x, v.y, v.z);
+					colors.push(Math.random()*0.5+0.5, Math.random()*0.5+0.5, 1);
+					return next_positions_index++;
+				}
+
+				// simple Koch curve
+				function snowflake_iteration(p0, p4, depth) {
+
+					if (--depth < 0) {
+
+						var i = next_positions_index-1; // p0 already there
+						add_vertex(p4);
+						indices_array.push(i, i+1);
+						return;
+					}
+
+					var v = p4.clone().sub(p0);
+					var v_tier = v.clone().multiplyScalar(1.0/3.0);
+					var p1 = p0.clone().add(v_tier);
+
+					var angle = Math.atan2(v.y, v.x) + rangle;
+					var length = v_tier.length();
+					var p2 = p1.clone();
+					p2.x += Math.cos(angle) * length;
+					p2.y += Math.sin(angle) * length;
+
+					var p3 = p0.clone().add(v_tier).add(v_tier);
+
+					snowflake_iteration(p0, p1, depth);
+					snowflake_iteration(p1, p2, depth);
+					snowflake_iteration(p2, p3, depth);
+					snowflake_iteration(p3, p4, depth);
+				}
+
+				function snowflake(points, loop, x_offset) {
+
+					for (var iteration = 0; iteration != iteration_count; ++iteration) {
+
+						add_vertex(points[0]);
+						for (var p_index=0, p_count=points.length-1; p_index != p_count; ++p_index) {
+							snowflake_iteration(points[p_index], points[p_index+1], iteration);
+						}
+
+						if (loop) snowflake_iteration(points[points.length-1], points[0], iteration);
+
+						// translate input curve for next iteration
+						for (var p_index=0, p_count=points.length; p_index != p_count; ++p_index) {
+							points[p_index].x += x_offset;
+						}
+
+					}
+				}
+
+				var y = 0;
+				snowflake
+				(
+					[
+						new THREE.Vector3(0, y+0, 0),
+						new THREE.Vector3(500, y+0, 0)
+					],
+					false, 600
+				);
+
+				y += 600;
+				snowflake
+				(
+					[
+						new THREE.Vector3(0, y+0, 0),
+						new THREE.Vector3(250, y+400, 0),
+						new THREE.Vector3(500, y+0, 0)
+					],
+					true, 600
+				);
+
+				y += 600;
+				snowflake
+				(
+					[
+						new THREE.Vector3(0, y+0, 0),
+						new THREE.Vector3(500, y, 0),
+						new THREE.Vector3(500, y+500, 0),
+						new THREE.Vector3(0, y+500, 0),
+					],
+					true, 600
+				);
+
+				y += 1000;
+				snowflake
+				(
+					[
+						new THREE.Vector3(250, y+0, 0),
+						new THREE.Vector3(500, y+0, 0),
+						new THREE.Vector3(250, y+0, 0),
+						new THREE.Vector3(250, y+250, 0),
+						new THREE.Vector3(250, y+0, 0),
+						new THREE.Vector3(0, y, 0),
+						new THREE.Vector3(250, y+0, 0),
+						new THREE.Vector3(250, y-250, 0),
+						new THREE.Vector3(250, y+0, 0),
+					],
+					false, 600
+				);
+				// --------------------------------
+
+				geometry.addAttribute( 'position', Float32Array, positions.length, 3 );
+				geometry.attributes.position.array = new Float32Array(positions);
+
+				geometry.addAttribute( 'color', Float32Array, colors.length, 3 );
+				geometry.attributes.color.array = new Float32Array(colors);
+
+				geometry.addAttribute( 'index', Uint16Array, indices_array.length, 1 );
+				geometry.attributes.index.array = new Uint16Array(indices_array);
+				geometry.offsets = [ {start:0, index:0, count:indices_array.length} ];
+
+				geometry.computeBoundingSphere();
+
+				mesh = new THREE.Line( geometry, material, THREE.LinePieces);
+				mesh.position.x -= 1200;
+				mesh.position.y -= 1200;
+
+				parent_node = new THREE.Object3D();
+				parent_node.add(mesh);
+
+				scene.add( parent_node );
+
+				renderer = new THREE.WebGLRenderer( { antialias: false } );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+				renderer.gammaInput = true;
+				renderer.gammaOutput = true;
+
+				container.appendChild( renderer.domElement );
+
+				//
+
+				stats = new Stats();
+				stats.domElement.style.position = 'absolute';
+				stats.domElement.style.top = '0px';
+				container.appendChild( stats.domElement );
+
+				//
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			//
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+
+				render();
+				stats.update();
+
+			}
+
+			function render() {
+
+				var time = Date.now() * 0.001;
+
+				//mesh.rotation.x = time * 0.25;
+				//mesh.rotation.y = time * 0.5;
+				parent_node.rotation.z = time * 0.5;
+
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+
+	</body>
+</html>

+ 1 - 1
src/core/BufferGeometry.js

@@ -38,7 +38,7 @@ THREE.BufferGeometry.prototype = {
 
 
 	constructor: THREE.BufferGeometry,
 	constructor: THREE.BufferGeometry,
 
 
-	addAttribute: function( name, type, numItems, itemSize ) {
+	addAttribute: function ( name, type, numItems, itemSize ) {
 
 
 		this.attributes[ name ] = {
 		this.attributes[ name ] = {
 
 

+ 75 - 25
src/renderers/WebGLRenderer.js

@@ -2382,6 +2382,45 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 	};
 	};
 
 
+	// used by renderBufferDirect for THREE.Line
+	function setupLinesVertexAttributes( material, programAttributes, geometryAttributes, startIndex ) {
+
+		var attributeItem, attributeName, attributePointer, attributeSize;
+
+		for ( attributeName in programAttributes ) {
+
+			attributePointer = programAttributes[ attributeName ];
+			attributeItem = geometryAttributes[ attributeName ];
+			
+			if ( attributePointer >= 0 ) {
+
+				if ( attributeItem ) {
+
+					attributeSize = attributeItem.itemSize;
+					_gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer );
+					enableAttribute( attributePointer );
+					_gl.vertexAttribPointer( attributePointer, attributeSize, _gl.FLOAT, false, 0, startIndex * attributeSize * 4 ); // 4 bytes per Float32
+
+				} else if ( material.defaultAttributeValues ) {
+
+					if ( material.defaultAttributeValues[ attributeName ].length === 2 ) {
+
+						_gl.vertexAttrib2fv( attributePointer, material.defaultAttributeValues[ attributeName ] );
+
+					} else if ( material.defaultAttributeValues[ attributeName ].length === 3 ) {
+
+						_gl.vertexAttrib3fv( attributePointer, material.defaultAttributeValues[ attributeName ] );
+
+					}
+
+				}
+
+			}
+
+		}
+
+	}
+
 	function setDirectBuffers ( geometry, hint, dispose ) {
 	function setDirectBuffers ( geometry, hint, dispose ) {
 
 
 		var attributes = geometry.attributes;
 		var attributes = geometry.attributes;
@@ -2721,54 +2760,65 @@ THREE.WebGLRenderer = function ( parameters ) {
 
 
 		} else if ( object instanceof THREE.Line ) {
 		} else if ( object instanceof THREE.Line ) {
 
 
-			if ( updateBuffers ) {
+			var primitives = ( object.type === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES;
 
 
-				for ( attributeName in programAttributes ) {
+			setLineWidth( material.linewidth );
 
 
-					attributePointer = programAttributes[ attributeName ];
-					attributeItem = geometryAttributes[ attributeName ];
-					
-					if ( attributePointer >= 0 ) {
+			var index = geometryAttributes[ "index" ];
 
 
-						if ( attributeItem ) {
+			// indexed lines
+			
+			if ( index ) {
 
 
-							attributeSize = attributeItem.itemSize;
-							_gl.bindBuffer( _gl.ARRAY_BUFFER, attributeItem.buffer );
-							enableAttribute( attributePointer );
-							_gl.vertexAttribPointer( attributePointer, attributeSize, _gl.FLOAT, false, 0, 0 );
+				var offsets = geometry.offsets;
 
 
-						} else if ( material.defaultAttributeValues && material.defaultAttributeValues[ attributeName ] ) {
+				// if there is more than 1 chunk
+				// must set attribute pointers to use new offsets for each chunk
+				// even if geometry and materials didn't change
 
 
-							if ( material.defaultAttributeValues[ attributeName ].length === 2 ) {
+				if ( offsets.length > 1 ) updateBuffers = true;
 
 
-								_gl.vertexAttrib2fv( attributePointer, material.defaultAttributeValues[ attributeName ] );
+				for ( var i = 0, il = offsets.length; i < il; i ++ ) {
 
 
-							} else if ( material.defaultAttributeValues[ attributeName ].length === 3 ) {
+					var startIndex = offsets[ i ].index;
 
 
-								_gl.vertexAttrib3fv( attributePointer, material.defaultAttributeValues[ attributeName ] );
+					if ( updateBuffers ) {
 
 
-							}
+						setupLinesVertexAttributes(material, programAttributes, geometryAttributes, startIndex);
 
 
-						}
+						// indices
+						_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer );
 
 
 					}
 					}
 
 
+					// render indexed lines
+
+					_gl.drawElements( _gl.LINES, offsets[ i ].count, _gl.UNSIGNED_SHORT, offsets[ i ].start * 2 ); // 2 bytes per Uint16Array
+
+					_this.info.render.calls ++;
+					_this.info.render.vertices += offsets[ i ].count; // not really true, here vertices can be shared
+
 				}
 				}
 
 
 			}
 			}
 
 
-			// render lines
+			// non-indexed lines
 
 
-			var primitives = ( object.type === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES;
+			else {
 
 
-			setLineWidth( material.linewidth );
+				if ( updateBuffers ) {
 
 
-			var position = geometryAttributes[ "position" ];
+					setupLinesVertexAttributes(material, programAttributes, geometryAttributes, 0);
+				}
+
+				var position = geometryAttributes[ "position" ];
+
+				_gl.drawArrays( primitives, 0, position.numItems / 3 );
+				_this.info.render.calls ++;
+				_this.info.render.points += position.numItems;
+			}
 
 
-			_gl.drawArrays( primitives, 0, position.numItems / 3 );
 
 
-			_this.info.render.calls ++;
-			_this.info.render.points += position.numItems;
 
 
 		}
 		}
 
 

Some files were not shown because too many files changed in this diff