UNPKG

threex

Version:

Game Extensions for three.js http://www.threejsgames.com/extensions/

2,092 lines (1,181 loc) 79.4 kB
/** * @author supereggbert / http://www.paulbrunt.co.uk/ * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ * @author szimek / https://github.com/szimek/ * @author gero3 / https://github.com/gero3/ */ THREE.WebGLRenderer = function ( parameters ) { console.log( 'THREE.WebGLRenderer', THREE.REVISION ); parameters = parameters || {}; var info = { memory: { programs: 0, geometries: 0, textures: 0 }, render: { calls: 0, vertices: 0, faces: 0, points: 0 } }; var renderer = new THREE.WebGLRenderer.LowLevelRenderer(parameters); var meshRenderer = new THREE.WebGLRenderer.MeshRenderer(renderer, info); var particleRenderer = new THREE.WebGLRenderer.ParticleRenderer(renderer, info); var lineRenderer = new THREE.WebGLRenderer.LineRenderer(renderer, info); var ribbonRenderer = new THREE.WebGLRenderer.RibbonRenderer(renderer, info); var shaderBuilder = new THREE.WebGLRenderer.ShaderBuilder(renderer, info); // clearing this.autoClear = true; this.autoClearColor = true; this.autoClearDepth = true; this.autoClearStencil = true; // scene graph this.sortObjects = true; this.autoUpdateObjects = true; // physically based shading this.gammaInput = false; this.gammaOutput = false; this.physicallyBasedShading = false; // shadow map this.shadowMapEnabled = false; this.shadowMapAutoUpdate = true; this.shadowMapType = THREE.PCFShadowMap; this.shadowMapCullFace = THREE.CullFaceFront; this.shadowMapDebug = false; this.shadowMapCascade = false; // morphs this.maxMorphTargets = 8; this.maxMorphNormals = 4; // flags this.autoScaleCubemaps = true; // custom render plugins this.renderPluginsPre = []; this.renderPluginsPost = []; // info this.info = info; // internal properties var _this = this, // internal state cache _currentProgram = null, _currentFramebuffer = null, _currentMaterialId = -1, _currentGeometryGroupHash = null, _currentCamera = null, _geometryGroupCounter = 0, _usedTextureUnits = 0, // GL state _viewportX = 0, _viewportY = 0, _viewportWidth = 0, _viewportHeight = 0, _currentWidth = 0, _currentHeight = 0, _enabledAttributes = {}, // frustum _frustum = new THREE.Frustum(), // camera matrices cache _projScreenMatrix = new THREE.Matrix4(), _projScreenMatrixPS = new THREE.Matrix4(), _vector3 = new THREE.Vector3(), // light arrays cache _direction = new THREE.Vector3(), _lightsNeedUpdate = true, _lights = { ambient: [ 0, 0, 0 ], directional: { length: 0, colors: [], positions: [] }, point: { length: 0, colors: [], positions: [], distances: [] }, spot: { length: 0, colors: [], positions: [], distances: [], directions: [], anglesCos: [], exponents: [] }, hemi: { length: 0, skyColors: [], groundColors: [], positions: [] } }; // initialize this.context = renderer.getContext(); this.domElement = renderer.getDomElement(); this.getPrecision = renderer.getPrecision; // low level API this.getPrecision = renderer.getPrecision; this.getContext = renderer.getContext; this.supportsVertexTextures = renderer.supportsVertexTextures; this.supportsFloatTextures = renderer.supportsFloatTextures; this.supportsStandardDerivatives = renderer.supportsStandardDerivatives; this.supportsCompressedTextureS3TC = renderer.supportsCompressedTextureS3TC; this.getMaxAnisotropy = renderer.getMaxAnisotropy; this.setSize = renderer.setSize; this.setViewport = renderer.setViewport; this.setScissor = renderer.setScissor; this.enableScissorTest = renderer.enableScissorTest; this.setDepthWrite = renderer.setDepthWrite; this.setDepthTest = renderer.setDepthTest; this.setRenderTarget = renderer.setRenderTarget; this.setBlending = renderer.setBlending; this.setTexture = renderer.setTexture; this.setMaterialFaces = renderer.setMaterialFaces; this.setFaceCulling = renderer.setFaceCulling; // Clearing this.setClearColorHex = renderer.setClearColorHex; this.setClearColor = renderer.setClearColor; this.getClearColor = renderer.getClearColor; this.getClearAlpha = renderer.getClearAlpha; this.clear = renderer.clear; this.clearTarget = renderer.clearTarget; // Plugins this.addPostPlugin = function ( plugin ) { plugin.init( this ); this.renderPluginsPost.push( plugin ); }; this.addPrePlugin = function ( plugin ) { plugin.init( this ); this.renderPluginsPre.push( plugin ); }; // Rendering this.updateShadowMap = function ( scene, camera ) { _currentProgram = null; _currentGeometryGroupHash = -1; _currentMaterialId = -1; _lightsNeedUpdate = true; renderer.resetState(); this.shadowMapPlugin.update( scene, camera ); }; // Events var onGeometryDispose = function ( event ) { var geometry = event.target; geometry.removeEventListener( 'dispose', onGeometryDispose ); deallocateGeometry( geometry ); _this.info.memory.geometries --; }; var onTextureDispose = function ( event ) { var texture = event.target; texture.removeEventListener( 'dispose', onTextureDispose ); deallocateTexture( texture ); _this.info.memory.textures --; }; var onRenderTargetDispose = function ( event ) { var renderTarget = event.target; renderTarget.removeEventListener( 'dispose', onRenderTargetDispose ); deallocateRenderTarget( renderTarget ); _this.info.memory.textures --; }; var onMaterialDispose = function ( event ) { var material = event.target; material.removeEventListener( 'dispose', onMaterialDispose ); deallocateMaterial( material ); }; // Buffer deallocation var deallocateGeometry = function ( geometry ) { var m,ml; geometry.__webglInit = undefined; if ( geometry.__webglVertexBuffer !== undefined ) renderer.deleteBuffer( geometry.__webglVertexBuffer ); if ( geometry.__webglNormalBuffer !== undefined ) renderer.deleteBuffer( geometry.__webglNormalBuffer ); if ( geometry.__webglTangentBuffer !== undefined ) renderer.deleteBuffer( geometry.__webglTangentBuffer ); if ( geometry.__webglColorBuffer !== undefined ) renderer.deleteBuffer( geometry.__webglColorBuffer ); if ( geometry.__webglUVBuffer !== undefined ) renderer.deleteBuffer( geometry.__webglUVBuffer ); if ( geometry.__webglUV2Buffer !== undefined ) renderer.deleteBuffer( geometry.__webglUV2Buffer ); if ( geometry.__webglSkinIndicesBuffer !== undefined ) renderer.deleteBuffer( geometry.__webglSkinIndicesBuffer ); if ( geometry.__webglSkinWeightsBuffer !== undefined ) renderer.deleteBuffer( geometry.__webglSkinWeightsBuffer ); if ( geometry.__webglFaceBuffer !== undefined ) renderer.deleteBuffer( geometry.__webglFaceBuffer ); if ( geometry.__webglLineBuffer !== undefined ) renderer.deleteBuffer( geometry.__webglLineBuffer ); if ( geometry.__webglLineDistanceBuffer !== undefined ) renderer.deleteBuffer( geometry.__webglLineDistanceBuffer ); // geometry groups if ( geometry.geometryGroups !== undefined ) { for ( var g in geometry.geometryGroups ) { var geometryGroup = geometry.geometryGroups[ g ]; if ( geometryGroup.numMorphTargets !== undefined ) { for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) { renderer.deleteBuffer( geometryGroup.__webglMorphTargetsBuffers[ m ] ); } } if ( geometryGroup.numMorphNormals !== undefined ) { for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) { renderer.deleteBuffer( geometryGroup.__webglMorphNormalsBuffers[ m ] ); } } deleteCustomAttributesBuffers( geometryGroup ); } } deleteCustomAttributesBuffers( geometry ); }; var deallocateTexture = function ( texture ) { if ( texture.image && texture.image.__webglTextureCube ) { // cube texture renderer.deleteTexture( texture.image.__webglTextureCube ); } else { // 2D texture if ( ! texture.__webglInit ) return; texture.__webglInit = false; renderer.deleteTexture( texture.__webglTexture ); } }; var deallocateRenderTarget = function ( renderTarget ) { if ( !renderTarget || ! renderTarget.__webglTexture ) return; renderer.deleteTexture( renderTarget.__webglTexture ); if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) { for ( var i = 0; i < 6; i ++ ) { renderer.deleteFramebuffer( renderTarget.__webglFramebuffer[ i ] ); renderer.deleteRenderbuffer( renderTarget.__webglRenderbuffer[ i ] ); } } else { renderer.deleteFramebuffer( renderTarget.__webglFramebuffer ); renderer.deleteRenderbuffer( renderTarget.__webglRenderbuffer ); } }; var deallocateMaterial = function ( material ) { var program = material.program; if ( program === undefined ) return; material.program = undefined; // only deallocate GL program if this was the last use of shared program // assumed there is only single copy of any program in the _programs list // (that's how it's constructed) shaderBuilder.removeProgram(program) }; function deleteCustomAttributesBuffers( geometry ) { if ( geometry.__webglCustomAttributesList ) { for ( var id in geometry.__webglCustomAttributesList ) { renderer.deleteBuffer( geometry.__webglCustomAttributesList[ id ].buffer ); } } }; // Buffer initialization function initCustomAttributes ( geometry, object ) { var nvertices = geometry.vertices.length; var material = object.material; if ( material.attributes ) { if ( geometry.__webglCustomAttributesList === undefined ) { geometry.__webglCustomAttributesList = []; } for ( var a in material.attributes ) { var attribute = material.attributes[ a ]; if ( !attribute.__webglInitialized || attribute.createUniqueBuffers ) { attribute.__webglInitialized = true; var size = 1; // "f" and "i" if ( attribute.type === "v2" ) size = 2; else if ( attribute.type === "v3" ) size = 3; else if ( attribute.type === "v4" ) size = 4; else if ( attribute.type === "c" ) size = 3; attribute.size = size; attribute.array = new Float32Array( nvertices * size ); attribute.buffer = renderer.createBuffer(); attribute.buffer.belongsToAttribute = a; attribute.needsUpdate = true; } geometry.__webglCustomAttributesList.push( attribute ); } } }; function getBufferMaterial( object, geometryGroup ) { return object.material instanceof THREE.MeshFaceMaterial ? object.material.materials[ geometryGroup.materialIndex ] : object.material; }; // function initDirectBuffers( geometry ) { var a, attribute; for ( a in geometry.attributes ) { attribute = geometry.attributes[ a ]; if ( attribute.numItems === undefined ) { attribute.numItems = attribute.array.length; } attribute.buffer = renderer.createBuffer(); if ( a === "index" ) { renderer.setStaticIndexBuffer(attribute.buffer,attribute.array); } else { renderer.setStaticArrayBuffer(attribute.buffer,attribute.array); } } }; // Buffer setting function setDirectBuffers ( geometry, dispose ) { var attributes = geometry.attributes; var attributeName, attributeItem; for ( attributeName in attributes ) { attributeItem = attributes[ attributeName ]; if ( attributeItem.needsUpdate ) { if ( attributeName === 'index' ) { renderer.setDynamicIndexBuffer( attributeItem.buffer, attributeItem.array ); } else { renderer.setDynamicArrayBuffer( attributeItem.buffer, attributeItem.array ); } attributeItem.needsUpdate = false; } if ( dispose && ! attributeItem.dynamic ) { delete attributeItem.array; } } }; // Buffer rendering this.renderBufferImmediate = function ( object, program, material ) { if ( object.hasPositions && ! object.__webglVertexBuffer ) object.__webglVertexBuffer = renderer.createBuffer(); if ( object.hasNormals && ! object.__webglNormalBuffer ) object.__webglNormalBuffer = renderer.createBuffer(); if ( object.hasUvs && ! object.__webglUvBuffer ) object.__webglUvBuffer = renderer.createBuffer(); if ( object.hasColors && ! object.__webglColorBuffer ) object.__webglColorBuffer = renderer.createBuffer(); if ( object.hasPositions ) { renderer.setDynamicArrayBuffer( object.__webglVertexBuffer, object.positionArray); renderer.setFloatAttribute(program.attributes.position, object.__webglVertexBuffer, 3, 0); } if ( object.hasNormals ) { if ( material.shading === THREE.FlatShading ) { var nx, ny, nz, nax, nbx, ncx, nay, nby, ncy, naz, nbz, ncz, normalArray, i, il = object.count * 3; for( i = 0; i < il; i += 9 ) { normalArray = object.normalArray; nax = normalArray[ i ]; nay = normalArray[ i + 1 ]; naz = normalArray[ i + 2 ]; nbx = normalArray[ i + 3 ]; nby = normalArray[ i + 4 ]; nbz = normalArray[ i + 5 ]; ncx = normalArray[ i + 6 ]; ncy = normalArray[ i + 7 ]; ncz = normalArray[ i + 8 ]; nx = ( nax + nbx + ncx ) / 3; ny = ( nay + nby + ncy ) / 3; nz = ( naz + nbz + ncz ) / 3; normalArray[ i ] = nx; normalArray[ i + 1 ] = ny; normalArray[ i + 2 ] = nz; normalArray[ i + 3 ] = nx; normalArray[ i + 4 ] = ny; normalArray[ i + 5 ] = nz; normalArray[ i + 6 ] = nx; normalArray[ i + 7 ] = ny; normalArray[ i + 8 ] = nz; } } renderer.setDynamicArrayBuffer( object.__webglNormalBuffer, object.normalArray); renderer.setFloatAttribute(program.attributes.normal, object.__webglNormalBuffer, 3, 0); } if ( object.hasUvs && material.map ) { renderer.setDynamicArrayBuffer( object.__webglUvBuffer, object.uvArray); renderer.setFloatAttribute(program.attributes.uv, object.__webglUvBuffer, 2, 0); } if ( object.hasColors && material.vertexColors !== THREE.NoColors ) { renderer.setDynamicArrayBuffer( object.__webglColorBuffer, object.colorArray); renderer.setFloatAttribute(program.attributes.color, object.__webglColorBuffer, 3, 0); } renderer.drawTriangles(object.count ); object.count = 0; }; this.renderBufferDirect = function ( camera, lights, fog, material, geometry, object ) { if ( material.visible === false ) return; var program, programAttributes, linewidth, primitives, a, attribute, geometryAttributes; var attributeItem, attributeName, attributePointer, attributeSize; program = setProgram( camera, lights, fog, material, object ); programAttributes = program.attributes; geometryAttributes = geometry.attributes; var updateBuffers = false, wireframeBit = material.wireframe ? 1 : 0, geometryHash = ( geometry.id * 0xffffff ) + ( program.id * 2 ) + wireframeBit; if ( geometryHash !== _currentGeometryGroupHash ) { _currentGeometryGroupHash = geometryHash; updateBuffers = true; } if ( updateBuffers ) { renderer.disableAttributes(); } // render mesh if ( object instanceof THREE.Mesh ) { var index = geometryAttributes[ "index" ]; // indexed triangles if ( index ) { var offsets = geometry.offsets; // if there is more than 1 chunk // must set attribute pointers to use new offsets for each chunk // even if geometry and materials didn't change if ( offsets.length > 1 ) updateBuffers = true; for ( var i = 0, il = offsets.length; i < il; i ++ ) { var startIndex = offsets[ i ].index; if ( updateBuffers ) { for ( attributeName in geometryAttributes ) { if ( attributeName === 'index' ) continue; attributePointer = programAttributes[ attributeName ]; attributeItem = geometryAttributes[ attributeName ]; attributeSize = attributeItem.itemSize; if ( attributePointer >= 0 ) { renderer.setFloatAttribute( attributePointer , attributeItem.buffer, attributeSize, startIndex * attributeSize * 4 ); } } } // render indexed triangles renderer.drawTriangleElements(index.buffer, offsets[ i ].count, offsets[ i ].start * 2); _this.info.render.calls ++; _this.info.render.vertices += offsets[ i ].count; // not really true, here vertices can be shared _this.info.render.faces += offsets[ i ].count / 3; } // non-indexed triangles } else { if ( updateBuffers ) { for ( attributeName in geometryAttributes ) { attributePointer = programAttributes[ attributeName ]; attributeItem = geometryAttributes[ attributeName ]; attributeSize = attributeItem.itemSize; if ( attributePointer >= 0 ) { renderer.setFloatAttribute( attributePointer , attributeItem.buffer, attributeSize, 0 ); } } } var position = geometry.attributes[ "position" ]; // render non-indexed triangles renderer.drawTriangles( position.numItems / 3) _this.info.render.calls ++; _this.info.render.vertices += position.numItems / 3; _this.info.render.faces += position.numItems / 3 / 3; } // render particles } else if ( object instanceof THREE.ParticleSystem ) { if ( updateBuffers ) { for ( attributeName in geometryAttributes ) { attributePointer = programAttributes[ attributeName ]; attributeItem = geometryAttributes[ attributeName ]; attributeSize = attributeItem.itemSize; if ( attributePointer >= 0 ) { renderer.setFloatAttribute( attributePointer , attributeItem.buffer, attributeSize, 0 ); } } var position = geometryAttributes[ "position" ]; // render particles renderer.drawPoints(position.numItems / 3); _this.info.render.calls ++; _this.info.render.points += position.numItems / 3; } } else if ( object instanceof THREE.Line ) { if ( updateBuffers ) { for ( attributeName in geometryAttributes ) { attributePointer = programAttributes[ attributeName ]; attributeItem = geometryAttributes[ attributeName ]; attributeSize = attributeItem.itemSize; if ( attributePointer >= 0 ) { renderer.setFloatAttribute( attributePointer , attributeItem.buffer, attributeSize, 0 ); } } var position = geometryAttributes[ "position" ]; // render lines renderer.setLineWidth( material.linewidth ); renderer.drawLineStrip(position.numItems / 3); _this.info.render.calls ++; _this.info.render.points += position.numItems; } } }; this.renderBuffer = function ( camera, lights, fog, material, geometryGroup, object ) { if ( material.visible === false ) return; var program, attributes, linewidth, primitives, a, attribute, i, il; program = setProgram( camera, lights, fog, material, object ); attributes = program.attributes; var updateBuffers = false, wireframeBit = material.wireframe ? 1 : 0, geometryGroupHash = ( geometryGroup.id * 0xffffff ) + ( program.id * 2 ) + wireframeBit; if ( geometryGroupHash !== _currentGeometryGroupHash ) { _currentGeometryGroupHash = geometryGroupHash; updateBuffers = true; } if ( updateBuffers ) { renderer.disableAttributes(); } // vertices if ( !material.morphTargets && attributes.position >= 0 ) { if ( updateBuffers ) { renderer.setFloatAttribute(attributes.position , geometryGroup.__webglVertexBuffer, 3, 0); } } else { if ( object.morphTargetBase ) { setupMorphTargets( material, geometryGroup, object ); } } if ( updateBuffers ) { // custom attributes // Use the per-geometryGroup custom attribute arrays which are setup in initMeshBuffers if ( geometryGroup.__webglCustomAttributesList ) { for ( i = 0, il = geometryGroup.__webglCustomAttributesList.length; i < il; i ++ ) { attribute = geometryGroup.__webglCustomAttributesList[ i ]; if ( attributes[ attribute.buffer.belongsToAttribute ] >= 0 ) { renderer.setFloatAttribute(attributes[ attribute.buffer.belongsToAttribute ] , attribute.buffer, attribute.size, 0); } } } // colors if ( attributes.color >= 0 ) { renderer.setFloatAttribute(attributes.color , geometryGroup.__webglColorBuffer,3, 0); } // normals if ( attributes.normal >= 0 ) { renderer.setFloatAttribute(attributes.normal, geometryGroup.__webglNormalBuffer, 3, 0); } // tangents if ( attributes.tangent >= 0 ) { renderer.setFloatAttribute(attributes.tangent, geometryGroup.__webglTangentBuffer, 4, 0); } // uvs if ( attributes.uv >= 0 ) { renderer.setFloatAttribute(attributes.uv, geometryGroup.__webglUVBuffer, 2, 0); } if ( attributes.uv2 >= 0 ) { renderer.setFloatAttribute(attributes.uv2, geometryGroup.__webglUV2Buffer, 2, 0); } if ( material.skinning && attributes.skinIndex >= 0 && attributes.skinWeight >= 0 ) { renderer.setFloatAttribute(attributes.skinIndex, geometryGroup.__webglSkinIndicesBuffer, 4, 0); renderer.setFloatAttribute(attributes.skinWeight, geometryGroup.__webglSkinWeightsBuffer, 4, 0); } // line distances if ( attributes.lineDistance >= 0 ) { renderer.setFloatAttribute(attributes.lineDistance, geometryGroup.__webglLineDistanceBuffer, 1, 0); } } // render mesh if ( object instanceof THREE.Mesh ) { // wireframe if ( material.wireframe ) { renderer.setLineWidth( material.wireframeLinewidth ); renderer.drawLineElements(geometryGroup.__webglLineBuffer,geometryGroup.__webglLineCount,0); // triangles } else { renderer.drawTriangleElements( geometryGroup.__webglFaceBuffer, geometryGroup.__webglFaceCount, 0); } _this.info.render.calls ++; _this.info.render.vertices += geometryGroup.__webglFaceCount; _this.info.render.faces += geometryGroup.__webglFaceCount / 3; // render lines } else if ( object instanceof THREE.Line ) { renderer.setLineWidth( material.linewidth ); if (object.type === THREE.LineStrip) { renderer.drawLineStrip(geometryGroup.__webglLineCount); } else { renderer.drawLines(geometryGroup.__webglLineCount); } _this.info.render.calls ++; // render particles } else if ( object instanceof THREE.ParticleSystem ) { renderer.drawPoints(geometryGroup.__webglParticleCount); _this.info.render.calls ++; _this.info.render.points += geometryGroup.__webglParticleCount; // render ribbon } else if ( object instanceof THREE.Ribbon ) { renderer.drawTriangleStrip(geometryGroup.__webglVertexCount); _this.info.render.calls ++; } }; function setupMorphTargets ( material, geometryGroup, object ) { // set base var attributes = material.program.attributes; if ( object.morphTargetBase !== -1 && attributes.position >= 0 ) { renderer.setFloatAttribute(attributes.position, geometryGroup.__webglMorphTargetsBuffers[ object.morphTargetBase ], 3, 0); } else if ( attributes.position >= 0 ) { renderer.setFloatAttribute(attributes.position, geometryGroup.__webglVertexBuffer, 3, 0); } if ( object.morphTargetForcedOrder.length ) { // set forced order var m = 0; var order = object.morphTargetForcedOrder; var influences = object.morphTargetInfluences; while ( m < material.numSupportedMorphTargets && m < order.length ) { if ( attributes[ "morphTarget" + m ] >= 0 ) { renderer.setFloatAttribute(attributes[ "morphTarget" + m ], geometryGroup.__webglMorphTargetsBuffers[ order[ m ] ], 3, 0); } if ( attributes[ "morphNormal" + m ] >= 0 && material.morphNormals ) { renderer.setFloatAttribute(attributes[ "morphNormal" + m ], geometryGroup.__webglMorphNormalsBuffers[ order[ m ] ], 3, 0); } object.__webglMorphTargetInfluences[ m ] = influences[ order[ m ] ]; m ++; } } else { // find the most influencing var influence, activeInfluenceIndices = []; var influences = object.morphTargetInfluences; var i, il = influences.length; for ( i = 0; i < il; i ++ ) { influence = influences[ i ]; if ( influence > 0 ) { activeInfluenceIndices.push( [ influence, i ] ); } } if ( activeInfluenceIndices.length > material.numSupportedMorphTargets ) { activeInfluenceIndices.sort( numericalSort ); activeInfluenceIndices.length = material.numSupportedMorphTargets; } else if ( activeInfluenceIndices.length > material.numSupportedMorphNormals ) { activeInfluenceIndices.sort( numericalSort ); } else if ( activeInfluenceIndices.length === 0 ) { activeInfluenceIndices.push( [ 0, 0 ] ); }; var influenceIndex, m = 0; while ( m < material.numSupportedMorphTargets ) { if ( activeInfluenceIndices[ m ] ) { influenceIndex = activeInfluenceIndices[ m ][ 1 ]; if ( attributes[ "morphTarget" + m ] >= 0 ) { renderer.setFloatAttribute(attributes[ "morphTarget" + m ], geometryGroup.__webglMorphTargetsBuffers[ influenceIndex ], 3, 0); } if ( attributes[ "morphNormal" + m ] >= 0 && material.morphNormals ) { renderer.setFloatAttribute(attributes[ "morphNormal" + m ], geometryGroup.__webglMorphNormalsBuffers[ influenceIndex ], 3, 0); } object.__webglMorphTargetInfluences[ m ] = influences[ influenceIndex ]; } else { /* _gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 ); if ( material.morphNormals ) { _gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 ); } */ object.__webglMorphTargetInfluences[ m ] = 0; } m ++; } } // load updated influences uniform if ( material.program.uniforms.morphTargetInfluences !== null ) { renderer.uniform1fv( material.program.uniforms.morphTargetInfluences, object.__webglMorphTargetInfluences ); } }; // Sorting function painterSortStable ( a, b ) { if ( a.z !== b.z ) { return b.z - a.z; } else { return 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 i, il, webglObject, object, renderList, lights = scene.__lights, fog = scene.fog; // reset caching for this frame _currentMaterialId = -1; _lightsNeedUpdate = true; // update scene graph if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); // update camera matrices and frustum if ( camera.parent === undefined ) camera.updateMatrixWorld(); camera.matrixWorldInverse.getInverse( camera.matrixWorld ); _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ); _frustum.setFromMatrix( _projScreenMatrix ); // update WebGL objects if ( this.autoUpdateObjects ) this.initWebGLObjects( scene ); // custom render plugins (pre pass) renderPlugins( this.renderPluginsPre, scene, camera ); // _this.info.render.calls = 0; _this.info.render.vertices = 0; _this.info.render.faces = 0; _this.info.render.points = 0; renderer.setRenderTarget( renderTarget ); if ( this.autoClear || forceClear ) { this.clear( this.autoClearColor, this.autoClearDepth, this.autoClearStencil ); } // set matrices for regular objects (frustum culled) renderList = scene.__webglObjects; for ( i = 0, il = renderList.length; i < il; i ++ ) { webglObject = renderList[ i ]; object = webglObject.object; webglObject.id = i; webglObject.render = false; if ( object.visible ) { if ( ! ( object instanceof THREE.Mesh || object instanceof THREE.ParticleSystem ) || ! ( object.frustumCulled ) || _frustum.intersectsObject( object ) ) { setupMatrices( object, camera ); unrollBufferMaterial( webglObject ); webglObject.render = true; if ( this.sortObjects === true ) { if ( object.renderDepth !== null ) { webglObject.z = object.renderDepth; } else { _vector3.getPositionFromMatrix( object.matrixWorld ); _vector3.applyProjection(_projScreenMatrix); webglObject.z = _vector3.z; } } } } } if ( this.sortObjects ) { renderList.sort( painterSortStable ); } // set matrices for immediate objects renderList = scene.__webglObjectsImmediate; for ( i = 0, il = renderList.length; i < il; i ++ ) { webglObject = renderList[ i ]; object = webglObject.object; if ( object.visible ) { setupMatrices( object, camera ); unrollImmediateBufferMaterial( webglObject ); } } if ( scene.overrideMaterial ) { var material = scene.overrideMaterial; renderer.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); renderer.setDepthTest( material.depthTest ); renderer.setDepthWrite( material.depthWrite ); renderer.setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); renderObjects( scene.__webglObjects, false, "", camera, lights, fog, true, material ); renderObjectsImmediate( scene.__webglObjectsImmediate, "", camera, lights, fog, false, material ); } else { var material = null; // opaque pass (front-to-back order) renderer.setBlending( THREE.NoBlending ); renderObjects( scene.__webglObjects, true, "opaque", camera, lights, fog, false, material ); renderObjectsImmediate( scene.__webglObjectsImmediate, "opaque", camera, lights, fog, false, material ); // transparent pass (back-to-front order) renderObjects( scene.__webglObjects, false, "transparent", camera, lights, fog, true, material ); renderObjectsImmediate( scene.__webglObjectsImmediate, "transparent", camera, lights, fog, true, material ); } // custom render plugins (post pass) renderPlugins( this.renderPluginsPost, scene, camera ); // Generate mipmap if we're using any kind of mipmap filtering if ( renderTarget && renderTarget.generateMipmaps && renderTarget.minFilter !== THREE.NearestFilter && renderTarget.minFilter !== THREE.LinearFilter ) { renderer.updateRenderTargetMipmap( renderTarget ); } // Ensure depth buffer writing is enabled so it can be cleared on next render renderer.setDepthTest( true ); renderer.setDepthWrite( true ); // _gl.finish(); }; function renderPlugins( plugins, scene, camera ) { if ( ! plugins.length ) return; for ( var i = 0, il = plugins.length; i < il; i ++ ) { // reset state for plugin (to start from clean slate) _currentProgram = null; _currentCamera = null; _currentGeometryGroupHash = -1; _currentMaterialId = -1; _lightsNeedUpdate = true; renderer.resetState(); plugins[ i ].render( scene, camera, renderer.getCurrentWidth(), renderer.getCurrentHeight() ); // reset state after plugin (anything could have changed) _currentProgram = null; _currentCamera = null; _currentGeometryGroupHash = -1; _currentMaterialId = -1; _lightsNeedUpdate = true; renderer.resetState(); } }; function renderObjects ( renderList, reverse, materialType, camera, lights, fog, useBlending, overrideMaterial ) { var webglObject, object, buffer, material, start, end, delta; if ( reverse ) { start = renderList.length - 1; end = -1; delta = -1; } else { start = 0; end = renderList.length; delta = 1; } for ( var i = start; i !== end; i += delta ) { webglObject = renderList[ i ]; if ( webglObject.render ) { object = webglObject.object; buffer = webglObject.buffer; if ( overrideMaterial ) { material = overrideMaterial; } else { material = webglObject[ materialType ]; if ( ! material ) continue; if ( useBlending ) renderer.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); renderer.setDepthTest( material.depthTest ); renderer.setDepthWrite( material.depthWrite ); renderer.setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); } renderer.setMaterialFaces( material ); if ( buffer instanceof THREE.BufferGeometry ) { _this.renderBufferDirect( camera, lights, fog, material, buffer, object ); } else { _this.renderBuffer( camera, lights, fog, material, buffer, object ); } } } }; function renderObjectsImmediate ( renderList, materialType, camera, lights, fog, useBlending, overrideMaterial ) { var webglObject, object, material, program; for ( var i = 0, il = renderList.length; i < il; i ++ ) { webglObject = renderList[ i ]; object = webglObject.object; if ( object.visible ) { if ( overrideMaterial ) { material = overrideMaterial; } else { material = webglObject[ materialType ]; if ( ! material ) continue; if ( useBlending ) renderer.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); renderer.setDepthTest( material.depthTest ); renderer.setDepthWrite( material.depthWrite ); renderer.setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); } _this.renderImmediateObject( camera, lights, fog, material, object ); } } }; this.renderImmediateObject = function ( camera, lights, fog, material, object ) { var program = setProgram( camera, lights, fog, material, object ); _currentGeometryGroupHash = -1; renderer.setMaterialFaces( material ); if ( object.immediateRenderCallback ) { object.immediateRenderCallback( program, renderer.getContext(), _frustum ); } else { object.render( function( object ) { _this.renderBufferImmediate( object, program, material ); } ); } }; function unrollImmediateBufferMaterial ( globject ) { var object = globject.object, material = object.material; if ( material.transparent ) { globject.transparent = material; globject.opaque = null; } else { globject.opaque = material; globject.transparent = null; } }; function unrollBufferMaterial ( globject ) { var object = globject.object, buffer = globject.buffer, material, materialIndex, meshMaterial; meshMaterial = object.material; if ( meshMaterial instanceof THREE.MeshFaceMaterial ) { materialIndex = buffer.materialIndex; material = meshMaterial.materials[ materialIndex ]; if ( material.transparent ) { globject.transparent = material; globject.opaque = null; } else { globject.opaque = material; globject.transparent = null; } } else { material = meshMaterial; if ( material ) { if ( material.transparent ) { globject.transparent = material; globject.opaque = null; } else { globject.opaque = material; globject.transparent = null; } } } }; // Geometry splitting function sortFacesByMaterial ( geometry, material ) { var f, fl, face, materialIndex, vertices, groupHash, hash_map = {}; var numMorphTargets = geometry.morphTargets.length; var numMorphNormals = geometry.morphNormals.length; var usesFaceMaterial = material instanceof THREE.MeshFaceMaterial; geometry.geometryGroups = {}; for ( f = 0, fl = geometry.faces.length; f < fl; f ++ ) { face = geometry.faces[ f ]; materialIndex = usesFaceMaterial ? face.materialIndex : 0; if ( hash_map[ materialIndex ] === undefined ) { hash_map[ materialIndex ] = { 'hash': materialIndex, 'counter': 0 }; } groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter; if ( geometry.geometryGroups[ groupHash ] === undefined ) { geometry.geometryGroups[ groupHash ] = { 'faces3': [], 'faces4': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets, 'numMorphNormals': numMorphNormals }; } vertices = face instanceof THREE.Face3 ? 3 : 4; if ( geometry.geometryGroups[ groupHash ].vertices + vertices > 65535 ) { hash_map[ materialIndex ].counter += 1; groupHash = hash_map[ materialIndex ].hash + '_' + hash_map[ materialIndex ].counter; if ( geometry.geometryGroups[ groupHash ] === undefined ) { geometry.geometryGroups[ groupHash ] = { 'faces3': [], 'faces4': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets, 'numMorphNormals': numMorphNormals }; } } if ( face instanceof THREE.Face3 ) { geometry.geometryGroups[ groupHash ].faces3.push( f ); } else { geometry.geometryGroups[ groupHash ].faces4.push( f ); } geometry.geometryGroups[ groupHash ].vertices += vertices; } geometry.geometryGroupsList = []; for ( var g in geometry.geometryGroups ) { geometry.geometryGroups[ g ].id = _geometryGroupCounter ++; geometry.geometryGroupsList.push( geometry.geometryGroups[ g ] ); } }; // Objects refresh this.initWebGLObjects = function ( scene ) { if ( !scene.__webglObjects ) { scene.__webglObjects = []; scene.__webglObjectsImmediate = []; scene.__webglSprites = []; scene.__webglFlares = []; } while ( scene.__objectsAdded.length ) { addObject( scene.__objectsAdded[ 0 ], scene ); scene.__objectsAdded.splice( 0, 1 ); } while ( scene.__objectsRemoved.length ) { removeObject( scene.__objectsRemoved[ 0 ], scene ); scene.__objectsRemoved.splice( 0, 1 ); } // update must be called after objects adding / removal for ( var o = 0, ol = scene.__webglObjects.length; o < ol; o ++ ) { updateObject( scene.__webglObjects[ o ].object ); } }; // Objects adding function addObject( object, scene ) { var g, geometry, material, geometryGroup; if ( ! object.__webglInit ) { object.__webglInit = true; object._modelViewMatrix = new THREE.Matrix4(); object._normalMatrix = new THREE.Matrix3(); if ( object.geometry !== undefined && object.geometry.__webglInit === undefined ) { object.geometry.__webglInit = true; object.geometry.addEventListener( 'dispose', onGeometryDispose ); } if ( object instanceof THREE.Mesh ) { geometry = object.geometry; material = object.material; if ( geometry instanceof THREE.Geometry ) { if ( geometry.geometryGroups === undefined ) { sortFacesByMaterial( geometry, material ); } // create separate VBOs per geometry chunk for ( g in geometry.geometryGroups ) { geometryGroup = geometry.geometryGroups[ g ]; // initialise VBO on the first access if ( ! geometryGroup.__webglVertexBuffer ) { meshRenderer.createBuffers( geometryGroup ); meshRenderer.initBuffers( geometryGroup, object ); geometry.verticesNeedUpdate = true; geometry.morphTargetsNeedUpdate = true; geometry.elementsNeedUpdate = true; geometry.uvsNeedUpdate = true; geometry.normalsNeedUpdate = true; geometry.tangentsNeedUpdate = true; geometry.colorsNeedUpdate = true; } } } else if ( geometry instanceof THREE.BufferGeometry ) { initDirectBuffers( geometry ); } } else if ( object instanceof THREE.Ribbon ) { geometry = object.geometry; if ( ! geometry.__webglVertexBuffer ) { ribbonRenderer.createBuffers( geometry ); ribbonRenderer.initBuffers( geometry, object ); geometry.verticesNeedUpdate = true; geometry.colorsNeedUpdate = true; geometry.normalsNeedUpdate = true; } } else if ( object instanceof THREE.Line ) { geometry = object.geometry; if ( ! geometry.__webglVertexBuffer ) { if ( geometry instanceof THREE.Geometry ) { lineRenderer.createBuffers( geometry ); lineRenderer.initBuffers( geometry, object ); geometry.verticesNeedUpdate = true; geometry.colorsNeedUpdate = true; geometry.lineDistancesNeedUpdate = true; } else if ( geometry instanceof THREE.BufferGeometry ) { initDirectBuffers( geometry ); } } } else if ( object instanceof THREE.ParticleSystem ) { geometry = object.geometry; if ( ! geometry.__webglVertexBuffer ) { if ( geometry instanceof THREE.Geometry ) { particleRenderer.createBuffers( geometry ); particleRenderer.initBuffers( geometry, object ); geometry.verticesNeedUpdate = true; geometry.colorsNeedUpdate = true; } else if ( geometry instanceof THREE.BufferGeometry ) { initDirectBuffers( geometry ); } } } } if ( ! object.__webglActive ) { if ( object instanceof THREE.Mesh ) { geometry = object.geometry; if ( geometry instanceof THREE.BufferGeometry ) { addBuffer( scene.__webglObjects, geometry, object ); } else if ( geometry instanceof THREE.Geometry ) { for ( g in geometry.geometryGroups ) { geometryGroup = geometry.geometryGroups[ g ]; addBuffer( scene.__webglObjects, geometryGroup, object ); } } } else if ( object instanceof THREE.Ribbon || object instanceof THREE.Line || object instanceof THREE.ParticleSystem ) { geometry = object.geometry; addBuffer( scene.__webglObjects, geometry, object ); } else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) { addBufferImmediate( scene.__webglObjectsImmediate, object ); } else if ( object instanceof THREE.Sprite ) { scene.__webglSprites.push( object ); } else if ( object instanceof THREE.LensFlare ) { scene.__webglFlares.push( object ); } object.__webglActive = true; } }; function addBuffer ( objlist, buffer, object ) { objlist.push( { id: null, buffer: buffer, object: object, opaque: null, transparent: null, render: false, z: 0 } ); }; function addBufferImmediate ( objlist, object ) { objlist.push( { object: object, opaque: null, transparent: null } ); }; // Objects updates function updateObject ( object ) { var geometry = object.geometry, geometryGroup, customAttributesDirty, material; if ( object instanceof THREE.Mesh ) { if ( geometry instanceof THREE.BufferGeometry ) { if ( geometry.verticesNeedUpdate || geometry.elementsNeedUpdate || geometry.uvsNeedUpdate || geometry.normalsNeedUpdate || geometry.colorsNeedUpdate || geometry.tangentsNeedUpdate ) { setDirectBuffers( geometry, !geometry.dynamic ); } geometry.verticesNeedUpdate = false; geometry.elementsNeedUpdate = false; geometry.uvsNeedUpdate = false; geometry.normalsNeedUpdate = false; geometry.colorsNeedUpdate = false; geometry.tangentsNeedUpdate = false; } else { // check all geometry groups for( var i = 0, il = geometry.geometryGroupsList.length; i < il; i ++ ) { geometryGroup = geometry.geometryGroupsList[ i ]; material = getBufferMaterial( object, geometryGroup ); if ( geometry.buffersNeedUpdate ) { meshRenderer.initBuffers( geometryGroup, object ); } customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); if ( geometry.verticesNeedUpdate || geometry.morphTargetsNeedUpdate || geometry.elementsNeedUpdate || geometry.uvsNeedUpdate || geometry.normalsNeedUpdate || geometry.colorsNeedUpdate || geometry.tangentsNeedUpdate || customAttributesDirty ) { meshRenderer.setBuffers( geometryGroup, object, !geometry.dynamic, material ); } } geometry.verticesNeedUpdate = false; geometry.morphTargetsNeedUpdate = false; geometry.elementsNeedUpdate = false; geometry.uvsNeedUpdate = false; geometry.normalsNeedUpdate = false; geometry.colorsNeedUpdate = false; geometry.tangentsNeedUpdate = false; geometry.buffersNeedUpdate = false; material.attributes && clearCustomAttributes( material ); } } else if ( object instanceof THREE.Ribbon ) { material = getBufferMaterial( object, geometry ); customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || geometry.normalsNeedUpdate || customAttributesDirty ) { ribbonRenderer.setBuffers( geometry); } geometry.verticesNeedUpdate = false; geometry.colorsNeedUpdate = false; geometry.normalsNeedUpdate = false; material.attributes && clearCustomAttributes( material ); } else if ( object instanceof THREE.Line ) { if ( geometry instanceof THREE.BufferGeometry ) { if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate ) { setDirectBuffers( geometry, !geometry.dynamic ); } geometry.verticesNeedUpdate = false; geometry.colorsNeedUpdate = false; } else { material = getBufferMaterial( object, geometry ); customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || geometry.lineDistancesNeedUpdate || customAttributesDirty ) { lineRenderer.setBuffers( geometry); } geometry.verticesNeedUpdate = false; geometry.colorsNeedUpdate = false; geometry.lineDistancesNeedUpdate = false; material.attributes && clearCustomAttributes( material ); } } else if ( object instanceof THREE.ParticleSystem ) { if ( geometry instanceof THREE.BufferGeometry ) { if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate ) { setDirectBuffers( geometry, !geometry.dynamic ); } geometry.verticesNeedUpdate = false; geometry.colorsNeedUpdate = false; } else { material = getBufferMaterial( object, geometry ); customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || object.sortParticles || customAttributesDirty ) { particleRenderer.setBuffers( geometry, object, _projScreenMatrix); } geometry.verticesNeedUpdate = false; geometry.colorsNeedUpdate = false; material.attributes && clearCustomAttributes( material ); } } }; // Objects updates - custom attributes check function areCustomAttributesDirty ( material ) { for ( var a in material.attributes ) { if ( material.attributes[ a ].needsUpdate ) return true; } return false; }; function clearCustomAttributes ( material ) { for ( var a in material.attributes ) { material.attributes[ a ].needsUpdate = false; } }; // Objects removal function removeObject ( object, scene ) { if ( object instanceof THREE.Mesh || object instanceof THREE.ParticleSystem || object instanceof THREE.Ribbon || object instanceof THREE.Line ) { removeInstances( scene.__webglObjects, object ); } else if ( object instanceof THREE.Sprite ) { removeInstancesDirect( scene.__webglSprites, object ); } else if ( object instanceof THREE.LensFlare ) { removeInstancesDirect( scene.__webglFlares, object ); } else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) { removeInstances( scene.__webglObjectsImmediate, object ); } object.__webglActive = false; }; function removeInstances ( objlist, object ) { for ( var o = objlist.length - 1; o >= 0; o -- ) { if ( objlist[ o ].object === object ) { objlist.splice( o, 1 ); } } }; function removeInstancesDirect ( objlist, object ) { for ( var o = objlist.length - 1; o >= 0; o -- ) { if ( objlist[ o ] === object ) { objlist.splice( o, 1 ); } } }; // Materials this.initMaterial = function ( material, lights, fog, object ) { material.addEventListener( 'dispose', onMaterialDispose ); var u, a, identifiers, i, parameters, maxLightCount, maxBones, maxShadows, shaderID; if ( material instanceof THREE.MeshDepthMa