/** * @author supereggbert / http://www.paulbrunt.co.uk/ * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * @author szimek / https://github.com/szimek/ */ THREE.WebGLRenderer = function ( parameters ) { // constructor 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, _maxLights = parameters.maxLights !== undefined ? parameters.maxLights : 4; // public properties this.domElement = _canvas; this.context = null; // 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.shadowMapSoft = true; this.shadowMapCullFrontFaces = true; 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 = { memory: { programs: 0, geometries: 0, textures: 0 }, render: { calls: 0, vertices: 0, faces: 0, points: 0 } }; // internal properties var _this = this, _gl, _programs = [], // internal state cache _currentProgram = null, _currentFramebuffer = null, _currentMaterialId = -1, _currentGeometryGroupHash = null, _currentCamera = null, _geometryGroupCounter = 0, // GL state cache _oldDoubleSided = null, _oldFlipSided = null, _oldBlending = null, _oldDepthTest = null, _oldDepthWrite = null, _oldPolygonOffset = null, _oldPolygonOffsetFactor = null, _oldPolygonOffsetUnits = null, _oldLineWidth = null, _viewportX = 0, _viewportY = 0, _viewportWidth = 0, _viewportHeight = 0, _currentWidth = 0, _currentHeight = 0, // frustum _frustum = new THREE.Frustum(), // camera matrices cache _projScreenMatrix = new THREE.Matrix4(), _projScreenMatrixPS = new THREE.Matrix4(), _vector3 = new THREE.Vector4(), // light arrays cache _direction = new THREE.Vector3(), _lights = { ambient: [ 0, 0, 0 ], directional: { length: 0, colors: new Array(), positions: new Array() }, point: { length: 0, colors: new Array(), positions: new Array(), distances: new Array() } }; // initialize _gl = initGL(); setDefaultGLState(); this.context = _gl; // GPU capabilities var _maxVertexTextures = _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ), _maxTextureSize = _gl.getParameter( _gl.MAX_TEXTURE_SIZE ), _maxCubemapSize = _gl.getParameter( _gl.MAX_CUBE_MAP_TEXTURE_SIZE ); // API this.getContext = function () { return _gl; }; this.supportsVertexTextures = function () { return _maxVertexTextures > 0; }; this.setSize = function ( width, height ) { _canvas.width = width; _canvas.height = height; this.setViewport( 0, 0, _canvas.width, _canvas.height ); }; this.setViewport = function ( x, y, width, height ) { _viewportX = x; _viewportY = y; _viewportWidth = width; _viewportHeight = height; _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight ); }; this.setScissor = function ( x, y, width, height ) { _gl.scissor( x, y, width, height ); }; this.enableScissorTest = function ( enable ) { enable ? _gl.enable( _gl.SCISSOR_TEST ) : _gl.disable( _gl.SCISSOR_TEST ); }; // Clearing this.setClearColorHex = function ( hex, alpha ) { _clearColor.setHex( hex ); _clearAlpha = alpha; _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); }; this.setClearColor = function ( color, alpha ) { _clearColor.copy( color ); _clearAlpha = alpha; _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); }; this.getClearColor = function () { return _clearColor; }; this.getClearAlpha = function () { return _clearAlpha; }; this.clear = function ( 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 ); }; this.clearTarget = function ( renderTarget, color, depth, stencil ) { this.setRenderTarget( renderTarget ); this.clear( color, depth, stencil ); }; // Plugins this.addPostPlugin = function ( plugin ) { plugin.init( this ); this.renderPluginsPost.push( plugin ); }; this.addPrePlugin = function ( plugin ) { plugin.init( this ); this.renderPluginsPre.push( plugin ); }; // Deallocation this.deallocateObject = function ( object ) { if ( ! object.__webglInit ) return; object.__webglInit = false; delete object._modelViewMatrix; delete object._normalMatrixArray; delete object._modelViewMatrixArray; delete object._objectMatrixArray; if ( object instanceof THREE.Mesh ) { for ( var g in object.geometry.geometryGroups ) { deleteMeshBuffers( object.geometry.geometryGroups[ g ] ); } } else if ( object instanceof THREE.Ribbon ) { deleteRibbonBuffers( object.geometry ); } else if ( object instanceof THREE.Line ) { deleteLineBuffers( object.geometry ); } else if ( object instanceof THREE.ParticleSystem ) { deleteParticleBuffers( object.geometry ); } }; this.deallocateTexture = function ( texture ) { if ( ! texture.__webglInit ) return; texture.__webglInit = false; _gl.deleteTexture( texture.__webglTexture ); _this.info.memory.textures --; }; // Rendering this.updateShadowMap = function ( scene, camera ) { _currentProgram = null; _oldBlending = -1; _oldDepthTest = -1; _oldDepthWrite = -1; _currentGeometryGroupHash = -1; _currentMaterialId = -1; this.shadowMapPlugin.update( scene, camera ); }; // Internal functions // Buffer allocation function createParticleBuffers ( geometry ) { geometry.__webglVertexBuffer = _gl.createBuffer(); geometry.__webglColorBuffer = _gl.createBuffer(); _this.info.geometries ++; }; function createLineBuffers ( geometry ) { geometry.__webglVertexBuffer = _gl.createBuffer(); geometry.__webglColorBuffer = _gl.createBuffer(); _this.info.memory.geometries ++; }; function createRibbonBuffers ( geometry ) { geometry.__webglVertexBuffer = _gl.createBuffer(); geometry.__webglColorBuffer = _gl.createBuffer(); _this.info.memory.geometries ++; }; function createMeshBuffers ( geometryGroup ) { geometryGroup.__webglVertexBuffer = _gl.createBuffer(); geometryGroup.__webglNormalBuffer = _gl.createBuffer(); geometryGroup.__webglTangentBuffer = _gl.createBuffer(); geometryGroup.__webglColorBuffer = _gl.createBuffer(); geometryGroup.__webglUVBuffer = _gl.createBuffer(); geometryGroup.__webglUV2Buffer = _gl.createBuffer(); geometryGroup.__webglSkinVertexABuffer = _gl.createBuffer(); geometryGroup.__webglSkinVertexBBuffer = _gl.createBuffer(); geometryGroup.__webglSkinIndicesBuffer = _gl.createBuffer(); geometryGroup.__webglSkinWeightsBuffer = _gl.createBuffer(); geometryGroup.__webglFaceBuffer = _gl.createBuffer(); geometryGroup.__webglLineBuffer = _gl.createBuffer(); var m, ml; if ( geometryGroup.numMorphTargets ) { geometryGroup.__webglMorphTargetsBuffers = []; for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) { geometryGroup.__webglMorphTargetsBuffers.push( _gl.createBuffer() ); } } if ( geometryGroup.numMorphNormals ) { geometryGroup.__webglMorphNormalsBuffers = []; for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) { geometryGroup.__webglMorphNormalsBuffers.push( _gl.createBuffer() ); } } _this.info.memory.geometries ++; }; // Buffer deallocation function deleteParticleBuffers ( geometry ) { _gl.deleteBuffer( geometry.__webglVertexBuffer ); _gl.deleteBuffer( geometry.__webglColorBuffer ); _this.info.memory.geometries --; }; function deleteLineBuffers ( geometry ) { _gl.deleteBuffer( geometry.__webglVertexBuffer ); _gl.deleteBuffer( geometry.__webglColorBuffer ); _this.info.memory.geometries --; }; function deleteRibbonBuffers ( geometry ) { _gl.deleteBuffer( geometry.__webglVertexBuffer ); _gl.deleteBuffer( geometry.__webglColorBuffer ); _this.info.memory.geometries --; }; function deleteMeshBuffers ( geometryGroup ) { _gl.deleteBuffer( geometryGroup.__webglVertexBuffer ); _gl.deleteBuffer( geometryGroup.__webglNormalBuffer ); _gl.deleteBuffer( geometryGroup.__webglTangentBuffer ); _gl.deleteBuffer( geometryGroup.__webglColorBuffer ); _gl.deleteBuffer( geometryGroup.__webglUVBuffer ); _gl.deleteBuffer( geometryGroup.__webglUV2Buffer ); _gl.deleteBuffer( geometryGroup.__webglSkinVertexABuffer ); _gl.deleteBuffer( geometryGroup.__webglSkinVertexBBuffer ); _gl.deleteBuffer( geometryGroup.__webglSkinIndicesBuffer ); _gl.deleteBuffer( geometryGroup.__webglSkinWeightsBuffer ); _gl.deleteBuffer( geometryGroup.__webglFaceBuffer ); _gl.deleteBuffer( geometryGroup.__webglLineBuffer ); var m, ml; if ( geometryGroup.numMorphTargets ) { for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) { _gl.deleteBuffer( geometryGroup.__webglMorphTargetsBuffers[ m ] ); } } if ( geometryGroup.numMorphNormals ) { for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) { _gl.deleteBuffer( geometryGroup.__webglMorphNormalsBuffers[ m ] ); } } if ( geometryGroup.__webglCustomAttributesList ) { for ( var id in geometryGroup.__webglCustomAttributesList ) { _gl.deleteBuffer( geometryGroup.__webglCustomAttributesList[ id ].buffer ); } } _this.info.memory.geometries --; }; // 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 = _gl.createBuffer(); attribute.buffer.belongsToAttribute = a; attribute.needsUpdate = true; } geometry.__webglCustomAttributesList.push( attribute ); } } }; function initParticleBuffers ( geometry, object ) { var nvertices = geometry.vertices.length; geometry.__vertexArray = new Float32Array( nvertices * 3 ); geometry.__colorArray = new Float32Array( nvertices * 3 ); geometry.__sortArray = []; geometry.__webglParticleCount = nvertices; initCustomAttributes ( geometry, object ); }; function initLineBuffers ( geometry, object ) { var nvertices = geometry.vertices.length; geometry.__vertexArray = new Float32Array( nvertices * 3 ); geometry.__colorArray = new Float32Array( nvertices * 3 ); geometry.__webglLineCount = nvertices; initCustomAttributes ( geometry, object ); }; function initRibbonBuffers ( geometry ) { var nvertices = geometry.vertices.length; geometry.__vertexArray = new Float32Array( nvertices * 3 ); geometry.__colorArray = new Float32Array( nvertices * 3 ); geometry.__webglVertexCount = nvertices; }; function initMeshBuffers ( 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 = getBufferMaterial( object, geometryGroup ), uvType = bufferGuessUVType( material ), normalType = bufferGuessNormalType( material ), vertexColorType = 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.__skinVertexAArray = new Float32Array( nvertices * 4 ); geometryGroup.__skinVertexBArray = new Float32Array( nvertices * 4 ); 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 = _gl.createBuffer(); attribute.buffer.belongsToAttribute = a; originalAttribute.needsUpdate = true; attribute.__original = originalAttribute; } geometryGroup.__webglCustomAttributesList.push( attribute ); } } geometryGroup.__inittedArrays = true; }; function getBufferMaterial( object, geometryGroup ) { if ( object.material && ! ( object.material instanceof THREE.MeshFaceMaterial ) ) { return object.material; } else if ( geometryGroup.materialIndex >= 0 ) { return object.geometry.materials[ geometryGroup.materialIndex ]; } }; function materialNeedsSmoothNormals ( material ) { return material && material.shading !== undefined && material.shading === THREE.SmoothShading; }; function bufferGuessNormalType ( material ) { // only MeshBasicMaterial and MeshDepthMaterial don't need normals if ( ( material instanceof THREE.MeshBasicMaterial && !material.envMap ) || material instanceof THREE.MeshDepthMaterial ) { return false; } if ( materialNeedsSmoothNormals( material ) ) { return THREE.SmoothShading; } else { return THREE.FlatShading; } }; function bufferGuessVertexColorType ( material ) { if ( material.vertexColors ) { return material.vertexColors; } return false; }; function bufferGuessUVType ( material ) { // material must use some texture to require uvs if ( material.map || material.lightMap || material instanceof THREE.ShaderMaterial ) { return true; } return false; }; // Buffer setting function setParticleBuffers ( geometry, hint, object ) { 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.__dirtyVertices, dirtyElements = geometry.__dirtyElements, dirtyColors = geometry.__dirtyColors, customAttributes = geometry.__webglCustomAttributesList, i, il, a, ca, cal, value, customAttribute; if ( object.sortParticles ) { _projScreenMatrixPS.copy( _projScreenMatrix ); _projScreenMatrixPS.multiplySelf( object.matrixWorld ); for ( v = 0; v < vl; v ++ ) { vertex = vertices[ v ].position; _vector3.copy( vertex ); _projScreenMatrixPS.multiplyVector3( _vector3 ); sortArray[ v ] = [ _vector3.z, v ]; } sortArray.sort( function( a, b ) { return b[ 0 ] - a[ 0 ]; } ); for ( v = 0; v < vl; v ++ ) { vertex = vertices[ sortArray[v][1] ].position; 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 ].position; 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 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); } if ( dirtyColors || object.sortParticles ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); } if ( customAttributes ) { for ( i = 0, il = customAttributes.length; i < il; i ++ ) { customAttribute = customAttributes[ i ]; if ( customAttribute.needsUpdate || object.sortParticles ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); } } } }; function setLineBuffers ( geometry, hint ) { var v, c, vertex, offset, color, vertices = geometry.vertices, colors = geometry.colors, vl = vertices.length, cl = colors.length, vertexArray = geometry.__vertexArray, colorArray = geometry.__colorArray, dirtyVertices = geometry.__dirtyVertices, dirtyColors = geometry.__dirtyColors, customAttributes = geometry.__webglCustomAttributesList, i, il, a, ca, cal, value, customAttribute; if ( dirtyVertices ) { for ( v = 0; v < vl; v ++ ) { vertex = vertices[ v ].position; offset = v * 3; vertexArray[ offset ] = vertex.x; vertexArray[ offset + 1 ] = vertex.y; vertexArray[ offset + 2 ] = vertex.z; } _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); } 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; } _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); } 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; } } _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); } } } }; function setRibbonBuffers ( geometry, hint ) { var v, c, vertex, offset, color, vertices = geometry.vertices, colors = geometry.colors, vl = vertices.length, cl = colors.length, vertexArray = geometry.__vertexArray, colorArray = geometry.__colorArray, dirtyVertices = geometry.__dirtyVertices, dirtyColors = geometry.__dirtyColors; if ( dirtyVertices ) { for ( v = 0; v < vl; v ++ ) { vertex = vertices[ v ].position; offset = v * 3; vertexArray[ offset ] = vertex.x; vertexArray[ offset + 1 ] = vertex.y; vertexArray[ offset + 2 ] = vertex.z; } _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); } 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; } _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); } }; function setMeshBuffers( geometryGroup, object, hint, dispose, material ) { if ( ! geometryGroup.__inittedArrays ) { // console.log( object ); return; } var normalType = bufferGuessNormalType( material ), vertexColorType = bufferGuessVertexColorType( material ), uvType = 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, skinVertexAArray = geometryGroup.__skinVertexAArray, skinVertexBArray = geometryGroup.__skinVertexBArray, 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.__dirtyVertices, dirtyElements = geometry.__dirtyElements, dirtyUvs = geometry.__dirtyUvs, dirtyNormals = geometry.__dirtyNormals, dirtyTangents = geometry.__dirtyTangents, dirtyColors = geometry.__dirtyColors, dirtyMorphTargets = geometry.__dirtyMorphTargets, 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_skinVerticesA = geometry.skinVerticesA, obj_skinVerticesB = geometry.skinVerticesB, 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 ].position; v2 = vertices[ face.b ].position; v3 = vertices[ face.c ].position; 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 ].position; v2 = vertices[ face.b ].position; v3 = vertices[ face.c ].position; v4 = vertices[ face.d ].position; 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; } _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); } 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 ].position; v2 = morphTargets[ vk ].vertices[ face.b ].position; v3 = morphTargets[ vk ].vertices[ face.c ].position; 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 ].position; v2 = morphTargets[ vk ].vertices[ face.b ].position; v3 = morphTargets[ vk ].vertices[ face.c ].position; v4 = morphTargets[ vk ].vertices[ face.d ].position; 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; } _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ vk ] ); _gl.bufferData( _gl.ARRAY_BUFFER, morphTargetsArrays[ vk ], hint ); if ( material.morphNormals ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ vk ] ); _gl.bufferData( _gl.ARRAY_BUFFER, morphNormalsArrays[ vk ], hint ); } } } 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; // vertices A sa1 = obj_skinVerticesA[ face.a ]; sa2 = obj_skinVerticesA[ face.b ]; sa3 = obj_skinVerticesA[ face.c ]; skinVertexAArray[ offset_skin ] = sa1.x; skinVertexAArray[ offset_skin + 1 ] = sa1.y; skinVertexAArray[ offset_skin + 2 ] = sa1.z; skinVertexAArray[ offset_skin + 3 ] = 1; // pad for faster vertex shader skinVertexAArray[ offset_skin + 4 ] = sa2.x; skinVertexAArray[ offset_skin + 5 ] = sa2.y; skinVertexAArray[ offset_skin + 6 ] = sa2.z; skinVertexAArray[ offset_skin + 7 ] = 1; skinVertexAArray[ offset_skin + 8 ] = sa3.x; skinVertexAArray[ offset_skin + 9 ] = sa3.y; skinVertexAArray[ offset_skin + 10 ] = sa3.z; skinVertexAArray[ offset_skin + 11 ] = 1; // vertices B sb1 = obj_skinVerticesB[ face.a ]; sb2 = obj_skinVerticesB[ face.b ]; sb3 = obj_skinVerticesB[ face.c ]; skinVertexBArray[ offset_skin ] = sb1.x; skinVertexBArray[ offset_skin + 1 ] = sb1.y; skinVertexBArray[ offset_skin + 2 ] = sb1.z; skinVertexBArray[ offset_skin + 3 ] = 1; // pad for faster vertex shader skinVertexBArray[ offset_skin + 4 ] = sb2.x; skinVertexBArray[ offset_skin + 5 ] = sb2.y; skinVertexBArray[ offset_skin + 6 ] = sb2.z; skinVertexBArray[ offset_skin + 7 ] = 1; skinVertexBArray[ offset_skin + 8 ] = sb3.x; skinVertexBArray[ offset_skin + 9 ] = sb3.y; skinVertexBArray[ offset_skin + 10 ] = sb3.z; skinVertexBArray[ offset_skin + 11 ] = 1; 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; // vertices A sa1 = obj_skinVerticesA[ face.a ]; sa2 = obj_skinVerticesA[ face.b ]; sa3 = obj_skinVerticesA[ face.c ]; sa4 = obj_skinVerticesA[ face.d ]; skinVertexAArray[ offset_skin ] = sa1.x; skinVertexAArray[ offset_skin + 1 ] = sa1.y; skinVertexAArray[ offset_skin + 2 ] = sa1.z; skinVertexAArray[ offset_skin + 3 ] = 1; // pad for faster vertex shader skinVertexAArray[ offset_skin + 4 ] = sa2.x; skinVertexAArray[ offset_skin + 5 ] = sa2.y; skinVertexAArray[ offset_skin + 6 ] = sa2.z; skinVertexAArray[ offset_skin + 7 ] = 1; skinVertexAArray[ offset_skin + 8 ] = sa3.x; skinVertexAArray[ offset_skin + 9 ] = sa3.y; skinVertexAArray[ offset_skin + 10 ] = sa3.z; skinVertexAArray[ offset_skin + 11 ] = 1; skinVertexAArray[ offset_skin + 12 ] = sa4.x; skinVertexAArray[ offset_skin + 13 ] = sa4.y; skinVertexAArray[ offset_skin + 14 ] = sa4.z; skinVertexAArray[ offset_skin + 15 ] = 1; // vertices B sb1 = obj_skinVerticesB[ face.a ]; sb2 = obj_skinVerticesB[ face.b ]; sb3 = obj_skinVerticesB[ face.c ]; sb4 = obj_skinVerticesB[ face.d ]; skinVertexBArray[ offset_skin ] = sb1.x; skinVertexBArray[ offset_skin + 1 ] = sb1.y; skinVertexBArray[ offset_skin + 2 ] = sb1.z; skinVertexBArray[ offset_skin + 3 ] = 1; // pad for faster vertex shader skinVertexBArray[ offset_skin + 4 ] = sb2.x; skinVertexBArray[ offset_skin + 5 ] = sb2.y; skinVertexBArray[ offset_skin + 6 ] = sb2.z; skinVertexBArray[ offset_skin + 7 ] = 1; skinVertexBArray[ offset_skin + 8 ] = sb3.x; skinVertexBArray[ offset_skin + 9 ] = sb3.y; skinVertexBArray[ offset_skin + 10 ] = sb3.z; skinVertexBArray[ offset_skin + 11 ] = 1; skinVertexBArray[ offset_skin + 12 ] = sb4.x; skinVertexBArray[ offset_skin + 13 ] = sb4.y; skinVertexBArray[ offset_skin + 14 ] = sb4.z; skinVertexBArray[ offset_skin + 15 ] = 1; offset_skin += 16; } if ( offset_skin > 0 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinVertexABuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, skinVertexAArray, hint ); _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinVertexBBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, skinVertexBArray, hint ); _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, skinIndexArray, hint ); _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, skinWeightArray, hint ); } } 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 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); } } 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; } _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, tangentArray, hint ); } 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; } } } _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, normalArray, hint ); } if ( dirtyUvs && obj_uvs && uvType ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { fi = chunk_faces3[ f ]; face = obj_faces[ fi ]; uv = obj_uvs[ fi ]; if ( uv === undefined ) continue; for ( i = 0; i < 3; i ++ ) { uvi = uv[ i ]; uvArray[ offset_uv ] = uvi.u; uvArray[ offset_uv + 1 ] = uvi.v; offset_uv += 2; } } for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { fi = chunk_faces4[ f ]; face = obj_faces[ fi ]; uv = obj_uvs[ fi ]; if ( uv === undefined ) continue; for ( i = 0; i < 4; i ++ ) { uvi = uv[ i ]; uvArray[ offset_uv ] = uvi.u; uvArray[ offset_uv + 1 ] = uvi.v; offset_uv += 2; } } if ( offset_uv > 0 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, uvArray, hint ); } } if ( dirtyUvs && obj_uvs2 && uvType ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { fi = chunk_faces3[ f ]; face = obj_faces[ fi ]; uv2 = obj_uvs2[ fi ]; if ( uv2 === undefined ) continue; for ( i = 0; i < 3; i ++ ) { uv2i = uv2[ i ]; uv2Array[ offset_uv2 ] = uv2i.u; uv2Array[ offset_uv2 + 1 ] = uv2i.v; offset_uv2 += 2; } } for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { fi = chunk_faces4[ f ]; face = obj_faces[ fi ]; uv2 = obj_uvs2[ fi ]; if ( uv2 === undefined ) continue; for ( i = 0; i < 4; i ++ ) { uv2i = uv2[ i ]; uv2Array[ offset_uv2 ] = uv2i.u; uv2Array[ offset_uv2 + 1 ] = uv2i.v; offset_uv2 += 2; } } if ( offset_uv2 > 0 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer ); _gl.bufferData( _gl.ARRAY_BUFFER, uv2Array, hint ); } } if ( dirtyElements ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { face = obj_faces[ chunk_faces3[ 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 ++ ) { face = obj_faces[ chunk_faces4[ 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; } _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer ); _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, faceArray, hint ); _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer ); _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, lineArray, hint ); } 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.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; } } } _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); } } 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.__skinVertexAArray; delete geometryGroup.__skinVertexBArray; delete geometryGroup.__skinIndexArray; delete geometryGroup.__skinWeightArray; } }; // Buffer rendering this.renderBufferImmediate = function ( object, program, shading ) { if ( ! object.__webglVertexBuffer ) object.__webglVertexBuffer = _gl.createBuffer(); if ( ! object.__webglNormalBuffer ) object.__webglNormalBuffer = _gl.createBuffer(); if ( object.hasPos ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglVertexBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW ); _gl.enableVertexAttribArray( program.attributes.position ); _gl.vertexAttribPointer( program.attributes.position, 3, _gl.FLOAT, false, 0, 0 ); } if ( object.hasNormal ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglNormalBuffer ); if ( 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; } } _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW ); _gl.enableVertexAttribArray( program.attributes.normal ); _gl.vertexAttribPointer( program.attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); } _gl.drawArrays( _gl.TRIANGLES, 0, object.count ); object.count = 0; }; this.renderBufferDirect = function ( camera, lights, fog, material, geometryGroup, object ) { if ( material.opacity === 0 ) 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, geometryGroupHash = ( geometryGroup.id * 0xffffff ) + ( program.id * 2 ) + wireframeBit; if ( geometryGroupHash !== _currentGeometryGroupHash ) { _currentGeometryGroupHash = geometryGroupHash; updateBuffers = true; } // render mesh if ( object instanceof THREE.Mesh ) { var offsets = geometryGroup.offsets; for ( var i = 0, il = offsets.length; i < il; ++ i ) { if ( updateBuffers ) { // vertices _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.vertexPositionBuffer ); _gl.vertexAttribPointer( attributes.position, geometryGroup.vertexPositionBuffer.itemSize, _gl.FLOAT, false, 0, offsets[ i ].index * 4 * 3 ); // normals if ( attributes.normal >= 0 && geometryGroup.vertexNormalBuffer ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.vertexNormalBuffer ); _gl.vertexAttribPointer( attributes.normal, geometryGroup.vertexNormalBuffer.itemSize, _gl.FLOAT, false, 0, offsets[ i ].index * 4 * 3 ); } // uvs if ( attributes.uv >= 0 && geometryGroup.vertexUvBuffer ) { if ( geometryGroup.vertexUvBuffer ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.vertexUvBuffer ); _gl.vertexAttribPointer( attributes.uv, geometryGroup.vertexUvBuffer.itemSize, _gl.FLOAT, false, 0, offsets[ i ].index * 4 * 2 ); _gl.enableVertexAttribArray( attributes.uv ); } else { _gl.disableVertexAttribArray( attributes.uv ); } } // colors if ( attributes.color >= 0 && geometryGroup.vertexColorBuffer ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.vertexColorBuffer ); _gl.vertexAttribPointer( attributes.color, geometryGroup.vertexColorBuffer.itemSize, _gl.FLOAT, false, 0, offsets[ i ].index * 4 * 4 ); } _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.vertexIndexBuffer ); } // render indexed triangles _gl.drawElements( _gl.TRIANGLES, offsets[ i ].count, _gl.UNSIGNED_SHORT, offsets[ i ].start * 2 ); // 2 = Uint16 _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; } } }; this.renderBuffer = function ( camera, lights, fog, material, geometryGroup, object ) { if ( material.opacity === 0 ) 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; } // vertices if ( !material.morphTargets && attributes.position >= 0 ) { if ( updateBuffers ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 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 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, attribute.buffer ); _gl.vertexAttribPointer( attributes[ attribute.buffer.belongsToAttribute ], attribute.size, _gl.FLOAT, false, 0, 0 ); } } } // colors if ( attributes.color >= 0 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer ); _gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 ); } // normals if ( attributes.normal >= 0 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer ); _gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); } // tangents if ( attributes.tangent >= 0 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer ); _gl.vertexAttribPointer( attributes.tangent, 4, _gl.FLOAT, false, 0, 0 ); } // uvs if ( attributes.uv >= 0 ) { if ( geometryGroup.__webglUVBuffer ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer ); _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); _gl.enableVertexAttribArray( attributes.uv ); } else { _gl.disableVertexAttribArray( attributes.uv ); } } if ( attributes.uv2 >= 0 ) { if ( geometryGroup.__webglUV2Buffer ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer ); _gl.vertexAttribPointer( attributes.uv2, 2, _gl.FLOAT, false, 0, 0 ); _gl.enableVertexAttribArray( attributes.uv2 ); } else { _gl.disableVertexAttribArray( attributes.uv2 ); } } if ( material.skinning && attributes.skinVertexA >= 0 && attributes.skinVertexB >= 0 && attributes.skinIndex >= 0 && attributes.skinWeight >= 0 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinVertexABuffer ); _gl.vertexAttribPointer( attributes.skinVertexA, 4, _gl.FLOAT, false, 0, 0 ); _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinVertexBBuffer ); _gl.vertexAttribPointer( attributes.skinVertexB, 4, _gl.FLOAT, false, 0, 0 ); _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer ); _gl.vertexAttribPointer( attributes.skinIndex, 4, _gl.FLOAT, false, 0, 0 ); _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer ); _gl.vertexAttribPointer( attributes.skinWeight, 4, _gl.FLOAT, false, 0, 0 ); } } // render mesh if ( object instanceof THREE.Mesh ) { // wireframe if ( material.wireframe ) { setLineWidth( material.wireframeLinewidth ); if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer ); _gl.drawElements( _gl.LINES, geometryGroup.__webglLineCount, _gl.UNSIGNED_SHORT, 0 ); // triangles } else { if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer ); _gl.drawElements( _gl.TRIANGLES, geometryGroup.__webglFaceCount, _gl.UNSIGNED_SHORT, 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 ) { primitives = ( object.type === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES; setLineWidth( material.linewidth ); _gl.drawArrays( primitives, 0, geometryGroup.__webglLineCount ); _this.info.render.calls ++; // render particles } else if ( object instanceof THREE.ParticleSystem ) { _gl.drawArrays( _gl.POINTS, 0, geometryGroup.__webglParticleCount ); _this.info.render.calls ++; _this.info.render.points += geometryGroup.__webglParticleCount; // render ribbon } else if ( object instanceof THREE.Ribbon ) { _gl.drawArrays( _gl.TRIANGLE_STRIP, 0, geometryGroup.__webglVertexCount ); _this.info.render.calls ++; } }; function setupMorphTargets ( material, geometryGroup, object ) { // set base var attributes = material.program.attributes; if ( object.morphTargetBase !== - 1 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ object.morphTargetBase ] ); _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); } else if ( attributes.position >= 0 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 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 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ order[ m ] ] ); _gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 ); if ( material.morphNormals ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ order[ m ] ] ); _gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 ); } object.__webglMorphTargetInfluences[ m ] = influences[ order[ m ] ]; m ++; } } else { // find most influencing var used = []; var candidateInfluence = - 1; var candidate = 0; var influences = object.morphTargetInfluences; var i, il = influences.length; var m = 0; if ( object.morphTargetBase !== - 1 ) { used[ object.morphTargetBase ] = true; } while ( m < material.numSupportedMorphTargets ) { for ( i = 0; i < il; i ++ ) { if ( !used[ i ] && influences[ i ] > candidateInfluence ) { candidate = i; candidateInfluence = influences[ candidate ]; } } _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ candidate ] ); _gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 ); if ( material.morphNormals ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ candidate ] ); _gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 ); } object.__webglMorphTargetInfluences[ m ] = candidateInfluence; used[ candidate ] = 1; candidateInfluence = -1; m ++; } } // load updated influences uniform if( material.program.uniforms.morphTargetInfluences !== null ) { _gl.uniform1fv( material.program.uniforms.morphTargetInfluences, object.__webglMorphTargetInfluences ); } }; function painterSort ( a, b ) { return b.z - a.z; }; // Rendering this.render = function ( scene, camera, renderTarget, forceClear ) { var i, il, program, material, webglObject, object, renderList, lights = scene.__lights, fog = scene.fog; _currentMaterialId = -1; // update scene graph if ( camera.parent === undefined ) { console.warn( 'DEPRECATED: Camera hasn\'t been added to a Scene. Adding it...' ); scene.add( camera ); } if ( this.autoUpdateScene ) scene.updateMatrixWorld(); // update camera matrices and frustum if ( ! camera._viewMatrixArray ) camera._viewMatrixArray = new Float32Array( 16 ); if ( ! camera._projectionMatrixArray ) camera._projectionMatrixArray = new Float32Array( 16 ); camera.matrixWorldInverse.getInverse( camera.matrixWorld ); camera.matrixWorldInverse.flattenToArray( camera._viewMatrixArray ); camera.projectionMatrix.flattenToArray( camera._projectionMatrixArray ); _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; this.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.contains( object ) ) { object.matrixWorld.flattenToArray( object._objectMatrixArray ); setupMatrices( object, camera ); unrollBufferMaterial( webglObject ); webglObject.render = true; if ( this.sortObjects ) { if ( object.renderDepth ) { webglObject.z = object.renderDepth; } else { _vector3.copy( object.matrixWorld.getPosition() ); _projScreenMatrix.multiplyVector3( _vector3 ); webglObject.z = _vector3.z; } } } } } if ( this.sortObjects ) { renderList.sort( painterSort ); } // 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 ) { if( object.matrixAutoUpdate ) { object.matrixWorld.flattenToArray( object._objectMatrixArray ); } setupMatrices( object, camera ); unrollImmediateBufferMaterial( webglObject ); } } if ( scene.overrideMaterial ) { this.setBlending( scene.overrideMaterial.blending ); this.setDepthTest( scene.overrideMaterial.depthTest ); this.setDepthWrite( scene.overrideMaterial.depthWrite ); setPolygonOffset( scene.overrideMaterial.polygonOffset, scene.overrideMaterial.polygonOffsetFactor, scene.overrideMaterial.polygonOffsetUnits ); renderObjects( scene.__webglObjects, false, "", camera, lights, fog, true, scene.overrideMaterial ); renderObjectsImmediate( scene.__webglObjectsImmediate, "", camera, lights, fog, false, scene.overrideMaterial ); } else { // opaque pass (front-to-back order) this.setBlending( THREE.NormalBlending ); renderObjects( scene.__webglObjects, true, "opaque", camera, lights, fog, false ); renderObjectsImmediate( scene.__webglObjectsImmediate, "opaque", camera, lights, fog, false ); // transparent pass (back-to-front order) renderObjects( scene.__webglObjects, false, "transparent", camera, lights, fog, true ); renderObjectsImmediate( scene.__webglObjectsImmediate, "transparent", camera, lights, fog, true ); } // 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 ) { updateRenderTargetMipmap( renderTarget ); } // Ensure depth buffer writing is enabled so it can be cleared on next render this.setDepthTest( true ); this.setDepthWrite( true ); // _gl.finish(); }; function renderPlugins( plugins, scene, camera ) { if ( ! plugins.length ) return; for ( var i = 0, il = plugins.length; i < il; i ++ ) { _currentProgram = null; _currentCamera = null; _oldBlending = -1; _oldDepthTest = -1; _oldDepthWrite = -1; _currentGeometryGroupHash = -1; _currentMaterialId = -1; plugins[ i ].render( scene, camera, _currentWidth, _currentHeight ); _currentProgram = null; _currentCamera = null; _oldBlending = -1; _oldDepthTest = -1; _oldDepthWrite = -1; _currentGeometryGroupHash = -1; _currentMaterialId = -1; } }; 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 ) _this.setBlending( material.blending ); _this.setDepthTest( material.depthTest ); _this.setDepthWrite( material.depthWrite ); setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); } _this.setObjectFaces( object ); 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 ) _this.setBlending( material.blending ); _this.setDepthTest( material.depthTest ); _this.setDepthWrite( material.depthWrite ); 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; _this.setObjectFaces( object ); if ( object.immediateRenderCallback ) { object.immediateRenderCallback( program, _gl, _frustum ); } else { object.render( function( object ) { _this.renderBufferImmediate( object, program, material.shading ); } ); } }; 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; if ( materialIndex >= 0 ) { material = object.geometry.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 ) { var f, fl, face, materialIndex, vertices, materialHash, groupHash, hash_map = {}; var numMorphTargets = geometry.morphTargets.length; var numMorphNormals = geometry.morphNormals.length; geometry.geometryGroups = {}; for ( f = 0, fl = geometry.faces.length; f < fl; f ++ ) { face = geometry.faces[ f ]; materialIndex = face.materialIndex; materialHash = ( materialIndex !== undefined ) ? materialIndex : -1; if ( hash_map[ materialHash ] === undefined ) { hash_map[ materialHash ] = { 'hash': materialHash, 'counter': 0 }; } groupHash = hash_map[ materialHash ].hash + '_' + hash_map[ materialHash ].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[ materialHash ].counter += 1; groupHash = hash_map[ materialHash ].hash + '_' + hash_map[ materialHash ].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, geometryGroup; if ( ! object.__webglInit ) { object.__webglInit = true; object._modelViewMatrix = new THREE.Matrix4(); object._normalMatrixArray = new Float32Array( 9 ); object._modelViewMatrixArray = new Float32Array( 16 ); object._objectMatrixArray = new Float32Array( 16 ); object.matrixWorld.flattenToArray( object._objectMatrixArray ); if ( object instanceof THREE.Mesh ) { geometry = object.geometry; if ( geometry instanceof THREE.Geometry ) { if ( geometry.geometryGroups === undefined ) { sortFacesByMaterial( geometry ); } // create separate VBOs per geometry chunk for ( g in geometry.geometryGroups ) { geometryGroup = geometry.geometryGroups[ g ]; // initialise VBO on the first access if ( ! geometryGroup.__webglVertexBuffer ) { createMeshBuffers( geometryGroup ); initMeshBuffers( geometryGroup, object ); geometry.__dirtyVertices = true; geometry.__dirtyMorphTargets = true; geometry.__dirtyElements = true; geometry.__dirtyUvs = true; geometry.__dirtyNormals = true; geometry.__dirtyTangents = true; geometry.__dirtyColors = true; } } } } else if ( object instanceof THREE.Ribbon ) { geometry = object.geometry; if( ! geometry.__webglVertexBuffer ) { createRibbonBuffers( geometry ); initRibbonBuffers( geometry ); geometry.__dirtyVertices = true; geometry.__dirtyColors = true; } } else if ( object instanceof THREE.Line ) { geometry = object.geometry; if( ! geometry.__webglVertexBuffer ) { createLineBuffers( geometry ); initLineBuffers( geometry, object ); geometry.__dirtyVertices = true; geometry.__dirtyColors = true; } } else if ( object instanceof THREE.ParticleSystem ) { geometry = object.geometry; if ( ! geometry.__webglVertexBuffer ) { createParticleBuffers( geometry ); initParticleBuffers( geometry, object ); geometry.__dirtyVertices = true; geometry.__dirtyColors = true; } } } if ( ! object.__webglActive ) { if ( object instanceof THREE.Mesh ) { geometry = object.geometry; if ( geometry instanceof THREE.BufferGeometry ) { addBuffer( scene.__webglObjects, geometry, object ); } else { 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 ( THREE.MarchingCubes !== undefined && object instanceof THREE.MarchingCubes || 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.__dirtyVertices || geometry.__dirtyElements || geometry.__dirtyUvs || geometry.__dirtyNormals || geometry.__dirtyColors ) { // TODO // set buffers from typed arrays } */ geometry.__dirtyVertices = false; geometry.__dirtyElements = false; geometry.__dirtyUvs = false; geometry.__dirtyNormals = false; geometry.__dirtyColors = 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 ); customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); if ( geometry.__dirtyVertices || geometry.__dirtyMorphTargets || geometry.__dirtyElements || geometry.__dirtyUvs || geometry.__dirtyNormals || geometry.__dirtyColors || geometry.__dirtyTangents || customAttributesDirty ) { setMeshBuffers( geometryGroup, object, _gl.DYNAMIC_DRAW, !geometry.dynamic, material ); } } geometry.__dirtyVertices = false; geometry.__dirtyMorphTargets = false; geometry.__dirtyElements = false; geometry.__dirtyUvs = false; geometry.__dirtyNormals = false; geometry.__dirtyColors = false; geometry.__dirtyTangents = false; material.attributes && clearCustomAttributes( material ); } } else if ( object instanceof THREE.Ribbon ) { if ( geometry.__dirtyVertices || geometry.__dirtyColors ) { setRibbonBuffers( geometry, _gl.DYNAMIC_DRAW ); } geometry.__dirtyVertices = false; geometry.__dirtyColors = false; } else if ( object instanceof THREE.Line ) { material = getBufferMaterial( object, geometryGroup ); customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); if ( geometry.__dirtyVertices || geometry.__dirtyColors || customAttributesDirty ) { setLineBuffers( geometry, _gl.DYNAMIC_DRAW ); } geometry.__dirtyVertices = false; geometry.__dirtyColors = false; material.attributes && clearCustomAttributes( material ); } else if ( object instanceof THREE.ParticleSystem ) { material = getBufferMaterial( object, geometryGroup ); customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); if ( geometry.__dirtyVertices || geometry.__dirtyColors || object.sortParticles || customAttributesDirty ) { setParticleBuffers( geometry, _gl.DYNAMIC_DRAW, object ); } geometry.__dirtyVertices = false; geometry.__dirtyColors = 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.MarchingCubes || 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 ) { 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.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, vertexColors: material.vertexColors, fog: fog, useFog: material.fog, sizeAttenuation: material.sizeAttenuation, skinning: material.skinning, morphTargets: material.morphTargets, morphNormals: material.morphNormals, maxMorphTargets: this.maxMorphTargets, maxMorphNormals: this.maxMorphNormals, maxDirLights: maxLightCount.directional, maxPointLights: maxLightCount.point, maxBones: maxBones, shadowMapEnabled: this.shadowMapEnabled && object.receiveShadow, shadowMapSoft: this.shadowMapSoft, shadowMapDebug: this.shadowMapDebug, shadowMapCascade: this.shadowMapCascade, maxShadows: maxShadows, alphaTest: material.alphaTest, metal: material.metal, perPixel: material.perPixel, wrapAround: material.wrapAround, doubleSided: object && object.doubleSided }; material.program = buildProgram( shaderID, material.fragmentShader, material.vertexShader, material.uniforms, material.attributes, parameters ); var attributes = material.program.attributes; if ( attributes.position >= 0 ) _gl.enableVertexAttribArray( attributes.position ); if ( attributes.color >= 0 ) _gl.enableVertexAttribArray( attributes.color ); if ( attributes.normal >= 0 ) _gl.enableVertexAttribArray( attributes.normal ); if ( attributes.tangent >= 0 ) _gl.enableVertexAttribArray( attributes.tangent ); if ( material.skinning && attributes.skinVertexA >=0 && attributes.skinVertexB >= 0 && attributes.skinIndex >= 0 && attributes.skinWeight >= 0 ) { _gl.enableVertexAttribArray( attributes.skinVertexA ); _gl.enableVertexAttribArray( attributes.skinVertexB ); _gl.enableVertexAttribArray( attributes.skinIndex ); _gl.enableVertexAttribArray( attributes.skinWeight ); } if ( material.attributes ) { for ( a in material.attributes ) { if( attributes[ a ] !== undefined && attributes[ a ] >= 0 ) _gl.enableVertexAttribArray( attributes[ a ] ); } } if ( material.morphTargets ) { material.numSupportedMorphTargets = 0; var id, base = "morphTarget"; for ( i = 0; i < this.maxMorphTargets; i ++ ) { id = base + i; if ( attributes[ id ] >= 0 ) { _gl.enableVertexAttribArray( attributes[ id ] ); 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 ) { _gl.enableVertexAttribArray( attributes[ id ] ); 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 ) { if ( ! material.program || material.needsUpdate ) { _this.initMaterial( material, lights, fog, object ); material.needsUpdate = false; } if ( material.morphTargets ) { if ( ! object.__webglMorphTargetInfluences ) { object.__webglMorphTargetInfluences = new Float32Array( _this.maxMorphTargets ); for ( var i = 0, il = _this.maxMorphTargets; i < il; i ++ ) { object.__webglMorphTargetInfluences[ i ] = 0; } } } var refreshMaterial = false; var program = material.program, p_uniforms = program.uniforms, m_uniforms = material.uniforms; if ( program !== _currentProgram ) { _gl.useProgram( program ); _currentProgram = program; refreshMaterial = true; } if ( material.id !== _currentMaterialId ) { _currentMaterialId = material.id; refreshMaterial = true; } if ( refreshMaterial || camera !== _currentCamera ) { _gl.uniformMatrix4fv( p_uniforms.projectionMatrix, false, camera._projectionMatrixArray ); if ( camera !== _currentCamera ) _currentCamera = camera; } 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 ) { setupLights( program, lights ); 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.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(); _gl.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 ) { _gl.uniformMatrix4fv( p_uniforms.viewMatrix, false, camera._viewMatrixArray ); } } if ( material.skinning ) { _gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, object.boneMatrices ); } } loadUniformsMatrices( p_uniforms, object ); if ( material instanceof THREE.ShaderMaterial || material.envMap || material.skinning || object.receiveShadow ) { if ( p_uniforms.objectMatrix !== null ) { _gl.uniformMatrix4fv( p_uniforms.objectMatrix, false, object._objectMatrixArray ); } } 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.texture = material.map; if ( material.map ) { uniforms.offsetRepeat.value.set( material.map.offset.x, material.map.offset.y, material.map.repeat.x, material.map.repeat.y ); } uniforms.lightMap.texture = material.lightMap; uniforms.envMap.texture = 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 refreshUniformsParticle ( uniforms, material ) { uniforms.psColor.value = material.color; uniforms.opacity.value = material.opacity; uniforms.size.value = material.size; uniforms.scale.value = _canvas.height / 2.0; // TODO: Cache this. uniforms.map.texture = 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; }; 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.texture[ 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 ) { _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, object._modelViewMatrixArray ); if ( uniforms.normalMatrix ) { _gl.uniformMatrix3fv( uniforms.normalMatrix, false, object._normalMatrixArray ); } }; function loadUniformsGeneric ( program, uniforms ) { var uniform, value, type, location, texture, 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; // single integer if( type === "i" ) { _gl.uniform1i( location, value ); // single float } else if( type === "f" ) { _gl.uniform1f( location, value ); // single THREE.Vector2 } else if( type === "v2" ) { _gl.uniform2f( location, value.x, value.y ); // single THREE.Vector3 } else if( type === "v3" ) { _gl.uniform3f( location, value.x, value.y, value.z ); // single THREE.Vector4 } else if( type === "v4" ) { _gl.uniform4f( location, value.x, value.y, value.z, value.w ); // single THREE.Color } else if( type === "c" ) { _gl.uniform3f( location, value.r, value.g, value.b ); // flat array of floats (JS or typed array) } else if( type === "fv1" ) { _gl.uniform1fv( location, value ); // flat array of floats with 3 x N size (JS or typed array) } else if( type === "fv" ) { _gl.uniform3fv( location, value ); // array of THREE.Vector2 } else if( type === "v2v" ) { if ( ! uniform._array ) { 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; } _gl.uniform2fv( location, uniform._array ); // array of THREE.Vector3 } else if( type === "v3v" ) { if ( ! uniform._array ) { 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; } _gl.uniform3fv( location, uniform._array ); // array of THREE.Vector4 } else if( type == "v4v" ) { if ( ! uniform._array ) { 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; } _gl.uniform4fv( location, uniform._array ); // single THREE.Matrix4 } else if( type === "m4" ) { if ( ! uniform._array ) { uniform._array = new Float32Array( 16 ); } value.flattenToArray( uniform._array ); _gl.uniformMatrix4fv( location, false, uniform._array ); // array of THREE.Matrix4 } else if( type === "m4v" ) { if ( ! uniform._array ) { uniform._array = new Float32Array( 16 * value.length ); } for ( i = 0, il = value.length; i < il; i ++ ) { value[ i ].flattenToArrayOffset( uniform._array, i * 16 ); } _gl.uniformMatrix4fv( location, false, uniform._array ); // single THREE.Texture (2d or cube) } else if( type === "t" ) { _gl.uniform1i( location, value ); texture = uniform.texture; if ( !texture ) continue; if ( texture.image instanceof Array && texture.image.length === 6 ) { setCubeTexture( texture, value ); } else if ( texture instanceof THREE.WebGLRenderTargetCube ) { setCubeTextureDynamic( texture, value ); } else { _this.setTexture( texture, value ); } // array of THREE.Texture (2d) } else if( type === "tv" ) { if ( ! uniform._array ) { uniform._array = []; for( i = 0, il = uniform.texture.length; i < il; i ++ ) { uniform._array[ i ] = value + i; } } _gl.uniform1iv( location, uniform._array ); for( i = 0, il = uniform.texture.length; i < il; i ++ ) { texture = uniform.texture[ i ]; if ( !texture ) continue; _this.setTexture( texture, uniform._array[ i ] ); } } } }; function setupMatrices ( object, camera ) { object._modelViewMatrix.multiplyToArray( camera.matrixWorldInverse, object.matrixWorld, object._modelViewMatrixArray ); var inverseMatrix = THREE.Matrix4.makeInvert3x3( object._modelViewMatrix ); if ( inverseMatrix ) { inverseMatrix.transposeIntoArray( object._normalMatrixArray ); } }; function setupLights ( program, lights ) { var l, ll, light, n, r = 0, g = 0, b = 0, color, position, intensity, distance, zlights = _lights, dcolors = zlights.directional.colors, dpositions = zlights.directional.positions, pcolors = zlights.point.colors, ppositions = zlights.point.positions, pdistances = zlights.point.distances, dlength = 0, plength = 0, doffset = 0, poffset = 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 ( _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 ) { doffset = dlength * 3; if ( _this.gammaInput ) { dcolors[ doffset ] = color.r * color.r * intensity * intensity; dcolors[ doffset + 1 ] = color.g * color.g * intensity * intensity; dcolors[ doffset + 2 ] = color.b * color.b * intensity * intensity; } else { dcolors[ doffset ] = color.r * intensity; dcolors[ doffset + 1 ] = color.g * intensity; dcolors[ doffset + 2 ] = color.b * intensity; } _direction.copy( light.matrixWorld.getPosition() ); _direction.subSelf( light.target.matrixWorld.getPosition() ); _direction.normalize(); dpositions[ doffset ] = _direction.x; dpositions[ doffset + 1 ] = _direction.y; dpositions[ doffset + 2 ] = _direction.z; dlength += 1; } else if( light instanceof THREE.PointLight || light instanceof THREE.SpotLight ) { poffset = plength * 3; if ( _this.gammaInput ) { pcolors[ poffset ] = color.r * color.r * intensity * intensity; pcolors[ poffset + 1 ] = color.g * color.g * intensity * intensity; pcolors[ poffset + 2 ] = color.b * color.b * intensity * intensity; } else { pcolors[ poffset ] = color.r * intensity; pcolors[ poffset + 1 ] = color.g * intensity; pcolors[ poffset + 2 ] = color.b * intensity; } position = light.matrixWorld.getPosition(); ppositions[ poffset ] = position.x; ppositions[ poffset + 1 ] = position.y; ppositions[ poffset + 2 ] = position.z; pdistances[ plength ] = distance; plength += 1; } } // null eventual remains from removed lights // (this is to avoid if in shader) for ( l = dlength * 3, ll = dcolors.length; l < ll; l ++ ) dcolors[ l ] = 0.0; for ( l = plength * 3, ll = pcolors.length; l < ll; l ++ ) pcolors[ l ] = 0.0; zlights.point.length = plength; zlights.directional.length = dlength; zlights.ambient[ 0 ] = r; zlights.ambient[ 1 ] = g; zlights.ambient[ 2 ] = b; }; // GL state setting this.setFaceCulling = function ( cullFace, frontFace ) { if ( cullFace ) { if ( !frontFace || frontFace === "ccw" ) { _gl.frontFace( _gl.CCW ); } else { _gl.frontFace( _gl.CW ); } if( cullFace === "back" ) { _gl.cullFace( _gl.BACK ); } else if( cullFace === "front" ) { _gl.cullFace( _gl.FRONT ); } else { _gl.cullFace( _gl.FRONT_AND_BACK ); } _gl.enable( _gl.CULL_FACE ); } else { _gl.disable( _gl.CULL_FACE ); } }; this.setObjectFaces = function ( object ) { if ( _oldDoubleSided !== object.doubleSided ) { if( object.doubleSided ) { _gl.disable( _gl.CULL_FACE ); } else { _gl.enable( _gl.CULL_FACE ); } _oldDoubleSided = object.doubleSided; } if ( _oldFlipSided !== object.flipSided ) { if( object.flipSided ) { _gl.frontFace( _gl.CW ); } else { _gl.frontFace( _gl.CCW ); } _oldFlipSided = object.flipSided; } }; this.setDepthTest = function ( depthTest ) { if ( _oldDepthTest !== depthTest ) { if ( depthTest ) { _gl.enable( _gl.DEPTH_TEST ); } else { _gl.disable( _gl.DEPTH_TEST ); } _oldDepthTest = depthTest; } }; this.setDepthWrite = function ( depthWrite ) { if ( _oldDepthWrite !== depthWrite ) { _gl.depthMask( depthWrite ); _oldDepthWrite = depthWrite; } }; function setLineWidth ( width ) { if ( width !== _oldLineWidth ) { _gl.lineWidth( width ); _oldLineWidth = width; } }; 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; } }; this.setBlending = function ( blending ) { if ( blending !== _oldBlending ) { switch ( blending ) { case THREE.NoBlending: _gl.disable( _gl.BLEND ); break; case THREE.AdditiveBlending: _gl.enable( _gl.BLEND ); _gl.blendEquation( _gl.FUNC_ADD ); _gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE ); break; case THREE.SubtractiveBlending: // TODO: Find blendFuncSeparate() combination _gl.enable( _gl.BLEND ); _gl.blendEquation( _gl.FUNC_ADD ); _gl.blendFunc( _gl.ZERO, _gl.ONE_MINUS_SRC_COLOR ); break; case THREE.MultiplyBlending: // TODO: Find blendFuncSeparate() combination _gl.enable( _gl.BLEND ); _gl.blendEquation( _gl.FUNC_ADD ); _gl.blendFunc( _gl.ZERO, _gl.SRC_COLOR ); break; default: _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 ); break; } _oldBlending = blending; } }; // Shaders function buildProgram ( shaderID, fragmentShader, vertexShader, uniforms, attributes, parameters ) { var p, pl, program, code; var chunks = []; // Generate code if ( shaderID ) { chunks.push( shaderID ); } else { chunks.push( fragmentShader ); chunks.push( vertexShader ); } 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 = _programs.length; p < pl; p ++ ) { if ( _programs[ p ].code === code ) { // console.log( "Code already compiled." /*: \n\n" + code*/ ); return _programs[ p ].program; } } //console.log( "building new program " ); // program = _gl.createProgram(); var prefix_vertex = [ "precision " + _precision + " float;", ( _maxVertexTextures > 0 ) ? "#define VERTEX_TEXTURES" : "", _this.gammaInput ? "#define GAMMA_INPUT" : "", _this.gammaOutput ? "#define GAMMA_OUTPUT" : "", _this.physicallyBasedShading ? "#define PHYSICALLY_BASED_SHADING" : "", "#define MAX_DIR_LIGHTS " + parameters.maxDirLights, "#define MAX_POINT_LIGHTS " + parameters.maxPointLights, "#define MAX_SHADOWS " + parameters.maxShadows, "#define MAX_BONES " + parameters.maxBones, parameters.map ? "#define USE_MAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.vertexColors ? "#define USE_COLOR" : "", parameters.skinning ? "#define USE_SKINNING" : "", 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.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapSoft ? "#define SHADOWMAP_SOFT" : "", parameters.shadowMapDebug ? "#define SHADOWMAP_DEBUG" : "", parameters.shadowMapCascade ? "#define SHADOWMAP_CASCADE" : "", parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "", "uniform mat4 objectMatrix;", "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 skinVertexA;", "attribute vec4 skinVertexB;", "attribute vec4 skinIndex;", "attribute vec4 skinWeight;", "#endif", "" ].join("\n"); var prefix_fragment = [ "precision " + _precision + " float;", "#define MAX_DIR_LIGHTS " + parameters.maxDirLights, "#define MAX_POINT_LIGHTS " + parameters.maxPointLights, "#define MAX_SHADOWS " + parameters.maxShadows, parameters.alphaTest ? "#define ALPHATEST " + parameters.alphaTest: "", _this.gammaInput ? "#define GAMMA_INPUT" : "", _this.gammaOutput ? "#define GAMMA_OUTPUT" : "", _this.physicallyBasedShading ? "#define PHYSICALLY_BASED_SHADING" : "", ( parameters.useFog && parameters.fog ) ? "#define USE_FOG" : "", ( parameters.useFog && parameters.fog instanceof THREE.FogExp2 ) ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", 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.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapSoft ? "#define SHADOWMAP_SOFT" : "", parameters.shadowMapDebug ? "#define SHADOWMAP_DEBUG" : "", parameters.shadowMapCascade ? "#define SHADOWMAP_CASCADE" : "", "uniform mat4 viewMatrix;", "uniform vec3 cameraPosition;", "" ].join("\n"); _gl.attachShader( program, getShader( "fragment", prefix_fragment + fragmentShader ) ); _gl.attachShader( program, getShader( "vertex", prefix_vertex + vertexShader ) ); _gl.linkProgram( program ); if ( !_gl.getProgramParameter( program, _gl.LINK_STATUS ) ) { console.error( "Could not initialise shader\n" + "VALIDATE_STATUS: " + _gl.getProgramParameter( program, _gl.VALIDATE_STATUS ) + ", gl error [" + _gl.getError() + "]" ); } //console.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', 'objectMatrix', 'cameraPosition', 'boneGlobalMatrices', 'morphTargetInfluences' ]; for ( u in uniforms ) { identifiers.push( u ); } cacheUniformLocations( program, identifiers ); // cache attributes locations identifiers = [ "position", "normal", "uv", "uv2", "tangent", "color", "skinVertexA", "skinVertexB", "skinIndex", "skinWeight" ]; 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 ); } cacheAttributeLocations( program, identifiers ); program.id = _programs.length; _programs.push( { program: program, code: code } ); _this.info.memory.programs = _programs.length; return program; }; // Shader parameters cache function cacheUniformLocations ( program, identifiers ) { var i, l, id; for( i = 0, l = identifiers.length; i < l; i ++ ) { id = identifiers[ i ]; program.uniforms[ id ] = _gl.getUniformLocation( program, id ); } }; function cacheAttributeLocations ( program, identifiers ) { var i, l, id; for( i = 0, l = identifiers.length; i < l; i ++ ) { id = identifiers[ i ]; program.attributes[ id ] = _gl.getAttribLocation( program, id ); } }; 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( string ); return null; } return shader; }; // 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 ) ); } }; this.setTexture = function ( texture, slot ) { if ( texture.needsUpdate ) { if ( ! texture.__webglInit ) { texture.__webglInit = true; texture.__webglTexture = _gl.createTexture(); _this.info.memory.textures ++; } _gl.activeTexture( _gl.TEXTURE0 + slot ); _gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture ); 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 ); if ( texture instanceof THREE.DataTexture ) { _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data ); } 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 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 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 ); var cubeImage = []; for ( var i = 0; i < 6; i ++ ) { if ( _this.autoScaleCubemaps ) { 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 ++ ) { _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 ); } } }; function setCubeTextureDynamic ( texture, slot ) { _gl.activeTexture( _gl.TEXTURE0 + slot ); _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.__webglTexture ); }; // Render targets 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 ); } }; this.setRenderTarget = function ( 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.__webglTexture = _gl.createTexture(); // 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 ); } } else { renderTarget.__webglFramebuffer = _gl.createFramebuffer(); 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 ); setupRenderBuffer( renderTarget.__webglRenderbuffer, renderTarget ); } // 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 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 ); } }; // Fallback filters for non-power-of-2 textures function filterFallback ( f ) { switch ( f ) { case THREE.NearestFilter: case THREE.NearestMipMapNearestFilter: case THREE.NearestMipMapLinearFilter: return _gl.NEAREST; break; case THREE.LinearFilter: case THREE.LinearMipMapNearestFilter: case THREE.LinearMipMapLinearFilter: default: return _gl.LINEAR; break; } }; // Map three.js constants to WebGL constants function paramThreeToGL ( p ) { switch ( p ) { case THREE.RepeatWrapping: return _gl.REPEAT; break; case THREE.ClampToEdgeWrapping: return _gl.CLAMP_TO_EDGE; break; case THREE.MirroredRepeatWrapping: return _gl.MIRRORED_REPEAT; break; case THREE.NearestFilter: return _gl.NEAREST; break; case THREE.NearestMipMapNearestFilter: return _gl.NEAREST_MIPMAP_NEAREST; break; case THREE.NearestMipMapLinearFilter: return _gl.NEAREST_MIPMAP_LINEAR; break; case THREE.LinearFilter: return _gl.LINEAR; break; case THREE.LinearMipMapNearestFilter: return _gl.LINEAR_MIPMAP_NEAREST; break; case THREE.LinearMipMapLinearFilter: return _gl.LINEAR_MIPMAP_LINEAR; break; case THREE.ByteType: return _gl.BYTE; break; case THREE.UnsignedByteType: return _gl.UNSIGNED_BYTE; break; case THREE.ShortType: return _gl.SHORT; break; case THREE.UnsignedShortType: return _gl.UNSIGNED_SHORT; break; case THREE.IntType: return _gl.INT; break; case THREE.UnsignedIntType: return _gl.UNSIGNED_INT; break; case THREE.FloatType: return _gl.FLOAT; break; case THREE.AlphaFormat: return _gl.ALPHA; break; case THREE.RGBFormat: return _gl.RGB; break; case THREE.RGBAFormat: return _gl.RGBA; break; case THREE.LuminanceFormat: return _gl.LUMINANCE; break; case THREE.LuminanceAlphaFormat: return _gl.LUMINANCE_ALPHA; break; } return 0; }; // Allocations function allocateBones ( object ) { // 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 maxBones = 50; if ( object !== undefined && object instanceof THREE.SkinnedMesh ) { maxBones = object.bones.length; } return maxBones; }; function allocateLights ( lights ) { var l, ll, light, dirLights, pointLights, maxDirLights, maxPointLights; dirLights = pointLights = maxDirLights = maxPointLights = 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 ) pointLights ++; } if ( ( pointLights + dirLights ) <= _maxLights ) { maxDirLights = dirLights; maxPointLights = pointLights; } else { maxDirLights = Math.ceil( _maxLights * dirLights / ( pointLights + dirLights ) ); maxPointLights = _maxLights - maxDirLights; } return { 'directional' : maxDirLights, 'point' : maxPointLights }; }; 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; }; // Initialization function initGL () { var gl; try { if ( ! ( gl = _canvas.getContext( 'experimental-webgl', { alpha: _alpha, premultipliedAlpha: _premultipliedAlpha, antialias: _antialias, stencil: _stencil, preserveDrawingBuffer: _preserveDrawingBuffer } ) ) ) { throw 'Error creating WebGL context.'; } console.log( navigator.userAgent + " | " + gl.getParameter( gl.VERSION ) + " | " + gl.getParameter( gl.VENDOR ) + " | " + gl.getParameter( gl.RENDERER ) + " | " + gl.getParameter( gl.SHADING_LANGUAGE_VERSION ) ); } catch ( error ) { console.error( error ); } return gl; }; 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 ); }; // default plugins (order is important) this.shadowMapPlugin = new THREE.ShadowMapPlugin(); this.addPrePlugin( this.shadowMapPlugin ); this.addPostPlugin( new THREE.SpritePlugin() ); this.addPostPlugin( new THREE.LensFlarePlugin() ); };