/** * @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 ) { console.log( 'THREE.WebGLRenderer', THREE.REVISION ); parameters = parameters || {}; var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElement( 'canvas' ), _context = parameters.context !== undefined ? parameters.context : null, pixelRatio = 1, _precision = parameters.precision !== undefined ? parameters.precision : 'highp', _alpha = parameters.alpha !== undefined ? parameters.alpha : false, _depth = parameters.depth !== undefined ? parameters.depth : true, _stencil = parameters.stencil !== undefined ? parameters.stencil : true, _antialias = parameters.antialias !== undefined ? parameters.antialias : false, _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, _logarithmicDepthBuffer = parameters.logarithmicDepthBuffer !== undefined ? parameters.logarithmicDepthBuffer : false, _clearColor = new THREE.Color( 0x000000 ), _clearAlpha = 0; var lights = []; var _webglObjects = {}; var _webglObjectsImmediate = []; var opaqueObjects = []; var transparentObjects = []; var sprites = []; var lensFlares = []; // 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; // physically based shading this.gammaInput = false; this.gammaOutput = false; // shadow map this.shadowMapEnabled = false; this.shadowMapType = THREE.PCFShadowMap; this.shadowMapCullFace = THREE.CullFaceFront; this.shadowMapDebug = false; this.shadowMapCascade = false; // morphs this.maxMorphTargets = 8; this.maxMorphNormals = 4; // flags this.autoScaleCubemaps = true; // info this.info = { memory: { programs: 0, geometries: 0, textures: 0 }, render: { calls: 0, vertices: 0, faces: 0, points: 0 } }; // internal properties var _this = this, _programs = [], // internal state cache _currentProgram = null, _currentFramebuffer = null, _currentMaterialId = - 1, _currentGeometryProgram = '', _currentCamera = null, _usedTextureUnits = 0, // GL state cache _oldDoubleSided = - 1, _oldFlipSided = - 1, _oldBlending = - 1, _oldBlendEquation = - 1, _oldBlendSrc = - 1, _oldBlendDst = - 1, _oldBlendEquationAlpha = - 1, _oldBlendSrcAlpha = - 1, _oldBlendDstAlpha = - 1, _oldDepthTest = - 1, _oldDepthWrite = - 1, _oldPolygonOffset = null, _oldPolygonOffsetFactor = null, _oldPolygonOffsetUnits = null, _oldLineWidth = null, _viewportX = 0, _viewportY = 0, _viewportWidth = _canvas.width, _viewportHeight = _canvas.height, _currentWidth = 0, _currentHeight = 0, _newAttributes = new Uint8Array( 16 ), _enabledAttributes = new Uint8Array( 16 ), // frustum _frustum = new THREE.Frustum(), // camera matrices cache _projScreenMatrix = new THREE.Matrix4(), _projScreenMatrixPS = new THREE.Matrix4(), _vector3 = new THREE.Vector3(), // light arrays cache _direction = new THREE.Vector3(), _lightsNeedUpdate = true, _lights = { ambient: [ 0, 0, 0 ], directional: { length: 0, colors:[], positions: [] }, point: { length: 0, colors: [], positions: [], distances: [] }, spot: { length: 0, colors: [], positions: [], distances: [], directions: [], anglesCos: [], exponents: [] }, hemi: { length: 0, skyColors: [], groundColors: [], positions: [] } }; // initialize var _gl; try { var attributes = { alpha: _alpha, depth: _depth, stencil: _stencil, antialias: _antialias, premultipliedAlpha: _premultipliedAlpha, preserveDrawingBuffer: _preserveDrawingBuffer }; _gl = _context || _canvas.getContext( 'webgl', attributes ) || _canvas.getContext( 'experimental-webgl', attributes ); if ( _gl === null ) { if ( _canvas.getContext( 'webgl') !== null ) { throw 'Error creating WebGL context with your selected attributes.'; } else { throw 'Error creating WebGL context.'; } } _canvas.addEventListener( 'webglcontextlost', function ( event ) { event.preventDefault(); resetGLState(); setDefaultGLState(); _webglObjects = {}; }, false); } catch ( error ) { console.error( error ); } if ( _gl.getShaderPrecisionFormat === undefined ) { _gl.getShaderPrecisionFormat = function () { return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 }; } } var extensions = new THREE.WebGLExtensions( _gl ); extensions.get( 'OES_texture_float' ); extensions.get( 'OES_texture_float_linear' ); extensions.get( 'OES_standard_derivatives' ); if ( _logarithmicDepthBuffer ) { extensions.get( 'EXT_frag_depth' ); } // var setDefaultGLState = function () { _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.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight ); _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); }; var resetGLState = function () { _currentProgram = null; _currentCamera = null; _oldBlending = - 1; _oldDepthTest = - 1; _oldDepthWrite = - 1; _oldDoubleSided = - 1; _oldFlipSided = - 1; _currentGeometryProgram = ''; _currentMaterialId = - 1; _lightsNeedUpdate = true; for ( var i = 0; i < _enabledAttributes.length; i ++ ) { _enabledAttributes[ i ] = 0; } }; setDefaultGLState(); this.context = _gl; // GPU capabilities var _maxTextures = _gl.getParameter( _gl.MAX_TEXTURE_IMAGE_UNITS ); var _maxVertexTextures = _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ); var _maxTextureSize = _gl.getParameter( _gl.MAX_TEXTURE_SIZE ); var _maxCubemapSize = _gl.getParameter( _gl.MAX_CUBE_MAP_TEXTURE_SIZE ); var _supportsVertexTextures = _maxVertexTextures > 0; var _supportsBoneTextures = _supportsVertexTextures && extensions.get( 'OES_texture_float' ); // var _vertexShaderPrecisionHighpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.HIGH_FLOAT ); var _vertexShaderPrecisionMediumpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.MEDIUM_FLOAT ); var _vertexShaderPrecisionLowpFloat = _gl.getShaderPrecisionFormat( _gl.VERTEX_SHADER, _gl.LOW_FLOAT ); var _fragmentShaderPrecisionHighpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.HIGH_FLOAT ); var _fragmentShaderPrecisionMediumpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.MEDIUM_FLOAT ); var _fragmentShaderPrecisionLowpFloat = _gl.getShaderPrecisionFormat( _gl.FRAGMENT_SHADER, _gl.LOW_FLOAT ); var getCompressedTextureFormats = ( function () { var array; return function () { if ( array !== undefined ) { return array; } array = []; if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) || extensions.get( 'WEBGL_compressed_texture_s3tc' ) ) { var formats = _gl.getParameter( _gl.COMPRESSED_TEXTURE_FORMATS ); for ( var i = 0; i < formats.length; i ++ ){ array.push( formats[ i ] ); } } return array; }; } )(); // clamp precision to maximum available var highpAvailable = _vertexShaderPrecisionHighpFloat.precision > 0 && _fragmentShaderPrecisionHighpFloat.precision > 0; var mediumpAvailable = _vertexShaderPrecisionMediumpFloat.precision > 0 && _fragmentShaderPrecisionMediumpFloat.precision > 0; if ( _precision === 'highp' && ! highpAvailable ) { if ( mediumpAvailable ) { _precision = 'mediump'; console.warn( 'THREE.WebGLRenderer: highp not supported, using mediump.' ); } else { _precision = 'lowp'; console.warn( 'THREE.WebGLRenderer: highp and mediump not supported, using lowp.' ); } } if ( _precision === 'mediump' && ! mediumpAvailable ) { _precision = 'lowp'; console.warn( 'THREE.WebGLRenderer: mediump not supported, using lowp.' ); } // Plugins var shadowMapPlugin = new THREE.ShadowMapPlugin( this, lights, _webglObjects, _webglObjectsImmediate ); var spritePlugin = new THREE.SpritePlugin( this, sprites ); var lensFlarePlugin = new THREE.LensFlarePlugin( this, lensFlares ); // API this.getContext = function () { return _gl; }; this.forceContextLoss = function () { extensions.get( 'WEBGL_lose_context' ).loseContext(); }; this.supportsVertexTextures = function () { return _supportsVertexTextures; }; this.supportsFloatTextures = function () { return extensions.get( 'OES_texture_float' ); }; this.supportsStandardDerivatives = function () { return extensions.get( 'OES_standard_derivatives' ); }; this.supportsCompressedTextureS3TC = function () { return extensions.get( 'WEBGL_compressed_texture_s3tc' ); }; this.supportsCompressedTexturePVRTC = function () { return extensions.get( 'WEBGL_compressed_texture_pvrtc' ); }; this.supportsBlendMinMax = function () { return extensions.get( 'EXT_blend_minmax' ); }; this.getMaxAnisotropy = ( function () { var value; return function () { if ( value !== undefined ) { return value; } var extension = extensions.get( 'EXT_texture_filter_anisotropic' ); value = extension !== null ? _gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ) : 0; return value; } } )(); this.getPrecision = function () { return _precision; }; this.getPixelRatio = function () { return pixelRatio; }; this.setPixelRatio = function ( value ) { pixelRatio = value; }; this.setSize = function ( width, height, updateStyle ) { _canvas.width = width * pixelRatio; _canvas.height = height * pixelRatio; if ( updateStyle !== false ) { _canvas.style.width = width + 'px'; _canvas.style.height = height + 'px'; } this.setViewport( 0, 0, width, height ); }; this.setViewport = function ( x, y, width, height ) { _viewportX = x * pixelRatio; _viewportY = y * pixelRatio; _viewportWidth = width * pixelRatio; _viewportHeight = height * pixelRatio; _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight ); }; this.setScissor = function ( x, y, width, height ) { _gl.scissor( x * pixelRatio, y * pixelRatio, width * pixelRatio, height * pixelRatio ); }; this.enableScissorTest = function ( enable ) { enable ? _gl.enable( _gl.SCISSOR_TEST ) : _gl.disable( _gl.SCISSOR_TEST ); }; // Clearing this.getClearColor = function () { return _clearColor; }; this.setClearColor = function ( color, alpha ) { _clearColor.set( color ); _clearAlpha = alpha !== undefined ? alpha : 1; _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); }; this.getClearAlpha = function () { return _clearAlpha; }; this.setClearAlpha = function ( alpha ) { _clearAlpha = alpha; _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _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.clearColor = function () { _gl.clear( _gl.COLOR_BUFFER_BIT ); }; this.clearDepth = function () { _gl.clear( _gl.DEPTH_BUFFER_BIT ); }; this.clearStencil = function () { _gl.clear( _gl.STENCIL_BUFFER_BIT ); }; this.clearTarget = function ( renderTarget, color, depth, stencil ) { this.setRenderTarget( renderTarget ); this.clear( color, depth, stencil ); }; // Reset this.resetGLState = resetGLState; // Buffer allocation function createParticleBuffers ( geometry ) { geometry.__webglVertexBuffer = _gl.createBuffer(); geometry.__webglColorBuffer = _gl.createBuffer(); _this.info.memory.geometries ++; }; function createLineBuffers ( geometry ) { geometry.__webglVertexBuffer = _gl.createBuffer(); geometry.__webglColorBuffer = _gl.createBuffer(); geometry.__webglLineDistanceBuffer = _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.__webglSkinIndicesBuffer = _gl.createBuffer(); geometryGroup.__webglSkinWeightsBuffer = _gl.createBuffer(); geometryGroup.__webglFaceBuffer = _gl.createBuffer(); geometryGroup.__webglLineBuffer = _gl.createBuffer(); var numMorphTargets = geometryGroup.numMorphTargets; if ( numMorphTargets ) { geometryGroup.__webglMorphTargetsBuffers = []; for ( var m = 0, ml = numMorphTargets; m < ml; m ++ ) { geometryGroup.__webglMorphTargetsBuffers.push( _gl.createBuffer() ); } } var numMorphNormals = geometryGroup.numMorphNormals; if ( numMorphNormals ) { geometryGroup.__webglMorphNormalsBuffers = []; for ( var m = 0, ml = numMorphNormals; m < ml; m ++ ) { geometryGroup.__webglMorphNormalsBuffers.push( _gl.createBuffer() ); } } _this.info.memory.geometries ++; }; // Events var onObjectRemoved = function ( event ) { var object = event.target; object.traverse( function ( child ) { child.removeEventListener( 'remove', onObjectRemoved ); removeObject( child ); } ); }; var onGeometryDispose = function ( event ) { var geometry = event.target; geometry.removeEventListener( 'dispose', onGeometryDispose ); deallocateGeometry( geometry ); }; var onTextureDispose = function ( event ) { var texture = event.target; texture.removeEventListener( 'dispose', onTextureDispose ); deallocateTexture( texture ); _this.info.memory.textures --; }; var onRenderTargetDispose = function ( event ) { var renderTarget = event.target; renderTarget.removeEventListener( 'dispose', onRenderTargetDispose ); deallocateRenderTarget( renderTarget ); _this.info.memory.textures --; }; var onMaterialDispose = function ( event ) { var material = event.target; material.removeEventListener( 'dispose', onMaterialDispose ); deallocateMaterial( material ); }; // Buffer deallocation var deleteBuffers = function ( geometry ) { var buffers = [ '__webglVertexBuffer', '__webglNormalBuffer', '__webglTangentBuffer', '__webglColorBuffer', '__webglUVBuffer', '__webglUV2Buffer', '__webglSkinIndicesBuffer', '__webglSkinWeightsBuffer', '__webglFaceBuffer', '__webglLineBuffer', '__webglLineDistanceBuffer' ]; for ( var i = 0, l = buffers.length; i < l; i ++ ) { var name = buffers[ i ]; if ( geometry[ name ] !== undefined ) { _gl.deleteBuffer( geometry[ name ] ); delete geometry[ name ]; } } // custom attributes if ( geometry.__webglCustomAttributesList !== undefined ) { for ( var name in geometry.__webglCustomAttributesList ) { _gl.deleteBuffer( geometry.__webglCustomAttributesList[ name ].buffer ); } delete geometry.__webglCustomAttributesList; } _this.info.memory.geometries --; }; var deallocateGeometry = function ( geometry ) { delete geometry.__webglInit; if ( geometry instanceof THREE.BufferGeometry ) { for ( var name in geometry.attributes ) { var attribute = geometry.attributes[ name ]; if ( attribute.buffer !== undefined ) { _gl.deleteBuffer( attribute.buffer ); delete attribute.buffer; } } _this.info.memory.geometries --; } else { var geometryGroupsList = geometryGroups[ geometry.id ]; if ( geometryGroupsList !== undefined ) { for ( var i = 0, l = geometryGroupsList.length; i < l; i ++ ) { var geometryGroup = geometryGroupsList[ i ]; if ( geometryGroup.numMorphTargets !== undefined ) { for ( var m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) { _gl.deleteBuffer( geometryGroup.__webglMorphTargetsBuffers[ m ] ); } delete geometryGroup.__webglMorphTargetsBuffers; } if ( geometryGroup.numMorphNormals !== undefined ) { for ( var m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) { _gl.deleteBuffer( geometryGroup.__webglMorphNormalsBuffers[ m ] ); } delete geometryGroup.__webglMorphNormalsBuffers; } deleteBuffers( geometryGroup ); } delete geometryGroups[ geometry.id ]; } else { deleteBuffers( geometry ); } } // TOFIX: Workaround for deleted geometry being currently bound _currentGeometryProgram = ''; }; var deallocateTexture = function ( texture ) { if ( texture.image && texture.image.__webglTextureCube ) { // cube texture _gl.deleteTexture( texture.image.__webglTextureCube ); delete texture.image.__webglTextureCube; } else { // 2D texture if ( texture.__webglInit === undefined ) return; _gl.deleteTexture( texture.__webglTexture ); delete texture.__webglTexture; delete texture.__webglInit; } }; var deallocateRenderTarget = function ( renderTarget ) { if ( ! renderTarget || renderTarget.__webglTexture === undefined ) return; _gl.deleteTexture( renderTarget.__webglTexture ); delete renderTarget.__webglTexture; if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) { for ( var i = 0; i < 6; i ++ ) { _gl.deleteFramebuffer( renderTarget.__webglFramebuffer[ i ] ); _gl.deleteRenderbuffer( renderTarget.__webglRenderbuffer[ i ] ); } } else { _gl.deleteFramebuffer( renderTarget.__webglFramebuffer ); _gl.deleteRenderbuffer( renderTarget.__webglRenderbuffer ); } delete renderTarget.__webglFramebuffer; delete renderTarget.__webglRenderbuffer; }; var deallocateMaterial = function ( material ) { var program = material.program.program; if ( program === undefined ) return; material.program = undefined; // only deallocate GL program if this was the last use of shared program // assumed there is only single copy of any program in the _programs list // (that's how it's constructed) var i, il, programInfo; var deleteProgram = false; for ( i = 0, il = _programs.length; i < il; i ++ ) { programInfo = _programs[ i ]; if ( programInfo.program === program ) { programInfo.usedTimes --; if ( programInfo.usedTimes === 0 ) { deleteProgram = true; } break; } } if ( deleteProgram === true ) { // avoid using array.splice, this is costlier than creating new array from scratch var newPrograms = []; for ( i = 0, il = _programs.length; i < il; i ++ ) { programInfo = _programs[ i ]; if ( programInfo.program !== program ) { newPrograms.push( programInfo ); } } _programs = newPrograms; _gl.deleteProgram( program ); _this.info.memory.programs --; } }; // Buffer initialization function initCustomAttributes ( object ) { var geometry = object.geometry; var material = object.material; var nvertices = geometry.vertices.length; if ( material.attributes ) { if ( geometry.__webglCustomAttributesList === undefined ) { geometry.__webglCustomAttributesList = []; } for ( var name in material.attributes ) { var attribute = material.attributes[ name ]; 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 = name; 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( object ); }; function initLineBuffers ( geometry, object ) { var nvertices = geometry.vertices.length; geometry.__vertexArray = new Float32Array( nvertices * 3 ); geometry.__colorArray = new Float32Array( nvertices * 3 ); geometry.__lineDistanceArray = new Float32Array( nvertices * 1 ); geometry.__webglLineCount = nvertices; initCustomAttributes( object ); }; function initMeshBuffers ( geometryGroup, object ) { var geometry = object.geometry, faces3 = geometryGroup.faces3, nvertices = faces3.length * 3, ntris = faces3.length * 1, nlines = faces3.length * 3, material = getBufferMaterial( object, geometryGroup ); geometryGroup.__vertexArray = new Float32Array( nvertices * 3 ); geometryGroup.__normalArray = new Float32Array( nvertices * 3 ); geometryGroup.__colorArray = new Float32Array( nvertices * 3 ); geometryGroup.__uvArray = new Float32Array( nvertices * 2 ); if ( geometry.faceVertexUvs.length > 1 ) { geometryGroup.__uv2Array = new Float32Array( nvertices * 2 ); } if ( geometry.hasTangents ) { geometryGroup.__tangentArray = new Float32Array( nvertices * 4 ); } if ( object.geometry.skinWeights.length && object.geometry.skinIndices.length ) { geometryGroup.__skinIndexArray = new Float32Array( nvertices * 4 ); geometryGroup.__skinWeightArray = new Float32Array( nvertices * 4 ); } var UintArray = extensions.get( 'OES_element_index_uint' ) !== null && ntris > 21845 ? Uint32Array : Uint16Array; // 65535 / 3 geometryGroup.__typeArray = UintArray; geometryGroup.__faceArray = new UintArray( ntris * 3 ); geometryGroup.__lineArray = new UintArray( nlines * 2 ); var numMorphTargets = geometryGroup.numMorphTargets; if ( numMorphTargets ) { geometryGroup.__morphTargetsArrays = []; for ( var m = 0, ml = numMorphTargets; m < ml; m ++ ) { geometryGroup.__morphTargetsArrays.push( new Float32Array( nvertices * 3 ) ); } } var numMorphNormals = geometryGroup.numMorphNormals; if ( numMorphNormals ) { geometryGroup.__morphNormalsArrays = []; for ( var m = 0, ml = 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 name 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[ name ]; 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 = name; originalAttribute.needsUpdate = true; attribute.__original = originalAttribute; } geometryGroup.__webglCustomAttributesList.push( attribute ); } } geometryGroup.__inittedArrays = true; }; function getBufferMaterial( object, geometryGroup ) { return object.material instanceof THREE.MeshFaceMaterial ? object.material.materials[ geometryGroup.materialIndex ] : object.material; }; function materialNeedsSmoothNormals ( material ) { return material && material.shading !== undefined && material.shading === THREE.SmoothShading; }; // 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.verticesNeedUpdate, dirtyElements = geometry.elementsNeedUpdate, dirtyColors = geometry.colorsNeedUpdate, customAttributes = geometry.__webglCustomAttributesList, i, il, a, ca, cal, value, customAttribute; if ( dirtyVertices ) { for ( v = 0; v < vl; v ++ ) { vertex = vertices[ v ]; offset = v * 3; vertexArray[ offset ] = vertex.x; vertexArray[ offset + 1 ] = vertex.y; vertexArray[ offset + 2 ] = vertex.z; } _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' ) ) { 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; } } } _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); customAttribute.needsUpdate = false; } } } function setLineBuffers ( geometry, hint ) { var v, c, d, vertex, offset, color, vertices = geometry.vertices, colors = geometry.colors, lineDistances = geometry.lineDistances, vl = vertices.length, cl = colors.length, dl = lineDistances.length, vertexArray = geometry.__vertexArray, colorArray = geometry.__colorArray, lineDistanceArray = geometry.__lineDistanceArray, dirtyVertices = geometry.verticesNeedUpdate, dirtyColors = geometry.colorsNeedUpdate, dirtyLineDistances = geometry.lineDistancesNeedUpdate, customAttributes = geometry.__webglCustomAttributesList, i, il, a, ca, cal, value, customAttribute; if ( dirtyVertices ) { for ( v = 0; v < vl; v ++ ) { vertex = vertices[ v ]; offset = v * 3; vertexArray[ offset ] = vertex.x; vertexArray[ offset + 1 ] = vertex.y; vertexArray[ offset + 2 ] = vertex.z; } _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 ( dirtyLineDistances ) { for ( d = 0; d < dl; d ++ ) { lineDistanceArray[ d ] = lineDistances[ d ]; } _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglLineDistanceBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, lineDistanceArray, 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 ); customAttribute.needsUpdate = false; } } } } function setMeshBuffers( geometryGroup, object, hint, dispose, material ) { if ( ! geometryGroup.__inittedArrays ) { return; } var needsSmoothNormals = materialNeedsSmoothNormals( material ); 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, sw1, sw2, sw3, sw4, si1, si2, si3, si4, sa1, sa2, sa3, sa4, sb1, sb2, sb3, sb4, m, ml, i, il, vn, uvi, uv2i, vk, vkl, vka, nka, chf, faceVertexNormals, a, vertexIndex = 0, offset = 0, offset_uv = 0, offset_uv2 = 0, offset_face = 0, offset_normal = 0, offset_tangent = 0, offset_line = 0, offset_color = 0, offset_skin = 0, offset_morphTarget = 0, offset_custom = 0, offset_customSrc = 0, value, vertexArray = geometryGroup.__vertexArray, uvArray = geometryGroup.__uvArray, uv2Array = geometryGroup.__uv2Array, normalArray = geometryGroup.__normalArray, tangentArray = geometryGroup.__tangentArray, colorArray = geometryGroup.__colorArray, skinIndexArray = geometryGroup.__skinIndexArray, skinWeightArray = geometryGroup.__skinWeightArray, morphTargetsArrays = geometryGroup.__morphTargetsArrays, morphNormalsArrays = geometryGroup.__morphNormalsArrays, customAttributes = geometryGroup.__webglCustomAttributesList, customAttribute, faceArray = geometryGroup.__faceArray, lineArray = geometryGroup.__lineArray, geometry = object.geometry, // this is shared for all chunks dirtyVertices = geometry.verticesNeedUpdate, dirtyElements = geometry.elementsNeedUpdate, dirtyUvs = geometry.uvsNeedUpdate, dirtyNormals = geometry.normalsNeedUpdate, dirtyTangents = geometry.tangentsNeedUpdate, dirtyColors = geometry.colorsNeedUpdate, dirtyMorphTargets = geometry.morphTargetsNeedUpdate, vertices = geometry.vertices, chunk_faces3 = geometryGroup.faces3, obj_faces = geometry.faces, obj_uvs = geometry.faceVertexUvs[ 0 ], obj_uvs2 = geometry.faceVertexUvs[ 1 ], obj_colors = geometry.colors, obj_skinIndices = geometry.skinIndices, obj_skinWeights = geometry.skinWeights, morphTargets = geometry.morphTargets, morphNormals = geometry.morphNormals; if ( dirtyVertices ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { face = obj_faces[ chunk_faces3[ f ] ]; v1 = vertices[ face.a ]; v2 = vertices[ face.b ]; v3 = vertices[ face.c ]; vertexArray[ offset ] = v1.x; vertexArray[ offset + 1 ] = v1.y; vertexArray[ offset + 2 ] = v1.z; vertexArray[ offset + 3 ] = v2.x; vertexArray[ offset + 4 ] = v2.y; vertexArray[ offset + 5 ] = v2.z; vertexArray[ offset + 6 ] = v3.x; vertexArray[ offset + 7 ] = v3.y; vertexArray[ offset + 8 ] = v3.z; offset += 9; } _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 ]; v2 = morphTargets[ vk ].vertices[ face.b ]; v3 = morphTargets[ vk ].vertices[ face.c ]; vka = morphTargetsArrays[ vk ]; vka[ offset_morphTarget ] = v1.x; vka[ offset_morphTarget + 1 ] = v1.y; vka[ offset_morphTarget + 2 ] = v1.z; vka[ offset_morphTarget + 3 ] = v2.x; vka[ offset_morphTarget + 4 ] = v2.y; vka[ offset_morphTarget + 5 ] = v2.z; vka[ offset_morphTarget + 6 ] = v3.x; vka[ offset_morphTarget + 7 ] = v3.y; vka[ offset_morphTarget + 8 ] = v3.z; // morph normals if ( material.morphNormals ) { if ( needsSmoothNormals ) { faceVertexNormals = morphNormals[ vk ].vertexNormals[ chf ]; n1 = faceVertexNormals.a; n2 = faceVertexNormals.b; n3 = faceVertexNormals.c; } else { n1 = morphNormals[ vk ].faceNormals[ chf ]; n2 = n1; n3 = n1; } nka = morphNormalsArrays[ vk ]; nka[ offset_morphTarget ] = n1.x; nka[ offset_morphTarget + 1 ] = n1.y; nka[ offset_morphTarget + 2 ] = n1.z; nka[ offset_morphTarget + 3 ] = n2.x; nka[ offset_morphTarget + 4 ] = n2.y; nka[ offset_morphTarget + 5 ] = n2.z; nka[ offset_morphTarget + 6 ] = n3.x; nka[ offset_morphTarget + 7 ] = n3.y; nka[ offset_morphTarget + 8 ] = n3.z; } // offset_morphTarget += 9; } _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; offset_skin += 12; } if ( offset_skin > 0 ) { _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 ) { 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 && material.vertexColors === 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; } 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; } _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, tangentArray, hint ); } if ( dirtyNormals ) { 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; } } } _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, normalArray, hint ); } if ( dirtyUvs && obj_uvs ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { fi = chunk_faces3[ f ]; uv = obj_uvs[ fi ]; if ( uv === undefined ) continue; for ( i = 0; i < 3; i ++ ) { uvi = uv[ i ]; uvArray[ offset_uv ] = uvi.x; uvArray[ offset_uv + 1 ] = uvi.y; offset_uv += 2; } } if ( offset_uv > 0 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, uvArray, hint ); } } if ( dirtyUvs && obj_uvs2 ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { fi = chunk_faces3[ f ]; uv2 = obj_uvs2[ fi ]; if ( uv2 === undefined ) continue; for ( i = 0; i < 3; i ++ ) { uv2i = uv2[ i ]; uv2Array[ offset_uv2 ] = uv2i.x; uv2Array[ offset_uv2 + 1 ] = uv2i.y; offset_uv2 += 2; } } 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 ++ ) { 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; } _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; } } 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; } } } 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; } } 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; } } } 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; } } 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; } } else if ( customAttribute.boundTo === 'faceVertices' ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { value = customAttribute.value[ chunk_faces3[ f ] ]; v1 = value[ 0 ]; v2 = value[ 1 ]; v3 = value[ 2 ]; customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; offset_custom += 9; } } } 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; } } 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; } } else if ( customAttribute.boundTo === 'faceVertices' ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { value = customAttribute.value[ chunk_faces3[ f ] ]; v1 = value[ 0 ]; v2 = value[ 1 ]; v3 = value[ 2 ]; customAttribute.array[ offset_custom ] = v1.x; customAttribute.array[ offset_custom + 1 ] = v1.y; customAttribute.array[ offset_custom + 2 ] = v1.z; customAttribute.array[ offset_custom + 3 ] = v1.w; customAttribute.array[ offset_custom + 4 ] = v2.x; customAttribute.array[ offset_custom + 5 ] = v2.y; customAttribute.array[ offset_custom + 6 ] = v2.z; customAttribute.array[ offset_custom + 7 ] = v2.w; customAttribute.array[ offset_custom + 8 ] = v3.x; customAttribute.array[ offset_custom + 9 ] = v3.y; customAttribute.array[ offset_custom + 10 ] = v3.z; customAttribute.array[ offset_custom + 11 ] = v3.w; offset_custom += 12; } } } _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.__skinIndexArray; delete geometryGroup.__skinWeightArray; } }; // Buffer rendering this.renderBufferImmediate = function ( object, program, material ) { initAttributes(); if ( object.hasPositions && ! object.__webglVertexBuffer ) object.__webglVertexBuffer = _gl.createBuffer(); if ( object.hasNormals && ! object.__webglNormalBuffer ) object.__webglNormalBuffer = _gl.createBuffer(); if ( object.hasUvs && ! object.__webglUvBuffer ) object.__webglUvBuffer = _gl.createBuffer(); if ( object.hasColors && ! object.__webglColorBuffer ) object.__webglColorBuffer = _gl.createBuffer(); if ( object.hasPositions ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglVertexBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW ); enableAttribute( program.attributes.position ); _gl.vertexAttribPointer( program.attributes.position, 3, _gl.FLOAT, false, 0, 0 ); } if ( object.hasNormals ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglNormalBuffer ); if ( material.shading === THREE.FlatShading ) { var nx, ny, nz, nax, nbx, ncx, nay, nby, ncy, naz, nbz, ncz, normalArray, i, il = object.count * 3; for ( i = 0; i < il; i += 9 ) { normalArray = object.normalArray; nax = normalArray[ i ]; nay = normalArray[ i + 1 ]; naz = normalArray[ i + 2 ]; nbx = normalArray[ i + 3 ]; nby = normalArray[ i + 4 ]; nbz = normalArray[ i + 5 ]; ncx = normalArray[ i + 6 ]; ncy = normalArray[ i + 7 ]; ncz = normalArray[ i + 8 ]; nx = ( nax + nbx + ncx ) / 3; ny = ( nay + nby + ncy ) / 3; nz = ( naz + nbz + ncz ) / 3; normalArray[ i ] = nx; normalArray[ i + 1 ] = ny; normalArray[ i + 2 ] = nz; normalArray[ i + 3 ] = nx; normalArray[ i + 4 ] = ny; normalArray[ i + 5 ] = nz; normalArray[ i + 6 ] = nx; normalArray[ i + 7 ] = ny; normalArray[ i + 8 ] = nz; } } _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW ); enableAttribute( program.attributes.normal ); _gl.vertexAttribPointer( program.attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); } if ( object.hasUvs && material.map ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglUvBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW ); enableAttribute( program.attributes.uv ); _gl.vertexAttribPointer( program.attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); } if ( object.hasColors && material.vertexColors !== THREE.NoColors ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglColorBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW ); enableAttribute( program.attributes.color ); _gl.vertexAttribPointer( program.attributes.color, 3, _gl.FLOAT, false, 0, 0 ); } disableUnusedAttributes(); _gl.drawArrays( _gl.TRIANGLES, 0, object.count ); object.count = 0; }; function setupVertexAttributes( material, program, geometry, startIndex ) { var geometryAttributes = geometry.attributes; var programAttributes = program.attributes; var programAttributesKeys = program.attributesKeys; for ( var i = 0, l = programAttributesKeys.length; i < l; i ++ ) { var key = programAttributesKeys[ i ]; var programAttribute = programAttributes[ key ]; if ( programAttribute >= 0 ) { var geometryAttribute = geometryAttributes[ key ]; if ( geometryAttribute !== undefined ) { var size = geometryAttribute.itemSize; _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryAttribute.buffer ); enableAttribute( programAttribute ); _gl.vertexAttribPointer( programAttribute, size, _gl.FLOAT, false, 0, startIndex * size * 4 ); // 4 bytes per Float32 } else if ( material.defaultAttributeValues !== undefined ) { if ( material.defaultAttributeValues[ key ].length === 2 ) { _gl.vertexAttrib2fv( programAttribute, material.defaultAttributeValues[ key ] ); } else if ( material.defaultAttributeValues[ key ].length === 3 ) { _gl.vertexAttrib3fv( programAttribute, material.defaultAttributeValues[ key ] ); } } } } disableUnusedAttributes(); } this.renderBufferDirect = function ( camera, lights, fog, material, geometry, object ) { if ( material.visible === false ) return; updateObject( object ); var program = setProgram( camera, lights, fog, material, object ); var updateBuffers = false, wireframeBit = material.wireframe ? 1 : 0, geometryProgram = 'direct_' + geometry.id + '_' + program.id + '_' + wireframeBit; if ( geometryProgram !== _currentGeometryProgram ) { _currentGeometryProgram = geometryProgram; updateBuffers = true; } if ( updateBuffers ) { initAttributes(); } // render mesh if ( object instanceof THREE.Mesh ) { var mode = material.wireframe === true ? _gl.LINES : _gl.TRIANGLES; var index = geometry.attributes.index; if ( index ) { // indexed triangles var type, size; if ( index.array instanceof Uint32Array && extensions.get( 'OES_element_index_uint' ) ) { type = _gl.UNSIGNED_INT; size = 4; } else { type = _gl.UNSIGNED_SHORT; size = 2; } var offsets = geometry.offsets; if ( offsets.length === 0 ) { if ( updateBuffers ) { setupVertexAttributes( material, program, geometry, 0 ); _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); } _gl.drawElements( mode, index.array.length, type, 0 ); _this.info.render.calls ++; _this.info.render.vertices += index.array.length; // not really true, here vertices can be shared _this.info.render.faces += index.array.length / 3; } else { // if there is more than 1 chunk // must set attribute pointers to use new offsets for each chunk // even if geometry and materials didn't change updateBuffers = true; for ( var i = 0, il = offsets.length; i < il; i ++ ) { var startIndex = offsets[ i ].index; if ( updateBuffers ) { setupVertexAttributes( material, program, geometry, startIndex ); _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); } // render indexed triangles _gl.drawElements( mode, offsets[ i ].count, type, offsets[ i ].start * size ); _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; } } } else { // non-indexed triangles if ( updateBuffers ) { setupVertexAttributes( material, program, geometry, 0 ); } var position = geometry.attributes[ 'position' ]; // render non-indexed triangles _gl.drawArrays( mode, 0, position.array.length / 3 ); _this.info.render.calls ++; _this.info.render.vertices += position.array.length / 3; _this.info.render.faces += position.array.length / 9; } } else if ( object instanceof THREE.PointCloud ) { // render particles var mode = _gl.POINTS; var index = geometry.attributes.index; if ( index ) { // indexed points var type, size; if ( index.array instanceof Uint32Array && extensions.get( 'OES_element_index_uint' ) ) { type = _gl.UNSIGNED_INT; size = 4; } else { type = _gl.UNSIGNED_SHORT; size = 2; } var offsets = geometry.offsets; if ( offsets.length === 0 ) { if ( updateBuffers ) { setupVertexAttributes( material, program, geometry, 0 ); _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); } _gl.drawElements( mode, index.array.length, type, 0); _this.info.render.calls ++; _this.info.render.points += index.array.length; } else { // if there is more than 1 chunk // must set attribute pointers to use new offsets for each chunk // even if geometry and materials didn't change if ( offsets.length > 1 ) updateBuffers = true; for ( var i = 0, il = offsets.length; i < il; i ++ ) { var startIndex = offsets[ i ].index; if ( updateBuffers ) { setupVertexAttributes( material, program, geometry, startIndex ); _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); } // render indexed points _gl.drawElements( mode, offsets[ i ].count, type, offsets[ i ].start * size ); _this.info.render.calls ++; _this.info.render.points += offsets[ i ].count; } } } else { // non-indexed points if ( updateBuffers ) { setupVertexAttributes( material, program, geometry, 0 ); } var position = geometry.attributes.position; var offsets = geometry.offsets; if ( offsets.length === 0 ) { _gl.drawArrays( mode, 0, position.array.length / 3 ); _this.info.render.calls ++; _this.info.render.points += position.array.length / 3; } else { for ( var i = 0, il = offsets.length; i < il; i ++ ) { _gl.drawArrays( mode, offsets[ i ].index, offsets[ i ].count ); _this.info.render.calls ++; _this.info.render.points += offsets[ i ].count; } } } } else if ( object instanceof THREE.Line ) { var mode = ( object.mode === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES; setLineWidth( material.linewidth ); var index = geometry.attributes.index; if ( index ) { // indexed lines var type, size; if ( index.array instanceof Uint32Array ) { type = _gl.UNSIGNED_INT; size = 4; } else { type = _gl.UNSIGNED_SHORT; size = 2; } var offsets = geometry.offsets; if ( offsets.length === 0 ) { if ( updateBuffers ) { setupVertexAttributes( material, program, geometry, 0 ); _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); } _gl.drawElements( mode, index.array.length, type, 0 ); // 2 bytes per Uint16Array _this.info.render.calls ++; _this.info.render.vertices += index.array.length; // not really true, here vertices can be shared } else { // if there is more than 1 chunk // must set attribute pointers to use new offsets for each chunk // even if geometry and materials didn't change if ( offsets.length > 1 ) updateBuffers = true; for ( var i = 0, il = offsets.length; i < il; i ++ ) { var startIndex = offsets[ i ].index; if ( updateBuffers ) { setupVertexAttributes( material, program, geometry, startIndex ); _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); } // render indexed lines _gl.drawElements( mode, offsets[ i ].count, type, offsets[ i ].start * size ); // 2 bytes per Uint16Array _this.info.render.calls ++; _this.info.render.vertices += offsets[ i ].count; // not really true, here vertices can be shared } } } else { // non-indexed lines if ( updateBuffers ) { setupVertexAttributes( material, program, geometry, 0 ); } var position = geometry.attributes.position; var offsets = geometry.offsets; if ( offsets.length === 0 ) { _gl.drawArrays( mode, 0, position.array.length / 3 ); _this.info.render.calls ++; _this.info.render.vertices += position.array.length / 3; } else { for ( var i = 0, il = offsets.length; i < il; i ++ ) { _gl.drawArrays( mode, offsets[ i ].index, offsets[ i ].count ); _this.info.render.calls ++; _this.info.render.vertices += offsets[ i ].count; } } } } }; this.renderBuffer = function ( camera, lights, fog, material, geometryGroup, object ) { if ( material.visible === false ) return; updateObject( object ); var program = setProgram( camera, lights, fog, material, object ); var attributes = program.attributes; var updateBuffers = false, wireframeBit = material.wireframe ? 1 : 0, geometryProgram = geometryGroup.id + '_' + program.id + '_' + wireframeBit; if ( geometryProgram !== _currentGeometryProgram ) { _currentGeometryProgram = geometryProgram; updateBuffers = true; } if ( updateBuffers ) { initAttributes(); } // vertices if ( ! material.morphTargets && attributes.position >= 0 ) { if ( updateBuffers ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); enableAttribute( attributes.position ); _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 ( var i = 0, il = geometryGroup.__webglCustomAttributesList.length; i < il; i ++ ) { var attribute = geometryGroup.__webglCustomAttributesList[ i ]; if ( attributes[ attribute.buffer.belongsToAttribute ] >= 0 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, attribute.buffer ); enableAttribute( attributes[ attribute.buffer.belongsToAttribute ] ); _gl.vertexAttribPointer( attributes[ attribute.buffer.belongsToAttribute ], attribute.size, _gl.FLOAT, false, 0, 0 ); } } } // colors if ( attributes.color >= 0 ) { if ( object.geometry.colors.length > 0 || object.geometry.faces.length > 0 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer ); enableAttribute( attributes.color ); _gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 ); } else if ( material.defaultAttributeValues !== undefined ) { _gl.vertexAttrib3fv( attributes.color, material.defaultAttributeValues.color ); } } // normals if ( attributes.normal >= 0 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer ); enableAttribute( attributes.normal ); _gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); } // tangents if ( attributes.tangent >= 0 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer ); enableAttribute( attributes.tangent ); _gl.vertexAttribPointer( attributes.tangent, 4, _gl.FLOAT, false, 0, 0 ); } // uvs if ( attributes.uv >= 0 ) { if ( object.geometry.faceVertexUvs[ 0 ] ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer ); enableAttribute( attributes.uv ); _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); } else if ( material.defaultAttributeValues !== undefined ) { _gl.vertexAttrib2fv( attributes.uv, material.defaultAttributeValues.uv ); } } if ( attributes.uv2 >= 0 ) { if ( object.geometry.faceVertexUvs[ 1 ] ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer ); enableAttribute( attributes.uv2 ); _gl.vertexAttribPointer( attributes.uv2, 2, _gl.FLOAT, false, 0, 0 ); } else if ( material.defaultAttributeValues !== undefined ) { _gl.vertexAttrib2fv( attributes.uv2, material.defaultAttributeValues.uv2 ); } } if ( material.skinning && attributes.skinIndex >= 0 && attributes.skinWeight >= 0 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer ); enableAttribute( attributes.skinIndex ); _gl.vertexAttribPointer( attributes.skinIndex, 4, _gl.FLOAT, false, 0, 0 ); _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer ); enableAttribute( attributes.skinWeight ); _gl.vertexAttribPointer( attributes.skinWeight, 4, _gl.FLOAT, false, 0, 0 ); } // line distances if ( attributes.lineDistance >= 0 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglLineDistanceBuffer ); enableAttribute( attributes.lineDistance ); _gl.vertexAttribPointer( attributes.lineDistance, 1, _gl.FLOAT, false, 0, 0 ); } } disableUnusedAttributes(); // render mesh if ( object instanceof THREE.Mesh ) { var type = geometryGroup.__typeArray === Uint32Array ? _gl.UNSIGNED_INT : _gl.UNSIGNED_SHORT; // wireframe if ( material.wireframe ) { setLineWidth( material.wireframeLinewidth ); if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer ); _gl.drawElements( _gl.LINES, geometryGroup.__webglLineCount, type, 0 ); // triangles } else { if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer ); _gl.drawElements( _gl.TRIANGLES, geometryGroup.__webglFaceCount, type, 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 ) { var mode = ( object.mode === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES; setLineWidth( material.linewidth ); _gl.drawArrays( mode, 0, geometryGroup.__webglLineCount ); _this.info.render.calls ++; // render particles } else if ( object instanceof THREE.PointCloud ) { _gl.drawArrays( _gl.POINTS, 0, geometryGroup.__webglParticleCount ); _this.info.render.calls ++; _this.info.render.points += geometryGroup.__webglParticleCount; } }; function initAttributes() { for ( var i = 0, l = _newAttributes.length; i < l; i ++ ) { _newAttributes[ i ] = 0; } } function enableAttribute( attribute ) { _newAttributes[ attribute ] = 1; if ( _enabledAttributes[ attribute ] === 0 ) { _gl.enableVertexAttribArray( attribute ); _enabledAttributes[ attribute ] = 1; } } function disableUnusedAttributes() { for ( var i = 0, l = _enabledAttributes.length; i < l; i ++ ) { if ( _enabledAttributes[ i ] !== _newAttributes[ i ] ) { _gl.disableVertexAttribArray( i ); _enabledAttributes[ i ] = 0; } } } function setupMorphTargets ( material, geometryGroup, object ) { // set base var attributes = material.program.attributes; if ( object.morphTargetBase !== - 1 && attributes.position >= 0 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ object.morphTargetBase ] ); enableAttribute( attributes.position ); _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); } else if ( attributes.position >= 0 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); enableAttribute( attributes.position ); _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; var attribute; while ( m < material.numSupportedMorphTargets && m < order.length ) { attribute = attributes[ 'morphTarget' + m ]; if ( attribute >= 0 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ order[ m ] ] ); enableAttribute( attribute ); _gl.vertexAttribPointer( attribute, 3, _gl.FLOAT, false, 0, 0 ); } attribute = attributes[ 'morphNormal' + m ]; if ( attribute >= 0 && material.morphNormals ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ order[ m ] ] ); enableAttribute( attribute ); _gl.vertexAttribPointer( attribute, 3, _gl.FLOAT, false, 0, 0 ); } object.__webglMorphTargetInfluences[ m ] = influences[ order[ m ] ]; m ++; } } else { // find the most influencing var activeInfluenceIndices = []; var influences = object.morphTargetInfluences; for ( var i = 0, il = influences.length; i < il; i ++ ) { var influence = influences[ i ]; activeInfluenceIndices.push( [ influence, i ] ); } if ( activeInfluenceIndices.length > material.numSupportedMorphTargets ) { activeInfluenceIndices.sort( numericalSort ); activeInfluenceIndices.length = material.numSupportedMorphTargets; } else if ( activeInfluenceIndices.length > material.numSupportedMorphNormals ) { activeInfluenceIndices.sort( numericalSort ); } else if ( activeInfluenceIndices.length === 0 ) { activeInfluenceIndices.push( [ 0, 0 ] ); } var attribute; for ( var m = 0, ml = material.numSupportedMorphTargets; m < ml; m ++ ) { if ( activeInfluenceIndices[ m ] ) { var influenceIndex = activeInfluenceIndices[ m ][ 1 ]; attribute = attributes[ 'morphTarget' + m ]; if ( attribute >= 0 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ influenceIndex ] ); enableAttribute( attribute ); _gl.vertexAttribPointer( attribute, 3, _gl.FLOAT, false, 0, 0 ); } attribute = attributes[ 'morphNormal' + m ]; if ( attribute >= 0 && material.morphNormals ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ influenceIndex ] ); enableAttribute( attribute ); _gl.vertexAttribPointer( attribute, 3, _gl.FLOAT, false, 0, 0 ); } object.__webglMorphTargetInfluences[ m ] = influences[ influenceIndex ]; } else { /* _gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 ); if ( material.morphNormals ) { _gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 ); } */ object.__webglMorphTargetInfluences[ m ] = 0; } } } // load updated influences uniform if ( material.program.uniforms.morphTargetInfluences !== null ) { _gl.uniform1fv( material.program.uniforms.morphTargetInfluences, object.__webglMorphTargetInfluences ); } } // Sorting function painterSortStable ( a, b ) { if ( a.material.id !== b.material.id ) { return a.material.id - b.material.id; } else if ( a.z !== b.z ) { return a.z - b.z; } else { return a.id - b.id; } } function reversePainterSortStable ( a, b ) { if ( a.z !== b.z ) { return b.z - a.z; } else { return a.id - b.id; } } function numericalSort ( a, b ) { return b[ 0 ] - a[ 0 ]; } // Rendering this.render = function ( scene, camera, renderTarget, forceClear ) { if ( camera instanceof THREE.Camera === false ) { console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); return; } var fog = scene.fog; // reset caching for this frame _currentGeometryProgram = ''; _currentMaterialId = - 1; _currentCamera = null; _lightsNeedUpdate = true; // update scene graph if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); // update camera matrices and frustum if ( camera.parent === undefined ) camera.updateMatrixWorld(); // update Skeleton objects scene.traverse( function ( object ) { if ( object instanceof THREE.SkinnedMesh ) { object.skeleton.update(); } } ); camera.matrixWorldInverse.getInverse( camera.matrixWorld ); _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); _frustum.setFromMatrix( _projScreenMatrix ); lights.length = 0; opaqueObjects.length = 0; transparentObjects.length = 0; sprites.length = 0; lensFlares.length = 0; projectObject( scene ); if ( _this.sortObjects === true ) { opaqueObjects.sort( painterSortStable ); transparentObjects.sort( reversePainterSortStable ); } // custom render plugins (pre pass) shadowMapPlugin.render( 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 immediate objects for ( var i = 0, il = _webglObjectsImmediate.length; i < il; i ++ ) { var webglObject = _webglObjectsImmediate[ i ]; var object = webglObject.object; if ( object.visible ) { setupMatrices( object, camera ); unrollImmediateBufferMaterial( webglObject ); } } if ( scene.overrideMaterial ) { var material = scene.overrideMaterial; this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha ); this.setDepthTest( material.depthTest ); this.setDepthWrite( material.depthWrite ); setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); renderObjects( opaqueObjects, camera, lights, fog, true, material ); renderObjects( transparentObjects, camera, lights, fog, true, material ); renderObjectsImmediate( _webglObjectsImmediate, '', camera, lights, fog, false, material ); } else { var material = null; // opaque pass (front-to-back order) this.setBlending( THREE.NoBlending ); renderObjects( opaqueObjects, camera, lights, fog, false, material ); renderObjectsImmediate( _webglObjectsImmediate, 'opaque', camera, lights, fog, false, material ); // transparent pass (back-to-front order) renderObjects( transparentObjects, camera, lights, fog, true, material ); renderObjectsImmediate( _webglObjectsImmediate, 'transparent', camera, lights, fog, true, material ); } // custom render plugins (post pass) spritePlugin.render( scene, camera ); lensFlarePlugin.render( scene, camera, _currentWidth, _currentHeight ); // 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 projectObject( object ) { if ( object.visible === false ) return; if ( object instanceof THREE.Scene || object instanceof THREE.Group ) { // skip } else { initObject( object ); if ( object instanceof THREE.Light ) { lights.push( object ); } else if ( object instanceof THREE.Sprite ) { sprites.push( object ); } else if ( object instanceof THREE.LensFlare ) { lensFlares.push( object ); } else { var webglObjects = _webglObjects[ object.id ]; if ( webglObjects && ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) ) { for ( var i = 0, l = webglObjects.length; i < l; i ++ ) { var webglObject = webglObjects[i]; unrollBufferMaterial( webglObject ); webglObject.render = true; if ( _this.sortObjects === true ) { _vector3.setFromMatrixPosition( object.matrixWorld ); _vector3.applyProjection( _projScreenMatrix ); webglObject.z = _vector3.z; } } } } } for ( var i = 0, l = object.children.length; i < l; i ++ ) { projectObject( object.children[ i ] ); } } function renderObjects( renderList, camera, lights, fog, useBlending, overrideMaterial ) { var material; for ( var i = 0, l = renderList.length; i < l; i ++ ) { var webglObject = renderList[ i ]; var object = webglObject.object; var buffer = webglObject.buffer; setupMatrices( object, camera ); if ( overrideMaterial ) { material = overrideMaterial; } else { material = webglObject.material; if ( ! material ) continue; if ( useBlending ) _this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha ); _this.setDepthTest( material.depthTest ); _this.setDepthWrite( material.depthWrite ); setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); } _this.setMaterialFaces( material ); if ( buffer instanceof THREE.BufferGeometry ) { _this.renderBufferDirect( camera, lights, fog, material, buffer, object ); } else { _this.renderBuffer( camera, lights, fog, material, buffer, object ); } } } function renderObjectsImmediate ( renderList, materialType, camera, lights, fog, useBlending, overrideMaterial ) { var material; for ( var i = 0, l = renderList.length; i < l; i ++ ) { var webglObject = renderList[ i ]; var object = webglObject.object; if ( object.visible ) { if ( overrideMaterial ) { material = overrideMaterial; } else { material = webglObject[ materialType ]; if ( ! material ) continue; if ( useBlending ) _this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha ); _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 ); _currentGeometryProgram = ''; _this.setMaterialFaces( material ); if ( object.immediateRenderCallback ) { object.immediateRenderCallback( program, _gl, _frustum ); } else { object.render( function ( object ) { _this.renderBufferImmediate( object, program, material ); } ); } }; function unrollImmediateBufferMaterial ( globject ) { var object = globject.object, material = object.material; if ( material.transparent ) { globject.transparent = material; globject.opaque = null; } else { globject.opaque = material; globject.transparent = null; } } function unrollBufferMaterial ( globject ) { var object = globject.object; var buffer = globject.buffer; var geometry = object.geometry; var material = object.material; if ( material instanceof THREE.MeshFaceMaterial ) { var materialIndex = geometry instanceof THREE.BufferGeometry ? 0 : buffer.materialIndex; material = material.materials[ materialIndex ]; globject.material = material; if ( material.transparent ) { transparentObjects.push( globject ); } else { opaqueObjects.push( globject ); } } else if ( material ) { globject.material = material; if ( material.transparent ) { transparentObjects.push( globject ); } else { opaqueObjects.push( globject ); } } } function initObject( object ) { if ( object.__webglInit === undefined ) { object.__webglInit = true; object._modelViewMatrix = new THREE.Matrix4(); object._normalMatrix = new THREE.Matrix3(); object.addEventListener( 'removed', onObjectRemoved ); } var geometry = object.geometry; if ( geometry === undefined ) { // ImmediateRenderObject } else if ( geometry.__webglInit === undefined ) { geometry.__webglInit = true; geometry.addEventListener( 'dispose', onGeometryDispose ); if ( geometry instanceof THREE.BufferGeometry ) { _this.info.memory.geometries ++; } else if ( object instanceof THREE.Mesh ) { initGeometryGroups( object, geometry ); } else if ( object instanceof THREE.Line ) { if ( geometry.__webglVertexBuffer === undefined ) { createLineBuffers( geometry ); initLineBuffers( geometry, object ); geometry.verticesNeedUpdate = true; geometry.colorsNeedUpdate = true; geometry.lineDistancesNeedUpdate = true; } } else if ( object instanceof THREE.PointCloud ) { if ( geometry.__webglVertexBuffer === undefined ) { createParticleBuffers( geometry ); initParticleBuffers( geometry, object ); geometry.verticesNeedUpdate = true; geometry.colorsNeedUpdate = true; } } } if ( object.__webglActive === undefined) { object.__webglActive = true; if ( object instanceof THREE.Mesh ) { if ( geometry instanceof THREE.BufferGeometry ) { addBuffer( _webglObjects, geometry, object ); } else if ( geometry instanceof THREE.Geometry ) { var geometryGroupsList = geometryGroups[ geometry.id ]; for ( var i = 0,l = geometryGroupsList.length; i < l; i ++ ) { addBuffer( _webglObjects, geometryGroupsList[ i ], object ); } } } else if ( object instanceof THREE.Line || object instanceof THREE.PointCloud ) { addBuffer( _webglObjects, geometry, object ); } else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) { addBufferImmediate( _webglObjectsImmediate, object ); } } } // Geometry splitting var geometryGroups = {}; var geometryGroupCounter = 0; function makeGroups( geometry, usesFaceMaterial ) { var maxVerticesInGroup = extensions.get( 'OES_element_index_uint' ) ? 4294967296 : 65535; var groupHash, hash_map = {}; var numMorphTargets = geometry.morphTargets.length; var numMorphNormals = geometry.morphNormals.length; var group; var groups = {}; var groupsList = []; for ( var f = 0, fl = geometry.faces.length; f < fl; f ++ ) { var face = geometry.faces[ f ]; var materialIndex = usesFaceMaterial ? face.materialIndex : 0; if ( ! ( materialIndex in hash_map ) ) { hash_map[ materialIndex ] = { hash: materialIndex, counter: 0 }; } groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter; if ( ! ( groupHash in groups ) ) { group = { id: geometryGroupCounter ++, faces3: [], materialIndex: materialIndex, vertices: 0, numMorphTargets: numMorphTargets, numMorphNormals: numMorphNormals }; groups[ groupHash ] = group; groupsList.push( group ); } if ( groups[ groupHash ].vertices + 3 > maxVerticesInGroup ) { hash_map[ materialIndex ].counter += 1; groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter; if ( ! ( groupHash in groups ) ) { group = { id: geometryGroupCounter ++, faces3: [], materialIndex: materialIndex, vertices: 0, numMorphTargets: numMorphTargets, numMorphNormals: numMorphNormals }; groups[ groupHash ] = group; groupsList.push( group ); } } groups[ groupHash ].faces3.push( f ); groups[ groupHash ].vertices += 3; } return groupsList; } function initGeometryGroups( object, geometry ) { var material = object.material, addBuffers = false; if ( geometryGroups[ geometry.id ] === undefined || geometry.groupsNeedUpdate === true ) { delete _webglObjects[ object.id ]; geometryGroups[ geometry.id ] = makeGroups( geometry, material instanceof THREE.MeshFaceMaterial ); geometry.groupsNeedUpdate = false; } var geometryGroupsList = geometryGroups[ geometry.id ]; // create separate VBOs per geometry chunk for ( var i = 0, il = geometryGroupsList.length; i < il; i ++ ) { var geometryGroup = geometryGroupsList[ i ]; // initialise VBO on the first access if ( geometryGroup.__webglVertexBuffer === undefined ) { createMeshBuffers( geometryGroup ); initMeshBuffers( geometryGroup, object ); geometry.verticesNeedUpdate = true; geometry.morphTargetsNeedUpdate = true; geometry.elementsNeedUpdate = true; geometry.uvsNeedUpdate = true; geometry.normalsNeedUpdate = true; geometry.tangentsNeedUpdate = true; geometry.colorsNeedUpdate = true; addBuffers = true; } else { addBuffers = false; } if ( addBuffers || object.__webglActive === undefined ) { addBuffer( _webglObjects, geometryGroup, object ); } } object.__webglActive = true; } function addBuffer( objlist, buffer, object ) { var id = object.id; objlist[id] = objlist[id] || []; objlist[id].push( { id: id, buffer: buffer, object: object, material: null, z: 0 } ); }; function addBufferImmediate( objlist, object ) { objlist.push( { id: null, object: object, opaque: null, transparent: null, z: 0 } ); }; // Objects updates function updateObject( object ) { var geometry = object.geometry; if ( geometry instanceof THREE.BufferGeometry ) { var attributes = geometry.attributes; var attributesKeys = geometry.attributesKeys; for ( var i = 0, l = attributesKeys.length; i < l; i ++ ) { var key = attributesKeys[ i ]; var attribute = attributes[ key ]; if ( attribute.buffer === undefined ) { attribute.buffer = _gl.createBuffer(); attribute.needsUpdate = true; } if ( attribute.needsUpdate === true ) { var bufferType = ( key === 'index' ) ? _gl.ELEMENT_ARRAY_BUFFER : _gl.ARRAY_BUFFER; _gl.bindBuffer( bufferType, attribute.buffer ); _gl.bufferData( bufferType, attribute.array, _gl.STATIC_DRAW ); attribute.needsUpdate = false; } } } else if ( object instanceof THREE.Mesh ) { // check all geometry groups if ( geometry.groupsNeedUpdate === true ) { initGeometryGroups( object, geometry ); } var geometryGroupsList = geometryGroups[ geometry.id ]; for ( var i = 0, il = geometryGroupsList.length; i < il; i ++ ) { var geometryGroup = geometryGroupsList[ i ]; var material = getBufferMaterial( object, geometryGroup ); if ( geometry.groupsNeedUpdate === true ) { initMeshBuffers( geometryGroup, object ); } var customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); if ( geometry.verticesNeedUpdate || geometry.morphTargetsNeedUpdate || geometry.elementsNeedUpdate || geometry.uvsNeedUpdate || geometry.normalsNeedUpdate || geometry.colorsNeedUpdate || geometry.tangentsNeedUpdate || customAttributesDirty ) { setMeshBuffers( geometryGroup, object, _gl.DYNAMIC_DRAW, ! geometry.dynamic, material ); } } geometry.verticesNeedUpdate = false; geometry.morphTargetsNeedUpdate = false; geometry.elementsNeedUpdate = false; geometry.uvsNeedUpdate = false; geometry.normalsNeedUpdate = false; geometry.colorsNeedUpdate = false; geometry.tangentsNeedUpdate = false; material.attributes && clearCustomAttributes( material ); } else if ( object instanceof THREE.Line ) { var material = getBufferMaterial( object, geometry ); var customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || geometry.lineDistancesNeedUpdate || customAttributesDirty ) { setLineBuffers( geometry, _gl.DYNAMIC_DRAW ); } geometry.verticesNeedUpdate = false; geometry.colorsNeedUpdate = false; geometry.lineDistancesNeedUpdate = false; material.attributes && clearCustomAttributes( material ); } else if ( object instanceof THREE.PointCloud ) { var material = getBufferMaterial( object, geometry ); var customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || customAttributesDirty ) { setParticleBuffers( geometry, _gl.DYNAMIC_DRAW, object ); } geometry.verticesNeedUpdate = false; geometry.colorsNeedUpdate = false; material.attributes && clearCustomAttributes( material ); } } // Objects updates - custom attributes check function areCustomAttributesDirty( material ) { for ( var name in material.attributes ) { if ( material.attributes[ name ].needsUpdate ) return true; } return false; } function clearCustomAttributes( material ) { for ( var name in material.attributes ) { material.attributes[ name ].needsUpdate = false; } } // Objects removal function removeObject( object ) { if ( object instanceof THREE.Mesh || object instanceof THREE.PointCloud || object instanceof THREE.Line ) { delete _webglObjects[ object.id ]; } else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) { removeInstances( _webglObjectsImmediate, object ); } delete object.__webglInit; delete object._modelViewMatrix; delete object._normalMatrix; delete object.__webglActive; } function removeInstances( objlist, object ) { for ( var o = objlist.length - 1; o >= 0; o -- ) { if ( objlist[ o ].object === object ) { objlist.splice( o, 1 ); } } } // Materials var shaderIDs = { MeshDepthMaterial: 'depth', MeshNormalMaterial: 'normal', MeshBasicMaterial: 'basic', MeshLambertMaterial: 'lambert', MeshPhongMaterial: 'phong', LineBasicMaterial: 'basic', LineDashedMaterial: 'dashed', PointCloudMaterial: 'particle_basic' }; function initMaterial( material, lights, fog, object ) { material.addEventListener( 'dispose', onMaterialDispose ); var shaderID = shaderIDs[ material.type ]; if ( shaderID ) { var shader = THREE.ShaderLib[ shaderID ]; material.__webglShader = { uniforms: THREE.UniformsUtils.clone( shader.uniforms ), vertexShader: shader.vertexShader, fragmentShader: shader.fragmentShader } } else { material.__webglShader = { uniforms: material.uniforms, vertexShader: material.vertexShader, fragmentShader: material.fragmentShader } } // heuristics to create shader parameters according to lights in the scene // (not to blow over maxLights budget) var maxLightCount = allocateLights( lights ); var maxShadows = allocateShadows( lights ); var maxBones = allocateBones( object ); var parameters = { precision: _precision, supportsVertexTextures: _supportsVertexTextures, map: !! material.map, envMap: !! material.envMap, envMapMode: material.envMap && material.envMap.mapping, lightMap: !! material.lightMap, bumpMap: !! material.bumpMap, normalMap: !! material.normalMap, specularMap: !! material.specularMap, alphaMap: !! material.alphaMap, combine: material.combine, vertexColors: material.vertexColors, fog: fog, useFog: material.fog, fogExp: fog instanceof THREE.FogExp2, sizeAttenuation: material.sizeAttenuation, logarithmicDepthBuffer: _logarithmicDepthBuffer, skinning: material.skinning, maxBones: maxBones, useVertexTexture: _supportsBoneTextures && object && object.skeleton && object.skeleton.useVertexTexture, morphTargets: material.morphTargets, morphNormals: material.morphNormals, maxMorphTargets: _this.maxMorphTargets, maxMorphNormals: _this.maxMorphNormals, maxDirLights: maxLightCount.directional, maxPointLights: maxLightCount.point, maxSpotLights: maxLightCount.spot, maxHemiLights: maxLightCount.hemi, maxShadows: maxShadows, shadowMapEnabled: _this.shadowMapEnabled && object.receiveShadow && maxShadows > 0, shadowMapType: _this.shadowMapType, shadowMapDebug: _this.shadowMapDebug, shadowMapCascade: _this.shadowMapCascade, alphaTest: material.alphaTest, metal: material.metal, wrapAround: material.wrapAround, doubleSided: material.side === THREE.DoubleSide, flipSided: material.side === THREE.BackSide }; // Generate code var chunks = []; if ( shaderID ) { chunks.push( shaderID ); } else { chunks.push( material.fragmentShader ); chunks.push( material.vertexShader ); } if ( material.defines !== undefined ) { for ( var name in material.defines ) { chunks.push( name ); chunks.push( material.defines[ name ] ); } } for ( var name in parameters ) { chunks.push( name ); chunks.push( parameters[ name ] ); } var code = chunks.join(); var program; // Check if code has been already compiled for ( var p = 0, pl = _programs.length; p < pl; p ++ ) { var programInfo = _programs[ p ]; if ( programInfo.code === code ) { program = programInfo; program.usedTimes ++; break; } } if ( program === undefined ) { program = new THREE.WebGLProgram( _this, code, material, parameters ); _programs.push( program ); _this.info.memory.programs = _programs.length; } material.program = program; var attributes = program.attributes; if ( material.morphTargets ) { material.numSupportedMorphTargets = 0; var id, base = 'morphTarget'; for ( var i = 0; i < _this.maxMorphTargets; i ++ ) { id = base + i; if ( attributes[ id ] >= 0 ) { material.numSupportedMorphTargets ++; } } } if ( material.morphNormals ) { material.numSupportedMorphNormals = 0; var id, base = 'morphNormal'; for ( i = 0; i < _this.maxMorphNormals; i ++ ) { id = base + i; if ( attributes[ id ] >= 0 ) { material.numSupportedMorphNormals ++; } } } material.uniformsList = []; for ( var u in material.__webglShader.uniforms ) { var location = material.program.uniforms[ u ]; if ( location ) { material.uniformsList.push( [ material.__webglShader.uniforms[ u ], location ] ); } } } function setProgram( camera, lights, fog, material, object ) { _usedTextureUnits = 0; if ( material.needsUpdate ) { if ( material.program ) deallocateMaterial( material ); initMaterial( material, lights, fog, object ); material.needsUpdate = false; } if ( material.morphTargets ) { if ( ! object.__webglMorphTargetInfluences ) { object.__webglMorphTargetInfluences = new Float32Array( _this.maxMorphTargets ); } } var refreshProgram = false; var refreshMaterial = false; var refreshLights = false; var program = material.program, p_uniforms = program.uniforms, m_uniforms = material.__webglShader.uniforms; if ( program.id !== _currentProgram ) { _gl.useProgram( program.program ); _currentProgram = program.id; refreshProgram = true; refreshMaterial = true; refreshLights = true; } if ( material.id !== _currentMaterialId ) { if ( _currentMaterialId === -1 ) refreshLights = true; _currentMaterialId = material.id; refreshMaterial = true; } if ( refreshProgram || camera !== _currentCamera ) { _gl.uniformMatrix4fv( p_uniforms.projectionMatrix, false, camera.projectionMatrix.elements ); if ( _logarithmicDepthBuffer ) { _gl.uniform1f( p_uniforms.logDepthBufFC, 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) ); } if ( camera !== _currentCamera ) _currentCamera = camera; // 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 ) { _vector3.setFromMatrixPosition( camera.matrixWorld ); _gl.uniform3f( p_uniforms.cameraPosition, _vector3.x, _vector3.y, _vector3.z ); } } if ( material instanceof THREE.MeshPhongMaterial || material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshBasicMaterial || material instanceof THREE.ShaderMaterial || material.skinning ) { if ( p_uniforms.viewMatrix !== null ) { _gl.uniformMatrix4fv( p_uniforms.viewMatrix, false, camera.matrixWorldInverse.elements ); } } } // skinning uniforms must be set even if material didn't change // auto-setting of texture unit for bone texture must go before other textures // not sure why, but otherwise weird things happen if ( material.skinning ) { if ( object.bindMatrix && p_uniforms.bindMatrix !== null ) { _gl.uniformMatrix4fv( p_uniforms.bindMatrix, false, object.bindMatrix.elements ); } if ( object.bindMatrixInverse && p_uniforms.bindMatrixInverse !== null ) { _gl.uniformMatrix4fv( p_uniforms.bindMatrixInverse, false, object.bindMatrixInverse.elements ); } if ( _supportsBoneTextures && object.skeleton && object.skeleton.useVertexTexture ) { if ( p_uniforms.boneTexture !== null ) { var textureUnit = getTextureUnit(); _gl.uniform1i( p_uniforms.boneTexture, textureUnit ); _this.setTexture( object.skeleton.boneTexture, textureUnit ); } if ( p_uniforms.boneTextureWidth !== null ) { _gl.uniform1i( p_uniforms.boneTextureWidth, object.skeleton.boneTextureWidth ); } if ( p_uniforms.boneTextureHeight !== null ) { _gl.uniform1i( p_uniforms.boneTextureHeight, object.skeleton.boneTextureHeight ); } } else if ( object.skeleton && object.skeleton.boneMatrices ) { if ( p_uniforms.boneGlobalMatrices !== null ) { _gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, object.skeleton.boneMatrices ); } } } if ( refreshMaterial ) { // refresh uniforms common to several materials if ( fog && material.fog ) { refreshUniformsFog( m_uniforms, fog ); } if ( material instanceof THREE.MeshPhongMaterial || material instanceof THREE.MeshLambertMaterial || material.lights ) { if ( _lightsNeedUpdate ) { refreshLights = true; setupLights( lights ); _lightsNeedUpdate = false; } if ( refreshLights ) { refreshUniformsLights( m_uniforms, _lights ); markUniformsLightsNeedsUpdate( m_uniforms, true ); } else { markUniformsLightsNeedsUpdate( m_uniforms, false ); } } if ( material instanceof THREE.MeshBasicMaterial || material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) { refreshUniformsCommon( m_uniforms, material ); } // refresh single material specific uniforms if ( material instanceof THREE.LineBasicMaterial ) { refreshUniformsLine( m_uniforms, material ); } else if ( material instanceof THREE.LineDashedMaterial ) { refreshUniformsLine( m_uniforms, material ); refreshUniformsDash( m_uniforms, material ); } else if ( material instanceof THREE.PointCloudMaterial ) { 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( material.uniformsList ); } loadUniformsMatrices( p_uniforms, object ); if ( p_uniforms.modelMatrix !== null ) { _gl.uniformMatrix4fv( p_uniforms.modelMatrix, false, object.matrixWorld.elements ); } return program; } // Uniforms (refresh uniforms objects) function refreshUniformsCommon ( uniforms, material ) { uniforms.opacity.value = material.opacity; if ( _this.gammaInput ) { uniforms.diffuse.value.copyGammaToLinear( material.color ); } else { uniforms.diffuse.value = material.color; } uniforms.map.value = material.map; uniforms.lightMap.value = material.lightMap; uniforms.specularMap.value = material.specularMap; uniforms.alphaMap.value = material.alphaMap; if ( material.bumpMap ) { uniforms.bumpMap.value = material.bumpMap; uniforms.bumpScale.value = material.bumpScale; } if ( material.normalMap ) { uniforms.normalMap.value = material.normalMap; uniforms.normalScale.value.copy( material.normalScale ); } // uv repeat and offset setting priorities // 1. color map // 2. specular map // 3. normal map // 4. bump map // 5. alpha map var uvScaleMap; if ( material.map ) { uvScaleMap = material.map; } else if ( material.specularMap ) { uvScaleMap = material.specularMap; } else if ( material.normalMap ) { uvScaleMap = material.normalMap; } else if ( material.bumpMap ) { uvScaleMap = material.bumpMap; } else if ( material.alphaMap ) { uvScaleMap = material.alphaMap; } if ( uvScaleMap !== undefined ) { var offset = uvScaleMap.offset; var repeat = uvScaleMap.repeat; uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y ); } uniforms.envMap.value = material.envMap; uniforms.flipEnvMap.value = ( material.envMap instanceof THREE.WebGLRenderTargetCube ) ? 1 : - 1; if ( _this.gammaInput ) { //uniforms.reflectivity.value = material.reflectivity * material.reflectivity; uniforms.reflectivity.value = material.reflectivity; } else { uniforms.reflectivity.value = material.reflectivity; } uniforms.refractionRatio.value = material.refractionRatio; } function refreshUniformsLine ( uniforms, material ) { uniforms.diffuse.value = material.color; uniforms.opacity.value = material.opacity; } function refreshUniformsDash ( uniforms, material ) { uniforms.dashSize.value = material.dashSize; uniforms.totalSize.value = material.dashSize + material.gapSize; uniforms.scale.value = material.scale; } function refreshUniformsParticle ( uniforms, material ) { uniforms.psColor.value = material.color; uniforms.opacity.value = material.opacity; uniforms.size.value = material.size; uniforms.scale.value = _canvas.height / 2.0; // TODO: Cache this. uniforms.map.value = material.map; } function refreshUniformsFog ( uniforms, fog ) { uniforms.fogColor.value = fog.color; if ( fog instanceof THREE.Fog ) { uniforms.fogNear.value = fog.near; uniforms.fogFar.value = fog.far; } else if ( fog instanceof THREE.FogExp2 ) { uniforms.fogDensity.value = fog.density; } } function refreshUniformsPhong ( uniforms, material ) { uniforms.shininess.value = material.shininess; if ( _this.gammaInput ) { uniforms.ambient.value.copyGammaToLinear( material.ambient ); uniforms.emissive.value.copyGammaToLinear( material.emissive ); uniforms.specular.value.copyGammaToLinear( material.specular ); } else { uniforms.ambient.value = material.ambient; uniforms.emissive.value = material.emissive; uniforms.specular.value = material.specular; } if ( material.wrapAround ) { uniforms.wrapRGB.value.copy( material.wrapRGB ); } } function refreshUniformsLambert ( uniforms, material ) { if ( _this.gammaInput ) { uniforms.ambient.value.copyGammaToLinear( material.ambient ); uniforms.emissive.value.copyGammaToLinear( material.emissive ); } else { uniforms.ambient.value = material.ambient; uniforms.emissive.value = material.emissive; } if ( material.wrapAround ) { uniforms.wrapRGB.value.copy( material.wrapRGB ); } } function refreshUniformsLights ( uniforms, lights ) { uniforms.ambientLightColor.value = lights.ambient; uniforms.directionalLightColor.value = lights.directional.colors; uniforms.directionalLightDirection.value = lights.directional.positions; uniforms.pointLightColor.value = lights.point.colors; uniforms.pointLightPosition.value = lights.point.positions; uniforms.pointLightDistance.value = lights.point.distances; uniforms.spotLightColor.value = lights.spot.colors; uniforms.spotLightPosition.value = lights.spot.positions; uniforms.spotLightDistance.value = lights.spot.distances; uniforms.spotLightDirection.value = lights.spot.directions; uniforms.spotLightAngleCos.value = lights.spot.anglesCos; uniforms.spotLightExponent.value = lights.spot.exponents; uniforms.hemisphereLightSkyColor.value = lights.hemi.skyColors; uniforms.hemisphereLightGroundColor.value = lights.hemi.groundColors; uniforms.hemisphereLightDirection.value = lights.hemi.positions; } // If uniforms are marked as clean, they don't need to be loaded to the GPU. function markUniformsLightsNeedsUpdate ( uniforms, boolean ) { uniforms.ambientLightColor.needsUpdate = boolean; uniforms.directionalLightColor.needsUpdate = boolean; uniforms.directionalLightDirection.needsUpdate = boolean; uniforms.pointLightColor.needsUpdate = boolean; uniforms.pointLightPosition.needsUpdate = boolean; uniforms.pointLightDistance.needsUpdate = boolean; uniforms.spotLightColor.needsUpdate = boolean; uniforms.spotLightPosition.needsUpdate = boolean; uniforms.spotLightDistance.needsUpdate = boolean; uniforms.spotLightDirection.needsUpdate = boolean; uniforms.spotLightAngleCos.needsUpdate = boolean; uniforms.spotLightExponent.needsUpdate = boolean; uniforms.hemisphereLightSkyColor.needsUpdate = boolean; uniforms.hemisphereLightGroundColor.needsUpdate = boolean; uniforms.hemisphereLightDirection.needsUpdate = boolean; } function refreshUniformsShadow ( uniforms, lights ) { if ( uniforms.shadowMatrix ) { var j = 0; for ( var i = 0, il = lights.length; i < il; i ++ ) { var light = lights[ i ]; if ( ! light.castShadow ) continue; if ( light instanceof THREE.SpotLight || ( light instanceof THREE.DirectionalLight && ! light.shadowCascade ) ) { uniforms.shadowMap.value[ j ] = light.shadowMap; uniforms.shadowMapSize.value[ j ] = light.shadowMapSize; uniforms.shadowMatrix.value[ j ] = light.shadowMatrix; uniforms.shadowDarkness.value[ j ] = light.shadowDarkness; uniforms.shadowBias.value[ j ] = light.shadowBias; j ++; } } } } // Uniforms (load to GPU) function loadUniformsMatrices ( uniforms, object ) { _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, object._modelViewMatrix.elements ); if ( uniforms.normalMatrix ) { _gl.uniformMatrix3fv( uniforms.normalMatrix, false, object._normalMatrix.elements ); } } function getTextureUnit() { var textureUnit = _usedTextureUnits; if ( textureUnit >= _maxTextures ) { console.warn( 'WebGLRenderer: trying to use ' + textureUnit + ' texture units while this GPU supports only ' + _maxTextures ); } _usedTextureUnits += 1; return textureUnit; } function loadUniformsGeneric ( uniforms ) { var texture, textureUnit, offset; for ( var j = 0, jl = uniforms.length; j < jl; j ++ ) { var uniform = uniforms[ j ][ 0 ]; // needsUpdate property is not added to all uniforms. if ( uniform.needsUpdate === false ) continue; var type = uniform.type; var value = uniform.value; var location = uniforms[ j ][ 1 ]; switch ( type ) { case '1i': _gl.uniform1i( location, value ); break; case '1f': _gl.uniform1f( location, value ); break; case '2f': _gl.uniform2f( location, value[ 0 ], value[ 1 ] ); break; case '3f': _gl.uniform3f( location, value[ 0 ], value[ 1 ], value[ 2 ] ); break; case '4f': _gl.uniform4f( location, value[ 0 ], value[ 1 ], value[ 2 ], value[ 3 ] ); break; case '1iv': _gl.uniform1iv( location, value ); break; case '3iv': _gl.uniform3iv( location, value ); break; case '1fv': _gl.uniform1fv( location, value ); break; case '2fv': _gl.uniform2fv( location, value ); break; case '3fv': _gl.uniform3fv( location, value ); break; case '4fv': _gl.uniform4fv( location, value ); break; case 'Matrix3fv': _gl.uniformMatrix3fv( location, false, value ); break; case 'Matrix4fv': _gl.uniformMatrix4fv( location, false, value ); break; // case 'i': // single integer _gl.uniform1i( location, value ); break; case 'f': // single float _gl.uniform1f( location, value ); break; case 'v2': // single THREE.Vector2 _gl.uniform2f( location, value.x, value.y ); break; case 'v3': // single THREE.Vector3 _gl.uniform3f( location, value.x, value.y, value.z ); break; case 'v4': // single THREE.Vector4 _gl.uniform4f( location, value.x, value.y, value.z, value.w ); break; case 'c': // single THREE.Color _gl.uniform3f( location, value.r, value.g, value.b ); break; case 'iv1': // flat array of integers (JS or typed array) _gl.uniform1iv( location, value ); break; case 'iv': // flat array of integers with 3 x N size (JS or typed array) _gl.uniform3iv( location, value ); break; case 'fv1': // flat array of floats (JS or typed array) _gl.uniform1fv( location, value ); break; case 'fv': // flat array of floats with 3 x N size (JS or typed array) _gl.uniform3fv( location, value ); break; case 'v2v': // array of THREE.Vector2 if ( uniform._array === undefined ) { uniform._array = new Float32Array( 2 * value.length ); } for ( var 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 ); break; case 'v3v': // array of THREE.Vector3 if ( uniform._array === undefined ) { uniform._array = new Float32Array( 3 * value.length ); } for ( var 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 ); break; case 'v4v': // array of THREE.Vector4 if ( uniform._array === undefined ) { uniform._array = new Float32Array( 4 * value.length ); } for ( var 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 ); break; case 'm3': // single THREE.Matrix3 _gl.uniformMatrix3fv( location, false, value.elements ); break; case 'm3v': // array of THREE.Matrix3 if ( uniform._array === undefined ) { uniform._array = new Float32Array( 9 * value.length ); } for ( var i = 0, il = value.length; i < il; i ++ ) { value[ i ].flattenToArrayOffset( uniform._array, i * 9 ); } _gl.uniformMatrix3fv( location, false, uniform._array ); break; case 'm4': // single THREE.Matrix4 _gl.uniformMatrix4fv( location, false, value.elements ); break; case 'm4v': // array of THREE.Matrix4 if ( uniform._array === undefined ) { uniform._array = new Float32Array( 16 * value.length ); } for ( var i = 0, il = value.length; i < il; i ++ ) { value[ i ].flattenToArrayOffset( uniform._array, i * 16 ); } _gl.uniformMatrix4fv( location, false, uniform._array ); break; case 't': // single THREE.Texture (2d or cube) texture = value; textureUnit = getTextureUnit(); _gl.uniform1i( location, textureUnit ); if ( ! texture ) continue; if ( texture instanceof THREE.CubeTexture || ( texture.image instanceof Array && texture.image.length === 6 ) ) { // CompressedTexture can have Array in image :/ setCubeTexture( texture, textureUnit ); } else if ( texture instanceof THREE.WebGLRenderTargetCube ) { setCubeTextureDynamic( texture, textureUnit ); } else { _this.setTexture( texture, textureUnit ); } break; case 'tv': // array of THREE.Texture (2d) if ( uniform._array === undefined ) { uniform._array = []; } for ( var i = 0, il = uniform.value.length; i < il; i ++ ) { uniform._array[ i ] = getTextureUnit(); } _gl.uniform1iv( location, uniform._array ); for ( var i = 0, il = uniform.value.length; i < il; i ++ ) { texture = uniform.value[ i ]; textureUnit = uniform._array[ i ]; if ( ! texture ) continue; _this.setTexture( texture, textureUnit ); } break; default: console.warn( 'THREE.WebGLRenderer: Unknown uniform type: ' + type ); } } } function setupMatrices ( object, camera ) { object._modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld ); object._normalMatrix.getNormalMatrix( object._modelViewMatrix ); } // function setColorGamma( array, offset, color, intensitySq ) { array[ offset ] = color.r * color.r * intensitySq; array[ offset + 1 ] = color.g * color.g * intensitySq; array[ offset + 2 ] = color.b * color.b * intensitySq; } function setColorLinear( array, offset, color, intensity ) { array[ offset ] = color.r * intensity; array[ offset + 1 ] = color.g * intensity; array[ offset + 2 ] = color.b * intensity; } function setupLights ( lights ) { var l, ll, light, n, r = 0, g = 0, b = 0, color, skyColor, groundColor, intensity, intensitySq, position, distance, zlights = _lights, dirColors = zlights.directional.colors, dirPositions = zlights.directional.positions, pointColors = zlights.point.colors, pointPositions = zlights.point.positions, pointDistances = zlights.point.distances, spotColors = zlights.spot.colors, spotPositions = zlights.spot.positions, spotDistances = zlights.spot.distances, spotDirections = zlights.spot.directions, spotAnglesCos = zlights.spot.anglesCos, spotExponents = zlights.spot.exponents, hemiSkyColors = zlights.hemi.skyColors, hemiGroundColors = zlights.hemi.groundColors, hemiPositions = zlights.hemi.positions, dirLength = 0, pointLength = 0, spotLength = 0, hemiLength = 0, dirCount = 0, pointCount = 0, spotCount = 0, hemiCount = 0, dirOffset = 0, pointOffset = 0, spotOffset = 0, hemiOffset = 0; for ( l = 0, ll = lights.length; l < ll; l ++ ) { light = lights[ l ]; if ( light.onlyShadow ) continue; color = light.color; intensity = light.intensity; distance = light.distance; if ( light instanceof THREE.AmbientLight ) { if ( ! light.visible ) continue; if ( _this.gammaInput ) { r += color.r * color.r; g += color.g * color.g; b += color.b * color.b; } else { r += color.r; g += color.g; b += color.b; } } else if ( light instanceof THREE.DirectionalLight ) { dirCount += 1; if ( ! light.visible ) continue; _direction.setFromMatrixPosition( light.matrixWorld ); _vector3.setFromMatrixPosition( light.target.matrixWorld ); _direction.sub( _vector3 ); _direction.normalize(); dirOffset = dirLength * 3; dirPositions[ dirOffset ] = _direction.x; dirPositions[ dirOffset + 1 ] = _direction.y; dirPositions[ dirOffset + 2 ] = _direction.z; if ( _this.gammaInput ) { setColorGamma( dirColors, dirOffset, color, intensity * intensity ); } else { setColorLinear( dirColors, dirOffset, color, intensity ); } dirLength += 1; } else if ( light instanceof THREE.PointLight ) { pointCount += 1; if ( ! light.visible ) continue; pointOffset = pointLength * 3; if ( _this.gammaInput ) { setColorGamma( pointColors, pointOffset, color, intensity * intensity ); } else { setColorLinear( pointColors, pointOffset, color, intensity ); } _vector3.setFromMatrixPosition( light.matrixWorld ); pointPositions[ pointOffset ] = _vector3.x; pointPositions[ pointOffset + 1 ] = _vector3.y; pointPositions[ pointOffset + 2 ] = _vector3.z; pointDistances[ pointLength ] = distance; pointLength += 1; } else if ( light instanceof THREE.SpotLight ) { spotCount += 1; if ( ! light.visible ) continue; spotOffset = spotLength * 3; if ( _this.gammaInput ) { setColorGamma( spotColors, spotOffset, color, intensity * intensity ); } else { setColorLinear( spotColors, spotOffset, color, intensity ); } _direction.setFromMatrixPosition( light.matrixWorld ); spotPositions[ spotOffset ] = _direction.x; spotPositions[ spotOffset + 1 ] = _direction.y; spotPositions[ spotOffset + 2 ] = _direction.z; spotDistances[ spotLength ] = distance; _vector3.setFromMatrixPosition( light.target.matrixWorld ); _direction.sub( _vector3 ); _direction.normalize(); spotDirections[ spotOffset ] = _direction.x; spotDirections[ spotOffset + 1 ] = _direction.y; spotDirections[ spotOffset + 2 ] = _direction.z; spotAnglesCos[ spotLength ] = Math.cos( light.angle ); spotExponents[ spotLength ] = light.exponent; spotLength += 1; } else if ( light instanceof THREE.HemisphereLight ) { hemiCount += 1; if ( ! light.visible ) continue; _direction.setFromMatrixPosition( light.matrixWorld ); _direction.normalize(); hemiOffset = hemiLength * 3; hemiPositions[ hemiOffset ] = _direction.x; hemiPositions[ hemiOffset + 1 ] = _direction.y; hemiPositions[ hemiOffset + 2 ] = _direction.z; skyColor = light.color; groundColor = light.groundColor; if ( _this.gammaInput ) { intensitySq = intensity * intensity; setColorGamma( hemiSkyColors, hemiOffset, skyColor, intensitySq ); setColorGamma( hemiGroundColors, hemiOffset, groundColor, intensitySq ); } else { setColorLinear( hemiSkyColors, hemiOffset, skyColor, intensity ); setColorLinear( hemiGroundColors, hemiOffset, groundColor, intensity ); } hemiLength += 1; } } // null eventual remains from removed lights // (this is to avoid if in shader) for ( l = dirLength * 3, ll = Math.max( dirColors.length, dirCount * 3 ); l < ll; l ++ ) dirColors[ l ] = 0.0; for ( l = pointLength * 3, ll = Math.max( pointColors.length, pointCount * 3 ); l < ll; l ++ ) pointColors[ l ] = 0.0; for ( l = spotLength * 3, ll = Math.max( spotColors.length, spotCount * 3 ); l < ll; l ++ ) spotColors[ l ] = 0.0; for ( l = hemiLength * 3, ll = Math.max( hemiSkyColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiSkyColors[ l ] = 0.0; for ( l = hemiLength * 3, ll = Math.max( hemiGroundColors.length, hemiCount * 3 ); l < ll; l ++ ) hemiGroundColors[ l ] = 0.0; zlights.directional.length = dirLength; zlights.point.length = pointLength; zlights.spot.length = spotLength; zlights.hemi.length = hemiLength; zlights.ambient[ 0 ] = r; zlights.ambient[ 1 ] = g; zlights.ambient[ 2 ] = b; } // GL state setting this.setFaceCulling = function ( cullFace, frontFaceDirection ) { if ( cullFace === THREE.CullFaceNone ) { _gl.disable( _gl.CULL_FACE ); } else { if ( frontFaceDirection === THREE.FrontFaceDirectionCW ) { _gl.frontFace( _gl.CW ); } else { _gl.frontFace( _gl.CCW ); } if ( cullFace === THREE.CullFaceBack ) { _gl.cullFace( _gl.BACK ); } else if ( cullFace === THREE.CullFaceFront ) { _gl.cullFace( _gl.FRONT ); } else { _gl.cullFace( _gl.FRONT_AND_BACK ); } _gl.enable( _gl.CULL_FACE ); } }; this.setMaterialFaces = function ( material ) { var doubleSided = material.side === THREE.DoubleSide; var flipSided = material.side === THREE.BackSide; if ( _oldDoubleSided !== doubleSided ) { if ( doubleSided ) { _gl.disable( _gl.CULL_FACE ); } else { _gl.enable( _gl.CULL_FACE ); } _oldDoubleSided = doubleSided; } if ( _oldFlipSided !== flipSided ) { if ( flipSided ) { _gl.frontFace( _gl.CW ); } else { _gl.frontFace( _gl.CCW ); } _oldFlipSided = flipSided; } }; 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 ) { width *= pixelRatio; 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, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha ) { if ( blending !== _oldBlending ) { if ( blending === THREE.NoBlending ) { _gl.disable( _gl.BLEND ); } else if ( blending === THREE.AdditiveBlending ) { _gl.enable( _gl.BLEND ); _gl.blendEquation( _gl.FUNC_ADD ); _gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE ); } else if ( blending === THREE.SubtractiveBlending ) { // TODO: Find blendFuncSeparate() combination _gl.enable( _gl.BLEND ); _gl.blendEquation( _gl.FUNC_ADD ); _gl.blendFunc( _gl.ZERO, _gl.ONE_MINUS_SRC_COLOR ); } else if ( blending === THREE.MultiplyBlending ) { // TODO: Find blendFuncSeparate() combination _gl.enable( _gl.BLEND ); _gl.blendEquation( _gl.FUNC_ADD ); _gl.blendFunc( _gl.ZERO, _gl.SRC_COLOR ); } else if ( blending === THREE.CustomBlending ) { _gl.enable( _gl.BLEND ); } else { _gl.enable( _gl.BLEND ); _gl.blendEquationSeparate( _gl.FUNC_ADD, _gl.FUNC_ADD ); _gl.blendFuncSeparate( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA, _gl.ONE, _gl.ONE_MINUS_SRC_ALPHA ); } _oldBlending = blending; } if ( blending === THREE.CustomBlending ) { blendEquationAlpha = blendEquationAlpha || blendEquation; blendSrcAlpha = blendSrcAlpha || blendSrc; blendDstAlpha = blendDstAlpha || blendDst; if ( blendEquation !== _oldBlendEquation || blendEquationAlpha !== _oldBlendEquationAlpha ) { _gl.blendEquationSeparate( paramThreeToGL( blendEquation ), paramThreeToGL( blendEquationAlpha ) ); _oldBlendEquation = blendEquation; _oldBlendEquationAlpha = blendEquationAlpha; } if ( blendSrc !== _oldBlendSrc || blendDst !== _oldBlendDst || blendSrcAlpha !== _oldBlendSrcAlpha || blendDstAlpha !== _oldBlendDstAlpha ) { _gl.blendFuncSeparate( paramThreeToGL( blendSrc ), paramThreeToGL( blendDst ), paramThreeToGL( blendSrcAlpha ), paramThreeToGL( blendDstAlpha ) ); _oldBlendSrc = blendSrc; _oldBlendDst = blendDst; _oldBlendSrcAlpha = blendSrcAlpha; _oldBlendDstAlpha = blendDstAlpha; } } else { _oldBlendEquation = null; _oldBlendSrc = null; _oldBlendDst = null; _oldBlendEquationAlpha = null; _oldBlendSrcAlpha = null; _oldBlendDstAlpha = null; } }; // Textures function setTextureParameters ( textureType, texture, isImagePowerOfTwo ) { var extension; 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 ); if ( texture.wrapS !== THREE.ClampToEdgeWrapping || texture.wrapT !== THREE.ClampToEdgeWrapping ) { console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT is set to THREE.ClampToEdgeWrapping. ( ' + texture.sourceFile + ' )' ); } _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) ); _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) ); if ( texture.minFilter !== THREE.NearestFilter && texture.minFilter !== THREE.LinearFilter ) { console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter is set to THREE.LinearFilter or THREE.NearestFilter. ( ' + texture.sourceFile + ' )' ); } } extension = extensions.get( 'EXT_texture_filter_anisotropic' ); if ( extension && texture.type !== THREE.FloatType ) { if ( texture.anisotropy > 1 || texture.__oldAnisotropy ) { _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, _this.getMaxAnisotropy() ) ); texture.__oldAnisotropy = texture.anisotropy; } } } this.uploadTexture = function ( texture ) { if ( texture.__webglInit === undefined ) { texture.__webglInit = true; texture.addEventListener( 'dispose', onTextureDispose ); texture.__webglTexture = _gl.createTexture(); _this.info.memory.textures ++; } _gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture ); _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha ); _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment ); texture.image = clampToMaxSize( texture.image, _maxTextureSize ); var image = texture.image, isImagePowerOfTwo = THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height ), glFormat = paramThreeToGL( texture.format ), glType = paramThreeToGL( texture.type ); setTextureParameters( _gl.TEXTURE_2D, texture, isImagePowerOfTwo ); var mipmap, mipmaps = texture.mipmaps; if ( texture instanceof THREE.DataTexture ) { // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if ( mipmaps.length > 0 && isImagePowerOfTwo ) { for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; _gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } texture.generateMipmaps = false; } else { _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data ); } } else if ( texture instanceof THREE.CompressedTexture ) { for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) { if ( getCompressedTextureFormats().indexOf( glFormat ) > -1 ) { _gl.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); } else { console.warn( "Attempt to load unsupported compressed texture format" ); } } else { _gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } } } else { // regular Texture (image, video, canvas) // use manually created mipmaps if available // if there are no manual mipmaps // set 0 level mipmap and then use GL to generate other mipmap levels if ( mipmaps.length > 0 && isImagePowerOfTwo ) { for ( var i = 0, il = mipmaps.length; i < il; i ++ ) { mipmap = mipmaps[ i ]; _gl.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap ); } texture.generateMipmaps = false; } else { _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, texture.image ); } } if ( texture.generateMipmaps && isImagePowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); texture.needsUpdate = false; if ( texture.onUpdate ) texture.onUpdate(); }; this.setTexture = function ( texture, slot ) { _gl.activeTexture( _gl.TEXTURE0 + slot ); if ( texture.needsUpdate ) { _this.uploadTexture( texture ); } else { _gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture ); } }; function clampToMaxSize ( image, maxSize ) { if ( image.width > maxSize || image.height > maxSize ) { // Warning: Scaling through the canvas will only work with images that use // premultiplied alpha. var scale = maxSize / Math.max( image.width, image.height ); var canvas = document.createElement( 'canvas' ); canvas.width = Math.floor( image.width * scale ); canvas.height = Math.floor( image.height * scale ); var context = canvas.getContext( '2d' ); context.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height ); console.log( 'THREE.WebGLRenderer:', image, 'is too big (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height + '.' ); return canvas; } return image; } function setCubeTexture ( texture, slot ) { if ( texture.image.length === 6 ) { if ( texture.needsUpdate ) { if ( ! texture.image.__webglTextureCube ) { texture.addEventListener( 'dispose', onTextureDispose ); texture.image.__webglTextureCube = _gl.createTexture(); _this.info.memory.textures ++; } _gl.activeTexture( _gl.TEXTURE0 + slot ); _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube ); _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); var isCompressed = texture instanceof THREE.CompressedTexture; var isDataTexture = texture.image[ 0 ] instanceof THREE.DataTexture; var cubeImage = []; for ( var i = 0; i < 6; i ++ ) { if ( _this.autoScaleCubemaps && ! isCompressed && ! isDataTexture ) { cubeImage[ i ] = clampToMaxSize( texture.image[ i ], _maxCubemapSize ); } else { cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ]; } } var image = cubeImage[ 0 ], isImagePowerOfTwo = THREE.Math.isPowerOfTwo( image.width ) && THREE.Math.isPowerOfTwo( image.height ), glFormat = paramThreeToGL( texture.format ), glType = paramThreeToGL( texture.type ); setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isImagePowerOfTwo ); for ( var i = 0; i < 6; i ++ ) { if ( ! isCompressed ) { if ( isDataTexture ) { _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data ); } else { _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] ); } } else { var mipmap, mipmaps = cubeImage[ i ].mipmaps; for ( var j = 0, jl = mipmaps.length; j < jl; j ++ ) { mipmap = mipmaps[ j ]; if ( texture.format !== THREE.RGBAFormat && texture.format !== THREE.RGBFormat ) { if ( getCompressedTextureFormats().indexOf( glFormat ) > -1 ) { _gl.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); } else { console.warn( "Attempt to load unsupported compressed texture format" ); } } else { _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data ); } } } } 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 === undefined ) { if ( renderTarget.depthBuffer === undefined ) renderTarget.depthBuffer = true; if ( renderTarget.stencilBuffer === undefined ) renderTarget.stencilBuffer = true; renderTarget.addEventListener( 'dispose', onRenderTargetDispose ); renderTarget.__webglTexture = _gl.createTexture(); _this.info.memory.textures ++; // Setup texture, create render and frame buffers var isTargetPowerOfTwo = THREE.Math.isPowerOfTwo( renderTarget.width ) && THREE.Math.isPowerOfTwo( renderTarget.height ), glFormat = paramThreeToGL( renderTarget.format ), glType = paramThreeToGL( renderTarget.type ); if ( isCube ) { renderTarget.__webglFramebuffer = []; renderTarget.__webglRenderbuffer = []; _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTarget.__webglTexture ); setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget, isTargetPowerOfTwo ); for ( var i = 0; i < 6; i ++ ) { renderTarget.__webglFramebuffer[ i ] = _gl.createFramebuffer(); renderTarget.__webglRenderbuffer[ i ] = _gl.createRenderbuffer(); _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); setupFrameBuffer( renderTarget.__webglFramebuffer[ i ], renderTarget, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i ); setupRenderBuffer( renderTarget.__webglRenderbuffer[ i ], renderTarget ); } if ( isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); } else { renderTarget.__webglFramebuffer = _gl.createFramebuffer(); if ( renderTarget.shareDepthFrom ) { renderTarget.__webglRenderbuffer = renderTarget.shareDepthFrom.__webglRenderbuffer; } else { renderTarget.__webglRenderbuffer = _gl.createRenderbuffer(); } _gl.bindTexture( _gl.TEXTURE_2D, renderTarget.__webglTexture ); setTextureParameters( _gl.TEXTURE_2D, renderTarget, isTargetPowerOfTwo ); _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); setupFrameBuffer( renderTarget.__webglFramebuffer, renderTarget, _gl.TEXTURE_2D ); if ( renderTarget.shareDepthFrom ) { if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderTarget.__webglRenderbuffer ); } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderTarget.__webglRenderbuffer ); } } else { setupRenderBuffer( renderTarget.__webglRenderbuffer, renderTarget ); } if ( isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); } // Release everything if ( isCube ) { _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); } else { _gl.bindTexture( _gl.TEXTURE_2D, null ); } _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); } var framebuffer, width, height, vx, vy; if ( renderTarget ) { if ( isCube ) { framebuffer = renderTarget.__webglFramebuffer[ renderTarget.activeCubeFace ]; } else { framebuffer = renderTarget.__webglFramebuffer; } width = renderTarget.width; height = renderTarget.height; vx = 0; vy = 0; } else { framebuffer = null; width = _viewportWidth; height = _viewportHeight; vx = _viewportX; vy = _viewportY; } if ( framebuffer !== _currentFramebuffer ) { _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); _gl.viewport( vx, vy, width, height ); _currentFramebuffer = framebuffer; } _currentWidth = width; _currentHeight = height; }; function 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 ) { if ( f === THREE.NearestFilter || f === THREE.NearestMipMapNearestFilter || f === THREE.NearestMipMapLinearFilter ) { return _gl.NEAREST; } return _gl.LINEAR; } // Map three.js constants to WebGL constants function paramThreeToGL ( p ) { var extension; if ( p === THREE.RepeatWrapping ) return _gl.REPEAT; if ( p === THREE.ClampToEdgeWrapping ) return _gl.CLAMP_TO_EDGE; if ( p === THREE.MirroredRepeatWrapping ) return _gl.MIRRORED_REPEAT; if ( p === THREE.NearestFilter ) return _gl.NEAREST; if ( p === THREE.NearestMipMapNearestFilter ) return _gl.NEAREST_MIPMAP_NEAREST; if ( p === THREE.NearestMipMapLinearFilter ) return _gl.NEAREST_MIPMAP_LINEAR; if ( p === THREE.LinearFilter ) return _gl.LINEAR; if ( p === THREE.LinearMipMapNearestFilter ) return _gl.LINEAR_MIPMAP_NEAREST; if ( p === THREE.LinearMipMapLinearFilter ) return _gl.LINEAR_MIPMAP_LINEAR; if ( p === THREE.UnsignedByteType ) return _gl.UNSIGNED_BYTE; if ( p === THREE.UnsignedShort4444Type ) return _gl.UNSIGNED_SHORT_4_4_4_4; if ( p === THREE.UnsignedShort5551Type ) return _gl.UNSIGNED_SHORT_5_5_5_1; if ( p === THREE.UnsignedShort565Type ) return _gl.UNSIGNED_SHORT_5_6_5; if ( p === THREE.ByteType ) return _gl.BYTE; if ( p === THREE.ShortType ) return _gl.SHORT; if ( p === THREE.UnsignedShortType ) return _gl.UNSIGNED_SHORT; if ( p === THREE.IntType ) return _gl.INT; if ( p === THREE.UnsignedIntType ) return _gl.UNSIGNED_INT; if ( p === THREE.FloatType ) return _gl.FLOAT; if ( p === THREE.AlphaFormat ) return _gl.ALPHA; if ( p === THREE.RGBFormat ) return _gl.RGB; if ( p === THREE.RGBAFormat ) return _gl.RGBA; if ( p === THREE.LuminanceFormat ) return _gl.LUMINANCE; if ( p === THREE.LuminanceAlphaFormat ) return _gl.LUMINANCE_ALPHA; if ( p === THREE.AddEquation ) return _gl.FUNC_ADD; if ( p === THREE.SubtractEquation ) return _gl.FUNC_SUBTRACT; if ( p === THREE.ReverseSubtractEquation ) return _gl.FUNC_REVERSE_SUBTRACT; if ( p === THREE.ZeroFactor ) return _gl.ZERO; if ( p === THREE.OneFactor ) return _gl.ONE; if ( p === THREE.SrcColorFactor ) return _gl.SRC_COLOR; if ( p === THREE.OneMinusSrcColorFactor ) return _gl.ONE_MINUS_SRC_COLOR; if ( p === THREE.SrcAlphaFactor ) return _gl.SRC_ALPHA; if ( p === THREE.OneMinusSrcAlphaFactor ) return _gl.ONE_MINUS_SRC_ALPHA; if ( p === THREE.DstAlphaFactor ) return _gl.DST_ALPHA; if ( p === THREE.OneMinusDstAlphaFactor ) return _gl.ONE_MINUS_DST_ALPHA; if ( p === THREE.DstColorFactor ) return _gl.DST_COLOR; if ( p === THREE.OneMinusDstColorFactor ) return _gl.ONE_MINUS_DST_COLOR; if ( p === THREE.SrcAlphaSaturateFactor ) return _gl.SRC_ALPHA_SATURATE; extension = extensions.get( 'WEBGL_compressed_texture_s3tc' ); if ( extension !== null ) { if ( p === THREE.RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT; if ( p === THREE.RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT; if ( p === THREE.RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT; if ( p === THREE.RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT; } extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' ); if ( extension !== null ) { if ( p === THREE.RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; if ( p === THREE.RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; if ( p === THREE.RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; if ( p === THREE.RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; } extension = extensions.get( 'EXT_blend_minmax' ); if ( extension !== null ) { if ( p === THREE.MinEquation ) return extension.MIN_EXT; if ( p === THREE.MaxEquation ) return extension.MAX_EXT; } return 0; } // Allocations function allocateBones ( object ) { if ( _supportsBoneTextures && object && object.skeleton && object.skeleton.useVertexTexture ) { return 1024; } else { // default for when object is not specified // ( for example when prebuilding shader // to be used with multiple objects ) // // - leave some extra space for other uniforms // - limit here is ANGLE's 254 max uniform vectors // (up to 54 should be safe) var nVertexUniforms = _gl.getParameter( _gl.MAX_VERTEX_UNIFORM_VECTORS ); var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); var maxBones = nVertexMatrices; if ( object !== undefined && object instanceof THREE.SkinnedMesh ) { maxBones = Math.min( object.skeleton.bones.length, maxBones ); if ( maxBones < object.skeleton.bones.length ) { console.warn( 'WebGLRenderer: too many bones - ' + object.skeleton.bones.length + ', this GPU supports just ' + maxBones + ' (try OpenGL instead of ANGLE)' ); } } return maxBones; } } function allocateLights( lights ) { var dirLights = 0; var pointLights = 0; var spotLights = 0; var hemiLights = 0; for ( var l = 0, ll = lights.length; l < ll; l ++ ) { var light = lights[ l ]; if ( light.onlyShadow || light.visible === false ) continue; if ( light instanceof THREE.DirectionalLight ) dirLights ++; if ( light instanceof THREE.PointLight ) pointLights ++; if ( light instanceof THREE.SpotLight ) spotLights ++; if ( light instanceof THREE.HemisphereLight ) hemiLights ++; } return { 'directional': dirLights, 'point': pointLights, 'spot': spotLights, 'hemi': hemiLights }; } function allocateShadows( lights ) { var maxShadows = 0; for ( var l = 0, ll = lights.length; l < ll; l ++ ) { var light = lights[ l ]; if ( ! light.castShadow ) continue; if ( light instanceof THREE.SpotLight ) maxShadows ++; if ( light instanceof THREE.DirectionalLight && ! light.shadowCascade ) maxShadows ++; } return maxShadows; } // DEPRECATED this.initMaterial = function () { console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' ); }; this.addPrePlugin = function () { console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' ); }; this.addPostPlugin = function () { console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' ); }; this.updateShadowMap = function () { console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' ); }; };