1 /**
  2  * @author supereggbert / http://www.paulbrunt.co.uk/
  3  * @author mrdoob / http://mrdoob.com/
  4  * @author alteredq / http://alteredqualia.com/
  5  * @author szimek / https://github.com/szimek/
  6  */
  7 
  8 /**@constructor*/
  9 THREE.WebGLRenderer = function ( parameters ) {
 10 
 11 	console.log( 'THREE.WebGLRenderer', THREE.REVISION );
 12 
 13 	parameters = parameters || {};
 14 
 15 	var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElement( 'canvas' ),
 16 
 17 	_precision = parameters.precision !== undefined ? parameters.precision : 'highp',
 18 
 19 	_alpha = parameters.alpha !== undefined ? parameters.alpha : true,
 20 	_premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true,
 21 	_antialias = parameters.antialias !== undefined ? parameters.antialias : false,
 22 	_stencil = parameters.stencil !== undefined ? parameters.stencil : true,
 23 	_preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false,
 24 
 25 	_clearColor = parameters.clearColor !== undefined ? new THREE.Color( parameters.clearColor ) : new THREE.Color( 0x000000 ),
 26 	_clearAlpha = parameters.clearAlpha !== undefined ? parameters.clearAlpha : 0;
 27 
 28 	// public properties
 29 
 30 	this.domElement = _canvas;
 31 	this.context = null;
 32 
 33 	// clearing
 34 
 35 	this.autoClear = true;
 36 	this.autoClearColor = true;
 37 	this.autoClearDepth = true;
 38 	this.autoClearStencil = true;
 39 
 40 	// scene graph
 41 
 42 	this.sortObjects = true;
 43 
 44 	this.autoUpdateObjects = true;
 45 	this.autoUpdateScene = true;
 46 
 47 	// physically based shading
 48 
 49 	this.gammaInput = false;
 50 	this.gammaOutput = false;
 51 	this.physicallyBasedShading = false;
 52 
 53 	// shadow map
 54 
 55 	this.shadowMapEnabled = false;
 56 	this.shadowMapAutoUpdate = true;
 57 	this.shadowMapSoft = true;
 58 	this.shadowMapCullFrontFaces = true;
 59 	this.shadowMapDebug = false;
 60 	this.shadowMapCascade = false;
 61 
 62 	// morphs
 63 
 64 	this.maxMorphTargets = 8;
 65 	this.maxMorphNormals = 4;
 66 
 67 	// flags
 68 
 69 	this.autoScaleCubemaps = true;
 70 
 71 	// custom render plugins
 72 
 73 	this.renderPluginsPre = [];
 74 	this.renderPluginsPost = [];
 75 
 76 	// info
 77 
 78 	this.info = {
 79 
 80 		memory: {
 81 
 82 			programs: 0,
 83 			geometries: 0,
 84 			textures: 0
 85 
 86 		},
 87 
 88 		render: {
 89 
 90 			calls: 0,
 91 			vertices: 0,
 92 			faces: 0,
 93 			points: 0
 94 
 95 		}
 96 
 97 	};
 98 
 99 	// internal properties
100 
101 	var _this = this,
102 
103 	_programs = [],
104 	_programs_counter = 0,
105 
106 	// internal state cache
107 
108 	_currentProgram = null,
109 	_currentFramebuffer = null,
110 	_currentMaterialId = -1,
111 	_currentGeometryGroupHash = null,
112 	_currentCamera = null,
113 	_geometryGroupCounter = 0,
114 
115 	_usedTextureUnits = 0,
116 
117 	// GL state cache
118 
119 	_oldDoubleSided = -1,
120 	_oldFlipSided = -1,
121 
122 	_oldBlending = -1,
123 
124 	_oldBlendEquation = -1,
125 	_oldBlendSrc = -1,
126 	_oldBlendDst = -1,
127 
128 	_oldDepthTest = -1,
129 	_oldDepthWrite = -1,
130 
131 	_oldPolygonOffset = null,
132 	_oldPolygonOffsetFactor = null,
133 	_oldPolygonOffsetUnits = null,
134 
135 	_oldLineWidth = null,
136 
137 	_viewportX = 0,
138 	_viewportY = 0,
139 	_viewportWidth = 0,
140 	_viewportHeight = 0,
141 	_currentWidth = 0,
142 	_currentHeight = 0,
143 
144 	// frustum
145 
146 	_frustum = new THREE.Frustum(),
147 
148 	 // camera matrices cache
149 
150 	_projScreenMatrix = new THREE.Matrix4(),
151 	_projScreenMatrixPS = new THREE.Matrix4(),
152 
153 	_vector3 = new THREE.Vector4(),
154 
155 	// light arrays cache
156 
157 	_direction = new THREE.Vector3(),
158 
159 	_lightsNeedUpdate = true,
160 
161 	_lights = {
162 
163 		ambient: [ 0, 0, 0 ],
164 		directional: { length: 0, colors: new Array(), positions: new Array() },
165 		point: { length: 0, colors: new Array(), positions: new Array(), distances: new Array() },
166 		spot: { length: 0, colors: new Array(), positions: new Array(), distances: new Array(), directions: new Array(), anglesCos: new Array(), exponents: new Array() },
167 		hemi: { length: 0, skyColors: new Array(), groundColors: new Array(), positions: new Array() }
168 
169 	};
170 
171 	// initialize
172 
173 	var _gl;
174 
175 	var _glExtensionTextureFloat;
176 	var _glExtensionStandardDerivatives;
177 	var _glExtensionTextureFilterAnisotropic;
178 	var _glExtensionCompressedTextureS3TC;
179 
180 	initGL();
181 
182 	setDefaultGLState();
183 
184 	this.context = _gl;
185 
186 	// GPU capabilities
187 
188 	var _maxTextures = _gl.getParameter( _gl.MAX_TEXTURE_IMAGE_UNITS );
189 	var _maxVertexTextures = _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS );
190 	var _maxTextureSize = _gl.getParameter( _gl.MAX_TEXTURE_SIZE );
191 	var _maxCubemapSize = _gl.getParameter( _gl.MAX_CUBE_MAP_TEXTURE_SIZE );
192 
193 	var _maxAnisotropy = _glExtensionTextureFilterAnisotropic ? _gl.getParameter( _glExtensionTextureFilterAnisotropic.MAX_TEXTURE_MAX_ANISOTROPY_EXT ) : 0;
194 
195 	var _supportsVertexTextures = ( _maxVertexTextures > 0 );
196 	var _supportsBoneTextures = _supportsVertexTextures && _glExtensionTextureFloat;
197 
198 	var _compressedTextureFormats = _glExtensionCompressedTextureS3TC ? _gl.getParameter( _gl.COMPRESSED_TEXTURE_FORMATS ) : [];
199 
200 	// API
201 
202 	this.getContext = function () {
203 
204 		return _gl;
205 
206 	};
207 
208 	this.supportsVertexTextures = function () {
209 
210 		return _supportsVertexTextures;
211 
212 	};
213 
214 	this.getMaxAnisotropy  = function () {
215 
216 		return _maxAnisotropy;
217 
218 	};
219 
220 	this.setSize = function ( width, height ) {
221 
222 		_canvas.width = width;
223 		_canvas.height = height;
224 
225 		this.setViewport( 0, 0, _canvas.width, _canvas.height );
226 
227 	};
228 
229 	this.setViewport = function ( x, y, width, height ) {
230 
231 		_viewportX = x !== undefined ? x : 0;
232 		_viewportY = y !== undefined ? y : 0;
233 
234 		_viewportWidth = width !== undefined ? width : _canvas.width;
235 		_viewportHeight = height !== undefined ? height : _canvas.height;
236 
237 		_gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight );
238 
239 	};
240 
241 	this.setScissor = function ( x, y, width, height ) {
242 
243 		_gl.scissor( x, y, width, height );
244 
245 	};
246 
247 	this.enableScissorTest = function ( enable ) {
248 
249 		enable ? _gl.enable( _gl.SCISSOR_TEST ) : _gl.disable( _gl.SCISSOR_TEST );
250 
251 	};
252 
253 	// Clearing
254 
255 	this.setClearColorHex = function ( hex, alpha ) {
256 
257 		_clearColor.setHex( hex );
258 		_clearAlpha = alpha;
259 
260 		_gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
261 
262 	};
263 
264 	this.setClearColor = function ( color, alpha ) {
265 
266 		_clearColor.copy( color );
267 		_clearAlpha = alpha;
268 
269 		_gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
270 
271 	};
272 
273 	this.getClearColor = function () {
274 
275 		return _clearColor;
276 
277 	};
278 
279 	this.getClearAlpha = function () {
280 
281 		return _clearAlpha;
282 
283 	};
284 
285 	this.clear = function ( color, depth, stencil ) {
286 
287 		var bits = 0;
288 
289 		if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT;
290 		if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT;
291 		if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT;
292 
293 		_gl.clear( bits );
294 
295 	};
296 
297 	this.clearTarget = function ( renderTarget, color, depth, stencil ) {
298 
299 		this.setRenderTarget( renderTarget );
300 		this.clear( color, depth, stencil );
301 
302 	};
303 
304 	// Plugins
305 
306 	this.addPostPlugin = function ( plugin ) {
307 
308 		plugin.init( this );
309 		this.renderPluginsPost.push( plugin );
310 
311 	};
312 
313 	this.addPrePlugin = function ( plugin ) {
314 
315 		plugin.init( this );
316 		this.renderPluginsPre.push( plugin );
317 
318 	};
319 
320 	// Deallocation
321 
322 	this.deallocateObject = function ( object ) {
323 
324 		if ( ! object.__webglInit ) return;
325 
326 		object.__webglInit = false;
327 
328 		delete object._modelViewMatrix;
329 		delete object._normalMatrix;
330 
331 		delete object._normalMatrixArray;
332 		delete object._modelViewMatrixArray;
333 		delete object._modelMatrixArray;
334 
335 		if ( object instanceof THREE.Mesh ) {
336 
337 			for ( var g in object.geometry.geometryGroups ) {
338 
339 				deleteMeshBuffers( object.geometry.geometryGroups[ g ] );
340 
341 			}
342 
343 		} else if ( object instanceof THREE.Ribbon ) {
344 
345 			deleteRibbonBuffers( object.geometry );
346 
347 		} else if ( object instanceof THREE.Line ) {
348 
349 			deleteLineBuffers( object.geometry );
350 
351 		} else if ( object instanceof THREE.ParticleSystem ) {
352 
353 			deleteParticleBuffers( object.geometry );
354 
355 		}
356 
357 	};
358 
359 	this.deallocateTexture = function ( texture ) {
360 
361 		if ( ! texture.__webglInit ) return;
362 
363 		texture.__webglInit = false;
364 		_gl.deleteTexture( texture.__webglTexture );
365 
366 		_this.info.memory.textures --;
367 
368 	};
369 
370 	this.deallocateRenderTarget = function ( renderTarget ) {
371 
372 		if ( !renderTarget || ! renderTarget.__webglTexture ) return;
373 
374 		_gl.deleteTexture( renderTarget.__webglTexture );
375 
376 		if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) {
377 
378 			for ( var i = 0; i < 6; i ++ ) {
379 
380 				_gl.deleteFramebuffer( renderTarget.__webglFramebuffer[ i ] );
381 				_gl.deleteRenderbuffer( renderTarget.__webglRenderbuffer[ i ] );
382 
383 			}
384 
385 		} else {
386 
387 			_gl.deleteFramebuffer( renderTarget.__webglFramebuffer );
388 			_gl.deleteRenderbuffer( renderTarget.__webglRenderbuffer );
389 
390 		}
391 
392 	};
393 
394 	this.deallocateMaterial = function ( material ) {
395 
396 		var program = material.program;
397 
398 		if ( ! program ) return;
399 
400 		material.program = undefined;
401 
402 		// only deallocate GL program if this was the last use of shared program
403 		// assumed there is only single copy of any program in the _programs list
404 		// (that's how it's constructed)
405 
406 		var i, il, programInfo;
407 		var deleteProgram = false;
408 
409 		for ( i = 0, il = _programs.length; i < il; i ++ ) {
410 
411 			programInfo = _programs[ i ];
412 
413 			if ( programInfo.program === program ) {
414 
415 				programInfo.usedTimes --;
416 
417 				if ( programInfo.usedTimes === 0 ) {
418 
419 					deleteProgram = true;
420 
421 				}
422 
423 				break;
424 
425 			}
426 
427 		}
428 
429 		if ( deleteProgram ) {
430 
431 			// avoid using array.splice, this is costlier than creating new array from scratch
432 
433 			var newPrograms = [];
434 
435 			for ( i = 0, il = _programs.length; i < il; i ++ ) {
436 
437 				programInfo = _programs[ i ];
438 
439 				if ( programInfo.program !== program ) {
440 
441 					newPrograms.push( programInfo );
442 
443 				}
444 
445 			}
446 
447 			_programs = newPrograms;
448 
449 			_gl.deleteProgram( program );
450 
451 			_this.info.memory.programs --;
452 
453 		}
454 
455 	};
456 
457 	// Rendering
458 
459 	this.updateShadowMap = function ( scene, camera ) {
460 
461 		_currentProgram = null;
462 		_oldBlending = -1;
463 		_oldDepthTest = -1;
464 		_oldDepthWrite = -1;
465 		_currentGeometryGroupHash = -1;
466 		_currentMaterialId = -1;
467 		_lightsNeedUpdate = true;
468 		_oldDoubleSided = -1;
469 		_oldFlipSided = -1;
470 
471 		this.shadowMapPlugin.update( scene, camera );
472 
473 	};
474 
475 	// Internal functions
476 
477 	// Buffer allocation
478 
479 	function createParticleBuffers ( geometry ) {
480 
481 		geometry.__webglVertexBuffer = _gl.createBuffer();
482 		geometry.__webglColorBuffer = _gl.createBuffer();
483 
484 		_this.info.memory.geometries ++;
485 
486 	};
487 
488 	function createLineBuffers ( geometry ) {
489 
490 		geometry.__webglVertexBuffer = _gl.createBuffer();
491 		geometry.__webglColorBuffer = _gl.createBuffer();
492 		geometry.__webglLineDistanceBuffer = _gl.createBuffer();
493 
494 		_this.info.memory.geometries ++;
495 
496 	};
497 
498 	function createRibbonBuffers ( geometry ) {
499 
500 		geometry.__webglVertexBuffer = _gl.createBuffer();
501 		geometry.__webglColorBuffer = _gl.createBuffer();
502 		geometry.__webglNormalBuffer = _gl.createBuffer();
503 
504 		_this.info.memory.geometries ++;
505 
506 	};
507 
508 	function createMeshBuffers ( geometryGroup ) {
509 
510 		geometryGroup.__webglVertexBuffer = _gl.createBuffer();
511 		geometryGroup.__webglNormalBuffer = _gl.createBuffer();
512 		geometryGroup.__webglTangentBuffer = _gl.createBuffer();
513 		geometryGroup.__webglColorBuffer = _gl.createBuffer();
514 		geometryGroup.__webglUVBuffer = _gl.createBuffer();
515 		geometryGroup.__webglUV2Buffer = _gl.createBuffer();
516 
517 		geometryGroup.__webglSkinIndicesBuffer = _gl.createBuffer();
518 		geometryGroup.__webglSkinWeightsBuffer = _gl.createBuffer();
519 
520 		geometryGroup.__webglFaceBuffer = _gl.createBuffer();
521 		geometryGroup.__webglLineBuffer = _gl.createBuffer();
522 
523 		var m, ml;
524 
525 		if ( geometryGroup.numMorphTargets ) {
526 
527 			geometryGroup.__webglMorphTargetsBuffers = [];
528 
529 			for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) {
530 
531 				geometryGroup.__webglMorphTargetsBuffers.push( _gl.createBuffer() );
532 
533 			}
534 
535 		}
536 
537 		if ( geometryGroup.numMorphNormals ) {
538 
539 			geometryGroup.__webglMorphNormalsBuffers = [];
540 
541 			for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) {
542 
543 				geometryGroup.__webglMorphNormalsBuffers.push( _gl.createBuffer() );
544 
545 			}
546 
547 		}
548 
549 		_this.info.memory.geometries ++;
550 
551 	};
552 
553 	// Buffer deallocation
554 
555 	function deleteParticleBuffers ( geometry ) {
556 
557 		_gl.deleteBuffer( geometry.__webglVertexBuffer );
558 		_gl.deleteBuffer( geometry.__webglColorBuffer );
559 
560 		deleteCustomAttributesBuffers( geometry );
561 
562 		_this.info.memory.geometries --;
563 
564 	};
565 
566 	function deleteLineBuffers ( geometry ) {
567 
568 		_gl.deleteBuffer( geometry.__webglVertexBuffer );
569 		_gl.deleteBuffer( geometry.__webglColorBuffer );
570 		_gl.deleteBuffer( geometry.__webglLineDistanceBuffer );
571 
572 		deleteCustomAttributesBuffers( geometry );
573 
574 		_this.info.memory.geometries --;
575 
576 	};
577 
578 	function deleteRibbonBuffers ( geometry ) {
579 
580 		_gl.deleteBuffer( geometry.__webglVertexBuffer );
581 		_gl.deleteBuffer( geometry.__webglColorBuffer );
582 		_gl.deleteBuffer( geometry.__webglNormalBuffer );
583 
584 		deleteCustomAttributesBuffers( geometry );
585 
586 		_this.info.memory.geometries --;
587 
588 	};
589 
590 	function deleteMeshBuffers ( geometryGroup ) {
591 
592 		_gl.deleteBuffer( geometryGroup.__webglVertexBuffer );
593 		_gl.deleteBuffer( geometryGroup.__webglNormalBuffer );
594 		_gl.deleteBuffer( geometryGroup.__webglTangentBuffer );
595 		_gl.deleteBuffer( geometryGroup.__webglColorBuffer );
596 		_gl.deleteBuffer( geometryGroup.__webglUVBuffer );
597 		_gl.deleteBuffer( geometryGroup.__webglUV2Buffer );
598 
599 		_gl.deleteBuffer( geometryGroup.__webglSkinIndicesBuffer );
600 		_gl.deleteBuffer( geometryGroup.__webglSkinWeightsBuffer );
601 
602 		_gl.deleteBuffer( geometryGroup.__webglFaceBuffer );
603 		_gl.deleteBuffer( geometryGroup.__webglLineBuffer );
604 
605 		var m, ml;
606 
607 		if ( geometryGroup.numMorphTargets ) {
608 
609 			for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) {
610 
611 				_gl.deleteBuffer( geometryGroup.__webglMorphTargetsBuffers[ m ] );
612 
613 			}
614 
615 		}
616 
617 		if ( geometryGroup.numMorphNormals ) {
618 
619 			for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) {
620 
621 				_gl.deleteBuffer( geometryGroup.__webglMorphNormalsBuffers[ m ] );
622 
623 			}
624 
625 		}
626 
627 		deleteCustomAttributesBuffers( geometryGroup );
628 
629 		_this.info.memory.geometries --;
630 
631 	};
632 
633 	function deleteCustomAttributesBuffers( geometry ) {
634 
635 		if ( geometry.__webglCustomAttributesList ) {
636 
637 			for ( var id in geometry.__webglCustomAttributesList ) {
638 
639 				_gl.deleteBuffer( geometry.__webglCustomAttributesList[ id ].buffer );
640 
641 			}
642 
643 		}
644 
645 	};
646 
647 	// Buffer initialization
648 
649 	function initCustomAttributes ( geometry, object ) {
650 
651 		var nvertices = geometry.vertices.length;
652 
653 		var material = object.material;
654 
655 		if ( material.attributes ) {
656 
657 			if ( geometry.__webglCustomAttributesList === undefined ) {
658 
659 				geometry.__webglCustomAttributesList = [];
660 
661 			}
662 
663 			for ( var a in material.attributes ) {
664 
665 				var attribute = material.attributes[ a ];
666 
667 				if ( !attribute.__webglInitialized || attribute.createUniqueBuffers ) {
668 
669 					attribute.__webglInitialized = true;
670 
671 					var size = 1;		// "f" and "i"
672 
673 					if ( attribute.type === "v2" ) size = 2;
674 					else if ( attribute.type === "v3" ) size = 3;
675 					else if ( attribute.type === "v4" ) size = 4;
676 					else if ( attribute.type === "c"  ) size = 3;
677 
678 					attribute.size = size;
679 
680 					attribute.array = new Float32Array( nvertices * size );
681 
682 					attribute.buffer = _gl.createBuffer();
683 					attribute.buffer.belongsToAttribute = a;
684 
685 					attribute.needsUpdate = true;
686 
687 				}
688 
689 				geometry.__webglCustomAttributesList.push( attribute );
690 
691 			}
692 
693 		}
694 
695 	};
696 
697 	function initParticleBuffers ( geometry, object ) {
698 
699 		var nvertices = geometry.vertices.length;
700 
701 		geometry.__vertexArray = new Float32Array( nvertices * 3 );
702 		geometry.__colorArray = new Float32Array( nvertices * 3 );
703 
704 		geometry.__sortArray = [];
705 
706 		geometry.__webglParticleCount = nvertices;
707 
708 		initCustomAttributes ( geometry, object );
709 
710 	};
711 
712 	function initLineBuffers ( geometry, object ) {
713 
714 		var nvertices = geometry.vertices.length;
715 
716 		geometry.__vertexArray = new Float32Array( nvertices * 3 );
717 		geometry.__colorArray = new Float32Array( nvertices * 3 );
718 		geometry.__lineDistanceArray = new Float32Array( nvertices * 1 );
719 
720 		geometry.__webglLineCount = nvertices;
721 
722 		initCustomAttributes ( geometry, object );
723 
724 	};
725 
726 	function initRibbonBuffers ( geometry, object ) {
727 
728 		var nvertices = geometry.vertices.length;
729 
730 		geometry.__vertexArray = new Float32Array( nvertices * 3 );
731 		geometry.__colorArray = new Float32Array( nvertices * 3 );
732 		geometry.__normalArray = new Float32Array( nvertices * 3 );
733 
734 		geometry.__webglVertexCount = nvertices;
735 
736 		initCustomAttributes ( geometry, object );
737 
738 	};
739 
740 	function initMeshBuffers ( geometryGroup, object ) {
741 
742 		var geometry = object.geometry,
743 			faces3 = geometryGroup.faces3,
744 			faces4 = geometryGroup.faces4,
745 
746 			nvertices = faces3.length * 3 + faces4.length * 4,
747 			ntris     = faces3.length * 1 + faces4.length * 2,
748 			nlines    = faces3.length * 3 + faces4.length * 4,
749 
750 			material = getBufferMaterial( object, geometryGroup ),
751 
752 			uvType = bufferGuessUVType( material ),
753 			normalType = bufferGuessNormalType( material ),
754 			vertexColorType = bufferGuessVertexColorType( material );
755 
756 		//console.log( "uvType", uvType, "normalType", normalType, "vertexColorType", vertexColorType, object, geometryGroup, material );
757 
758 		geometryGroup.__vertexArray = new Float32Array( nvertices * 3 );
759 
760 		if ( normalType ) {
761 
762 			geometryGroup.__normalArray = new Float32Array( nvertices * 3 );
763 
764 		}
765 
766 		if ( geometry.hasTangents ) {
767 
768 			geometryGroup.__tangentArray = new Float32Array( nvertices * 4 );
769 
770 		}
771 
772 		if ( vertexColorType ) {
773 
774 			geometryGroup.__colorArray = new Float32Array( nvertices * 3 );
775 
776 		}
777 
778 		if ( uvType ) {
779 
780 			if ( geometry.faceUvs.length > 0 || geometry.faceVertexUvs.length > 0 ) {
781 
782 				geometryGroup.__uvArray = new Float32Array( nvertices * 2 );
783 
784 			}
785 
786 			if ( geometry.faceUvs.length > 1 || geometry.faceVertexUvs.length > 1 ) {
787 
788 				geometryGroup.__uv2Array = new Float32Array( nvertices * 2 );
789 
790 			}
791 
792 		}
793 
794 		if ( object.geometry.skinWeights.length && object.geometry.skinIndices.length ) {
795 
796 			geometryGroup.__skinIndexArray = new Float32Array( nvertices * 4 );
797 			geometryGroup.__skinWeightArray = new Float32Array( nvertices * 4 );
798 
799 		}
800 
801 		geometryGroup.__faceArray = new Uint16Array( ntris * 3 );
802 		geometryGroup.__lineArray = new Uint16Array( nlines * 2 );
803 
804 		var m, ml;
805 
806 		if ( geometryGroup.numMorphTargets ) {
807 
808 			geometryGroup.__morphTargetsArrays = [];
809 
810 			for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) {
811 
812 				geometryGroup.__morphTargetsArrays.push( new Float32Array( nvertices * 3 ) );
813 
814 			}
815 
816 		}
817 
818 		if ( geometryGroup.numMorphNormals ) {
819 
820 			geometryGroup.__morphNormalsArrays = [];
821 
822 			for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) {
823 
824 				geometryGroup.__morphNormalsArrays.push( new Float32Array( nvertices * 3 ) );
825 
826 			}
827 
828 		}
829 
830 		geometryGroup.__webglFaceCount = ntris * 3;
831 		geometryGroup.__webglLineCount = nlines * 2;
832 
833 
834 		// custom attributes
835 
836 		if ( material.attributes ) {
837 
838 			if ( geometryGroup.__webglCustomAttributesList === undefined ) {
839 
840 				geometryGroup.__webglCustomAttributesList = [];
841 
842 			}
843 
844 			for ( var a in material.attributes ) {
845 
846 				// Do a shallow copy of the attribute object so different geometryGroup chunks use different
847 				// attribute buffers which are correctly indexed in the setMeshBuffers function
848 
849 				var originalAttribute = material.attributes[ a ];
850 
851 				var attribute = {};
852 
853 				for ( var property in originalAttribute ) {
854 
855 					attribute[ property ] = originalAttribute[ property ];
856 
857 				}
858 
859 				if ( !attribute.__webglInitialized || attribute.createUniqueBuffers ) {
860 
861 					attribute.__webglInitialized = true;
862 
863 					var size = 1;		// "f" and "i"
864 
865 					if( attribute.type === "v2" ) size = 2;
866 					else if( attribute.type === "v3" ) size = 3;
867 					else if( attribute.type === "v4" ) size = 4;
868 					else if( attribute.type === "c"  ) size = 3;
869 
870 					attribute.size = size;
871 
872 					attribute.array = new Float32Array( nvertices * size );
873 
874 					attribute.buffer = _gl.createBuffer();
875 					attribute.buffer.belongsToAttribute = a;
876 
877 					originalAttribute.needsUpdate = true;
878 					attribute.__original = originalAttribute;
879 
880 				}
881 
882 				geometryGroup.__webglCustomAttributesList.push( attribute );
883 
884 			}
885 
886 		}
887 
888 		geometryGroup.__inittedArrays = true;
889 
890 	};
891 
892 	function getBufferMaterial( object, geometryGroup ) {
893 
894 		return object.material instanceof THREE.MeshFaceMaterial
895 			? object.material.materials[ geometryGroup.materialIndex ]
896 			: object.material;
897 
898 	};
899 
900 	function materialNeedsSmoothNormals ( material ) {
901 
902 		return material && material.shading !== undefined && material.shading === THREE.SmoothShading;
903 
904 	};
905 
906 	function bufferGuessNormalType ( material ) {
907 
908 		// only MeshBasicMaterial and MeshDepthMaterial don't need normals
909 
910 		if ( ( material instanceof THREE.MeshBasicMaterial && !material.envMap ) || material instanceof THREE.MeshDepthMaterial ) {
911 
912 			return false;
913 
914 		}
915 
916 		if ( materialNeedsSmoothNormals( material ) ) {
917 
918 			return THREE.SmoothShading;
919 
920 		} else {
921 
922 			return THREE.FlatShading;
923 
924 		}
925 
926 	};
927 
928 	function bufferGuessVertexColorType ( material ) {
929 
930 		if ( material.vertexColors ) {
931 
932 			return material.vertexColors;
933 
934 		}
935 
936 		return false;
937 
938 	};
939 
940 	function bufferGuessUVType ( material ) {
941 
942 		// material must use some texture to require uvs
943 
944 		if ( material.map || material.lightMap || material.bumpMap || material.normalMap || material.specularMap || material instanceof THREE.ShaderMaterial ) {
945 
946 			return true;
947 
948 		}
949 
950 		return false;
951 
952 	};
953 
954 	//
955 
956 	function initDirectBuffers( geometry ) {
957 
958 		var a, attribute, type;
959 
960 		for ( a in geometry.attributes ) {
961 
962 			if ( a === "index" ) {
963 
964 				type = _gl.ELEMENT_ARRAY_BUFFER;
965 
966 			} else {
967 
968 				type = _gl.ARRAY_BUFFER;
969 
970 			}
971 
972 			attribute = geometry.attributes[ a ];
973 
974 			attribute.buffer = _gl.createBuffer();
975 
976 			_gl.bindBuffer( type, attribute.buffer );
977 			_gl.bufferData( type, attribute.array, _gl.STATIC_DRAW );
978 
979 		}
980 
981 	};
982 
983 	// Buffer setting
984 
985 	function setParticleBuffers ( geometry, hint, object ) {
986 
987 		var v, c, vertex, offset, index, color,
988 
989 		vertices = geometry.vertices,
990 		vl = vertices.length,
991 
992 		colors = geometry.colors,
993 		cl = colors.length,
994 
995 		vertexArray = geometry.__vertexArray,
996 		colorArray = geometry.__colorArray,
997 
998 		sortArray = geometry.__sortArray,
999 
1000 		dirtyVertices = geometry.verticesNeedUpdate,
1001 		dirtyElements = geometry.elementsNeedUpdate,
1002 		dirtyColors = geometry.colorsNeedUpdate,
1003 
1004 		customAttributes = geometry.__webglCustomAttributesList,
1005 		i, il,
1006 		a, ca, cal, value,
1007 		customAttribute;
1008 
1009 		if ( object.sortParticles ) {
1010 
1011 			_projScreenMatrixPS.copy( _projScreenMatrix );
1012 			_projScreenMatrixPS.multiplySelf( object.matrixWorld );
1013 
1014 			for ( v = 0; v < vl; v ++ ) {
1015 
1016 				vertex = vertices[ v ];
1017 
1018 				_vector3.copy( vertex );
1019 				_projScreenMatrixPS.multiplyVector3( _vector3 );
1020 
1021 				sortArray[ v ] = [ _vector3.z, v ];
1022 
1023 			}
1024 
1025 			sortArray.sort( function( a, b ) { return b[ 0 ] - a[ 0 ]; } );
1026 
1027 			for ( v = 0; v < vl; v ++ ) {
1028 
1029 				vertex = vertices[ sortArray[v][1] ];
1030 
1031 				offset = v * 3;
1032 
1033 				vertexArray[ offset ]     = vertex.x;
1034 				vertexArray[ offset + 1 ] = vertex.y;
1035 				vertexArray[ offset + 2 ] = vertex.z;
1036 
1037 			}
1038 
1039 			for ( c = 0; c < cl; c ++ ) {
1040 
1041 				offset = c * 3;
1042 
1043 				color = colors[ sortArray[c][1] ];
1044 
1045 				colorArray[ offset ]     = color.r;
1046 				colorArray[ offset + 1 ] = color.g;
1047 				colorArray[ offset + 2 ] = color.b;
1048 
1049 			}
1050 
1051 			if ( customAttributes ) {
1052 
1053 				for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
1054 
1055 					customAttribute = customAttributes[ i ];
1056 
1057 					if ( ! ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) ) continue;
1058 
1059 					offset = 0;
1060 
1061 					cal = customAttribute.value.length;
1062 
1063 					if ( customAttribute.size === 1 ) {
1064 
1065 						for ( ca = 0; ca < cal; ca ++ ) {
1066 
1067 							index = sortArray[ ca ][ 1 ];
1068 
1069 							customAttribute.array[ ca ] = customAttribute.value[ index ];
1070 
1071 						}
1072 
1073 					} else if ( customAttribute.size === 2 ) {
1074 
1075 						for ( ca = 0; ca < cal; ca ++ ) {
1076 
1077 							index = sortArray[ ca ][ 1 ];
1078 
1079 							value = customAttribute.value[ index ];
1080 
1081 							customAttribute.array[ offset ] 	= value.x;
1082 							customAttribute.array[ offset + 1 ] = value.y;
1083 
1084 							offset += 2;
1085 
1086 						}
1087 
1088 					} else if ( customAttribute.size === 3 ) {
1089 
1090 						if ( customAttribute.type === "c" ) {
1091 
1092 							for ( ca = 0; ca < cal; ca ++ ) {
1093 
1094 								index = sortArray[ ca ][ 1 ];
1095 
1096 								value = customAttribute.value[ index ];
1097 
1098 								customAttribute.array[ offset ]     = value.r;
1099 								customAttribute.array[ offset + 1 ] = value.g;
1100 								customAttribute.array[ offset + 2 ] = value.b;
1101 
1102 								offset += 3;
1103 
1104 							}
1105 
1106 						} else {
1107 
1108 							for ( ca = 0; ca < cal; ca ++ ) {
1109 
1110 								index = sortArray[ ca ][ 1 ];
1111 
1112 								value = customAttribute.value[ index ];
1113 
1114 								customAttribute.array[ offset ] 	= value.x;
1115 								customAttribute.array[ offset + 1 ] = value.y;
1116 								customAttribute.array[ offset + 2 ] = value.z;
1117 
1118 								offset += 3;
1119 
1120 							}
1121 
1122 						}
1123 
1124 					} else if ( customAttribute.size === 4 ) {
1125 
1126 						for ( ca = 0; ca < cal; ca ++ ) {
1127 
1128 							index = sortArray[ ca ][ 1 ];
1129 
1130 							value = customAttribute.value[ index ];
1131 
1132 							customAttribute.array[ offset ]      = value.x;
1133 							customAttribute.array[ offset + 1  ] = value.y;
1134 							customAttribute.array[ offset + 2  ] = value.z;
1135 							customAttribute.array[ offset + 3  ] = value.w;
1136 
1137 							offset += 4;
1138 
1139 						}
1140 
1141 					}
1142 
1143 				}
1144 
1145 			}
1146 
1147 		} else {
1148 
1149 			if ( dirtyVertices ) {
1150 
1151 				for ( v = 0; v < vl; v ++ ) {
1152 
1153 					vertex = vertices[ v ];
1154 
1155 					offset = v * 3;
1156 
1157 					vertexArray[ offset ]     = vertex.x;
1158 					vertexArray[ offset + 1 ] = vertex.y;
1159 					vertexArray[ offset + 2 ] = vertex.z;
1160 
1161 				}
1162 
1163 			}
1164 
1165 			if ( dirtyColors ) {
1166 
1167 				for ( c = 0; c < cl; c ++ ) {
1168 
1169 					color = colors[ c ];
1170 
1171 					offset = c * 3;
1172 
1173 					colorArray[ offset ]     = color.r;
1174 					colorArray[ offset + 1 ] = color.g;
1175 					colorArray[ offset + 2 ] = color.b;
1176 
1177 				}
1178 
1179 			}
1180 
1181 			if ( customAttributes ) {
1182 
1183 				for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
1184 
1185 					customAttribute = customAttributes[ i ];
1186 
1187 					if ( customAttribute.needsUpdate &&
1188 						 ( customAttribute.boundTo === undefined ||
1189 						   customAttribute.boundTo === "vertices") ) {
1190 
1191 						cal = customAttribute.value.length;
1192 
1193 						offset = 0;
1194 
1195 						if ( customAttribute.size === 1 ) {
1196 
1197 							for ( ca = 0; ca < cal; ca ++ ) {
1198 
1199 								customAttribute.array[ ca ] = customAttribute.value[ ca ];
1200 
1201 							}
1202 
1203 						} else if ( customAttribute.size === 2 ) {
1204 
1205 							for ( ca = 0; ca < cal; ca ++ ) {
1206 
1207 								value = customAttribute.value[ ca ];
1208 
1209 								customAttribute.array[ offset ] 	= value.x;
1210 								customAttribute.array[ offset + 1 ] = value.y;
1211 
1212 								offset += 2;
1213 
1214 							}
1215 
1216 						} else if ( customAttribute.size === 3 ) {
1217 
1218 							if ( customAttribute.type === "c" ) {
1219 
1220 								for ( ca = 0; ca < cal; ca ++ ) {
1221 
1222 									value = customAttribute.value[ ca ];
1223 
1224 									customAttribute.array[ offset ] 	= value.r;
1225 									customAttribute.array[ offset + 1 ] = value.g;
1226 									customAttribute.array[ offset + 2 ] = value.b;
1227 
1228 									offset += 3;
1229 
1230 								}
1231 
1232 							} else {
1233 
1234 								for ( ca = 0; ca < cal; ca ++ ) {
1235 
1236 									value = customAttribute.value[ ca ];
1237 
1238 									customAttribute.array[ offset ] 	= value.x;
1239 									customAttribute.array[ offset + 1 ] = value.y;
1240 									customAttribute.array[ offset + 2 ] = value.z;
1241 
1242 									offset += 3;
1243 
1244 								}
1245 
1246 							}
1247 
1248 						} else if ( customAttribute.size === 4 ) {
1249 
1250 							for ( ca = 0; ca < cal; ca ++ ) {
1251 
1252 								value = customAttribute.value[ ca ];
1253 
1254 								customAttribute.array[ offset ]      = value.x;
1255 								customAttribute.array[ offset + 1  ] = value.y;
1256 								customAttribute.array[ offset + 2  ] = value.z;
1257 								customAttribute.array[ offset + 3  ] = value.w;
1258 
1259 								offset += 4;
1260 
1261 							}
1262 
1263 						}
1264 
1265 					}
1266 
1267 				}
1268 
1269 			}
1270 
1271 		}
1272 
1273 		if ( dirtyVertices || object.sortParticles ) {
1274 
1275 			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer );
1276 			_gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint );
1277 
1278 		}
1279 
1280 		if ( dirtyColors || object.sortParticles ) {
1281 
1282 			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer );
1283 			_gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint );
1284 
1285 		}
1286 
1287 		if ( customAttributes ) {
1288 
1289 			for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
1290 
1291 				customAttribute = customAttributes[ i ];
1292 
1293 				if ( customAttribute.needsUpdate || object.sortParticles ) {
1294 
1295 					_gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer );
1296 					_gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint );
1297 
1298 				}
1299 
1300 			}
1301 
1302 		}
1303 
1304 
1305 	};
1306 
1307 	function setLineBuffers ( geometry, hint ) {
1308 
1309 		var v, c, d, vertex, offset, color,
1310 
1311 		vertices = geometry.vertices,
1312 		colors = geometry.colors,
1313 		lineDistances = geometry.lineDistances,
1314 
1315 		vl = vertices.length,
1316 		cl = colors.length,
1317 		dl = lineDistances.length,
1318 
1319 		vertexArray = geometry.__vertexArray,
1320 		colorArray = geometry.__colorArray,
1321 		lineDistanceArray = geometry.__lineDistanceArray,
1322 
1323 		dirtyVertices = geometry.verticesNeedUpdate,
1324 		dirtyColors = geometry.colorsNeedUpdate,
1325 		dirtyLineDistances = geometry.lineDistancesNeedUpdate,
1326 
1327 		customAttributes = geometry.__webglCustomAttributesList,
1328 
1329 		i, il,
1330 		a, ca, cal, value,
1331 		customAttribute;
1332 
1333 		if ( dirtyVertices ) {
1334 
1335 			for ( v = 0; v < vl; v ++ ) {
1336 
1337 				vertex = vertices[ v ];
1338 
1339 				offset = v * 3;
1340 
1341 				vertexArray[ offset ]     = vertex.x;
1342 				vertexArray[ offset + 1 ] = vertex.y;
1343 				vertexArray[ offset + 2 ] = vertex.z;
1344 
1345 			}
1346 
1347 			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer );
1348 			_gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint );
1349 
1350 		}
1351 
1352 		if ( dirtyColors ) {
1353 
1354 			for ( c = 0; c < cl; c ++ ) {
1355 
1356 				color = colors[ c ];
1357 
1358 				offset = c * 3;
1359 
1360 				colorArray[ offset ]     = color.r;
1361 				colorArray[ offset + 1 ] = color.g;
1362 				colorArray[ offset + 2 ] = color.b;
1363 
1364 			}
1365 
1366 			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer );
1367 			_gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint );
1368 
1369 		}
1370 
1371 		if ( dirtyLineDistances ) {
1372 
1373 			for ( d = 0; d < dl; d ++ ) {
1374 
1375 				lineDistanceArray[ d ] = lineDistances[ d ];
1376 
1377 			}
1378 
1379 			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglLineDistanceBuffer );
1380 			_gl.bufferData( _gl.ARRAY_BUFFER, lineDistanceArray, hint );
1381 
1382 		}
1383 
1384 		if ( customAttributes ) {
1385 
1386 			for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
1387 
1388 				customAttribute = customAttributes[ i ];
1389 
1390 				if ( customAttribute.needsUpdate &&
1391 					 ( customAttribute.boundTo === undefined ||
1392 					   customAttribute.boundTo === "vertices" ) ) {
1393 
1394 					offset = 0;
1395 
1396 					cal = customAttribute.value.length;
1397 
1398 					if ( customAttribute.size === 1 ) {
1399 
1400 						for ( ca = 0; ca < cal; ca ++ ) {
1401 
1402 							customAttribute.array[ ca ] = customAttribute.value[ ca ];
1403 
1404 						}
1405 
1406 					} else if ( customAttribute.size === 2 ) {
1407 
1408 						for ( ca = 0; ca < cal; ca ++ ) {
1409 
1410 							value = customAttribute.value[ ca ];
1411 
1412 							customAttribute.array[ offset ] 	= value.x;
1413 							customAttribute.array[ offset + 1 ] = value.y;
1414 
1415 							offset += 2;
1416 
1417 						}
1418 
1419 					} else if ( customAttribute.size === 3 ) {
1420 
1421 						if ( customAttribute.type === "c" ) {
1422 
1423 							for ( ca = 0; ca < cal; ca ++ ) {
1424 
1425 								value = customAttribute.value[ ca ];
1426 
1427 								customAttribute.array[ offset ] 	= value.r;
1428 								customAttribute.array[ offset + 1 ] = value.g;
1429 								customAttribute.array[ offset + 2 ] = value.b;
1430 
1431 								offset += 3;
1432 
1433 							}
1434 
1435 						} else {
1436 
1437 							for ( ca = 0; ca < cal; ca ++ ) {
1438 
1439 								value = customAttribute.value[ ca ];
1440 
1441 								customAttribute.array[ offset ] 	= value.x;
1442 								customAttribute.array[ offset + 1 ] = value.y;
1443 								customAttribute.array[ offset + 2 ] = value.z;
1444 
1445 								offset += 3;
1446 
1447 							}
1448 
1449 						}
1450 
1451 					} else if ( customAttribute.size === 4 ) {
1452 
1453 						for ( ca = 0; ca < cal; ca ++ ) {
1454 
1455 							value = customAttribute.value[ ca ];
1456 
1457 							customAttribute.array[ offset ] 	 = value.x;
1458 							customAttribute.array[ offset + 1  ] = value.y;
1459 							customAttribute.array[ offset + 2  ] = value.z;
1460 							customAttribute.array[ offset + 3  ] = value.w;
1461 
1462 							offset += 4;
1463 
1464 						}
1465 
1466 					}
1467 
1468 					_gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer );
1469 					_gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint );
1470 
1471 				}
1472 
1473 			}
1474 
1475 		}
1476 
1477 	};
1478 
1479 	function setRibbonBuffers ( geometry, hint ) {
1480 
1481 		var v, c, n, vertex, offset, color, normal,
1482 
1483 		i, il, ca, cal, customAttribute, value,
1484 
1485 		vertices = geometry.vertices,
1486 		colors = geometry.colors,
1487 		normals = geometry.normals,
1488 
1489 		vl = vertices.length,
1490 		cl = colors.length,
1491 		nl = normals.length,
1492 
1493 		vertexArray = geometry.__vertexArray,
1494 		colorArray = geometry.__colorArray,
1495 		normalArray = geometry.__normalArray,
1496 
1497 		dirtyVertices = geometry.verticesNeedUpdate,
1498 		dirtyColors = geometry.colorsNeedUpdate,
1499 		dirtyNormals = geometry.normalsNeedUpdate,
1500 
1501 		customAttributes = geometry.__webglCustomAttributesList;
1502 
1503 		if ( dirtyVertices ) {
1504 
1505 			for ( v = 0; v < vl; v ++ ) {
1506 
1507 				vertex = vertices[ v ];
1508 
1509 				offset = v * 3;
1510 
1511 				vertexArray[ offset ]     = vertex.x;
1512 				vertexArray[ offset + 1 ] = vertex.y;
1513 				vertexArray[ offset + 2 ] = vertex.z;
1514 
1515 			}
1516 
1517 			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer );
1518 			_gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint );
1519 
1520 		}
1521 
1522 		if ( dirtyColors ) {
1523 
1524 			for ( c = 0; c < cl; c ++ ) {
1525 
1526 				color = colors[ c ];
1527 
1528 				offset = c * 3;
1529 
1530 				colorArray[ offset ]     = color.r;
1531 				colorArray[ offset + 1 ] = color.g;
1532 				colorArray[ offset + 2 ] = color.b;
1533 
1534 			}
1535 
1536 			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer );
1537 			_gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint );
1538 
1539 		}
1540 
1541 		if ( dirtyNormals ) {
1542 
1543 			for ( n = 0; n < nl; n ++ ) {
1544 
1545 				normal = normals[ n ];
1546 
1547 				offset = n * 3;
1548 
1549 				normalArray[ offset ]     = normal.x;
1550 				normalArray[ offset + 1 ] = normal.y;
1551 				normalArray[ offset + 2 ] = normal.z;
1552 
1553 			}
1554 
1555 			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglNormalBuffer );
1556 			_gl.bufferData( _gl.ARRAY_BUFFER, normalArray, hint );
1557 
1558 		}
1559 
1560 		if ( customAttributes ) {
1561 
1562 			for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
1563 
1564 				customAttribute = customAttributes[ i ];
1565 
1566 				if ( customAttribute.needsUpdate &&
1567 					 ( customAttribute.boundTo === undefined ||
1568 					   customAttribute.boundTo === "vertices" ) ) {
1569 
1570 					offset = 0;
1571 
1572 					cal = customAttribute.value.length;
1573 
1574 					if ( customAttribute.size === 1 ) {
1575 
1576 						for ( ca = 0; ca < cal; ca ++ ) {
1577 
1578 							customAttribute.array[ ca ] = customAttribute.value[ ca ];
1579 
1580 						}
1581 
1582 					} else if ( customAttribute.size === 2 ) {
1583 
1584 						for ( ca = 0; ca < cal; ca ++ ) {
1585 
1586 							value = customAttribute.value[ ca ];
1587 
1588 							customAttribute.array[ offset ] 	= value.x;
1589 							customAttribute.array[ offset + 1 ] = value.y;
1590 
1591 							offset += 2;
1592 
1593 						}
1594 
1595 					} else if ( customAttribute.size === 3 ) {
1596 
1597 						if ( customAttribute.type === "c" ) {
1598 
1599 							for ( ca = 0; ca < cal; ca ++ ) {
1600 
1601 								value = customAttribute.value[ ca ];
1602 
1603 								customAttribute.array[ offset ] 	= value.r;
1604 								customAttribute.array[ offset + 1 ] = value.g;
1605 								customAttribute.array[ offset + 2 ] = value.b;
1606 
1607 								offset += 3;
1608 
1609 							}
1610 
1611 						} else {
1612 
1613 							for ( ca = 0; ca < cal; ca ++ ) {
1614 
1615 								value = customAttribute.value[ ca ];
1616 
1617 								customAttribute.array[ offset ] 	= value.x;
1618 								customAttribute.array[ offset + 1 ] = value.y;
1619 								customAttribute.array[ offset + 2 ] = value.z;
1620 
1621 								offset += 3;
1622 
1623 							}
1624 
1625 						}
1626 
1627 					} else if ( customAttribute.size === 4 ) {
1628 
1629 						for ( ca = 0; ca < cal; ca ++ ) {
1630 
1631 							value = customAttribute.value[ ca ];
1632 
1633 							customAttribute.array[ offset ] 	 = value.x;
1634 							customAttribute.array[ offset + 1  ] = value.y;
1635 							customAttribute.array[ offset + 2  ] = value.z;
1636 							customAttribute.array[ offset + 3  ] = value.w;
1637 
1638 							offset += 4;
1639 
1640 						}
1641 
1642 					}
1643 
1644 					_gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer );
1645 					_gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint );
1646 
1647 				}
1648 
1649 			}
1650 
1651 		}
1652 
1653 	};
1654 
1655 	function setMeshBuffers( geometryGroup, object, hint, dispose, material ) {
1656 
1657 		if ( ! geometryGroup.__inittedArrays ) {
1658 
1659 			return;
1660 
1661 		}
1662 
1663 		var normalType = bufferGuessNormalType( material ),
1664 		vertexColorType = bufferGuessVertexColorType( material ),
1665 		uvType = bufferGuessUVType( material ),
1666 
1667 		needsSmoothNormals = ( normalType === THREE.SmoothShading );
1668 
1669 		var f, fl, fi, face,
1670 		vertexNormals, faceNormal, normal,
1671 		vertexColors, faceColor,
1672 		vertexTangents,
1673 		uv, uv2, v1, v2, v3, v4, t1, t2, t3, t4, n1, n2, n3, n4,
1674 		c1, c2, c3, c4,
1675 		sw1, sw2, sw3, sw4,
1676 		si1, si2, si3, si4,
1677 		sa1, sa2, sa3, sa4,
1678 		sb1, sb2, sb3, sb4,
1679 		m, ml, i, il,
1680 		vn, uvi, uv2i,
1681 		vk, vkl, vka,
1682 		nka, chf, faceVertexNormals,
1683 		a,
1684 
1685 		vertexIndex = 0,
1686 
1687 		offset = 0,
1688 		offset_uv = 0,
1689 		offset_uv2 = 0,
1690 		offset_face = 0,
1691 		offset_normal = 0,
1692 		offset_tangent = 0,
1693 		offset_line = 0,
1694 		offset_color = 0,
1695 		offset_skin = 0,
1696 		offset_morphTarget = 0,
1697 		offset_custom = 0,
1698 		offset_customSrc = 0,
1699 
1700 		value,
1701 
1702 		vertexArray = geometryGroup.__vertexArray,
1703 		uvArray = geometryGroup.__uvArray,
1704 		uv2Array = geometryGroup.__uv2Array,
1705 		normalArray = geometryGroup.__normalArray,
1706 		tangentArray = geometryGroup.__tangentArray,
1707 		colorArray = geometryGroup.__colorArray,
1708 
1709 		skinIndexArray = geometryGroup.__skinIndexArray,
1710 		skinWeightArray = geometryGroup.__skinWeightArray,
1711 
1712 		morphTargetsArrays = geometryGroup.__morphTargetsArrays,
1713 		morphNormalsArrays = geometryGroup.__morphNormalsArrays,
1714 
1715 		customAttributes = geometryGroup.__webglCustomAttributesList,
1716 		customAttribute,
1717 
1718 		faceArray = geometryGroup.__faceArray,
1719 		lineArray = geometryGroup.__lineArray,
1720 
1721 		geometry = object.geometry, // this is shared for all chunks
1722 
1723 		dirtyVertices = geometry.verticesNeedUpdate,
1724 		dirtyElements = geometry.elementsNeedUpdate,
1725 		dirtyUvs = geometry.uvsNeedUpdate,
1726 		dirtyNormals = geometry.normalsNeedUpdate,
1727 		dirtyTangents = geometry.tangentsNeedUpdate,
1728 		dirtyColors = geometry.colorsNeedUpdate,
1729 		dirtyMorphTargets = geometry.morphTargetsNeedUpdate,
1730 
1731 		vertices = geometry.vertices,
1732 		chunk_faces3 = geometryGroup.faces3,
1733 		chunk_faces4 = geometryGroup.faces4,
1734 		obj_faces = geometry.faces,
1735 
1736 		obj_uvs  = geometry.faceVertexUvs[ 0 ],
1737 		obj_uvs2 = geometry.faceVertexUvs[ 1 ],
1738 
1739 		obj_colors = geometry.colors,
1740 
1741 		obj_skinIndices = geometry.skinIndices,
1742 		obj_skinWeights = geometry.skinWeights,
1743 
1744 		morphTargets = geometry.morphTargets,
1745 		morphNormals = geometry.morphNormals;
1746 
1747 		if ( dirtyVertices ) {
1748 
1749 			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
1750 
1751 				face = obj_faces[ chunk_faces3[ f ] ];
1752 
1753 				v1 = vertices[ face.a ];
1754 				v2 = vertices[ face.b ];
1755 				v3 = vertices[ face.c ];
1756 
1757 				vertexArray[ offset ]     = v1.x;
1758 				vertexArray[ offset + 1 ] = v1.y;
1759 				vertexArray[ offset + 2 ] = v1.z;
1760 
1761 				vertexArray[ offset + 3 ] = v2.x;
1762 				vertexArray[ offset + 4 ] = v2.y;
1763 				vertexArray[ offset + 5 ] = v2.z;
1764 
1765 				vertexArray[ offset + 6 ] = v3.x;
1766 				vertexArray[ offset + 7 ] = v3.y;
1767 				vertexArray[ offset + 8 ] = v3.z;
1768 
1769 				offset += 9;
1770 
1771 			}
1772 
1773 			for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
1774 
1775 				face = obj_faces[ chunk_faces4[ f ] ];
1776 
1777 				v1 = vertices[ face.a ];
1778 				v2 = vertices[ face.b ];
1779 				v3 = vertices[ face.c ];
1780 				v4 = vertices[ face.d ];
1781 
1782 				vertexArray[ offset ]     = v1.x;
1783 				vertexArray[ offset + 1 ] = v1.y;
1784 				vertexArray[ offset + 2 ] = v1.z;
1785 
1786 				vertexArray[ offset + 3 ] = v2.x;
1787 				vertexArray[ offset + 4 ] = v2.y;
1788 				vertexArray[ offset + 5 ] = v2.z;
1789 
1790 				vertexArray[ offset + 6 ] = v3.x;
1791 				vertexArray[ offset + 7 ] = v3.y;
1792 				vertexArray[ offset + 8 ] = v3.z;
1793 
1794 				vertexArray[ offset + 9 ]  = v4.x;
1795 				vertexArray[ offset + 10 ] = v4.y;
1796 				vertexArray[ offset + 11 ] = v4.z;
1797 
1798 				offset += 12;
1799 
1800 			}
1801 
1802 			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer );
1803 			_gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint );
1804 
1805 		}
1806 
1807 		if ( dirtyMorphTargets ) {
1808 
1809 			for ( vk = 0, vkl = morphTargets.length; vk < vkl; vk ++ ) {
1810 
1811 				offset_morphTarget = 0;
1812 
1813 				for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
1814 
1815 					chf = chunk_faces3[ f ];
1816 					face = obj_faces[ chf ];
1817 
1818 					// morph positions
1819 
1820 					v1 = morphTargets[ vk ].vertices[ face.a ];
1821 					v2 = morphTargets[ vk ].vertices[ face.b ];
1822 					v3 = morphTargets[ vk ].vertices[ face.c ];
1823 
1824 					vka = morphTargetsArrays[ vk ];
1825 
1826 					vka[ offset_morphTarget ] 	  = v1.x;
1827 					vka[ offset_morphTarget + 1 ] = v1.y;
1828 					vka[ offset_morphTarget + 2 ] = v1.z;
1829 
1830 					vka[ offset_morphTarget + 3 ] = v2.x;
1831 					vka[ offset_morphTarget + 4 ] = v2.y;
1832 					vka[ offset_morphTarget + 5 ] = v2.z;
1833 
1834 					vka[ offset_morphTarget + 6 ] = v3.x;
1835 					vka[ offset_morphTarget + 7 ] = v3.y;
1836 					vka[ offset_morphTarget + 8 ] = v3.z;
1837 
1838 					// morph normals
1839 
1840 					if ( material.morphNormals ) {
1841 
1842 						if ( needsSmoothNormals ) {
1843 
1844 							faceVertexNormals = morphNormals[ vk ].vertexNormals[ chf ];
1845 
1846 							n1 = faceVertexNormals.a;
1847 							n2 = faceVertexNormals.b;
1848 							n3 = faceVertexNormals.c;
1849 
1850 						} else {
1851 
1852 							n1 = morphNormals[ vk ].faceNormals[ chf ];
1853 							n2 = n1;
1854 							n3 = n1;
1855 
1856 						}
1857 
1858 						nka = morphNormalsArrays[ vk ];
1859 
1860 						nka[ offset_morphTarget ] 	  = n1.x;
1861 						nka[ offset_morphTarget + 1 ] = n1.y;
1862 						nka[ offset_morphTarget + 2 ] = n1.z;
1863 
1864 						nka[ offset_morphTarget + 3 ] = n2.x;
1865 						nka[ offset_morphTarget + 4 ] = n2.y;
1866 						nka[ offset_morphTarget + 5 ] = n2.z;
1867 
1868 						nka[ offset_morphTarget + 6 ] = n3.x;
1869 						nka[ offset_morphTarget + 7 ] = n3.y;
1870 						nka[ offset_morphTarget + 8 ] = n3.z;
1871 
1872 					}
1873 
1874 					//
1875 
1876 					offset_morphTarget += 9;
1877 
1878 				}
1879 
1880 				for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
1881 
1882 					chf = chunk_faces4[ f ];
1883 					face = obj_faces[ chf ];
1884 
1885 					// morph positions
1886 
1887 					v1 = morphTargets[ vk ].vertices[ face.a ];
1888 					v2 = morphTargets[ vk ].vertices[ face.b ];
1889 					v3 = morphTargets[ vk ].vertices[ face.c ];
1890 					v4 = morphTargets[ vk ].vertices[ face.d ];
1891 
1892 					vka = morphTargetsArrays[ vk ];
1893 
1894 					vka[ offset_morphTarget ] 	  = v1.x;
1895 					vka[ offset_morphTarget + 1 ] = v1.y;
1896 					vka[ offset_morphTarget + 2 ] = v1.z;
1897 
1898 					vka[ offset_morphTarget + 3 ] = v2.x;
1899 					vka[ offset_morphTarget + 4 ] = v2.y;
1900 					vka[ offset_morphTarget + 5 ] = v2.z;
1901 
1902 					vka[ offset_morphTarget + 6 ] = v3.x;
1903 					vka[ offset_morphTarget + 7 ] = v3.y;
1904 					vka[ offset_morphTarget + 8 ] = v3.z;
1905 
1906 					vka[ offset_morphTarget + 9 ]  = v4.x;
1907 					vka[ offset_morphTarget + 10 ] = v4.y;
1908 					vka[ offset_morphTarget + 11 ] = v4.z;
1909 
1910 					// morph normals
1911 
1912 					if ( material.morphNormals ) {
1913 
1914 						if ( needsSmoothNormals ) {
1915 
1916 							faceVertexNormals = morphNormals[ vk ].vertexNormals[ chf ];
1917 
1918 							n1 = faceVertexNormals.a;
1919 							n2 = faceVertexNormals.b;
1920 							n3 = faceVertexNormals.c;
1921 							n4 = faceVertexNormals.d;
1922 
1923 						} else {
1924 
1925 							n1 = morphNormals[ vk ].faceNormals[ chf ];
1926 							n2 = n1;
1927 							n3 = n1;
1928 							n4 = n1;
1929 
1930 						}
1931 
1932 						nka = morphNormalsArrays[ vk ];
1933 
1934 						nka[ offset_morphTarget ] 	  = n1.x;
1935 						nka[ offset_morphTarget + 1 ] = n1.y;
1936 						nka[ offset_morphTarget + 2 ] = n1.z;
1937 
1938 						nka[ offset_morphTarget + 3 ] = n2.x;
1939 						nka[ offset_morphTarget + 4 ] = n2.y;
1940 						nka[ offset_morphTarget + 5 ] = n2.z;
1941 
1942 						nka[ offset_morphTarget + 6 ] = n3.x;
1943 						nka[ offset_morphTarget + 7 ] = n3.y;
1944 						nka[ offset_morphTarget + 8 ] = n3.z;
1945 
1946 						nka[ offset_morphTarget + 9 ]  = n4.x;
1947 						nka[ offset_morphTarget + 10 ] = n4.y;
1948 						nka[ offset_morphTarget + 11 ] = n4.z;
1949 
1950 					}
1951 
1952 					//
1953 
1954 					offset_morphTarget += 12;
1955 
1956 				}
1957 
1958 				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ vk ] );
1959 				_gl.bufferData( _gl.ARRAY_BUFFER, morphTargetsArrays[ vk ], hint );
1960 
1961 				if ( material.morphNormals ) {
1962 
1963 					_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ vk ] );
1964 					_gl.bufferData( _gl.ARRAY_BUFFER, morphNormalsArrays[ vk ], hint );
1965 
1966 				}
1967 
1968 			}
1969 
1970 		}
1971 
1972 		if ( obj_skinWeights.length ) {
1973 
1974 			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
1975 
1976 				face = obj_faces[ chunk_faces3[ f ]	];
1977 
1978 				// weights
1979 
1980 				sw1 = obj_skinWeights[ face.a ];
1981 				sw2 = obj_skinWeights[ face.b ];
1982 				sw3 = obj_skinWeights[ face.c ];
1983 
1984 				skinWeightArray[ offset_skin ]     = sw1.x;
1985 				skinWeightArray[ offset_skin + 1 ] = sw1.y;
1986 				skinWeightArray[ offset_skin + 2 ] = sw1.z;
1987 				skinWeightArray[ offset_skin + 3 ] = sw1.w;
1988 
1989 				skinWeightArray[ offset_skin + 4 ] = sw2.x;
1990 				skinWeightArray[ offset_skin + 5 ] = sw2.y;
1991 				skinWeightArray[ offset_skin + 6 ] = sw2.z;
1992 				skinWeightArray[ offset_skin + 7 ] = sw2.w;
1993 
1994 				skinWeightArray[ offset_skin + 8 ]  = sw3.x;
1995 				skinWeightArray[ offset_skin + 9 ]  = sw3.y;
1996 				skinWeightArray[ offset_skin + 10 ] = sw3.z;
1997 				skinWeightArray[ offset_skin + 11 ] = sw3.w;
1998 
1999 				// indices
2000 
2001 				si1 = obj_skinIndices[ face.a ];
2002 				si2 = obj_skinIndices[ face.b ];
2003 				si3 = obj_skinIndices[ face.c ];
2004 
2005 				skinIndexArray[ offset_skin ]     = si1.x;
2006 				skinIndexArray[ offset_skin + 1 ] = si1.y;
2007 				skinIndexArray[ offset_skin + 2 ] = si1.z;
2008 				skinIndexArray[ offset_skin + 3 ] = si1.w;
2009 
2010 				skinIndexArray[ offset_skin + 4 ] = si2.x;
2011 				skinIndexArray[ offset_skin + 5 ] = si2.y;
2012 				skinIndexArray[ offset_skin + 6 ] = si2.z;
2013 				skinIndexArray[ offset_skin + 7 ] = si2.w;
2014 
2015 				skinIndexArray[ offset_skin + 8 ]  = si3.x;
2016 				skinIndexArray[ offset_skin + 9 ]  = si3.y;
2017 				skinIndexArray[ offset_skin + 10 ] = si3.z;
2018 				skinIndexArray[ offset_skin + 11 ] = si3.w;
2019 
2020 				offset_skin += 12;
2021 
2022 			}
2023 
2024 			for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
2025 
2026 				face = obj_faces[ chunk_faces4[ f ] ];
2027 
2028 				// weights
2029 
2030 				sw1 = obj_skinWeights[ face.a ];
2031 				sw2 = obj_skinWeights[ face.b ];
2032 				sw3 = obj_skinWeights[ face.c ];
2033 				sw4 = obj_skinWeights[ face.d ];
2034 
2035 				skinWeightArray[ offset_skin ]     = sw1.x;
2036 				skinWeightArray[ offset_skin + 1 ] = sw1.y;
2037 				skinWeightArray[ offset_skin + 2 ] = sw1.z;
2038 				skinWeightArray[ offset_skin + 3 ] = sw1.w;
2039 
2040 				skinWeightArray[ offset_skin + 4 ] = sw2.x;
2041 				skinWeightArray[ offset_skin + 5 ] = sw2.y;
2042 				skinWeightArray[ offset_skin + 6 ] = sw2.z;
2043 				skinWeightArray[ offset_skin + 7 ] = sw2.w;
2044 
2045 				skinWeightArray[ offset_skin + 8 ]  = sw3.x;
2046 				skinWeightArray[ offset_skin + 9 ]  = sw3.y;
2047 				skinWeightArray[ offset_skin + 10 ] = sw3.z;
2048 				skinWeightArray[ offset_skin + 11 ] = sw3.w;
2049 
2050 				skinWeightArray[ offset_skin + 12 ] = sw4.x;
2051 				skinWeightArray[ offset_skin + 13 ] = sw4.y;
2052 				skinWeightArray[ offset_skin + 14 ] = sw4.z;
2053 				skinWeightArray[ offset_skin + 15 ] = sw4.w;
2054 
2055 				// indices
2056 
2057 				si1 = obj_skinIndices[ face.a ];
2058 				si2 = obj_skinIndices[ face.b ];
2059 				si3 = obj_skinIndices[ face.c ];
2060 				si4 = obj_skinIndices[ face.d ];
2061 
2062 				skinIndexArray[ offset_skin ]     = si1.x;
2063 				skinIndexArray[ offset_skin + 1 ] = si1.y;
2064 				skinIndexArray[ offset_skin + 2 ] = si1.z;
2065 				skinIndexArray[ offset_skin + 3 ] = si1.w;
2066 
2067 				skinIndexArray[ offset_skin + 4 ] = si2.x;
2068 				skinIndexArray[ offset_skin + 5 ] = si2.y;
2069 				skinIndexArray[ offset_skin + 6 ] = si2.z;
2070 				skinIndexArray[ offset_skin + 7 ] = si2.w;
2071 
2072 				skinIndexArray[ offset_skin + 8 ]  = si3.x;
2073 				skinIndexArray[ offset_skin + 9 ]  = si3.y;
2074 				skinIndexArray[ offset_skin + 10 ] = si3.z;
2075 				skinIndexArray[ offset_skin + 11 ] = si3.w;
2076 
2077 				skinIndexArray[ offset_skin + 12 ] = si4.x;
2078 				skinIndexArray[ offset_skin + 13 ] = si4.y;
2079 				skinIndexArray[ offset_skin + 14 ] = si4.z;
2080 				skinIndexArray[ offset_skin + 15 ] = si4.w;
2081 
2082 				offset_skin += 16;
2083 
2084 			}
2085 
2086 			if ( offset_skin > 0 ) {
2087 
2088 				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer );
2089 				_gl.bufferData( _gl.ARRAY_BUFFER, skinIndexArray, hint );
2090 
2091 				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer );
2092 				_gl.bufferData( _gl.ARRAY_BUFFER, skinWeightArray, hint );
2093 
2094 			}
2095 
2096 		}
2097 
2098 		if ( dirtyColors && vertexColorType ) {
2099 
2100 			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
2101 
2102 				face = obj_faces[ chunk_faces3[ f ]	];
2103 
2104 				vertexColors = face.vertexColors;
2105 				faceColor = face.color;
2106 
2107 				if ( vertexColors.length === 3 && vertexColorType === THREE.VertexColors ) {
2108 
2109 					c1 = vertexColors[ 0 ];
2110 					c2 = vertexColors[ 1 ];
2111 					c3 = vertexColors[ 2 ];
2112 
2113 				} else {
2114 
2115 					c1 = faceColor;
2116 					c2 = faceColor;
2117 					c3 = faceColor;
2118 
2119 				}
2120 
2121 				colorArray[ offset_color ]     = c1.r;
2122 				colorArray[ offset_color + 1 ] = c1.g;
2123 				colorArray[ offset_color + 2 ] = c1.b;
2124 
2125 				colorArray[ offset_color + 3 ] = c2.r;
2126 				colorArray[ offset_color + 4 ] = c2.g;
2127 				colorArray[ offset_color + 5 ] = c2.b;
2128 
2129 				colorArray[ offset_color + 6 ] = c3.r;
2130 				colorArray[ offset_color + 7 ] = c3.g;
2131 				colorArray[ offset_color + 8 ] = c3.b;
2132 
2133 				offset_color += 9;
2134 
2135 			}
2136 
2137 			for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
2138 
2139 				face = obj_faces[ chunk_faces4[ f ] ];
2140 
2141 				vertexColors = face.vertexColors;
2142 				faceColor = face.color;
2143 
2144 				if ( vertexColors.length === 4 && vertexColorType === THREE.VertexColors ) {
2145 
2146 					c1 = vertexColors[ 0 ];
2147 					c2 = vertexColors[ 1 ];
2148 					c3 = vertexColors[ 2 ];
2149 					c4 = vertexColors[ 3 ];
2150 
2151 				} else {
2152 
2153 					c1 = faceColor;
2154 					c2 = faceColor;
2155 					c3 = faceColor;
2156 					c4 = faceColor;
2157 
2158 				}
2159 
2160 				colorArray[ offset_color ]     = c1.r;
2161 				colorArray[ offset_color + 1 ] = c1.g;
2162 				colorArray[ offset_color + 2 ] = c1.b;
2163 
2164 				colorArray[ offset_color + 3 ] = c2.r;
2165 				colorArray[ offset_color + 4 ] = c2.g;
2166 				colorArray[ offset_color + 5 ] = c2.b;
2167 
2168 				colorArray[ offset_color + 6 ] = c3.r;
2169 				colorArray[ offset_color + 7 ] = c3.g;
2170 				colorArray[ offset_color + 8 ] = c3.b;
2171 
2172 				colorArray[ offset_color + 9 ]  = c4.r;
2173 				colorArray[ offset_color + 10 ] = c4.g;
2174 				colorArray[ offset_color + 11 ] = c4.b;
2175 
2176 				offset_color += 12;
2177 
2178 			}
2179 
2180 			if ( offset_color > 0 ) {
2181 
2182 				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer );
2183 				_gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint );
2184 
2185 			}
2186 
2187 		}
2188 
2189 		if ( dirtyTangents && geometry.hasTangents ) {
2190 
2191 			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
2192 
2193 				face = obj_faces[ chunk_faces3[ f ]	];
2194 
2195 				vertexTangents = face.vertexTangents;
2196 
2197 				t1 = vertexTangents[ 0 ];
2198 				t2 = vertexTangents[ 1 ];
2199 				t3 = vertexTangents[ 2 ];
2200 
2201 				tangentArray[ offset_tangent ]     = t1.x;
2202 				tangentArray[ offset_tangent + 1 ] = t1.y;
2203 				tangentArray[ offset_tangent + 2 ] = t1.z;
2204 				tangentArray[ offset_tangent + 3 ] = t1.w;
2205 
2206 				tangentArray[ offset_tangent + 4 ] = t2.x;
2207 				tangentArray[ offset_tangent + 5 ] = t2.y;
2208 				tangentArray[ offset_tangent + 6 ] = t2.z;
2209 				tangentArray[ offset_tangent + 7 ] = t2.w;
2210 
2211 				tangentArray[ offset_tangent + 8 ]  = t3.x;
2212 				tangentArray[ offset_tangent + 9 ]  = t3.y;
2213 				tangentArray[ offset_tangent + 10 ] = t3.z;
2214 				tangentArray[ offset_tangent + 11 ] = t3.w;
2215 
2216 				offset_tangent += 12;
2217 
2218 			}
2219 
2220 			for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
2221 
2222 				face = obj_faces[ chunk_faces4[ f ] ];
2223 
2224 				vertexTangents = face.vertexTangents;
2225 
2226 				t1 = vertexTangents[ 0 ];
2227 				t2 = vertexTangents[ 1 ];
2228 				t3 = vertexTangents[ 2 ];
2229 				t4 = vertexTangents[ 3 ];
2230 
2231 				tangentArray[ offset_tangent ]     = t1.x;
2232 				tangentArray[ offset_tangent + 1 ] = t1.y;
2233 				tangentArray[ offset_tangent + 2 ] = t1.z;
2234 				tangentArray[ offset_tangent + 3 ] = t1.w;
2235 
2236 				tangentArray[ offset_tangent + 4 ] = t2.x;
2237 				tangentArray[ offset_tangent + 5 ] = t2.y;
2238 				tangentArray[ offset_tangent + 6 ] = t2.z;
2239 				tangentArray[ offset_tangent + 7 ] = t2.w;
2240 
2241 				tangentArray[ offset_tangent + 8 ]  = t3.x;
2242 				tangentArray[ offset_tangent + 9 ]  = t3.y;
2243 				tangentArray[ offset_tangent + 10 ] = t3.z;
2244 				tangentArray[ offset_tangent + 11 ] = t3.w;
2245 
2246 				tangentArray[ offset_tangent + 12 ] = t4.x;
2247 				tangentArray[ offset_tangent + 13 ] = t4.y;
2248 				tangentArray[ offset_tangent + 14 ] = t4.z;
2249 				tangentArray[ offset_tangent + 15 ] = t4.w;
2250 
2251 				offset_tangent += 16;
2252 
2253 			}
2254 
2255 			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer );
2256 			_gl.bufferData( _gl.ARRAY_BUFFER, tangentArray, hint );
2257 
2258 		}
2259 
2260 		if ( dirtyNormals && normalType ) {
2261 
2262 			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
2263 
2264 				face = obj_faces[ chunk_faces3[ f ]	];
2265 
2266 				vertexNormals = face.vertexNormals;
2267 				faceNormal = face.normal;
2268 
2269 				if ( vertexNormals.length === 3 && needsSmoothNormals ) {
2270 
2271 					for ( i = 0; i < 3; i ++ ) {
2272 
2273 						vn = vertexNormals[ i ];
2274 
2275 						normalArray[ offset_normal ]     = vn.x;
2276 						normalArray[ offset_normal + 1 ] = vn.y;
2277 						normalArray[ offset_normal + 2 ] = vn.z;
2278 
2279 						offset_normal += 3;
2280 
2281 					}
2282 
2283 				} else {
2284 
2285 					for ( i = 0; i < 3; i ++ ) {
2286 
2287 						normalArray[ offset_normal ]     = faceNormal.x;
2288 						normalArray[ offset_normal + 1 ] = faceNormal.y;
2289 						normalArray[ offset_normal + 2 ] = faceNormal.z;
2290 
2291 						offset_normal += 3;
2292 
2293 					}
2294 
2295 				}
2296 
2297 			}
2298 
2299 			for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
2300 
2301 				face = obj_faces[ chunk_faces4[ f ] ];
2302 
2303 				vertexNormals = face.vertexNormals;
2304 				faceNormal = face.normal;
2305 
2306 				if ( vertexNormals.length === 4 && needsSmoothNormals ) {
2307 
2308 					for ( i = 0; i < 4; i ++ ) {
2309 
2310 						vn = vertexNormals[ i ];
2311 
2312 						normalArray[ offset_normal ]     = vn.x;
2313 						normalArray[ offset_normal + 1 ] = vn.y;
2314 						normalArray[ offset_normal + 2 ] = vn.z;
2315 
2316 						offset_normal += 3;
2317 
2318 					}
2319 
2320 				} else {
2321 
2322 					for ( i = 0; i < 4; i ++ ) {
2323 
2324 						normalArray[ offset_normal ]     = faceNormal.x;
2325 						normalArray[ offset_normal + 1 ] = faceNormal.y;
2326 						normalArray[ offset_normal + 2 ] = faceNormal.z;
2327 
2328 						offset_normal += 3;
2329 
2330 					}
2331 
2332 				}
2333 
2334 			}
2335 
2336 			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer );
2337 			_gl.bufferData( _gl.ARRAY_BUFFER, normalArray, hint );
2338 
2339 		}
2340 
2341 		if ( dirtyUvs && obj_uvs && uvType ) {
2342 
2343 			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
2344 
2345 				fi = chunk_faces3[ f ];
2346 
2347 				uv = obj_uvs[ fi ];
2348 
2349 				if ( uv === undefined ) continue;
2350 
2351 				for ( i = 0; i < 3; i ++ ) {
2352 
2353 					uvi = uv[ i ];
2354 
2355 					uvArray[ offset_uv ]     = uvi.u;
2356 					uvArray[ offset_uv + 1 ] = uvi.v;
2357 
2358 					offset_uv += 2;
2359 
2360 				}
2361 
2362 			}
2363 
2364 			for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
2365 
2366 				fi = chunk_faces4[ f ];
2367 
2368 				uv = obj_uvs[ fi ];
2369 
2370 				if ( uv === undefined ) continue;
2371 
2372 				for ( i = 0; i < 4; i ++ ) {
2373 
2374 					uvi = uv[ i ];
2375 
2376 					uvArray[ offset_uv ]     = uvi.u;
2377 					uvArray[ offset_uv + 1 ] = uvi.v;
2378 
2379 					offset_uv += 2;
2380 
2381 				}
2382 
2383 			}
2384 
2385 			if ( offset_uv > 0 ) {
2386 
2387 				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer );
2388 				_gl.bufferData( _gl.ARRAY_BUFFER, uvArray, hint );
2389 
2390 			}
2391 
2392 		}
2393 
2394 		if ( dirtyUvs && obj_uvs2 && uvType ) {
2395 
2396 			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
2397 
2398 				fi = chunk_faces3[ f ];
2399 
2400 				uv2 = obj_uvs2[ fi ];
2401 
2402 				if ( uv2 === undefined ) continue;
2403 
2404 				for ( i = 0; i < 3; i ++ ) {
2405 
2406 					uv2i = uv2[ i ];
2407 
2408 					uv2Array[ offset_uv2 ]     = uv2i.u;
2409 					uv2Array[ offset_uv2 + 1 ] = uv2i.v;
2410 
2411 					offset_uv2 += 2;
2412 
2413 				}
2414 
2415 			}
2416 
2417 			for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
2418 
2419 				fi = chunk_faces4[ f ];
2420 
2421 				uv2 = obj_uvs2[ fi ];
2422 
2423 				if ( uv2 === undefined ) continue;
2424 
2425 				for ( i = 0; i < 4; i ++ ) {
2426 
2427 					uv2i = uv2[ i ];
2428 
2429 					uv2Array[ offset_uv2 ]     = uv2i.u;
2430 					uv2Array[ offset_uv2 + 1 ] = uv2i.v;
2431 
2432 					offset_uv2 += 2;
2433 
2434 				}
2435 
2436 			}
2437 
2438 			if ( offset_uv2 > 0 ) {
2439 
2440 				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer );
2441 				_gl.bufferData( _gl.ARRAY_BUFFER, uv2Array, hint );
2442 
2443 			}
2444 
2445 		}
2446 
2447 		if ( dirtyElements ) {
2448 
2449 			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
2450 
2451 				faceArray[ offset_face ] 	 = vertexIndex;
2452 				faceArray[ offset_face + 1 ] = vertexIndex + 1;
2453 				faceArray[ offset_face + 2 ] = vertexIndex + 2;
2454 
2455 				offset_face += 3;
2456 
2457 				lineArray[ offset_line ]     = vertexIndex;
2458 				lineArray[ offset_line + 1 ] = vertexIndex + 1;
2459 
2460 				lineArray[ offset_line + 2 ] = vertexIndex;
2461 				lineArray[ offset_line + 3 ] = vertexIndex + 2;
2462 
2463 				lineArray[ offset_line + 4 ] = vertexIndex + 1;
2464 				lineArray[ offset_line + 5 ] = vertexIndex + 2;
2465 
2466 				offset_line += 6;
2467 
2468 				vertexIndex += 3;
2469 
2470 			}
2471 
2472 			for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
2473 
2474 				faceArray[ offset_face ]     = vertexIndex;
2475 				faceArray[ offset_face + 1 ] = vertexIndex + 1;
2476 				faceArray[ offset_face + 2 ] = vertexIndex + 3;
2477 
2478 				faceArray[ offset_face + 3 ] = vertexIndex + 1;
2479 				faceArray[ offset_face + 4 ] = vertexIndex + 2;
2480 				faceArray[ offset_face + 5 ] = vertexIndex + 3;
2481 
2482 				offset_face += 6;
2483 
2484 				lineArray[ offset_line ]     = vertexIndex;
2485 				lineArray[ offset_line + 1 ] = vertexIndex + 1;
2486 
2487 				lineArray[ offset_line + 2 ] = vertexIndex;
2488 				lineArray[ offset_line + 3 ] = vertexIndex + 3;
2489 
2490 				lineArray[ offset_line + 4 ] = vertexIndex + 1;
2491 				lineArray[ offset_line + 5 ] = vertexIndex + 2;
2492 
2493 				lineArray[ offset_line + 6 ] = vertexIndex + 2;
2494 				lineArray[ offset_line + 7 ] = vertexIndex + 3;
2495 
2496 				offset_line += 8;
2497 
2498 				vertexIndex += 4;
2499 
2500 			}
2501 
2502 			_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer );
2503 			_gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, faceArray, hint );
2504 
2505 			_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer );
2506 			_gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, lineArray, hint );
2507 
2508 		}
2509 
2510 		if ( customAttributes ) {
2511 
2512 			for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
2513 
2514 				customAttribute = customAttributes[ i ];
2515 
2516 				if ( ! customAttribute.__original.needsUpdate ) continue;
2517 
2518 				offset_custom = 0;
2519 				offset_customSrc = 0;
2520 
2521 				if ( customAttribute.size === 1 ) {
2522 
2523 					if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) {
2524 
2525 						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
2526 
2527 							face = obj_faces[ chunk_faces3[ f ]	];
2528 
2529 							customAttribute.array[ offset_custom ] 	   = customAttribute.value[ face.a ];
2530 							customAttribute.array[ offset_custom + 1 ] = customAttribute.value[ face.b ];
2531 							customAttribute.array[ offset_custom + 2 ] = customAttribute.value[ face.c ];
2532 
2533 							offset_custom += 3;
2534 
2535 						}
2536 
2537 						for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
2538 
2539 							face = obj_faces[ chunk_faces4[ f ] ];
2540 
2541 							customAttribute.array[ offset_custom ] 	   = customAttribute.value[ face.a ];
2542 							customAttribute.array[ offset_custom + 1 ] = customAttribute.value[ face.b ];
2543 							customAttribute.array[ offset_custom + 2 ] = customAttribute.value[ face.c ];
2544 							customAttribute.array[ offset_custom + 3 ] = customAttribute.value[ face.d ];
2545 
2546 							offset_custom += 4;
2547 
2548 						}
2549 
2550 					} else if ( customAttribute.boundTo === "faces" ) {
2551 
2552 						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
2553 
2554 							value = customAttribute.value[ chunk_faces3[ f ] ];
2555 
2556 							customAttribute.array[ offset_custom ] 	   = value;
2557 							customAttribute.array[ offset_custom + 1 ] = value;
2558 							customAttribute.array[ offset_custom + 2 ] = value;
2559 
2560 							offset_custom += 3;
2561 
2562 						}
2563 
2564 						for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
2565 
2566 							value = customAttribute.value[ chunk_faces4[ f ] ];
2567 
2568 							customAttribute.array[ offset_custom ] 	   = value;
2569 							customAttribute.array[ offset_custom + 1 ] = value;
2570 							customAttribute.array[ offset_custom + 2 ] = value;
2571 							customAttribute.array[ offset_custom + 3 ] = value;
2572 
2573 							offset_custom += 4;
2574 
2575 						}
2576 
2577 					}
2578 
2579 				} else if ( customAttribute.size === 2 ) {
2580 
2581 					if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) {
2582 
2583 						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
2584 
2585 							face = obj_faces[ chunk_faces3[ f ]	];
2586 
2587 							v1 = customAttribute.value[ face.a ];
2588 							v2 = customAttribute.value[ face.b ];
2589 							v3 = customAttribute.value[ face.c ];
2590 
2591 							customAttribute.array[ offset_custom ] 	   = v1.x;
2592 							customAttribute.array[ offset_custom + 1 ] = v1.y;
2593 
2594 							customAttribute.array[ offset_custom + 2 ] = v2.x;
2595 							customAttribute.array[ offset_custom + 3 ] = v2.y;
2596 
2597 							customAttribute.array[ offset_custom + 4 ] = v3.x;
2598 							customAttribute.array[ offset_custom + 5 ] = v3.y;
2599 
2600 							offset_custom += 6;
2601 
2602 						}
2603 
2604 						for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
2605 
2606 							face = obj_faces[ chunk_faces4[ f ] ];
2607 
2608 							v1 = customAttribute.value[ face.a ];
2609 							v2 = customAttribute.value[ face.b ];
2610 							v3 = customAttribute.value[ face.c ];
2611 							v4 = customAttribute.value[ face.d ];
2612 
2613 							customAttribute.array[ offset_custom ] 	   = v1.x;
2614 							customAttribute.array[ offset_custom + 1 ] = v1.y;
2615 
2616 							customAttribute.array[ offset_custom + 2 ] = v2.x;
2617 							customAttribute.array[ offset_custom + 3 ] = v2.y;
2618 
2619 							customAttribute.array[ offset_custom + 4 ] = v3.x;
2620 							customAttribute.array[ offset_custom + 5 ] = v3.y;
2621 
2622 							customAttribute.array[ offset_custom + 6 ] = v4.x;
2623 							customAttribute.array[ offset_custom + 7 ] = v4.y;
2624 
2625 							offset_custom += 8;
2626 
2627 						}
2628 
2629 					} else if ( customAttribute.boundTo === "faces" ) {
2630 
2631 						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
2632 
2633 							value = customAttribute.value[ chunk_faces3[ f ] ];
2634 
2635 							v1 = value;
2636 							v2 = value;
2637 							v3 = value;
2638 
2639 							customAttribute.array[ offset_custom ] 	   = v1.x;
2640 							customAttribute.array[ offset_custom + 1 ] = v1.y;
2641 
2642 							customAttribute.array[ offset_custom + 2 ] = v2.x;
2643 							customAttribute.array[ offset_custom + 3 ] = v2.y;
2644 
2645 							customAttribute.array[ offset_custom + 4 ] = v3.x;
2646 							customAttribute.array[ offset_custom + 5 ] = v3.y;
2647 
2648 							offset_custom += 6;
2649 
2650 						}
2651 
2652 						for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
2653 
2654 							value = customAttribute.value[ chunk_faces4[ f ] ];
2655 
2656 							v1 = value;
2657 							v2 = value;
2658 							v3 = value;
2659 							v4 = value;
2660 
2661 							customAttribute.array[ offset_custom ] 	   = v1.x;
2662 							customAttribute.array[ offset_custom + 1 ] = v1.y;
2663 
2664 							customAttribute.array[ offset_custom + 2 ] = v2.x;
2665 							customAttribute.array[ offset_custom + 3 ] = v2.y;
2666 
2667 							customAttribute.array[ offset_custom + 4 ] = v3.x;
2668 							customAttribute.array[ offset_custom + 5 ] = v3.y;
2669 
2670 							customAttribute.array[ offset_custom + 6 ] = v4.x;
2671 							customAttribute.array[ offset_custom + 7 ] = v4.y;
2672 
2673 							offset_custom += 8;
2674 
2675 						}
2676 
2677 					}
2678 
2679 				} else if ( customAttribute.size === 3 ) {
2680 
2681 					var pp;
2682 
2683 					if ( customAttribute.type === "c" ) {
2684 
2685 						pp = [ "r", "g", "b" ];
2686 
2687 					} else {
2688 
2689 						pp = [ "x", "y", "z" ];
2690 
2691 					}
2692 
2693 					if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) {
2694 
2695 						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
2696 
2697 							face = obj_faces[ chunk_faces3[ f ]	];
2698 
2699 							v1 = customAttribute.value[ face.a ];
2700 							v2 = customAttribute.value[ face.b ];
2701 							v3 = customAttribute.value[ face.c ];
2702 
2703 							customAttribute.array[ offset_custom ] 	   = v1[ pp[ 0 ] ];
2704 							customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ];
2705 							customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ];
2706 
2707 							customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ];
2708 							customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ];
2709 							customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ];
2710 
2711 							customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ];
2712 							customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ];
2713 							customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ];
2714 
2715 							offset_custom += 9;
2716 
2717 						}
2718 
2719 						for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
2720 
2721 							face = obj_faces[ chunk_faces4[ f ] ];
2722 
2723 							v1 = customAttribute.value[ face.a ];
2724 							v2 = customAttribute.value[ face.b ];
2725 							v3 = customAttribute.value[ face.c ];
2726 							v4 = customAttribute.value[ face.d ];
2727 
2728 							customAttribute.array[ offset_custom  ] 	= v1[ pp[ 0 ] ];
2729 							customAttribute.array[ offset_custom + 1  ] = v1[ pp[ 1 ] ];
2730 							customAttribute.array[ offset_custom + 2  ] = v1[ pp[ 2 ] ];
2731 
2732 							customAttribute.array[ offset_custom + 3  ] = v2[ pp[ 0 ] ];
2733 							customAttribute.array[ offset_custom + 4  ] = v2[ pp[ 1 ] ];
2734 							customAttribute.array[ offset_custom + 5  ] = v2[ pp[ 2 ] ];
2735 
2736 							customAttribute.array[ offset_custom + 6  ] = v3[ pp[ 0 ] ];
2737 							customAttribute.array[ offset_custom + 7  ] = v3[ pp[ 1 ] ];
2738 							customAttribute.array[ offset_custom + 8  ] = v3[ pp[ 2 ] ];
2739 
2740 							customAttribute.array[ offset_custom + 9  ] = v4[ pp[ 0 ] ];
2741 							customAttribute.array[ offset_custom + 10 ] = v4[ pp[ 1 ] ];
2742 							customAttribute.array[ offset_custom + 11 ] = v4[ pp[ 2 ] ];
2743 
2744 							offset_custom += 12;
2745 
2746 						}
2747 
2748 					} else if ( customAttribute.boundTo === "faces" ) {
2749 
2750 						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
2751 
2752 							value = customAttribute.value[ chunk_faces3[ f ] ];
2753 
2754 							v1 = value;
2755 							v2 = value;
2756 							v3 = value;
2757 
2758 							customAttribute.array[ offset_custom ] 	   = v1[ pp[ 0 ] ];
2759 							customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ];
2760 							customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ];
2761 
2762 							customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ];
2763 							customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ];
2764 							customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ];
2765 
2766 							customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ];
2767 							customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ];
2768 							customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ];
2769 
2770 							offset_custom += 9;
2771 
2772 						}
2773 
2774 						for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
2775 
2776 							value = customAttribute.value[ chunk_faces4[ f ] ];
2777 
2778 							v1 = value;
2779 							v2 = value;
2780 							v3 = value;
2781 							v4 = value;
2782 
2783 							customAttribute.array[ offset_custom  ] 	= v1[ pp[ 0 ] ];
2784 							customAttribute.array[ offset_custom + 1  ] = v1[ pp[ 1 ] ];
2785 							customAttribute.array[ offset_custom + 2  ] = v1[ pp[ 2 ] ];
2786 
2787 							customAttribute.array[ offset_custom + 3  ] = v2[ pp[ 0 ] ];
2788 							customAttribute.array[ offset_custom + 4  ] = v2[ pp[ 1 ] ];
2789 							customAttribute.array[ offset_custom + 5  ] = v2[ pp[ 2 ] ];
2790 
2791 							customAttribute.array[ offset_custom + 6  ] = v3[ pp[ 0 ] ];
2792 							customAttribute.array[ offset_custom + 7  ] = v3[ pp[ 1 ] ];
2793 							customAttribute.array[ offset_custom + 8  ] = v3[ pp[ 2 ] ];
2794 
2795 							customAttribute.array[ offset_custom + 9  ] = v4[ pp[ 0 ] ];
2796 							customAttribute.array[ offset_custom + 10 ] = v4[ pp[ 1 ] ];
2797 							customAttribute.array[ offset_custom + 11 ] = v4[ pp[ 2 ] ];
2798 
2799 							offset_custom += 12;
2800 
2801 						}
2802 
2803 					} else if ( customAttribute.boundTo === "faceVertices" ) {
2804 
2805 						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
2806 
2807 							value = customAttribute.value[ chunk_faces3[ f ] ];
2808 
2809 							v1 = value[ 0 ];
2810 							v2 = value[ 1 ];
2811 							v3 = value[ 2 ];
2812 
2813 							customAttribute.array[ offset_custom ] 	   = v1[ pp[ 0 ] ];
2814 							customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ];
2815 							customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ];
2816 
2817 							customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ];
2818 							customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ];
2819 							customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ];
2820 
2821 							customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ];
2822 							customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ];
2823 							customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ];
2824 
2825 							offset_custom += 9;
2826 
2827 						}
2828 
2829 						for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
2830 
2831 							value = customAttribute.value[ chunk_faces4[ f ] ];
2832 
2833 							v1 = value[ 0 ];
2834 							v2 = value[ 1 ];
2835 							v3 = value[ 2 ];
2836 							v4 = value[ 3 ];
2837 
2838 							customAttribute.array[ offset_custom  ] 	= v1[ pp[ 0 ] ];
2839 							customAttribute.array[ offset_custom + 1  ] = v1[ pp[ 1 ] ];
2840 							customAttribute.array[ offset_custom + 2  ] = v1[ pp[ 2 ] ];
2841 
2842 							customAttribute.array[ offset_custom + 3  ] = v2[ pp[ 0 ] ];
2843 							customAttribute.array[ offset_custom + 4  ] = v2[ pp[ 1 ] ];
2844 							customAttribute.array[ offset_custom + 5  ] = v2[ pp[ 2 ] ];
2845 
2846 							customAttribute.array[ offset_custom + 6  ] = v3[ pp[ 0 ] ];
2847 							customAttribute.array[ offset_custom + 7  ] = v3[ pp[ 1 ] ];
2848 							customAttribute.array[ offset_custom + 8  ] = v3[ pp[ 2 ] ];
2849 
2850 							customAttribute.array[ offset_custom + 9  ] = v4[ pp[ 0 ] ];
2851 							customAttribute.array[ offset_custom + 10 ] = v4[ pp[ 1 ] ];
2852 							customAttribute.array[ offset_custom + 11 ] = v4[ pp[ 2 ] ];
2853 
2854 							offset_custom += 12;
2855 
2856 						}
2857 
2858 					}
2859 
2860 				} else if ( customAttribute.size === 4 ) {
2861 
2862 					if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) {
2863 
2864 						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
2865 
2866 							face = obj_faces[ chunk_faces3[ f ]	];
2867 
2868 							v1 = customAttribute.value[ face.a ];
2869 							v2 = customAttribute.value[ face.b ];
2870 							v3 = customAttribute.value[ face.c ];
2871 
2872 							customAttribute.array[ offset_custom  ] 	= v1.x;
2873 							customAttribute.array[ offset_custom + 1  ] = v1.y;
2874 							customAttribute.array[ offset_custom + 2  ] = v1.z;
2875 							customAttribute.array[ offset_custom + 3  ] = v1.w;
2876 
2877 							customAttribute.array[ offset_custom + 4  ] = v2.x;
2878 							customAttribute.array[ offset_custom + 5  ] = v2.y;
2879 							customAttribute.array[ offset_custom + 6  ] = v2.z;
2880 							customAttribute.array[ offset_custom + 7  ] = v2.w;
2881 
2882 							customAttribute.array[ offset_custom + 8  ] = v3.x;
2883 							customAttribute.array[ offset_custom + 9  ] = v3.y;
2884 							customAttribute.array[ offset_custom + 10 ] = v3.z;
2885 							customAttribute.array[ offset_custom + 11 ] = v3.w;
2886 
2887 							offset_custom += 12;
2888 
2889 						}
2890 
2891 						for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
2892 
2893 							face = obj_faces[ chunk_faces4[ f ] ];
2894 
2895 							v1 = customAttribute.value[ face.a ];
2896 							v2 = customAttribute.value[ face.b ];
2897 							v3 = customAttribute.value[ face.c ];
2898 							v4 = customAttribute.value[ face.d ];
2899 
2900 							customAttribute.array[ offset_custom  ] 	= v1.x;
2901 							customAttribute.array[ offset_custom + 1  ] = v1.y;
2902 							customAttribute.array[ offset_custom + 2  ] = v1.z;
2903 							customAttribute.array[ offset_custom + 3  ] = v1.w;
2904 
2905 							customAttribute.array[ offset_custom + 4  ] = v2.x;
2906 							customAttribute.array[ offset_custom + 5  ] = v2.y;
2907 							customAttribute.array[ offset_custom + 6  ] = v2.z;
2908 							customAttribute.array[ offset_custom + 7  ] = v2.w;
2909 
2910 							customAttribute.array[ offset_custom + 8  ] = v3.x;
2911 							customAttribute.array[ offset_custom + 9  ] = v3.y;
2912 							customAttribute.array[ offset_custom + 10 ] = v3.z;
2913 							customAttribute.array[ offset_custom + 11 ] = v3.w;
2914 
2915 							customAttribute.array[ offset_custom + 12 ] = v4.x;
2916 							customAttribute.array[ offset_custom + 13 ] = v4.y;
2917 							customAttribute.array[ offset_custom + 14 ] = v4.z;
2918 							customAttribute.array[ offset_custom + 15 ] = v4.w;
2919 
2920 							offset_custom += 16;
2921 
2922 						}
2923 
2924 					} else if ( customAttribute.boundTo === "faces" ) {
2925 
2926 						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
2927 
2928 							value = customAttribute.value[ chunk_faces3[ f ] ];
2929 
2930 							v1 = value;
2931 							v2 = value;
2932 							v3 = value;
2933 
2934 							customAttribute.array[ offset_custom  ] 	= v1.x;
2935 							customAttribute.array[ offset_custom + 1  ] = v1.y;
2936 							customAttribute.array[ offset_custom + 2  ] = v1.z;
2937 							customAttribute.array[ offset_custom + 3  ] = v1.w;
2938 
2939 							customAttribute.array[ offset_custom + 4  ] = v2.x;
2940 							customAttribute.array[ offset_custom + 5  ] = v2.y;
2941 							customAttribute.array[ offset_custom + 6  ] = v2.z;
2942 							customAttribute.array[ offset_custom + 7  ] = v2.w;
2943 
2944 							customAttribute.array[ offset_custom + 8  ] = v3.x;
2945 							customAttribute.array[ offset_custom + 9  ] = v3.y;
2946 							customAttribute.array[ offset_custom + 10 ] = v3.z;
2947 							customAttribute.array[ offset_custom + 11 ] = v3.w;
2948 
2949 							offset_custom += 12;
2950 
2951 						}
2952 
2953 						for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
2954 
2955 							value = customAttribute.value[ chunk_faces4[ f ] ];
2956 
2957 							v1 = value;
2958 							v2 = value;
2959 							v3 = value;
2960 							v4 = value;
2961 
2962 							customAttribute.array[ offset_custom  ] 	= v1.x;
2963 							customAttribute.array[ offset_custom + 1  ] = v1.y;
2964 							customAttribute.array[ offset_custom + 2  ] = v1.z;
2965 							customAttribute.array[ offset_custom + 3  ] = v1.w;
2966 
2967 							customAttribute.array[ offset_custom + 4  ] = v2.x;
2968 							customAttribute.array[ offset_custom + 5  ] = v2.y;
2969 							customAttribute.array[ offset_custom + 6  ] = v2.z;
2970 							customAttribute.array[ offset_custom + 7  ] = v2.w;
2971 
2972 							customAttribute.array[ offset_custom + 8  ] = v3.x;
2973 							customAttribute.array[ offset_custom + 9  ] = v3.y;
2974 							customAttribute.array[ offset_custom + 10 ] = v3.z;
2975 							customAttribute.array[ offset_custom + 11 ] = v3.w;
2976 
2977 							customAttribute.array[ offset_custom + 12 ] = v4.x;
2978 							customAttribute.array[ offset_custom + 13 ] = v4.y;
2979 							customAttribute.array[ offset_custom + 14 ] = v4.z;
2980 							customAttribute.array[ offset_custom + 15 ] = v4.w;
2981 
2982 							offset_custom += 16;
2983 
2984 						}
2985 
2986 					} else if ( customAttribute.boundTo === "faceVertices" ) {
2987 
2988 						for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
2989 
2990 							value = customAttribute.value[ chunk_faces3[ f ] ];
2991 
2992 							v1 = value[ 0 ];
2993 							v2 = value[ 1 ];
2994 							v3 = value[ 2 ];
2995 
2996 							customAttribute.array[ offset_custom  ] 	= v1.x;
2997 							customAttribute.array[ offset_custom + 1  ] = v1.y;
2998 							customAttribute.array[ offset_custom + 2  ] = v1.z;
2999 							customAttribute.array[ offset_custom + 3  ] = v1.w;
3000 
3001 							customAttribute.array[ offset_custom + 4  ] = v2.x;
3002 							customAttribute.array[ offset_custom + 5  ] = v2.y;
3003 							customAttribute.array[ offset_custom + 6  ] = v2.z;
3004 							customAttribute.array[ offset_custom + 7  ] = v2.w;
3005 
3006 							customAttribute.array[ offset_custom + 8  ] = v3.x;
3007 							customAttribute.array[ offset_custom + 9  ] = v3.y;
3008 							customAttribute.array[ offset_custom + 10 ] = v3.z;
3009 							customAttribute.array[ offset_custom + 11 ] = v3.w;
3010 
3011 							offset_custom += 12;
3012 
3013 						}
3014 
3015 						for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
3016 
3017 							value = customAttribute.value[ chunk_faces4[ f ] ];
3018 
3019 							v1 = value[ 0 ];
3020 							v2 = value[ 1 ];
3021 							v3 = value[ 2 ];
3022 							v4 = value[ 3 ];
3023 
3024 							customAttribute.array[ offset_custom  ] 	= v1.x;
3025 							customAttribute.array[ offset_custom + 1  ] = v1.y;
3026 							customAttribute.array[ offset_custom + 2  ] = v1.z;
3027 							customAttribute.array[ offset_custom + 3  ] = v1.w;
3028 
3029 							customAttribute.array[ offset_custom + 4  ] = v2.x;
3030 							customAttribute.array[ offset_custom + 5  ] = v2.y;
3031 							customAttribute.array[ offset_custom + 6  ] = v2.z;
3032 							customAttribute.array[ offset_custom + 7  ] = v2.w;
3033 
3034 							customAttribute.array[ offset_custom + 8  ] = v3.x;
3035 							customAttribute.array[ offset_custom + 9  ] = v3.y;
3036 							customAttribute.array[ offset_custom + 10 ] = v3.z;
3037 							customAttribute.array[ offset_custom + 11 ] = v3.w;
3038 
3039 							customAttribute.array[ offset_custom + 12 ] = v4.x;
3040 							customAttribute.array[ offset_custom + 13 ] = v4.y;
3041 							customAttribute.array[ offset_custom + 14 ] = v4.z;
3042 							customAttribute.array[ offset_custom + 15 ] = v4.w;
3043 
3044 							offset_custom += 16;
3045 
3046 						}
3047 
3048 					}
3049 
3050 				}
3051 
3052 				_gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer );
3053 				_gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint );
3054 
3055 			}
3056 
3057 		}
3058 
3059 		if ( dispose ) {
3060 
3061 			delete geometryGroup.__inittedArrays;
3062 			delete geometryGroup.__colorArray;
3063 			delete geometryGroup.__normalArray;
3064 			delete geometryGroup.__tangentArray;
3065 			delete geometryGroup.__uvArray;
3066 			delete geometryGroup.__uv2Array;
3067 			delete geometryGroup.__faceArray;
3068 			delete geometryGroup.__vertexArray;
3069 			delete geometryGroup.__lineArray;
3070 			delete geometryGroup.__skinIndexArray;
3071 			delete geometryGroup.__skinWeightArray;
3072 
3073 		}
3074 
3075 	};
3076 
3077 	function setDirectBuffers ( geometry, hint, dispose ) {
3078 
3079 		var attributes = geometry.attributes;
3080 
3081 		var index = attributes[ "index" ];
3082 		var position = attributes[ "position" ];
3083 		var normal = attributes[ "normal" ];
3084 		var uv = attributes[ "uv" ];
3085 		var color = attributes[ "color" ];
3086 		var tangent = attributes[ "tangent" ];
3087 
3088 		if ( geometry.elementsNeedUpdate && index !== undefined ) {
3089 
3090 			_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer );
3091 			_gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, index.array, hint );
3092 
3093 		}
3094 
3095 		if ( geometry.verticesNeedUpdate && position !== undefined ) {
3096 
3097 			_gl.bindBuffer( _gl.ARRAY_BUFFER, position.buffer );
3098 			_gl.bufferData( _gl.ARRAY_BUFFER, position.array, hint );
3099 
3100 		}
3101 
3102 		if ( geometry.normalsNeedUpdate && normal !== undefined ) {
3103 
3104 			_gl.bindBuffer( _gl.ARRAY_BUFFER, normal.buffer );
3105 			_gl.bufferData( _gl.ARRAY_BUFFER, normal.array, hint );
3106 
3107 		}
3108 
3109 		if ( geometry.uvsNeedUpdate && uv !== undefined ) {
3110 
3111 			_gl.bindBuffer( _gl.ARRAY_BUFFER, uv.buffer );
3112 			_gl.bufferData( _gl.ARRAY_BUFFER, uv.array, hint );
3113 
3114 		}
3115 
3116 		if ( geometry.colorsNeedUpdate && color !== undefined ) {
3117 
3118 			_gl.bindBuffer( _gl.ARRAY_BUFFER, color.buffer );
3119 			_gl.bufferData( _gl.ARRAY_BUFFER, color.array, hint );
3120 
3121 		}
3122 
3123 		if ( geometry.tangentsNeedUpdate && tangent !== undefined ) {
3124 
3125 			_gl.bindBuffer( _gl.ARRAY_BUFFER, tangent.buffer );
3126 			_gl.bufferData( _gl.ARRAY_BUFFER, tangent.array, hint );
3127 
3128 		}
3129 
3130 		if ( dispose ) {
3131 
3132 			for ( var i in geometry.attributes ) {
3133 
3134 				delete geometry.attributes[ i ].array;
3135 
3136 			}
3137 
3138 		}
3139 
3140 	};
3141 
3142 	// Buffer rendering
3143 
3144 	this.renderBufferImmediate = function ( object, program, material ) {
3145 
3146 		if ( object.hasPositions && ! object.__webglVertexBuffer ) object.__webglVertexBuffer = _gl.createBuffer();
3147 		if ( object.hasNormals && ! object.__webglNormalBuffer ) object.__webglNormalBuffer = _gl.createBuffer();
3148 		if ( object.hasUvs && ! object.__webglUvBuffer ) object.__webglUvBuffer = _gl.createBuffer();
3149 		if ( object.hasColors && ! object.__webglColorBuffer ) object.__webglColorBuffer = _gl.createBuffer();
3150 
3151 		if ( object.hasPositions ) {
3152 
3153 			_gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglVertexBuffer );
3154 			_gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW );
3155 			_gl.enableVertexAttribArray( program.attributes.position );
3156 			_gl.vertexAttribPointer( program.attributes.position, 3, _gl.FLOAT, false, 0, 0 );
3157 
3158 		}
3159 
3160 		if ( object.hasNormals ) {
3161 
3162 			_gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglNormalBuffer );
3163 
3164 			if ( material.shading === THREE.FlatShading ) {
3165 
3166 				var nx, ny, nz,
3167 					nax, nbx, ncx, nay, nby, ncy, naz, nbz, ncz,
3168 					normalArray,
3169 					i, il = object.count * 3;
3170 
3171 				for( i = 0; i < il; i += 9 ) {
3172 
3173 					normalArray = object.normalArray;
3174 
3175 					nax  = normalArray[ i ];
3176 					nay  = normalArray[ i + 1 ];
3177 					naz  = normalArray[ i + 2 ];
3178 
3179 					nbx  = normalArray[ i + 3 ];
3180 					nby  = normalArray[ i + 4 ];
3181 					nbz  = normalArray[ i + 5 ];
3182 
3183 					ncx  = normalArray[ i + 6 ];
3184 					ncy  = normalArray[ i + 7 ];
3185 					ncz  = normalArray[ i + 8 ];
3186 
3187 					nx = ( nax + nbx + ncx ) / 3;
3188 					ny = ( nay + nby + ncy ) / 3;
3189 					nz = ( naz + nbz + ncz ) / 3;
3190 
3191 					normalArray[ i ] 	 = nx;
3192 					normalArray[ i + 1 ] = ny;
3193 					normalArray[ i + 2 ] = nz;
3194 
3195 					normalArray[ i + 3 ] = nx;
3196 					normalArray[ i + 4 ] = ny;
3197 					normalArray[ i + 5 ] = nz;
3198 
3199 					normalArray[ i + 6 ] = nx;
3200 					normalArray[ i + 7 ] = ny;
3201 					normalArray[ i + 8 ] = nz;
3202 
3203 				}
3204 
3205 			}
3206 
3207 			_gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW );
3208 			_gl.enableVertexAttribArray( program.attributes.normal );
3209 			_gl.vertexAttribPointer( program.attributes.normal, 3, _gl.FLOAT, false, 0, 0 );
3210 
3211 		}
3212 
3213 		if ( object.hasUvs && material.map ) {
3214 
3215 			_gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglUvBuffer );
3216 			_gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW );
3217 			_gl.enableVertexAttribArray( program.attributes.uv );
3218 			_gl.vertexAttribPointer( program.attributes.uv, 2, _gl.FLOAT, false, 0, 0 );
3219 
3220 		}
3221 
3222 		if ( object.hasColors && material.vertexColors !== THREE.NoColors ) {
3223 
3224 			_gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglColorBuffer );
3225 			_gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW );
3226 			_gl.enableVertexAttribArray( program.attributes.color );
3227 			_gl.vertexAttribPointer( program.attributes.color, 3, _gl.FLOAT, false, 0, 0 );
3228 
3229 		}
3230 
3231 		_gl.drawArrays( _gl.TRIANGLES, 0, object.count );
3232 
3233 		object.count = 0;
3234 
3235 	};
3236 
3237 	this.renderBufferDirect = function ( camera, lights, fog, material, geometry, object ) {
3238 
3239 		if ( material.visible === false ) return;
3240 
3241 		var program, attributes, linewidth, primitives, a, attribute;
3242 
3243 		program = setProgram( camera, lights, fog, material, object );
3244 
3245 		attributes = program.attributes;
3246 
3247 		var updateBuffers = false,
3248 			wireframeBit = material.wireframe ? 1 : 0,
3249 			geometryHash = ( geometry.id * 0xffffff ) + ( program.id * 2 ) + wireframeBit;
3250 
3251 		if ( geometryHash !== _currentGeometryGroupHash ) {
3252 
3253 			_currentGeometryGroupHash = geometryHash;
3254 			updateBuffers = true;
3255 
3256 		}
3257 
3258 		// render mesh
3259 
3260 		if ( object instanceof THREE.Mesh ) {
3261 
3262 			var offsets = geometry.offsets;
3263 
3264 			// if there is more than 1 chunk
3265 			// must set attribute pointers to use new offsets for each chunk
3266 			// even if geometry and materials didn't change
3267 
3268 			if ( offsets.length > 1 ) updateBuffers = true;
3269 
3270 			for ( var i = 0, il = offsets.length; i < il; ++ i ) {
3271 
3272 				var startIndex = offsets[ i ].index;
3273 
3274 				if ( updateBuffers ) {
3275 
3276 					// vertices
3277 
3278 					var position = geometry.attributes[ "position" ];
3279 					var positionSize = position.itemSize;
3280 
3281 					_gl.bindBuffer( _gl.ARRAY_BUFFER, position.buffer );
3282 					_gl.vertexAttribPointer( attributes.position, positionSize, _gl.FLOAT, false, 0, startIndex * positionSize * 4 ); // 4 bytes per Float32
3283 
3284 					// normals
3285 
3286 					var normal = geometry.attributes[ "normal" ];
3287 
3288 					if ( attributes.normal >= 0 && normal ) {
3289 
3290 						var normalSize = normal.itemSize;
3291 
3292 						_gl.bindBuffer( _gl.ARRAY_BUFFER, normal.buffer );
3293 						_gl.vertexAttribPointer( attributes.normal, normalSize, _gl.FLOAT, false, 0, startIndex * normalSize * 4 );
3294 
3295 					}
3296 
3297 					// uvs
3298 
3299 					var uv = geometry.attributes[ "uv" ];
3300 
3301 					if ( attributes.uv >= 0 && uv ) {
3302 
3303 						if ( uv.buffer ) {
3304 
3305 							var uvSize = uv.itemSize;
3306 
3307 							_gl.bindBuffer( _gl.ARRAY_BUFFER, uv.buffer );
3308 							_gl.vertexAttribPointer( attributes.uv, uvSize, _gl.FLOAT, false, 0, startIndex * uvSize * 4 );
3309 
3310 							_gl.enableVertexAttribArray( attributes.uv );
3311 
3312 						} else {
3313 
3314 							_gl.disableVertexAttribArray( attributes.uv );
3315 
3316 						}
3317 
3318 					}
3319 
3320 					// colors
3321 
3322 					var color = geometry.attributes[ "color" ];
3323 
3324 					if ( attributes.color >= 0 && color ) {
3325 
3326 						var colorSize = color.itemSize;
3327 
3328 						_gl.bindBuffer( _gl.ARRAY_BUFFER, color.buffer );
3329 						_gl.vertexAttribPointer( attributes.color, colorSize, _gl.FLOAT, false, 0, startIndex * colorSize * 4 );
3330 
3331 					}
3332 
3333 					// tangents
3334 
3335 					var tangent = geometry.attributes[ "tangent" ];
3336 
3337 					if ( attributes.tangent >= 0 && tangent ) {
3338 
3339 						var tangentSize = tangent.itemSize;
3340 
3341 						_gl.bindBuffer( _gl.ARRAY_BUFFER, tangent.buffer );
3342 						_gl.vertexAttribPointer( attributes.tangent, tangentSize, _gl.FLOAT, false, 0, startIndex * tangentSize * 4 );
3343 
3344 					}
3345 
3346 					// indices
3347 
3348 					var index = geometry.attributes[ "index" ];
3349 
3350 					_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer );
3351 
3352 				}
3353 
3354 				// render indexed triangles
3355 
3356 				_gl.drawElements( _gl.TRIANGLES, offsets[ i ].count, _gl.UNSIGNED_SHORT, offsets[ i ].start * 2 ); // 2 bytes per Uint16
3357 
3358 				_this.info.render.calls ++;
3359 				_this.info.render.vertices += offsets[ i ].count; // not really true, here vertices can be shared
3360 				_this.info.render.faces += offsets[ i ].count / 3;
3361 
3362 			}
3363 
3364 		// render particles
3365 
3366 		} else if ( object instanceof THREE.ParticleSystem ) {
3367 
3368 			if ( updateBuffers ) {
3369 
3370 				// vertices
3371 
3372 				var position = geometry.attributes[ "position" ];
3373 				var positionSize = position.itemSize;
3374 
3375 				_gl.bindBuffer( _gl.ARRAY_BUFFER, position.buffer );
3376 				_gl.vertexAttribPointer( attributes.position, positionSize, _gl.FLOAT, false, 0, 0 );
3377 
3378 				// colors
3379 
3380 				var color = geometry.attributes[ "color" ];
3381 
3382 				if ( attributes.color >= 0 && color ) {
3383 
3384 					var colorSize = color.itemSize;
3385 
3386 					_gl.bindBuffer( _gl.ARRAY_BUFFER, color.buffer );
3387 					_gl.vertexAttribPointer( attributes.color, colorSize, _gl.FLOAT, false, 0, 0 );
3388 
3389 				}
3390 
3391 				// render particles
3392 
3393 				_gl.drawArrays( _gl.POINTS, 0, position.numItems / 3 );
3394 
3395 				_this.info.render.calls ++;
3396 				_this.info.render.points += position.numItems / 3;
3397 
3398 			}
3399 
3400 		}
3401 
3402 	};
3403 
3404 	this.renderBuffer = function ( camera, lights, fog, material, geometryGroup, object ) {
3405 
3406 		if ( material.visible === false ) return;
3407 
3408 		var program, attributes, linewidth, primitives, a, attribute, i, il;
3409 
3410 		program = setProgram( camera, lights, fog, material, object );
3411 
3412 		attributes = program.attributes;
3413 
3414 		var updateBuffers = false,
3415 			wireframeBit = material.wireframe ? 1 : 0,
3416 			geometryGroupHash = ( geometryGroup.id * 0xffffff ) + ( program.id * 2 ) + wireframeBit;
3417 
3418 		if ( geometryGroupHash !== _currentGeometryGroupHash ) {
3419 
3420 			_currentGeometryGroupHash = geometryGroupHash;
3421 			updateBuffers = true;
3422 
3423 		}
3424 
3425 		// vertices
3426 
3427 		if ( !material.morphTargets && attributes.position >= 0 ) {
3428 
3429 			if ( updateBuffers ) {
3430 
3431 				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer );
3432 				_gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 );
3433 
3434 			}
3435 
3436 		} else {
3437 
3438 			if ( object.morphTargetBase ) {
3439 
3440 				setupMorphTargets( material, geometryGroup, object );
3441 
3442 			}
3443 
3444 		}
3445 
3446 
3447 		if ( updateBuffers ) {
3448 
3449 			// custom attributes
3450 
3451 			// Use the per-geometryGroup custom attribute arrays which are setup in initMeshBuffers
3452 
3453 			if ( geometryGroup.__webglCustomAttributesList ) {
3454 
3455 				for ( i = 0, il = geometryGroup.__webglCustomAttributesList.length; i < il; i ++ ) {
3456 
3457 					attribute = geometryGroup.__webglCustomAttributesList[ i ];
3458 
3459 					if ( attributes[ attribute.buffer.belongsToAttribute ] >= 0 ) {
3460 
3461 						_gl.bindBuffer( _gl.ARRAY_BUFFER, attribute.buffer );
3462 						_gl.vertexAttribPointer( attributes[ attribute.buffer.belongsToAttribute ], attribute.size, _gl.FLOAT, false, 0, 0 );
3463 
3464 					}
3465 
3466 				}
3467 
3468 			}
3469 
3470 
3471 			// colors
3472 
3473 			if ( attributes.color >= 0 ) {
3474 
3475 				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer );
3476 				_gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 );
3477 
3478 			}
3479 
3480 			// normals
3481 
3482 			if ( attributes.normal >= 0 ) {
3483 
3484 				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer );
3485 				_gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 );
3486 
3487 			}
3488 
3489 			// tangents
3490 
3491 			if ( attributes.tangent >= 0 ) {
3492 
3493 				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer );
3494 				_gl.vertexAttribPointer( attributes.tangent, 4, _gl.FLOAT, false, 0, 0 );
3495 
3496 			}
3497 
3498 			// uvs
3499 
3500 			if ( attributes.uv >= 0 ) {
3501 
3502 				if ( geometryGroup.__webglUVBuffer ) {
3503 
3504 					_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer );
3505 					_gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 );
3506 
3507 					_gl.enableVertexAttribArray( attributes.uv );
3508 
3509 				} else {
3510 
3511 					_gl.disableVertexAttribArray( attributes.uv );
3512 
3513 				}
3514 
3515 			}
3516 
3517 			if ( attributes.uv2 >= 0 ) {
3518 
3519 				if ( geometryGroup.__webglUV2Buffer ) {
3520 
3521 					_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer );
3522 					_gl.vertexAttribPointer( attributes.uv2, 2, _gl.FLOAT, false, 0, 0 );
3523 
3524 					_gl.enableVertexAttribArray( attributes.uv2 );
3525 
3526 				} else {
3527 
3528 					_gl.disableVertexAttribArray( attributes.uv2 );
3529 
3530 				}
3531 
3532 			}
3533 
3534 			if ( material.skinning &&
3535 				 attributes.skinIndex >= 0 && attributes.skinWeight >= 0 ) {
3536 
3537 				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer );
3538 				_gl.vertexAttribPointer( attributes.skinIndex, 4, _gl.FLOAT, false, 0, 0 );
3539 
3540 				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer );
3541 				_gl.vertexAttribPointer( attributes.skinWeight, 4, _gl.FLOAT, false, 0, 0 );
3542 
3543 			}
3544 
3545 			// line distances
3546 
3547 			if ( attributes.lineDistance >= 0 ) {
3548 
3549 				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglLineDistanceBuffer );
3550 				_gl.vertexAttribPointer( attributes.lineDistance, 1, _gl.FLOAT, false, 0, 0 );
3551 
3552 			}
3553 
3554 		}
3555 
3556 		// render mesh
3557 
3558 		if ( object instanceof THREE.Mesh ) {
3559 
3560 			// wireframe
3561 
3562 			if ( material.wireframe ) {
3563 
3564 				setLineWidth( material.wireframeLinewidth );
3565 
3566 				if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer );
3567 				_gl.drawElements( _gl.LINES, geometryGroup.__webglLineCount, _gl.UNSIGNED_SHORT, 0 );
3568 
3569 			// triangles
3570 
3571 			} else {
3572 
3573 				if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer );
3574 				_gl.drawElements( _gl.TRIANGLES, geometryGroup.__webglFaceCount, _gl.UNSIGNED_SHORT, 0 );
3575 
3576 			}
3577 
3578 			_this.info.render.calls ++;
3579 			_this.info.render.vertices += geometryGroup.__webglFaceCount;
3580 			_this.info.render.faces += geometryGroup.__webglFaceCount / 3;
3581 
3582 		// render lines
3583 
3584 		} else if ( object instanceof THREE.Line ) {
3585 
3586 			primitives = ( object.type === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES;
3587 
3588 			setLineWidth( material.linewidth );
3589 
3590 			_gl.drawArrays( primitives, 0, geometryGroup.__webglLineCount );
3591 
3592 			_this.info.render.calls ++;
3593 
3594 		// render particles
3595 
3596 		} else if ( object instanceof THREE.ParticleSystem ) {
3597 
3598 			_gl.drawArrays( _gl.POINTS, 0, geometryGroup.__webglParticleCount );
3599 
3600 			_this.info.render.calls ++;
3601 			_this.info.render.points += geometryGroup.__webglParticleCount;
3602 
3603 		// render ribbon
3604 
3605 		} else if ( object instanceof THREE.Ribbon ) {
3606 
3607 			_gl.drawArrays( _gl.TRIANGLE_STRIP, 0, geometryGroup.__webglVertexCount );
3608 
3609 			_this.info.render.calls ++;
3610 
3611 		}
3612 
3613 	};
3614 
3615 	function setupMorphTargets ( material, geometryGroup, object ) {
3616 
3617 		// set base
3618 
3619 		var attributes = material.program.attributes;
3620 
3621 		if ( object.morphTargetBase !== -1 ) {
3622 
3623 			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ object.morphTargetBase ] );
3624 			_gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 );
3625 
3626 		} else if ( attributes.position >= 0 ) {
3627 
3628 			_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer );
3629 			_gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 );
3630 
3631 		}
3632 
3633 		if ( object.morphTargetForcedOrder.length ) {
3634 
3635 			// set forced order
3636 
3637 			var m = 0;
3638 			var order = object.morphTargetForcedOrder;
3639 			var influences = object.morphTargetInfluences;
3640 
3641 			while ( m < material.numSupportedMorphTargets && m < order.length ) {
3642 
3643 				_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ order[ m ] ] );
3644 				_gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 );
3645 
3646 				if ( material.morphNormals ) {
3647 
3648 					_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ order[ m ] ] );
3649 					_gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 );
3650 
3651 				}
3652 
3653 				object.__webglMorphTargetInfluences[ m ] = influences[ order[ m ] ];
3654 
3655 				m ++;
3656 			}
3657 
3658 		} else {
3659 
3660 			// find the most influencing
3661 
3662 			var influence, activeInfluenceIndices = [];
3663 			var influences = object.morphTargetInfluences;
3664 			var i, il = influences.length;
3665 
3666 			for ( i = 0; i < il; i ++ ) {
3667 
3668 				influence = influences[ i ];
3669 
3670 				if ( influence > 0 ) {
3671 
3672 					activeInfluenceIndices.push( [ i, influence ] );
3673 
3674 				}
3675 
3676 			}
3677 
3678 			if ( activeInfluenceIndices.length > material.numSupportedMorphTargets ) {
3679 
3680 				activeInfluenceIndices.sort( numericalSort );
3681 				activeInfluenceIndices.length = material.numSupportedMorphTargets;
3682 
3683 			} else if ( activeInfluenceIndices.length > material.numSupportedMorphNormals ) {
3684 
3685 				activeInfluenceIndices.sort( numericalSort );
3686 
3687 			} else if ( activeInfluenceIndices.length === 0 ) {
3688 
3689 				activeInfluenceIndices.push( [ 0, 0 ] );
3690 
3691 			};
3692 
3693 			var influenceIndex, m = 0;
3694 
3695 			while ( m < material.numSupportedMorphTargets ) {
3696 
3697 				if ( activeInfluenceIndices[ m ] ) {
3698 
3699 					influenceIndex = activeInfluenceIndices[ m ][ 0 ];
3700 
3701 					_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ influenceIndex ] );
3702 
3703 					_gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 );
3704 
3705 					if ( material.morphNormals ) {
3706 
3707 						_gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ influenceIndex ] );
3708 						_gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 );
3709 
3710 					}
3711 
3712 					object.__webglMorphTargetInfluences[ m ] = influences[ influenceIndex ];
3713 
3714 				} else {
3715 
3716 					_gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 );
3717 
3718 					if ( material.morphNormals ) {
3719 
3720 						_gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 );
3721 
3722 					}
3723 
3724 					object.__webglMorphTargetInfluences[ m ] = 0;
3725 
3726 				}
3727 
3728 				m ++;
3729 
3730 			}
3731 
3732 		}
3733 
3734 		// load updated influences uniform
3735 
3736 		if ( material.program.uniforms.morphTargetInfluences !== null ) {
3737 
3738 			_gl.uniform1fv( material.program.uniforms.morphTargetInfluences, object.__webglMorphTargetInfluences );
3739 
3740 		}
3741 
3742 	};
3743 
3744 	// Sorting
3745 
3746 	function painterSortStable ( a, b ) {
3747 
3748 		if ( a.z !== b.z ) {
3749 
3750 			return b.z - a.z;
3751 
3752 		} else {
3753 
3754 			return b.id - a.id;
3755 
3756 		}
3757 
3758 	};
3759 
3760 	function numericalSort ( a, b ) {
3761 
3762 		return b[ 1 ] - a[ 1 ];
3763 
3764 	};
3765 
3766 
3767 	// Rendering
3768 
3769 	this.render = function ( scene, camera, renderTarget, forceClear ) {
3770 
3771 		if ( camera instanceof THREE.Camera === false ) {
3772 
3773 			console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' );
3774 			return;
3775 
3776 		}
3777 
3778 		var i, il,
3779 
3780 		webglObject, object,
3781 		renderList,
3782 
3783 		lights = scene.__lights,
3784 		fog = scene.fog;
3785 
3786 		// reset caching for this frame
3787 
3788 		_currentMaterialId = -1;
3789 		_lightsNeedUpdate = true;
3790 
3791 		// update scene graph
3792 
3793 		if ( this.autoUpdateScene ) scene.updateMatrixWorld();
3794 
3795 		// update camera matrices and frustum
3796 
3797 		if ( camera.parent === undefined ) camera.updateMatrixWorld();
3798 
3799 		if ( ! camera._viewMatrixArray ) camera._viewMatrixArray = new Float32Array( 16 );
3800 		if ( ! camera._projectionMatrixArray ) camera._projectionMatrixArray = new Float32Array( 16 );
3801 
3802 		camera.matrixWorldInverse.getInverse( camera.matrixWorld );
3803 
3804 		camera.matrixWorldInverse.flattenToArray( camera._viewMatrixArray );
3805 		camera.projectionMatrix.flattenToArray( camera._projectionMatrixArray );
3806 
3807 		_projScreenMatrix.multiply( camera.projectionMatrix, camera.matrixWorldInverse );
3808 		_frustum.setFromMatrix( _projScreenMatrix );
3809 
3810 		// update WebGL objects
3811 
3812 		if ( this.autoUpdateObjects ) this.initWebGLObjects( scene );
3813 
3814 		// custom render plugins (pre pass)
3815 
3816 		renderPlugins( this.renderPluginsPre, scene, camera );
3817 
3818 		//
3819 
3820 		_this.info.render.calls = 0;
3821 		_this.info.render.vertices = 0;
3822 		_this.info.render.faces = 0;
3823 		_this.info.render.points = 0;
3824 
3825 		this.setRenderTarget( renderTarget );
3826 
3827 		if ( this.autoClear || forceClear ) {
3828 
3829 			this.clear( this.autoClearColor, this.autoClearDepth, this.autoClearStencil );
3830 
3831 		}
3832 
3833 		// set matrices for regular objects (frustum culled)
3834 
3835 		renderList = scene.__webglObjects;
3836 
3837 		for ( i = 0, il = renderList.length; i < il; i ++ ) {
3838 
3839 			webglObject = renderList[ i ];
3840 			object = webglObject.object;
3841 
3842 			webglObject.render = false;
3843 
3844 			if ( object.visible ) {
3845 
3846 				if ( ! ( object instanceof THREE.Mesh || object instanceof THREE.ParticleSystem ) || ! ( object.frustumCulled ) || _frustum.contains( object ) ) {
3847 
3848 					//object.matrixWorld.flattenToArray( object._modelMatrixArray );
3849 
3850 					setupMatrices( object, camera );
3851 
3852 					unrollBufferMaterial( webglObject );
3853 
3854 					webglObject.render = true;
3855 
3856 					if ( this.sortObjects === true ) {
3857 
3858 						if ( object.renderDepth !== null ) {
3859 
3860 							webglObject.z = object.renderDepth;
3861 
3862 						} else {
3863 
3864 							_vector3.copy( object.matrixWorld.getPosition() );
3865 							_projScreenMatrix.multiplyVector3( _vector3 );
3866 
3867 							webglObject.z = _vector3.z;
3868 
3869 						}
3870 
3871 						webglObject.id = object.id;
3872 
3873 					}
3874 
3875 				}
3876 
3877 			}
3878 
3879 		}
3880 
3881 		if ( this.sortObjects ) {
3882 
3883 			renderList.sort( painterSortStable );
3884 
3885 		}
3886 
3887 		// set matrices for immediate objects
3888 
3889 		renderList = scene.__webglObjectsImmediate;
3890 
3891 		for ( i = 0, il = renderList.length; i < il; i ++ ) {
3892 
3893 			webglObject = renderList[ i ];
3894 			object = webglObject.object;
3895 
3896 			if ( object.visible ) {
3897 
3898 				/*
3899 				if ( object.matrixAutoUpdate ) {
3900 
3901 					object.matrixWorld.flattenToArray( object._modelMatrixArray );
3902 
3903 				}
3904 				*/
3905 
3906 				setupMatrices( object, camera );
3907 
3908 				unrollImmediateBufferMaterial( webglObject );
3909 
3910 			}
3911 
3912 		}
3913 
3914 		if ( scene.overrideMaterial ) {
3915 
3916 			var material = scene.overrideMaterial;
3917 
3918 			this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst );
3919 			this.setDepthTest( material.depthTest );
3920 			this.setDepthWrite( material.depthWrite );
3921 			setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );
3922 
3923 			renderObjects( scene.__webglObjects, false, "", camera, lights, fog, true, material );
3924 			renderObjectsImmediate( scene.__webglObjectsImmediate, "", camera, lights, fog, false, material );
3925 
3926 		} else {
3927 
3928 			// opaque pass (front-to-back order)
3929 
3930 			this.setBlending( THREE.NormalBlending );
3931 
3932 			renderObjects( scene.__webglObjects, true, "opaque", camera, lights, fog, false );
3933 			renderObjectsImmediate( scene.__webglObjectsImmediate, "opaque", camera, lights, fog, false );
3934 
3935 			// transparent pass (back-to-front order)
3936 
3937 			renderObjects( scene.__webglObjects, false, "transparent", camera, lights, fog, true );
3938 			renderObjectsImmediate( scene.__webglObjectsImmediate, "transparent", camera, lights, fog, true );
3939 
3940 		}
3941 
3942 		// custom render plugins (post pass)
3943 
3944 		renderPlugins( this.renderPluginsPost, scene, camera );
3945 
3946 
3947 		// Generate mipmap if we're using any kind of mipmap filtering
3948 
3949 		if ( renderTarget && renderTarget.generateMipmaps && renderTarget.minFilter !== THREE.NearestFilter && renderTarget.minFilter !== THREE.LinearFilter ) {
3950 
3951 			updateRenderTargetMipmap( renderTarget );
3952 
3953 		}
3954 
3955 		// Ensure depth buffer writing is enabled so it can be cleared on next render
3956 
3957 		this.setDepthTest( true );
3958 		this.setDepthWrite( true );
3959 
3960 		// _gl.finish();
3961 
3962 	};
3963 
3964 	function renderPlugins( plugins, scene, camera ) {
3965 
3966 		if ( ! plugins.length ) return;
3967 
3968 		for ( var i = 0, il = plugins.length; i < il; i ++ ) {
3969 
3970 			// reset state for plugin (to start from clean slate)
3971 
3972 			_currentProgram = null;
3973 			_currentCamera = null;
3974 
3975 			_oldBlending = -1;
3976 			_oldDepthTest = -1;
3977 			_oldDepthWrite = -1;
3978 			_oldDoubleSided = -1;
3979 			_oldFlipSided = -1;
3980 			_currentGeometryGroupHash = -1;
3981 			_currentMaterialId = -1;
3982 
3983 			_lightsNeedUpdate = true;
3984 
3985 			plugins[ i ].render( scene, camera, _currentWidth, _currentHeight );
3986 
3987 			// reset state after plugin (anything could have changed)
3988 
3989 			_currentProgram = null;
3990 			_currentCamera = null;
3991 
3992 			_oldBlending = -1;
3993 			_oldDepthTest = -1;
3994 			_oldDepthWrite = -1;
3995 			_oldDoubleSided = -1;
3996 			_oldFlipSided = -1;
3997 			_currentGeometryGroupHash = -1;
3998 			_currentMaterialId = -1;
3999 
4000 			_lightsNeedUpdate = true;
4001 
4002 		}
4003 
4004 	};
4005 
4006 	function renderObjects ( renderList, reverse, materialType, camera, lights, fog, useBlending, overrideMaterial ) {
4007 
4008 		var webglObject, object, buffer, material, start, end, delta;
4009 
4010 		if ( reverse ) {
4011 
4012 			start = renderList.length - 1;
4013 			end = -1;
4014 			delta = -1;
4015 
4016 		} else {
4017 
4018 			start = 0;
4019 			end = renderList.length;
4020 			delta = 1;
4021 		}
4022 
4023 		for ( var i = start; i !== end; i += delta ) {
4024 
4025 			webglObject = renderList[ i ];
4026 
4027 			if ( webglObject.render ) {
4028 
4029 				object = webglObject.object;
4030 				buffer = webglObject.buffer;
4031 
4032 				if ( overrideMaterial ) {
4033 
4034 					material = overrideMaterial;
4035 
4036 				} else {
4037 
4038 					material = webglObject[ materialType ];
4039 
4040 					if ( ! material ) continue;
4041 
4042 					if ( useBlending ) _this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst );
4043 
4044 					_this.setDepthTest( material.depthTest );
4045 					_this.setDepthWrite( material.depthWrite );
4046 					setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );
4047 
4048 				}
4049 
4050 				_this.setMaterialFaces( material );
4051 
4052 				if ( buffer instanceof THREE.BufferGeometry ) {
4053 
4054 					_this.renderBufferDirect( camera, lights, fog, material, buffer, object );
4055 
4056 				} else {
4057 
4058 					_this.renderBuffer( camera, lights, fog, material, buffer, object );
4059 
4060 				}
4061 
4062 			}
4063 
4064 		}
4065 
4066 	};
4067 
4068 	function renderObjectsImmediate ( renderList, materialType, camera, lights, fog, useBlending, overrideMaterial ) {
4069 
4070 		var webglObject, object, material, program;
4071 
4072 		for ( var i = 0, il = renderList.length; i < il; i ++ ) {
4073 
4074 			webglObject = renderList[ i ];
4075 			object = webglObject.object;
4076 
4077 			if ( object.visible ) {
4078 
4079 				if ( overrideMaterial ) {
4080 
4081 					material = overrideMaterial;
4082 
4083 				} else {
4084 
4085 					material = webglObject[ materialType ];
4086 
4087 					if ( ! material ) continue;
4088 
4089 					if ( useBlending ) _this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst );
4090 
4091 					_this.setDepthTest( material.depthTest );
4092 					_this.setDepthWrite( material.depthWrite );
4093 					setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );
4094 
4095 				}
4096 
4097 				_this.renderImmediateObject( camera, lights, fog, material, object );
4098 
4099 			}
4100 
4101 		}
4102 
4103 	};
4104 
4105 	this.renderImmediateObject = function ( camera, lights, fog, material, object ) {
4106 
4107 		var program = setProgram( camera, lights, fog, material, object );
4108 
4109 		_currentGeometryGroupHash = -1;
4110 
4111 		_this.setMaterialFaces( material );
4112 
4113 		if ( object.immediateRenderCallback ) {
4114 
4115 			object.immediateRenderCallback( program, _gl, _frustum );
4116 
4117 		} else {
4118 
4119 			object.render( function( object ) { _this.renderBufferImmediate( object, program, material ); } );
4120 
4121 		}
4122 
4123 	};
4124 
4125 	function unrollImmediateBufferMaterial ( globject ) {
4126 
4127 		var object = globject.object,
4128 			material = object.material;
4129 
4130 		if ( material.transparent ) {
4131 
4132 			globject.transparent = material;
4133 			globject.opaque = null;
4134 
4135 		} else {
4136 
4137 			globject.opaque = material;
4138 			globject.transparent = null;
4139 
4140 		}
4141 
4142 	};
4143 
4144 	function unrollBufferMaterial ( globject ) {
4145 
4146 		var object = globject.object,
4147 			buffer = globject.buffer,
4148 			material, materialIndex, meshMaterial;
4149 
4150 		meshMaterial = object.material;
4151 
4152 		if ( meshMaterial instanceof THREE.MeshFaceMaterial ) {
4153 
4154 			materialIndex = buffer.materialIndex;
4155 
4156 			if ( materialIndex >= 0 ) {
4157 
4158 				material = meshMaterial.materials[ materialIndex ];
4159 
4160 				if ( material.transparent ) {
4161 
4162 					globject.transparent = material;
4163 					globject.opaque = null;
4164 
4165 				} else {
4166 
4167 					globject.opaque = material;
4168 					globject.transparent = null;
4169 
4170 				}
4171 
4172 			}
4173 
4174 		} else {
4175 
4176 			material = meshMaterial;
4177 
4178 			if ( material ) {
4179 
4180 				if ( material.transparent ) {
4181 
4182 					globject.transparent = material;
4183 					globject.opaque = null;
4184 
4185 				} else {
4186 
4187 					globject.opaque = material;
4188 					globject.transparent = null;
4189 
4190 				}
4191 
4192 			}
4193 
4194 		}
4195 
4196 	};
4197 
4198 	// Geometry splitting
4199 
4200 	function sortFacesByMaterial ( geometry, material ) {
4201 
4202 		var f, fl, face, materialIndex, vertices,
4203 			materialHash, groupHash,
4204 			hash_map = {};
4205 
4206 		var numMorphTargets = geometry.morphTargets.length;
4207 		var numMorphNormals = geometry.morphNormals.length;
4208 
4209 		var usesFaceMaterial = material instanceof THREE.MeshFaceMaterial;
4210 
4211 		geometry.geometryGroups = {};
4212 
4213 		for ( f = 0, fl = geometry.faces.length; f < fl; f ++ ) {
4214 
4215 			face = geometry.faces[ f ];
4216 			materialIndex = usesFaceMaterial ? face.materialIndex : undefined;
4217 
4218 			materialHash = ( materialIndex !== undefined ) ? materialIndex : -1;
4219 
4220 			if ( hash_map[ materialHash ] === undefined ) {
4221 
4222 				hash_map[ materialHash ] = { 'hash': materialHash, 'counter': 0 };
4223 
4224 			}
4225 
4226 			groupHash = hash_map[ materialHash ].hash + '_' + hash_map[ materialHash ].counter;
4227 
4228 			if ( geometry.geometryGroups[ groupHash ] === undefined ) {
4229 
4230 				geometry.geometryGroups[ groupHash ] = { 'faces3': [], 'faces4': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets, 'numMorphNormals': numMorphNormals };
4231 
4232 			}
4233 
4234 			vertices = face instanceof THREE.Face3 ? 3 : 4;
4235 
4236 			if ( geometry.geometryGroups[ groupHash ].vertices + vertices > 65535 ) {
4237 
4238 				hash_map[ materialHash ].counter += 1;
4239 				groupHash = hash_map[ materialHash ].hash + '_' + hash_map[ materialHash ].counter;
4240 
4241 				if ( geometry.geometryGroups[ groupHash ] === undefined ) {
4242 
4243 					geometry.geometryGroups[ groupHash ] = { 'faces3': [], 'faces4': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets, 'numMorphNormals': numMorphNormals };
4244 
4245 				}
4246 
4247 			}
4248 
4249 			if ( face instanceof THREE.Face3 ) {
4250 
4251 				geometry.geometryGroups[ groupHash ].faces3.push( f );
4252 
4253 			} else {
4254 
4255 				geometry.geometryGroups[ groupHash ].faces4.push( f );
4256 
4257 			}
4258 
4259 			geometry.geometryGroups[ groupHash ].vertices += vertices;
4260 
4261 		}
4262 
4263 		geometry.geometryGroupsList = [];
4264 
4265 		for ( var g in geometry.geometryGroups ) {
4266 
4267 			geometry.geometryGroups[ g ].id = _geometryGroupCounter ++;
4268 
4269 			geometry.geometryGroupsList.push( geometry.geometryGroups[ g ] );
4270 
4271 		}
4272 
4273 	};
4274 
4275 	// Objects refresh
4276 
4277 	this.initWebGLObjects = function ( scene ) {
4278 
4279 		if ( !scene.__webglObjects ) {
4280 
4281 			scene.__webglObjects = [];
4282 			scene.__webglObjectsImmediate = [];
4283 			scene.__webglSprites = [];
4284 			scene.__webglFlares = [];
4285 
4286 		}
4287 
4288 		while ( scene.__objectsAdded.length ) {
4289 
4290 			addObject( scene.__objectsAdded[ 0 ], scene );
4291 			scene.__objectsAdded.splice( 0, 1 );
4292 
4293 		}
4294 
4295 		while ( scene.__objectsRemoved.length ) {
4296 
4297 			removeObject( scene.__objectsRemoved[ 0 ], scene );
4298 			scene.__objectsRemoved.splice( 0, 1 );
4299 
4300 		}
4301 
4302 		// update must be called after objects adding / removal
4303 
4304 		for ( var o = 0, ol = scene.__webglObjects.length; o < ol; o ++ ) {
4305 
4306 			updateObject( scene.__webglObjects[ o ].object );
4307 
4308 		}
4309 
4310 	};
4311 
4312 	// Objects adding
4313 
4314 	function addObject ( object, scene ) {
4315 
4316 		var g, geometry, material, geometryGroup;
4317 
4318 		if ( ! object.__webglInit ) {
4319 
4320 			object.__webglInit = true;
4321 
4322 			object._modelViewMatrix = new THREE.Matrix4();
4323 			object._normalMatrix = new THREE.Matrix3();
4324 
4325 			if ( object instanceof THREE.Mesh ) {
4326 
4327 				geometry = object.geometry;
4328 				material = object.material;
4329 
4330 				if ( geometry instanceof THREE.Geometry ) {
4331 
4332 					if ( geometry.geometryGroups === undefined ) {
4333 
4334 						sortFacesByMaterial( geometry, material );
4335 
4336 					}
4337 
4338 					// create separate VBOs per geometry chunk
4339 
4340 					for ( g in geometry.geometryGroups ) {
4341 
4342 						geometryGroup = geometry.geometryGroups[ g ];
4343 
4344 						// initialise VBO on the first access
4345 
4346 						if ( ! geometryGroup.__webglVertexBuffer ) {
4347 
4348 							createMeshBuffers( geometryGroup );
4349 							initMeshBuffers( geometryGroup, object );
4350 
4351 							geometry.verticesNeedUpdate = true;
4352 							geometry.morphTargetsNeedUpdate = true;
4353 							geometry.elementsNeedUpdate = true;
4354 							geometry.uvsNeedUpdate = true;
4355 							geometry.normalsNeedUpdate = true;
4356 							geometry.tangentsNeedUpdate = true;
4357 							geometry.colorsNeedUpdate = true;
4358 
4359 						}
4360 
4361 					}
4362 
4363 				} else if ( geometry instanceof THREE.BufferGeometry ) {
4364 
4365 					initDirectBuffers( geometry );
4366 
4367 				}
4368 
4369 			} else if ( object instanceof THREE.Ribbon ) {
4370 
4371 				geometry = object.geometry;
4372 
4373 				if ( ! geometry.__webglVertexBuffer ) {
4374 
4375 					createRibbonBuffers( geometry );
4376 					initRibbonBuffers( geometry, object );
4377 
4378 					geometry.verticesNeedUpdate = true;
4379 					geometry.colorsNeedUpdate = true;
4380 					geometry.normalsNeedUpdate = true;
4381 
4382 				}
4383 
4384 			} else if ( object instanceof THREE.Line ) {
4385 
4386 				geometry = object.geometry;
4387 
4388 				if ( ! geometry.__webglVertexBuffer ) {
4389 
4390 					createLineBuffers( geometry );
4391 					initLineBuffers( geometry, object );
4392 
4393 					geometry.verticesNeedUpdate = true;
4394 					geometry.colorsNeedUpdate = true;
4395 					geometry.lineDistancesNeedUpdate = true;
4396 
4397 				}
4398 
4399 			} else if ( object instanceof THREE.ParticleSystem ) {
4400 
4401 				geometry = object.geometry;
4402 
4403 				if ( ! geometry.__webglVertexBuffer ) {
4404 
4405 					if ( geometry instanceof THREE.Geometry ) {
4406 
4407 						createParticleBuffers( geometry );
4408 						initParticleBuffers( geometry, object );
4409 
4410 						geometry.verticesNeedUpdate = true;
4411 						geometry.colorsNeedUpdate = true;
4412 
4413 					} else if ( geometry instanceof THREE.BufferGeometry ) {
4414 
4415 						initDirectBuffers( geometry );
4416 
4417 					}
4418 
4419 
4420 				}
4421 
4422 			}
4423 
4424 		}
4425 
4426 		if ( ! object.__webglActive ) {
4427 
4428 			if ( object instanceof THREE.Mesh ) {
4429 
4430 				geometry = object.geometry;
4431 
4432 				if ( geometry instanceof THREE.BufferGeometry ) {
4433 
4434 					addBuffer( scene.__webglObjects, geometry, object );
4435 
4436 				} else {
4437 
4438 					for ( g in geometry.geometryGroups ) {
4439 
4440 						geometryGroup = geometry.geometryGroups[ g ];
4441 
4442 						addBuffer( scene.__webglObjects, geometryGroup, object );
4443 
4444 					}
4445 
4446 				}
4447 
4448 			} else if ( object instanceof THREE.Ribbon ||
4449 						object instanceof THREE.Line ||
4450 						object instanceof THREE.ParticleSystem ) {
4451 
4452 				geometry = object.geometry;
4453 				addBuffer( scene.__webglObjects, geometry, object );
4454 
4455 			} else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) {
4456 
4457 				addBufferImmediate( scene.__webglObjectsImmediate, object );
4458 
4459 			} else if ( object instanceof THREE.Sprite ) {
4460 
4461 				scene.__webglSprites.push( object );
4462 
4463 			} else if ( object instanceof THREE.LensFlare ) {
4464 
4465 				scene.__webglFlares.push( object );
4466 
4467 			}
4468 
4469 			object.__webglActive = true;
4470 
4471 		}
4472 
4473 	};
4474 
4475 	function addBuffer ( objlist, buffer, object ) {
4476 
4477 		objlist.push(
4478 			{
4479 				buffer: buffer,
4480 				object: object,
4481 				opaque: null,
4482 				transparent: null
4483 			}
4484 		);
4485 
4486 	};
4487 
4488 	function addBufferImmediate ( objlist, object ) {
4489 
4490 		objlist.push(
4491 			{
4492 				object: object,
4493 				opaque: null,
4494 				transparent: null
4495 			}
4496 		);
4497 
4498 	};
4499 
4500 	// Objects updates
4501 
4502 	function updateObject ( object ) {
4503 
4504 		var geometry = object.geometry,
4505 			geometryGroup, customAttributesDirty, material;
4506 
4507 		if ( object instanceof THREE.Mesh ) {
4508 
4509 			if ( geometry instanceof THREE.BufferGeometry ) {
4510 
4511 				if ( geometry.verticesNeedUpdate || geometry.elementsNeedUpdate ||
4512 					 geometry.uvsNeedUpdate || geometry.normalsNeedUpdate ||
4513 					 geometry.colorsNeedUpdate || geometry.tangentsNeedUpdate ) {
4514 
4515 					setDirectBuffers( geometry, _gl.DYNAMIC_DRAW, !geometry.dynamic );
4516 
4517 				}
4518 
4519 				geometry.verticesNeedUpdate = false;
4520 				geometry.elementsNeedUpdate = false;
4521 				geometry.uvsNeedUpdate = false;
4522 				geometry.normalsNeedUpdate = false;
4523 				geometry.colorsNeedUpdate = false;
4524 				geometry.tangentsNeedUpdate = false;
4525 
4526 			} else {
4527 
4528 				// check all geometry groups
4529 
4530 				for( var i = 0, il = geometry.geometryGroupsList.length; i < il; i ++ ) {
4531 
4532 					geometryGroup = geometry.geometryGroupsList[ i ];
4533 
4534 					material = getBufferMaterial( object, geometryGroup );
4535 
4536 					if ( geometry.buffersNeedUpdate ) {
4537 
4538 						initMeshBuffers( geometryGroup, object );
4539 
4540 					}
4541 
4542 					customAttributesDirty = material.attributes && areCustomAttributesDirty( material );
4543 
4544 					if ( geometry.verticesNeedUpdate || geometry.morphTargetsNeedUpdate || geometry.elementsNeedUpdate ||
4545 						 geometry.uvsNeedUpdate || geometry.normalsNeedUpdate ||
4546 						 geometry.colorsNeedUpdate || geometry.tangentsNeedUpdate || customAttributesDirty ) {
4547 
4548 						setMeshBuffers( geometryGroup, object, _gl.DYNAMIC_DRAW, !geometry.dynamic, material );
4549 
4550 					}
4551 
4552 				}
4553 
4554 				geometry.verticesNeedUpdate = false;
4555 				geometry.morphTargetsNeedUpdate = false;
4556 				geometry.elementsNeedUpdate = false;
4557 				geometry.uvsNeedUpdate = false;
4558 				geometry.normalsNeedUpdate = false;
4559 				geometry.colorsNeedUpdate = false;
4560 				geometry.tangentsNeedUpdate = false;
4561 
4562 				geometry.buffersNeedUpdate = false;
4563 
4564 				material.attributes && clearCustomAttributes( material );
4565 
4566 			}
4567 
4568 		} else if ( object instanceof THREE.Ribbon ) {
4569 
4570 			material = getBufferMaterial( object, geometry );
4571 
4572 			customAttributesDirty = material.attributes && areCustomAttributesDirty( material );
4573 
4574 			if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || geometry.normalsNeedUpdate || customAttributesDirty ) {
4575 
4576 				setRibbonBuffers( geometry, _gl.DYNAMIC_DRAW );
4577 
4578 			}
4579 
4580 			geometry.verticesNeedUpdate = false;
4581 			geometry.colorsNeedUpdate = false;
4582 			geometry.normalsNeedUpdate = false;
4583 
4584 			material.attributes && clearCustomAttributes( material );
4585 
4586 		} else if ( object instanceof THREE.Line ) {
4587 
4588 			material = getBufferMaterial( object, geometry );
4589 
4590 			customAttributesDirty = material.attributes && areCustomAttributesDirty( material );
4591 
4592 			if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || geometry.lineDistancesNeedUpdate || customAttributesDirty ) {
4593 
4594 				setLineBuffers( geometry, _gl.DYNAMIC_DRAW );
4595 
4596 			}
4597 
4598 			geometry.verticesNeedUpdate = false;
4599 			geometry.colorsNeedUpdate = false;
4600 			geometry.lineDistancesNeedUpdate = false;
4601 
4602 			material.attributes && clearCustomAttributes( material );
4603 
4604 		} else if ( object instanceof THREE.ParticleSystem ) {
4605 
4606 			if ( geometry instanceof THREE.BufferGeometry ) {
4607 
4608 				if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate ) {
4609 
4610 					setDirectBuffers( geometry, _gl.DYNAMIC_DRAW, !geometry.dynamic );
4611 
4612 				}
4613 
4614 				geometry.verticesNeedUpdate = false;
4615 				geometry.colorsNeedUpdate = false;
4616 
4617 			} else {
4618 
4619 				material = getBufferMaterial( object, geometry );
4620 
4621 				customAttributesDirty = material.attributes && areCustomAttributesDirty( material );
4622 
4623 				if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || object.sortParticles || customAttributesDirty ) {
4624 
4625 					setParticleBuffers( geometry, _gl.DYNAMIC_DRAW, object );
4626 
4627 				}
4628 
4629 				geometry.verticesNeedUpdate = false;
4630 				geometry.colorsNeedUpdate = false;
4631 
4632 				material.attributes && clearCustomAttributes( material );
4633 
4634 			}
4635 
4636 		}
4637 
4638 	};
4639 
4640 	// Objects updates - custom attributes check
4641 
4642 	function areCustomAttributesDirty ( material ) {
4643 
4644 		for ( var a in material.attributes ) {
4645 
4646 			if ( material.attributes[ a ].needsUpdate ) return true;
4647 
4648 		}
4649 
4650 		return false;
4651 
4652 	};
4653 
4654 	function clearCustomAttributes ( material ) {
4655 
4656 		for ( var a in material.attributes ) {
4657 
4658 			material.attributes[ a ].needsUpdate = false;
4659 
4660 		}
4661 
4662 	};
4663 
4664 	// Objects removal
4665 
4666 	function removeObject ( object, scene ) {
4667 
4668 		if ( object instanceof THREE.Mesh  ||
4669 			 object instanceof THREE.ParticleSystem ||
4670 			 object instanceof THREE.Ribbon ||
4671 			 object instanceof THREE.Line ) {
4672 
4673 			removeInstances( scene.__webglObjects, object );
4674 
4675 		} else if ( object instanceof THREE.Sprite ) {
4676 
4677 			removeInstancesDirect( scene.__webglSprites, object );
4678 
4679 		} else if ( object instanceof THREE.LensFlare ) {
4680 
4681 			removeInstancesDirect( scene.__webglFlares, object );
4682 
4683 		} else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) {
4684 
4685 			removeInstances( scene.__webglObjectsImmediate, object );
4686 
4687 		}
4688 
4689 		object.__webglActive = false;
4690 
4691 	};
4692 
4693 	function removeInstances ( objlist, object ) {
4694 
4695 		for ( var o = objlist.length - 1; o >= 0; o -- ) {
4696 
4697 			if ( objlist[ o ].object === object ) {
4698 
4699 				objlist.splice( o, 1 );
4700 
4701 			}
4702 
4703 		}
4704 
4705 	};
4706 
4707 	function removeInstancesDirect ( objlist, object ) {
4708 
4709 		for ( var o = objlist.length - 1; o >= 0; o -- ) {
4710 
4711 			if ( objlist[ o ] === object ) {
4712 
4713 				objlist.splice( o, 1 );
4714 
4715 			}
4716 
4717 		}
4718 
4719 	};
4720 
4721 	// Materials
4722 
4723 	this.initMaterial = function ( material, lights, fog, object ) {
4724 
4725 		var u, a, identifiers, i, parameters, maxLightCount, maxBones, maxShadows, shaderID;
4726 
4727 		if ( material instanceof THREE.MeshDepthMaterial ) {
4728 
4729 			shaderID = 'depth';
4730 
4731 		} else if ( material instanceof THREE.MeshNormalMaterial ) {
4732 
4733 			shaderID = 'normal';
4734 
4735 		} else if ( material instanceof THREE.MeshBasicMaterial ) {
4736 
4737 			shaderID = 'basic';
4738 
4739 		} else if ( material instanceof THREE.MeshLambertMaterial ) {
4740 
4741 			shaderID = 'lambert';
4742 
4743 		} else if ( material instanceof THREE.MeshPhongMaterial ) {
4744 
4745 			shaderID = 'phong';
4746 
4747 		} else if ( material instanceof THREE.LineBasicMaterial ) {
4748 
4749 			shaderID = 'basic';
4750 
4751 		} else if ( material instanceof THREE.LineDashedMaterial ) {
4752 
4753 			shaderID = 'dashed';
4754 
4755 		} else if ( material instanceof THREE.ParticleBasicMaterial ) {
4756 
4757 			shaderID = 'particle_basic';
4758 
4759 		}
4760 
4761 		if ( shaderID ) {
4762 
4763 			setMaterialShaders( material, THREE.ShaderLib[ shaderID ] );
4764 
4765 		}
4766 
4767 		// heuristics to create shader parameters according to lights in the scene
4768 		// (not to blow over maxLights budget)
4769 
4770 		maxLightCount = allocateLights( lights );
4771 
4772 		maxShadows = allocateShadows( lights );
4773 
4774 		maxBones = allocateBones( object );
4775 
4776 		parameters = {
4777 
4778 			map: !!material.map,
4779 			envMap: !!material.envMap,
4780 			lightMap: !!material.lightMap,
4781 			bumpMap: !!material.bumpMap,
4782 			normalMap: !!material.normalMap,
4783 			specularMap: !!material.specularMap,
4784 
4785 			vertexColors: material.vertexColors,
4786 
4787 			fog: fog,
4788 			useFog: material.fog,
4789 			fogExp: fog instanceof THREE.FogExp2,
4790 
4791 			sizeAttenuation: material.sizeAttenuation,
4792 
4793 			skinning: material.skinning,
4794 			maxBones: maxBones,
4795 			useVertexTexture: _supportsBoneTextures && object && object.useVertexTexture,
4796 			boneTextureWidth: object && object.boneTextureWidth,
4797 			boneTextureHeight: object && object.boneTextureHeight,
4798 
4799 			morphTargets: material.morphTargets,
4800 			morphNormals: material.morphNormals,
4801 			maxMorphTargets: this.maxMorphTargets,
4802 			maxMorphNormals: this.maxMorphNormals,
4803 
4804 			maxDirLights: maxLightCount.directional,
4805 			maxPointLights: maxLightCount.point,
4806 			maxSpotLights: maxLightCount.spot,
4807 			maxHemiLights: maxLightCount.hemi,
4808 
4809 			maxShadows: maxShadows,
4810 			shadowMapEnabled: this.shadowMapEnabled && object.receiveShadow,
4811 			shadowMapSoft: this.shadowMapSoft,
4812 			shadowMapDebug: this.shadowMapDebug,
4813 			shadowMapCascade: this.shadowMapCascade,
4814 
4815 			alphaTest: material.alphaTest,
4816 			metal: material.metal,
4817 			perPixel: material.perPixel,
4818 			wrapAround: material.wrapAround,
4819 			doubleSided: material.side === THREE.DoubleSide,
4820 			flipSided: material.side === THREE.BackSide
4821 
4822 		};
4823 
4824 		material.program = buildProgram( shaderID, material.fragmentShader, material.vertexShader, material.uniforms, material.attributes, material.defines, parameters );
4825 
4826 		var attributes = material.program.attributes;
4827 
4828 		if ( attributes.position >= 0 ) _gl.enableVertexAttribArray( attributes.position );
4829 		if ( attributes.color >= 0 ) _gl.enableVertexAttribArray( attributes.color );
4830 		if ( attributes.normal >= 0 ) _gl.enableVertexAttribArray( attributes.normal );
4831 		if ( attributes.tangent >= 0 ) _gl.enableVertexAttribArray( attributes.tangent );
4832 		if ( attributes.lineDistance >= 0 ) _gl.enableVertexAttribArray( attributes.lineDistance );
4833 
4834 		if ( material.skinning &&
4835 			 attributes.skinIndex >= 0 && attributes.skinWeight >= 0 ) {
4836 
4837 			_gl.enableVertexAttribArray( attributes.skinIndex );
4838 			_gl.enableVertexAttribArray( attributes.skinWeight );
4839 
4840 		}
4841 
4842 		if ( material.attributes ) {
4843 
4844 			for ( a in material.attributes ) {
4845 
4846 				if ( attributes[ a ] !== undefined && attributes[ a ] >= 0 ) _gl.enableVertexAttribArray( attributes[ a ] );
4847 
4848 			}
4849 
4850 		}
4851 
4852 		if ( material.morphTargets ) {
4853 
4854 			material.numSupportedMorphTargets = 0;
4855 
4856 			var id, base = "morphTarget";
4857 
4858 			for ( i = 0; i < this.maxMorphTargets; i ++ ) {
4859 
4860 				id = base + i;
4861 
4862 				if ( attributes[ id ] >= 0 ) {
4863 
4864 					_gl.enableVertexAttribArray( attributes[ id ] );
4865 					material.numSupportedMorphTargets ++;
4866 
4867 				}
4868 
4869 			}
4870 
4871 		}
4872 
4873 		if ( material.morphNormals ) {
4874 
4875 			material.numSupportedMorphNormals = 0;
4876 
4877 			var id, base = "morphNormal";
4878 
4879 			for ( i = 0; i < this.maxMorphNormals; i ++ ) {
4880 
4881 				id = base + i;
4882 
4883 				if ( attributes[ id ] >= 0 ) {
4884 
4885 					_gl.enableVertexAttribArray( attributes[ id ] );
4886 					material.numSupportedMorphNormals ++;
4887 
4888 				}
4889 
4890 			}
4891 
4892 		}
4893 
4894 		material.uniformsList = [];
4895 
4896 		for ( u in material.uniforms ) {
4897 
4898 			material.uniformsList.push( [ material.uniforms[ u ], u ] );
4899 
4900 		}
4901 
4902 	};
4903 
4904 	function setMaterialShaders( material, shaders ) {
4905 
4906 		material.uniforms = THREE.UniformsUtils.clone( shaders.uniforms );
4907 		material.vertexShader = shaders.vertexShader;
4908 		material.fragmentShader = shaders.fragmentShader;
4909 
4910 	};
4911 
4912 	function setProgram( camera, lights, fog, material, object ) {
4913 
4914 		_usedTextureUnits = 0;
4915 
4916 		if ( material.needsUpdate ) {
4917 
4918 			if ( material.program ) _this.deallocateMaterial( material );
4919 
4920 			_this.initMaterial( material, lights, fog, object );
4921 			material.needsUpdate = false;
4922 
4923 		}
4924 
4925 		if ( material.morphTargets ) {
4926 
4927 			if ( ! object.__webglMorphTargetInfluences ) {
4928 
4929 				object.__webglMorphTargetInfluences = new Float32Array( _this.maxMorphTargets );
4930 
4931 			}
4932 
4933 		}
4934 
4935 		var refreshMaterial = false;
4936 
4937 		var program = material.program,
4938 			p_uniforms = program.uniforms,
4939 			m_uniforms = material.uniforms;
4940 
4941 		if ( program !== _currentProgram ) {
4942 
4943 			_gl.useProgram( program );
4944 			_currentProgram = program;
4945 
4946 			refreshMaterial = true;
4947 
4948 		}
4949 
4950 		if ( material.id !== _currentMaterialId ) {
4951 
4952 			_currentMaterialId = material.id;
4953 			refreshMaterial = true;
4954 
4955 		}
4956 
4957 		if ( refreshMaterial || camera !== _currentCamera ) {
4958 
4959 			_gl.uniformMatrix4fv( p_uniforms.projectionMatrix, false, camera._projectionMatrixArray );
4960 
4961 			if ( camera !== _currentCamera ) _currentCamera = camera;
4962 
4963 		}
4964 
4965 		// skinning uniforms must be set even if material didn't change
4966 		// auto-setting of texture unit for bone texture must go before other textures
4967 		// not sure why, but otherwise weird things happen
4968 
4969 		if ( material.skinning ) {
4970 
4971 			if ( _supportsBoneTextures && object.useVertexTexture ) {
4972 
4973 				if ( p_uniforms.boneTexture !== null ) {
4974 
4975 					var textureUnit = getTextureUnit();
4976 
4977 					_gl.uniform1i( p_uniforms.boneTexture, textureUnit );
4978 					_this.setTexture( object.boneTexture, textureUnit );
4979 
4980 				}
4981 
4982 			} else {
4983 
4984 				if ( p_uniforms.boneGlobalMatrices !== null ) {
4985 
4986 					_gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, object.boneMatrices );
4987 
4988 				}
4989 
4990 			}
4991 
4992 		}
4993 
4994 		if ( refreshMaterial ) {
4995 
4996 			// refresh uniforms common to several materials
4997 
4998 			if ( fog && material.fog ) {
4999 
5000 				refreshUniformsFog( m_uniforms, fog );
5001 
5002 			}
5003 
5004 			if ( material instanceof THREE.MeshPhongMaterial ||
5005 				 material instanceof THREE.MeshLambertMaterial ||
5006 				 material.lights ) {
5007 
5008 				if ( _lightsNeedUpdate ) {
5009 
5010 					setupLights( program, lights );
5011 					_lightsNeedUpdate = false;
5012 
5013 				}
5014 
5015 				refreshUniformsLights( m_uniforms, _lights );
5016 
5017 			}
5018 
5019 			if ( material instanceof THREE.MeshBasicMaterial ||
5020 				 material instanceof THREE.MeshLambertMaterial ||
5021 				 material instanceof THREE.MeshPhongMaterial ) {
5022 
5023 				refreshUniformsCommon( m_uniforms, material );
5024 
5025 			}
5026 
5027 			// refresh single material specific uniforms
5028 
5029 			if ( material instanceof THREE.LineBasicMaterial ) {
5030 
5031 				refreshUniformsLine( m_uniforms, material );
5032 
5033 			} else if ( material instanceof THREE.LineDashedMaterial ) {
5034 
5035 				refreshUniformsLine( m_uniforms, material );
5036 				refreshUniformsDash( m_uniforms, material );
5037 
5038 			} else if ( material instanceof THREE.ParticleBasicMaterial ) {
5039 
5040 				refreshUniformsParticle( m_uniforms, material );
5041 
5042 			} else if ( material instanceof THREE.MeshPhongMaterial ) {
5043 
5044 				refreshUniformsPhong( m_uniforms, material );
5045 
5046 			} else if ( material instanceof THREE.MeshLambertMaterial ) {
5047 
5048 				refreshUniformsLambert( m_uniforms, material );
5049 
5050 			} else if ( material instanceof THREE.MeshDepthMaterial ) {
5051 
5052 				m_uniforms.mNear.value = camera.near;
5053 				m_uniforms.mFar.value = camera.far;
5054 				m_uniforms.opacity.value = material.opacity;
5055 
5056 			} else if ( material instanceof THREE.MeshNormalMaterial ) {
5057 
5058 				m_uniforms.opacity.value = material.opacity;
5059 
5060 			}
5061 
5062 			if ( object.receiveShadow && ! material._shadowPass ) {
5063 
5064 				refreshUniformsShadow( m_uniforms, lights );
5065 
5066 			}
5067 
5068 			// load common uniforms
5069 
5070 			loadUniformsGeneric( program, material.uniformsList );
5071 
5072 			// load material specific uniforms
5073 			// (shader material also gets them for the sake of genericity)
5074 
5075 			if ( material instanceof THREE.ShaderMaterial ||
5076 				 material instanceof THREE.MeshPhongMaterial ||
5077 				 material.envMap ) {
5078 
5079 				if ( p_uniforms.cameraPosition !== null ) {
5080 
5081 					var position = camera.matrixWorld.getPosition();
5082 					_gl.uniform3f( p_uniforms.cameraPosition, position.x, position.y, position.z );
5083 
5084 				}
5085 
5086 			}
5087 
5088 			if ( material instanceof THREE.MeshPhongMaterial ||
5089 				 material instanceof THREE.MeshLambertMaterial ||
5090 				 material instanceof THREE.ShaderMaterial ||
5091 				 material.skinning ) {
5092 
5093 				if ( p_uniforms.viewMatrix !== null ) {
5094 
5095 					_gl.uniformMatrix4fv( p_uniforms.viewMatrix, false, camera._viewMatrixArray );
5096 
5097 				}
5098 
5099 			}
5100 
5101 		}
5102 
5103 		loadUniformsMatrices( p_uniforms, object );
5104 
5105 		if ( p_uniforms.modelMatrix !== null ) {
5106 
5107 			_gl.uniformMatrix4fv( p_uniforms.modelMatrix, false, object.matrixWorld.elements );
5108 
5109 		}
5110 
5111 		return program;
5112 
5113 	};
5114 
5115 	// Uniforms (refresh uniforms objects)
5116 
5117 	function refreshUniformsCommon ( uniforms, material ) {
5118 
5119 		uniforms.opacity.value = material.opacity;
5120 
5121 		if ( _this.gammaInput ) {
5122 
5123 			uniforms.diffuse.value.copyGammaToLinear( material.color );
5124 
5125 		} else {
5126 
5127 			uniforms.diffuse.value = material.color;
5128 
5129 		}
5130 
5131 		uniforms.map.value = material.map;
5132 		uniforms.lightMap.value = material.lightMap;
5133 		uniforms.specularMap.value = material.specularMap;
5134 
5135 		if ( material.bumpMap ) {
5136 
5137 			uniforms.bumpMap.value = material.bumpMap;
5138 			uniforms.bumpScale.value = material.bumpScale;
5139 
5140 		}
5141 
5142 		if ( material.normalMap ) {
5143 
5144 			uniforms.normalMap.value = material.normalMap;
5145 			uniforms.normalScale.value.copy( material.normalScale );
5146 
5147 		}
5148 
5149 		// uv repeat and offset setting priorities
5150 		//	1. color map
5151 		//	2. specular map
5152 		//	3. normal map
5153 		//	4. bump map
5154 
5155 		var uvScaleMap;
5156 
5157 		if ( material.map ) {
5158 
5159 			uvScaleMap = material.map;
5160 
5161 		} else if ( material.specularMap ) {
5162 
5163 			uvScaleMap = material.specularMap;
5164 
5165 		} else if ( material.normalMap ) {
5166 
5167 			uvScaleMap = material.normalMap;
5168 
5169 		} else if ( material.bumpMap ) {
5170 
5171 			uvScaleMap = material.bumpMap;
5172 
5173 		}
5174 
5175 		if ( uvScaleMap !== undefined ) {
5176 
5177 			var offset = uvScaleMap.offset;
5178 			var repeat = uvScaleMap.repeat;
5179 
5180 			uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y );
5181 
5182 		}
5183 
5184 		uniforms.envMap.value = material.envMap;
5185 		uniforms.flipEnvMap.value = ( material.envMap instanceof THREE.WebGLRenderTargetCube ) ? 1 : -1;
5186 
5187 		if ( _this.gammaInput ) {
5188 
5189 			//uniforms.reflectivity.value = material.reflectivity * material.reflectivity;
5190 			uniforms.reflectivity.value = material.reflectivity;
5191 
5192 		} else {
5193 
5194 			uniforms.reflectivity.value = material.reflectivity;
5195 
5196 		}
5197 
5198 		uniforms.refractionRatio.value = material.refractionRatio;
5199 		uniforms.combine.value = material.combine;
5200 		uniforms.useRefract.value = material.envMap && material.envMap.mapping instanceof THREE.CubeRefractionMapping;
5201 
5202 	};
5203 
5204 	function refreshUniformsLine ( uniforms, material ) {
5205 
5206 		uniforms.diffuse.value = material.color;
5207 		uniforms.opacity.value = material.opacity;
5208 
5209 	};
5210 
5211 	function refreshUniformsDash ( uniforms, material ) {
5212 
5213 		uniforms.dashSize.value = material.dashSize;
5214 		uniforms.totalSize.value = material.dashSize + material.gapSize;
5215 		uniforms.scale.value = material.scale;
5216 
5217 	};
5218 
5219 	function refreshUniformsParticle ( uniforms, material ) {
5220 
5221 		uniforms.psColor.value = material.color;
5222 		uniforms.opacity.value = material.opacity;
5223 		uniforms.size.value = material.size;
5224 		uniforms.scale.value = _canvas.height / 2.0; // TODO: Cache this.
5225 
5226 		uniforms.map.value = material.map;
5227 
5228 	};
5229 
5230 	function refreshUniformsFog ( uniforms, fog ) {
5231 
5232 		uniforms.fogColor.value = fog.color;
5233 
5234 		if ( fog instanceof THREE.Fog ) {
5235 
5236 			uniforms.fogNear.value = fog.near;
5237 			uniforms.fogFar.value = fog.far;
5238 
5239 		} else if ( fog instanceof THREE.FogExp2 ) {
5240 
5241 			uniforms.fogDensity.value = fog.density;
5242 
5243 		}
5244 
5245 	};
5246 
5247 	function refreshUniformsPhong ( uniforms, material ) {
5248 
5249 		uniforms.shininess.value = material.shininess;
5250 
5251 		if ( _this.gammaInput ) {
5252 
5253 			uniforms.ambient.value.copyGammaToLinear( material.ambient );
5254 			uniforms.emissive.value.copyGammaToLinear( material.emissive );
5255 			uniforms.specular.value.copyGammaToLinear( material.specular );
5256 
5257 		} else {
5258 
5259 			uniforms.ambient.value = material.ambient;
5260 			uniforms.emissive.value = material.emissive;
5261 			uniforms.specular.value = material.specular;
5262 
5263 		}
5264 
5265 		if ( material.wrapAround ) {
5266 
5267 			uniforms.wrapRGB.value.copy( material.wrapRGB );
5268 
5269 		}
5270 
5271 	};
5272 
5273 	function refreshUniformsLambert ( uniforms, material ) {
5274 
5275 		if ( _this.gammaInput ) {
5276 
5277 			uniforms.ambient.value.copyGammaToLinear( material.ambient );
5278 			uniforms.emissive.value.copyGammaToLinear( material.emissive );
5279 
5280 		} else {
5281 
5282 			uniforms.ambient.value = material.ambient;
5283 			uniforms.emissive.value = material.emissive;
5284 
5285 		}
5286 
5287 		if ( material.wrapAround ) {
5288 
5289 			uniforms.wrapRGB.value.copy( material.wrapRGB );
5290 
5291 		}
5292 
5293 	};
5294 
5295 	function refreshUniformsLights ( uniforms, lights ) {
5296 
5297 		uniforms.ambientLightColor.value = lights.ambient;
5298 
5299 		uniforms.directionalLightColor.value = lights.directional.colors;
5300 		uniforms.directionalLightDirection.value = lights.directional.positions;
5301 
5302 		uniforms.pointLightColor.value = lights.point.colors;
5303 		uniforms.pointLightPosition.value = lights.point.positions;
5304 		uniforms.pointLightDistance.value = lights.point.distances;
5305 
5306 		uniforms.spotLightColor.value = lights.spot.colors;
5307 		uniforms.spotLightPosition.value = lights.spot.positions;
5308 		uniforms.spotLightDistance.value = lights.spot.distances;
5309 		uniforms.spotLightDirection.value = lights.spot.directions;
5310 		uniforms.spotLightAngleCos.value = lights.spot.anglesCos;
5311 		uniforms.spotLightExponent.value = lights.spot.exponents;
5312 
5313 		uniforms.hemisphereLightSkyColor.value = lights.hemi.skyColors;
5314 		uniforms.hemisphereLightGroundColor.value = lights.hemi.groundColors;
5315 		uniforms.hemisphereLightDirection.value = lights.hemi.positions;
5316 
5317 	};
5318 
5319 	function refreshUniformsShadow ( uniforms, lights ) {
5320 
5321 		if ( uniforms.shadowMatrix ) {
5322 
5323 			var j = 0;
5324 
5325 			for ( var i = 0, il = lights.length; i < il; i ++ ) {
5326 
5327 				var light = lights[ i ];
5328 
5329 				if ( ! light.castShadow ) continue;
5330 
5331 				if ( light instanceof THREE.SpotLight || ( light instanceof THREE.DirectionalLight && ! light.shadowCascade ) ) {
5332 
5333 					uniforms.shadowMap.value[ j ] = light.shadowMap;
5334 					uniforms.shadowMapSize.value[ j ] = light.shadowMapSize;
5335 
5336 					uniforms.shadowMatrix.value[ j ] = light.shadowMatrix;
5337 
5338 					uniforms.shadowDarkness.value[ j ] = light.shadowDarkness;
5339 					uniforms.shadowBias.value[ j ] = light.shadowBias;
5340 
5341 					j ++;
5342 
5343 				}
5344 
5345 			}
5346 
5347 		}
5348 
5349 	};
5350 
5351 	// Uniforms (load to GPU)
5352 
5353 	function loadUniformsMatrices ( uniforms, object ) {
5354 
5355 		_gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, object._modelViewMatrix.elements );
5356 
5357 		if ( uniforms.normalMatrix ) {
5358 
5359 			_gl.uniformMatrix3fv( uniforms.normalMatrix, false, object._normalMatrix.elements );
5360 
5361 		}
5362 
5363 	};
5364 
5365 	function getTextureUnit() {
5366 
5367 		var textureUnit = _usedTextureUnits;
5368 
5369 		if ( textureUnit >= _maxTextures ) {
5370 
5371 			console.warn( "Trying to use " + textureUnit + " texture units while this GPU supports only " + _maxTextures );
5372 
5373 		}
5374 
5375 		_usedTextureUnits += 1;
5376 
5377 		return textureUnit;
5378 
5379 	};
5380 
5381 	function loadUniformsGeneric ( program, uniforms ) {
5382 
5383 		var uniform, value, type, location, texture, textureUnit, i, il, j, jl, offset;
5384 
5385 		for ( j = 0, jl = uniforms.length; j < jl; j ++ ) {
5386 
5387 			location = program.uniforms[ uniforms[ j ][ 1 ] ];
5388 			if ( !location ) continue;
5389 
5390 			uniform = uniforms[ j ][ 0 ];
5391 
5392 			type = uniform.type;
5393 			value = uniform.value;
5394 
5395 			if ( type === "i" ) { // single integer
5396 
5397 				_gl.uniform1i( location, value );
5398 
5399 			} else if ( type === "f" ) { // single float
5400 
5401 				_gl.uniform1f( location, value );
5402 
5403 			} else if ( type === "v2" ) { // single THREE.Vector2
5404 
5405 				_gl.uniform2f( location, value.x, value.y );
5406 
5407 			} else if ( type === "v3" ) { // single THREE.Vector3
5408 
5409 				_gl.uniform3f( location, value.x, value.y, value.z );
5410 
5411 			} else if ( type === "v4" ) { // single THREE.Vector4
5412 
5413 				_gl.uniform4f( location, value.x, value.y, value.z, value.w );
5414 
5415 			} else if ( type === "c" ) { // single THREE.Color
5416 
5417 				_gl.uniform3f( location, value.r, value.g, value.b );
5418 
5419 			} else if ( type === "iv1" ) { // flat array of integers (JS or typed array)
5420 
5421 				_gl.uniform1iv( location, value );
5422 
5423 			} else if ( type === "iv" ) { // flat array of integers with 3 x N size (JS or typed array)
5424 
5425 				_gl.uniform3iv( location, value );
5426 
5427 			} else if ( type === "fv1" ) { // flat array of floats (JS or typed array)
5428 
5429 				_gl.uniform1fv( location, value );
5430 
5431 			} else if ( type === "fv" ) { // flat array of floats with 3 x N size (JS or typed array)
5432 
5433 				_gl.uniform3fv( location, value );
5434 
5435 			} else if ( type === "v2v" ) { // array of THREE.Vector2
5436 
5437 				if ( uniform._array === undefined ) {
5438 
5439 					uniform._array = new Float32Array( 2 * value.length );
5440 
5441 				}
5442 
5443 				for ( i = 0, il = value.length; i < il; i ++ ) {
5444 
5445 					offset = i * 2;
5446 
5447 					uniform._array[ offset ] 	 = value[ i ].x;
5448 					uniform._array[ offset + 1 ] = value[ i ].y;
5449 
5450 				}
5451 
5452 				_gl.uniform2fv( location, uniform._array );
5453 
5454 			} else if ( type === "v3v" ) { // array of THREE.Vector3
5455 
5456 				if ( uniform._array === undefined ) {
5457 
5458 					uniform._array = new Float32Array( 3 * value.length );
5459 
5460 				}
5461 
5462 				for ( i = 0, il = value.length; i < il; i ++ ) {
5463 
5464 					offset = i * 3;
5465 
5466 					uniform._array[ offset ] 	 = value[ i ].x;
5467 					uniform._array[ offset + 1 ] = value[ i ].y;
5468 					uniform._array[ offset + 2 ] = value[ i ].z;
5469 
5470 				}
5471 
5472 				_gl.uniform3fv( location, uniform._array );
5473 
5474 			} else if ( type === "v4v" ) { // array of THREE.Vector4
5475 
5476 				if ( uniform._array === undefined ) {
5477 
5478 					uniform._array = new Float32Array( 4 * value.length );
5479 
5480 				}
5481 
5482 				for ( i = 0, il = value.length; i < il; i ++ ) {
5483 
5484 					offset = i * 4;
5485 
5486 					uniform._array[ offset ] 	 = value[ i ].x;
5487 					uniform._array[ offset + 1 ] = value[ i ].y;
5488 					uniform._array[ offset + 2 ] = value[ i ].z;
5489 					uniform._array[ offset + 3 ] = value[ i ].w;
5490 
5491 				}
5492 
5493 				_gl.uniform4fv( location, uniform._array );
5494 
5495 			} else if ( type === "m4") { // single THREE.Matrix4
5496 
5497 				if ( uniform._array === undefined ) {
5498 
5499 					uniform._array = new Float32Array( 16 );
5500 
5501 				}
5502 
5503 				value.flattenToArray( uniform._array );
5504 				_gl.uniformMatrix4fv( location, false, uniform._array );
5505 
5506 			} else if ( type === "m4v" ) { // array of THREE.Matrix4
5507 
5508 				if ( uniform._array === undefined ) {
5509 
5510 					uniform._array = new Float32Array( 16 * value.length );
5511 
5512 				}
5513 
5514 				for ( i = 0, il = value.length; i < il; i ++ ) {
5515 
5516 					value[ i ].flattenToArrayOffset( uniform._array, i * 16 );
5517 
5518 				}
5519 
5520 				_gl.uniformMatrix4fv( location, false, uniform._array );
5521 
5522 			} else if ( type === "t" ) { // single THREE.Texture (2d or cube)
5523 
5524 				texture = value;
5525 				textureUnit = getTextureUnit();
5526 
5527 				_gl.uniform1i( location, textureUnit );
5528 
5529 				if ( !texture ) continue;
5530 
5531 				if ( texture.image instanceof Array && texture.image.length === 6 ) {
5532 
5533 					setCubeTexture( texture, textureUnit );
5534 
5535 				} else if ( texture instanceof THREE.WebGLRenderTargetCube ) {
5536 
5537 					setCubeTextureDynamic( texture, textureUnit );
5538 
5539 				} else {
5540 
5541 					_this.setTexture( texture, textureUnit );
5542 
5543 				}
5544 
5545 			} else if ( type === "tv" ) { // array of THREE.Texture (2d)
5546 
5547 				if ( uniform._array === undefined ) {
5548 
5549 					uniform._array = [];
5550 
5551 				}
5552 
5553 				for( i = 0, il = uniform.value.length; i < il; i ++ ) {
5554 
5555 					uniform._array[ i ] = getTextureUnit();
5556 
5557 				}
5558 
5559 				_gl.uniform1iv( location, uniform._array );
5560 
5561 				for( i = 0, il = uniform.value.length; i < il; i ++ ) {
5562 
5563 					texture = uniform.value[ i ];
5564 					textureUnit = uniform._array[ i ];
5565 
5566 					if ( !texture ) continue;
5567 
5568 					_this.setTexture( texture, textureUnit );
5569 
5570 				}
5571 
5572 			}
5573 
5574 		}
5575 
5576 	};
5577 
5578 	function setupMatrices ( object, camera ) {
5579 
5580 		object._modelViewMatrix.multiply( camera.matrixWorldInverse, object.matrixWorld );
5581 
5582 		object._normalMatrix.getInverse( object._modelViewMatrix );
5583 		object._normalMatrix.transpose();
5584 
5585 	};
5586 
5587 	//
5588 
5589 	function setColorGamma( array, offset, color, intensitySq ) {
5590 
5591 		array[ offset ]     = color.r * color.r * intensitySq;
5592 		array[ offset + 1 ] = color.g * color.g * intensitySq;
5593 		array[ offset + 2 ] = color.b * color.b * intensitySq;
5594 
5595 	};
5596 
5597 	function setColorLinear( array, offset, color, intensity ) {
5598 
5599 		array[ offset ]     = color.r * intensity;
5600 		array[ offset + 1 ] = color.g * intensity;
5601 		array[ offset + 2 ] = color.b * intensity;
5602 
5603 	};
5604 
5605 	function setupLights ( program, lights ) {
5606 
5607 		var l, ll, light, n,
5608 		r = 0, g = 0, b = 0,
5609 		color, skyColor, groundColor,
5610 		intensity,  intensitySq,
5611 		position,
5612 		distance,
5613 
5614 		zlights = _lights,
5615 
5616 		dirColors = zlights.directional.colors,
5617 		dirPositions = zlights.directional.positions,
5618 
5619 		pointColors = zlights.point.colors,
5620 		pointPositions = zlights.point.positions,
5621 		pointDistances = zlights.point.distances,
5622 
5623 		spotColors = zlights.spot.colors,
5624 		spotPositions = zlights.spot.positions,
5625 		spotDistances = zlights.spot.distances,
5626 		spotDirections = zlights.spot.directions,
5627 		spotAnglesCos = zlights.spot.anglesCos,
5628 		spotExponents = zlights.spot.exponents,
5629 
5630 		hemiSkyColors = zlights.hemi.skyColors,
5631 		hemiGroundColors = zlights.hemi.groundColors,
5632 		hemiPositions = zlights.hemi.positions,
5633 
5634 		dirLength = 0,
5635 		pointLength = 0,
5636 		spotLength = 0,
5637 		hemiLength = 0,
5638 
5639 		dirCount = 0,
5640 		pointCount = 0,
5641 		spotCount = 0,
5642 		hemiCount = 0,
5643 
5644 		dirOffset = 0,
5645 		pointOffset = 0,
5646 		spotOffset = 0,
5647 		hemiOffset = 0;
5648 
5649 		for ( l = 0, ll = lights.length; l < ll; l ++ ) {
5650 
5651 			light = lights[ l ];
5652 
5653 			if ( light.onlyShadow ) continue;
5654 
5655 			color = light.color;
5656 			intensity = light.intensity;
5657 			distance = light.distance;
5658 
5659 			if ( light instanceof THREE.AmbientLight ) {
5660 
5661 				if ( ! light.visible ) continue;
5662 
5663 				if ( _this.gammaInput ) {
5664 
5665 					r += color.r * color.r;
5666 					g += color.g * color.g;
5667 					b += color.b * color.b;
5668 
5669 				} else {
5670 
5671 					r += color.r;
5672 					g += color.g;
5673 					b += color.b;
5674 
5675 				}
5676 
5677 			} else if ( light instanceof THREE.DirectionalLight ) {
5678 
5679 				dirCount += 1;
5680 
5681 				if ( ! light.visible ) continue;
5682 
5683 				dirOffset = dirLength * 3;
5684 
5685 				if ( _this.gammaInput ) {
5686 
5687 					setColorGamma( dirColors, dirOffset, color, intensity * intensity );
5688 
5689 				} else {
5690 
5691 					setColorLinear( dirColors, dirOffset, color, intensity );
5692 
5693 				}
5694 
5695 				_direction.copy( light.matrixWorld.getPosition() );
5696 				_direction.subSelf( light.target.matrixWorld.getPosition() );
5697 				_direction.normalize();
5698 
5699 				dirPositions[ dirOffset ]     = _direction.x;
5700 				dirPositions[ dirOffset + 1 ] = _direction.y;
5701 				dirPositions[ dirOffset + 2 ] = _direction.z;
5702 
5703 				dirLength += 1;
5704 
5705 			} else if ( light instanceof THREE.PointLight ) {
5706 
5707 				pointCount += 1;
5708 
5709 				if ( ! light.visible ) continue;
5710 
5711 				pointOffset = pointLength * 3;
5712 
5713 				if ( _this.gammaInput ) {
5714 
5715 					setColorGamma( pointColors, pointOffset, color, intensity * intensity );
5716 
5717 				} else {
5718 
5719 					setColorLinear( pointColors, pointOffset, color, intensity );
5720 
5721 				}
5722 
5723 				position = light.matrixWorld.getPosition();
5724 
5725 				pointPositions[ pointOffset ]     = position.x;
5726 				pointPositions[ pointOffset + 1 ] = position.y;
5727 				pointPositions[ pointOffset + 2 ] = position.z;
5728 
5729 				pointDistances[ pointLength ] = distance;
5730 
5731 				pointLength += 1;
5732 
5733 			} else if ( light instanceof THREE.SpotLight ) {
5734 
5735 				spotCount += 1;
5736 
5737 				if ( ! light.visible ) continue;
5738 
5739 				spotOffset = spotLength * 3;
5740 
5741 				if ( _this.gammaInput ) {
5742 
5743 					setColorGamma( spotColors, spotOffset, color, intensity * intensity );
5744 
5745 				} else {
5746 
5747 					setColorLinear( spotColors, spotOffset, color, intensity );
5748 
5749 				}
5750 
5751 				position = light.matrixWorld.getPosition();
5752 
5753 				spotPositions[ spotOffset ]     = position.x;
5754 				spotPositions[ spotOffset + 1 ] = position.y;
5755 				spotPositions[ spotOffset + 2 ] = position.z;
5756 
5757 				spotDistances[ spotLength ] = distance;
5758 
5759 				_direction.copy( position );
5760 				_direction.subSelf( light.target.matrixWorld.getPosition() );
5761 				_direction.normalize();
5762 
5763 				spotDirections[ spotOffset ]     = _direction.x;
5764 				spotDirections[ spotOffset + 1 ] = _direction.y;
5765 				spotDirections[ spotOffset + 2 ] = _direction.z;
5766 
5767 				spotAnglesCos[ spotLength ] = Math.cos( light.angle );
5768 				spotExponents[ spotLength ] = light.exponent;
5769 
5770 				spotLength += 1;
5771 
5772 			} else if ( light instanceof THREE.HemisphereLight ) {
5773 
5774 				hemiCount += 1;
5775 
5776 				if ( ! light.visible ) continue;
5777 
5778 				skyColor = light.color;
5779 				groundColor = light.groundColor;
5780 
5781 				hemiOffset = hemiLength * 3;
5782 
5783 				if ( _this.gammaInput ) {
5784 
5785 					intensitySq = intensity * intensity;
5786 
5787 					setColorGamma( hemiSkyColors, hemiOffset, skyColor, intensitySq );
5788 					setColorGamma( hemiGroundColors, hemiOffset, groundColor, intensitySq );
5789 
5790 				} else {
5791 
5792 					setColorLinear( hemiSkyColors, hemiOffset, skyColor, intensity );
5793 					setColorLinear( hemiGroundColors, hemiOffset, groundColor, intensity );
5794 
5795 				}
5796 
5797 				_direction.copy( light.matrixWorld.getPosition() );
5798 				_direction.normalize();
5799 
5800 				hemiPositions[ hemiOffset ]     = _direction.x;
5801 				hemiPositions[ hemiOffset + 1 ] = _direction.y;
5802 				hemiPositions[ hemiOffset + 2 ] = _direction.z;
5803 
5804 				hemiLength += 1;
5805 
5806 			}
5807 
5808 		}
5809 
5810 		// null eventual remains from removed lights
5811 		// (this is to avoid if in shader)
5812 
5813 		for ( l = dirLength * 3, ll = Math.max( dirColors.length, dirCount * 3 ); l < ll; l ++ ) dirColors[ l ] = 0.0;
5814 		for ( l = dirLength * 3, ll = Math.max( dirPositions.length, dirCount * 3 ); l < ll; l ++ ) dirPositions[ l ] = 0.0;
5815 
5816 		for ( l = pointLength * 3, ll = Math.max( pointColors.length, pointCount * 3 ); l < ll; l ++ ) pointColors[ l ] = 0.0;
5817 		for ( l = pointLength * 3, ll = Math.max( pointPositions.length, pointCount * 3 ); l < ll; l ++ ) pointPositions[ l ] = 0.0;
5818 		for ( l = pointLength, ll = Math.max( pointDistances.length, pointCount ); l < ll; l ++ ) pointDistances[ l ] = 0.0;
5819 
5820 		for ( l = spotLength * 3, ll = Math.max( spotColors.length, spotCount * 3 ); l < ll; l ++ ) spotColors[ l ] = 0.0;
5821 		for ( l = spotLength * 3, ll = Math.max( spotPositions.length, spotCount * 3 ); l < ll; l ++ ) spotPositions[ l ] = 0.0;
5822 		for ( l = spotLength * 3, ll = Math.max( spotDirections.length, spotCount * 3 ); l < ll; l ++ ) spotDirections[ l ] = 0.0;
5823 		for ( l = spotLength, ll = Math.max( spotAnglesCos.length, spotCount ); l < ll; l ++ ) spotAnglesCos[ l ] = 0.0;
5824 		for ( l = spotLength, ll = Math.max( spotExponents.length, spotCount ); l < ll; l ++ ) spotExponents[ l ] = 0.0;
5825 		for ( l = spotLength, ll = Math.max( spotDistances.length, spotCount ); l < ll; l ++ ) spotDistances[ l ] = 0.0;
5826 
5827 		for ( l = hemiLength * 3, ll = Math.max( hemiSkyColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiSkyColors[ l ] = 0.0;
5828 		for ( l = hemiLength * 3, ll = Math.max( hemiGroundColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiGroundColors[ l ] = 0.0;
5829 		for ( l = hemiLength * 3, ll = Math.max( hemiPositions.length, hemiCount * 3 ); l < ll; l ++ ) hemiPositions[ l ] = 0.0;
5830 
5831 		zlights.directional.length = dirLength;
5832 		zlights.point.length = pointLength;
5833 		zlights.spot.length = spotLength;
5834 		zlights.hemi.length = hemiLength;
5835 
5836 		zlights.ambient[ 0 ] = r;
5837 		zlights.ambient[ 1 ] = g;
5838 		zlights.ambient[ 2 ] = b;
5839 
5840 	};
5841 
5842 	// GL state setting
5843 
5844 	this.setFaceCulling = function ( cullFace, frontFace ) {
5845 
5846 		if ( cullFace ) {
5847 
5848 			if ( !frontFace || frontFace === "ccw" ) {
5849 
5850 				_gl.frontFace( _gl.CCW );
5851 
5852 			} else {
5853 
5854 				_gl.frontFace( _gl.CW );
5855 
5856 			}
5857 
5858 			if( cullFace === "back" ) {
5859 
5860 				_gl.cullFace( _gl.BACK );
5861 
5862 			} else if( cullFace === "front" ) {
5863 
5864 				_gl.cullFace( _gl.FRONT );
5865 
5866 			} else {
5867 
5868 				_gl.cullFace( _gl.FRONT_AND_BACK );
5869 
5870 			}
5871 
5872 			_gl.enable( _gl.CULL_FACE );
5873 
5874 		} else {
5875 
5876 			_gl.disable( _gl.CULL_FACE );
5877 
5878 		}
5879 
5880 	};
5881 
5882 	this.setMaterialFaces = function ( material ) {
5883 
5884 		var doubleSided = material.side === THREE.DoubleSide;
5885 		var flipSided = material.side === THREE.BackSide;
5886 
5887 		if ( _oldDoubleSided !== doubleSided ) {
5888 
5889 			if ( doubleSided ) {
5890 
5891 				_gl.disable( _gl.CULL_FACE );
5892 
5893 			} else {
5894 
5895 				_gl.enable( _gl.CULL_FACE );
5896 
5897 			}
5898 
5899 			_oldDoubleSided = doubleSided;
5900 
5901 		}
5902 
5903 		if ( _oldFlipSided !== flipSided ) {
5904 
5905 			if ( flipSided ) {
5906 
5907 				_gl.frontFace( _gl.CW );
5908 
5909 			} else {
5910 
5911 				_gl.frontFace( _gl.CCW );
5912 
5913 			}
5914 
5915 			_oldFlipSided = flipSided;
5916 
5917 		}
5918 
5919 	};
5920 
5921 	this.setDepthTest = function ( depthTest ) {
5922 
5923 		if ( _oldDepthTest !== depthTest ) {
5924 
5925 			if ( depthTest ) {
5926 
5927 				_gl.enable( _gl.DEPTH_TEST );
5928 
5929 			} else {
5930 
5931 				_gl.disable( _gl.DEPTH_TEST );
5932 
5933 			}
5934 
5935 			_oldDepthTest = depthTest;
5936 
5937 		}
5938 
5939 	};
5940 
5941 	this.setDepthWrite = function ( depthWrite ) {
5942 
5943 		if ( _oldDepthWrite !== depthWrite ) {
5944 
5945 			_gl.depthMask( depthWrite );
5946 			_oldDepthWrite = depthWrite;
5947 
5948 		}
5949 
5950 	};
5951 
5952 	function setLineWidth ( width ) {
5953 
5954 		if ( width !== _oldLineWidth ) {
5955 
5956 			_gl.lineWidth( width );
5957 
5958 			_oldLineWidth = width;
5959 
5960 		}
5961 
5962 	};
5963 
5964 	function setPolygonOffset ( polygonoffset, factor, units ) {
5965 
5966 		if ( _oldPolygonOffset !== polygonoffset ) {
5967 
5968 			if ( polygonoffset ) {
5969 
5970 				_gl.enable( _gl.POLYGON_OFFSET_FILL );
5971 
5972 			} else {
5973 
5974 				_gl.disable( _gl.POLYGON_OFFSET_FILL );
5975 
5976 			}
5977 
5978 			_oldPolygonOffset = polygonoffset;
5979 
5980 		}
5981 
5982 		if ( polygonoffset && ( _oldPolygonOffsetFactor !== factor || _oldPolygonOffsetUnits !== units ) ) {
5983 
5984 			_gl.polygonOffset( factor, units );
5985 
5986 			_oldPolygonOffsetFactor = factor;
5987 			_oldPolygonOffsetUnits = units;
5988 
5989 		}
5990 
5991 	};
5992 
5993 	this.setBlending = function ( blending, blendEquation, blendSrc, blendDst ) {
5994 
5995 		if ( blending !== _oldBlending ) {
5996 
5997 			if ( blending === THREE.NoBlending ) {
5998 
5999 				_gl.disable( _gl.BLEND );
6000 
6001 			} else if ( blending === THREE.AdditiveBlending ) {
6002 
6003 				_gl.enable( _gl.BLEND );
6004 				_gl.blendEquation( _gl.FUNC_ADD );
6005 				_gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE );
6006 
6007 			} else if ( blending === THREE.SubtractiveBlending ) {
6008 
6009 				// TODO: Find blendFuncSeparate() combination
6010 				_gl.enable( _gl.BLEND );
6011 				_gl.blendEquation( _gl.FUNC_ADD );
6012 				_gl.blendFunc( _gl.ZERO, _gl.ONE_MINUS_SRC_COLOR );
6013 
6014 			} else if ( blending === THREE.MultiplyBlending ) {
6015 
6016 				// TODO: Find blendFuncSeparate() combination
6017 				_gl.enable( _gl.BLEND );
6018 				_gl.blendEquation( _gl.FUNC_ADD );
6019 				_gl.blendFunc( _gl.ZERO, _gl.SRC_COLOR );
6020 
6021 			} else if ( blending === THREE.CustomBlending ) {
6022 
6023 				_gl.enable( _gl.BLEND );
6024 
6025 			} else {
6026 
6027 				_gl.enable( _gl.BLEND );
6028 				_gl.blendEquationSeparate( _gl.FUNC_ADD, _gl.FUNC_ADD );
6029 				_gl.blendFuncSeparate( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA, _gl.ONE, _gl.ONE_MINUS_SRC_ALPHA );
6030 
6031 			}
6032 
6033 			_oldBlending = blending;
6034 
6035 		}
6036 
6037 		if ( blending === THREE.CustomBlending ) {
6038 
6039 			if ( blendEquation !== _oldBlendEquation ) {
6040 
6041 				_gl.blendEquation( paramThreeToGL( blendEquation ) );
6042 
6043 				_oldBlendEquation = blendEquation;
6044 
6045 			}
6046 
6047 			if ( blendSrc !== _oldBlendSrc || blendDst !== _oldBlendDst ) {
6048 
6049 				_gl.blendFunc( paramThreeToGL( blendSrc ), paramThreeToGL( blendDst ) );
6050 
6051 				_oldBlendSrc = blendSrc;
6052 				_oldBlendDst = blendDst;
6053 
6054 			}
6055 
6056 		} else {
6057 
6058 			_oldBlendEquation = null;
6059 			_oldBlendSrc = null;
6060 			_oldBlendDst = null;
6061 
6062 		}
6063 
6064 	};
6065 
6066 	// Defines
6067 
6068 	function generateDefines ( defines ) {
6069 
6070 		var value, chunk, chunks = [];
6071 
6072 		for ( var d in defines ) {
6073 
6074 			value = defines[ d ];
6075 			if ( value === false ) continue;
6076 
6077 			chunk = "#define " + d + " " + value;
6078 			chunks.push( chunk );
6079 
6080 		}
6081 
6082 		return chunks.join( "\n" );
6083 
6084 	};
6085 
6086 	// Shaders
6087 
6088 	function buildProgram ( shaderID, fragmentShader, vertexShader, uniforms, attributes, defines, parameters ) {
6089 
6090 		var p, pl, d, program, code;
6091 		var chunks = [];
6092 
6093 		// Generate code
6094 
6095 		if ( shaderID ) {
6096 
6097 			chunks.push( shaderID );
6098 
6099 		} else {
6100 
6101 			chunks.push( fragmentShader );
6102 			chunks.push( vertexShader );
6103 
6104 		}
6105 
6106 		for ( d in defines ) {
6107 
6108 			chunks.push( d );
6109 			chunks.push( defines[ d ] );
6110 
6111 		}
6112 
6113 		for ( p in parameters ) {
6114 
6115 			chunks.push( p );
6116 			chunks.push( parameters[ p ] );
6117 
6118 		}
6119 
6120 		code = chunks.join();
6121 
6122 		// Check if code has been already compiled
6123 
6124 		for ( p = 0, pl = _programs.length; p < pl; p ++ ) {
6125 
6126 			var programInfo = _programs[ p ];
6127 
6128 			if ( programInfo.code === code ) {
6129 
6130 				//console.log( "Code already compiled." /*: \n\n" + code*/ );
6131 
6132 				programInfo.usedTimes ++;
6133 
6134 				return programInfo.program;
6135 
6136 			}
6137 
6138 		}
6139 
6140 		//console.log( "building new program " );
6141 
6142 		//
6143 
6144 		var customDefines = generateDefines( defines );
6145 
6146 		//
6147 
6148 		program = _gl.createProgram();
6149 
6150 		var prefix_vertex = [
6151 
6152 			"precision " + _precision + " float;",
6153 
6154 			customDefines,
6155 
6156 			_supportsVertexTextures ? "#define VERTEX_TEXTURES" : "",
6157 
6158 			_this.gammaInput ? "#define GAMMA_INPUT" : "",
6159 			_this.gammaOutput ? "#define GAMMA_OUTPUT" : "",
6160 			_this.physicallyBasedShading ? "#define PHYSICALLY_BASED_SHADING" : "",
6161 
6162 			"#define MAX_DIR_LIGHTS " + parameters.maxDirLights,
6163 			"#define MAX_POINT_LIGHTS " + parameters.maxPointLights,
6164 			"#define MAX_SPOT_LIGHTS " + parameters.maxSpotLights,
6165 			"#define MAX_HEMI_LIGHTS " + parameters.maxHemiLights,
6166 
6167 			"#define MAX_SHADOWS " + parameters.maxShadows,
6168 
6169 			"#define MAX_BONES " + parameters.maxBones,
6170 
6171 			parameters.map ? "#define USE_MAP" : "",
6172 			parameters.envMap ? "#define USE_ENVMAP" : "",
6173 			parameters.lightMap ? "#define USE_LIGHTMAP" : "",
6174 			parameters.bumpMap ? "#define USE_BUMPMAP" : "",
6175 			parameters.normalMap ? "#define USE_NORMALMAP" : "",
6176 			parameters.specularMap ? "#define USE_SPECULARMAP" : "",
6177 			parameters.vertexColors ? "#define USE_COLOR" : "",
6178 
6179 			parameters.skinning ? "#define USE_SKINNING" : "",
6180 			parameters.useVertexTexture ? "#define BONE_TEXTURE" : "",
6181 			parameters.boneTextureWidth ? "#define N_BONE_PIXEL_X " + parameters.boneTextureWidth.toFixed( 1 ) : "",
6182 			parameters.boneTextureHeight ? "#define N_BONE_PIXEL_Y " + parameters.boneTextureHeight.toFixed( 1 ) : "",
6183 
6184 			parameters.morphTargets ? "#define USE_MORPHTARGETS" : "",
6185 			parameters.morphNormals ? "#define USE_MORPHNORMALS" : "",
6186 			parameters.perPixel ? "#define PHONG_PER_PIXEL" : "",
6187 			parameters.wrapAround ? "#define WRAP_AROUND" : "",
6188 			parameters.doubleSided ? "#define DOUBLE_SIDED" : "",
6189 			parameters.flipSided ? "#define FLIP_SIDED" : "",
6190 
6191 			parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "",
6192 			parameters.shadowMapSoft ? "#define SHADOWMAP_SOFT" : "",
6193 			parameters.shadowMapDebug ? "#define SHADOWMAP_DEBUG" : "",
6194 			parameters.shadowMapCascade ? "#define SHADOWMAP_CASCADE" : "",
6195 
6196 			parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "",
6197 
6198 			"uniform mat4 modelMatrix;",
6199 			"uniform mat4 modelViewMatrix;",
6200 			"uniform mat4 projectionMatrix;",
6201 			"uniform mat4 viewMatrix;",
6202 			"uniform mat3 normalMatrix;",
6203 			"uniform vec3 cameraPosition;",
6204 
6205 			"attribute vec3 position;",
6206 			"attribute vec3 normal;",
6207 			"attribute vec2 uv;",
6208 			"attribute vec2 uv2;",
6209 
6210 			"#ifdef USE_COLOR",
6211 
6212 				"attribute vec3 color;",
6213 
6214 			"#endif",
6215 
6216 			"#ifdef USE_MORPHTARGETS",
6217 
6218 				"attribute vec3 morphTarget0;",
6219 				"attribute vec3 morphTarget1;",
6220 				"attribute vec3 morphTarget2;",
6221 				"attribute vec3 morphTarget3;",
6222 
6223 				"#ifdef USE_MORPHNORMALS",
6224 
6225 					"attribute vec3 morphNormal0;",
6226 					"attribute vec3 morphNormal1;",
6227 					"attribute vec3 morphNormal2;",
6228 					"attribute vec3 morphNormal3;",
6229 
6230 				"#else",
6231 
6232 					"attribute vec3 morphTarget4;",
6233 					"attribute vec3 morphTarget5;",
6234 					"attribute vec3 morphTarget6;",
6235 					"attribute vec3 morphTarget7;",
6236 
6237 				"#endif",
6238 
6239 			"#endif",
6240 
6241 			"#ifdef USE_SKINNING",
6242 
6243 				"attribute vec4 skinIndex;",
6244 				"attribute vec4 skinWeight;",
6245 
6246 			"#endif",
6247 
6248 			""
6249 
6250 		].join("\n");
6251 
6252 		var prefix_fragment = [
6253 
6254 			"precision " + _precision + " float;",
6255 
6256 			( parameters.bumpMap || parameters.normalMap ) ? "#extension GL_OES_standard_derivatives : enable" : "",
6257 
6258 			customDefines,
6259 
6260 			"#define MAX_DIR_LIGHTS " + parameters.maxDirLights,
6261 			"#define MAX_POINT_LIGHTS " + parameters.maxPointLights,
6262 			"#define MAX_SPOT_LIGHTS " + parameters.maxSpotLights,
6263 			"#define MAX_HEMI_LIGHTS " + parameters.maxHemiLights,
6264 
6265 			"#define MAX_SHADOWS " + parameters.maxShadows,
6266 
6267 			parameters.alphaTest ? "#define ALPHATEST " + parameters.alphaTest: "",
6268 
6269 			_this.gammaInput ? "#define GAMMA_INPUT" : "",
6270 			_this.gammaOutput ? "#define GAMMA_OUTPUT" : "",
6271 			_this.physicallyBasedShading ? "#define PHYSICALLY_BASED_SHADING" : "",
6272 
6273 			( parameters.useFog && parameters.fog ) ? "#define USE_FOG" : "",
6274 			( parameters.useFog && parameters.fogExp ) ? "#define FOG_EXP2" : "",
6275 
6276 			parameters.map ? "#define USE_MAP" : "",
6277 			parameters.envMap ? "#define USE_ENVMAP" : "",
6278 			parameters.lightMap ? "#define USE_LIGHTMAP" : "",
6279 			parameters.bumpMap ? "#define USE_BUMPMAP" : "",
6280 			parameters.normalMap ? "#define USE_NORMALMAP" : "",
6281 			parameters.specularMap ? "#define USE_SPECULARMAP" : "",
6282 			parameters.vertexColors ? "#define USE_COLOR" : "",
6283 
6284 			parameters.metal ? "#define METAL" : "",
6285 			parameters.perPixel ? "#define PHONG_PER_PIXEL" : "",
6286 			parameters.wrapAround ? "#define WRAP_AROUND" : "",
6287 			parameters.doubleSided ? "#define DOUBLE_SIDED" : "",
6288 			parameters.flipSided ? "#define FLIP_SIDED" : "",
6289 
6290 			parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "",
6291 			parameters.shadowMapSoft ? "#define SHADOWMAP_SOFT" : "",
6292 			parameters.shadowMapDebug ? "#define SHADOWMAP_DEBUG" : "",
6293 			parameters.shadowMapCascade ? "#define SHADOWMAP_CASCADE" : "",
6294 
6295 			"uniform mat4 viewMatrix;",
6296 			"uniform vec3 cameraPosition;",
6297 			""
6298 
6299 		].join("\n");
6300 
6301 		var glFragmentShader = getShader( "fragment", prefix_fragment + fragmentShader );
6302 		var glVertexShader = getShader( "vertex", prefix_vertex + vertexShader );
6303 
6304 		_gl.attachShader( program, glVertexShader );
6305 		_gl.attachShader( program, glFragmentShader );
6306 
6307 		_gl.linkProgram( program );
6308 
6309 		if ( !_gl.getProgramParameter( program, _gl.LINK_STATUS ) ) {
6310 
6311 			console.error( "Could not initialise shader\n" + "VALIDATE_STATUS: " + _gl.getProgramParameter( program, _gl.VALIDATE_STATUS ) + ", gl error [" + _gl.getError() + "]" );
6312 
6313 		}
6314 
6315 		// clean up
6316 
6317 		_gl.deleteShader( glFragmentShader );
6318 		_gl.deleteShader( glVertexShader );
6319 
6320 		//console.log( prefix_fragment + fragmentShader );
6321 		//console.log( prefix_vertex + vertexShader );
6322 
6323 		program.uniforms = {};
6324 		program.attributes = {};
6325 
6326 		var identifiers, u, a, i;
6327 
6328 		// cache uniform locations
6329 
6330 		identifiers = [
6331 
6332 			'viewMatrix', 'modelViewMatrix', 'projectionMatrix', 'normalMatrix', 'modelMatrix', 'cameraPosition',
6333 			'morphTargetInfluences'
6334 
6335 		];
6336 
6337 		if ( parameters.useVertexTexture ) {
6338 
6339 			identifiers.push( 'boneTexture' );
6340 
6341 		} else {
6342 
6343 			identifiers.push( 'boneGlobalMatrices' );
6344 
6345 		}
6346 
6347 		for ( u in uniforms ) {
6348 
6349 			identifiers.push( u );
6350 
6351 		}
6352 
6353 		cacheUniformLocations( program, identifiers );
6354 
6355 		// cache attributes locations
6356 
6357 		identifiers = [
6358 
6359 			"position", "normal", "uv", "uv2", "tangent", "color",
6360 			"skinIndex", "skinWeight", "lineDistance"
6361 
6362 		];
6363 
6364 		for ( i = 0; i < parameters.maxMorphTargets; i ++ ) {
6365 
6366 			identifiers.push( "morphTarget" + i );
6367 
6368 		}
6369 
6370 		for ( i = 0; i < parameters.maxMorphNormals; i ++ ) {
6371 
6372 			identifiers.push( "morphNormal" + i );
6373 
6374 		}
6375 
6376 		for ( a in attributes ) {
6377 
6378 			identifiers.push( a );
6379 
6380 		}
6381 
6382 		cacheAttributeLocations( program, identifiers );
6383 
6384 		program.id = _programs_counter ++;
6385 
6386 		_programs.push( { program: program, code: code, usedTimes: 1 } );
6387 
6388 		_this.info.memory.programs = _programs.length;
6389 
6390 		return program;
6391 
6392 	};
6393 
6394 	// Shader parameters cache
6395 
6396 	function cacheUniformLocations ( program, identifiers ) {
6397 
6398 		var i, l, id;
6399 
6400 		for( i = 0, l = identifiers.length; i < l; i ++ ) {
6401 
6402 			id = identifiers[ i ];
6403 			program.uniforms[ id ] = _gl.getUniformLocation( program, id );
6404 
6405 		}
6406 
6407 	};
6408 
6409 	function cacheAttributeLocations ( program, identifiers ) {
6410 
6411 		var i, l, id;
6412 
6413 		for( i = 0, l = identifiers.length; i < l; i ++ ) {
6414 
6415 			id = identifiers[ i ];
6416 			program.attributes[ id ] = _gl.getAttribLocation( program, id );
6417 
6418 		}
6419 
6420 	};
6421 
6422 	function addLineNumbers ( string ) {
6423 
6424 		var chunks = string.split( "\n" );
6425 
6426 		for ( var i = 0, il = chunks.length; i < il; i ++ ) {
6427 
6428 			// Chrome reports shader errors on lines
6429 			// starting counting from 1
6430 
6431 			chunks[ i ] = ( i + 1 ) + ": " + chunks[ i ];
6432 
6433 		}
6434 
6435 		return chunks.join( "\n" );
6436 
6437 	};
6438 
6439 	function getShader ( type, string ) {
6440 
6441 		var shader;
6442 
6443 		if ( type === "fragment" ) {
6444 
6445 			shader = _gl.createShader( _gl.FRAGMENT_SHADER );
6446 
6447 		} else if ( type === "vertex" ) {
6448 
6449 			shader = _gl.createShader( _gl.VERTEX_SHADER );
6450 
6451 		}
6452 
6453 		_gl.shaderSource( shader, string );
6454 		_gl.compileShader( shader );
6455 
6456 		if ( !_gl.getShaderParameter( shader, _gl.COMPILE_STATUS ) ) {
6457 
6458 			console.error( _gl.getShaderInfoLog( shader ) );
6459 			console.error( addLineNumbers( string ) );
6460 			return null;
6461 
6462 		}
6463 
6464 		return shader;
6465 
6466 	};
6467 
6468 	// Textures
6469 
6470 
6471 	function isPowerOfTwo ( value ) {
6472 
6473 		return ( value & ( value - 1 ) ) === 0;
6474 
6475 	};
6476 
6477 	function setTextureParameters ( textureType, texture, isImagePowerOfTwo ) {
6478 
6479 		if ( isImagePowerOfTwo ) {
6480 
6481 			_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, paramThreeToGL( texture.wrapS ) );
6482 			_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, paramThreeToGL( texture.wrapT ) );
6483 
6484 			_gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, paramThreeToGL( texture.magFilter ) );
6485 			_gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, paramThreeToGL( texture.minFilter ) );
6486 
6487 		} else {
6488 
6489 			_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE );
6490 			_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE );
6491 
6492 			_gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) );
6493 			_gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) );
6494 
6495 		}
6496 
6497 		if ( _glExtensionTextureFilterAnisotropic && texture.type !== THREE.FloatType ) {
6498 
6499 			if ( texture.anisotropy > 1 || texture.__oldAnisotropy ) {
6500 
6501 				_gl.texParameterf( textureType, _glExtensionTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, _maxAnisotropy ) );
6502 				texture.__oldAnisotropy = texture.anisotropy;
6503 
6504 			}
6505 
6506 		}
6507 
6508 	};
6509 
6510 	this.setTexture = function ( texture, slot ) {
6511 
6512 		if ( texture.needsUpdate ) {
6513 
6514 			if ( ! texture.__webglInit ) {
6515 
6516 				texture.__webglInit = true;
6517 				texture.__webglTexture = _gl.createTexture();
6518 
6519 				_this.info.memory.textures ++;
6520 
6521 			}
6522 
6523 			_gl.activeTexture( _gl.TEXTURE0 + slot );
6524 			_gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture );
6525 
6526 			_gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );
6527 			_gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha );
6528 
6529 			var image = texture.image,
6530 			isImagePowerOfTwo = isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ),
6531 			glFormat = paramThreeToGL( texture.format ),
6532 			glType = paramThreeToGL( texture.type );
6533 
6534 			setTextureParameters( _gl.TEXTURE_2D, texture, isImagePowerOfTwo );
6535 
6536 			if ( texture instanceof THREE.CompressedTexture ) {
6537 
6538 				var mipmap, mipmaps = texture.mipmaps;
6539 
6540 				for( var i = 0, il = mipmaps.length; i < il; i ++ ) {
6541 
6542 					mipmap = mipmaps[ i ];
6543 					_gl.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data );
6544 
6545 				}
6546 
6547 			} else if ( texture instanceof THREE.DataTexture ) {
6548 
6549 				_gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data );
6550 
6551 			} else {
6552 
6553 				_gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, texture.image );
6554 
6555 			}
6556 
6557 			if ( texture.generateMipmaps && isImagePowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D );
6558 
6559 			texture.needsUpdate = false;
6560 
6561 			if ( texture.onUpdate ) texture.onUpdate();
6562 
6563 		} else {
6564 
6565 			_gl.activeTexture( _gl.TEXTURE0 + slot );
6566 			_gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture );
6567 
6568 		}
6569 
6570 	};
6571 
6572 	function clampToMaxSize ( image, maxSize ) {
6573 
6574 		if ( image.width <= maxSize && image.height <= maxSize ) {
6575 
6576 			return image;
6577 
6578 		}
6579 
6580 		// Warning: Scaling through the canvas will only work with images that use
6581 		// premultiplied alpha.
6582 
6583 		var maxDimension = Math.max( image.width, image.height );
6584 		var newWidth = Math.floor( image.width * maxSize / maxDimension );
6585 		var newHeight = Math.floor( image.height * maxSize / maxDimension );
6586 
6587 		var canvas = document.createElement( 'canvas' );
6588 		canvas.width = newWidth;
6589 		canvas.height = newHeight;
6590 
6591 		var ctx = canvas.getContext( "2d" );
6592 		ctx.drawImage( image, 0, 0, image.width, image.height, 0, 0, newWidth, newHeight );
6593 
6594 		return canvas;
6595 
6596 	}
6597 
6598 	function setCubeTexture ( texture, slot ) {
6599 
6600 		if ( texture.image.length === 6 ) {
6601 
6602 			if ( texture.needsUpdate ) {
6603 
6604 				if ( ! texture.image.__webglTextureCube ) {
6605 
6606 					texture.image.__webglTextureCube = _gl.createTexture();
6607 
6608 				}
6609 
6610 				_gl.activeTexture( _gl.TEXTURE0 + slot );
6611 				_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube );
6612 
6613 				_gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );
6614 
6615 				var isCompressed = texture instanceof THREE.CompressedTexture;
6616 
6617 				var cubeImage = [];
6618 
6619 				for ( var i = 0; i < 6; i ++ ) {
6620 
6621 					if ( _this.autoScaleCubemaps && ! isCompressed ) {
6622 
6623 						cubeImage[ i ] = clampToMaxSize( texture.image[ i ], _maxCubemapSize );
6624 
6625 					} else {
6626 
6627 						cubeImage[ i ] = texture.image[ i ];
6628 
6629 					}
6630 
6631 				}
6632 
6633 				var image = cubeImage[ 0 ],
6634 				isImagePowerOfTwo = isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ),
6635 				glFormat = paramThreeToGL( texture.format ),
6636 				glType = paramThreeToGL( texture.type );
6637 
6638 				setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isImagePowerOfTwo );
6639 
6640 				for ( var i = 0; i < 6; i ++ ) {
6641 
6642 					if ( isCompressed ) {
6643 
6644 						var mipmap, mipmaps = cubeImage[ i ].mipmaps;
6645 
6646 						for( var j = 0, jl = mipmaps.length; j < jl; j ++ ) {
6647 
6648 							mipmap = mipmaps[ j ];
6649 							_gl.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data );
6650 
6651 						}
6652 
6653 					} else {
6654 
6655 						_gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] );
6656 
6657 					}
6658 
6659 				}
6660 
6661 				if ( texture.generateMipmaps && isImagePowerOfTwo ) {
6662 
6663 					_gl.generateMipmap( _gl.TEXTURE_CUBE_MAP );
6664 
6665 				}
6666 
6667 				texture.needsUpdate = false;
6668 
6669 				if ( texture.onUpdate ) texture.onUpdate();
6670 
6671 			} else {
6672 
6673 				_gl.activeTexture( _gl.TEXTURE0 + slot );
6674 				_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube );
6675 
6676 			}
6677 
6678 		}
6679 
6680 	};
6681 
6682 	function setCubeTextureDynamic ( texture, slot ) {
6683 
6684 		_gl.activeTexture( _gl.TEXTURE0 + slot );
6685 		_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.__webglTexture );
6686 
6687 	};
6688 
6689 	// Render targets
6690 
6691 	function setupFrameBuffer ( framebuffer, renderTarget, textureTarget ) {
6692 
6693 		_gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
6694 		_gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureTarget, renderTarget.__webglTexture, 0 );
6695 
6696 	};
6697 
6698 	function setupRenderBuffer ( renderbuffer, renderTarget  ) {
6699 
6700 		_gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer );
6701 
6702 		if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) {
6703 
6704 			_gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height );
6705 			_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );
6706 
6707 		/* For some reason this is not working. Defaulting to RGBA4.
6708 		} else if( ! renderTarget.depthBuffer && renderTarget.stencilBuffer ) {
6709 
6710 			_gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.STENCIL_INDEX8, renderTarget.width, renderTarget.height );
6711 			_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );
6712 		*/
6713 		} else if( renderTarget.depthBuffer && renderTarget.stencilBuffer ) {
6714 
6715 			_gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height );
6716 			_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );
6717 
6718 		} else {
6719 
6720 			_gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height );
6721 
6722 		}
6723 
6724 	};
6725 
6726 	this.setRenderTarget = function ( renderTarget ) {
6727 
6728 		var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube );
6729 
6730 		if ( renderTarget && ! renderTarget.__webglFramebuffer ) {
6731 
6732 			if ( renderTarget.depthBuffer === undefined ) renderTarget.depthBuffer = true;
6733 			if ( renderTarget.stencilBuffer === undefined ) renderTarget.stencilBuffer = true;
6734 
6735 			renderTarget.__webglTexture = _gl.createTexture();
6736 
6737 			// Setup texture, create render and frame buffers
6738 
6739 			var isTargetPowerOfTwo = isPowerOfTwo( renderTarget.width ) && isPowerOfTwo( renderTarget.height ),
6740 				glFormat = paramThreeToGL( renderTarget.format ),
6741 				glType = paramThreeToGL( renderTarget.type );
6742 
6743 			if ( isCube ) {
6744 
6745 				renderTarget.__webglFramebuffer = [];
6746 				renderTarget.__webglRenderbuffer = [];
6747 
6748 				_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTarget.__webglTexture );
6749 				setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget, isTargetPowerOfTwo );
6750 
6751 				for ( var i = 0; i < 6; i ++ ) {
6752 
6753 					renderTarget.__webglFramebuffer[ i ] = _gl.createFramebuffer();
6754 					renderTarget.__webglRenderbuffer[ i ] = _gl.createRenderbuffer();
6755 
6756 					_gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null );
6757 
6758 					setupFrameBuffer( renderTarget.__webglFramebuffer[ i ], renderTarget, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i );
6759 					setupRenderBuffer( renderTarget.__webglRenderbuffer[ i ], renderTarget );
6760 
6761 				}
6762 
6763 				if ( isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP );
6764 
6765 			} else {
6766 
6767 				renderTarget.__webglFramebuffer = _gl.createFramebuffer();
6768 				renderTarget.__webglRenderbuffer = _gl.createRenderbuffer();
6769 
6770 				_gl.bindTexture( _gl.TEXTURE_2D, renderTarget.__webglTexture );
6771 				setTextureParameters( _gl.TEXTURE_2D, renderTarget, isTargetPowerOfTwo );
6772 
6773 				_gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null );
6774 
6775 				setupFrameBuffer( renderTarget.__webglFramebuffer, renderTarget, _gl.TEXTURE_2D );
6776 				setupRenderBuffer( renderTarget.__webglRenderbuffer, renderTarget );
6777 
6778 				if ( isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D );
6779 
6780 			}
6781 
6782 			// Release everything
6783 
6784 			if ( isCube ) {
6785 
6786 				_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, null );
6787 
6788 			} else {
6789 
6790 				_gl.bindTexture( _gl.TEXTURE_2D, null );
6791 
6792 			}
6793 
6794 			_gl.bindRenderbuffer( _gl.RENDERBUFFER, null );
6795 			_gl.bindFramebuffer( _gl.FRAMEBUFFER, null);
6796 
6797 		}
6798 
6799 		var framebuffer, width, height, vx, vy;
6800 
6801 		if ( renderTarget ) {
6802 
6803 			if ( isCube ) {
6804 
6805 				framebuffer = renderTarget.__webglFramebuffer[ renderTarget.activeCubeFace ];
6806 
6807 			} else {
6808 
6809 				framebuffer = renderTarget.__webglFramebuffer;
6810 
6811 			}
6812 
6813 			width = renderTarget.width;
6814 			height = renderTarget.height;
6815 
6816 			vx = 0;
6817 			vy = 0;
6818 
6819 		} else {
6820 
6821 			framebuffer = null;
6822 
6823 			width = _viewportWidth;
6824 			height = _viewportHeight;
6825 
6826 			vx = _viewportX;
6827 			vy = _viewportY;
6828 
6829 		}
6830 
6831 		if ( framebuffer !== _currentFramebuffer ) {
6832 
6833 			_gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
6834 			_gl.viewport( vx, vy, width, height );
6835 
6836 			_currentFramebuffer = framebuffer;
6837 
6838 		}
6839 
6840 		_currentWidth = width;
6841 		_currentHeight = height;
6842 
6843 	};
6844 
6845 	function updateRenderTargetMipmap ( renderTarget ) {
6846 
6847 		if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) {
6848 
6849 			_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTarget.__webglTexture );
6850 			_gl.generateMipmap( _gl.TEXTURE_CUBE_MAP );
6851 			_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, null );
6852 
6853 		} else {
6854 
6855 			_gl.bindTexture( _gl.TEXTURE_2D, renderTarget.__webglTexture );
6856 			_gl.generateMipmap( _gl.TEXTURE_2D );
6857 			_gl.bindTexture( _gl.TEXTURE_2D, null );
6858 
6859 		}
6860 
6861 	};
6862 
6863 	// Fallback filters for non-power-of-2 textures
6864 
6865 	function filterFallback ( f ) {
6866 
6867 		if ( f === THREE.NearestFilter || f === THREE.NearestMipMapNearestFilter || f === THREE.NearestMipMapLinearFilter ) {
6868 
6869 			return _gl.NEAREST;
6870 
6871 		}
6872 
6873 		return _gl.LINEAR;
6874 
6875 	};
6876 
6877 	// Map three.js constants to WebGL constants
6878 
6879 	function paramThreeToGL ( p ) {
6880 
6881 		if ( p === THREE.RepeatWrapping ) return _gl.REPEAT;
6882 		if ( p === THREE.ClampToEdgeWrapping ) return _gl.CLAMP_TO_EDGE;
6883 		if ( p === THREE.MirroredRepeatWrapping ) return _gl.MIRRORED_REPEAT;
6884 
6885 		if ( p === THREE.NearestFilter ) return _gl.NEAREST;
6886 		if ( p === THREE.NearestMipMapNearestFilter ) return _gl.NEAREST_MIPMAP_NEAREST;
6887 		if ( p === THREE.NearestMipMapLinearFilter ) return _gl.NEAREST_MIPMAP_LINEAR;
6888 
6889 		if ( p === THREE.LinearFilter ) return _gl.LINEAR;
6890 		if ( p === THREE.LinearMipMapNearestFilter ) return _gl.LINEAR_MIPMAP_NEAREST;
6891 		if ( p === THREE.LinearMipMapLinearFilter ) return _gl.LINEAR_MIPMAP_LINEAR;
6892 
6893 		if ( p === THREE.UnsignedByteType ) return _gl.UNSIGNED_BYTE;
6894 		if ( p === THREE.UnsignedShort4444Type ) return _gl.UNSIGNED_SHORT_4_4_4_4;
6895 		if ( p === THREE.UnsignedShort5551Type ) return _gl.UNSIGNED_SHORT_5_5_5_1;
6896 		if ( p === THREE.UnsignedShort565Type ) return _gl.UNSIGNED_SHORT_5_6_5;
6897 
6898 		if ( p === THREE.ByteType ) return _gl.BYTE;
6899 		if ( p === THREE.ShortType ) return _gl.SHORT;
6900 		if ( p === THREE.UnsignedShortType ) return _gl.UNSIGNED_SHORT;
6901 		if ( p === THREE.IntType ) return _gl.INT;
6902 		if ( p === THREE.UnsignedIntType ) return _gl.UNSIGNED_INT;
6903 		if ( p === THREE.FloatType ) return _gl.FLOAT;
6904 
6905 		if ( p === THREE.AlphaFormat ) return _gl.ALPHA;
6906 		if ( p === THREE.RGBFormat ) return _gl.RGB;
6907 		if ( p === THREE.RGBAFormat ) return _gl.RGBA;
6908 		if ( p === THREE.LuminanceFormat ) return _gl.LUMINANCE;
6909 		if ( p === THREE.LuminanceAlphaFormat ) return _gl.LUMINANCE_ALPHA;
6910 
6911 		if ( p === THREE.AddEquation ) return _gl.FUNC_ADD;
6912 		if ( p === THREE.SubtractEquation ) return _gl.FUNC_SUBTRACT;
6913 		if ( p === THREE.ReverseSubtractEquation ) return _gl.FUNC_REVERSE_SUBTRACT;
6914 
6915 		if ( p === THREE.ZeroFactor ) return _gl.ZERO;
6916 		if ( p === THREE.OneFactor ) return _gl.ONE;
6917 		if ( p === THREE.SrcColorFactor ) return _gl.SRC_COLOR;
6918 		if ( p === THREE.OneMinusSrcColorFactor ) return _gl.ONE_MINUS_SRC_COLOR;
6919 		if ( p === THREE.SrcAlphaFactor ) return _gl.SRC_ALPHA;
6920 		if ( p === THREE.OneMinusSrcAlphaFactor ) return _gl.ONE_MINUS_SRC_ALPHA;
6921 		if ( p === THREE.DstAlphaFactor ) return _gl.DST_ALPHA;
6922 		if ( p === THREE.OneMinusDstAlphaFactor ) return _gl.ONE_MINUS_DST_ALPHA;
6923 
6924 		if ( p === THREE.DstColorFactor ) return _gl.DST_COLOR;
6925 		if ( p === THREE.OneMinusDstColorFactor ) return _gl.ONE_MINUS_DST_COLOR;
6926 		if ( p === THREE.SrcAlphaSaturateFactor ) return _gl.SRC_ALPHA_SATURATE;
6927 
6928 		if ( _glExtensionCompressedTextureS3TC !== undefined ) {
6929 
6930 			if ( p === THREE.RGB_S3TC_DXT1_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGB_S3TC_DXT1_EXT;
6931 			if ( p === THREE.RGBA_S3TC_DXT1_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGBA_S3TC_DXT1_EXT;
6932 			if ( p === THREE.RGBA_S3TC_DXT3_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGBA_S3TC_DXT3_EXT;
6933 			if ( p === THREE.RGBA_S3TC_DXT5_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGBA_S3TC_DXT5_EXT;
6934 
6935 		}
6936 
6937 		return 0;
6938 
6939 	};
6940 
6941 	// Allocations
6942 
6943 	function allocateBones ( object ) {
6944 
6945 		if ( _supportsBoneTextures && object && object.useVertexTexture ) {
6946 
6947 			return 1024;
6948 
6949 		} else {
6950 
6951 			// default for when object is not specified
6952 			// ( for example when prebuilding shader
6953 			//   to be used with multiple objects )
6954 			//
6955 			// 	- leave some extra space for other uniforms
6956 			//  - limit here is ANGLE's 254 max uniform vectors
6957 			//    (up to 54 should be safe)
6958 
6959 			var nVertexUniforms = _gl.getParameter( _gl.MAX_VERTEX_UNIFORM_VECTORS );
6960 			var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 );
6961 
6962 			var maxBones = nVertexMatrices;
6963 
6964 			if ( object !== undefined && object instanceof THREE.SkinnedMesh ) {
6965 
6966 				maxBones = Math.min( object.bones.length, maxBones );
6967 
6968 				if ( maxBones < object.bones.length ) {
6969 
6970 					console.warn( "WebGLRenderer: too many bones - " + object.bones.length + ", this GPU supports just " + maxBones + " (try OpenGL instead of ANGLE)" );
6971 
6972 				}
6973 
6974 			}
6975 
6976 			return maxBones;
6977 
6978 		}
6979 
6980 	};
6981 
6982 	function allocateLights ( lights ) {
6983 
6984 		var l, ll, light, dirLights, pointLights, spotLights, hemiLights;
6985 
6986 		dirLights = pointLights = spotLights = hemiLights = 0;
6987 
6988 		for ( l = 0, ll = lights.length; l < ll; l ++ ) {
6989 
6990 			light = lights[ l ];
6991 
6992 			if ( light.onlyShadow ) continue;
6993 
6994 			if ( light instanceof THREE.DirectionalLight ) dirLights ++;
6995 			if ( light instanceof THREE.PointLight ) pointLights ++;
6996 			if ( light instanceof THREE.SpotLight ) spotLights ++;
6997 			if ( light instanceof THREE.HemisphereLight ) hemiLights ++;
6998 
6999 		}
7000 
7001 		return { 'directional' : dirLights, 'point' : pointLights, 'spot': spotLights, 'hemi': hemiLights };
7002 
7003 	};
7004 
7005 	function allocateShadows ( lights ) {
7006 
7007 		var l, ll, light, maxShadows = 0;
7008 
7009 		for ( l = 0, ll = lights.length; l < ll; l++ ) {
7010 
7011 			light = lights[ l ];
7012 
7013 			if ( ! light.castShadow ) continue;
7014 
7015 			if ( light instanceof THREE.SpotLight ) maxShadows ++;
7016 			if ( light instanceof THREE.DirectionalLight && ! light.shadowCascade ) maxShadows ++;
7017 
7018 		}
7019 
7020 		return maxShadows;
7021 
7022 	};
7023 
7024 	// Initialization
7025 
7026 	function initGL () {
7027 
7028 		try {
7029 
7030 			if ( ! ( _gl = _canvas.getContext( 'experimental-webgl', { alpha: _alpha, premultipliedAlpha: _premultipliedAlpha, antialias: _antialias, stencil: _stencil, preserveDrawingBuffer: _preserveDrawingBuffer } ) ) ) {
7031 
7032 				throw 'Error creating WebGL context.';
7033 
7034 			}
7035 
7036 		} catch ( error ) {
7037 
7038 			console.error( error );
7039 
7040 		}
7041 
7042 		_glExtensionTextureFloat = _gl.getExtension( 'OES_texture_float' );
7043 		_glExtensionStandardDerivatives = _gl.getExtension( 'OES_standard_derivatives' );
7044 
7045 		_glExtensionTextureFilterAnisotropic = _gl.getExtension( 'EXT_texture_filter_anisotropic' ) ||
7046 											   _gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) ||
7047 											   _gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' );
7048 
7049 
7050 		_glExtensionCompressedTextureS3TC = _gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) ||
7051 											_gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) ||
7052 											_gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' );
7053 
7054 		if ( ! _glExtensionTextureFloat ) {
7055 
7056 			console.log( 'THREE.WebGLRenderer: Float textures not supported.' );
7057 
7058 		}
7059 
7060 		if ( ! _glExtensionStandardDerivatives ) {
7061 
7062 			console.log( 'THREE.WebGLRenderer: Standard derivatives not supported.' );
7063 
7064 		}
7065 
7066 		if ( ! _glExtensionTextureFilterAnisotropic ) {
7067 
7068 			console.log( 'THREE.WebGLRenderer: Anisotropic texture filtering not supported.' );
7069 
7070 		}
7071 
7072 		if ( ! _glExtensionCompressedTextureS3TC ) {
7073 
7074 			console.log( 'THREE.WebGLRenderer: S3TC compressed textures not supported.' );
7075 
7076 		}
7077 
7078 	};
7079 
7080 	function setDefaultGLState () {
7081 
7082 		_gl.clearColor( 0, 0, 0, 1 );
7083 		_gl.clearDepth( 1 );
7084 		_gl.clearStencil( 0 );
7085 
7086 		_gl.enable( _gl.DEPTH_TEST );
7087 		_gl.depthFunc( _gl.LEQUAL );
7088 
7089 		_gl.frontFace( _gl.CCW );
7090 		_gl.cullFace( _gl.BACK );
7091 		_gl.enable( _gl.CULL_FACE );
7092 
7093 		_gl.enable( _gl.BLEND );
7094 		_gl.blendEquation( _gl.FUNC_ADD );
7095 		_gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA );
7096 
7097 		_gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
7098 
7099 	};
7100 
7101 	// default plugins (order is important)
7102 
7103 	this.shadowMapPlugin = new THREE.ShadowMapPlugin();
7104 	this.addPrePlugin( this.shadowMapPlugin );
7105 
7106 	this.addPostPlugin( new THREE.SpritePlugin() );
7107 	this.addPostPlugin( new THREE.LensFlarePlugin() );
7108 
7109 };
7110 

nike free rn new balance hombre baratas cinturones gucci ugg rebajas cinturon gucci ray ban baratas nike cortez peuterey mujer christian louboutin madrid mbt zapatos gafas ray ban baratas mbt ofertas air max blancas mbt barcelona nike air max 90 woolrich barcelona nike mujer botas ugg gafas de sol carrera aratas air max 2016 baratas oakley baratas nike air max 2016

mbt skor nike sverige louboutin skor hollister sverige polo ralph lauren skjorta woolrich jacka dam canada goose jacka woolrich jacka ray ban rea canada goose rea michael kors rea new balance skor ralph lauren skjorta new balance rea uggs sverige lacoste rea christian louboutin skor moncler jacka nike shox barbour jacka uggs rea