2
0
Эх сурвалжийг харах

Merge remote-tracking branch 'gero3/dev' into dev

Mr.doob 12 жил өмнө
parent
commit
1efe37f9fe

+ 3298 - 0
src/renderers/WebGLRenderer2.js

@@ -0,0 +1,3298 @@
+/**
+ * @author supereggbert / http://www.paulbrunt.co.uk/
+ * @author mrdoob / http://mrdoob.com/
+ * @author alteredq / http://alteredqualia.com/
+ * @author szimek / https://github.com/szimek/
+ * @author gero3 / https://github.com/gero3/
+ */
+
+THREE.WebGLRenderer2 = function ( parameters ) {
+
+	console.log( 'THREE.WebGLRenderer', THREE.REVISION );
+
+	parameters = parameters || {};
+
+	var info = {
+
+		memory: {
+
+			programs: 0,
+			geometries: 0,
+			textures: 0
+
+		},
+
+		render: {
+
+			calls: 0,
+			vertices: 0,
+			faces: 0,
+			points: 0
+
+		}
+
+	};
+
+
+	var renderer = new THREE.WebGLRenderer2.LowLevelRenderer(parameters);
+	var meshRenderer = new THREE.WebGLRenderer2.MeshRenderer(renderer, info);
+	var particleRenderer = new THREE.WebGLRenderer2.ParticleRenderer(renderer, info);
+	var lineRenderer = new THREE.WebGLRenderer2.LineRenderer(renderer, info);
+	var ribbonRenderer = new THREE.WebGLRenderer2.RibbonRenderer(renderer, info);
+	
+	var shaderBuilder = new THREE.WebGLRenderer2.ShaderBuilder(renderer, info);
+	
+	// clearing
+
+	this.autoClear = true;
+	this.autoClearColor = true;
+	this.autoClearDepth = true;
+	this.autoClearStencil = true;
+
+	// scene graph
+
+	this.sortObjects = true;
+
+	this.autoUpdateObjects = true;
+	this.autoUpdateScene = true;
+
+	// physically based shading
+
+	this.gammaInput = false;
+	this.gammaOutput = false;
+	this.physicallyBasedShading = false;
+
+	// shadow map
+
+	this.shadowMapEnabled = false;
+	this.shadowMapAutoUpdate = true;
+	this.shadowMapType = THREE.PCFShadowMap;
+	this.shadowMapCullFace = THREE.CullFaceFront;
+	this.shadowMapDebug = false;
+	this.shadowMapCascade = false;
+
+	// morphs
+
+	this.maxMorphTargets = 8;
+	this.maxMorphNormals = 4;
+
+	// flags
+
+	this.autoScaleCubemaps = true;
+
+	// custom render plugins
+
+	this.renderPluginsPre = [];
+	this.renderPluginsPost = [];
+
+	// info
+
+	this.info = info;
+
+
+	// internal properties
+
+	var _this = this,
+
+	// internal state cache
+
+	_currentProgram = null,
+	_currentFramebuffer = null,
+	_currentMaterialId = -1,
+	_currentGeometryGroupHash = null,
+	_currentCamera = null,
+	_geometryGroupCounter = 0,
+
+	_usedTextureUnits = 0,
+
+	// GL state 
+
+	_viewportX = 0,
+	_viewportY = 0,
+	_viewportWidth = 0,
+	_viewportHeight = 0,
+	_currentWidth = 0,
+	_currentHeight = 0,
+
+	_enabledAttributes = {},
+
+	// frustum
+
+	_frustum = new THREE.Frustum(),
+
+	 // camera matrices cache
+
+	_projScreenMatrix = new THREE.Matrix4(),
+	_projScreenMatrixPS = new THREE.Matrix4(),
+
+	_vector3 = new THREE.Vector3(),
+
+	// light arrays cache
+
+	_direction = new THREE.Vector3(),
+
+	_lightsNeedUpdate = true,
+
+	_lights = {
+
+		ambient: [ 0, 0, 0 ],
+		directional: { length: 0, colors: [], positions: [] },
+		point: { length: 0, colors: [], positions: [], distances: [] },
+		spot: { length: 0, colors: [], positions: [], distances: [], directions: [], anglesCos: [], exponents: [] },
+		hemi: { length: 0, skyColors: [], groundColors: [], positions: [] }
+
+	};
+
+	// initialize
+
+	this.context = renderer.getContext();
+	this.domElement = renderer.getDomElement();
+	this.getPrecision = renderer.getPrecision;
+
+	// low level API
+
+	this.getPrecision = renderer.getPrecision;
+	this.getContext = renderer.getContext;
+	this.supportsVertexTextures = renderer.supportsVertexTextures;
+	this.getMaxAnisotropy  = renderer.getMaxAnisotropy;
+	this.setSize = renderer.setSize;
+	this.setViewport = renderer.setViewport;
+	this.setScissor = renderer.setScissor;
+	this.enableScissorTest = renderer.enableScissorTest;
+	this.setDepthTest = renderer.setDepthTest;
+	this.setRenderTarget = renderer.setRenderTarget;
+
+	// Clearing
+
+	this.setClearColorHex = renderer.setClearColorHex;
+	this.setClearColor = renderer.setClearColor;
+	this.getClearColor = renderer.getClearColor;
+	this.getClearAlpha = renderer.getClearAlpha;
+	this.clear = renderer.clear;
+	this.clearTarget = renderer.clearTarget;
+
+	// Plugins
+
+	this.addPostPlugin = function ( plugin ) {
+
+		plugin.init( this );
+		this.renderPluginsPost.push( plugin );
+
+	};
+
+	this.addPrePlugin = function ( plugin ) {
+
+		plugin.init( this );
+		this.renderPluginsPre.push( plugin );
+
+	};
+
+	// Rendering
+
+	this.updateShadowMap = function ( scene, camera ) {
+
+		_currentProgram = null;
+		_currentGeometryGroupHash = -1;
+		_currentMaterialId = -1;
+		_lightsNeedUpdate = true;
+		
+		renderer.resetState();
+
+
+		this.shadowMapPlugin.update( scene, camera );
+
+	};
+
+	// Events
+
+	var onGeometryDispose = function ( event ) {
+
+		var geometry = event.target;
+
+		geometry.removeEventListener( 'dispose', onGeometryDispose );
+
+		deallocateGeometry( geometry );
+
+		_this.info.memory.geometries --;
+
+	};
+
+	var onTextureDispose = function ( event ) {
+
+		var texture = event.target;
+
+		texture.removeEventListener( 'dispose', onTextureDispose );
+
+		deallocateTexture( texture );
+
+		_this.info.memory.textures --;
+
+
+	};
+
+	var onRenderTargetDispose = function ( event ) {
+
+		var renderTarget = event.target;
+
+		renderTarget.removeEventListener( 'dispose', onRenderTargetDispose );
+
+		deallocateRenderTarget( renderTarget );
+
+		_this.info.memory.textures --;
+
+	};
+
+	var onMaterialDispose = function ( event ) {
+
+		var material = event.target;
+
+		material.removeEventListener( 'dispose', onMaterialDispose );
+
+		deallocateMaterial( material );
+
+	};
+
+	// Buffer deallocation
+
+	var deallocateGeometry = function ( geometry ) {
+
+		var m,ml;
+		geometry.__webglInit = undefined;
+
+		if ( geometry.__webglVertexBuffer !== undefined ) renderer.deleteBuffer( geometry.__webglVertexBuffer );
+		if ( geometry.__webglNormalBuffer !== undefined ) renderer.deleteBuffer( geometry.__webglNormalBuffer );
+		if ( geometry.__webglTangentBuffer !== undefined ) renderer.deleteBuffer( geometry.__webglTangentBuffer );
+		if ( geometry.__webglColorBuffer !== undefined ) renderer.deleteBuffer( geometry.__webglColorBuffer );
+		if ( geometry.__webglUVBuffer !== undefined ) renderer.deleteBuffer( geometry.__webglUVBuffer );
+		if ( geometry.__webglUV2Buffer !== undefined ) renderer.deleteBuffer( geometry.__webglUV2Buffer );
+
+		if ( geometry.__webglSkinIndicesBuffer !== undefined ) renderer.deleteBuffer( geometry.__webglSkinIndicesBuffer );
+		if ( geometry.__webglSkinWeightsBuffer !== undefined ) renderer.deleteBuffer( geometry.__webglSkinWeightsBuffer );
+
+		if ( geometry.__webglFaceBuffer !== undefined ) renderer.deleteBuffer( geometry.__webglFaceBuffer );
+		if ( geometry.__webglLineBuffer !== undefined ) renderer.deleteBuffer( geometry.__webglLineBuffer );
+
+		if ( geometry.__webglLineDistanceBuffer !== undefined ) renderer.deleteBuffer( geometry.__webglLineDistanceBuffer );
+
+		// geometry groups
+
+		if ( geometry.geometryGroups !== undefined ) {
+
+			for ( var g in geometry.geometryGroups ) {
+
+				var geometryGroup = geometry.geometryGroups[ g ];
+
+				if ( geometryGroup.numMorphTargets !== undefined ) {
+
+					for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) {
+
+						renderer.deleteBuffer( geometryGroup.__webglMorphTargetsBuffers[ m ] );
+
+					}
+
+				}
+
+				if ( geometryGroup.numMorphNormals !== undefined ) {
+
+					for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) {
+
+						renderer.deleteBuffer( geometryGroup.__webglMorphNormalsBuffers[ m ] );
+
+					}
+
+				}
+
+				deleteCustomAttributesBuffers( geometryGroup );
+
+			}
+
+		}
+
+		deleteCustomAttributesBuffers( geometry );
+
+	};
+
+	var deallocateTexture = function ( texture ) {
+
+		if ( texture.image && texture.image.__webglTextureCube ) {
+
+			// cube texture
+
+			renderer.deleteTexture( texture.image.__webglTextureCube );
+
+		} else {
+
+			// 2D texture
+
+			if ( ! texture.__webglInit ) return;
+
+			texture.__webglInit = false;
+			renderer.deleteTexture( texture.__webglTexture );
+
+		}
+
+	};
+
+	var deallocateRenderTarget = function ( renderTarget ) {
+
+		if ( !renderTarget || ! renderTarget.__webglTexture ) return;
+
+		renderer.deleteTexture( renderTarget.__webglTexture );
+
+		if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) {
+
+			for ( var i = 0; i < 6; i ++ ) {
+
+				renderer.deleteFramebuffer( renderTarget.__webglFramebuffer[ i ] );
+				renderer.deleteRenderbuffer( renderTarget.__webglRenderbuffer[ i ] );
+
+			}
+
+		} else {
+
+			renderer.deleteFramebuffer( renderTarget.__webglFramebuffer );
+			renderer.deleteRenderbuffer( renderTarget.__webglRenderbuffer );
+
+		}
+
+	};
+
+	var deallocateMaterial = function ( material ) {
+
+		var program = material.program;
+
+		if ( program === undefined ) return;
+
+		material.program = undefined;
+
+		// only deallocate GL program if this was the last use of shared program
+		// assumed there is only single copy of any program in the _programs list
+		// (that's how it's constructed)
+		
+		shaderBuilder.removeProgram(program)
+
+	};
+
+
+	function deleteCustomAttributesBuffers( geometry ) {
+
+		if ( geometry.__webglCustomAttributesList ) {
+
+			for ( var id in geometry.__webglCustomAttributesList ) {
+
+				renderer.deleteBuffer( geometry.__webglCustomAttributesList[ id ].buffer );
+
+			}
+
+		}
+
+	};
+
+	// Buffer initialization
+
+	function initCustomAttributes ( geometry, object ) {
+
+		var nvertices = geometry.vertices.length;
+
+		var material = object.material;
+
+		if ( material.attributes ) {
+
+			if ( geometry.__webglCustomAttributesList === undefined ) {
+
+				geometry.__webglCustomAttributesList = [];
+
+			}
+
+			for ( var a in material.attributes ) {
+
+				var attribute = material.attributes[ a ];
+
+				if ( !attribute.__webglInitialized || attribute.createUniqueBuffers ) {
+
+					attribute.__webglInitialized = true;
+
+					var size = 1;		// "f" and "i"
+
+					if ( attribute.type === "v2" ) size = 2;
+					else if ( attribute.type === "v3" ) size = 3;
+					else if ( attribute.type === "v4" ) size = 4;
+					else if ( attribute.type === "c"  ) size = 3;
+
+					attribute.size = size;
+
+					attribute.array = new Float32Array( nvertices * size );
+
+					attribute.buffer = renderer.createBuffer();
+					attribute.buffer.belongsToAttribute = a;
+
+					attribute.needsUpdate = true;
+
+				}
+
+				geometry.__webglCustomAttributesList.push( attribute );
+
+			}
+
+		}
+
+	};
+	
+	function getBufferMaterial( object, geometryGroup ) {
+
+		return object.material instanceof THREE.MeshFaceMaterial
+			? object.material.materials[ geometryGroup.materialIndex ]
+			: object.material;
+
+	};
+
+	//
+
+	function initDirectBuffers( geometry ) {
+
+		var a, attribute;
+
+		for ( a in geometry.attributes ) {
+
+			attribute = geometry.attributes[ a ];
+
+			attribute.buffer = renderer.createBuffer();
+			
+			if ( a === "index" ) {
+
+				renderer.setStaticIndexBuffer(attribute.buffer,attribute.array);
+
+			} else {
+
+				renderer.setStaticArrayBuffer(attribute.buffer,attribute.array);
+
+			}
+
+		}
+
+	};
+
+	// Buffer setting
+
+	function setDirectBuffers ( geometry, dispose ) {
+
+		var attributes = geometry.attributes;
+
+		var index = attributes[ "index" ];
+		var position = attributes[ "position" ];
+		var normal = attributes[ "normal" ];
+		var uv = attributes[ "uv" ];
+		var color = attributes[ "color" ];
+		var tangent = attributes[ "tangent" ];
+
+		if ( geometry.elementsNeedUpdate && index !== undefined ) {
+			
+			renderer.setDynamicIndexBuffer(	index.buffer, index.array);
+
+		}
+
+		if ( geometry.verticesNeedUpdate && position !== undefined ) {
+
+			renderer.setDynamicArrayBuffer( position.buffer,  position.array);
+
+		}
+
+		if ( geometry.normalsNeedUpdate && normal !== undefined ) {
+
+			renderer.setDynamicArrayBuffer( normal.buffer,  normal.array);
+
+		}
+
+		if ( geometry.uvsNeedUpdate && uv !== undefined ) {
+
+			renderer.setDynamicArrayBuffer( uv.buffer,  uv.array);
+
+		}
+
+		if ( geometry.colorsNeedUpdate && color !== undefined ) {
+
+			renderer.setDynamicArrayBuffer( color.buffer,  color.array);
+
+		}
+
+		if ( geometry.tangentsNeedUpdate && tangent !== undefined ) {
+
+			renderer.setDynamicArrayBuffer( tangent.buffer, tangent.array);
+
+		}
+
+		if ( dispose ) {
+
+			for ( var i in geometry.attributes ) {
+
+				delete geometry.attributes[ i ].array;
+
+			}
+
+		}
+
+	};
+
+	// Buffer rendering
+
+	this.renderBufferImmediate = function ( object, program, material ) {
+
+		if ( object.hasPositions && ! object.__webglVertexBuffer ) object.__webglVertexBuffer = renderer.createBuffer();
+		if ( object.hasNormals && ! object.__webglNormalBuffer ) object.__webglNormalBuffer = renderer.createBuffer();
+		if ( object.hasUvs && ! object.__webglUvBuffer ) object.__webglUvBuffer = renderer.createBuffer();
+		if ( object.hasColors && ! object.__webglColorBuffer ) object.__webglColorBuffer = renderer.createBuffer();
+
+		if ( object.hasPositions ) {
+
+			renderer.setDynamicArrayBuffer( object.__webglVertexBuffer, object.positionArray);
+			renderer.setFloatAttribute(program.attributes.position, object.__webglVertexBuffer, 3, 0);
+
+		}
+
+		if ( object.hasNormals ) {
+
+			if ( material.shading === THREE.FlatShading ) {
+
+				var nx, ny, nz,
+					nax, nbx, ncx, nay, nby, ncy, naz, nbz, ncz,
+					normalArray,
+					i, il = object.count * 3;
+
+				for( i = 0; i < il; i += 9 ) {
+
+					normalArray = object.normalArray;
+
+					nax  = normalArray[ i ];
+					nay  = normalArray[ i + 1 ];
+					naz  = normalArray[ i + 2 ];
+
+					nbx  = normalArray[ i + 3 ];
+					nby  = normalArray[ i + 4 ];
+					nbz  = normalArray[ i + 5 ];
+
+					ncx  = normalArray[ i + 6 ];
+					ncy  = normalArray[ i + 7 ];
+					ncz  = normalArray[ i + 8 ];
+
+					nx = ( nax + nbx + ncx ) / 3;
+					ny = ( nay + nby + ncy ) / 3;
+					nz = ( naz + nbz + ncz ) / 3;
+
+					normalArray[ i ] 	 = nx;
+					normalArray[ i + 1 ] = ny;
+					normalArray[ i + 2 ] = nz;
+
+					normalArray[ i + 3 ] = nx;
+					normalArray[ i + 4 ] = ny;
+					normalArray[ i + 5 ] = nz;
+
+					normalArray[ i + 6 ] = nx;
+					normalArray[ i + 7 ] = ny;
+					normalArray[ i + 8 ] = nz;
+
+				}
+
+			}
+			
+			renderer.setDynamicArrayBuffer( object.__webglNormalBuffer, object.normalArray);
+			renderer.setFloatAttribute(program.attributes.normal, object.__webglNormalBuffer, 3, 0);
+
+		}
+
+		if ( object.hasUvs && material.map ) {
+			
+			renderer.setDynamicArrayBuffer( object.__webglUvBuffer, object.uvArray);
+			renderer.setFloatAttribute(program.attributes.uv, object.__webglUvBuffer, 2, 0);
+
+		}
+
+		if ( object.hasColors && material.vertexColors !== THREE.NoColors ) {
+			
+			renderer.setDynamicArrayBuffer( object.__webglColorBuffer, object.colorArray);
+			renderer.setFloatAttribute(program.attributes.color, object.__webglColorBuffer, 3, 0);
+
+		}
+
+		renderer.drawTriangles(object.count );
+		object.count = 0;
+
+	};
+
+	this.renderBufferDirect = function ( camera, lights, fog, material, geometry, object ) {
+
+		if ( material.visible === false ) return;
+
+		var program, attributes, linewidth, primitives, a, attribute;
+
+		program = setProgram( camera, lights, fog, material, object );
+
+		attributes = program.attributes;
+
+		var updateBuffers = false,
+			wireframeBit = material.wireframe ? 1 : 0,
+			geometryHash = ( geometry.id * 0xffffff ) + ( program.id * 2 ) + wireframeBit;
+
+		if ( geometryHash !== _currentGeometryGroupHash ) {
+
+			_currentGeometryGroupHash = geometryHash;
+			updateBuffers = true;
+
+		}
+
+		if ( updateBuffers ) {
+
+			renderer.disableAttributes();
+
+		}
+
+		// render mesh
+
+		if ( object instanceof THREE.Mesh ) {
+
+			var index = geometry.attributes[ "index" ];
+
+			// indexed triangles
+
+			if ( index ) {
+
+				var offsets = geometry.offsets;
+
+				// if there is more than 1 chunk
+				// must set attribute pointers to use new offsets for each chunk
+				// even if geometry and materials didn't change
+
+				if ( offsets.length > 1 ) updateBuffers = true;
+
+				for ( var i = 0, il = offsets.length; i < il; i ++ ) {
+
+					var startIndex = offsets[ i ].index;
+
+					if ( updateBuffers ) {
+
+						// vertices
+
+						var position = geometry.attributes[ "position" ];
+						var positionSize = position.itemSize;
+						renderer.setFloatAttribute(attributes.position , position.buffer, positionSize, startIndex * positionSize * 4);
+
+						// normals
+
+						var normal = geometry.attributes[ "normal" ];
+
+						if ( attributes.normal >= 0 && normal ) {
+
+							var normalSize = normal.itemSize;
+							renderer.setFloatAttribute(attributes.normal , normal.buffer, normalSize, startIndex * normalSize * 4);
+
+						}
+
+						// uvs
+
+						var uv = geometry.attributes[ "uv" ];
+
+						if ( attributes.uv >= 0 && uv ) {
+
+							var uvSize = uv.itemSize;
+							renderer.setFloatAttribute(attributes.uv , uv.buffer, uvSize, startIndex * uvSize * 4);
+
+						}
+
+						// colors
+
+						var color = geometry.attributes[ "color" ];
+
+						if ( attributes.color >= 0 && color ) {
+
+							var colorSize = color.itemSize;
+							renderer.setFloatAttribute(attributes.color , color.buffer, colorSize, startIndex * colorSize * 4);
+
+						}
+
+						// tangents
+
+						var tangent = geometry.attributes[ "tangent" ];
+
+						if ( attributes.tangent >= 0 && tangent ) {
+
+							var tangentSize = tangent.itemSize;
+							renderer.setFloatAttribute(attributes.tangent , tangent.buffer, tangentSize, startIndex * tangentSize * 4);
+
+						}
+
+					}
+
+					// render indexed triangles
+					
+					renderer.drawTriangleElements(index.buffer, offsets[ i ].count, offsets[ i ].start * 2);
+
+					_this.info.render.calls ++;
+					_this.info.render.vertices += offsets[ i ].count; // not really true, here vertices can be shared
+					_this.info.render.faces += offsets[ i ].count / 3;
+
+				}
+
+			// non-indexed triangles
+
+			} else {
+
+				if ( updateBuffers ) {
+
+					// vertices
+
+					var position = geometry.attributes[ "position" ];
+					var positionSize = position.itemSize;
+					renderer.setFloatAttribute(attributes.position , position.buffer, positionSize, 0);
+
+					// normals
+
+					var normal = geometry.attributes[ "normal" ];
+
+					if ( attributes.normal >= 0 && normal ) {
+
+						var normalSize = normal.itemSize;
+						renderer.setFloatAttribute(attributes.normal , normal.buffer, normalSize, 0);
+
+					}
+
+					// uvs
+
+					var uv = geometry.attributes[ "uv" ];
+
+					if ( attributes.uv >= 0 && uv ) {
+
+						var uvSize = uv.itemSize;
+						renderer.setFloatAttribute(attributes.uv , uv.buffer, uvSize, 0);
+
+					}
+
+					// colors
+
+					var color = geometry.attributes[ "color" ];
+
+					if ( attributes.color >= 0 && color ) {
+
+						var colorSize = color.itemSize;
+						renderer.setFloatAttribute(attributes.color , color.buffer, colorSize, 0);
+
+					}
+
+					// tangents
+
+					var tangent = geometry.attributes[ "tangent" ];
+
+					if ( attributes.tangent >= 0 && tangent ) {
+
+						var tangentSize = tangent.itemSize;
+						renderer.setFloatAttribute(attributes.tangent , tangent.buffer, tangentSize, 0);
+
+					}
+
+				}
+
+				// render non-indexed triangles
+				renderer.drawTriangles( position.numItems / 3)
+
+				_this.info.render.calls ++;
+				_this.info.render.vertices += position.numItems / 3;
+				_this.info.render.faces += position.numItems / 3 / 3;
+
+			}
+
+		// render particles
+
+		} else if ( object instanceof THREE.ParticleSystem ) {
+
+			if ( updateBuffers ) {
+
+				// vertices
+
+				var position = geometry.attributes[ "position" ];
+				var positionSize = position.itemSize;
+				renderer.setFloatAttribute(attributes.position , position.buffer, positionSize, 0);
+
+				// colors
+
+				var color = geometry.attributes[ "color" ];
+
+				if ( attributes.color >= 0 && color ) {
+
+					var colorSize = color.itemSize;
+					renderer.setFloatAttribute(attributes.color , color.buffer, colorSize, 0);
+
+				}
+
+				// render particles
+				renderer.drawPoints(position.numItems / 3);
+
+				_this.info.render.calls ++;
+				_this.info.render.points += position.numItems / 3;
+
+			}
+
+		}
+
+	};
+
+	this.renderBuffer = function ( camera, lights, fog, material, geometryGroup, object ) {
+
+		if ( material.visible === false ) return;
+
+		var program, attributes, linewidth, primitives, a, attribute, i, il;
+
+		program = setProgram( camera, lights, fog, material, object );
+
+		attributes = program.attributes;
+
+		var updateBuffers = false,
+			wireframeBit = material.wireframe ? 1 : 0,
+			geometryGroupHash = ( geometryGroup.id * 0xffffff ) + ( program.id * 2 ) + wireframeBit;
+
+		if ( geometryGroupHash !== _currentGeometryGroupHash ) {
+
+			_currentGeometryGroupHash = geometryGroupHash;
+			updateBuffers = true;
+
+		}
+
+		if ( updateBuffers ) {
+
+			renderer.disableAttributes();
+
+		}
+
+		// vertices
+
+		if ( !material.morphTargets && attributes.position >= 0 ) {
+
+			if ( updateBuffers ) {
+				
+				renderer.setFloatAttribute(attributes.position , geometryGroup.__webglVertexBuffer, 3, 0);
+
+			}
+
+		} else {
+
+			if ( object.morphTargetBase ) {
+
+				setupMorphTargets( material, geometryGroup, object );
+
+			}
+
+		}
+
+
+		if ( updateBuffers ) {
+
+			// custom attributes
+
+			// Use the per-geometryGroup custom attribute arrays which are setup in initMeshBuffers
+
+			if ( geometryGroup.__webglCustomAttributesList ) {
+
+				for ( i = 0, il = geometryGroup.__webglCustomAttributesList.length; i < il; i ++ ) {
+
+					attribute = geometryGroup.__webglCustomAttributesList[ i ];
+
+					if ( attributes[ attribute.buffer.belongsToAttribute ] >= 0 ) {
+						
+						renderer.setFloatAttribute(attributes[ attribute.buffer.belongsToAttribute ] , attribute.buffer, attribute.size, 0);
+
+					}
+
+				}
+
+			}
+
+
+			// colors
+
+			if ( attributes.color >= 0 ) {
+
+				renderer.setFloatAttribute(attributes.color , geometryGroup.__webglColorBuffer,3, 0);
+
+			}
+
+			// normals
+
+			if ( attributes.normal >= 0 ) {
+
+				renderer.setFloatAttribute(attributes.normal, geometryGroup.__webglNormalBuffer, 3, 0);
+
+			}
+
+			// tangents
+
+			if ( attributes.tangent >= 0 ) {
+
+				renderer.setFloatAttribute(attributes.tangent, geometryGroup.__webglTangentBuffer, 4, 0);
+
+			}
+
+			// uvs
+
+			if ( attributes.uv >= 0 ) {
+
+				renderer.setFloatAttribute(attributes.uv, geometryGroup.__webglUVBuffer, 2, 0);
+
+			}
+
+			if ( attributes.uv2 >= 0 ) {
+
+				renderer.setFloatAttribute(attributes.uv2, geometryGroup.__webglUV2Buffer, 2, 0);
+
+			}
+
+			if ( material.skinning &&
+				 attributes.skinIndex >= 0 && attributes.skinWeight >= 0 ) {
+
+				renderer.setFloatAttribute(attributes.skinIndex, geometryGroup.__webglSkinIndicesBuffer, 4, 0);
+				renderer.setFloatAttribute(attributes.skinWeight, geometryGroup.__webglSkinWeightsBuffer, 4, 0);
+
+			}
+
+			// line distances
+
+			if ( attributes.lineDistance >= 0 ) {
+				
+				renderer.setFloatAttribute(attributes.lineDistance, geometryGroup.__webglLineDistanceBuffer, 1, 0);
+
+			}
+
+		}
+
+		// render mesh
+
+		if ( object instanceof THREE.Mesh ) {
+
+			// wireframe
+
+			if ( material.wireframe ) {
+
+				renderer.setLineWidth( material.wireframeLinewidth );
+				renderer.drawLineElements(geometryGroup.__webglLineBuffer,geometryGroup.__webglLineCount,0);
+
+			// triangles
+
+			} else {
+
+				renderer.drawTriangleElements( geometryGroup.__webglFaceBuffer, geometryGroup.__webglFaceCount, 0);
+
+			}
+
+			_this.info.render.calls ++;
+			_this.info.render.vertices += geometryGroup.__webglFaceCount;
+			_this.info.render.faces += geometryGroup.__webglFaceCount / 3;
+
+		// render lines
+
+		} else if ( object instanceof THREE.Line ) {
+
+			renderer.setLineWidth( material.linewidth );
+			
+			if (object.type === THREE.LineStrip) {
+				
+				renderer.drawLineStrip(geometryGroup.__webglLineCount);
+				
+			} else {
+				
+				renderer.drawLines(geometryGroup.__webglLineCount);
+				
+			}
+
+			_this.info.render.calls ++;
+
+		// render particles
+
+		} else if ( object instanceof THREE.ParticleSystem ) {
+			
+			renderer.drawPoints(geometryGroup.__webglParticleCount);
+
+			_this.info.render.calls ++;
+			_this.info.render.points += geometryGroup.__webglParticleCount;
+
+		// render ribbon
+
+		} else if ( object instanceof THREE.Ribbon ) {
+
+			renderer.drawTriangleStrip(geometryGroup.__webglVertexCount);
+
+			_this.info.render.calls ++;
+
+		}
+
+	};
+
+	function setupMorphTargets ( material, geometryGroup, object ) {
+
+		// set base
+
+		var attributes = material.program.attributes;
+
+		if ( object.morphTargetBase !== -1 && attributes.position >= 0 ) {
+
+			renderer.setFloatAttribute(attributes.position, geometryGroup.__webglMorphTargetsBuffers[ object.morphTargetBase ], 3, 0);
+
+		} else if ( attributes.position >= 0 ) {
+
+			renderer.setFloatAttribute(attributes.position, geometryGroup.__webglVertexBuffer, 3, 0);
+
+		}
+
+		if ( object.morphTargetForcedOrder.length ) {
+
+			// set forced order
+
+			var m = 0;
+			var order = object.morphTargetForcedOrder;
+			var influences = object.morphTargetInfluences;
+
+			while ( m < material.numSupportedMorphTargets && m < order.length ) {
+
+				if ( attributes[ "morphTarget" + m ] >= 0 ) {
+
+					renderer.setFloatAttribute(attributes[ "morphTarget" + m ], geometryGroup.__webglMorphTargetsBuffers[ order[ m ] ], 3, 0);
+
+				}
+
+				if ( attributes[ "morphNormal" + m ] >= 0 && material.morphNormals ) {
+
+					renderer.setFloatAttribute(attributes[ "morphNormal" + m ], geometryGroup.__webglMorphNormalsBuffers[ order[ m ] ], 3, 0);
+
+				}
+
+				object.__webglMorphTargetInfluences[ m ] = influences[ order[ m ] ];
+
+				m ++;
+			}
+
+		} else {
+
+			// find the most influencing
+
+			var influence, activeInfluenceIndices = [];
+			var influences = object.morphTargetInfluences;
+			var i, il = influences.length;
+
+			for ( i = 0; i < il; i ++ ) {
+
+				influence = influences[ i ];
+
+				if ( influence > 0 ) {
+
+					activeInfluenceIndices.push( [ influence, i ] );
+
+				}
+
+			}
+
+			if ( activeInfluenceIndices.length > material.numSupportedMorphTargets ) {
+
+				activeInfluenceIndices.sort( numericalSort );
+				activeInfluenceIndices.length = material.numSupportedMorphTargets;
+
+			} else if ( activeInfluenceIndices.length > material.numSupportedMorphNormals ) {
+
+				activeInfluenceIndices.sort( numericalSort );
+
+			} else if ( activeInfluenceIndices.length === 0 ) {
+
+				activeInfluenceIndices.push( [ 0, 0 ] );
+
+			};
+
+			var influenceIndex, m = 0;
+
+			while ( m < material.numSupportedMorphTargets ) {
+
+				if ( activeInfluenceIndices[ m ] ) {
+
+					influenceIndex = activeInfluenceIndices[ m ][ 1 ];
+
+					if ( attributes[ "morphTarget" + m ] >= 0 ) {
+						
+						renderer.setFloatAttribute(attributes[ "morphTarget" + m ], geometryGroup.__webglMorphTargetsBuffers[ influenceIndex ], 3, 0);
+
+					}
+
+					if ( attributes[ "morphNormal" + m ] >= 0 && material.morphNormals ) {
+						
+						renderer.setFloatAttribute(attributes[ "morphNormal" + m ], geometryGroup.__webglMorphNormalsBuffers[ influenceIndex ], 3, 0);
+
+					}
+
+					object.__webglMorphTargetInfluences[ m ] = influences[ influenceIndex ];
+
+				} else {
+
+					/*
+					_gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 );
+
+					if ( material.morphNormals ) {
+
+						_gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 );
+
+					}
+					*/
+
+					object.__webglMorphTargetInfluences[ m ] = 0;
+
+				}
+
+				m ++;
+
+			}
+
+		}
+
+		// load updated influences uniform
+
+		if ( material.program.uniforms.morphTargetInfluences !== null ) {
+
+			renderer.uniform1fv( material.program.uniforms.morphTargetInfluences, object.__webglMorphTargetInfluences );
+
+		}
+
+	};
+
+	// Sorting
+
+	function painterSortStable ( a, b ) {
+
+		if ( a.z !== b.z ) {
+
+			return b.z - a.z;
+
+		} else {
+
+			return b.id - a.id;
+
+		}
+
+	};
+
+	function numericalSort ( a, b ) {
+
+		return b[ 0 ] - a[ 0 ];
+
+	};
+
+
+	// Rendering
+
+	this.render = function ( scene, camera, renderTarget, forceClear ) {
+
+		if ( camera instanceof THREE.Camera === false ) {
+
+			console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' );
+			return;
+
+		}
+
+		var i, il,
+
+		webglObject, object,
+		renderList,
+
+		lights = scene.__lights,
+		fog = scene.fog;
+
+		// reset caching for this frame
+
+		_currentMaterialId = -1;
+		_lightsNeedUpdate = true;
+
+		// update scene graph
+
+		if ( this.autoUpdateScene ) scene.updateMatrixWorld();
+
+		// update camera matrices and frustum
+
+		if ( camera.parent === undefined ) camera.updateMatrixWorld();
+
+		camera.matrixWorldInverse.getInverse( camera.matrixWorld );
+
+		_projScreenMatrix.multiply( camera.projectionMatrix, camera.matrixWorldInverse );
+		_frustum.setFromMatrix( _projScreenMatrix );
+
+		// update WebGL objects
+
+		if ( this.autoUpdateObjects ) this.initWebGLObjects( scene );
+
+		// custom render plugins (pre pass)
+
+		renderPlugins( this.renderPluginsPre, scene, camera );
+
+		//
+
+		_this.info.render.calls = 0;
+		_this.info.render.vertices = 0;
+		_this.info.render.faces = 0;
+		_this.info.render.points = 0;
+
+		renderer.setRenderTarget( renderTarget );
+
+		if ( this.autoClear || forceClear ) {
+
+			this.clear( this.autoClearColor, this.autoClearDepth, this.autoClearStencil );
+
+		}
+
+		// set matrices for regular objects (frustum culled)
+
+		renderList = scene.__webglObjects;
+
+		for ( i = 0, il = renderList.length; i < il; i ++ ) {
+
+			webglObject = renderList[ i ];
+			object = webglObject.object;
+
+			webglObject.render = false;
+
+			if ( object.visible ) {
+
+				if ( ! ( object instanceof THREE.Mesh || object instanceof THREE.ParticleSystem ) || ! ( object.frustumCulled ) || _frustum.intersectsObject( object ) ) {
+
+					setupMatrices( object, camera );
+
+					unrollBufferMaterial( webglObject );
+
+					webglObject.render = true;
+
+					if ( this.sortObjects === true ) {
+
+						if ( object.renderDepth !== null ) {
+
+							webglObject.z = object.renderDepth;
+
+						} else {
+
+							_vector3.copy( object.matrixWorld.getPosition() );
+							_vector3.applyMatrix4(_projScreenMatrix);
+
+							webglObject.z = _vector3.z;
+
+						}
+
+						webglObject.id = object.id;
+
+					}
+
+				}
+
+			}
+
+		}
+
+		if ( this.sortObjects ) {
+
+			renderList.sort( painterSortStable );
+
+		}
+
+		// set matrices for immediate objects
+
+		renderList = scene.__webglObjectsImmediate;
+
+		for ( i = 0, il = renderList.length; i < il; i ++ ) {
+
+			webglObject = renderList[ i ];
+			object = webglObject.object;
+
+			if ( object.visible ) {
+
+				setupMatrices( object, camera );
+
+				unrollImmediateBufferMaterial( webglObject );
+
+			}
+
+		}
+
+		if ( scene.overrideMaterial ) {
+
+			var material = scene.overrideMaterial;
+
+			renderer.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst );
+			renderer.setDepthTest( material.depthTest );
+			renderer.setDepthWrite( material.depthWrite );
+			renderer.setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );
+
+			renderObjects( scene.__webglObjects, false, "", camera, lights, fog, true, material );
+			renderObjectsImmediate( scene.__webglObjectsImmediate, "", camera, lights, fog, false, material );
+
+		} else {
+
+			var material = null;
+
+			// opaque pass (front-to-back order)
+
+			renderer.setBlending( THREE.NoBlending );
+
+			renderObjects( scene.__webglObjects, true, "opaque", camera, lights, fog, false, material );
+			renderObjectsImmediate( scene.__webglObjectsImmediate, "opaque", camera, lights, fog, false, material );
+
+			// transparent pass (back-to-front order)
+
+			renderObjects( scene.__webglObjects, false, "transparent", camera, lights, fog, true, material );
+			renderObjectsImmediate( scene.__webglObjectsImmediate, "transparent", camera, lights, fog, true, material );
+
+		}
+
+		// custom render plugins (post pass)
+
+		renderPlugins( this.renderPluginsPost, scene, camera );
+
+
+		// Generate mipmap if we're using any kind of mipmap filtering
+
+		if ( renderTarget && renderTarget.generateMipmaps && renderTarget.minFilter !== THREE.NearestFilter && renderTarget.minFilter !== THREE.LinearFilter ) {
+
+			renderer.updateRenderTargetMipmap( renderTarget );
+
+		}
+
+		// Ensure depth buffer writing is enabled so it can be cleared on next render
+
+		renderer.setDepthTest( true );
+		renderer.setDepthWrite( true );
+
+		// _gl.finish();
+
+	};
+
+	function renderPlugins( plugins, scene, camera ) {
+
+		if ( ! plugins.length ) return;
+
+		for ( var i = 0, il = plugins.length; i < il; i ++ ) {
+
+			// reset state for plugin (to start from clean slate)
+
+			_currentProgram = null;
+			_currentCamera = null;
+			_currentGeometryGroupHash = -1;
+			_currentMaterialId = -1;
+			_lightsNeedUpdate = true;
+			renderer.resetState();
+
+			plugins[ i ].render( scene, camera, renderer.getCurrentWidth(), renderer.getCurrentWidth() );
+
+			// reset state after plugin (anything could have changed)
+
+			_currentProgram = null;
+			_currentCamera = null;
+			_currentGeometryGroupHash = -1;
+			_currentMaterialId = -1;
+			_lightsNeedUpdate = true;
+			renderer.resetState();
+
+		}
+
+	};
+
+	function renderObjects ( renderList, reverse, materialType, camera, lights, fog, useBlending, overrideMaterial ) {
+
+		var webglObject, object, buffer, material, start, end, delta;
+
+		if ( reverse ) {
+
+			start = renderList.length - 1;
+			end = -1;
+			delta = -1;
+
+		} else {
+
+			start = 0;
+			end = renderList.length;
+			delta = 1;
+		}
+
+		for ( var i = start; i !== end; i += delta ) {
+
+			webglObject = renderList[ i ];
+
+			if ( webglObject.render ) {
+
+				object = webglObject.object;
+				buffer = webglObject.buffer;
+
+				if ( overrideMaterial ) {
+
+					material = overrideMaterial;
+
+				} else {
+
+					material = webglObject[ materialType ];
+
+					if ( ! material ) continue;
+
+					if ( useBlending ) renderer.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst );
+
+					renderer.setDepthTest( material.depthTest );
+					renderer.setDepthWrite( material.depthWrite );
+					renderer.setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );
+
+				}
+
+				renderer.setMaterialFaces( material );
+
+				if ( buffer instanceof THREE.BufferGeometry ) {
+
+					_this.renderBufferDirect( camera, lights, fog, material, buffer, object );
+
+				} else {
+
+					_this.renderBuffer( camera, lights, fog, material, buffer, object );
+
+				}
+
+			}
+
+		}
+
+	};
+
+	function renderObjectsImmediate ( renderList, materialType, camera, lights, fog, useBlending, overrideMaterial ) {
+
+		var webglObject, object, material, program;
+
+		for ( var i = 0, il = renderList.length; i < il; i ++ ) {
+
+			webglObject = renderList[ i ];
+			object = webglObject.object;
+
+			if ( object.visible ) {
+
+				if ( overrideMaterial ) {
+
+					material = overrideMaterial;
+
+				} else {
+
+					material = webglObject[ materialType ];
+
+					if ( ! material ) continue;
+
+					if ( useBlending ) renderer.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst );
+
+					renderer.setDepthTest( material.depthTest );
+					renderer.setDepthWrite( material.depthWrite );
+					renderer.setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );
+
+				}
+
+				_this.renderImmediateObject( camera, lights, fog, material, object );
+
+			}
+
+		}
+
+	};
+
+	this.renderImmediateObject = function ( camera, lights, fog, material, object ) {
+
+		var program = setProgram( camera, lights, fog, material, object );
+
+		_currentGeometryGroupHash = -1;
+
+		renderer.setMaterialFaces( material );
+
+		if ( object.immediateRenderCallback ) {
+
+			object.immediateRenderCallback( program, renderer.getContext(), _frustum );
+
+		} else {
+
+			object.render( function( object ) { _this.renderBufferImmediate( object, program, material ); } );
+
+		}
+
+	};
+
+	function unrollImmediateBufferMaterial ( globject ) {
+
+		var object = globject.object,
+			material = object.material;
+
+		if ( material.transparent ) {
+
+			globject.transparent = material;
+			globject.opaque = null;
+
+		} else {
+
+			globject.opaque = material;
+			globject.transparent = null;
+
+		}
+
+	};
+
+	function unrollBufferMaterial ( globject ) {
+
+		var object = globject.object,
+			buffer = globject.buffer,
+			material, materialIndex, meshMaterial;
+
+		meshMaterial = object.material;
+
+		if ( meshMaterial instanceof THREE.MeshFaceMaterial ) {
+
+			materialIndex = buffer.materialIndex;
+
+			material = meshMaterial.materials[ materialIndex ];
+
+			if ( material.transparent ) {
+
+				globject.transparent = material;
+				globject.opaque = null;
+
+			} else {
+
+				globject.opaque = material;
+				globject.transparent = null;
+
+			}
+
+		} else {
+
+			material = meshMaterial;
+
+			if ( material ) {
+
+				if ( material.transparent ) {
+
+					globject.transparent = material;
+					globject.opaque = null;
+
+				} else {
+
+					globject.opaque = material;
+					globject.transparent = null;
+
+				}
+
+			}
+
+		}
+
+	};
+
+	// Geometry splitting
+
+	function sortFacesByMaterial ( geometry, material ) {
+
+		var f, fl, face, materialIndex, vertices,
+			groupHash, hash_map = {};
+
+		var numMorphTargets = geometry.morphTargets.length;
+		var numMorphNormals = geometry.morphNormals.length;
+
+		var usesFaceMaterial = material instanceof THREE.MeshFaceMaterial;
+
+		geometry.geometryGroups = {};
+
+		for ( f = 0, fl = geometry.faces.length; f < fl; f ++ ) {
+
+			face = geometry.faces[ f ];
+			materialIndex = usesFaceMaterial ? face.materialIndex : 0;
+
+			if ( hash_map[ materialIndex ] === undefined ) {
+
+				hash_map[ materialIndex ] = { 'hash': materialIndex, 'counter': 0 };
+
+			}
+
+			groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter;
+
+			if ( geometry.geometryGroups[ groupHash ] === undefined ) {
+
+				geometry.geometryGroups[ groupHash ] = { 'faces3': [], 'faces4': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets, 'numMorphNormals': numMorphNormals };
+
+			}
+
+			vertices = face instanceof THREE.Face3 ? 3 : 4;
+
+			if ( geometry.geometryGroups[ groupHash ].vertices + vertices > 65535 ) {
+
+				hash_map[ materialIndex ].counter += 1;
+				groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter;
+
+				if ( geometry.geometryGroups[ groupHash ] === undefined ) {
+
+					geometry.geometryGroups[ groupHash ] = { 'faces3': [], 'faces4': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets, 'numMorphNormals': numMorphNormals };
+
+				}
+
+			}
+
+			if ( face instanceof THREE.Face3 ) {
+
+				geometry.geometryGroups[ groupHash ].faces3.push( f );
+
+			} else {
+
+				geometry.geometryGroups[ groupHash ].faces4.push( f );
+
+			}
+
+			geometry.geometryGroups[ groupHash ].vertices += vertices;
+
+		}
+
+		geometry.geometryGroupsList = [];
+
+		for ( var g in geometry.geometryGroups ) {
+
+			geometry.geometryGroups[ g ].id = _geometryGroupCounter ++;
+
+			geometry.geometryGroupsList.push( geometry.geometryGroups[ g ] );
+
+		}
+
+	};
+
+	// Objects refresh
+
+	this.initWebGLObjects = function ( scene ) {
+
+		if ( !scene.__webglObjects ) {
+
+			scene.__webglObjects = [];
+			scene.__webglObjectsImmediate = [];
+			scene.__webglSprites = [];
+			scene.__webglFlares = [];
+
+		}
+
+		while ( scene.__objectsAdded.length ) {
+
+			addObject( scene.__objectsAdded[ 0 ], scene );
+			scene.__objectsAdded.splice( 0, 1 );
+
+		}
+
+		while ( scene.__objectsRemoved.length ) {
+
+			removeObject( scene.__objectsRemoved[ 0 ], scene );
+			scene.__objectsRemoved.splice( 0, 1 );
+
+		}
+
+		// update must be called after objects adding / removal
+
+		for ( var o = 0, ol = scene.__webglObjects.length; o < ol; o ++ ) {
+
+			updateObject( scene.__webglObjects[ o ].object );
+
+		}
+
+	};
+
+	// Objects adding
+
+	function addObject ( object, scene ) {
+
+		var g, geometry, material, geometryGroup;
+
+		if ( ! object.__webglInit ) {
+
+			object.__webglInit = true;
+
+			object._modelViewMatrix = new THREE.Matrix4();
+			object._normalMatrix = new THREE.Matrix3();
+
+			if ( object.geometry !== undefined && object.geometry.__webglInit === undefined ) {
+
+				object.geometry.__webglInit = true;
+				object.geometry.addEventListener( 'dispose', onGeometryDispose );
+
+			}
+
+			if ( object instanceof THREE.Mesh ) {
+
+				geometry = object.geometry;
+				material = object.material;
+
+				if ( geometry instanceof THREE.Geometry ) {
+
+					if ( geometry.geometryGroups === undefined ) {
+
+						sortFacesByMaterial( geometry, material );
+
+					}
+
+					// create separate VBOs per geometry chunk
+
+					for ( g in geometry.geometryGroups ) {
+
+						geometryGroup = geometry.geometryGroups[ g ];
+
+						// initialise VBO on the first access
+
+						if ( ! geometryGroup.__webglVertexBuffer ) {
+
+							meshRenderer.createBuffers( geometryGroup );
+							meshRenderer.initBuffers( geometryGroup, object );
+
+							geometry.verticesNeedUpdate = true;
+							geometry.morphTargetsNeedUpdate = true;
+							geometry.elementsNeedUpdate = true;
+							geometry.uvsNeedUpdate = true;
+							geometry.normalsNeedUpdate = true;
+							geometry.tangentsNeedUpdate = true;
+							geometry.colorsNeedUpdate = true;
+
+						}
+
+					}
+
+				} else if ( geometry instanceof THREE.BufferGeometry ) {
+
+					initDirectBuffers( geometry );
+
+				}
+
+			} else if ( object instanceof THREE.Ribbon ) {
+
+				geometry = object.geometry;
+
+				if ( ! geometry.__webglVertexBuffer ) {
+
+					ribbonRenderer.createBuffers( geometry );
+					ribbonRenderer.initBuffers( geometry, object );
+
+					geometry.verticesNeedUpdate = true;
+					geometry.colorsNeedUpdate = true;
+					geometry.normalsNeedUpdate = true;
+
+				}
+
+			} else if ( object instanceof THREE.Line ) {
+
+				geometry = object.geometry;
+
+				if ( ! geometry.__webglVertexBuffer ) {
+
+					lineRenderer.createBuffers( geometry );
+					lineRenderer.initBuffers( geometry, object );
+
+					geometry.verticesNeedUpdate = true;
+					geometry.colorsNeedUpdate = true;
+					geometry.lineDistancesNeedUpdate = true;
+
+				}
+
+			} else if ( object instanceof THREE.ParticleSystem ) {
+
+				geometry = object.geometry;
+
+				if ( ! geometry.__webglVertexBuffer ) {
+
+					if ( geometry instanceof THREE.Geometry ) {
+
+						particleRenderer.createBuffers( geometry );
+						particleRenderer.initBuffers( geometry, object );
+
+						geometry.verticesNeedUpdate = true;
+						geometry.colorsNeedUpdate = true;
+
+					} else if ( geometry instanceof THREE.BufferGeometry ) {
+
+						initDirectBuffers( geometry );
+
+					}
+
+
+				}
+
+			}
+
+		}
+
+		if ( ! object.__webglActive ) {
+
+			if ( object instanceof THREE.Mesh ) {
+
+				geometry = object.geometry;
+
+				if ( geometry instanceof THREE.BufferGeometry ) {
+
+					addBuffer( scene.__webglObjects, geometry, object );
+
+				} else if ( geometry instanceof THREE.Geometry ) {
+
+					for ( g in geometry.geometryGroups ) {
+
+						geometryGroup = geometry.geometryGroups[ g ];
+
+						addBuffer( scene.__webglObjects, geometryGroup, object );
+
+					}
+
+				}
+
+			} else if ( object instanceof THREE.Ribbon ||
+						object instanceof THREE.Line ||
+						object instanceof THREE.ParticleSystem ) {
+
+				geometry = object.geometry;
+				addBuffer( scene.__webglObjects, geometry, object );
+
+			} else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) {
+
+				addBufferImmediate( scene.__webglObjectsImmediate, object );
+
+			} else if ( object instanceof THREE.Sprite ) {
+
+				scene.__webglSprites.push( object );
+
+			} else if ( object instanceof THREE.LensFlare ) {
+
+				scene.__webglFlares.push( object );
+
+			}
+
+			object.__webglActive = true;
+
+		}
+
+	};
+
+	function addBuffer ( objlist, buffer, object ) {
+
+		objlist.push(
+			{
+				buffer: buffer,
+				object: object,
+				opaque: null,
+				transparent: null
+			}
+		);
+
+	};
+
+	function addBufferImmediate ( objlist, object ) {
+
+		objlist.push(
+			{
+				object: object,
+				opaque: null,
+				transparent: null
+			}
+		);
+
+	};
+
+	// Objects updates
+
+	function updateObject ( object ) {
+
+		var geometry = object.geometry,
+			geometryGroup, customAttributesDirty, material;
+
+		if ( object instanceof THREE.Mesh ) {
+
+			if ( geometry instanceof THREE.BufferGeometry ) {
+
+				if ( geometry.verticesNeedUpdate || geometry.elementsNeedUpdate ||
+					 geometry.uvsNeedUpdate || geometry.normalsNeedUpdate ||
+					 geometry.colorsNeedUpdate || geometry.tangentsNeedUpdate ) {
+
+					setDirectBuffers( geometry, !geometry.dynamic );
+
+				}
+
+				geometry.verticesNeedUpdate = false;
+				geometry.elementsNeedUpdate = false;
+				geometry.uvsNeedUpdate = false;
+				geometry.normalsNeedUpdate = false;
+				geometry.colorsNeedUpdate = false;
+				geometry.tangentsNeedUpdate = false;
+
+			} else {
+
+				// check all geometry groups
+
+				for( var i = 0, il = geometry.geometryGroupsList.length; i < il; i ++ ) {
+
+					geometryGroup = geometry.geometryGroupsList[ i ];
+
+					material = getBufferMaterial( object, geometryGroup );
+
+					if ( geometry.buffersNeedUpdate ) {
+
+						meshRenderer.initBuffers( geometryGroup, object );
+
+					}
+
+					customAttributesDirty = material.attributes && areCustomAttributesDirty( material );
+
+					if ( geometry.verticesNeedUpdate || geometry.morphTargetsNeedUpdate || geometry.elementsNeedUpdate ||
+						 geometry.uvsNeedUpdate || geometry.normalsNeedUpdate ||
+						 geometry.colorsNeedUpdate || geometry.tangentsNeedUpdate || customAttributesDirty ) {
+
+						meshRenderer.setBuffers( geometryGroup, object, !geometry.dynamic, material );
+
+					}
+
+				}
+
+				geometry.verticesNeedUpdate = false;
+				geometry.morphTargetsNeedUpdate = false;
+				geometry.elementsNeedUpdate = false;
+				geometry.uvsNeedUpdate = false;
+				geometry.normalsNeedUpdate = false;
+				geometry.colorsNeedUpdate = false;
+				geometry.tangentsNeedUpdate = false;
+
+				geometry.buffersNeedUpdate = false;
+
+				material.attributes && clearCustomAttributes( material );
+
+			}
+
+		} else if ( object instanceof THREE.Ribbon ) {
+
+			material = getBufferMaterial( object, geometry );
+
+			customAttributesDirty = material.attributes && areCustomAttributesDirty( material );
+
+			if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || geometry.normalsNeedUpdate || customAttributesDirty ) {
+
+				ribbonRenderer.setBuffers( geometry);
+
+			}
+
+			geometry.verticesNeedUpdate = false;
+			geometry.colorsNeedUpdate = false;
+			geometry.normalsNeedUpdate = false;
+
+			material.attributes && clearCustomAttributes( material );
+
+		} else if ( object instanceof THREE.Line ) {
+
+			material = getBufferMaterial( object, geometry );
+
+			customAttributesDirty = material.attributes && areCustomAttributesDirty( material );
+
+			if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || geometry.lineDistancesNeedUpdate || customAttributesDirty ) {
+
+				lineRenderer.setBuffers( geometry);
+
+			}
+
+			geometry.verticesNeedUpdate = false;
+			geometry.colorsNeedUpdate = false;
+			geometry.lineDistancesNeedUpdate = false;
+
+			material.attributes && clearCustomAttributes( material );
+
+		} else if ( object instanceof THREE.ParticleSystem ) {
+
+			if ( geometry instanceof THREE.BufferGeometry ) {
+
+				if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate ) {
+
+					setDirectBuffers( geometry, !geometry.dynamic );
+
+				}
+
+				geometry.verticesNeedUpdate = false;
+				geometry.colorsNeedUpdate = false;
+
+			} else {
+
+				material = getBufferMaterial( object, geometry );
+
+				customAttributesDirty = material.attributes && areCustomAttributesDirty( material );
+
+				if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || object.sortParticles || customAttributesDirty ) {
+
+					particleRenderer.setBuffers( geometry, object, _projScreenMatrix);
+
+				}
+
+				geometry.verticesNeedUpdate = false;
+				geometry.colorsNeedUpdate = false;
+
+				material.attributes && clearCustomAttributes( material );
+
+			}
+
+		}
+
+	};
+
+	// Objects updates - custom attributes check
+
+	function areCustomAttributesDirty ( material ) {
+
+		for ( var a in material.attributes ) {
+
+			if ( material.attributes[ a ].needsUpdate ) return true;
+
+		}
+
+		return false;
+
+	};
+
+	function clearCustomAttributes ( material ) {
+
+		for ( var a in material.attributes ) {
+
+			material.attributes[ a ].needsUpdate = false;
+
+		}
+
+	};
+
+	// Objects removal
+
+	function removeObject ( object, scene ) {
+
+		if ( object instanceof THREE.Mesh  ||
+			 object instanceof THREE.ParticleSystem ||
+			 object instanceof THREE.Ribbon ||
+			 object instanceof THREE.Line ) {
+
+			removeInstances( scene.__webglObjects, object );
+
+		} else if ( object instanceof THREE.Sprite ) {
+
+			removeInstancesDirect( scene.__webglSprites, object );
+
+		} else if ( object instanceof THREE.LensFlare ) {
+
+			removeInstancesDirect( scene.__webglFlares, object );
+
+		} else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) {
+
+			removeInstances( scene.__webglObjectsImmediate, object );
+
+		}
+
+		object.__webglActive = false;
+
+	};
+
+	function removeInstances ( objlist, object ) {
+
+		for ( var o = objlist.length - 1; o >= 0; o -- ) {
+
+			if ( objlist[ o ].object === object ) {
+
+				objlist.splice( o, 1 );
+
+			}
+
+		}
+
+	};
+
+	function removeInstancesDirect ( objlist, object ) {
+
+		for ( var o = objlist.length - 1; o >= 0; o -- ) {
+
+			if ( objlist[ o ] === object ) {
+
+				objlist.splice( o, 1 );
+
+			}
+
+		}
+
+	};
+
+	// Materials
+
+	this.initMaterial = function ( material, lights, fog, object ) {
+
+		material.addEventListener( 'dispose', onMaterialDispose );
+
+		var u, a, identifiers, i, parameters, maxLightCount, maxBones, maxShadows, shaderID;
+
+		if ( material instanceof THREE.MeshDepthMaterial ) {
+
+			shaderID = 'depth';
+
+		} else if ( material instanceof THREE.MeshNormalMaterial ) {
+
+			shaderID = 'normal';
+
+		} else if ( material instanceof THREE.MeshBasicMaterial ) {
+
+			shaderID = 'basic';
+
+		} else if ( material instanceof THREE.MeshLambertMaterial ) {
+
+			shaderID = 'lambert';
+
+		} else if ( material instanceof THREE.MeshPhongMaterial ) {
+
+			shaderID = 'phong';
+
+		} else if ( material instanceof THREE.LineBasicMaterial ) {
+
+			shaderID = 'basic';
+
+		} else if ( material instanceof THREE.LineDashedMaterial ) {
+
+			shaderID = 'dashed';
+
+		} else if ( material instanceof THREE.ParticleBasicMaterial ) {
+
+			shaderID = 'particle_basic';
+
+		}
+
+		if ( shaderID ) {
+
+			setMaterialShaders( material, THREE.ShaderLib[ shaderID ] );
+
+		}
+
+		// heuristics to create shader parameters according to lights in the scene
+		// (not to blow over maxLights budget)
+
+		maxLightCount = allocateLights( lights );
+
+		maxShadows = allocateShadows( lights );
+
+		maxBones = allocateBones( object );
+
+		parameters = {
+
+			map: !!material.map,
+			envMap: !!material.envMap,
+			lightMap: !!material.lightMap,
+			bumpMap: !!material.bumpMap,
+			normalMap: !!material.normalMap,
+			specularMap: !!material.specularMap,
+
+			vertexColors: material.vertexColors,
+
+			fog: fog,
+			useFog: material.fog,
+			fogExp: fog instanceof THREE.FogExp2,
+
+			sizeAttenuation: material.sizeAttenuation,
+
+			skinning: material.skinning,
+			maxBones: maxBones,
+			useVertexTexture: renderer.supportsBoneTextures && object && object.useVertexTexture,
+			boneTextureWidth: object && object.boneTextureWidth,
+			boneTextureHeight: object && object.boneTextureHeight,
+
+			morphTargets: material.morphTargets,
+			morphNormals: material.morphNormals,
+			maxMorphTargets: this.maxMorphTargets,
+			maxMorphNormals: this.maxMorphNormals,
+
+			maxDirLights: maxLightCount.directional,
+			maxPointLights: maxLightCount.point,
+			maxSpotLights: maxLightCount.spot,
+			maxHemiLights: maxLightCount.hemi,
+
+			maxShadows: maxShadows,
+			shadowMapEnabled: this.shadowMapEnabled && object.receiveShadow,
+			shadowMapType: this.shadowMapType,
+			shadowMapDebug: this.shadowMapDebug,
+			shadowMapCascade: this.shadowMapCascade,
+
+			alphaTest: material.alphaTest,
+			metal: material.metal,
+			perPixel: material.perPixel,
+			wrapAround: material.wrapAround,
+			doubleSided: material.side === THREE.DoubleSide,
+			flipSided: material.side === THREE.BackSide,
+			
+			gammaInput : this.gammaInput,
+			gammaOutput  : this.gammaOutput ,
+			physicallyBasedShading : this.physicallyBasedShading
+
+		};
+
+		material.program = shaderBuilder.buildProgram( shaderID, material.fragmentShader, material.vertexShader, material.uniforms, material.attributes, material.defines, parameters );
+
+		var attributes = material.program.attributes;
+
+		if ( material.morphTargets ) {
+
+			material.numSupportedMorphTargets = 0;
+
+			var id, base = "morphTarget";
+
+			for ( i = 0; i < this.maxMorphTargets; i ++ ) {
+
+				id = base + i;
+
+				if ( attributes[ id ] >= 0 ) {
+
+					material.numSupportedMorphTargets ++;
+
+				}
+
+			}
+
+		}
+
+		if ( material.morphNormals ) {
+
+			material.numSupportedMorphNormals = 0;
+
+			var id, base = "morphNormal";
+
+			for ( i = 0; i < this.maxMorphNormals; i ++ ) {
+
+				id = base + i;
+
+				if ( attributes[ id ] >= 0 ) {
+
+					material.numSupportedMorphNormals ++;
+
+				}
+
+			}
+
+		}
+
+		material.uniformsList = [];
+
+		for ( u in material.uniforms ) {
+
+			material.uniformsList.push( [ material.uniforms[ u ], u ] );
+
+		}
+
+	};
+
+	function setMaterialShaders( material, shaders ) {
+
+		material.uniforms = THREE.UniformsUtils.clone( shaders.uniforms );
+		material.vertexShader = shaders.vertexShader;
+		material.fragmentShader = shaders.fragmentShader;
+
+	};
+
+	function setProgram( camera, lights, fog, material, object ) {
+
+		_usedTextureUnits = 0;
+
+		if ( material.needsUpdate ) {
+
+			if ( material.program ) deallocateMaterial( material );
+
+			_this.initMaterial( material, lights, fog, object );
+			material.needsUpdate = false;
+
+		}
+
+		if ( material.morphTargets ) {
+
+			if ( ! object.__webglMorphTargetInfluences ) {
+
+				object.__webglMorphTargetInfluences = new Float32Array( _this.maxMorphTargets );
+
+			}
+
+		}
+
+		var refreshMaterial = false;
+
+		var program = material.program,
+			p_uniforms = program.uniforms,
+			m_uniforms = material.uniforms;
+
+		if ( program !== _currentProgram ) {
+
+			renderer.useProgram( program );
+			_currentProgram = program;
+
+			refreshMaterial = true;
+
+		}
+
+		if ( material.id !== _currentMaterialId ) {
+
+			_currentMaterialId = material.id;
+			refreshMaterial = true;
+
+		}
+
+		if ( refreshMaterial || camera !== _currentCamera ) {
+
+			renderer.uniformMatrix4fv( p_uniforms.projectionMatrix, camera.projectionMatrix.elements );
+
+			if ( camera !== _currentCamera ) _currentCamera = camera;
+
+		}
+
+		// skinning uniforms must be set even if material didn't change
+		// auto-setting of texture unit for bone texture must go before other textures
+		// not sure why, but otherwise weird things happen
+
+		if ( material.skinning ) {
+
+			if ( renderer.supportsBoneTextures && object.useVertexTexture ) {
+
+				if ( p_uniforms.boneTexture !== null ) {
+
+					var textureUnit = getTextureUnit();
+
+					renderer.uniform1i( p_uniforms.boneTexture, textureUnit );
+					renderer.setTexture( object.boneTexture, textureUnit );
+
+				}
+
+			} else {
+
+				if ( p_uniforms.boneGlobalMatrices !== null ) {
+
+					renderer.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, object.boneMatrices );
+
+				}
+
+			}
+
+		}
+
+		if ( refreshMaterial ) {
+
+			// refresh uniforms common to several materials
+
+			if ( fog && material.fog ) {
+
+				refreshUniformsFog( m_uniforms, fog );
+
+			}
+
+			if ( material instanceof THREE.MeshPhongMaterial ||
+				 material instanceof THREE.MeshLambertMaterial ||
+				 material.lights ) {
+
+				if ( _lightsNeedUpdate ) {
+
+					setupLights( program, lights );
+					_lightsNeedUpdate = false;
+
+				}
+
+				refreshUniformsLights( m_uniforms, _lights );
+
+			}
+
+			if ( material instanceof THREE.MeshBasicMaterial ||
+				 material instanceof THREE.MeshLambertMaterial ||
+				 material instanceof THREE.MeshPhongMaterial ) {
+
+				refreshUniformsCommon( m_uniforms, material );
+
+			}
+
+			// refresh single material specific uniforms
+
+			if ( material instanceof THREE.LineBasicMaterial ) {
+
+				refreshUniformsLine( m_uniforms, material );
+
+			} else if ( material instanceof THREE.LineDashedMaterial ) {
+
+				refreshUniformsLine( m_uniforms, material );
+				refreshUniformsDash( m_uniforms, material );
+
+			} else if ( material instanceof THREE.ParticleBasicMaterial ) {
+
+				refreshUniformsParticle( m_uniforms, material );
+
+			} else if ( material instanceof THREE.MeshPhongMaterial ) {
+
+				refreshUniformsPhong( m_uniforms, material );
+
+			} else if ( material instanceof THREE.MeshLambertMaterial ) {
+
+				refreshUniformsLambert( m_uniforms, material );
+
+			} else if ( material instanceof THREE.MeshDepthMaterial ) {
+
+				m_uniforms.mNear.value = camera.near;
+				m_uniforms.mFar.value = camera.far;
+				m_uniforms.opacity.value = material.opacity;
+
+			} else if ( material instanceof THREE.MeshNormalMaterial ) {
+
+				m_uniforms.opacity.value = material.opacity;
+
+			}
+
+			if ( object.receiveShadow && ! material._shadowPass ) {
+
+				refreshUniformsShadow( m_uniforms, lights );
+
+			}
+
+			// load common uniforms
+
+			loadUniformsGeneric( program, material.uniformsList );
+
+			// load material specific uniforms
+			// (shader material also gets them for the sake of genericity)
+
+			if ( material instanceof THREE.ShaderMaterial ||
+				 material instanceof THREE.MeshPhongMaterial ||
+				 material.envMap ) {
+
+				if ( p_uniforms.cameraPosition !== null ) {
+
+					var position = camera.matrixWorld.getPosition();
+					renderer.uniform3f( p_uniforms.cameraPosition, position.x, position.y, position.z );
+
+				}
+
+			}
+
+			if ( material instanceof THREE.MeshPhongMaterial ||
+				 material instanceof THREE.MeshLambertMaterial ||
+				 material instanceof THREE.ShaderMaterial ||
+				 material.skinning ) {
+
+				if ( p_uniforms.viewMatrix !== null ) {
+
+					renderer.uniformMatrix4fv( p_uniforms.viewMatrix, camera.matrixWorldInverse.elements );
+
+				}
+
+			}
+
+		}
+
+		loadUniformsMatrices( p_uniforms, object );
+
+		if ( p_uniforms.modelMatrix !== null ) {
+
+			renderer.uniformMatrix4fv( p_uniforms.modelMatrix, object.matrixWorld.elements );
+
+		}
+
+		return program;
+
+	};
+
+	// Uniforms (refresh uniforms objects)
+
+	function refreshUniformsCommon ( uniforms, material ) {
+
+		uniforms.opacity.value = material.opacity;
+
+		if ( _this.gammaInput ) {
+
+			uniforms.diffuse.value.copyGammaToLinear( material.color );
+
+		} else {
+
+			uniforms.diffuse.value = material.color;
+
+		}
+
+		uniforms.map.value = material.map;
+		uniforms.lightMap.value = material.lightMap;
+		uniforms.specularMap.value = material.specularMap;
+
+		if ( material.bumpMap ) {
+
+			uniforms.bumpMap.value = material.bumpMap;
+			uniforms.bumpScale.value = material.bumpScale;
+
+		}
+
+		if ( material.normalMap ) {
+
+			uniforms.normalMap.value = material.normalMap;
+			uniforms.normalScale.value.copy( material.normalScale );
+
+		}
+
+		// uv repeat and offset setting priorities
+		//	1. color map
+		//	2. specular map
+		//	3. normal map
+		//	4. bump map
+
+		var uvScaleMap;
+
+		if ( material.map ) {
+
+			uvScaleMap = material.map;
+
+		} else if ( material.specularMap ) {
+
+			uvScaleMap = material.specularMap;
+
+		} else if ( material.normalMap ) {
+
+			uvScaleMap = material.normalMap;
+
+		} else if ( material.bumpMap ) {
+
+			uvScaleMap = material.bumpMap;
+
+		}
+
+		if ( uvScaleMap !== undefined ) {
+
+			var offset = uvScaleMap.offset;
+			var repeat = uvScaleMap.repeat;
+
+			uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y );
+
+		}
+
+		uniforms.envMap.value = material.envMap;
+		uniforms.flipEnvMap.value = ( material.envMap instanceof THREE.WebGLRenderTargetCube ) ? 1 : -1;
+
+		if ( _this.gammaInput ) {
+
+			//uniforms.reflectivity.value = material.reflectivity * material.reflectivity;
+			uniforms.reflectivity.value = material.reflectivity;
+
+		} else {
+
+			uniforms.reflectivity.value = material.reflectivity;
+
+		}
+
+		uniforms.refractionRatio.value = material.refractionRatio;
+		uniforms.combine.value = material.combine;
+		uniforms.useRefract.value = material.envMap && material.envMap.mapping instanceof THREE.CubeRefractionMapping;
+
+	};
+
+	function refreshUniformsLine ( uniforms, material ) {
+
+		uniforms.diffuse.value = material.color;
+		uniforms.opacity.value = material.opacity;
+
+	};
+
+	function refreshUniformsDash ( uniforms, material ) {
+
+		uniforms.dashSize.value = material.dashSize;
+		uniforms.totalSize.value = material.dashSize + material.gapSize;
+		uniforms.scale.value = material.scale;
+
+	};
+
+	function refreshUniformsParticle ( uniforms, material ) {
+
+		uniforms.psColor.value = material.color;
+		uniforms.opacity.value = material.opacity;
+		uniforms.size.value = material.size;
+		uniforms.scale.value = renderer.getDomElement().height / 2.0; // TODO: Cache this.
+
+		uniforms.map.value = material.map;
+
+	};
+
+	function refreshUniformsFog ( uniforms, fog ) {
+
+		uniforms.fogColor.value = fog.color;
+
+		if ( fog instanceof THREE.Fog ) {
+
+			uniforms.fogNear.value = fog.near;
+			uniforms.fogFar.value = fog.far;
+
+		} else if ( fog instanceof THREE.FogExp2 ) {
+
+			uniforms.fogDensity.value = fog.density;
+
+		}
+
+	};
+
+	function refreshUniformsPhong ( uniforms, material ) {
+
+		uniforms.shininess.value = material.shininess;
+
+		if ( _this.gammaInput ) {
+
+			uniforms.ambient.value.copyGammaToLinear( material.ambient );
+			uniforms.emissive.value.copyGammaToLinear( material.emissive );
+			uniforms.specular.value.copyGammaToLinear( material.specular );
+
+		} else {
+
+			uniforms.ambient.value = material.ambient;
+			uniforms.emissive.value = material.emissive;
+			uniforms.specular.value = material.specular;
+
+		}
+
+		if ( material.wrapAround ) {
+
+			uniforms.wrapRGB.value.copy( material.wrapRGB );
+
+		}
+
+	};
+
+	function refreshUniformsLambert ( uniforms, material ) {
+
+		if ( _this.gammaInput ) {
+
+			uniforms.ambient.value.copyGammaToLinear( material.ambient );
+			uniforms.emissive.value.copyGammaToLinear( material.emissive );
+
+		} else {
+
+			uniforms.ambient.value = material.ambient;
+			uniforms.emissive.value = material.emissive;
+
+		}
+
+		if ( material.wrapAround ) {
+
+			uniforms.wrapRGB.value.copy( material.wrapRGB );
+
+		}
+
+	};
+
+	function refreshUniformsLights ( uniforms, lights ) {
+
+		uniforms.ambientLightColor.value = lights.ambient;
+
+		uniforms.directionalLightColor.value = lights.directional.colors;
+		uniforms.directionalLightDirection.value = lights.directional.positions;
+
+		uniforms.pointLightColor.value = lights.point.colors;
+		uniforms.pointLightPosition.value = lights.point.positions;
+		uniforms.pointLightDistance.value = lights.point.distances;
+
+		uniforms.spotLightColor.value = lights.spot.colors;
+		uniforms.spotLightPosition.value = lights.spot.positions;
+		uniforms.spotLightDistance.value = lights.spot.distances;
+		uniforms.spotLightDirection.value = lights.spot.directions;
+		uniforms.spotLightAngleCos.value = lights.spot.anglesCos;
+		uniforms.spotLightExponent.value = lights.spot.exponents;
+
+		uniforms.hemisphereLightSkyColor.value = lights.hemi.skyColors;
+		uniforms.hemisphereLightGroundColor.value = lights.hemi.groundColors;
+		uniforms.hemisphereLightDirection.value = lights.hemi.positions;
+
+	};
+
+	function refreshUniformsShadow ( uniforms, lights ) {
+
+		if ( uniforms.shadowMatrix ) {
+
+			var j = 0;
+
+			for ( var i = 0, il = lights.length; i < il; i ++ ) {
+
+				var light = lights[ i ];
+
+				if ( ! light.castShadow ) continue;
+
+				if ( light instanceof THREE.SpotLight || ( light instanceof THREE.DirectionalLight && ! light.shadowCascade ) ) {
+
+					uniforms.shadowMap.value[ j ] = light.shadowMap;
+					uniforms.shadowMapSize.value[ j ] = light.shadowMapSize;
+
+					uniforms.shadowMatrix.value[ j ] = light.shadowMatrix;
+
+					uniforms.shadowDarkness.value[ j ] = light.shadowDarkness;
+					uniforms.shadowBias.value[ j ] = light.shadowBias;
+
+					j ++;
+
+				}
+
+			}
+
+		}
+
+	};
+
+	// Uniforms (load to GPU)
+
+	function loadUniformsMatrices ( uniforms, object ) {
+
+		renderer.uniformMatrix4fv( uniforms.modelViewMatrix, object._modelViewMatrix.elements );
+
+		if ( uniforms.normalMatrix ) {
+
+			renderer.uniformMatrix3fv( uniforms.normalMatrix, object._normalMatrix.elements );
+
+		}
+
+	};
+
+	function getTextureUnit() {
+
+		var textureUnit = _usedTextureUnits;
+
+		if ( textureUnit >= renderer.maxTextures ) {
+
+			console.warn( "WebGLRenderer: trying to use " + textureUnit + " texture units while this GPU supports only " + renderer.maxTextures );
+
+		}
+
+		_usedTextureUnits += 1;
+
+		return textureUnit;
+
+	};
+
+	function loadUniformsGeneric ( program, uniforms ) {
+
+		var uniform, value, type, location, texture, textureUnit, i, il, j, jl, offset;
+
+		for ( j = 0, jl = uniforms.length; j < jl; j ++ ) {
+
+			location = program.uniforms[ uniforms[ j ][ 1 ] ];
+			if ( !location ) continue;
+
+			uniform = uniforms[ j ][ 0 ];
+
+			type = uniform.type;
+			value = uniform.value;
+
+			if ( type === "i" ) { // single integer
+
+				renderer.uniform1i( location, value );
+
+			} else if ( type === "f" ) { // single float
+
+				renderer.uniform1f( location, value );
+
+			} else if ( type === "v2" ) { // single THREE.Vector2
+
+				renderer.uniform2f( location, value.x, value.y );
+
+			} else if ( type === "v3" ) { // single THREE.Vector3
+
+				renderer.uniform3f( location, value.x, value.y, value.z );
+
+			} else if ( type === "v4" ) { // single THREE.Vector4
+
+				renderer.uniform4f( location, value.x, value.y, value.z, value.w );
+
+			} else if ( type === "c" ) { // single THREE.Color
+
+				renderer.uniform3f( location, value.r, value.g, value.b );
+
+			} else if ( type === "iv1" ) { // flat array of integers (JS or typed array)
+
+				renderer.uniform1iv( location, value );
+
+			} else if ( type === "iv" ) { // flat array of integers with 3 x N size (JS or typed array)
+
+				renderer.uniform3iv( location, value );
+
+			} else if ( type === "fv1" ) { // flat array of floats (JS or typed array)
+
+				renderer.uniform1fv( location, value );
+
+			} else if ( type === "fv" ) { // flat array of floats with 3 x N size (JS or typed array)
+
+				renderer.uniform3fv( location, value );
+
+			} else if ( type === "v2v" ) { // array of THREE.Vector2
+
+				if ( uniform._array === undefined ) {
+
+					uniform._array = new Float32Array( 2 * value.length );
+
+				}
+
+				for ( i = 0, il = value.length; i < il; i ++ ) {
+
+					offset = i * 2;
+
+					uniform._array[ offset ] 	 = value[ i ].x;
+					uniform._array[ offset + 1 ] = value[ i ].y;
+
+				}
+
+				renderer.uniform2fv( location, uniform._array );
+
+			} else if ( type === "v3v" ) { // array of THREE.Vector3
+
+				if ( uniform._array === undefined ) {
+
+					uniform._array = new Float32Array( 3 * value.length );
+
+				}
+
+				for ( i = 0, il = value.length; i < il; i ++ ) {
+
+					offset = i * 3;
+
+					uniform._array[ offset ] 	 = value[ i ].x;
+					uniform._array[ offset + 1 ] = value[ i ].y;
+					uniform._array[ offset + 2 ] = value[ i ].z;
+
+				}
+
+				renderer.uniform3fv( location, uniform._array );
+
+			} else if ( type === "v4v" ) { // array of THREE.Vector4
+
+				if ( uniform._array === undefined ) {
+
+					uniform._array = new Float32Array( 4 * value.length );
+
+				}
+
+				for ( i = 0, il = value.length; i < il; i ++ ) {
+
+					offset = i * 4;
+
+					uniform._array[ offset ] 	 = value[ i ].x;
+					uniform._array[ offset + 1 ] = value[ i ].y;
+					uniform._array[ offset + 2 ] = value[ i ].z;
+					uniform._array[ offset + 3 ] = value[ i ].w;
+
+				}
+
+				renderer.uniform4fv( location, uniform._array );
+
+			} else if ( type === "m4") { // single THREE.Matrix4
+
+				if ( uniform._array === undefined ) {
+
+					uniform._array = new Float32Array( 16 );
+
+				}
+
+				value.flattenToArray( uniform._array );
+				renderer.uniformMatrix4fv( location, uniform._array );
+
+			} else if ( type === "m4v" ) { // array of THREE.Matrix4
+
+				if ( uniform._array === undefined ) {
+
+					uniform._array = new Float32Array( 16 * value.length );
+
+				}
+
+				for ( i = 0, il = value.length; i < il; i ++ ) {
+
+					value[ i ].flattenToArrayOffset( uniform._array, i * 16 );
+
+				}
+
+				renderer.uniformMatrix4fv( location, uniform._array );
+
+			} else if ( type === "t" ) { // single THREE.Texture (2d or cube)
+
+				texture = value;
+				textureUnit = getTextureUnit();
+
+				renderer.uniform1i( location, textureUnit );
+
+				if ( !texture ) continue;
+
+				if ( texture.image instanceof Array && texture.image.length === 6 ) {
+
+					renderer.setCubeTexture( texture, textureUnit );
+
+				} else if ( texture instanceof THREE.WebGLRenderTargetCube ) {
+
+					renderer.setCubeTextureDynamic( texture, textureUnit );
+
+				} else {
+
+					renderer.setTexture( texture, textureUnit );
+
+				}
+
+			} else if ( type === "tv" ) { // array of THREE.Texture (2d)
+
+				if ( uniform._array === undefined ) {
+
+					uniform._array = [];
+
+				}
+
+				for( i = 0, il = uniform.value.length; i < il; i ++ ) {
+
+					uniform._array[ i ] = getTextureUnit();
+
+				}
+
+				renderer.uniform1iv( location, uniform._array );
+
+				for( i = 0, il = uniform.value.length; i < il; i ++ ) {
+
+					texture = uniform.value[ i ];
+					textureUnit = uniform._array[ i ];
+
+					if ( !texture ) continue;
+
+					renderer.setTexture( texture, textureUnit );
+
+				}
+
+			}
+
+		}
+
+	};
+
+	function setupMatrices ( object, camera ) {
+
+		object._modelViewMatrix.multiply( camera.matrixWorldInverse, object.matrixWorld );
+
+		object._normalMatrix.getInverse( object._modelViewMatrix );
+		object._normalMatrix.transpose();
+
+	};
+
+	//
+
+	function setColorGamma( array, offset, color, intensitySq ) {
+
+		array[ offset ]     = color.r * color.r * intensitySq;
+		array[ offset + 1 ] = color.g * color.g * intensitySq;
+		array[ offset + 2 ] = color.b * color.b * intensitySq;
+
+	};
+
+	function setColorLinear( array, offset, color, intensity ) {
+
+		array[ offset ]     = color.r * intensity;
+		array[ offset + 1 ] = color.g * intensity;
+		array[ offset + 2 ] = color.b * intensity;
+
+	};
+
+	function setupLights ( program, lights ) {
+
+		var l, ll, light, n,
+		r = 0, g = 0, b = 0,
+		color, skyColor, groundColor,
+		intensity,  intensitySq,
+		position,
+		distance,
+
+		zlights = _lights,
+
+		dirColors = zlights.directional.colors,
+		dirPositions = zlights.directional.positions,
+
+		pointColors = zlights.point.colors,
+		pointPositions = zlights.point.positions,
+		pointDistances = zlights.point.distances,
+
+		spotColors = zlights.spot.colors,
+		spotPositions = zlights.spot.positions,
+		spotDistances = zlights.spot.distances,
+		spotDirections = zlights.spot.directions,
+		spotAnglesCos = zlights.spot.anglesCos,
+		spotExponents = zlights.spot.exponents,
+
+		hemiSkyColors = zlights.hemi.skyColors,
+		hemiGroundColors = zlights.hemi.groundColors,
+		hemiPositions = zlights.hemi.positions,
+
+		dirLength = 0,
+		pointLength = 0,
+		spotLength = 0,
+		hemiLength = 0,
+
+		dirCount = 0,
+		pointCount = 0,
+		spotCount = 0,
+		hemiCount = 0,
+
+		dirOffset = 0,
+		pointOffset = 0,
+		spotOffset = 0,
+		hemiOffset = 0;
+
+		for ( l = 0, ll = lights.length; l < ll; l ++ ) {
+
+			light = lights[ l ];
+
+			if ( light.onlyShadow ) continue;
+
+			color = light.color;
+			intensity = light.intensity;
+			distance = light.distance;
+
+			if ( light instanceof THREE.AmbientLight ) {
+
+				if ( ! light.visible ) continue;
+
+				if ( _this.gammaInput ) {
+
+					r += color.r * color.r;
+					g += color.g * color.g;
+					b += color.b * color.b;
+
+				} else {
+
+					r += color.r;
+					g += color.g;
+					b += color.b;
+
+				}
+
+			} else if ( light instanceof THREE.DirectionalLight ) {
+
+				dirCount += 1;
+
+				if ( ! light.visible ) continue;
+
+				_direction.copy( light.matrixWorld.getPosition() );
+				_direction.subSelf( light.target.matrixWorld.getPosition() );
+				_direction.normalize();
+
+				// skip lights with undefined direction
+				// these create troubles in OpenGL (making pixel black)
+
+				if ( _direction.x === 0 && _direction.y === 0 && _direction.z === 0 ) continue;
+
+				dirOffset = dirLength * 3;
+
+				dirPositions[ dirOffset ]     = _direction.x;
+				dirPositions[ dirOffset + 1 ] = _direction.y;
+				dirPositions[ dirOffset + 2 ] = _direction.z;
+
+				if ( _this.gammaInput ) {
+
+					setColorGamma( dirColors, dirOffset, color, intensity * intensity );
+
+				} else {
+
+					setColorLinear( dirColors, dirOffset, color, intensity );
+
+				}
+
+				dirLength += 1;
+
+			} else if ( light instanceof THREE.PointLight ) {
+
+				pointCount += 1;
+
+				if ( ! light.visible ) continue;
+
+				pointOffset = pointLength * 3;
+
+				if ( _this.gammaInput ) {
+
+					setColorGamma( pointColors, pointOffset, color, intensity * intensity );
+
+				} else {
+
+					setColorLinear( pointColors, pointOffset, color, intensity );
+
+				}
+
+				position = light.matrixWorld.getPosition();
+
+				pointPositions[ pointOffset ]     = position.x;
+				pointPositions[ pointOffset + 1 ] = position.y;
+				pointPositions[ pointOffset + 2 ] = position.z;
+
+				pointDistances[ pointLength ] = distance;
+
+				pointLength += 1;
+
+			} else if ( light instanceof THREE.SpotLight ) {
+
+				spotCount += 1;
+
+				if ( ! light.visible ) continue;
+
+				spotOffset = spotLength * 3;
+
+				if ( _this.gammaInput ) {
+
+					setColorGamma( spotColors, spotOffset, color, intensity * intensity );
+
+				} else {
+
+					setColorLinear( spotColors, spotOffset, color, intensity );
+
+				}
+
+				position = light.matrixWorld.getPosition();
+
+				spotPositions[ spotOffset ]     = position.x;
+				spotPositions[ spotOffset + 1 ] = position.y;
+				spotPositions[ spotOffset + 2 ] = position.z;
+
+				spotDistances[ spotLength ] = distance;
+
+				_direction.copy( position );
+				_direction.subSelf( light.target.matrixWorld.getPosition() );
+				_direction.normalize();
+
+				spotDirections[ spotOffset ]     = _direction.x;
+				spotDirections[ spotOffset + 1 ] = _direction.y;
+				spotDirections[ spotOffset + 2 ] = _direction.z;
+
+				spotAnglesCos[ spotLength ] = Math.cos( light.angle );
+				spotExponents[ spotLength ] = light.exponent;
+
+				spotLength += 1;
+
+			} else if ( light instanceof THREE.HemisphereLight ) {
+
+				hemiCount += 1;
+
+				if ( ! light.visible ) continue;
+
+				_direction.copy( light.matrixWorld.getPosition() );
+				_direction.normalize();
+
+				// skip lights with undefined direction
+				// these create troubles in OpenGL (making pixel black)
+
+				if ( _direction.x === 0 && _direction.y === 0 && _direction.z === 0 ) continue;
+
+				hemiOffset = hemiLength * 3;
+
+				hemiPositions[ hemiOffset ]     = _direction.x;
+				hemiPositions[ hemiOffset + 1 ] = _direction.y;
+				hemiPositions[ hemiOffset + 2 ] = _direction.z;
+
+				skyColor = light.color;
+				groundColor = light.groundColor;
+
+				if ( _this.gammaInput ) {
+
+					intensitySq = intensity * intensity;
+
+					setColorGamma( hemiSkyColors, hemiOffset, skyColor, intensitySq );
+					setColorGamma( hemiGroundColors, hemiOffset, groundColor, intensitySq );
+
+				} else {
+
+					setColorLinear( hemiSkyColors, hemiOffset, skyColor, intensity );
+					setColorLinear( hemiGroundColors, hemiOffset, groundColor, intensity );
+
+				}
+
+				hemiLength += 1;
+
+			}
+
+		}
+
+		// null eventual remains from removed lights
+		// (this is to avoid if in shader)
+
+		for ( l = dirLength * 3, ll = Math.max( dirColors.length, dirCount * 3 ); l < ll; l ++ ) dirColors[ l ] = 0.0;
+		for ( l = pointLength * 3, ll = Math.max( pointColors.length, pointCount * 3 ); l < ll; l ++ ) pointColors[ l ] = 0.0;
+		for ( l = spotLength * 3, ll = Math.max( spotColors.length, spotCount * 3 ); l < ll; l ++ ) spotColors[ l ] = 0.0;
+		for ( l = hemiLength * 3, ll = Math.max( hemiSkyColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiSkyColors[ l ] = 0.0;
+		for ( l = hemiLength * 3, ll = Math.max( hemiGroundColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiGroundColors[ l ] = 0.0;
+
+		zlights.directional.length = dirLength;
+		zlights.point.length = pointLength;
+		zlights.spot.length = spotLength;
+		zlights.hemi.length = hemiLength;
+
+		zlights.ambient[ 0 ] = r;
+		zlights.ambient[ 1 ] = g;
+		zlights.ambient[ 2 ] = b;
+
+	};
+
+	// Allocations
+
+	function allocateBones ( object ) {
+
+		if ( renderer.supportsBoneTextures && object && object.useVertexTexture ) {
+
+			return 1024;
+
+		} else {
+
+			// default for when object is not specified
+			// ( for example when prebuilding shader
+			//   to be used with multiple objects )
+			//
+			// 	- leave some extra space for other uniforms
+			//  - limit here is ANGLE's 254 max uniform vectors
+			//    (up to 54 should be safe)
+
+			var nVertexUniforms = renderer.maxVertexUniformVectors
+			var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 );
+
+			var maxBones = nVertexMatrices;
+
+			if ( object !== undefined && object instanceof THREE.SkinnedMesh ) {
+
+				maxBones = Math.min( object.bones.length, maxBones );
+
+				if ( maxBones < object.bones.length ) {
+
+					console.warn( "WebGLRenderer: too many bones - " + object.bones.length + ", this GPU supports just " + maxBones + " (try OpenGL instead of ANGLE)" );
+
+				}
+
+			}
+
+			return maxBones;
+
+		}
+
+	};
+
+	function allocateLights ( lights ) {
+
+		var l, ll, light, dirLights, pointLights, spotLights, hemiLights;
+
+		dirLights = pointLights = spotLights = hemiLights = 0;
+
+		for ( l = 0, ll = lights.length; l < ll; l ++ ) {
+
+			light = lights[ l ];
+
+			if ( light.onlyShadow ) continue;
+
+			if ( light instanceof THREE.DirectionalLight ) dirLights ++;
+			if ( light instanceof THREE.PointLight ) pointLights ++;
+			if ( light instanceof THREE.SpotLight ) spotLights ++;
+			if ( light instanceof THREE.HemisphereLight ) hemiLights ++;
+
+		}
+
+		return { 'directional' : dirLights, 'point' : pointLights, 'spot': spotLights, 'hemi': hemiLights };
+
+	};
+
+	function allocateShadows ( lights ) {
+
+		var l, ll, light, maxShadows = 0;
+
+		for ( l = 0, ll = lights.length; l < ll; l++ ) {
+
+			light = lights[ l ];
+
+			if ( ! light.castShadow ) continue;
+
+			if ( light instanceof THREE.SpotLight ) maxShadows ++;
+			if ( light instanceof THREE.DirectionalLight && ! light.shadowCascade ) maxShadows ++;
+
+		}
+
+		return maxShadows;
+
+	};
+
+	// default plugins (order is important)
+
+	this.shadowMapPlugin = new THREE.ShadowMapPlugin();
+	this.addPrePlugin( this.shadowMapPlugin );
+
+	this.addPostPlugin( new THREE.SpritePlugin() );
+	this.addPostPlugin( new THREE.LensFlarePlugin() );
+
+};

+ 1526 - 0
src/renderers/webgl/LowLevelRenderer.js

@@ -0,0 +1,1526 @@
+
+/*global THREE:false */
+
+THREE.WebGLRenderer2.LowLevelRenderer = function(parameters){
+	
+	parameters = parameters || {};
+
+	var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElement( 'canvas' ),
+
+	_precision = parameters.precision !== undefined ? parameters.precision : 'highp',
+
+	_alpha = parameters.alpha !== undefined ? parameters.alpha : true,
+	_premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true,
+	_antialias = parameters.antialias !== undefined ? parameters.antialias : false,
+	_stencil = parameters.stencil !== undefined ? parameters.stencil : true,
+	_preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false,
+
+	_clearColor = parameters.clearColor !== undefined ? new THREE.Color( parameters.clearColor ) : new THREE.Color( 0x000000 ),
+	_clearAlpha = parameters.clearAlpha !== undefined ? parameters.clearAlpha : 0,
+	_autoScaleCubemaps = true;
+	
+	this.devicePixelRatio = parameters.devicePixelRatio !== undefined ? parameters.devicePixelRatio : window.devicePixelRatio !== undefined ? window.devicePixelRatio : 1;
+	
+	var _currentWidth = 0,
+		_currentHeight = 0;
+	
+	
+	var _gl;
+
+	var _glExtensionTextureFloat;
+	var _glExtensionStandardDerivatives;
+	var _glExtensionTextureFilterAnisotropic;
+	var _glExtensionCompressedTextureS3TC;
+	
+	initGL();
+
+	setDefaultGLState();	
+	
+	var _maxTextures = _gl.getParameter( _gl.MAX_TEXTURE_IMAGE_UNITS );
+	var _maxVertexTextures = _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS );
+	var _maxTextureSize = _gl.getParameter( _gl.MAX_TEXTURE_SIZE );
+	var _maxCubemapSize = _gl.getParameter( _gl.MAX_CUBE_MAP_TEXTURE_SIZE );
+
+	var _maxAnisotropy = _glExtensionTextureFilterAnisotropic ? _gl.getParameter( _glExtensionTextureFilterAnisotropic.MAX_TEXTURE_MAX_ANISOTROPY_EXT ) : 0;
+
+	var _supportsVertexTextures = ( _maxVertexTextures > 0 );
+	var _supportsBoneTextures = _supportsVertexTextures && _glExtensionTextureFloat;
+
+	var _compressedTextureFormats = _glExtensionCompressedTextureS3TC ? _gl.getParameter( _gl.COMPRESSED_TEXTURE_FORMATS ) : [];
+	
+		var _vertexShaderPrecisionHighpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.HIGH_FLOAT );
+	var _vertexShaderPrecisionMediumpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.MEDIUM_FLOAT );
+	var _vertexShaderPrecisionLowpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.LOW_FLOAT );
+
+	var _fragmentShaderPrecisionHighpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.HIGH_FLOAT );
+	var _fragmentShaderPrecisionMediumpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.MEDIUM_FLOAT );
+	var _fragmentShaderPrecisionLowpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.LOW_FLOAT );
+
+	var _vertexShaderPrecisionHighpInt = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.HIGH_INT );
+	var _vertexShaderPrecisionMediumpInt = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.MEDIUM_INT );
+	var _vertexShaderPrecisionLowpInt = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.LOW_INT );
+
+	var _fragmentShaderPrecisionHighpInt = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.HIGH_INT );
+	var _fragmentShaderPrecisionMediumpInt = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.MEDIUM_INT );
+	var _fragmentShaderPrecisionLowpInt = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.LOW_INT );
+
+	// clamp precision to maximum available
+
+	var highpAvailable = _vertexShaderPrecisionHighpFloat.precision > 0 && _fragmentShaderPrecisionHighpFloat.precision > 0;
+	var mediumpAvailable = _vertexShaderPrecisionMediumpFloat.precision > 0 && _fragmentShaderPrecisionMediumpFloat.precision > 0;
+
+	if ( _precision === "highp" && ! highpAvailable ) {
+
+		if ( mediumpAvailable ) {
+
+			_precision = "mediump";
+			console.warn( "WebGLRenderer: highp not supported, using mediump" );
+
+		} else {
+
+			_precision = "lowp";
+			console.warn( "WebGLRenderer: highp and mediump not supported, using lowp" );
+
+		}
+
+	}
+
+	if ( _precision === "mediump" && ! mediumpAvailable ) {
+
+		_precision = "lowp";
+		console.warn( "WebGLRenderer: mediump not supported, using lowp" );
+
+	}
+	
+	var _enabledAttributes = {}, 
+		_oldBlending, 
+		_oldBlendEquation,
+		_oldBlendSrc,
+		_oldBlendDst,
+		
+		_oldDoubleSided = -1,
+		_oldFlipSided = -1,
+
+		_oldDepthTest = -1,
+		_oldDepthWrite = -1,
+		
+		_oldLineWidth = -1,
+		
+		_viewportX = 0,
+		_viewportY = 0,
+		_viewportWidth = 0,
+		_viewportHeight = 0,
+			// GL state cache
+
+		_oldPolygonOffset = null,
+		_oldPolygonOffsetFactor = null,
+		_oldPolygonOffsetUnits = null,
+		_currentFramebuffer = null;
+	
+	function initGL () {
+
+		try {
+
+			if ( ! ( _gl = _canvas.getContext( 'experimental-webgl', { alpha: _alpha, premultipliedAlpha: _premultipliedAlpha, antialias: _antialias, stencil: _stencil, preserveDrawingBuffer: _preserveDrawingBuffer } ) ) ) {
+
+				throw 'Error creating WebGL context.';
+
+			}
+
+		} catch ( error ) {
+
+			console.error( error );
+
+		}
+
+		_glExtensionTextureFloat = _gl.getExtension( 'OES_texture_float' );
+		_glExtensionStandardDerivatives = _gl.getExtension( 'OES_standard_derivatives' );
+
+		_glExtensionTextureFilterAnisotropic = _gl.getExtension( 'EXT_texture_filter_anisotropic' ) ||
+											   _gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) ||
+											   _gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' );
+
+
+		_glExtensionCompressedTextureS3TC = _gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) ||
+											_gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) ||
+											_gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' );
+
+		if ( ! _glExtensionTextureFloat ) {
+
+			console.log( 'THREE.WebGLRenderer: Float textures not supported.' );
+
+		}
+
+		if ( ! _glExtensionStandardDerivatives ) {
+
+			console.log( 'THREE.WebGLRenderer: Standard derivatives not supported.' );
+
+		}
+
+		if ( ! _glExtensionTextureFilterAnisotropic ) {
+
+			console.log( 'THREE.WebGLRenderer: Anisotropic texture filtering not supported.' );
+
+		}
+
+		if ( ! _glExtensionCompressedTextureS3TC ) {
+
+			console.log( 'THREE.WebGLRenderer: S3TC compressed textures not supported.' );
+
+		}
+
+	};
+	
+	
+	function setDefaultGLState () {
+
+		_gl.clearColor( 0, 0, 0, 1 );
+		_gl.clearDepth( 1 );
+		_gl.clearStencil( 0 );
+
+		_gl.enable( _gl.DEPTH_TEST );
+		_gl.depthFunc( _gl.LEQUAL );
+
+		_gl.frontFace( _gl.CCW );
+		_gl.cullFace( _gl.BACK );
+		_gl.enable( _gl.CULL_FACE );
+
+		_gl.enable( _gl.BLEND );
+		_gl.blendEquation( _gl.FUNC_ADD );
+		_gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA );
+
+		_gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
+
+	};
+	
+	// Fallback filters for non-power-of-2 textures
+
+	function filterFallback ( f ) {
+
+		if ( f === THREE.NearestFilter || f === THREE.NearestMipMapNearestFilter || f === THREE.NearestMipMapLinearFilter ) {
+
+			return _gl.NEAREST;
+
+		}
+
+		return _gl.LINEAR;
+
+	};
+	
+	function getContext() {
+
+		return _gl;
+
+	};
+	
+	function getDomElement(){
+		
+		return _canvas;
+		
+	};
+
+	function getPrecision() {
+
+		return _precision;
+
+	};
+	
+	function getCurrentWidth(){
+		
+		return _currentWidth;
+		
+	};
+	
+	function getCurrentHeight(){
+		
+		return _currentHeight;
+		
+	};
+
+	function supportsVertexTextures() {
+
+		return _supportsVertexTextures;
+
+	};
+
+	function getMaxAnisotropy() {
+
+		return _maxAnisotropy;
+
+	};
+
+	function setSize( width, height ) {
+
+		_canvas.width = width;
+		_canvas.height = height;
+
+		setViewport( 0, 0, _canvas.width, _canvas.height );
+
+	};
+
+	function setViewport( x, y, width, height ) {
+
+		_viewportX = x !== undefined ? x : 0;
+		_viewportY = y !== undefined ? y : 0;
+
+		_viewportWidth = width !== undefined ? width : _canvas.width;
+		_viewportHeight = height !== undefined ? height : _canvas.height;
+
+		_gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight );
+
+	};
+
+	function setScissor( x, y, width, height ) {
+
+		_gl.scissor( x, y, width, height );
+
+	};
+
+	function enableScissorTest( enable ) {
+
+		enable ? _gl.enable( _gl.SCISSOR_TEST ) : _gl.disable( _gl.SCISSOR_TEST );
+
+	};
+
+	// Clearing
+
+	function setClearColorHex( hex, alpha ) {
+
+		_clearColor.setHex( hex );
+		_clearAlpha = alpha;
+
+		_gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
+
+	};
+
+	function setClearColor( color, alpha ) {
+
+		_clearColor.copy( color );
+		_clearAlpha = alpha;
+
+		_gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha );
+
+	};
+
+	function getClearColor() {
+
+		return _clearColor;
+
+	};
+
+	function getClearAlpha() {
+
+		return _clearAlpha;
+
+	};
+
+	function clear( color, depth, stencil ) {
+
+		var bits = 0;
+
+		if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT;
+		if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT;
+		if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT;
+
+		_gl.clear( bits );
+
+	};
+
+	function clearTarget( renderTarget, color, depth, stencil ) {
+
+		setRenderTarget( renderTarget );
+		clear( color, depth, stencil );
+
+	};
+	
+	function deleteBuffer(buffer){
+		_gl.deleteBuffer(buffer);
+	};
+	
+	function deleteTexture(texture){
+		_gl.deleteTexture( texture );
+	};
+	
+	function deleteFramebuffer(Framebuffer){
+		_gl.deleteFramebuffer(Framebuffer);
+	};
+	
+	function deleteRenderbuffer(RenderBuffer){
+		_gl.deleteRenderbuffer(RenderBuffer);
+	};
+	
+	function deleteProgram(RenderBuffer){
+		_gl.deleteProgram(RenderBuffer);
+	};
+	
+	function createBuffer(){
+		return _gl.createBuffer();
+	};
+	
+	function setStaticArrayBuffer(buffer,data){
+		
+		bindArrayBuffer( buffer );
+		_gl.bufferData( _gl.ARRAY_BUFFER, data, _gl.STATIC_DRAW );
+		
+	};
+	
+	function setStaticIndexBuffer(buffer,data){
+		
+		bindElementArrayBuffer( buffer );
+		_gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, data, _gl.STATIC_DRAW );
+		
+	};
+	
+	function setDynamicArrayBuffer(buffer,data){
+		
+		bindArrayBuffer( buffer );
+		_gl.bufferData( _gl.ARRAY_BUFFER, data, _gl.DYNAMIC_DRAW );
+		
+	};
+	
+	function setDynamicIndexBuffer(buffer,data){
+		
+		bindElementArrayBuffer( buffer );
+		_gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, data, _gl.DYNAMIC_DRAW );
+		
+	};
+	
+	function drawTriangles(count){
+		_gl.drawArrays( _gl.TRIANGLES, 0, count );
+	};
+	
+	function drawTriangleStrip(count){
+		_gl.drawArrays( _gl.TRIANGLE_STRIP, 0, count );
+	};
+	
+	function drawLines(count){
+		_gl.drawArrays( _gl.LINES, 0, count );
+	};
+	
+	function drawLineStrip(count){
+		_gl.drawArrays( _gl.LINE_STRIP, 0, count );
+	};
+	
+	function drawPoints(count){
+		_gl.drawArrays( _gl.POINTS, 0, count );
+	};
+	
+	function drawTriangleElements(buffer,count,offset){
+		bindElementArrayBuffer( buffer );
+		_gl.drawElements( _gl.TRIANGLES, count, _gl.UNSIGNED_SHORT, offset ); // 2 bytes per Uint16
+	};
+	
+	function drawLineElements(buffer,count,offset){
+		bindElementArrayBuffer(  buffer );
+		_gl.drawElements( _gl.LINES, count, _gl.UNSIGNED_SHORT, offset ); // 2 bytes per Uint16
+	};
+	
+	
+	var _boundBuffer;
+	function bindArrayBuffer(buffer){
+		if (_boundBuffer != buffer){
+			_gl.bindBuffer( _gl.ARRAY_BUFFER, buffer );
+			_boundBuffer = buffer;
+		}
+		
+	};
+	
+	function bindElementArrayBuffer(buffer){
+		
+		if (_boundBuffer != buffer){
+			_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, buffer );
+			_boundBuffer = buffer;
+		}
+		
+	};
+	
+	
+	function enableAttribute( attribute ) {
+
+		if ( ! _enabledAttributes[ attribute ] ) {
+
+			_gl.enableVertexAttribArray( attribute );
+			_enabledAttributes[ attribute ] = true;
+
+		}
+
+	};
+	
+	
+
+	function disableAttributes() {
+
+		for ( var attribute in _enabledAttributes ) {
+
+			if ( _enabledAttributes[ attribute ] ) {
+
+				_gl.disableVertexAttribArray( attribute );
+				_enabledAttributes[ attribute ] = false;
+
+			}
+
+		}
+
+	};
+	
+	function getAttribLocation( program, id ){
+		return _gl.getAttribLocation( program, id );
+	}
+	
+	function setFloatAttribute(index,buffer,size,offset){
+		
+		bindArrayBuffer( buffer );
+		enableAttribute( index );
+		_gl.vertexAttribPointer( index, size, _gl.FLOAT, false, 0, offset );
+		
+	};
+	
+	function getUniformLocation( program, id ){
+		
+		return _gl.getUniformLocation( program, id );
+		
+	}
+	
+	function uniform1i(uniform,value){
+		
+		_gl.uniform1i( uniform, value );
+		
+	};	
+	
+	function uniform1f(uniform,value){
+		
+		_gl.uniform1f( uniform, value );
+		
+	};
+	
+	function uniform2f(uniform,value1, value2){
+		
+		_gl.uniform2f( uniform, value1, value2 );
+		
+	};
+	
+	function uniform3f(uniform, value1, value2, value3){
+		
+		_gl.uniform3f( uniform, value1, value2, value3 );
+		
+	};
+	
+	function uniform4f(uniform, value1, value2, value3, value4){
+		
+		_gl.uniform4f( uniform, value1, value2, value3, value4);
+		
+	};
+	
+	function uniform1iv(uniform,value){
+		
+		_gl.uniform1iv( uniform, value );
+		
+	};
+	
+	function uniform2iv(uniform,value){
+		
+		_gl.uniform2iv( uniform, value );
+		
+	};
+	
+	function uniform3iv(uniform,value){
+		
+		_gl.uniform3iv( uniform, value );
+		
+	};
+	
+	function uniform1fv(uniform,value){
+		
+		_gl.uniform1fv( uniform, value );
+		
+	};
+	
+	function uniform2fv(uniform,value){
+		
+		_gl.uniform2fv( uniform, value );
+		
+	};
+	
+	function uniform3fv(uniform,value){
+		
+		_gl.uniform3fv( uniform, value );
+		
+	};
+	
+	function uniform4fv(uniform,value){
+		
+		_gl.uniform3fv( uniform, value );
+		
+	};
+	
+	function uniformMatrix3fv(location,value){
+		
+		_gl.uniformMatrix3fv( location, false, value );
+		
+	};
+	
+	function uniformMatrix4fv(location,value){
+		
+		_gl.uniformMatrix4fv( location, false, value );
+		
+	};
+	
+	function useProgram(program){
+	
+		_gl.useProgram( program );
+	
+	};
+	
+	function setFaceCulling( cullFace, frontFaceDirection ) {
+
+		if ( cullFace === THREE.CullFaceNone ) {
+
+			_gl.disable( _gl.CULL_FACE );
+
+		} else {
+
+			if ( frontFaceDirection === THREE.FrontFaceDirectionCW ) {
+
+				_gl.frontFace( _gl.CW );
+
+			} else {
+
+				_gl.frontFace( _gl.CCW );
+
+			}
+
+			if ( cullFace === THREE.CullFaceBack ) {
+
+				_gl.cullFace( _gl.BACK );
+
+			} else if ( cullFace === THREE.CullFaceFront ) {
+
+				_gl.cullFace( _gl.FRONT );
+
+			} else {
+
+				_gl.cullFace( _gl.FRONT_AND_BACK );
+
+			}
+
+			_gl.enable( _gl.CULL_FACE );
+
+		}
+
+	};
+	
+	function setMaterialFaces( material ) {
+
+		var doubleSided = material.side === THREE.DoubleSide;
+		var flipSided = material.side === THREE.BackSide;
+
+		if ( _oldDoubleSided !== doubleSided ) {
+
+			if ( doubleSided ) {
+
+				_gl.disable( _gl.CULL_FACE );
+
+			} else {
+
+				_gl.enable( _gl.CULL_FACE );
+
+			}
+
+			_oldDoubleSided = doubleSided;
+
+		}
+
+		if ( _oldFlipSided !== flipSided ) {
+
+			if ( flipSided ) {
+
+				_gl.frontFace( _gl.CW );
+
+			} else {
+
+				_gl.frontFace( _gl.CCW );
+
+			}
+
+			_oldFlipSided = flipSided;
+
+		}
+
+	};
+	
+	function setPolygonOffset ( polygonoffset, factor, units ) {
+
+		if ( _oldPolygonOffset !== polygonoffset ) {
+
+			if ( polygonoffset ) {
+
+				_gl.enable( _gl.POLYGON_OFFSET_FILL );
+
+			} else {
+
+				_gl.disable( _gl.POLYGON_OFFSET_FILL );
+
+			}
+
+			_oldPolygonOffset = polygonoffset;
+
+		}
+
+		if ( polygonoffset && ( _oldPolygonOffsetFactor !== factor || _oldPolygonOffsetUnits !== units ) ) {
+
+			_gl.polygonOffset( factor, units );
+
+			_oldPolygonOffsetFactor = factor;
+			_oldPolygonOffsetUnits = units;
+
+		}
+
+	};
+	
+	function setBlending( blending, blendEquation, blendSrc, blendDst ) {
+
+		if ( blending !== _oldBlending ) {
+
+			if ( blending === THREE.NoBlending ) {
+
+				_gl.disable( _gl.BLEND );
+
+			} else if ( blending === THREE.AdditiveBlending ) {
+
+				_gl.enable( _gl.BLEND );
+				_gl.blendEquation( _gl.FUNC_ADD );
+				_gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE );
+
+			} else if ( blending === THREE.SubtractiveBlending ) {
+
+				// TODO: Find blendFuncSeparate() combination
+				_gl.enable( _gl.BLEND );
+				_gl.blendEquation( _gl.FUNC_ADD );
+				_gl.blendFunc( _gl.ZERO, _gl.ONE_MINUS_SRC_COLOR );
+
+			} else if ( blending === THREE.MultiplyBlending ) {
+
+				// TODO: Find blendFuncSeparate() combination
+				_gl.enable( _gl.BLEND );
+				_gl.blendEquation( _gl.FUNC_ADD );
+				_gl.blendFunc( _gl.ZERO, _gl.SRC_COLOR );
+
+			} else if ( blending === THREE.CustomBlending ) {
+
+				_gl.enable( _gl.BLEND );
+
+			} else {
+
+				_gl.enable( _gl.BLEND );
+				_gl.blendEquationSeparate( _gl.FUNC_ADD, _gl.FUNC_ADD );
+				_gl.blendFuncSeparate( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA, _gl.ONE, _gl.ONE_MINUS_SRC_ALPHA );
+
+			}
+
+			_oldBlending = blending;
+
+		}
+
+		if ( blending === THREE.CustomBlending ) {
+
+			if ( blendEquation !== _oldBlendEquation ) {
+
+				_gl.blendEquation( paramThreeToGL( blendEquation ) );
+
+				_oldBlendEquation = blendEquation;
+
+			}
+
+			if ( blendSrc !== _oldBlendSrc || blendDst !== _oldBlendDst ) {
+
+				_gl.blendFunc( paramThreeToGL( blendSrc ), paramThreeToGL( blendDst ) );
+
+				_oldBlendSrc = blendSrc;
+				_oldBlendDst = blendDst;
+
+			}
+
+		} else {
+
+			_oldBlendEquation = null;
+			_oldBlendSrc = null;
+			_oldBlendDst = null;
+
+		}
+
+	};
+	
+	
+
+	function setDepthTest( depthTest ) {
+
+		if ( _oldDepthTest !== depthTest ) {
+
+			if ( depthTest ) {
+
+				_gl.enable( _gl.DEPTH_TEST );
+
+			} else {
+
+				_gl.disable( _gl.DEPTH_TEST );
+
+			}
+
+			_oldDepthTest = depthTest;
+
+		}
+
+	};
+
+	function setDepthWrite( depthWrite ) {
+
+		if ( _oldDepthWrite !== depthWrite ) {
+
+			_gl.depthMask( depthWrite );
+			_oldDepthWrite = depthWrite;
+
+		}
+
+	};
+	
+	
+	function setTexture( texture, slot ) {
+
+		if ( texture.needsUpdate ) {
+
+			if ( ! texture.__webglInit ) {
+
+				texture.__webglInit = true;
+
+				//texture.addEventListener( 'dispose', onTextureDispose );
+
+				texture.__webglTexture = _gl.createTexture();
+
+				//_this.info.memory.textures ++;
+
+			}
+
+			_gl.activeTexture( _gl.TEXTURE0 + slot );
+			_gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture );
+
+			_gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );
+			_gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha );
+			_gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment );
+
+			var image = texture.image,
+			isImagePowerOfTwo = isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ),
+			glFormat = paramThreeToGL( texture.format ),
+			glType = paramThreeToGL( texture.type );
+
+			setTextureParameters( _gl.TEXTURE_2D, texture, isImagePowerOfTwo );
+
+			var mipmap, mipmaps = texture.mipmaps;
+
+			if ( texture instanceof THREE.DataTexture ) {
+
+				// use manually created mipmaps if available
+				// if there are no manual mipmaps
+				// set 0 level mipmap and then use GL to generate other mipmap levels
+
+				if ( mipmaps.length > 0 && isImagePowerOfTwo ) {
+
+					for ( var i = 0, il = mipmaps.length; i < il; i ++ ) {
+
+						mipmap = mipmaps[ i ];
+						_gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );
+
+					}
+
+					texture.generateMipmaps = false;
+
+				} else {
+
+					_gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data );
+
+				}
+
+			} else if ( texture instanceof THREE.CompressedTexture ) {
+
+				// compressed textures can only use manually created mipmaps
+				// WebGL can't generate mipmaps for DDS textures
+
+				for( var i = 0, il = mipmaps.length; i < il; i ++ ) {
+
+					mipmap = mipmaps[ i ];
+					_gl.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data );
+
+				}
+
+			} else { // regular Texture (image, video, canvas)
+
+				// use manually created mipmaps if available
+				// if there are no manual mipmaps
+				// set 0 level mipmap and then use GL to generate other mipmap levels
+
+				if ( mipmaps.length > 0 && isImagePowerOfTwo ) {
+
+					for ( var i = 0, il = mipmaps.length; i < il; i ++ ) {
+
+						mipmap = mipmaps[ i ];
+						_gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap );
+
+					}
+
+					texture.generateMipmaps = false;
+
+				} else {
+
+					_gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, texture.image );
+
+				}
+
+			}
+
+			if ( texture.generateMipmaps && isImagePowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D );
+
+			texture.needsUpdate = false;
+
+			if ( texture.onUpdate ) texture.onUpdate();
+
+		} else {
+
+			_gl.activeTexture( _gl.TEXTURE0 + slot );
+			_gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture );
+
+		}
+
+	};
+	
+	function setCubeTexture ( texture, slot ) {
+
+		if ( texture.image.length === 6 ) {
+
+			if ( texture.needsUpdate ) {
+
+				if ( ! texture.image.__webglTextureCube ) {
+
+					texture.image.__webglTextureCube = _gl.createTexture();
+
+
+				}
+
+				_gl.activeTexture( _gl.TEXTURE0 + slot );
+				_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube );
+
+				_gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );
+
+				var isCompressed = texture instanceof THREE.CompressedTexture;
+
+				var cubeImage = [];
+
+				for ( var i = 0; i < 6; i ++ ) {
+
+					if ( _autoScaleCubemaps && ! isCompressed ) {
+
+						cubeImage[ i ] = clampToMaxSize( texture.image[ i ], _maxCubemapSize );
+
+					} else {
+
+						cubeImage[ i ] = texture.image[ i ];
+
+					}
+
+				}
+
+				var image = cubeImage[ 0 ],
+				isImagePowerOfTwo = isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ),
+				glFormat = paramThreeToGL( texture.format ),
+				glType = paramThreeToGL( texture.type );
+
+				setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isImagePowerOfTwo );
+
+				for ( var i = 0; i < 6; i ++ ) {
+
+					if ( isCompressed ) {
+
+						var mipmap, mipmaps = cubeImage[ i ].mipmaps;
+
+						for( var j = 0, jl = mipmaps.length; j < jl; j ++ ) {
+
+							mipmap = mipmaps[ j ];
+							_gl.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data );
+
+						}
+
+					} else {
+
+						_gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] );
+
+					}
+
+				}
+
+				if ( texture.generateMipmaps && isImagePowerOfTwo ) {
+
+					_gl.generateMipmap( _gl.TEXTURE_CUBE_MAP );
+
+				}
+
+				texture.needsUpdate = false;
+
+				if ( texture.onUpdate ) texture.onUpdate();
+
+			} else {
+
+				_gl.activeTexture( _gl.TEXTURE0 + slot );
+				_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube );
+
+			}
+
+		}
+
+	};
+	
+	// Textures
+
+
+	function isPowerOfTwo ( value ) {
+
+		return ( value & ( value - 1 ) ) === 0;
+
+	};
+	
+	
+	function setTextureParameters ( textureType, texture, isImagePowerOfTwo ) {
+
+		if ( isImagePowerOfTwo ) {
+
+			_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, paramThreeToGL( texture.wrapS ) );
+			_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, paramThreeToGL( texture.wrapT ) );
+
+			_gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, paramThreeToGL( texture.magFilter ) );
+			_gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, paramThreeToGL( texture.minFilter ) );
+
+		} else {
+
+			_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE );
+			_gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE );
+
+			_gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) );
+			_gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) );
+
+		}
+
+		if ( _glExtensionTextureFilterAnisotropic && texture.type !== THREE.FloatType ) {
+
+			if ( texture.anisotropy > 1 || texture.__oldAnisotropy ) {
+
+				_gl.texParameterf( textureType, _glExtensionTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, _maxAnisotropy ) );
+				texture.__oldAnisotropy = texture.anisotropy;
+
+			}
+
+		}
+
+	};
+	
+	
+	function setupFrameBuffer ( framebuffer, renderTarget, textureTarget ) {
+
+		_gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
+		_gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureTarget, renderTarget.__webglTexture, 0 );
+
+	};
+	
+	
+	function setupRenderBuffer ( renderbuffer, renderTarget  ) {
+
+		_gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer );
+
+		if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) {
+
+			_gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height );
+			_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );
+
+		/* For some reason this is not working. Defaulting to RGBA4.
+		} else if( ! renderTarget.depthBuffer && renderTarget.stencilBuffer ) {
+
+			_gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.STENCIL_INDEX8, renderTarget.width, renderTarget.height );
+			_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );
+		*/
+		} else if( renderTarget.depthBuffer && renderTarget.stencilBuffer ) {
+
+			_gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height );
+			_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );
+
+		} else {
+
+			_gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height );
+
+		}
+
+	};
+
+	
+	function setRenderTarget( renderTarget ) {
+
+		var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube );
+
+		if ( renderTarget && ! renderTarget.__webglFramebuffer ) {
+
+			if ( renderTarget.depthBuffer === undefined ) renderTarget.depthBuffer = true;
+			if ( renderTarget.stencilBuffer === undefined ) renderTarget.stencilBuffer = true;
+
+			//renderTarget.addEventListener( 'dispose', onRenderTargetDispose );
+
+			renderTarget.__webglTexture = _gl.createTexture();
+
+			//_this.info.memory.textures ++;
+
+			// Setup texture, create render and frame buffers
+
+			var isTargetPowerOfTwo = isPowerOfTwo( renderTarget.width ) && isPowerOfTwo( renderTarget.height ),
+				glFormat = paramThreeToGL( renderTarget.format ),
+				glType = paramThreeToGL( renderTarget.type );
+
+			if ( isCube ) {
+
+				renderTarget.__webglFramebuffer = [];
+				renderTarget.__webglRenderbuffer = [];
+
+				_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTarget.__webglTexture );
+				setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget, isTargetPowerOfTwo );
+
+				for ( var i = 0; i < 6; i ++ ) {
+
+					renderTarget.__webglFramebuffer[ i ] = _gl.createFramebuffer();
+					renderTarget.__webglRenderbuffer[ i ] = _gl.createRenderbuffer();
+
+					_gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null );
+
+					setupFrameBuffer( renderTarget.__webglFramebuffer[ i ], renderTarget, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i );
+					setupRenderBuffer( renderTarget.__webglRenderbuffer[ i ], renderTarget );
+
+				}
+
+				if ( isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP );
+
+			} else {
+
+				renderTarget.__webglFramebuffer = _gl.createFramebuffer();
+
+				if ( renderTarget.shareDepthFrom ) {
+
+					renderTarget.__webglRenderbuffer = renderTarget.shareDepthFrom.__webglRenderbuffer;
+
+				} else {
+
+					renderTarget.__webglRenderbuffer = _gl.createRenderbuffer();
+
+				}
+
+				_gl.bindTexture( _gl.TEXTURE_2D, renderTarget.__webglTexture );
+				setTextureParameters( _gl.TEXTURE_2D, renderTarget, isTargetPowerOfTwo );
+
+				_gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null );
+
+				setupFrameBuffer( renderTarget.__webglFramebuffer, renderTarget, _gl.TEXTURE_2D );
+
+				if ( renderTarget.shareDepthFrom ) {
+
+					if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) {
+
+						_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderTarget.__webglRenderbuffer );
+
+					} else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) {
+
+						_gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderTarget.__webglRenderbuffer );
+
+					}
+
+				} else {
+
+					setupRenderBuffer( renderTarget.__webglRenderbuffer, renderTarget );
+
+				}
+
+				if ( isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D );
+
+			}
+
+			// Release everything
+
+			if ( isCube ) {
+
+				_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, null );
+
+			} else {
+
+				_gl.bindTexture( _gl.TEXTURE_2D, null );
+
+			}
+
+			_gl.bindRenderbuffer( _gl.RENDERBUFFER, null );
+			_gl.bindFramebuffer( _gl.FRAMEBUFFER, null );
+
+		}
+
+		var framebuffer, width, height, vx, vy;
+
+		if ( renderTarget ) {
+
+			if ( isCube ) {
+
+				framebuffer = renderTarget.__webglFramebuffer[ renderTarget.activeCubeFace ];
+
+			} else {
+
+				framebuffer = renderTarget.__webglFramebuffer;
+
+			}
+
+			width = renderTarget.width;
+			height = renderTarget.height;
+
+			vx = 0;
+			vy = 0;
+
+		} else {
+
+			framebuffer = null;
+
+			width = _viewportWidth;
+			height = _viewportHeight;
+
+			vx = _viewportX;
+			vy = _viewportY;
+
+		}
+
+		if ( framebuffer !== _currentFramebuffer ) {
+
+			_gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
+			_gl.viewport( vx, vy, width, height );
+
+			_currentFramebuffer = framebuffer;
+
+		}
+
+		_currentWidth = width;
+		_currentHeight = height;
+
+	};
+	
+	
+	
+
+	function clampToMaxSize ( image, maxSize ) {
+
+		if ( image.width <= maxSize && image.height <= maxSize ) {
+
+			return image;
+
+		}
+
+		// Warning: Scaling through the canvas will only work with images that use
+		// premultiplied alpha.
+
+		var maxDimension = Math.max( image.width, image.height );
+		var newWidth = Math.floor( image.width * maxSize / maxDimension );
+		var newHeight = Math.floor( image.height * maxSize / maxDimension );
+
+		var canvas = document.createElement( 'canvas' );
+		canvas.width = newWidth;
+		canvas.height = newHeight;
+
+		var ctx = canvas.getContext( "2d" );
+		ctx.drawImage( image, 0, 0, image.width, image.height, 0, 0, newWidth, newHeight );
+
+		return canvas;
+
+	};
+	
+	function updateRenderTargetMipmap ( renderTarget ) {
+
+		if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) {
+
+			_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTarget.__webglTexture );
+			_gl.generateMipmap( _gl.TEXTURE_CUBE_MAP );
+			_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, null );
+
+		} else {
+
+			_gl.bindTexture( _gl.TEXTURE_2D, renderTarget.__webglTexture );
+			_gl.generateMipmap( _gl.TEXTURE_2D );
+			_gl.bindTexture( _gl.TEXTURE_2D, null );
+
+		}
+
+	};
+	
+	function setCubeTextureDynamic ( texture, slot ) {
+
+		_gl.activeTexture( _gl.TEXTURE0 + slot );
+		_gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.__webglTexture );
+
+	}; 
+
+
+	
+	
+	
+	// Map three.js constants to WebGL constants
+	function paramThreeToGL ( p ) {
+
+		if ( p === THREE.RepeatWrapping ) return _gl.REPEAT;
+		if ( p === THREE.ClampToEdgeWrapping ) return _gl.CLAMP_TO_EDGE;
+		if ( p === THREE.MirroredRepeatWrapping ) return _gl.MIRRORED_REPEAT;
+
+		if ( p === THREE.NearestFilter ) return _gl.NEAREST;
+		if ( p === THREE.NearestMipMapNearestFilter ) return _gl.NEAREST_MIPMAP_NEAREST;
+		if ( p === THREE.NearestMipMapLinearFilter ) return _gl.NEAREST_MIPMAP_LINEAR;
+
+		if ( p === THREE.LinearFilter ) return _gl.LINEAR;
+		if ( p === THREE.LinearMipMapNearestFilter ) return _gl.LINEAR_MIPMAP_NEAREST;
+		if ( p === THREE.LinearMipMapLinearFilter ) return _gl.LINEAR_MIPMAP_LINEAR;
+
+		if ( p === THREE.UnsignedByteType ) return _gl.UNSIGNED_BYTE;
+		if ( p === THREE.UnsignedShort4444Type ) return _gl.UNSIGNED_SHORT_4_4_4_4;
+		if ( p === THREE.UnsignedShort5551Type ) return _gl.UNSIGNED_SHORT_5_5_5_1;
+		if ( p === THREE.UnsignedShort565Type ) return _gl.UNSIGNED_SHORT_5_6_5;
+
+		if ( p === THREE.ByteType ) return _gl.BYTE;
+		if ( p === THREE.ShortType ) return _gl.SHORT;
+		if ( p === THREE.UnsignedShortType ) return _gl.UNSIGNED_SHORT;
+		if ( p === THREE.IntType ) return _gl.INT;
+		if ( p === THREE.UnsignedIntType ) return _gl.UNSIGNED_INT;
+		if ( p === THREE.FloatType ) return _gl.FLOAT;
+
+		if ( p === THREE.AlphaFormat ) return _gl.ALPHA;
+		if ( p === THREE.RGBFormat ) return _gl.RGB;
+		if ( p === THREE.RGBAFormat ) return _gl.RGBA;
+		if ( p === THREE.LuminanceFormat ) return _gl.LUMINANCE;
+		if ( p === THREE.LuminanceAlphaFormat ) return _gl.LUMINANCE_ALPHA;
+
+		if ( p === THREE.AddEquation ) return _gl.FUNC_ADD;
+		if ( p === THREE.SubtractEquation ) return _gl.FUNC_SUBTRACT;
+		if ( p === THREE.ReverseSubtractEquation ) return _gl.FUNC_REVERSE_SUBTRACT;
+
+		if ( p === THREE.ZeroFactor ) return _gl.ZERO;
+		if ( p === THREE.OneFactor ) return _gl.ONE;
+		if ( p === THREE.SrcColorFactor ) return _gl.SRC_COLOR;
+		if ( p === THREE.OneMinusSrcColorFactor ) return _gl.ONE_MINUS_SRC_COLOR;
+		if ( p === THREE.SrcAlphaFactor ) return _gl.SRC_ALPHA;
+		if ( p === THREE.OneMinusSrcAlphaFactor ) return _gl.ONE_MINUS_SRC_ALPHA;
+		if ( p === THREE.DstAlphaFactor ) return _gl.DST_ALPHA;
+		if ( p === THREE.OneMinusDstAlphaFactor ) return _gl.ONE_MINUS_DST_ALPHA;
+
+		if ( p === THREE.DstColorFactor ) return _gl.DST_COLOR;
+		if ( p === THREE.OneMinusDstColorFactor ) return _gl.ONE_MINUS_DST_COLOR;
+		if ( p === THREE.SrcAlphaSaturateFactor ) return _gl.SRC_ALPHA_SATURATE;
+
+		if ( _glExtensionCompressedTextureS3TC !== undefined ) {
+
+			if ( p === THREE.RGB_S3TC_DXT1_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGB_S3TC_DXT1_EXT;
+			if ( p === THREE.RGBA_S3TC_DXT1_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGBA_S3TC_DXT1_EXT;
+			if ( p === THREE.RGBA_S3TC_DXT3_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGBA_S3TC_DXT3_EXT;
+			if ( p === THREE.RGBA_S3TC_DXT5_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGBA_S3TC_DXT5_EXT;
+
+		}
+
+		return 0;
+
+	};
+	
+	
+	function compileShader(vertexShader, fragmentShader){
+		
+		var program = _gl.createProgram();
+
+		var glFragmentShader = getShader( "fragment", fragmentShader );
+		var glVertexShader = getShader( "vertex", vertexShader );
+
+		_gl.attachShader( program, glVertexShader );
+		_gl.attachShader( program, glFragmentShader );
+
+		_gl.linkProgram( program );
+
+		if ( !_gl.getProgramParameter( program, _gl.LINK_STATUS ) ) {
+
+			console.error( "Could not initialise shader\n" + "VALIDATE_STATUS: " + _gl.getProgramParameter( program, _gl.VALIDATE_STATUS ) + ", gl error [" + _gl.getError() + "]" );
+
+		}
+
+		// clean up
+
+		_gl.deleteShader( glFragmentShader );
+		_gl.deleteShader( glVertexShader );
+
+		return program;
+		
+	};
+	
+	function resetState(){
+		
+		_oldBlending = -1;
+		_oldDepthTest = -1;
+		_oldDepthWrite = -1;
+		_oldDoubleSided = -1;
+		_oldFlipSided = -1;
+		
+	}
+	
+	function getShader ( type, string ) {
+
+		var shader;
+
+		if ( type === "fragment" ) {
+
+			shader = _gl.createShader( _gl.FRAGMENT_SHADER );
+
+		} else if ( type === "vertex" ) {
+
+			shader = _gl.createShader( _gl.VERTEX_SHADER );
+
+		}
+
+		_gl.shaderSource( shader, string );
+		_gl.compileShader( shader );
+
+		if ( !_gl.getShaderParameter( shader, _gl.COMPILE_STATUS ) ) {
+
+			console.error( _gl.getShaderInfoLog( shader ) );
+			console.error( addLineNumbers( string ) );
+			return null;
+
+		}
+
+		return shader;
+
+	};
+	
+
+	function addLineNumbers ( string ) {
+
+		var chunks = string.split( "\n" );
+
+		for ( var i = 0, il = chunks.length; i < il; i ++ ) {
+
+			// Chrome reports shader errors on lines
+			// starting counting from 1
+
+			chunks[ i ] = ( i + 1 ) + ": " + chunks[ i ];
+
+		}
+
+		return chunks.join( "\n" );
+
+	};
+	
+	
+	
+	function setLineWidth ( width ) {
+
+		if ( width !== _oldLineWidth ) {
+
+			_gl.lineWidth( width );
+
+			_oldLineWidth = width;
+
+		}
+
+	};
+	
+	
+	this.context = _gl;
+	
+	this.autoScaleCubemaps = _autoScaleCubemaps;
+	this.supportsBoneTextures = _supportsBoneTextures;
+	this.precision = _precision;
+	this.maxVertexUniformVectors = _gl.getParameter( _gl.MAX_VERTEX_UNIFORM_VECTORS );	
+	
+	// Methods
+	this.getContext = getContext;
+	this.getDomElement = getDomElement;
+	this.getPrecision = getPrecision;
+	this.getCurrentWidth = getCurrentWidth;
+	this.getCurrentHeight = getCurrentHeight;
+	this.supportsVertexTextures = supportsVertexTextures;
+	this.getMaxAnisotropy  = getMaxAnisotropy;
+	
+	this.setRenderTarget = setRenderTarget;
+	this.setSize = setSize;
+	this.setViewport = setViewport;
+	this.setScissor = setScissor;
+	this.enableScissorTest = enableScissorTest;
+	
+	this.setClearColorHex = setClearColorHex;
+	this.setClearColor = setClearColor;
+	this.getClearColor = getClearColor;
+	this.getClearAlpha = getClearAlpha;
+	this.clear = clear;
+	this.clearTarget = clearTarget;
+	
+	this.deleteBuffer = deleteBuffer;
+	this.deleteTexture = deleteTexture;
+	this.deleteFramebuffer = deleteFramebuffer;
+	this.deleteRenderbuffer = deleteRenderbuffer;
+	this.deleteProgram = deleteProgram;
+	
+	this.createBuffer = createBuffer;
+	this.setStaticArrayBuffer = setStaticArrayBuffer;
+	this.setStaticIndexBuffer = setStaticIndexBuffer;
+	this.setDynamicArrayBuffer = setDynamicArrayBuffer;
+	this.setDynamicIndexBuffer = setDynamicIndexBuffer;
+	
+	this.drawTriangles = drawTriangles;
+	this.drawTriangleStrip = drawTriangleStrip;
+	this.drawLines = drawLines;
+	this.drawLineStrip = drawLineStrip;
+	this.drawPoints = drawPoints;
+	this.drawTriangleElements = drawTriangleElements;
+	this.drawLineElements = drawLineElements;
+	
+	this.bindArrayBuffer = bindArrayBuffer;
+	this.bindElementArrayBuffer = bindElementArrayBuffer;
+	
+	this.enableAttribute = enableAttribute;
+	this.disableAttributes = disableAttributes;
+	this.getAttribLocation = getAttribLocation;
+	this.setFloatAttribute = setFloatAttribute;
+	
+	this.getUniformLocation= getUniformLocation;
+		
+	this.uniform1i = uniform1i;
+	this.uniform1f = uniform1f;
+	this.uniform2f = uniform2f;
+	this.uniform3f = uniform3f;
+	this.uniform4f = uniform4f;
+	this.uniform1iv = uniform1iv;
+	this.uniform2iv = uniform2iv;
+	this.uniform3iv = uniform3iv;
+	this.uniform1fv = uniform1fv;
+	this.uniform2fv = uniform2fv;
+	this.uniform3fv = uniform3fv;
+	this.uniform4fv = uniform4fv;
+	this.uniformMatrix3fv = uniformMatrix3fv;
+	this.uniformMatrix4fv = uniformMatrix4fv;
+	
+	this.useProgram = useProgram;
+	this.compileShader = compileShader;
+	
+	this.setFaceCulling = setFaceCulling;
+	this.setMaterialFaces = setMaterialFaces;
+	this.setPolygonOffset = setPolygonOffset;	
+	this.setBlending = setBlending;
+	this.setDepthTest = setDepthTest;
+	this.setDepthWrite = setDepthWrite;
+	
+	this.setTexture = setTexture;
+	this.setCubeTexture = setCubeTexture;
+	this.updateRenderTargetMipmap = updateRenderTargetMipmap;
+	this.setCubeTextureDynamic = setCubeTextureDynamic;
+	
+	this.paramThreeToGL = paramThreeToGL;
+	this.setLineWidth = setLineWidth;
+	this.resetState = resetState;
+	
+};
+
+
+

+ 406 - 0
src/renderers/webgl/ShaderBuilder.js

@@ -0,0 +1,406 @@
+
+THREE.WebGLRenderer2.ShaderBuilder = function(renderer,info){
+	this.renderer = renderer;
+	this.info = info;
+	this.programs = [],
+	this.programs_counter = 0;
+}
+
+THREE.WebGLRenderer2.ShaderBuilder.prototype.buildProgram = function ( shaderID, fragmentShader, vertexShader, uniforms, attributes, defines, parameters ) {
+
+		var renderer = this.renderer;
+		var p, pl, d, program, code;
+		var chunks = [];
+
+		// Generate code
+
+		if ( shaderID ) {
+
+			chunks.push( shaderID );
+
+		} else {
+
+			chunks.push( fragmentShader );
+			chunks.push( vertexShader );
+
+		}
+
+		for ( d in defines ) {
+
+			chunks.push( d );
+			chunks.push( defines[ d ] );
+
+		}
+
+		for ( p in parameters ) {
+
+			chunks.push( p );
+			chunks.push( parameters[ p ] );
+
+		}
+
+		code = chunks.join();
+
+		// Check if code has been already compiled
+
+		for ( p = 0, pl = this.programs.length; p < pl; p ++ ) {
+
+			var programInfo = this.programs[ p ];
+
+			if ( programInfo.code === code ) {
+
+				//console.log( "Code already compiled." /*: \n\n" + code*/ );
+
+				programInfo.usedTimes ++;
+
+				return programInfo.program;
+
+			}
+
+		}
+
+		var shadowMapTypeDefine = "SHADOWMAP_TYPE_BASIC";
+
+		if ( parameters.shadowMapType === THREE.PCFShadowMap ) {
+
+			shadowMapTypeDefine = "SHADOWMAP_TYPE_PCF";
+
+		} else if ( parameters.shadowMapType === THREE.PCFSoftShadowMap ) {
+
+			shadowMapTypeDefine = "SHADOWMAP_TYPE_PCF_SOFT";
+
+		}
+
+		//console.log( "building new program " );
+
+		//
+
+		var customDefines = this.generateDefines( defines );
+
+		//
+
+		var prefix_vertex = [
+
+			"precision " + renderer.precision + " float;",
+
+			customDefines,
+
+			renderer.supportsVertexTextures ? "#define VERTEX_TEXTURES" : "",
+
+			parameters.gammaInput ? "#define GAMMA_INPUT" : "",
+			parameters.gammaOutput ? "#define GAMMA_OUTPUT" : "",
+			parameters.physicallyBasedShading ? "#define PHYSICALLY_BASED_SHADING" : "",
+
+			"#define MAX_DIR_LIGHTS " + parameters.maxDirLights,
+			"#define MAX_POINT_LIGHTS " + parameters.maxPointLights,
+			"#define MAX_SPOT_LIGHTS " + parameters.maxSpotLights,
+			"#define MAX_HEMI_LIGHTS " + parameters.maxHemiLights,
+
+			"#define MAX_SHADOWS " + parameters.maxShadows,
+
+			"#define MAX_BONES " + parameters.maxBones,
+
+			parameters.map ? "#define USE_MAP" : "",
+			parameters.envMap ? "#define USE_ENVMAP" : "",
+			parameters.lightMap ? "#define USE_LIGHTMAP" : "",
+			parameters.bumpMap ? "#define USE_BUMPMAP" : "",
+			parameters.normalMap ? "#define USE_NORMALMAP" : "",
+			parameters.specularMap ? "#define USE_SPECULARMAP" : "",
+			parameters.vertexColors ? "#define USE_COLOR" : "",
+
+			parameters.skinning ? "#define USE_SKINNING" : "",
+			parameters.useVertexTexture ? "#define BONE_TEXTURE" : "",
+			parameters.boneTextureWidth ? "#define N_BONE_PIXEL_X " + parameters.boneTextureWidth.toFixed( 1 ) : "",
+			parameters.boneTextureHeight ? "#define N_BONE_PIXEL_Y " + parameters.boneTextureHeight.toFixed( 1 ) : "",
+
+			parameters.morphTargets ? "#define USE_MORPHTARGETS" : "",
+			parameters.morphNormals ? "#define USE_MORPHNORMALS" : "",
+			parameters.perPixel ? "#define PHONG_PER_PIXEL" : "",
+			parameters.wrapAround ? "#define WRAP_AROUND" : "",
+			parameters.doubleSided ? "#define DOUBLE_SIDED" : "",
+			parameters.flipSided ? "#define FLIP_SIDED" : "",
+
+			parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "",
+			parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "",
+			parameters.shadowMapDebug ? "#define SHADOWMAP_DEBUG" : "",
+			parameters.shadowMapCascade ? "#define SHADOWMAP_CASCADE" : "",
+
+			parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "",
+
+			"uniform mat4 modelMatrix;",
+			"uniform mat4 modelViewMatrix;",
+			"uniform mat4 projectionMatrix;",
+			"uniform mat4 viewMatrix;",
+			"uniform mat3 normalMatrix;",
+			"uniform vec3 cameraPosition;",
+
+			"attribute vec3 position;",
+			"attribute vec3 normal;",
+			"attribute vec2 uv;",
+			"attribute vec2 uv2;",
+
+			"#ifdef USE_COLOR",
+
+				"attribute vec3 color;",
+
+			"#endif",
+
+			"#ifdef USE_MORPHTARGETS",
+
+				"attribute vec3 morphTarget0;",
+				"attribute vec3 morphTarget1;",
+				"attribute vec3 morphTarget2;",
+				"attribute vec3 morphTarget3;",
+
+				"#ifdef USE_MORPHNORMALS",
+
+					"attribute vec3 morphNormal0;",
+					"attribute vec3 morphNormal1;",
+					"attribute vec3 morphNormal2;",
+					"attribute vec3 morphNormal3;",
+
+				"#else",
+
+					"attribute vec3 morphTarget4;",
+					"attribute vec3 morphTarget5;",
+					"attribute vec3 morphTarget6;",
+					"attribute vec3 morphTarget7;",
+
+				"#endif",
+
+			"#endif",
+
+			"#ifdef USE_SKINNING",
+
+				"attribute vec4 skinIndex;",
+				"attribute vec4 skinWeight;",
+
+			"#endif",
+
+			""
+
+		].join("\n");
+
+		var prefix_fragment = [
+
+			"precision " + renderer.precision + " float;",
+
+			( parameters.bumpMap || parameters.normalMap ) ? "#extension GL_OES_standard_derivatives : enable" : "",
+
+			customDefines,
+
+			"#define MAX_DIR_LIGHTS " + parameters.maxDirLights,
+			"#define MAX_POINT_LIGHTS " + parameters.maxPointLights,
+			"#define MAX_SPOT_LIGHTS " + parameters.maxSpotLights,
+			"#define MAX_HEMI_LIGHTS " + parameters.maxHemiLights,
+
+			"#define MAX_SHADOWS " + parameters.maxShadows,
+
+			parameters.alphaTest ? "#define ALPHATEST " + parameters.alphaTest: "",
+
+			parameters.gammaInput ? "#define GAMMA_INPUT" : "",
+			parameters.gammaOutput ? "#define GAMMA_OUTPUT" : "",
+
+			( parameters.useFog && parameters.fog ) ? "#define USE_FOG" : "",
+			( parameters.useFog && parameters.fogExp ) ? "#define FOG_EXP2" : "",
+
+			parameters.map ? "#define USE_MAP" : "",
+			parameters.envMap ? "#define USE_ENVMAP" : "",
+			parameters.lightMap ? "#define USE_LIGHTMAP" : "",
+			parameters.bumpMap ? "#define USE_BUMPMAP" : "",
+			parameters.normalMap ? "#define USE_NORMALMAP" : "",
+			parameters.specularMap ? "#define USE_SPECULARMAP" : "",
+			parameters.vertexColors ? "#define USE_COLOR" : "",
+
+			parameters.metal ? "#define METAL" : "",
+			parameters.perPixel ? "#define PHONG_PER_PIXEL" : "",
+			parameters.wrapAround ? "#define WRAP_AROUND" : "",
+			parameters.doubleSided ? "#define DOUBLE_SIDED" : "",
+			parameters.flipSided ? "#define FLIP_SIDED" : "",
+
+			parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "",
+			parameters.shadowMapEnabled ? "#define " + shadowMapTypeDefine : "",
+			parameters.shadowMapDebug ? "#define SHADOWMAP_DEBUG" : "",
+			parameters.shadowMapCascade ? "#define SHADOWMAP_CASCADE" : "",
+
+			"uniform mat4 viewMatrix;",
+			"uniform vec3 cameraPosition;",
+			""
+
+		].join("\n");
+		
+
+		program = renderer.compileShader(prefix_vertex + vertexShader, prefix_fragment + fragmentShader);
+		
+		//console.log( prefix_fragment + fragmentShader );
+		//console.log( prefix_vertex + vertexShader );
+
+		program.uniforms = {};
+		program.attributes = {};
+
+		var identifiers, u, a, i;
+
+		// cache uniform locations
+
+		identifiers = [
+
+			'viewMatrix', 'modelViewMatrix', 'projectionMatrix', 'normalMatrix', 'modelMatrix', 'cameraPosition',
+			'morphTargetInfluences'
+
+		];
+
+		if ( parameters.useVertexTexture ) {
+
+			identifiers.push( 'boneTexture' );
+
+		} else {
+
+			identifiers.push( 'boneGlobalMatrices' );
+
+		}
+
+		for ( u in uniforms ) {
+
+			identifiers.push( u );
+
+		}
+
+		this.cacheUniformLocations( program, identifiers );
+
+		// cache attributes locations
+
+		identifiers = [
+
+			"position", "normal", "uv", "uv2", "tangent", "color",
+			"skinIndex", "skinWeight", "lineDistance"
+
+		];
+
+		for ( i = 0; i < parameters.maxMorphTargets; i ++ ) {
+
+			identifiers.push( "morphTarget" + i );
+
+		}
+
+		for ( i = 0; i < parameters.maxMorphNormals; i ++ ) {
+
+			identifiers.push( "morphNormal" + i );
+
+		}
+
+		for ( a in attributes ) {
+
+			identifiers.push( a );
+
+		}
+
+		this.cacheAttributeLocations( program, identifiers );
+
+		program.id = this.programs_counter ++;
+
+		this.programs.push( { program: program, code: code, usedTimes: 1 } );
+
+		this.info.memory.programs = this.programs.length;
+
+		return program;
+
+	};
+	
+THREE.WebGLRenderer2.ShaderBuilder.prototype.generateDefines = function( defines ) {
+
+	var value, chunk, chunks = [];
+
+	for ( var d in defines ) {
+
+		value = defines[ d ];
+		if ( value === false ) continue;
+
+		chunk = "#define " + d + " " + value;
+		chunks.push( chunk );
+
+	}
+
+	return chunks.join( "\n" );
+
+};
+
+	// Shader parameters cache
+
+THREE.WebGLRenderer2.ShaderBuilder.prototype.cacheUniformLocations = function( program, identifiers ) {
+
+	var i, l, id, renderer = this.renderer;
+
+	for( i = 0, l = identifiers.length; i < l; i ++ ) {
+
+		id = identifiers[ i ];
+		program.uniforms[ id ] = renderer.getUniformLocation( program, id );
+
+	}
+
+};
+
+THREE.WebGLRenderer2.ShaderBuilder.prototype.cacheAttributeLocations = function( program, identifiers ) {
+
+	var i, l, id, renderer = this.renderer;
+
+	for( i = 0, l = identifiers.length; i < l; i ++ ) {
+
+		id = identifiers[ i ];
+		program.attributes[ id ] = renderer.getAttribLocation( program, id );
+
+	}
+
+};
+
+THREE.WebGLRenderer2.ShaderBuilder.prototype.removeProgram = function( program ) {
+
+	var i, il, programInfo;
+	var deleteProgram = false;
+	var programs = this.programs;
+
+	for ( i = 0, il = programs.length; i < il; i ++ ) {
+
+		programInfo = programs[ i ];
+
+		if ( programInfo.program === program ) {
+
+			programInfo.usedTimes --;
+
+			if ( programInfo.usedTimes === 0 ) {
+
+				deleteProgram = true;
+
+			}
+
+			break;
+
+		}
+
+	}
+
+	if ( deleteProgram === true ) {
+
+		// avoid using array.splice, this is costlier than creating new array from scratch
+
+		var newPrograms = [];
+
+		for ( i = 0, il = programs.length; i < il; i ++ ) {
+
+			programInfo = programs[ i ];
+
+			if ( programInfo.program !== program ) {
+
+				newPrograms.push( programInfo );
+
+			}
+
+		}
+
+		programs = newPrograms;
+
+		this.renderer.deleteProgram( program );
+
+		this.info.memory.programs --;
+
+	}
+}

+ 200 - 0
src/renderers/webgl/objects/LineRenderer.js

@@ -0,0 +1,200 @@
+
+
+THREE.WebGLRenderer2.LineRenderer = function(lowlevelrenderer, info){
+	THREE.WebGLRenderer2.Object3DObjectRenderer.call( this, lowlevelrenderer, info );
+};
+
+
+THREE.WebGLRenderer2.LineRenderer.prototype = Object.create( THREE.WebGLRenderer2.Object3DObjectRenderer.prototype );
+
+THREE.WebGLRenderer2.LineRenderer.prototype.createBuffers = function( geometry ) {
+	
+	var renderer = this.renderer;
+	geometry.__webglVertexBuffer = renderer.createBuffer();
+	geometry.__webglColorBuffer = renderer.createBuffer();
+	geometry.__webglLineDistanceBuffer = renderer.createBuffer();
+
+	this.info.memory.geometries ++;
+};
+
+THREE.WebGLRenderer2.LineRenderer.prototype.initBuffers = function( geometry, object ) {
+
+	var nvertices = geometry.vertices.length;
+
+	geometry.__vertexArray = new Float32Array( nvertices * 3 );
+	geometry.__colorArray = new Float32Array( nvertices * 3 );
+	geometry.__lineDistanceArray = new Float32Array( nvertices * 1 );
+
+	geometry.__webglLineCount = nvertices;
+
+	this.initCustomAttributes ( geometry, object );
+};
+
+
+THREE.WebGLRenderer2.LineRenderer.prototype.setBuffers = function( geometry, object) {
+	
+	var renderer = this.renderer;
+	var v, c, d, vertex, offset, color,
+
+	vertices = geometry.vertices,
+	colors = geometry.colors,
+	lineDistances = geometry.lineDistances,
+
+	vl = vertices.length,
+	cl = colors.length,
+	dl = lineDistances.length,
+
+	vertexArray = geometry.__vertexArray,
+	colorArray = geometry.__colorArray,
+	lineDistanceArray = geometry.__lineDistanceArray,
+
+	dirtyVertices = geometry.verticesNeedUpdate,
+	dirtyColors = geometry.colorsNeedUpdate,
+	dirtyLineDistances = geometry.lineDistancesNeedUpdate,
+
+	customAttributes = geometry.__webglCustomAttributesList,
+
+	i, il,
+	a, ca, cal, value,
+	customAttribute;
+
+	if ( dirtyVertices ) {
+
+		for ( v = 0; v < vl; v ++ ) {
+
+			vertex = vertices[ v ];
+
+			offset = v * 3;
+
+			vertexArray[ offset ]     = vertex.x;
+			vertexArray[ offset + 1 ] = vertex.y;
+			vertexArray[ offset + 2 ] = vertex.z;
+
+		}
+		
+		renderer.setDynamicArrayBuffer(geometry.__webglVertexBuffer,vertexArray);
+
+	}
+
+	if ( dirtyColors ) {
+
+		for ( c = 0; c < cl; c ++ ) {
+
+			color = colors[ c ];
+
+			offset = c * 3;
+
+			colorArray[ offset ]     = color.r;
+			colorArray[ offset + 1 ] = color.g;
+			colorArray[ offset + 2 ] = color.b;
+
+		}
+		
+		renderer.setDynamicArrayBuffer(geometry.__webglColorBuffer,colorArray);
+
+	}
+
+	if ( dirtyLineDistances ) {
+
+		for ( d = 0; d < dl; d ++ ) {
+
+			lineDistanceArray[ d ] = lineDistances[ d ];
+
+		}
+		
+		renderer.setDynamicArrayBuffer( geometry.__webglLineDistanceBuffer,lineDistanceArray);
+
+	}
+
+	if ( customAttributes ) {
+
+		for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
+
+			customAttribute = customAttributes[ i ];
+
+			if ( customAttribute.needsUpdate &&
+				 ( customAttribute.boundTo === undefined ||
+				   customAttribute.boundTo === "vertices" ) ) {
+
+				offset = 0;
+
+				cal = customAttribute.value.length;
+
+				if ( customAttribute.size === 1 ) {
+
+					for ( ca = 0; ca < cal; ca ++ ) {
+
+						customAttribute.array[ ca ] = customAttribute.value[ ca ];
+
+					}
+
+				} else if ( customAttribute.size === 2 ) {
+
+					for ( ca = 0; ca < cal; ca ++ ) {
+
+						value = customAttribute.value[ ca ];
+
+						customAttribute.array[ offset ] 	= value.x;
+						customAttribute.array[ offset + 1 ] = value.y;
+
+						offset += 2;
+
+					}
+
+				} else if ( customAttribute.size === 3 ) {
+
+					if ( customAttribute.type === "c" ) {
+
+						for ( ca = 0; ca < cal; ca ++ ) {
+
+							value = customAttribute.value[ ca ];
+
+							customAttribute.array[ offset ] 	= value.r;
+							customAttribute.array[ offset + 1 ] = value.g;
+							customAttribute.array[ offset + 2 ] = value.b;
+
+							offset += 3;
+
+						}
+
+					} else {
+
+						for ( ca = 0; ca < cal; ca ++ ) {
+
+							value = customAttribute.value[ ca ];
+
+							customAttribute.array[ offset ] 	= value.x;
+							customAttribute.array[ offset + 1 ] = value.y;
+							customAttribute.array[ offset + 2 ] = value.z;
+
+							offset += 3;
+
+						}
+
+					}
+
+				} else if ( customAttribute.size === 4 ) {
+
+					for ( ca = 0; ca < cal; ca ++ ) {
+
+						value = customAttribute.value[ ca ];
+
+						customAttribute.array[ offset ] 	 = value.x;
+						customAttribute.array[ offset + 1  ] = value.y;
+						customAttribute.array[ offset + 2  ] = value.z;
+						customAttribute.array[ offset + 3  ] = value.w;
+
+						offset += 4;
+
+					}
+
+				}
+
+				renderer.setDynamicArrayBuffer( customAttribute.buffer,customAttribute.array);
+
+			}
+
+		}
+
+	}
+};

+ 1616 - 0
src/renderers/webgl/objects/MeshRenderer.js

@@ -0,0 +1,1616 @@
+
+
+THREE.WebGLRenderer2.MeshRenderer = function(lowlevelrenderer, info){
+	THREE.WebGLRenderer2.Object3DObjectRenderer.call( this, lowlevelrenderer, info );
+};
+
+
+THREE.WebGLRenderer2.MeshRenderer.prototype = Object.create( THREE.WebGLRenderer2.Object3DObjectRenderer.prototype );
+
+THREE.WebGLRenderer2.MeshRenderer.prototype.createBuffers = function( geometryGroup ) {
+
+	var renderer = this.renderer;
+	geometryGroup.__webglVertexBuffer = renderer.createBuffer();
+	geometryGroup.__webglNormalBuffer = renderer.createBuffer();
+	geometryGroup.__webglTangentBuffer = renderer.createBuffer();
+	geometryGroup.__webglColorBuffer = renderer.createBuffer();
+	geometryGroup.__webglUVBuffer = renderer.createBuffer();
+	geometryGroup.__webglUV2Buffer = renderer.createBuffer();
+
+	geometryGroup.__webglSkinIndicesBuffer = renderer.createBuffer();
+	geometryGroup.__webglSkinWeightsBuffer = renderer.createBuffer();
+
+	geometryGroup.__webglFaceBuffer = renderer.createBuffer();
+	geometryGroup.__webglLineBuffer = renderer.createBuffer();
+
+	var m, ml;
+
+	if ( geometryGroup.numMorphTargets ) {
+
+		geometryGroup.__webglMorphTargetsBuffers = [];
+
+		for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) {
+
+			geometryGroup.__webglMorphTargetsBuffers.push( renderer.createBuffer() );
+
+		}
+
+	}
+
+	if ( geometryGroup.numMorphNormals ) {
+
+		geometryGroup.__webglMorphNormalsBuffers = [];
+
+		for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) {
+
+			geometryGroup.__webglMorphNormalsBuffers.push( renderer.createBuffer() );
+
+		}
+
+	}
+
+	this.info.memory.geometries ++;
+
+};
+
+THREE.WebGLRenderer2.MeshRenderer.prototype.initBuffers = function( geometryGroup, object ) {
+
+	var geometry = object.geometry,
+		faces3 = geometryGroup.faces3,
+		faces4 = geometryGroup.faces4,
+
+		nvertices = faces3.length * 3 + faces4.length * 4,
+		ntris     = faces3.length * 1 + faces4.length * 2,
+		nlines    = faces3.length * 3 + faces4.length * 4,
+
+		material = this.getBufferMaterial( object, geometryGroup ),
+
+		uvType = this.bufferGuessUVType( material ),
+		normalType = this.bufferGuessNormalType( material ),
+		vertexColorType = this.bufferGuessVertexColorType( material );
+
+	//console.log( "uvType", uvType, "normalType", normalType, "vertexColorType", vertexColorType, object, geometryGroup, material );
+
+	geometryGroup.__vertexArray = new Float32Array( nvertices * 3 );
+
+	if ( normalType ) {
+
+		geometryGroup.__normalArray = new Float32Array( nvertices * 3 );
+
+	}
+
+	if ( geometry.hasTangents ) {
+
+		geometryGroup.__tangentArray = new Float32Array( nvertices * 4 );
+
+	}
+
+	if ( vertexColorType ) {
+
+		geometryGroup.__colorArray = new Float32Array( nvertices * 3 );
+
+	}
+
+	if ( uvType ) {
+
+		if ( geometry.faceUvs.length > 0 || geometry.faceVertexUvs.length > 0 ) {
+
+			geometryGroup.__uvArray = new Float32Array( nvertices * 2 );
+
+		}
+
+		if ( geometry.faceUvs.length > 1 || geometry.faceVertexUvs.length > 1 ) {
+
+			geometryGroup.__uv2Array = new Float32Array( nvertices * 2 );
+
+		}
+
+	}
+
+	if ( object.geometry.skinWeights.length && object.geometry.skinIndices.length ) {
+
+		geometryGroup.__skinIndexArray = new Float32Array( nvertices * 4 );
+		geometryGroup.__skinWeightArray = new Float32Array( nvertices * 4 );
+
+	}
+
+	geometryGroup.__faceArray = new Uint16Array( ntris * 3 );
+	geometryGroup.__lineArray = new Uint16Array( nlines * 2 );
+
+	var m, ml;
+
+	if ( geometryGroup.numMorphTargets ) {
+
+		geometryGroup.__morphTargetsArrays = [];
+
+		for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) {
+
+			geometryGroup.__morphTargetsArrays.push( new Float32Array( nvertices * 3 ) );
+
+		}
+
+	}
+
+	if ( geometryGroup.numMorphNormals ) {
+
+		geometryGroup.__morphNormalsArrays = [];
+
+		for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) {
+
+			geometryGroup.__morphNormalsArrays.push( new Float32Array( nvertices * 3 ) );
+
+		}
+
+	}
+
+	geometryGroup.__webglFaceCount = ntris * 3;
+	geometryGroup.__webglLineCount = nlines * 2;
+
+
+	// custom attributes
+
+	if ( material.attributes ) {
+
+		if ( geometryGroup.__webglCustomAttributesList === undefined ) {
+
+			geometryGroup.__webglCustomAttributesList = [];
+
+		}
+
+		for ( var a in material.attributes ) {
+
+			// Do a shallow copy of the attribute object so different geometryGroup chunks use different
+			// attribute buffers which are correctly indexed in the setMeshBuffers function
+
+			var originalAttribute = material.attributes[ a ];
+
+			var attribute = {};
+
+			for ( var property in originalAttribute ) {
+
+				attribute[ property ] = originalAttribute[ property ];
+
+			}
+
+			if ( !attribute.__webglInitialized || attribute.createUniqueBuffers ) {
+
+				attribute.__webglInitialized = true;
+
+				var size = 1;		// "f" and "i"
+
+				if( attribute.type === "v2" ) size = 2;
+				else if( attribute.type === "v3" ) size = 3;
+				else if( attribute.type === "v4" ) size = 4;
+				else if( attribute.type === "c"  ) size = 3;
+
+				attribute.size = size;
+
+				attribute.array = new Float32Array( nvertices * size );
+
+				attribute.buffer = this.renderer.createBuffer();
+				attribute.buffer.belongsToAttribute = a;
+
+				originalAttribute.needsUpdate = true;
+				attribute.__original = originalAttribute;
+
+			}
+
+			geometryGroup.__webglCustomAttributesList.push( attribute );
+
+		}
+
+	}
+
+	geometryGroup.__inittedArrays = true;
+
+};
+
+
+
+THREE.WebGLRenderer2.MeshRenderer.prototype.setBuffers = function( geometryGroup, object, dispose, material ) {
+
+	if ( ! geometryGroup.__inittedArrays ) {
+
+		return;
+
+	}
+
+	var renderer = this.renderer;
+	var normalType = this.bufferGuessNormalType( material ),
+	vertexColorType = this.bufferGuessVertexColorType( material ),
+	uvType = this.bufferGuessUVType( material ),
+
+	needsSmoothNormals = ( normalType === THREE.SmoothShading );
+
+	var f, fl, fi, face,
+	vertexNormals, faceNormal, normal,
+	vertexColors, faceColor,
+	vertexTangents,
+	uv, uv2, v1, v2, v3, v4, t1, t2, t3, t4, n1, n2, n3, n4,
+	c1, c2, c3, c4,
+	sw1, sw2, sw3, sw4,
+	si1, si2, si3, si4,
+	sa1, sa2, sa3, sa4,
+	sb1, sb2, sb3, sb4,
+	m, ml, i, il,
+	vn, uvi, uv2i,
+	vk, vkl, vka,
+	nka, chf, faceVertexNormals,
+	a,
+
+	vertexIndex = 0,
+
+	offset = 0,
+	offset_uv = 0,
+	offset_uv2 = 0,
+	offset_face = 0,
+	offset_normal = 0,
+	offset_tangent = 0,
+	offset_line = 0,
+	offset_color = 0,
+	offset_skin = 0,
+	offset_morphTarget = 0,
+	offset_custom = 0,
+	offset_customSrc = 0,
+
+	value,
+
+	vertexArray = geometryGroup.__vertexArray,
+	uvArray = geometryGroup.__uvArray,
+	uv2Array = geometryGroup.__uv2Array,
+	normalArray = geometryGroup.__normalArray,
+	tangentArray = geometryGroup.__tangentArray,
+	colorArray = geometryGroup.__colorArray,
+
+	skinIndexArray = geometryGroup.__skinIndexArray,
+	skinWeightArray = geometryGroup.__skinWeightArray,
+
+	morphTargetsArrays = geometryGroup.__morphTargetsArrays,
+	morphNormalsArrays = geometryGroup.__morphNormalsArrays,
+
+	customAttributes = geometryGroup.__webglCustomAttributesList,
+	customAttribute,
+
+	faceArray = geometryGroup.__faceArray,
+	lineArray = geometryGroup.__lineArray,
+
+	geometry = object.geometry, // this is shared for all chunks
+
+	dirtyVertices = geometry.verticesNeedUpdate,
+	dirtyElements = geometry.elementsNeedUpdate,
+	dirtyUvs = geometry.uvsNeedUpdate,
+	dirtyNormals = geometry.normalsNeedUpdate,
+	dirtyTangents = geometry.tangentsNeedUpdate,
+	dirtyColors = geometry.colorsNeedUpdate,
+	dirtyMorphTargets = geometry.morphTargetsNeedUpdate,
+
+	vertices = geometry.vertices,
+	chunk_faces3 = geometryGroup.faces3,
+	chunk_faces4 = geometryGroup.faces4,
+	obj_faces = geometry.faces,
+
+	obj_uvs  = geometry.faceVertexUvs[ 0 ],
+	obj_uvs2 = geometry.faceVertexUvs[ 1 ],
+
+	obj_colors = geometry.colors,
+
+	obj_skinIndices = geometry.skinIndices,
+	obj_skinWeights = geometry.skinWeights,
+
+	morphTargets = geometry.morphTargets,
+	morphNormals = geometry.morphNormals;
+
+	if ( dirtyVertices ) {
+
+		for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+			face = obj_faces[ chunk_faces3[ f ] ];
+
+			v1 = vertices[ face.a ];
+			v2 = vertices[ face.b ];
+			v3 = vertices[ face.c ];
+
+			vertexArray[ offset ]     = v1.x;
+			vertexArray[ offset + 1 ] = v1.y;
+			vertexArray[ offset + 2 ] = v1.z;
+
+			vertexArray[ offset + 3 ] = v2.x;
+			vertexArray[ offset + 4 ] = v2.y;
+			vertexArray[ offset + 5 ] = v2.z;
+
+			vertexArray[ offset + 6 ] = v3.x;
+			vertexArray[ offset + 7 ] = v3.y;
+			vertexArray[ offset + 8 ] = v3.z;
+
+			offset += 9;
+
+		}
+
+		for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+			face = obj_faces[ chunk_faces4[ f ] ];
+
+			v1 = vertices[ face.a ];
+			v2 = vertices[ face.b ];
+			v3 = vertices[ face.c ];
+			v4 = vertices[ face.d ];
+
+			vertexArray[ offset ]     = v1.x;
+			vertexArray[ offset + 1 ] = v1.y;
+			vertexArray[ offset + 2 ] = v1.z;
+
+			vertexArray[ offset + 3 ] = v2.x;
+			vertexArray[ offset + 4 ] = v2.y;
+			vertexArray[ offset + 5 ] = v2.z;
+
+			vertexArray[ offset + 6 ] = v3.x;
+			vertexArray[ offset + 7 ] = v3.y;
+			vertexArray[ offset + 8 ] = v3.z;
+
+			vertexArray[ offset + 9 ]  = v4.x;
+			vertexArray[ offset + 10 ] = v4.y;
+			vertexArray[ offset + 11 ] = v4.z;
+
+			offset += 12;
+
+		}
+
+		renderer.setDynamicArrayBuffer( geometryGroup.__webglVertexBuffer, vertexArray);
+
+	}
+
+	if ( dirtyMorphTargets ) {
+
+		for ( vk = 0, vkl = morphTargets.length; vk < vkl; vk ++ ) {
+
+			offset_morphTarget = 0;
+
+			for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+				chf = chunk_faces3[ f ];
+				face = obj_faces[ chf ];
+
+				// morph positions
+
+				v1 = morphTargets[ vk ].vertices[ face.a ];
+				v2 = morphTargets[ vk ].vertices[ face.b ];
+				v3 = morphTargets[ vk ].vertices[ face.c ];
+
+				vka = morphTargetsArrays[ vk ];
+
+				vka[ offset_morphTarget ] 	  = v1.x;
+				vka[ offset_morphTarget + 1 ] = v1.y;
+				vka[ offset_morphTarget + 2 ] = v1.z;
+
+				vka[ offset_morphTarget + 3 ] = v2.x;
+				vka[ offset_morphTarget + 4 ] = v2.y;
+				vka[ offset_morphTarget + 5 ] = v2.z;
+
+				vka[ offset_morphTarget + 6 ] = v3.x;
+				vka[ offset_morphTarget + 7 ] = v3.y;
+				vka[ offset_morphTarget + 8 ] = v3.z;
+
+				// morph normals
+
+				if ( material.morphNormals ) {
+
+					if ( needsSmoothNormals ) {
+
+						faceVertexNormals = morphNormals[ vk ].vertexNormals[ chf ];
+
+						n1 = faceVertexNormals.a;
+						n2 = faceVertexNormals.b;
+						n3 = faceVertexNormals.c;
+
+					} else {
+
+						n1 = morphNormals[ vk ].faceNormals[ chf ];
+						n2 = n1;
+						n3 = n1;
+
+					}
+
+					nka = morphNormalsArrays[ vk ];
+
+					nka[ offset_morphTarget ] 	  = n1.x;
+					nka[ offset_morphTarget + 1 ] = n1.y;
+					nka[ offset_morphTarget + 2 ] = n1.z;
+
+					nka[ offset_morphTarget + 3 ] = n2.x;
+					nka[ offset_morphTarget + 4 ] = n2.y;
+					nka[ offset_morphTarget + 5 ] = n2.z;
+
+					nka[ offset_morphTarget + 6 ] = n3.x;
+					nka[ offset_morphTarget + 7 ] = n3.y;
+					nka[ offset_morphTarget + 8 ] = n3.z;
+
+				}
+
+				//
+
+				offset_morphTarget += 9;
+
+			}
+
+			for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+				chf = chunk_faces4[ f ];
+				face = obj_faces[ chf ];
+
+				// morph positions
+
+				v1 = morphTargets[ vk ].vertices[ face.a ];
+				v2 = morphTargets[ vk ].vertices[ face.b ];
+				v3 = morphTargets[ vk ].vertices[ face.c ];
+				v4 = morphTargets[ vk ].vertices[ face.d ];
+
+				vka = morphTargetsArrays[ vk ];
+
+				vka[ offset_morphTarget ] 	  = v1.x;
+				vka[ offset_morphTarget + 1 ] = v1.y;
+				vka[ offset_morphTarget + 2 ] = v1.z;
+
+				vka[ offset_morphTarget + 3 ] = v2.x;
+				vka[ offset_morphTarget + 4 ] = v2.y;
+				vka[ offset_morphTarget + 5 ] = v2.z;
+
+				vka[ offset_morphTarget + 6 ] = v3.x;
+				vka[ offset_morphTarget + 7 ] = v3.y;
+				vka[ offset_morphTarget + 8 ] = v3.z;
+
+				vka[ offset_morphTarget + 9 ]  = v4.x;
+				vka[ offset_morphTarget + 10 ] = v4.y;
+				vka[ offset_morphTarget + 11 ] = v4.z;
+
+				// morph normals
+
+				if ( material.morphNormals ) {
+
+					if ( needsSmoothNormals ) {
+
+						faceVertexNormals = morphNormals[ vk ].vertexNormals[ chf ];
+
+						n1 = faceVertexNormals.a;
+						n2 = faceVertexNormals.b;
+						n3 = faceVertexNormals.c;
+						n4 = faceVertexNormals.d;
+
+					} else {
+
+						n1 = morphNormals[ vk ].faceNormals[ chf ];
+						n2 = n1;
+						n3 = n1;
+						n4 = n1;
+
+					}
+
+					nka = morphNormalsArrays[ vk ];
+
+					nka[ offset_morphTarget ] 	  = n1.x;
+					nka[ offset_morphTarget + 1 ] = n1.y;
+					nka[ offset_morphTarget + 2 ] = n1.z;
+
+					nka[ offset_morphTarget + 3 ] = n2.x;
+					nka[ offset_morphTarget + 4 ] = n2.y;
+					nka[ offset_morphTarget + 5 ] = n2.z;
+
+					nka[ offset_morphTarget + 6 ] = n3.x;
+					nka[ offset_morphTarget + 7 ] = n3.y;
+					nka[ offset_morphTarget + 8 ] = n3.z;
+
+					nka[ offset_morphTarget + 9 ]  = n4.x;
+					nka[ offset_morphTarget + 10 ] = n4.y;
+					nka[ offset_morphTarget + 11 ] = n4.z;
+
+				}
+
+				//
+
+				offset_morphTarget += 12;
+
+			}
+
+			this.renderer.setDynamicArrayBuffer( geometryGroup.__webglMorphTargetsBuffers[ vk ], morphTargetsArrays[ vk ]);
+
+			if ( material.morphNormals ) {
+
+				this.renderer.setDynamicArrayBuffer( geometryGroup.__webglMorphNormalsBuffers[ vk ], morphNormalsArrays[ vk ]);
+
+			}
+
+		}
+
+	}
+
+	if ( obj_skinWeights.length ) {
+
+		for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+			face = obj_faces[ chunk_faces3[ f ]	];
+
+			// weights
+
+			sw1 = obj_skinWeights[ face.a ];
+			sw2 = obj_skinWeights[ face.b ];
+			sw3 = obj_skinWeights[ face.c ];
+
+			skinWeightArray[ offset_skin ]     = sw1.x;
+			skinWeightArray[ offset_skin + 1 ] = sw1.y;
+			skinWeightArray[ offset_skin + 2 ] = sw1.z;
+			skinWeightArray[ offset_skin + 3 ] = sw1.w;
+
+			skinWeightArray[ offset_skin + 4 ] = sw2.x;
+			skinWeightArray[ offset_skin + 5 ] = sw2.y;
+			skinWeightArray[ offset_skin + 6 ] = sw2.z;
+			skinWeightArray[ offset_skin + 7 ] = sw2.w;
+
+			skinWeightArray[ offset_skin + 8 ]  = sw3.x;
+			skinWeightArray[ offset_skin + 9 ]  = sw3.y;
+			skinWeightArray[ offset_skin + 10 ] = sw3.z;
+			skinWeightArray[ offset_skin + 11 ] = sw3.w;
+
+			// indices
+
+			si1 = obj_skinIndices[ face.a ];
+			si2 = obj_skinIndices[ face.b ];
+			si3 = obj_skinIndices[ face.c ];
+
+			skinIndexArray[ offset_skin ]     = si1.x;
+			skinIndexArray[ offset_skin + 1 ] = si1.y;
+			skinIndexArray[ offset_skin + 2 ] = si1.z;
+			skinIndexArray[ offset_skin + 3 ] = si1.w;
+
+			skinIndexArray[ offset_skin + 4 ] = si2.x;
+			skinIndexArray[ offset_skin + 5 ] = si2.y;
+			skinIndexArray[ offset_skin + 6 ] = si2.z;
+			skinIndexArray[ offset_skin + 7 ] = si2.w;
+
+			skinIndexArray[ offset_skin + 8 ]  = si3.x;
+			skinIndexArray[ offset_skin + 9 ]  = si3.y;
+			skinIndexArray[ offset_skin + 10 ] = si3.z;
+			skinIndexArray[ offset_skin + 11 ] = si3.w;
+
+			offset_skin += 12;
+
+		}
+
+		for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+			face = obj_faces[ chunk_faces4[ f ] ];
+
+			// weights
+
+			sw1 = obj_skinWeights[ face.a ];
+			sw2 = obj_skinWeights[ face.b ];
+			sw3 = obj_skinWeights[ face.c ];
+			sw4 = obj_skinWeights[ face.d ];
+
+			skinWeightArray[ offset_skin ]     = sw1.x;
+			skinWeightArray[ offset_skin + 1 ] = sw1.y;
+			skinWeightArray[ offset_skin + 2 ] = sw1.z;
+			skinWeightArray[ offset_skin + 3 ] = sw1.w;
+
+			skinWeightArray[ offset_skin + 4 ] = sw2.x;
+			skinWeightArray[ offset_skin + 5 ] = sw2.y;
+			skinWeightArray[ offset_skin + 6 ] = sw2.z;
+			skinWeightArray[ offset_skin + 7 ] = sw2.w;
+
+			skinWeightArray[ offset_skin + 8 ]  = sw3.x;
+			skinWeightArray[ offset_skin + 9 ]  = sw3.y;
+			skinWeightArray[ offset_skin + 10 ] = sw3.z;
+			skinWeightArray[ offset_skin + 11 ] = sw3.w;
+
+			skinWeightArray[ offset_skin + 12 ] = sw4.x;
+			skinWeightArray[ offset_skin + 13 ] = sw4.y;
+			skinWeightArray[ offset_skin + 14 ] = sw4.z;
+			skinWeightArray[ offset_skin + 15 ] = sw4.w;
+
+			// indices
+
+			si1 = obj_skinIndices[ face.a ];
+			si2 = obj_skinIndices[ face.b ];
+			si3 = obj_skinIndices[ face.c ];
+			si4 = obj_skinIndices[ face.d ];
+
+			skinIndexArray[ offset_skin ]     = si1.x;
+			skinIndexArray[ offset_skin + 1 ] = si1.y;
+			skinIndexArray[ offset_skin + 2 ] = si1.z;
+			skinIndexArray[ offset_skin + 3 ] = si1.w;
+
+			skinIndexArray[ offset_skin + 4 ] = si2.x;
+			skinIndexArray[ offset_skin + 5 ] = si2.y;
+			skinIndexArray[ offset_skin + 6 ] = si2.z;
+			skinIndexArray[ offset_skin + 7 ] = si2.w;
+
+			skinIndexArray[ offset_skin + 8 ]  = si3.x;
+			skinIndexArray[ offset_skin + 9 ]  = si3.y;
+			skinIndexArray[ offset_skin + 10 ] = si3.z;
+			skinIndexArray[ offset_skin + 11 ] = si3.w;
+
+			skinIndexArray[ offset_skin + 12 ] = si4.x;
+			skinIndexArray[ offset_skin + 13 ] = si4.y;
+			skinIndexArray[ offset_skin + 14 ] = si4.z;
+			skinIndexArray[ offset_skin + 15 ] = si4.w;
+
+			offset_skin += 16;
+
+		}
+
+		if ( offset_skin > 0 ) {
+
+		renderer.setDynamicArrayBuffer( geometryGroup.__webglSkinIndicesBuffer, skinIndexArray);
+		renderer.setDynamicArrayBuffer( geometryGroup.__webglSkinWeightsBuffer, skinWeightArray);
+
+		}
+
+	}
+
+	if ( dirtyColors && vertexColorType ) {
+
+		for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+			face = obj_faces[ chunk_faces3[ f ]	];
+
+			vertexColors = face.vertexColors;
+			faceColor = face.color;
+
+			if ( vertexColors.length === 3 && vertexColorType === THREE.VertexColors ) {
+
+				c1 = vertexColors[ 0 ];
+				c2 = vertexColors[ 1 ];
+				c3 = vertexColors[ 2 ];
+
+			} else {
+
+				c1 = faceColor;
+				c2 = faceColor;
+				c3 = faceColor;
+
+			}
+
+			colorArray[ offset_color ]     = c1.r;
+			colorArray[ offset_color + 1 ] = c1.g;
+			colorArray[ offset_color + 2 ] = c1.b;
+
+			colorArray[ offset_color + 3 ] = c2.r;
+			colorArray[ offset_color + 4 ] = c2.g;
+			colorArray[ offset_color + 5 ] = c2.b;
+
+			colorArray[ offset_color + 6 ] = c3.r;
+			colorArray[ offset_color + 7 ] = c3.g;
+			colorArray[ offset_color + 8 ] = c3.b;
+
+			offset_color += 9;
+
+		}
+
+		for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+			face = obj_faces[ chunk_faces4[ f ] ];
+
+			vertexColors = face.vertexColors;
+			faceColor = face.color;
+
+			if ( vertexColors.length === 4 && vertexColorType === THREE.VertexColors ) {
+
+				c1 = vertexColors[ 0 ];
+				c2 = vertexColors[ 1 ];
+				c3 = vertexColors[ 2 ];
+				c4 = vertexColors[ 3 ];
+
+			} else {
+
+				c1 = faceColor;
+				c2 = faceColor;
+				c3 = faceColor;
+				c4 = faceColor;
+
+			}
+
+			colorArray[ offset_color ]     = c1.r;
+			colorArray[ offset_color + 1 ] = c1.g;
+			colorArray[ offset_color + 2 ] = c1.b;
+
+			colorArray[ offset_color + 3 ] = c2.r;
+			colorArray[ offset_color + 4 ] = c2.g;
+			colorArray[ offset_color + 5 ] = c2.b;
+
+			colorArray[ offset_color + 6 ] = c3.r;
+			colorArray[ offset_color + 7 ] = c3.g;
+			colorArray[ offset_color + 8 ] = c3.b;
+
+			colorArray[ offset_color + 9 ]  = c4.r;
+			colorArray[ offset_color + 10 ] = c4.g;
+			colorArray[ offset_color + 11 ] = c4.b;
+
+			offset_color += 12;
+
+		}
+
+		if ( offset_color > 0 ) {
+			
+			renderer.setDynamicArrayBuffer( geometryGroup.__webglColorBuffer, colorArray);
+
+		}
+
+	}
+
+	if ( dirtyTangents && geometry.hasTangents ) {
+
+		for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+			face = obj_faces[ chunk_faces3[ f ]	];
+
+			vertexTangents = face.vertexTangents;
+
+			t1 = vertexTangents[ 0 ];
+			t2 = vertexTangents[ 1 ];
+			t3 = vertexTangents[ 2 ];
+
+			tangentArray[ offset_tangent ]     = t1.x;
+			tangentArray[ offset_tangent + 1 ] = t1.y;
+			tangentArray[ offset_tangent + 2 ] = t1.z;
+			tangentArray[ offset_tangent + 3 ] = t1.w;
+
+			tangentArray[ offset_tangent + 4 ] = t2.x;
+			tangentArray[ offset_tangent + 5 ] = t2.y;
+			tangentArray[ offset_tangent + 6 ] = t2.z;
+			tangentArray[ offset_tangent + 7 ] = t2.w;
+
+			tangentArray[ offset_tangent + 8 ]  = t3.x;
+			tangentArray[ offset_tangent + 9 ]  = t3.y;
+			tangentArray[ offset_tangent + 10 ] = t3.z;
+			tangentArray[ offset_tangent + 11 ] = t3.w;
+
+			offset_tangent += 12;
+
+		}
+
+		for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+			face = obj_faces[ chunk_faces4[ f ] ];
+
+			vertexTangents = face.vertexTangents;
+
+			t1 = vertexTangents[ 0 ];
+			t2 = vertexTangents[ 1 ];
+			t3 = vertexTangents[ 2 ];
+			t4 = vertexTangents[ 3 ];
+
+			tangentArray[ offset_tangent ]     = t1.x;
+			tangentArray[ offset_tangent + 1 ] = t1.y;
+			tangentArray[ offset_tangent + 2 ] = t1.z;
+			tangentArray[ offset_tangent + 3 ] = t1.w;
+
+			tangentArray[ offset_tangent + 4 ] = t2.x;
+			tangentArray[ offset_tangent + 5 ] = t2.y;
+			tangentArray[ offset_tangent + 6 ] = t2.z;
+			tangentArray[ offset_tangent + 7 ] = t2.w;
+
+			tangentArray[ offset_tangent + 8 ]  = t3.x;
+			tangentArray[ offset_tangent + 9 ]  = t3.y;
+			tangentArray[ offset_tangent + 10 ] = t3.z;
+			tangentArray[ offset_tangent + 11 ] = t3.w;
+
+			tangentArray[ offset_tangent + 12 ] = t4.x;
+			tangentArray[ offset_tangent + 13 ] = t4.y;
+			tangentArray[ offset_tangent + 14 ] = t4.z;
+			tangentArray[ offset_tangent + 15 ] = t4.w;
+
+			offset_tangent += 16;
+
+		}
+		
+		renderer.setDynamicArrayBuffer( geometryGroup.__webglTangentBuffer, tangentArray);
+
+	}
+
+	if ( dirtyNormals && normalType ) {
+
+		for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+			face = obj_faces[ chunk_faces3[ f ]	];
+
+			vertexNormals = face.vertexNormals;
+			faceNormal = face.normal;
+
+			if ( vertexNormals.length === 3 && needsSmoothNormals ) {
+
+				for ( i = 0; i < 3; i ++ ) {
+
+					vn = vertexNormals[ i ];
+
+					normalArray[ offset_normal ]     = vn.x;
+					normalArray[ offset_normal + 1 ] = vn.y;
+					normalArray[ offset_normal + 2 ] = vn.z;
+
+					offset_normal += 3;
+
+				}
+
+			} else {
+
+				for ( i = 0; i < 3; i ++ ) {
+
+					normalArray[ offset_normal ]     = faceNormal.x;
+					normalArray[ offset_normal + 1 ] = faceNormal.y;
+					normalArray[ offset_normal + 2 ] = faceNormal.z;
+
+					offset_normal += 3;
+
+				}
+
+			}
+
+		}
+
+		for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+			face = obj_faces[ chunk_faces4[ f ] ];
+
+			vertexNormals = face.vertexNormals;
+			faceNormal = face.normal;
+
+			if ( vertexNormals.length === 4 && needsSmoothNormals ) {
+
+				for ( i = 0; i < 4; i ++ ) {
+
+					vn = vertexNormals[ i ];
+
+					normalArray[ offset_normal ]     = vn.x;
+					normalArray[ offset_normal + 1 ] = vn.y;
+					normalArray[ offset_normal + 2 ] = vn.z;
+
+					offset_normal += 3;
+
+				}
+
+			} else {
+
+				for ( i = 0; i < 4; i ++ ) {
+
+					normalArray[ offset_normal ]     = faceNormal.x;
+					normalArray[ offset_normal + 1 ] = faceNormal.y;
+					normalArray[ offset_normal + 2 ] = faceNormal.z;
+
+					offset_normal += 3;
+
+				}
+
+			}
+
+		}
+		
+		renderer.setDynamicArrayBuffer( geometryGroup.__webglNormalBuffer, normalArray);
+		
+	}
+
+	if ( dirtyUvs && obj_uvs && uvType ) {
+
+		for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+			fi = chunk_faces3[ f ];
+
+			uv = obj_uvs[ fi ];
+
+			if ( uv === undefined ) continue;
+
+			for ( i = 0; i < 3; i ++ ) {
+
+				uvi = uv[ i ];
+
+				uvArray[ offset_uv ]     = uvi.x;
+				uvArray[ offset_uv + 1 ] = uvi.y;
+
+				offset_uv += 2;
+
+			}
+
+		}
+
+		for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+			fi = chunk_faces4[ f ];
+
+			uv = obj_uvs[ fi ];
+
+			if ( uv === undefined ) continue;
+
+			for ( i = 0; i < 4; i ++ ) {
+
+				uvi = uv[ i ];
+
+				uvArray[ offset_uv ]     = uvi.x;
+				uvArray[ offset_uv + 1 ] = uvi.y;
+
+				offset_uv += 2;
+
+			}
+
+		}
+
+		if ( offset_uv > 0 ) {
+			
+			renderer.setDynamicArrayBuffer( geometryGroup.__webglUVBuffer, uvArray);
+
+		}
+
+	}
+
+	if ( dirtyUvs && obj_uvs2 && uvType ) {
+
+		for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+			fi = chunk_faces3[ f ];
+
+			uv2 = obj_uvs2[ fi ];
+
+			if ( uv2 === undefined ) continue;
+
+			for ( i = 0; i < 3; i ++ ) {
+
+				uv2i = uv2[ i ];
+
+				uv2Array[ offset_uv2 ]     = uv2i.x;
+				uv2Array[ offset_uv2 + 1 ] = uv2i.y;
+
+				offset_uv2 += 2;
+
+			}
+
+		}
+
+		for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+			fi = chunk_faces4[ f ];
+
+			uv2 = obj_uvs2[ fi ];
+
+			if ( uv2 === undefined ) continue;
+
+			for ( i = 0; i < 4; i ++ ) {
+
+				uv2i = uv2[ i ];
+
+				uv2Array[ offset_uv2 ]     = uv2i.x;
+				uv2Array[ offset_uv2 + 1 ] = uv2i.y;
+
+				offset_uv2 += 2;
+
+			}
+
+		}
+
+		if ( offset_uv2 > 0 ) {
+
+			renderer.setDynamicArrayBuffer( geometryGroup.__webglUV2Buffer, uv2Array);
+
+		}
+
+	}
+
+	if ( dirtyElements ) {
+
+		for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+			faceArray[ offset_face ] 	 = vertexIndex;
+			faceArray[ offset_face + 1 ] = vertexIndex + 1;
+			faceArray[ offset_face + 2 ] = vertexIndex + 2;
+
+			offset_face += 3;
+
+			lineArray[ offset_line ]     = vertexIndex;
+			lineArray[ offset_line + 1 ] = vertexIndex + 1;
+
+			lineArray[ offset_line + 2 ] = vertexIndex;
+			lineArray[ offset_line + 3 ] = vertexIndex + 2;
+
+			lineArray[ offset_line + 4 ] = vertexIndex + 1;
+			lineArray[ offset_line + 5 ] = vertexIndex + 2;
+
+			offset_line += 6;
+
+			vertexIndex += 3;
+
+		}
+
+		for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+			faceArray[ offset_face ]     = vertexIndex;
+			faceArray[ offset_face + 1 ] = vertexIndex + 1;
+			faceArray[ offset_face + 2 ] = vertexIndex + 3;
+
+			faceArray[ offset_face + 3 ] = vertexIndex + 1;
+			faceArray[ offset_face + 4 ] = vertexIndex + 2;
+			faceArray[ offset_face + 5 ] = vertexIndex + 3;
+
+			offset_face += 6;
+
+			lineArray[ offset_line ]     = vertexIndex;
+			lineArray[ offset_line + 1 ] = vertexIndex + 1;
+
+			lineArray[ offset_line + 2 ] = vertexIndex;
+			lineArray[ offset_line + 3 ] = vertexIndex + 3;
+
+			lineArray[ offset_line + 4 ] = vertexIndex + 1;
+			lineArray[ offset_line + 5 ] = vertexIndex + 2;
+
+			lineArray[ offset_line + 6 ] = vertexIndex + 2;
+			lineArray[ offset_line + 7 ] = vertexIndex + 3;
+
+			offset_line += 8;
+
+			vertexIndex += 4;
+
+		}
+
+		renderer.setDynamicIndexBuffer( geometryGroup.__webglFaceBuffer, faceArray);
+		renderer.setDynamicIndexBuffer( geometryGroup.__webglLineBuffer, lineArray);
+
+	}
+
+	if ( customAttributes ) {
+
+		for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
+
+			customAttribute = customAttributes[ i ];
+
+			if ( ! customAttribute.__original.needsUpdate ) continue;
+
+			offset_custom = 0;
+			offset_customSrc = 0;
+
+			if ( customAttribute.size === 1 ) {
+
+				if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) {
+
+					for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+						face = obj_faces[ chunk_faces3[ f ]	];
+
+						customAttribute.array[ offset_custom ] 	   = customAttribute.value[ face.a ];
+						customAttribute.array[ offset_custom + 1 ] = customAttribute.value[ face.b ];
+						customAttribute.array[ offset_custom + 2 ] = customAttribute.value[ face.c ];
+
+						offset_custom += 3;
+
+					}
+
+					for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+						face = obj_faces[ chunk_faces4[ f ] ];
+
+						customAttribute.array[ offset_custom ] 	   = customAttribute.value[ face.a ];
+						customAttribute.array[ offset_custom + 1 ] = customAttribute.value[ face.b ];
+						customAttribute.array[ offset_custom + 2 ] = customAttribute.value[ face.c ];
+						customAttribute.array[ offset_custom + 3 ] = customAttribute.value[ face.d ];
+
+						offset_custom += 4;
+
+					}
+
+				} else if ( customAttribute.boundTo === "faces" ) {
+
+					for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+						value = customAttribute.value[ chunk_faces3[ f ] ];
+
+						customAttribute.array[ offset_custom ] 	   = value;
+						customAttribute.array[ offset_custom + 1 ] = value;
+						customAttribute.array[ offset_custom + 2 ] = value;
+
+						offset_custom += 3;
+
+					}
+
+					for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+						value = customAttribute.value[ chunk_faces4[ f ] ];
+
+						customAttribute.array[ offset_custom ] 	   = value;
+						customAttribute.array[ offset_custom + 1 ] = value;
+						customAttribute.array[ offset_custom + 2 ] = value;
+						customAttribute.array[ offset_custom + 3 ] = value;
+
+						offset_custom += 4;
+
+					}
+
+				}
+
+			} else if ( customAttribute.size === 2 ) {
+
+				if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) {
+
+					for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+						face = obj_faces[ chunk_faces3[ f ]	];
+
+						v1 = customAttribute.value[ face.a ];
+						v2 = customAttribute.value[ face.b ];
+						v3 = customAttribute.value[ face.c ];
+
+						customAttribute.array[ offset_custom ] 	   = v1.x;
+						customAttribute.array[ offset_custom + 1 ] = v1.y;
+
+						customAttribute.array[ offset_custom + 2 ] = v2.x;
+						customAttribute.array[ offset_custom + 3 ] = v2.y;
+
+						customAttribute.array[ offset_custom + 4 ] = v3.x;
+						customAttribute.array[ offset_custom + 5 ] = v3.y;
+
+						offset_custom += 6;
+
+					}
+
+					for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+						face = obj_faces[ chunk_faces4[ f ] ];
+
+						v1 = customAttribute.value[ face.a ];
+						v2 = customAttribute.value[ face.b ];
+						v3 = customAttribute.value[ face.c ];
+						v4 = customAttribute.value[ face.d ];
+
+						customAttribute.array[ offset_custom ] 	   = v1.x;
+						customAttribute.array[ offset_custom + 1 ] = v1.y;
+
+						customAttribute.array[ offset_custom + 2 ] = v2.x;
+						customAttribute.array[ offset_custom + 3 ] = v2.y;
+
+						customAttribute.array[ offset_custom + 4 ] = v3.x;
+						customAttribute.array[ offset_custom + 5 ] = v3.y;
+
+						customAttribute.array[ offset_custom + 6 ] = v4.x;
+						customAttribute.array[ offset_custom + 7 ] = v4.y;
+
+						offset_custom += 8;
+
+					}
+
+				} else if ( customAttribute.boundTo === "faces" ) {
+
+					for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+						value = customAttribute.value[ chunk_faces3[ f ] ];
+
+						v1 = value;
+						v2 = value;
+						v3 = value;
+
+						customAttribute.array[ offset_custom ] 	   = v1.x;
+						customAttribute.array[ offset_custom + 1 ] = v1.y;
+
+						customAttribute.array[ offset_custom + 2 ] = v2.x;
+						customAttribute.array[ offset_custom + 3 ] = v2.y;
+
+						customAttribute.array[ offset_custom + 4 ] = v3.x;
+						customAttribute.array[ offset_custom + 5 ] = v3.y;
+
+						offset_custom += 6;
+
+					}
+
+					for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+						value = customAttribute.value[ chunk_faces4[ f ] ];
+
+						v1 = value;
+						v2 = value;
+						v3 = value;
+						v4 = value;
+
+						customAttribute.array[ offset_custom ] 	   = v1.x;
+						customAttribute.array[ offset_custom + 1 ] = v1.y;
+
+						customAttribute.array[ offset_custom + 2 ] = v2.x;
+						customAttribute.array[ offset_custom + 3 ] = v2.y;
+
+						customAttribute.array[ offset_custom + 4 ] = v3.x;
+						customAttribute.array[ offset_custom + 5 ] = v3.y;
+
+						customAttribute.array[ offset_custom + 6 ] = v4.x;
+						customAttribute.array[ offset_custom + 7 ] = v4.y;
+
+						offset_custom += 8;
+
+					}
+
+				}
+
+			} else if ( customAttribute.size === 3 ) {
+
+				var pp;
+
+				if ( customAttribute.type === "c" ) {
+
+					pp = [ "r", "g", "b" ];
+
+				} else {
+
+					pp = [ "x", "y", "z" ];
+
+				}
+
+				if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) {
+
+					for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+						face = obj_faces[ chunk_faces3[ f ]	];
+
+						v1 = customAttribute.value[ face.a ];
+						v2 = customAttribute.value[ face.b ];
+						v3 = customAttribute.value[ face.c ];
+
+						customAttribute.array[ offset_custom ] 	   = v1[ pp[ 0 ] ];
+						customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ];
+						customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ];
+
+						customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ];
+						customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ];
+						customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ];
+
+						customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ];
+						customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ];
+						customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ];
+
+						offset_custom += 9;
+
+					}
+
+					for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+						face = obj_faces[ chunk_faces4[ f ] ];
+
+						v1 = customAttribute.value[ face.a ];
+						v2 = customAttribute.value[ face.b ];
+						v3 = customAttribute.value[ face.c ];
+						v4 = customAttribute.value[ face.d ];
+
+						customAttribute.array[ offset_custom  ] 	= v1[ pp[ 0 ] ];
+						customAttribute.array[ offset_custom + 1  ] = v1[ pp[ 1 ] ];
+						customAttribute.array[ offset_custom + 2  ] = v1[ pp[ 2 ] ];
+
+						customAttribute.array[ offset_custom + 3  ] = v2[ pp[ 0 ] ];
+						customAttribute.array[ offset_custom + 4  ] = v2[ pp[ 1 ] ];
+						customAttribute.array[ offset_custom + 5  ] = v2[ pp[ 2 ] ];
+
+						customAttribute.array[ offset_custom + 6  ] = v3[ pp[ 0 ] ];
+						customAttribute.array[ offset_custom + 7  ] = v3[ pp[ 1 ] ];
+						customAttribute.array[ offset_custom + 8  ] = v3[ pp[ 2 ] ];
+
+						customAttribute.array[ offset_custom + 9  ] = v4[ pp[ 0 ] ];
+						customAttribute.array[ offset_custom + 10 ] = v4[ pp[ 1 ] ];
+						customAttribute.array[ offset_custom + 11 ] = v4[ pp[ 2 ] ];
+
+						offset_custom += 12;
+
+					}
+
+				} else if ( customAttribute.boundTo === "faces" ) {
+
+					for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+						value = customAttribute.value[ chunk_faces3[ f ] ];
+
+						v1 = value;
+						v2 = value;
+						v3 = value;
+
+						customAttribute.array[ offset_custom ] 	   = v1[ pp[ 0 ] ];
+						customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ];
+						customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ];
+
+						customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ];
+						customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ];
+						customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ];
+
+						customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ];
+						customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ];
+						customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ];
+
+						offset_custom += 9;
+
+					}
+
+					for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+						value = customAttribute.value[ chunk_faces4[ f ] ];
+
+						v1 = value;
+						v2 = value;
+						v3 = value;
+						v4 = value;
+
+						customAttribute.array[ offset_custom  ] 	= v1[ pp[ 0 ] ];
+						customAttribute.array[ offset_custom + 1  ] = v1[ pp[ 1 ] ];
+						customAttribute.array[ offset_custom + 2  ] = v1[ pp[ 2 ] ];
+
+						customAttribute.array[ offset_custom + 3  ] = v2[ pp[ 0 ] ];
+						customAttribute.array[ offset_custom + 4  ] = v2[ pp[ 1 ] ];
+						customAttribute.array[ offset_custom + 5  ] = v2[ pp[ 2 ] ];
+
+						customAttribute.array[ offset_custom + 6  ] = v3[ pp[ 0 ] ];
+						customAttribute.array[ offset_custom + 7  ] = v3[ pp[ 1 ] ];
+						customAttribute.array[ offset_custom + 8  ] = v3[ pp[ 2 ] ];
+
+						customAttribute.array[ offset_custom + 9  ] = v4[ pp[ 0 ] ];
+						customAttribute.array[ offset_custom + 10 ] = v4[ pp[ 1 ] ];
+						customAttribute.array[ offset_custom + 11 ] = v4[ pp[ 2 ] ];
+
+						offset_custom += 12;
+
+					}
+
+				} else if ( customAttribute.boundTo === "faceVertices" ) {
+
+					for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+						value = customAttribute.value[ chunk_faces3[ f ] ];
+
+						v1 = value[ 0 ];
+						v2 = value[ 1 ];
+						v3 = value[ 2 ];
+
+						customAttribute.array[ offset_custom ] 	   = v1[ pp[ 0 ] ];
+						customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ];
+						customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ];
+
+						customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ];
+						customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ];
+						customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ];
+
+						customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ];
+						customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ];
+						customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ];
+
+						offset_custom += 9;
+
+					}
+
+					for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+						value = customAttribute.value[ chunk_faces4[ f ] ];
+
+						v1 = value[ 0 ];
+						v2 = value[ 1 ];
+						v3 = value[ 2 ];
+						v4 = value[ 3 ];
+
+						customAttribute.array[ offset_custom  ] 	= v1[ pp[ 0 ] ];
+						customAttribute.array[ offset_custom + 1  ] = v1[ pp[ 1 ] ];
+						customAttribute.array[ offset_custom + 2  ] = v1[ pp[ 2 ] ];
+
+						customAttribute.array[ offset_custom + 3  ] = v2[ pp[ 0 ] ];
+						customAttribute.array[ offset_custom + 4  ] = v2[ pp[ 1 ] ];
+						customAttribute.array[ offset_custom + 5  ] = v2[ pp[ 2 ] ];
+
+						customAttribute.array[ offset_custom + 6  ] = v3[ pp[ 0 ] ];
+						customAttribute.array[ offset_custom + 7  ] = v3[ pp[ 1 ] ];
+						customAttribute.array[ offset_custom + 8  ] = v3[ pp[ 2 ] ];
+
+						customAttribute.array[ offset_custom + 9  ] = v4[ pp[ 0 ] ];
+						customAttribute.array[ offset_custom + 10 ] = v4[ pp[ 1 ] ];
+						customAttribute.array[ offset_custom + 11 ] = v4[ pp[ 2 ] ];
+
+						offset_custom += 12;
+
+					}
+
+				}
+
+			} else if ( customAttribute.size === 4 ) {
+
+				if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) {
+
+					for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+						face = obj_faces[ chunk_faces3[ f ]	];
+
+						v1 = customAttribute.value[ face.a ];
+						v2 = customAttribute.value[ face.b ];
+						v3 = customAttribute.value[ face.c ];
+
+						customAttribute.array[ offset_custom  ] 	= v1.x;
+						customAttribute.array[ offset_custom + 1  ] = v1.y;
+						customAttribute.array[ offset_custom + 2  ] = v1.z;
+						customAttribute.array[ offset_custom + 3  ] = v1.w;
+
+						customAttribute.array[ offset_custom + 4  ] = v2.x;
+						customAttribute.array[ offset_custom + 5  ] = v2.y;
+						customAttribute.array[ offset_custom + 6  ] = v2.z;
+						customAttribute.array[ offset_custom + 7  ] = v2.w;
+
+						customAttribute.array[ offset_custom + 8  ] = v3.x;
+						customAttribute.array[ offset_custom + 9  ] = v3.y;
+						customAttribute.array[ offset_custom + 10 ] = v3.z;
+						customAttribute.array[ offset_custom + 11 ] = v3.w;
+
+						offset_custom += 12;
+
+					}
+
+					for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+						face = obj_faces[ chunk_faces4[ f ] ];
+
+						v1 = customAttribute.value[ face.a ];
+						v2 = customAttribute.value[ face.b ];
+						v3 = customAttribute.value[ face.c ];
+						v4 = customAttribute.value[ face.d ];
+
+						customAttribute.array[ offset_custom  ] 	= v1.x;
+						customAttribute.array[ offset_custom + 1  ] = v1.y;
+						customAttribute.array[ offset_custom + 2  ] = v1.z;
+						customAttribute.array[ offset_custom + 3  ] = v1.w;
+
+						customAttribute.array[ offset_custom + 4  ] = v2.x;
+						customAttribute.array[ offset_custom + 5  ] = v2.y;
+						customAttribute.array[ offset_custom + 6  ] = v2.z;
+						customAttribute.array[ offset_custom + 7  ] = v2.w;
+
+						customAttribute.array[ offset_custom + 8  ] = v3.x;
+						customAttribute.array[ offset_custom + 9  ] = v3.y;
+						customAttribute.array[ offset_custom + 10 ] = v3.z;
+						customAttribute.array[ offset_custom + 11 ] = v3.w;
+
+						customAttribute.array[ offset_custom + 12 ] = v4.x;
+						customAttribute.array[ offset_custom + 13 ] = v4.y;
+						customAttribute.array[ offset_custom + 14 ] = v4.z;
+						customAttribute.array[ offset_custom + 15 ] = v4.w;
+
+						offset_custom += 16;
+
+					}
+
+				} else if ( customAttribute.boundTo === "faces" ) {
+
+					for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+						value = customAttribute.value[ chunk_faces3[ f ] ];
+
+						v1 = value;
+						v2 = value;
+						v3 = value;
+
+						customAttribute.array[ offset_custom  ] 	= v1.x;
+						customAttribute.array[ offset_custom + 1  ] = v1.y;
+						customAttribute.array[ offset_custom + 2  ] = v1.z;
+						customAttribute.array[ offset_custom + 3  ] = v1.w;
+
+						customAttribute.array[ offset_custom + 4  ] = v2.x;
+						customAttribute.array[ offset_custom + 5  ] = v2.y;
+						customAttribute.array[ offset_custom + 6  ] = v2.z;
+						customAttribute.array[ offset_custom + 7  ] = v2.w;
+
+						customAttribute.array[ offset_custom + 8  ] = v3.x;
+						customAttribute.array[ offset_custom + 9  ] = v3.y;
+						customAttribute.array[ offset_custom + 10 ] = v3.z;
+						customAttribute.array[ offset_custom + 11 ] = v3.w;
+
+						offset_custom += 12;
+
+					}
+
+					for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+						value = customAttribute.value[ chunk_faces4[ f ] ];
+
+						v1 = value;
+						v2 = value;
+						v3 = value;
+						v4 = value;
+
+						customAttribute.array[ offset_custom  ] 	= v1.x;
+						customAttribute.array[ offset_custom + 1  ] = v1.y;
+						customAttribute.array[ offset_custom + 2  ] = v1.z;
+						customAttribute.array[ offset_custom + 3  ] = v1.w;
+
+						customAttribute.array[ offset_custom + 4  ] = v2.x;
+						customAttribute.array[ offset_custom + 5  ] = v2.y;
+						customAttribute.array[ offset_custom + 6  ] = v2.z;
+						customAttribute.array[ offset_custom + 7  ] = v2.w;
+
+						customAttribute.array[ offset_custom + 8  ] = v3.x;
+						customAttribute.array[ offset_custom + 9  ] = v3.y;
+						customAttribute.array[ offset_custom + 10 ] = v3.z;
+						customAttribute.array[ offset_custom + 11 ] = v3.w;
+
+						customAttribute.array[ offset_custom + 12 ] = v4.x;
+						customAttribute.array[ offset_custom + 13 ] = v4.y;
+						customAttribute.array[ offset_custom + 14 ] = v4.z;
+						customAttribute.array[ offset_custom + 15 ] = v4.w;
+
+						offset_custom += 16;
+
+					}
+
+				} else if ( customAttribute.boundTo === "faceVertices" ) {
+
+					for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) {
+
+						value = customAttribute.value[ chunk_faces3[ f ] ];
+
+						v1 = value[ 0 ];
+						v2 = value[ 1 ];
+						v3 = value[ 2 ];
+
+						customAttribute.array[ offset_custom  ] 	= v1.x;
+						customAttribute.array[ offset_custom + 1  ] = v1.y;
+						customAttribute.array[ offset_custom + 2  ] = v1.z;
+						customAttribute.array[ offset_custom + 3  ] = v1.w;
+
+						customAttribute.array[ offset_custom + 4  ] = v2.x;
+						customAttribute.array[ offset_custom + 5  ] = v2.y;
+						customAttribute.array[ offset_custom + 6  ] = v2.z;
+						customAttribute.array[ offset_custom + 7  ] = v2.w;
+
+						customAttribute.array[ offset_custom + 8  ] = v3.x;
+						customAttribute.array[ offset_custom + 9  ] = v3.y;
+						customAttribute.array[ offset_custom + 10 ] = v3.z;
+						customAttribute.array[ offset_custom + 11 ] = v3.w;
+
+						offset_custom += 12;
+
+					}
+
+					for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) {
+
+						value = customAttribute.value[ chunk_faces4[ f ] ];
+
+						v1 = value[ 0 ];
+						v2 = value[ 1 ];
+						v3 = value[ 2 ];
+						v4 = value[ 3 ];
+
+						customAttribute.array[ offset_custom  ] 	= v1.x;
+						customAttribute.array[ offset_custom + 1  ] = v1.y;
+						customAttribute.array[ offset_custom + 2  ] = v1.z;
+						customAttribute.array[ offset_custom + 3  ] = v1.w;
+
+						customAttribute.array[ offset_custom + 4  ] = v2.x;
+						customAttribute.array[ offset_custom + 5  ] = v2.y;
+						customAttribute.array[ offset_custom + 6  ] = v2.z;
+						customAttribute.array[ offset_custom + 7  ] = v2.w;
+
+						customAttribute.array[ offset_custom + 8  ] = v3.x;
+						customAttribute.array[ offset_custom + 9  ] = v3.y;
+						customAttribute.array[ offset_custom + 10 ] = v3.z;
+						customAttribute.array[ offset_custom + 11 ] = v3.w;
+
+						customAttribute.array[ offset_custom + 12 ] = v4.x;
+						customAttribute.array[ offset_custom + 13 ] = v4.y;
+						customAttribute.array[ offset_custom + 14 ] = v4.z;
+						customAttribute.array[ offset_custom + 15 ] = v4.w;
+
+						offset_custom += 16;
+
+					}
+
+				}
+
+			}
+
+			renderer.setDynamicArrayBuffer( customAttribute.buffer, customAttribute.array);
+
+		}
+
+	}
+
+	if ( dispose ) {
+
+		delete geometryGroup.__inittedArrays;
+		delete geometryGroup.__colorArray;
+		delete geometryGroup.__normalArray;
+		delete geometryGroup.__tangentArray;
+		delete geometryGroup.__uvArray;
+		delete geometryGroup.__uv2Array;
+		delete geometryGroup.__faceArray;
+		delete geometryGroup.__vertexArray;
+		delete geometryGroup.__lineArray;
+		delete geometryGroup.__skinIndexArray;
+		delete geometryGroup.__skinWeightArray;
+
+	}
+
+};

+ 124 - 0
src/renderers/webgl/objects/Object3DRenderer.js

@@ -0,0 +1,124 @@
+
+
+THREE.WebGLRenderer2.Object3DRenderer = function(lowlevelrenderer, info){
+	this.renderer = lowlevelrenderer;
+	this.info = info;
+};
+
+THREE.WebGLRenderer2.Object3DRenderer.prototype.getBufferMaterial = function( object, geometryGroup ) {
+
+	return object.material instanceof THREE.MeshFaceMaterial
+		? object.material.materials[ geometryGroup.materialIndex ]
+		: object.material;
+
+};
+
+THREE.WebGLRenderer2.Object3DRenderer.prototype.bufferGuessUVType = function( material ) {
+
+	// material must use some texture to require uvs
+
+	if ( material.map || material.lightMap || material.bumpMap || material.normalMap || material.specularMap || material instanceof THREE.ShaderMaterial ) {
+
+		return true;
+
+	}
+
+	return false;
+
+};
+
+THREE.WebGLRenderer2.Object3DRenderer.prototype.bufferGuessNormalType = function ( material ) {
+
+	// only MeshBasicMaterial and MeshDepthMaterial don't need normals
+
+	if ( ( material instanceof THREE.MeshBasicMaterial && !material.envMap ) || material instanceof THREE.MeshDepthMaterial ) {
+
+		return false;
+
+	}
+
+	if ( this.materialNeedsSmoothNormals( material ) ) {
+
+		return THREE.SmoothShading;
+
+	} else {
+
+		return THREE.FlatShading;
+
+	}
+
+};
+
+THREE.WebGLRenderer2.Object3DRenderer.prototype.materialNeedsSmoothNormals = function ( material ) {
+
+	return material && material.shading !== undefined && material.shading === THREE.SmoothShading;
+
+};
+
+
+THREE.WebGLRenderer2.Object3DRenderer.prototype.bufferGuessVertexColorType = function ( material ) {
+
+	if ( material.vertexColors ) {
+
+		return material.vertexColors;
+
+	}
+
+	return false;
+
+};
+
+
+THREE.WebGLRenderer2.Object3DRenderer.prototype.initCustomAttributes = function( geometry, object ) {
+
+	var nvertices = geometry.vertices.length;
+
+	var material = object.material;
+
+	if ( material.attributes ) {
+
+		if ( geometry.__webglCustomAttributesList === undefined ) {
+
+			geometry.__webglCustomAttributesList = [];
+
+		}
+
+		for ( var a in material.attributes ) {
+
+			var attribute = material.attributes[ a ];
+
+			if ( !attribute.__webglInitialized || attribute.createUniqueBuffers ) {
+
+				attribute.__webglInitialized = true;
+
+				var size = 1;		// "f" and "i"
+
+				if ( attribute.type === "v2" ) size = 2;
+				else if ( attribute.type === "v3" ) size = 3;
+				else if ( attribute.type === "v4" ) size = 4;
+				else if ( attribute.type === "c"  ) size = 3;
+
+				attribute.size = size;
+
+				attribute.array = new Float32Array( nvertices * size );
+
+				attribute.buffer = this.renderer.createBuffer();
+				attribute.buffer.belongsToAttribute = a;
+
+				attribute.needsUpdate = true;
+
+			}
+
+			geometry.__webglCustomAttributesList.push( attribute );
+
+		}
+
+	}
+
+};
+
+THREE.WebGLRenderer2.Object3DRenderer.prototype.numericalSort = function( a, b ) {
+
+	return b[ 0 ] - a[ 0 ];
+
+};

+ 358 - 0
src/renderers/webgl/objects/ParticleRenderer.js

@@ -0,0 +1,358 @@
+
+
+THREE.WebGLRenderer2.ParticleRenderer = function(lowlevelrenderer, info){
+	THREE.WebGLRenderer2.Object3DObjectRenderer.call( this, lowlevelrenderer, info );
+};
+
+THREE.WebGLRenderer2.ParticleRenderer.prototype = Object.create( THREE.WebGLRenderer2.Object3DObjectRenderer.prototype );
+
+THREE.WebGLRenderer2.ParticleRenderer.prototype.createBuffers = function( geometry ) {
+	
+	var renderer = this.renderer;
+	geometry.__webglVertexBuffer = renderer.createBuffer();
+	geometry.__webglColorBuffer = renderer.createBuffer();
+
+	this.info.memory.geometries ++;
+};
+
+THREE.WebGLRenderer2.ParticleRenderer.prototype.initBuffers = function( geometry, object ) {
+
+	var nvertices = geometry.vertices.length;
+
+	geometry.__vertexArray = new Float32Array( nvertices * 3 );
+	geometry.__colorArray = new Float32Array( nvertices * 3 );
+
+	geometry.__sortArray = [];
+
+	geometry.__webglParticleCount = nvertices;
+
+	this.initCustomAttributes ( geometry, object );
+
+};
+
+
+THREE.WebGLRenderer2.ParticleRenderer.prototype.setBuffers = function( geometry, object , projectionScreenMatrix) {
+
+	var renderer = this.renderer;
+	var v, c, vertex, offset, index, color,
+
+	vertices = geometry.vertices,
+	vl = vertices.length,
+
+	colors = geometry.colors,
+	cl = colors.length,
+
+	vertexArray = geometry.__vertexArray,
+	colorArray = geometry.__colorArray,
+
+	sortArray = geometry.__sortArray,
+
+	dirtyVertices = geometry.verticesNeedUpdate,
+	dirtyElements = geometry.elementsNeedUpdate,
+	dirtyColors = geometry.colorsNeedUpdate,
+
+	customAttributes = geometry.__webglCustomAttributesList,
+	i, il,
+	a, ca, cal, value,
+	customAttribute;
+	
+	var _projScreenMatrixPS = THREE.WebGLRenderer2.ParticleRenderer._m1,
+		_vector3 = THREE.WebGLRenderer2.ParticleRenderer._v1;
+
+	if ( object.sortParticles ) {
+
+		_projScreenMatrixPS.copy( projectionScreenMatrix );
+		_projScreenMatrixPS.multiplySelf( object.matrixWorld );
+
+		for ( v = 0; v < vl; v ++ ) {
+
+			vertex = vertices[ v ];
+
+			_vector3.copy( vertex );
+			_vector3.applyMatrix4(_projScreenMatrixPS);
+
+			sortArray[ v ] = [ _vector3.z, v ];
+
+		}
+
+		sortArray.sort( this.numericalSort );
+
+		for ( v = 0; v < vl; v ++ ) {
+
+			vertex = vertices[ sortArray[v][1] ];
+
+			offset = v * 3;
+
+			vertexArray[ offset ]     = vertex.x;
+			vertexArray[ offset + 1 ] = vertex.y;
+			vertexArray[ offset + 2 ] = vertex.z;
+
+		}
+
+		for ( c = 0; c < cl; c ++ ) {
+
+			offset = c * 3;
+
+			color = colors[ sortArray[c][1] ];
+
+			colorArray[ offset ]     = color.r;
+			colorArray[ offset + 1 ] = color.g;
+			colorArray[ offset + 2 ] = color.b;
+
+		}
+
+		if ( customAttributes ) {
+
+			for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
+
+				customAttribute = customAttributes[ i ];
+
+				if ( ! ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) ) continue;
+
+				offset = 0;
+
+				cal = customAttribute.value.length;
+
+				if ( customAttribute.size === 1 ) {
+
+					for ( ca = 0; ca < cal; ca ++ ) {
+
+						index = sortArray[ ca ][ 1 ];
+
+						customAttribute.array[ ca ] = customAttribute.value[ index ];
+
+					}
+
+				} else if ( customAttribute.size === 2 ) {
+
+					for ( ca = 0; ca < cal; ca ++ ) {
+
+						index = sortArray[ ca ][ 1 ];
+
+						value = customAttribute.value[ index ];
+
+						customAttribute.array[ offset ] 	= value.x;
+						customAttribute.array[ offset + 1 ] = value.y;
+
+						offset += 2;
+
+					}
+
+				} else if ( customAttribute.size === 3 ) {
+
+					if ( customAttribute.type === "c" ) {
+
+						for ( ca = 0; ca < cal; ca ++ ) {
+
+							index = sortArray[ ca ][ 1 ];
+
+							value = customAttribute.value[ index ];
+
+							customAttribute.array[ offset ]     = value.r;
+							customAttribute.array[ offset + 1 ] = value.g;
+							customAttribute.array[ offset + 2 ] = value.b;
+
+							offset += 3;
+
+						}
+
+					} else {
+
+						for ( ca = 0; ca < cal; ca ++ ) {
+
+							index = sortArray[ ca ][ 1 ];
+
+							value = customAttribute.value[ index ];
+
+							customAttribute.array[ offset ] 	= value.x;
+							customAttribute.array[ offset + 1 ] = value.y;
+							customAttribute.array[ offset + 2 ] = value.z;
+
+							offset += 3;
+
+						}
+
+					}
+
+				} else if ( customAttribute.size === 4 ) {
+
+					for ( ca = 0; ca < cal; ca ++ ) {
+
+						index = sortArray[ ca ][ 1 ];
+
+						value = customAttribute.value[ index ];
+
+						customAttribute.array[ offset ]      = value.x;
+						customAttribute.array[ offset + 1  ] = value.y;
+						customAttribute.array[ offset + 2  ] = value.z;
+						customAttribute.array[ offset + 3  ] = value.w;
+
+						offset += 4;
+
+					}
+
+				}
+
+			}
+
+		}
+
+	} else {
+
+		if ( dirtyVertices ) {
+
+			for ( v = 0; v < vl; v ++ ) {
+
+				vertex = vertices[ v ];
+
+				offset = v * 3;
+
+				vertexArray[ offset ]     = vertex.x;
+				vertexArray[ offset + 1 ] = vertex.y;
+				vertexArray[ offset + 2 ] = vertex.z;
+
+			}
+
+		}
+
+		if ( dirtyColors ) {
+
+			for ( c = 0; c < cl; c ++ ) {
+
+				color = colors[ c ];
+
+				offset = c * 3;
+
+				colorArray[ offset ]     = color.r;
+				colorArray[ offset + 1 ] = color.g;
+				colorArray[ offset + 2 ] = color.b;
+
+			}
+
+		}
+
+		if ( customAttributes ) {
+
+			for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
+
+				customAttribute = customAttributes[ i ];
+
+				if ( customAttribute.needsUpdate &&
+					 ( customAttribute.boundTo === undefined ||
+					   customAttribute.boundTo === "vertices") ) {
+
+					cal = customAttribute.value.length;
+
+					offset = 0;
+
+					if ( customAttribute.size === 1 ) {
+
+						for ( ca = 0; ca < cal; ca ++ ) {
+
+							customAttribute.array[ ca ] = customAttribute.value[ ca ];
+
+						}
+
+					} else if ( customAttribute.size === 2 ) {
+
+						for ( ca = 0; ca < cal; ca ++ ) {
+
+							value = customAttribute.value[ ca ];
+
+							customAttribute.array[ offset ] 	= value.x;
+							customAttribute.array[ offset + 1 ] = value.y;
+
+							offset += 2;
+
+						}
+
+					} else if ( customAttribute.size === 3 ) {
+
+						if ( customAttribute.type === "c" ) {
+
+							for ( ca = 0; ca < cal; ca ++ ) {
+
+								value = customAttribute.value[ ca ];
+
+								customAttribute.array[ offset ] 	= value.r;
+								customAttribute.array[ offset + 1 ] = value.g;
+								customAttribute.array[ offset + 2 ] = value.b;
+
+								offset += 3;
+
+							}
+
+						} else {
+
+							for ( ca = 0; ca < cal; ca ++ ) {
+
+								value = customAttribute.value[ ca ];
+
+								customAttribute.array[ offset ] 	= value.x;
+								customAttribute.array[ offset + 1 ] = value.y;
+								customAttribute.array[ offset + 2 ] = value.z;
+
+								offset += 3;
+
+							}
+
+						}
+
+					} else if ( customAttribute.size === 4 ) {
+
+						for ( ca = 0; ca < cal; ca ++ ) {
+
+							value = customAttribute.value[ ca ];
+
+							customAttribute.array[ offset ]      = value.x;
+							customAttribute.array[ offset + 1  ] = value.y;
+							customAttribute.array[ offset + 2  ] = value.z;
+							customAttribute.array[ offset + 3  ] = value.w;
+
+							offset += 4;
+
+						}
+
+					}
+
+				}
+
+			}
+
+		}
+
+	}
+
+	if ( dirtyVertices || object.sortParticles ) {
+		
+		renderer.setDynamicArrayBuffer(geometry.__webglVertexBuffer,vertexArray);
+
+	}
+
+	if ( dirtyColors || object.sortParticles ) {
+		
+		renderer.setDynamicArrayBuffer(geometry.__webglColorBuffer,colorArray);
+
+	}
+
+	if ( customAttributes ) {
+
+		for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
+
+			customAttribute = customAttributes[ i ];
+
+			if ( customAttribute.needsUpdate || object.sortParticles ) {
+		
+				renderer.setDynamicArrayBuffer(customAttribute.buffer,customAttribute.array);
+
+			}
+
+		}
+
+	}
+
+
+};
+
+THREE.WebGLRenderer2.ParticleRenderer._m1 = new THREE.Matrix4();
+THREE.WebGLRenderer2.ParticleRenderer._v1 = new THREE.Vector3();

+ 205 - 0
src/renderers/webgl/objects/RibbonRenderer.js

@@ -0,0 +1,205 @@
+
+
+THREE.WebGLRenderer2.RibbonRenderer = function(lowlevelrenderer, info){
+	THREE.WebGLRenderer2.Object3DObjectRenderer.call( this, lowlevelrenderer, info );
+};
+
+THREE.WebGLRenderer2.RibbonRenderer.prototype = Object.create( THREE.WebGLRenderer2.Object3DObjectRenderer.prototype );
+
+THREE.WebGLRenderer2.RibbonRenderer.prototype.createBuffers = function( geometry ) {
+	
+	var renderer = this.renderer;
+	geometry.__webglVertexBuffer = renderer.createBuffer();
+	geometry.__webglColorBuffer = renderer.createBuffer();
+	geometry.__webglNormalBuffer = renderer.createBuffer();
+
+	this.info.memory.geometries ++;
+};
+
+THREE.WebGLRenderer2.RibbonRenderer.prototype.initBuffers = function( geometry, object ) {
+	
+	var nvertices = geometry.vertices.length;
+	
+	geometry.__vertexArray = new Float32Array( nvertices * 3 );
+	geometry.__colorArray = new Float32Array( nvertices * 3 );
+	geometry.__normalArray = new Float32Array( nvertices * 3 );
+
+	geometry.__webglVertexCount = nvertices;
+
+	this.initCustomAttributes ( geometry, object );
+	
+};
+
+
+THREE.WebGLRenderer2.RibbonRenderer.prototype.setBuffers = function( geometry, object , projectionScreenMatrix) {
+	
+	var renderer = this.renderer;
+	var v, c, n, vertex, offset, color, normal,
+
+	i, il, ca, cal, customAttribute, value,
+
+	vertices = geometry.vertices,
+	colors = geometry.colors,
+	normals = geometry.normals,
+
+	vl = vertices.length,
+	cl = colors.length,
+	nl = normals.length,
+
+	vertexArray = geometry.__vertexArray,
+	colorArray = geometry.__colorArray,
+	normalArray = geometry.__normalArray,
+
+	dirtyVertices = geometry.verticesNeedUpdate,
+	dirtyColors = geometry.colorsNeedUpdate,
+	dirtyNormals = geometry.normalsNeedUpdate,
+
+	customAttributes = geometry.__webglCustomAttributesList;
+
+	if ( dirtyVertices ) {
+
+		for ( v = 0; v < vl; v ++ ) {
+
+			vertex = vertices[ v ];
+
+			offset = v * 3;
+
+			vertexArray[ offset ]     = vertex.x;
+			vertexArray[ offset + 1 ] = vertex.y;
+			vertexArray[ offset + 2 ] = vertex.z;
+
+		}
+
+		renderer.setDynamicArrayBuffer( geometry.__webglVertexBuffer,vertexArray);
+
+	}
+
+	if ( dirtyColors ) {
+
+		for ( c = 0; c < cl; c ++ ) {
+
+			color = colors[ c ];
+
+			offset = c * 3;
+
+			colorArray[ offset ]     = color.r;
+			colorArray[ offset + 1 ] = color.g;
+			colorArray[ offset + 2 ] = color.b;
+
+		}
+
+		renderer.setDynamicArrayBuffer( geometry.__webglColorBuffer, colorArray);
+
+	}
+
+	if ( dirtyNormals ) {
+
+		for ( n = 0; n < nl; n ++ ) {
+
+			normal = normals[ n ];
+
+			offset = n * 3;
+
+			normalArray[ offset ]     = normal.x;
+			normalArray[ offset + 1 ] = normal.y;
+			normalArray[ offset + 2 ] = normal.z;
+
+		}
+
+		renderer.setDynamicArrayBuffer( geometry.__webglNormalBuffer, normalArray);
+
+	}
+
+	if ( customAttributes ) {
+
+		for ( i = 0, il = customAttributes.length; i < il; i ++ ) {
+
+			customAttribute = customAttributes[ i ];
+
+			if ( customAttribute.needsUpdate &&
+				 ( customAttribute.boundTo === undefined ||
+				   customAttribute.boundTo === "vertices" ) ) {
+
+				offset = 0;
+
+				cal = customAttribute.value.length;
+
+				if ( customAttribute.size === 1 ) {
+
+					for ( ca = 0; ca < cal; ca ++ ) {
+
+						customAttribute.array[ ca ] = customAttribute.value[ ca ];
+
+					}
+
+				} else if ( customAttribute.size === 2 ) {
+
+					for ( ca = 0; ca < cal; ca ++ ) {
+
+						value = customAttribute.value[ ca ];
+
+						customAttribute.array[ offset ] 	= value.x;
+						customAttribute.array[ offset + 1 ] = value.y;
+
+						offset += 2;
+
+					}
+
+				} else if ( customAttribute.size === 3 ) {
+
+					if ( customAttribute.type === "c" ) {
+
+						for ( ca = 0; ca < cal; ca ++ ) {
+
+							value = customAttribute.value[ ca ];
+
+							customAttribute.array[ offset ] 	= value.r;
+							customAttribute.array[ offset + 1 ] = value.g;
+							customAttribute.array[ offset + 2 ] = value.b;
+
+							offset += 3;
+
+						}
+
+					} else {
+
+						for ( ca = 0; ca < cal; ca ++ ) {
+
+							value = customAttribute.value[ ca ];
+
+							customAttribute.array[ offset ] 	= value.x;
+							customAttribute.array[ offset + 1 ] = value.y;
+							customAttribute.array[ offset + 2 ] = value.z;
+
+							offset += 3;
+
+						}
+
+					}
+
+				} else if ( customAttribute.size === 4 ) {
+
+					for ( ca = 0; ca < cal; ca ++ ) {
+
+						value = customAttribute.value[ ca ];
+
+						customAttribute.array[ offset ] 	 = value.x;
+						customAttribute.array[ offset + 1  ] = value.y;
+						customAttribute.array[ offset + 2  ] = value.z;
+						customAttribute.array[ offset + 3  ] = value.w;
+
+						offset += 4;
+
+					}
+
+				}
+
+				renderer.setDynamicArrayBuffer( customAttribute.buffer, customAttribute.array);
+
+			}
+
+		}
+
+	}
+
+};

+ 9 - 1
utils/includes/common.json

@@ -73,8 +73,16 @@
 	"../src/scenes/Fog.js",
 	"../src/scenes/FogExp2.js",
 	"../src/renderers/CanvasRenderer.js",
-	"../src/renderers/WebGLShaders.js",
 	"../src/renderers/WebGLRenderer.js",
+	"../src/renderers/WebGLShaders.js",
+	"../src/renderers/WebGLRenderer2.js",
+	"../src/renderers/webgl/LowLevelRenderer.js",
+	"../src/renderers/webgl/ShaderBuilder.js",
+	"../src/renderers/webgl/objects/Object3DRenderer.js",
+	"../src/renderers/webgl/objects/MeshRenderer.js",
+	"../src/renderers/webgl/objects/ParticleRenderer.js",
+	"../src/renderers/webgl/objects/LineRenderer.js",
+	"../src/renderers/webgl/objects/RibbonRenderer.js",
 	"../src/renderers/WebGLRenderTarget.js",
 	"../src/renderers/WebGLRenderTargetCube.js",
 	"../src/renderers/renderables/RenderableVertex.js",