/** * @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 ) { // By default you can use just up to 4 directional / point lights total. // ANGLE implementation (Chrome/Firefox on Windows) is bound to // 10 varying vectors due to DirectX9 limitation. var _this = this, _gl, _programs = [], _currentProgram = null, _currentFramebuffer = null, _currentMaterialId = -1, _currentGeometryGroupHash = null, _geometryGroupCounter = 0, // gl state cache _oldDoubleSided = null, _oldFlipSided = null, _oldBlending = null, _oldDepthTest = null, _oldDepthWrite = null, _oldPolygonOffset = null, _oldPolygonOffsetFactor = null, _oldPolygonOffsetUnits = null, _cullEnabled = true, _viewportX = 0, _viewportY = 0, _viewportWidth = 0, _viewportHeight = 0, // camera matrices caches _frustum = [ new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4() ], _projScreenMatrix = new THREE.Matrix4(), _projectionMatrixArray = new Float32Array( 16 ), _viewMatrixArray = new Float32Array( 16 ), _vector3 = new THREE.Vector4(), // light arrays cache _lights = { ambient: [ 0, 0, 0 ], directional: { length: 0, colors: new Array(), positions: new Array() }, point: { length: 0, colors: new Array(), positions: new Array(), distances: new Array() } }, // parameters parameters = parameters || {}, _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElement( 'canvas' ), _stencil = parameters.stencil !== undefined ? parameters.stencil : true, _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, _antialias = parameters.antialias !== undefined ? parameters.antialias : false, _clearColor = parameters.clearColor !== undefined ? new THREE.Color( parameters.clearColor ) : new THREE.Color( 0x000000 ), _clearAlpha = parameters.clearAlpha !== undefined ? parameters.clearAlpha : 0, _maxLights = parameters.maxLights !== undefined ? parameters.maxLights : 4; this.info = { memory: { programs: 0, geometries: 0, textures: 0 }, render: { calls: 0, vertices: 0, faces: 0 } }; this.maxMorphTargets = 8; this.domElement = _canvas; this.autoClear = true; this.autoClearColor = true; this.autoClearDepth = true; this.autoClearStencil = true; this.sortObjects = true; this.autoUpdateObjects = true; this.autoUpdateScene = true; // physically based shading this.gammaInput = false; this.gammaOutput = false; this.physicallyBasedShading = false; // shadow map this.shadowMapBias = 0.0039; this.shadowMapDarkness = 0.5; this.shadowMapWidth = 512; this.shadowMapHeight = 512; this.shadowCameraNear = 1; this.shadowCameraFar = 5000; this.shadowCameraFov = 50; this.shadowMap = []; this.shadowMapEnabled = false; this.shadowMapAutoUpdate = true; this.shadowMapSoft = true; var _cameraLight, _shadowMatrix = []; var depthShader = THREE.ShaderLib[ "depthRGBA" ]; var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms ); var _depthMaterial = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms } ); var _depthMaterialMorph = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true } ); _depthMaterial._shadowPass = true; _depthMaterialMorph._shadowPass = true; // Init GL try { if ( ! ( _gl = _canvas.getContext( 'experimental-webgl', { antialias: _antialias, stencil: _stencil, preserveDrawingBuffer: _preserveDrawingBuffer } ) ) ) { throw 'Error creating WebGL context.'; } console.log( navigator.userAgent + " | " + _gl.getParameter( _gl.VERSION ) + " | " + _gl.getParameter( _gl.VENDOR ) + " | " + _gl.getParameter( _gl.RENDERER ) + " | " + _gl.getParameter( _gl.SHADING_LANGUAGE_VERSION ) ); } catch ( error ) { console.error( error ); } _gl.clearColor( 0, 0, 0, 1 ); _gl.clearDepth( 1 ); _gl.clearStencil( 0 ); _gl.enable( _gl.DEPTH_TEST ); _gl.depthFunc( _gl.LEQUAL ); _gl.frontFace( _gl.CCW ); _gl.cullFace( _gl.BACK ); _gl.enable( _gl.CULL_FACE ); _gl.enable( _gl.BLEND ); _gl.blendEquation( _gl.FUNC_ADD ); _gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA ); _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); _cullEnabled = true; // this.context = _gl; var _supportsVertexTextures = ( maxVertexTextures() > 0 ); // prepare sprites var _sprite = {}; _sprite.vertices = new Float32Array( 8 + 8 ); _sprite.faces = new Uint16Array( 6 ); var i = 0; _sprite.vertices[ i++ ] = -1; _sprite.vertices[ i++ ] = -1; // vertex 0 _sprite.vertices[ i++ ] = 0; _sprite.vertices[ i++ ] = 1; // uv 0 _sprite.vertices[ i++ ] = 1; _sprite.vertices[ i++ ] = -1; // vertex 1 _sprite.vertices[ i++ ] = 1; _sprite.vertices[ i++ ] = 1; // uv 1 _sprite.vertices[ i++ ] = 1; _sprite.vertices[ i++ ] = 1; // vertex 2 _sprite.vertices[ i++ ] = 1; _sprite.vertices[ i++ ] = 0; // uv 2 _sprite.vertices[ i++ ] = -1; _sprite.vertices[ i++ ] = 1; // vertex 3 _sprite.vertices[ i++ ] = 0; _sprite.vertices[ i++ ] = 0; // uv 3 i = 0; _sprite.faces[ i++ ] = 0; _sprite.faces[ i++ ] = 1; _sprite.faces[ i++ ] = 2; _sprite.faces[ i++ ] = 0; _sprite.faces[ i++ ] = 2; _sprite.faces[ i++ ] = 3; _sprite.vertexBuffer = _gl.createBuffer(); _sprite.elementBuffer = _gl.createBuffer(); _gl.bindBuffer( _gl.ARRAY_BUFFER, _sprite.vertexBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, _sprite.vertices, _gl.STATIC_DRAW ); _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _sprite.elementBuffer ); _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, _sprite.faces, _gl.STATIC_DRAW ); _sprite.program = _gl.createProgram(); _gl.attachShader( _sprite.program, getShader( "fragment", THREE.ShaderLib.sprite.fragmentShader ) ); _gl.attachShader( _sprite.program, getShader( "vertex", THREE.ShaderLib.sprite.vertexShader ) ); _gl.linkProgram( _sprite.program ); _sprite.attributes = {}; _sprite.uniforms = {}; _sprite.attributes.position = _gl.getAttribLocation ( _sprite.program, "position" ); _sprite.attributes.uv = _gl.getAttribLocation ( _sprite.program, "uv" ); _sprite.uniforms.uvOffset = _gl.getUniformLocation( _sprite.program, "uvOffset" ); _sprite.uniforms.uvScale = _gl.getUniformLocation( _sprite.program, "uvScale" ); _sprite.uniforms.rotation = _gl.getUniformLocation( _sprite.program, "rotation" ); _sprite.uniforms.scale = _gl.getUniformLocation( _sprite.program, "scale" ); _sprite.uniforms.alignment = _gl.getUniformLocation( _sprite.program, "alignment" ); _sprite.uniforms.color = _gl.getUniformLocation( _sprite.program, "color" ); _sprite.uniforms.map = _gl.getUniformLocation( _sprite.program, "map" ); _sprite.uniforms.opacity = _gl.getUniformLocation( _sprite.program, "opacity" ); _sprite.uniforms.useScreenCoordinates = _gl.getUniformLocation( _sprite.program, "useScreenCoordinates" ); _sprite.uniforms.affectedByDistance = _gl.getUniformLocation( _sprite.program, "affectedByDistance" ); _sprite.uniforms.screenPosition = _gl.getUniformLocation( _sprite.program, "screenPosition" ); _sprite.uniforms.modelViewMatrix = _gl.getUniformLocation( _sprite.program, "modelViewMatrix" ); _sprite.uniforms.projectionMatrix = _gl.getUniformLocation( _sprite.program, "projectionMatrix" ); //_gl.enableVertexAttribArray( _sprite.attributes.position ); //_gl.enableVertexAttribArray( _sprite.attributes.uv ); var _spriteAttributesEnabled = false; this.setSize = function ( width, height ) { _canvas.width = width; _canvas.height = height; this.setViewport( 0, 0, _canvas.width, _canvas.height ); }; this.setViewport = function ( x, y, width, height ) { _viewportX = x; _viewportY = y; _viewportWidth = width; _viewportHeight = height; _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight ); }; this.setScissor = function ( x, y, width, height ) { _gl.scissor( x, y, width, height ); }; this.enableScissorTest = function ( enable ) { enable ? _gl.enable( _gl.SCISSOR_TEST ) : _gl.disable( _gl.SCISSOR_TEST ); }; this.setClearColorHex = function ( hex, alpha ) { _clearColor.setHex( hex ); _clearAlpha = alpha; _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); }; this.setClearColor = function ( color, alpha ) { _clearColor.copy( color ); _clearAlpha = alpha; _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); }; this.getClearColor = function () { return _clearColor; }; this.getClearAlpha = function () { return _clearAlpha; }; this.clear = function ( color, depth, stencil ) { var bits = 0; if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT; if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT; if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT; _gl.clear( bits ); }; this.getContext = function () { return _gl; }; this.deallocateObject = function ( object ) { if ( ! object.__webglInit ) return; object.__webglInit = false; delete object._modelViewMatrix; delete object._normalMatrixArray; delete object._modelViewMatrixArray; delete object._objectMatrixArray; if ( object instanceof THREE.Mesh ) { for ( g in object.geometry.geometryGroups ) { deleteMeshBuffers( object.geometry.geometryGroups[ g ] ); } } else if ( object instanceof THREE.Ribbon ) { deleteRibbonBuffers( object.geometry ); } else if ( object instanceof THREE.Line ) { deleteLineBuffers( object.geometry ); } else if ( object instanceof THREE.ParticleSystem ) { deleteParticleBuffers( object.geometry ); } }; this.deallocateTexture = function ( texture ) { if ( ! texture.__webglInit ) return; texture.__webglInit = false; _gl.deleteTexture( texture.__webglTexture ); _this.info.memory.textures --; }; // function setupLights( program, lights ) { var l, ll, light, n, r = 0, g = 0, b = 0, color, position, intensity, distance, zlights = _lights, dcolors = zlights.directional.colors, dpositions = zlights.directional.positions, pcolors = zlights.point.colors, ppositions = zlights.point.positions, pdistances = zlights.point.distances, dlength = 0, plength = 0, doffset = 0, poffset = 0; for ( l = 0, ll = lights.length; l < ll; l ++ ) { light = lights[ l ]; color = light.color; position = light.position; intensity = light.intensity; distance = light.distance; if ( light instanceof THREE.AmbientLight ) { if ( _this.gammaInput ) { r += color.r * color.r; g += color.g * color.g; b += color.b * color.b; } else { r += color.r; g += color.g; b += color.b; } } else if ( light instanceof THREE.DirectionalLight ) { doffset = dlength * 3; if ( _this.gammaInput ) { dcolors[ doffset ] = color.r * color.r * intensity * intensity; dcolors[ doffset + 1 ] = color.g * color.g * intensity * intensity; dcolors[ doffset + 2 ] = color.b * color.b * intensity * intensity; } else { dcolors[ doffset ] = color.r * intensity; dcolors[ doffset + 1 ] = color.g * intensity; dcolors[ doffset + 2 ] = color.b * intensity; } dpositions[ doffset ] = position.x; dpositions[ doffset + 1 ] = position.y; dpositions[ doffset + 2 ] = position.z; dlength += 1; } else if ( light instanceof THREE.SpotLight ) { // hack, not a proper spotlight doffset = dlength * 3; if ( _this.gammaInput ) { dcolors[ doffset ] = color.r * color.r * intensity * intensity; dcolors[ doffset + 1 ] = color.g * color.g * intensity * intensity; dcolors[ doffset + 2 ] = color.b * color.b * intensity * intensity; } else { dcolors[ doffset ] = color.r * intensity; dcolors[ doffset + 1 ] = color.g * intensity; dcolors[ doffset + 2 ] = color.b * intensity; } n = 1 / position.length(); dpositions[ doffset ] = position.x * n; dpositions[ doffset + 1 ] = position.y * n; dpositions[ doffset + 2 ] = position.z * n; dlength += 1; } else if( light instanceof THREE.PointLight ) { poffset = plength * 3; if ( _this.gammaInput ) { pcolors[ poffset ] = color.r * color.r * intensity * intensity; pcolors[ poffset + 1 ] = color.g * color.g * intensity * intensity; pcolors[ poffset + 2 ] = color.b * color.b * intensity * intensity; } else { pcolors[ poffset ] = color.r * intensity; pcolors[ poffset + 1 ] = color.g * intensity; pcolors[ poffset + 2 ] = color.b * intensity; } ppositions[ poffset ] = position.x; ppositions[ poffset + 1 ] = position.y; ppositions[ poffset + 2 ] = position.z; pdistances[ plength ] = distance; plength += 1; } } // null eventual remains from removed lights // (this is to avoid if in shader) for ( l = dlength * 3, ll = dcolors.length; l < ll; l ++ ) dcolors[ l ] = 0.0; for ( l = plength * 3, ll = pcolors.length; l < ll; l ++ ) pcolors[ l ] = 0.0; zlights.point.length = plength; zlights.directional.length = dlength; zlights.ambient[ 0 ] = r; zlights.ambient[ 1 ] = g; zlights.ambient[ 2 ] = b; }; // Buffer allocation function createParticleBuffers( geometry ) { geometry.__webglVertexBuffer = _gl.createBuffer(); geometry.__webglColorBuffer = _gl.createBuffer(); _this.info.geometries ++; }; function createLineBuffers( geometry ) { geometry.__webglVertexBuffer = _gl.createBuffer(); geometry.__webglColorBuffer = _gl.createBuffer(); _this.info.memory.geometries ++; }; function createRibbonBuffers( geometry ) { geometry.__webglVertexBuffer = _gl.createBuffer(); geometry.__webglColorBuffer = _gl.createBuffer(); _this.info.memory.geometries ++; }; function createMeshBuffers( geometryGroup ) { geometryGroup.__webglVertexBuffer = _gl.createBuffer(); geometryGroup.__webglNormalBuffer = _gl.createBuffer(); geometryGroup.__webglTangentBuffer = _gl.createBuffer(); geometryGroup.__webglColorBuffer = _gl.createBuffer(); geometryGroup.__webglUVBuffer = _gl.createBuffer(); geometryGroup.__webglUV2Buffer = _gl.createBuffer(); geometryGroup.__webglSkinVertexABuffer = _gl.createBuffer(); geometryGroup.__webglSkinVertexBBuffer = _gl.createBuffer(); geometryGroup.__webglSkinIndicesBuffer = _gl.createBuffer(); geometryGroup.__webglSkinWeightsBuffer = _gl.createBuffer(); geometryGroup.__webglFaceBuffer = _gl.createBuffer(); geometryGroup.__webglLineBuffer = _gl.createBuffer(); if ( geometryGroup.numMorphTargets ) { var m, ml; geometryGroup.__webglMorphTargetsBuffers = []; for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) { geometryGroup.__webglMorphTargetsBuffers.push( _gl.createBuffer() ); } } _this.info.memory.geometries ++; }; // Buffer deallocation function deleteParticleBuffers( geometry ) { _gl.deleteBuffer( geometry.__webglVertexBuffer ); _gl.deleteBuffer( geometry.__webglColorBuffer ); _this.info.memory.geometries --; }; function deleteLineBuffers( geometry ) { _gl.deleteBuffer( geometry.__webglVertexBuffer ); _gl.deleteBuffer( geometry.__webglColorBuffer ); _this.info.memory.geometries --; }; function deleteRibbonBuffers( geometry ) { _gl.deleteBuffer( geometry.__webglVertexBuffer ); _gl.deleteBuffer( geometry.__webglColorBuffer ); _this.info.memory.geometries --; }; function deleteMeshBuffers( geometryGroup ) { _gl.deleteBuffer( geometryGroup.__webglVertexBuffer ); _gl.deleteBuffer( geometryGroup.__webglNormalBuffer ); _gl.deleteBuffer( geometryGroup.__webglTangentBuffer ); _gl.deleteBuffer( geometryGroup.__webglColorBuffer ); _gl.deleteBuffer( geometryGroup.__webglUVBuffer ); _gl.deleteBuffer( geometryGroup.__webglUV2Buffer ); _gl.deleteBuffer( geometryGroup.__webglSkinVertexABuffer ); _gl.deleteBuffer( geometryGroup.__webglSkinVertexBBuffer ); _gl.deleteBuffer( geometryGroup.__webglSkinIndicesBuffer ); _gl.deleteBuffer( geometryGroup.__webglSkinWeightsBuffer ); _gl.deleteBuffer( geometryGroup.__webglFaceBuffer ); _gl.deleteBuffer( geometryGroup.__webglLineBuffer ); if ( geometryGroup.numMorphTargets ) { for ( var m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) { _gl.deleteBuffer( geometryGroup.__webglMorphTargetsBuffers[ m ] ); } } _this.info.memory.geometries --; }; // function initCustomAttributes ( geometry, object ) { var nvertices = geometry.vertices.length; var material = object.material; if ( material.attributes ) { if ( geometry.__webglCustomAttributesList === undefined ) { geometry.__webglCustomAttributesList = []; } for ( var a in material.attributes ) { var attribute = material.attributes[ a ]; if( !attribute.__webglInitialized || attribute.createUniqueBuffers ) { attribute.__webglInitialized = true; var size = 1; // "f" and "i" if ( attribute.type === "v2" ) size = 2; else if ( attribute.type === "v3" ) size = 3; else if ( attribute.type === "v4" ) size = 4; else if ( attribute.type === "c" ) size = 3; attribute.size = size; attribute.array = new Float32Array( nvertices * size ); attribute.buffer = _gl.createBuffer(); attribute.buffer.belongsToAttribute = a; attribute.needsUpdate = true; } geometry.__webglCustomAttributesList.push( attribute ); } } }; // function initLineBuffers ( geometry, object ) { var nvertices = geometry.vertices.length; geometry.__vertexArray = new Float32Array( nvertices * 3 ); geometry.__colorArray = new Float32Array( nvertices * 3 ); geometry.__webglLineCount = nvertices; initCustomAttributes ( geometry, object ); }; function initParticleBuffers ( geometry, object ) { var nvertices = geometry.vertices.length; geometry.__vertexArray = new Float32Array( nvertices * 3 ); geometry.__colorArray = new Float32Array( nvertices * 3 ); geometry.__sortArray = []; geometry.__webglParticleCount = nvertices; initCustomAttributes ( geometry, object ); }; function initRibbonBuffers ( geometry ) { var nvertices = geometry.vertices.length; geometry.__vertexArray = new Float32Array( nvertices * 3 ); geometry.__colorArray = new Float32Array( nvertices * 3 ); geometry.__webglVertexCount = nvertices; }; // function getBufferMaterial( object, geometryGroup ) { if ( object.material && ! ( object.material instanceof THREE.MeshFaceMaterial ) ) { return object.material; } else if ( geometryGroup.materialIndex >= 0 ) { return object.geometry.materials[ geometryGroup.materialIndex ]; } }; function initMeshBuffers ( geometryGroup, object ) { var geometry = object.geometry, faces3 = geometryGroup.faces3, faces4 = geometryGroup.faces4, nvertices = faces3.length * 3 + faces4.length * 4, ntris = faces3.length * 1 + faces4.length * 2, nlines = faces3.length * 3 + faces4.length * 4, material = getBufferMaterial( object, geometryGroup ), uvType = bufferGuessUVType( material ), normalType = bufferGuessNormalType( material ), vertexColorType = bufferGuessVertexColorType( material ); //console.log( "uvType", uvType, "normalType", normalType, "vertexColorType", vertexColorType, object, geometryGroup, material ); geometryGroup.__vertexArray = new Float32Array( nvertices * 3 ); if ( normalType ) { geometryGroup.__normalArray = new Float32Array( nvertices * 3 ); } if ( geometry.hasTangents ) { geometryGroup.__tangentArray = new Float32Array( nvertices * 4 ); } if ( vertexColorType ) { geometryGroup.__colorArray = new Float32Array( nvertices * 3 ); } if ( uvType ) { if ( geometry.faceUvs.length > 0 || geometry.faceVertexUvs.length > 0 ) { geometryGroup.__uvArray = new Float32Array( nvertices * 2 ); } if ( geometry.faceUvs.length > 1 || geometry.faceVertexUvs.length > 1 ) { geometryGroup.__uv2Array = new Float32Array( nvertices * 2 ); } } if ( object.geometry.skinWeights.length && object.geometry.skinIndices.length ) { geometryGroup.__skinVertexAArray = new Float32Array( nvertices * 4 ); geometryGroup.__skinVertexBArray = new Float32Array( nvertices * 4 ); geometryGroup.__skinIndexArray = new Float32Array( nvertices * 4 ); geometryGroup.__skinWeightArray = new Float32Array( nvertices * 4 ); } geometryGroup.__faceArray = new Uint16Array( ntris * 3 ); geometryGroup.__lineArray = new Uint16Array( nlines * 2 ); if ( geometryGroup.numMorphTargets ) { geometryGroup.__morphTargetsArrays = []; for ( var m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) { geometryGroup.__morphTargetsArrays.push( new Float32Array( nvertices * 3 ) ); } } geometryGroup.__needsSmoothNormals = ( normalType === THREE.SmoothShading ); geometryGroup.__uvType = uvType; geometryGroup.__vertexColorType = vertexColorType; geometryGroup.__normalType = normalType; geometryGroup.__webglFaceCount = ntris * 3; geometryGroup.__webglLineCount = nlines * 2; // custom attributes if ( material.attributes ) { if ( geometryGroup.__webglCustomAttributesList === undefined ) { geometryGroup.__webglCustomAttributesList = []; } for ( var a in material.attributes ) { // Do a shallow copy of the attribute object so different geometryGroup chunks use different // attribute buffers which are correctly indexed in the setMeshBuffers function var originalAttribute = material.attributes[ a ]; var attribute = {}; for ( var property in originalAttribute ) { attribute[ property ] = originalAttribute[ property ]; } if( !attribute.__webglInitialized || attribute.createUniqueBuffers ) { attribute.__webglInitialized = true; var size = 1; // "f" and "i" if( attribute.type === "v2" ) size = 2; else if( attribute.type === "v3" ) size = 3; else if( attribute.type === "v4" ) size = 4; else if( attribute.type === "c" ) size = 3; attribute.size = size; attribute.array = new Float32Array( nvertices * size ); attribute.buffer = _gl.createBuffer(); attribute.buffer.belongsToAttribute = a; originalAttribute.needsUpdate = true; attribute.__original = originalAttribute; } geometryGroup.__webglCustomAttributesList.push( attribute ); } } geometryGroup.__inittedArrays = true; }; function setMeshBuffers( geometryGroup, object, hint, dispose ) { if ( ! geometryGroup.__inittedArrays ) { // console.log( object ); return; } var f, fl, fi, face, vertexNormals, faceNormal, normal, vertexColors, faceColor, vertexTangents, uvType, vertexColorType, normalType, uv, uv2, v1, v2, v3, v4, t1, t2, t3, t4, c1, c2, c3, c4, sw1, sw2, sw3, sw4, si1, si2, si3, si4, sa1, sa2, sa3, sa4, sb1, sb2, sb3, sb4, m, ml, i, il, vn, uvi, uv2i, vk, vkl, vka, a, vertexIndex = 0, offset = 0, offset_uv = 0, offset_uv2 = 0, offset_face = 0, offset_normal = 0, offset_tangent = 0, offset_line = 0, offset_color = 0, offset_skin = 0, offset_morphTarget = 0, offset_custom = 0, offset_customSrc = 0, value, vertexArray = geometryGroup.__vertexArray, uvArray = geometryGroup.__uvArray, uv2Array = geometryGroup.__uv2Array, normalArray = geometryGroup.__normalArray, tangentArray = geometryGroup.__tangentArray, colorArray = geometryGroup.__colorArray, skinVertexAArray = geometryGroup.__skinVertexAArray, skinVertexBArray = geometryGroup.__skinVertexBArray, skinIndexArray = geometryGroup.__skinIndexArray, skinWeightArray = geometryGroup.__skinWeightArray, morphTargetsArrays = geometryGroup.__morphTargetsArrays, customAttributes = geometryGroup.__webglCustomAttributesList, customAttribute, faceArray = geometryGroup.__faceArray, lineArray = geometryGroup.__lineArray, needsSmoothNormals = geometryGroup.__needsSmoothNormals, vertexColorType = geometryGroup.__vertexColorType, uvType = geometryGroup.__uvType, normalType = geometryGroup.__normalType, geometry = object.geometry, // this is shared for all chunks dirtyVertices = geometry.__dirtyVertices, dirtyElements = geometry.__dirtyElements, dirtyUvs = geometry.__dirtyUvs, dirtyNormals = geometry.__dirtyNormals, dirtyTangents = geometry.__dirtyTangents, dirtyColors = geometry.__dirtyColors, dirtyMorphTargets = geometry.__dirtyMorphTargets, vertices = geometry.vertices, chunk_faces3 = geometryGroup.faces3, chunk_faces4 = geometryGroup.faces4, obj_faces = geometry.faces, obj_uvs = geometry.faceVertexUvs[ 0 ], obj_uvs2 = geometry.faceVertexUvs[ 1 ], obj_colors = geometry.colors, obj_skinVerticesA = geometry.skinVerticesA, obj_skinVerticesB = geometry.skinVerticesB, obj_skinIndices = geometry.skinIndices, obj_skinWeights = geometry.skinWeights, morphTargets = geometry.morphTargets; if ( dirtyVertices ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { face = obj_faces[ chunk_faces3[ f ] ]; v1 = vertices[ face.a ].position; v2 = vertices[ face.b ].position; v3 = vertices[ face.c ].position; vertexArray[ offset ] = v1.x; vertexArray[ offset + 1 ] = v1.y; vertexArray[ offset + 2 ] = v1.z; vertexArray[ offset + 3 ] = v2.x; vertexArray[ offset + 4 ] = v2.y; vertexArray[ offset + 5 ] = v2.z; vertexArray[ offset + 6 ] = v3.x; vertexArray[ offset + 7 ] = v3.y; vertexArray[ offset + 8 ] = v3.z; offset += 9; } for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { face = obj_faces[ chunk_faces4[ f ] ]; v1 = vertices[ face.a ].position; v2 = vertices[ face.b ].position; v3 = vertices[ face.c ].position; v4 = vertices[ face.d ].position; vertexArray[ offset ] = v1.x; vertexArray[ offset + 1 ] = v1.y; vertexArray[ offset + 2 ] = v1.z; vertexArray[ offset + 3 ] = v2.x; vertexArray[ offset + 4 ] = v2.y; vertexArray[ offset + 5 ] = v2.z; vertexArray[ offset + 6 ] = v3.x; vertexArray[ offset + 7 ] = v3.y; vertexArray[ offset + 8 ] = v3.z; vertexArray[ offset + 9 ] = v4.x; vertexArray[ offset + 10 ] = v4.y; vertexArray[ offset + 11 ] = v4.z; offset += 12; } _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); } if ( dirtyMorphTargets ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { face = obj_faces[ chunk_faces3[ f ] ]; for ( vk = 0, vkl = morphTargets.length; vk < vkl; vk ++ ) { v1 = morphTargets[ vk ].vertices[ face.a ].position; v2 = morphTargets[ vk ].vertices[ face.b ].position; v3 = morphTargets[ vk ].vertices[ face.c ].position; vka = morphTargetsArrays[ vk ]; vka[ offset_morphTarget ] = v1.x; vka[ offset_morphTarget + 1 ] = v1.y; vka[ offset_morphTarget + 2 ] = v1.z; vka[ offset_morphTarget + 3 ] = v2.x; vka[ offset_morphTarget + 4 ] = v2.y; vka[ offset_morphTarget + 5 ] = v2.z; vka[ offset_morphTarget + 6 ] = v3.x; vka[ offset_morphTarget + 7 ] = v3.y; vka[ offset_morphTarget + 8 ] = v3.z; } offset_morphTarget += 9; } for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { face = obj_faces[ chunk_faces4[ f ] ]; for ( vk = 0, vkl = morphTargets.length; vk < vkl; vk ++ ) { v1 = morphTargets[ vk ].vertices[ face.a ].position; v2 = morphTargets[ vk ].vertices[ face.b ].position; v3 = morphTargets[ vk ].vertices[ face.c ].position; v4 = morphTargets[ vk ].vertices[ face.d ].position; vka = morphTargetsArrays[ vk ]; vka[ offset_morphTarget ] = v1.x; vka[ offset_morphTarget + 1 ] = v1.y; vka[ offset_morphTarget + 2 ] = v1.z; vka[ offset_morphTarget + 3 ] = v2.x; vka[ offset_morphTarget + 4 ] = v2.y; vka[ offset_morphTarget + 5 ] = v2.z; vka[ offset_morphTarget + 6 ] = v3.x; vka[ offset_morphTarget + 7 ] = v3.y; vka[ offset_morphTarget + 8 ] = v3.z; vka[ offset_morphTarget + 9 ] = v4.x; vka[ offset_morphTarget + 10 ] = v4.y; vka[ offset_morphTarget + 11 ] = v4.z; } offset_morphTarget += 12; } for ( vk = 0, vkl = morphTargets.length; vk < vkl; vk ++ ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ vk ] ); _gl.bufferData( _gl.ARRAY_BUFFER, morphTargetsArrays[ vk ], hint ); } } if ( obj_skinWeights.length ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { face = obj_faces[ chunk_faces3[ f ] ]; // weights sw1 = obj_skinWeights[ face.a ]; sw2 = obj_skinWeights[ face.b ]; sw3 = obj_skinWeights[ face.c ]; skinWeightArray[ offset_skin ] = sw1.x; skinWeightArray[ offset_skin + 1 ] = sw1.y; skinWeightArray[ offset_skin + 2 ] = sw1.z; skinWeightArray[ offset_skin + 3 ] = sw1.w; skinWeightArray[ offset_skin + 4 ] = sw2.x; skinWeightArray[ offset_skin + 5 ] = sw2.y; skinWeightArray[ offset_skin + 6 ] = sw2.z; skinWeightArray[ offset_skin + 7 ] = sw2.w; skinWeightArray[ offset_skin + 8 ] = sw3.x; skinWeightArray[ offset_skin + 9 ] = sw3.y; skinWeightArray[ offset_skin + 10 ] = sw3.z; skinWeightArray[ offset_skin + 11 ] = sw3.w; // indices si1 = obj_skinIndices[ face.a ]; si2 = obj_skinIndices[ face.b ]; si3 = obj_skinIndices[ face.c ]; skinIndexArray[ offset_skin ] = si1.x; skinIndexArray[ offset_skin + 1 ] = si1.y; skinIndexArray[ offset_skin + 2 ] = si1.z; skinIndexArray[ offset_skin + 3 ] = si1.w; skinIndexArray[ offset_skin + 4 ] = si2.x; skinIndexArray[ offset_skin + 5 ] = si2.y; skinIndexArray[ offset_skin + 6 ] = si2.z; skinIndexArray[ offset_skin + 7 ] = si2.w; skinIndexArray[ offset_skin + 8 ] = si3.x; skinIndexArray[ offset_skin + 9 ] = si3.y; skinIndexArray[ offset_skin + 10 ] = si3.z; skinIndexArray[ offset_skin + 11 ] = si3.w; // vertices A sa1 = obj_skinVerticesA[ face.a ]; sa2 = obj_skinVerticesA[ face.b ]; sa3 = obj_skinVerticesA[ face.c ]; skinVertexAArray[ offset_skin ] = sa1.x; skinVertexAArray[ offset_skin + 1 ] = sa1.y; skinVertexAArray[ offset_skin + 2 ] = sa1.z; skinVertexAArray[ offset_skin + 3 ] = 1; // pad for faster vertex shader skinVertexAArray[ offset_skin + 4 ] = sa2.x; skinVertexAArray[ offset_skin + 5 ] = sa2.y; skinVertexAArray[ offset_skin + 6 ] = sa2.z; skinVertexAArray[ offset_skin + 7 ] = 1; skinVertexAArray[ offset_skin + 8 ] = sa3.x; skinVertexAArray[ offset_skin + 9 ] = sa3.y; skinVertexAArray[ offset_skin + 10 ] = sa3.z; skinVertexAArray[ offset_skin + 11 ] = 1; // vertices B sb1 = obj_skinVerticesB[ face.a ]; sb2 = obj_skinVerticesB[ face.b ]; sb3 = obj_skinVerticesB[ face.c ]; skinVertexBArray[ offset_skin ] = sb1.x; skinVertexBArray[ offset_skin + 1 ] = sb1.y; skinVertexBArray[ offset_skin + 2 ] = sb1.z; skinVertexBArray[ offset_skin + 3 ] = 1; // pad for faster vertex shader skinVertexBArray[ offset_skin + 4 ] = sb2.x; skinVertexBArray[ offset_skin + 5 ] = sb2.y; skinVertexBArray[ offset_skin + 6 ] = sb2.z; skinVertexBArray[ offset_skin + 7 ] = 1; skinVertexBArray[ offset_skin + 8 ] = sb3.x; skinVertexBArray[ offset_skin + 9 ] = sb3.y; skinVertexBArray[ offset_skin + 10 ] = sb3.z; skinVertexBArray[ offset_skin + 11 ] = 1; offset_skin += 12; } for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { face = obj_faces[ chunk_faces4[ f ] ]; // weights sw1 = obj_skinWeights[ face.a ]; sw2 = obj_skinWeights[ face.b ]; sw3 = obj_skinWeights[ face.c ]; sw4 = obj_skinWeights[ face.d ]; skinWeightArray[ offset_skin ] = sw1.x; skinWeightArray[ offset_skin + 1 ] = sw1.y; skinWeightArray[ offset_skin + 2 ] = sw1.z; skinWeightArray[ offset_skin + 3 ] = sw1.w; skinWeightArray[ offset_skin + 4 ] = sw2.x; skinWeightArray[ offset_skin + 5 ] = sw2.y; skinWeightArray[ offset_skin + 6 ] = sw2.z; skinWeightArray[ offset_skin + 7 ] = sw2.w; skinWeightArray[ offset_skin + 8 ] = sw3.x; skinWeightArray[ offset_skin + 9 ] = sw3.y; skinWeightArray[ offset_skin + 10 ] = sw3.z; skinWeightArray[ offset_skin + 11 ] = sw3.w; skinWeightArray[ offset_skin + 12 ] = sw4.x; skinWeightArray[ offset_skin + 13 ] = sw4.y; skinWeightArray[ offset_skin + 14 ] = sw4.z; skinWeightArray[ offset_skin + 15 ] = sw4.w; // indices si1 = obj_skinIndices[ face.a ]; si2 = obj_skinIndices[ face.b ]; si3 = obj_skinIndices[ face.c ]; si4 = obj_skinIndices[ face.d ]; skinIndexArray[ offset_skin ] = si1.x; skinIndexArray[ offset_skin + 1 ] = si1.y; skinIndexArray[ offset_skin + 2 ] = si1.z; skinIndexArray[ offset_skin + 3 ] = si1.w; skinIndexArray[ offset_skin + 4 ] = si2.x; skinIndexArray[ offset_skin + 5 ] = si2.y; skinIndexArray[ offset_skin + 6 ] = si2.z; skinIndexArray[ offset_skin + 7 ] = si2.w; skinIndexArray[ offset_skin + 8 ] = si3.x; skinIndexArray[ offset_skin + 9 ] = si3.y; skinIndexArray[ offset_skin + 10 ] = si3.z; skinIndexArray[ offset_skin + 11 ] = si3.w; skinIndexArray[ offset_skin + 12 ] = si4.x; skinIndexArray[ offset_skin + 13 ] = si4.y; skinIndexArray[ offset_skin + 14 ] = si4.z; skinIndexArray[ offset_skin + 15 ] = si4.w; // vertices A sa1 = obj_skinVerticesA[ face.a ]; sa2 = obj_skinVerticesA[ face.b ]; sa3 = obj_skinVerticesA[ face.c ]; sa4 = obj_skinVerticesA[ face.d ]; skinVertexAArray[ offset_skin ] = sa1.x; skinVertexAArray[ offset_skin + 1 ] = sa1.y; skinVertexAArray[ offset_skin + 2 ] = sa1.z; skinVertexAArray[ offset_skin + 3 ] = 1; // pad for faster vertex shader skinVertexAArray[ offset_skin + 4 ] = sa2.x; skinVertexAArray[ offset_skin + 5 ] = sa2.y; skinVertexAArray[ offset_skin + 6 ] = sa2.z; skinVertexAArray[ offset_skin + 7 ] = 1; skinVertexAArray[ offset_skin + 8 ] = sa3.x; skinVertexAArray[ offset_skin + 9 ] = sa3.y; skinVertexAArray[ offset_skin + 10 ] = sa3.z; skinVertexAArray[ offset_skin + 11 ] = 1; skinVertexAArray[ offset_skin + 12 ] = sa4.x; skinVertexAArray[ offset_skin + 13 ] = sa4.y; skinVertexAArray[ offset_skin + 14 ] = sa4.z; skinVertexAArray[ offset_skin + 15 ] = 1; // vertices B sb1 = obj_skinVerticesB[ face.a ]; sb2 = obj_skinVerticesB[ face.b ]; sb3 = obj_skinVerticesB[ face.c ]; sb4 = obj_skinVerticesB[ face.d ]; skinVertexBArray[ offset_skin ] = sb1.x; skinVertexBArray[ offset_skin + 1 ] = sb1.y; skinVertexBArray[ offset_skin + 2 ] = sb1.z; skinVertexBArray[ offset_skin + 3 ] = 1; // pad for faster vertex shader skinVertexBArray[ offset_skin + 4 ] = sb2.x; skinVertexBArray[ offset_skin + 5 ] = sb2.y; skinVertexBArray[ offset_skin + 6 ] = sb2.z; skinVertexBArray[ offset_skin + 7 ] = 1; skinVertexBArray[ offset_skin + 8 ] = sb3.x; skinVertexBArray[ offset_skin + 9 ] = sb3.y; skinVertexBArray[ offset_skin + 10 ] = sb3.z; skinVertexBArray[ offset_skin + 11 ] = 1; skinVertexBArray[ offset_skin + 12 ] = sb4.x; skinVertexBArray[ offset_skin + 13 ] = sb4.y; skinVertexBArray[ offset_skin + 14 ] = sb4.z; skinVertexBArray[ offset_skin + 15 ] = 1; offset_skin += 16; } if ( offset_skin > 0 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinVertexABuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, skinVertexAArray, hint ); _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinVertexBBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, skinVertexBArray, hint ); _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, skinIndexArray, hint ); _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, skinWeightArray, hint ); } } if ( dirtyColors && vertexColorType ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { face = obj_faces[ chunk_faces3[ f ] ]; vertexColors = face.vertexColors; faceColor = face.color; if ( vertexColors.length === 3 && vertexColorType === THREE.VertexColors ) { c1 = vertexColors[ 0 ]; c2 = vertexColors[ 1 ]; c3 = vertexColors[ 2 ]; } else { c1 = faceColor; c2 = faceColor; c3 = faceColor; } colorArray[ offset_color ] = c1.r; colorArray[ offset_color + 1 ] = c1.g; colorArray[ offset_color + 2 ] = c1.b; colorArray[ offset_color + 3 ] = c2.r; colorArray[ offset_color + 4 ] = c2.g; colorArray[ offset_color + 5 ] = c2.b; colorArray[ offset_color + 6 ] = c3.r; colorArray[ offset_color + 7 ] = c3.g; colorArray[ offset_color + 8 ] = c3.b; offset_color += 9; } for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { face = obj_faces[ chunk_faces4[ f ] ]; vertexColors = face.vertexColors; faceColor = face.color; if ( vertexColors.length === 4 && vertexColorType === THREE.VertexColors ) { c1 = vertexColors[ 0 ]; c2 = vertexColors[ 1 ]; c3 = vertexColors[ 2 ]; c4 = vertexColors[ 3 ]; } else { c1 = faceColor; c2 = faceColor; c3 = faceColor; c4 = faceColor; } colorArray[ offset_color ] = c1.r; colorArray[ offset_color + 1 ] = c1.g; colorArray[ offset_color + 2 ] = c1.b; colorArray[ offset_color + 3 ] = c2.r; colorArray[ offset_color + 4 ] = c2.g; colorArray[ offset_color + 5 ] = c2.b; colorArray[ offset_color + 6 ] = c3.r; colorArray[ offset_color + 7 ] = c3.g; colorArray[ offset_color + 8 ] = c3.b; colorArray[ offset_color + 9 ] = c4.r; colorArray[ offset_color + 10 ] = c4.g; colorArray[ offset_color + 11 ] = c4.b; offset_color += 12; } if ( offset_color > 0 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); } } if ( dirtyTangents && geometry.hasTangents ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { face = obj_faces[ chunk_faces3[ f ] ]; vertexTangents = face.vertexTangents; t1 = vertexTangents[ 0 ]; t2 = vertexTangents[ 1 ]; t3 = vertexTangents[ 2 ]; tangentArray[ offset_tangent ] = t1.x; tangentArray[ offset_tangent + 1 ] = t1.y; tangentArray[ offset_tangent + 2 ] = t1.z; tangentArray[ offset_tangent + 3 ] = t1.w; tangentArray[ offset_tangent + 4 ] = t2.x; tangentArray[ offset_tangent + 5 ] = t2.y; tangentArray[ offset_tangent + 6 ] = t2.z; tangentArray[ offset_tangent + 7 ] = t2.w; tangentArray[ offset_tangent + 8 ] = t3.x; tangentArray[ offset_tangent + 9 ] = t3.y; tangentArray[ offset_tangent + 10 ] = t3.z; tangentArray[ offset_tangent + 11 ] = t3.w; offset_tangent += 12; } for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { face = obj_faces[ chunk_faces4[ f ] ]; vertexTangents = face.vertexTangents; t1 = vertexTangents[ 0 ]; t2 = vertexTangents[ 1 ]; t3 = vertexTangents[ 2 ]; t4 = vertexTangents[ 3 ]; tangentArray[ offset_tangent ] = t1.x; tangentArray[ offset_tangent + 1 ] = t1.y; tangentArray[ offset_tangent + 2 ] = t1.z; tangentArray[ offset_tangent + 3 ] = t1.w; tangentArray[ offset_tangent + 4 ] = t2.x; tangentArray[ offset_tangent + 5 ] = t2.y; tangentArray[ offset_tangent + 6 ] = t2.z; tangentArray[ offset_tangent + 7 ] = t2.w; tangentArray[ offset_tangent + 8 ] = t3.x; tangentArray[ offset_tangent + 9 ] = t3.y; tangentArray[ offset_tangent + 10 ] = t3.z; tangentArray[ offset_tangent + 11 ] = t3.w; tangentArray[ offset_tangent + 12 ] = t4.x; tangentArray[ offset_tangent + 13 ] = t4.y; tangentArray[ offset_tangent + 14 ] = t4.z; tangentArray[ offset_tangent + 15 ] = t4.w; offset_tangent += 16; } _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, tangentArray, hint ); } if ( dirtyNormals && normalType ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { face = obj_faces[ chunk_faces3[ f ] ]; vertexNormals = face.vertexNormals; faceNormal = face.normal; if ( vertexNormals.length === 3 && needsSmoothNormals ) { for ( i = 0; i < 3; i ++ ) { vn = vertexNormals[ i ]; normalArray[ offset_normal ] = vn.x; normalArray[ offset_normal + 1 ] = vn.y; normalArray[ offset_normal + 2 ] = vn.z; offset_normal += 3; } } else { for ( i = 0; i < 3; i ++ ) { normalArray[ offset_normal ] = faceNormal.x; normalArray[ offset_normal + 1 ] = faceNormal.y; normalArray[ offset_normal + 2 ] = faceNormal.z; offset_normal += 3; } } } for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { face = obj_faces[ chunk_faces4[ f ] ]; vertexNormals = face.vertexNormals; faceNormal = face.normal; if ( vertexNormals.length === 4 && needsSmoothNormals ) { for ( i = 0; i < 4; i ++ ) { vn = vertexNormals[ i ]; normalArray[ offset_normal ] = vn.x; normalArray[ offset_normal + 1 ] = vn.y; normalArray[ offset_normal + 2 ] = vn.z; offset_normal += 3; } } else { for ( i = 0; i < 4; i ++ ) { normalArray[ offset_normal ] = faceNormal.x; normalArray[ offset_normal + 1 ] = faceNormal.y; normalArray[ offset_normal + 2 ] = faceNormal.z; offset_normal += 3; } } } _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, normalArray, hint ); } if ( dirtyUvs && obj_uvs && uvType ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { fi = chunk_faces3[ f ]; face = obj_faces[ fi ]; uv = obj_uvs[ fi ]; if ( uv === undefined ) continue; for ( i = 0; i < 3; i ++ ) { uvi = uv[ i ]; uvArray[ offset_uv ] = uvi.u; uvArray[ offset_uv + 1 ] = uvi.v; offset_uv += 2; } } for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { fi = chunk_faces4[ f ]; face = obj_faces[ fi ]; uv = obj_uvs[ fi ]; if ( uv === undefined ) continue; for ( i = 0; i < 4; i ++ ) { uvi = uv[ i ]; uvArray[ offset_uv ] = uvi.u; uvArray[ offset_uv + 1 ] = uvi.v; offset_uv += 2; } } if ( offset_uv > 0 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, uvArray, hint ); } } if ( dirtyUvs && obj_uvs2 && uvType ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { fi = chunk_faces3[ f ]; face = obj_faces[ fi ]; uv2 = obj_uvs2[ fi ]; if ( uv2 === undefined ) continue; for ( i = 0; i < 3; i ++ ) { uv2i = uv2[ i ]; uv2Array[ offset_uv2 ] = uv2i.u; uv2Array[ offset_uv2 + 1 ] = uv2i.v; offset_uv2 += 2; } } for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { fi = chunk_faces4[ f ]; face = obj_faces[ fi ]; uv2 = obj_uvs2[ fi ]; if ( uv2 === undefined ) continue; for ( i = 0; i < 4; i ++ ) { uv2i = uv2[ i ]; uv2Array[ offset_uv2 ] = uv2i.u; uv2Array[ offset_uv2 + 1 ] = uv2i.v; offset_uv2 += 2; } } if ( offset_uv2 > 0 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer ); _gl.bufferData( _gl.ARRAY_BUFFER, uv2Array, hint ); } } if ( dirtyElements ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { face = obj_faces[ chunk_faces3[ f ] ]; faceArray[ offset_face ] = vertexIndex; faceArray[ offset_face + 1 ] = vertexIndex + 1; faceArray[ offset_face + 2 ] = vertexIndex + 2; offset_face += 3; lineArray[ offset_line ] = vertexIndex; lineArray[ offset_line + 1 ] = vertexIndex + 1; lineArray[ offset_line + 2 ] = vertexIndex; lineArray[ offset_line + 3 ] = vertexIndex + 2; lineArray[ offset_line + 4 ] = vertexIndex + 1; lineArray[ offset_line + 5 ] = vertexIndex + 2; offset_line += 6; vertexIndex += 3; } for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { face = obj_faces[ chunk_faces4[ f ] ]; faceArray[ offset_face ] = vertexIndex; faceArray[ offset_face + 1 ] = vertexIndex + 1; faceArray[ offset_face + 2 ] = vertexIndex + 3; faceArray[ offset_face + 3 ] = vertexIndex + 1; faceArray[ offset_face + 4 ] = vertexIndex + 2; faceArray[ offset_face + 5 ] = vertexIndex + 3; offset_face += 6; lineArray[ offset_line ] = vertexIndex; lineArray[ offset_line + 1 ] = vertexIndex + 1; lineArray[ offset_line + 2 ] = vertexIndex; lineArray[ offset_line + 3 ] = vertexIndex + 3; lineArray[ offset_line + 4 ] = vertexIndex + 1; lineArray[ offset_line + 5 ] = vertexIndex + 2; lineArray[ offset_line + 6 ] = vertexIndex + 2; lineArray[ offset_line + 7 ] = vertexIndex + 3; offset_line += 8; vertexIndex += 4; } _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer ); _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, faceArray, hint ); _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer ); _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, lineArray, hint ); } if ( customAttributes ) { for ( i = 0, il = customAttributes.length; i < il; i ++ ) { customAttribute = customAttributes[ i ]; if ( ! customAttribute.__original.needsUpdate ) continue; offset_custom = 0; offset_customSrc = 0; if ( customAttribute.size === 1 ) { if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { face = obj_faces[ chunk_faces3[ f ] ]; customAttribute.array[ offset_custom ] = customAttribute.value[ face.a ]; customAttribute.array[ offset_custom + 1 ] = customAttribute.value[ face.b ]; customAttribute.array[ offset_custom + 2 ] = customAttribute.value[ face.c ]; offset_custom += 3; } for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { face = obj_faces[ chunk_faces4[ f ] ]; customAttribute.array[ offset_custom ] = customAttribute.value[ face.a ]; customAttribute.array[ offset_custom + 1 ] = customAttribute.value[ face.b ]; customAttribute.array[ offset_custom + 2 ] = customAttribute.value[ face.c ]; customAttribute.array[ offset_custom + 3 ] = customAttribute.value[ face.d ]; offset_custom += 4; } } else if ( customAttribute.boundTo === "faces" ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { value = customAttribute.value[ offset_customSrc ]; customAttribute.array[ offset_custom ] = value; customAttribute.array[ offset_custom + 1 ] = value; customAttribute.array[ offset_custom + 2 ] = value; offset_custom += 3; offset_customSrc += 1; } for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { value = customAttribute.value[ offset_customSrc ]; customAttribute.array[ offset_custom ] = value; customAttribute.array[ offset_custom + 1 ] = value; customAttribute.array[ offset_custom + 2 ] = value; customAttribute.array[ offset_custom + 3 ] = value; offset_custom += 4; offset_customSrc += 1; } } else if ( customAttribute.boundTo === "faceVertices" ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { customAttribute.array[ offset_custom ] = customAttribute.value[ offset_customSrc ]; customAttribute.array[ offset_custom + 1 ] = customAttribute.value[ offset_customSrc + 1 ]; customAttribute.array[ offset_custom + 2 ] = customAttribute.value[ offset_customSrc + 2 ]; offset_custom += 3; offset_customSrc += 3; } for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { customAttribute.array[ offset_custom ] = customAttribute.value[ offset_customSrc ]; customAttribute.array[ offset_custom + 1 ] = customAttribute.value[ offset_customSrc + 1 ]; customAttribute.array[ offset_custom + 2 ] = customAttribute.value[ offset_customSrc + 2 ]; customAttribute.array[ offset_custom + 3 ] = customAttribute.value[ offset_customSrc + 3 ]; offset_custom += 4; offset_customSrc += 4; } } } else if ( customAttribute.size === 2 ) { if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { face = obj_faces[ chunk_faces3[ f ] ]; v1 = customAttribute.value[ face.a ]; v2 = customAttribute.value[ face.b ]; v3 = customAttribute.value[ face.c ]; customAttribute.array[ offset_custom ] = v1.x; customAttribute.array[ offset_custom + 1 ] = v1.y; customAttribute.array[ offset_custom + 2 ] = v2.x; customAttribute.array[ offset_custom + 3 ] = v2.y; customAttribute.array[ offset_custom + 4 ] = v3.x; customAttribute.array[ offset_custom + 5 ] = v3.y; offset_custom += 6; } for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { face = obj_faces[ chunk_faces4[ f ] ]; v1 = customAttribute.value[ face.a ]; v2 = customAttribute.value[ face.b ]; v3 = customAttribute.value[ face.c ]; v4 = customAttribute.value[ face.d ]; customAttribute.array[ offset_custom ] = v1.x; customAttribute.array[ offset_custom + 1 ] = v1.y; customAttribute.array[ offset_custom + 2 ] = v2.x; customAttribute.array[ offset_custom + 3 ] = v2.y; customAttribute.array[ offset_custom + 4 ] = v3.x; customAttribute.array[ offset_custom + 5 ] = v3.y; customAttribute.array[ offset_custom + 6 ] = v4.x; customAttribute.array[ offset_custom + 7 ] = v4.y; offset_custom += 8; } } else if ( customAttribute.boundTo === "faces" ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { value = customAttribute.value[ offset_customSrc ]; 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; offset_customSrc += 1; } for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { value = customAttribute.value[ offset_customSrc ]; v1 = value; v2 = value; v3 = value; v4 = value; customAttribute.array[ offset_custom ] = v1.x; customAttribute.array[ offset_custom + 1 ] = v1.y; customAttribute.array[ offset_custom + 2 ] = v2.x; customAttribute.array[ offset_custom + 3 ] = v2.y; customAttribute.array[ offset_custom + 4 ] = v3.x; customAttribute.array[ offset_custom + 5 ] = v3.y; customAttribute.array[ offset_custom + 6 ] = v4.x; customAttribute.array[ offset_custom + 7 ] = v4.y; offset_custom += 8; offset_customSrc += 1; } } else if ( customAttribute.boundTo === "faceVertices" ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { v1 = customAttribute.value[ offset_customSrc ]; v2 = customAttribute.value[ offset_customSrc + 1 ]; v3 = customAttribute.value[ offset_customSrc + 2 ]; 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; offset_customSrc += 3; } for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { v1 = customAttribute.value[ offset_customSrc ]; v2 = customAttribute.value[ offset_customSrc + 1 ]; v3 = customAttribute.value[ offset_customSrc + 2 ]; v4 = customAttribute.value[ offset_customSrc + 3 ]; customAttribute.array[ offset_custom ] = v1.x; customAttribute.array[ offset_custom + 1 ] = v1.y; customAttribute.array[ offset_custom + 2 ] = v2.x; customAttribute.array[ offset_custom + 3 ] = v2.y; customAttribute.array[ offset_custom + 4 ] = v3.x; customAttribute.array[ offset_custom + 5 ] = v3.y; customAttribute.array[ offset_custom + 6 ] = v4.x; customAttribute.array[ offset_custom + 7 ] = v4.y; offset_custom += 8; offset_customSrc += 4; } } } else if ( customAttribute.size === 3 ) { var pp; if ( customAttribute.type === "c" ) { pp = [ "r", "g", "b" ]; } else { pp = [ "x", "y", "z" ]; } if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { face = obj_faces[ chunk_faces3[ f ] ]; v1 = customAttribute.value[ face.a ]; v2 = customAttribute.value[ face.b ]; v3 = customAttribute.value[ face.c ]; customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; offset_custom += 9; } for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { face = obj_faces[ chunk_faces4[ f ] ]; v1 = customAttribute.value[ face.a ]; v2 = customAttribute.value[ face.b ]; v3 = customAttribute.value[ face.c ]; v4 = customAttribute.value[ face.d ]; customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; customAttribute.array[ offset_custom + 9 ] = v4[ pp[ 0 ] ]; customAttribute.array[ offset_custom + 10 ] = v4[ pp[ 1 ] ]; customAttribute.array[ offset_custom + 11 ] = v4[ pp[ 2 ] ]; offset_custom += 12; offset_customSrc += 1; } } else if ( customAttribute.boundTo === "faces" ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { value = customAttribute.value[ offset_customSrc ]; 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; offset_customSrc += 1; } for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { value = customAttribute.value[ offset_customSrc ]; v1 = value; v2 = value; v3 = value; v4 = value; customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; customAttribute.array[ offset_custom + 9 ] = v4[ pp[ 0 ] ]; customAttribute.array[ offset_custom + 10 ] = v4[ pp[ 1 ] ]; customAttribute.array[ offset_custom + 11 ] = v4[ pp[ 2 ] ]; offset_custom += 12; offset_customSrc += 1; } } else if ( customAttribute.boundTo === "faceVertices" ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { v1 = customAttribute.value[ offset_customSrc ]; v2 = customAttribute.value[ offset_customSrc + 1 ]; v3 = customAttribute.value[ offset_customSrc + 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; offset_customSrc += 3; } for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { v1 = customAttribute.value[ offset_customSrc ]; v2 = customAttribute.value[ offset_customSrc + 1 ]; v3 = customAttribute.value[ offset_customSrc + 2 ]; v4 = customAttribute.value[ offset_customSrc + 3 ]; customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; customAttribute.array[ offset_custom + 9 ] = v4[ pp[ 0 ] ]; customAttribute.array[ offset_custom + 10 ] = v4[ pp[ 1 ] ]; customAttribute.array[ offset_custom + 11 ] = v4[ pp[ 2 ] ]; offset_custom += 12; offset_customSrc += 4; } } } else if ( customAttribute.size === 4 ) { if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { face = obj_faces[ chunk_faces3[ f ] ]; v1 = customAttribute.value[ face.a ]; v2 = customAttribute.value[ face.b ]; v3 = customAttribute.value[ face.c ]; customAttribute.array[ offset_custom ] = v1.x; customAttribute.array[ offset_custom + 1 ] = v1.y; customAttribute.array[ offset_custom + 2 ] = v1.z; customAttribute.array[ offset_custom + 3 ] = v1.w; customAttribute.array[ offset_custom + 4 ] = v2.x; customAttribute.array[ offset_custom + 5 ] = v2.y; customAttribute.array[ offset_custom + 6 ] = v2.z; customAttribute.array[ offset_custom + 7 ] = v2.w; customAttribute.array[ offset_custom + 8 ] = v3.x; customAttribute.array[ offset_custom + 9 ] = v3.y; customAttribute.array[ offset_custom + 10 ] = v3.z; customAttribute.array[ offset_custom + 11 ] = v3.w; offset_custom += 12; } for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { face = obj_faces[ chunk_faces4[ f ] ]; v1 = customAttribute.value[ face.a ]; v2 = customAttribute.value[ face.b ]; v3 = customAttribute.value[ face.c ]; v4 = customAttribute.value[ face.d ]; customAttribute.array[ offset_custom ] = v1.x; customAttribute.array[ offset_custom + 1 ] = v1.y; customAttribute.array[ offset_custom + 2 ] = v1.z; customAttribute.array[ offset_custom + 3 ] = v1.w; customAttribute.array[ offset_custom + 4 ] = v2.x; customAttribute.array[ offset_custom + 5 ] = v2.y; customAttribute.array[ offset_custom + 6 ] = v2.z; customAttribute.array[ offset_custom + 7 ] = v2.w; customAttribute.array[ offset_custom + 8 ] = v3.x; customAttribute.array[ offset_custom + 9 ] = v3.y; customAttribute.array[ offset_custom + 10 ] = v3.z; customAttribute.array[ offset_custom + 11 ] = v3.w; customAttribute.array[ offset_custom + 12 ] = v4.x; customAttribute.array[ offset_custom + 13 ] = v4.y; customAttribute.array[ offset_custom + 14 ] = v4.z; customAttribute.array[ offset_custom + 15 ] = v4.w; offset_custom += 16; } } else if ( customAttribute.boundTo === "faces" ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { value = customAttribute.value[ offset_customSrc ]; 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; offset_customSrc += 1; } for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { value = customAttribute.value[ offset_customSrc ]; v1 = value; v2 = value; v3 = value; v4 = value; customAttribute.array[ offset_custom ] = v1.x; customAttribute.array[ offset_custom + 1 ] = v1.y; customAttribute.array[ offset_custom + 2 ] = v1.z; customAttribute.array[ offset_custom + 3 ] = v1.w; customAttribute.array[ offset_custom + 4 ] = v2.x; customAttribute.array[ offset_custom + 5 ] = v2.y; customAttribute.array[ offset_custom + 6 ] = v2.z; customAttribute.array[ offset_custom + 7 ] = v2.w; customAttribute.array[ offset_custom + 8 ] = v3.x; customAttribute.array[ offset_custom + 9 ] = v3.y; customAttribute.array[ offset_custom + 10 ] = v3.z; customAttribute.array[ offset_custom + 11 ] = v3.w; customAttribute.array[ offset_custom + 12 ] = v4.x; customAttribute.array[ offset_custom + 13 ] = v4.y; customAttribute.array[ offset_custom + 14 ] = v4.z; customAttribute.array[ offset_custom + 15 ] = v4.w; offset_custom += 16; offset_customSrc += 1; } } else if ( customAttribute.boundTo === "faceVertices" ) { for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { v1 = customAttribute.value[ offset_customSrc ]; v2 = customAttribute.value[ offset_customSrc + 1 ]; v3 = customAttribute.value[ offset_customSrc + 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; offset_customSrc += 3; } for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { v1 = customAttribute.value[ offset_customSrc ]; v2 = customAttribute.value[ offset_customSrc + 1 ]; v3 = customAttribute.value[ offset_customSrc + 2 ]; v4 = customAttribute.value[ offset_customSrc + 3 ]; customAttribute.array[ offset_custom ] = v1.x; customAttribute.array[ offset_custom + 1 ] = v1.y; customAttribute.array[ offset_custom + 2 ] = v1.z; customAttribute.array[ offset_custom + 3 ] = v1.w; customAttribute.array[ offset_custom + 4 ] = v2.x; customAttribute.array[ offset_custom + 5 ] = v2.y; customAttribute.array[ offset_custom + 6 ] = v2.z; customAttribute.array[ offset_custom + 7 ] = v2.w; customAttribute.array[ offset_custom + 8 ] = v3.x; customAttribute.array[ offset_custom + 9 ] = v3.y; customAttribute.array[ offset_custom + 10 ] = v3.z; customAttribute.array[ offset_custom + 11 ] = v3.w; customAttribute.array[ offset_custom + 12 ] = v4.x; customAttribute.array[ offset_custom + 13 ] = v4.y; customAttribute.array[ offset_custom + 14 ] = v4.z; customAttribute.array[ offset_custom + 15 ] = v4.w; offset_custom += 16; offset_customSrc += 4; } } } _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); } } if ( dispose ) { delete geometryGroup.__inittedArrays; delete geometryGroup.__colorArray; delete geometryGroup.__normalArray; delete geometryGroup.__tangentArray; delete geometryGroup.__uvArray; delete geometryGroup.__uv2Array; delete geometryGroup.__faceArray; delete geometryGroup.__vertexArray; delete geometryGroup.__lineArray; delete geometryGroup.__skinVertexAArray; delete geometryGroup.__skinVertexBArray; delete geometryGroup.__skinIndexArray; delete geometryGroup.__skinWeightArray; } }; function setLineBuffers ( geometry, hint ) { var v, c, vertex, offset, vertices = geometry.vertices, colors = geometry.colors, vl = vertices.length, cl = colors.length, vertexArray = geometry.__vertexArray, colorArray = geometry.__colorArray, dirtyVertices = geometry.__dirtyVertices, dirtyColors = geometry.__dirtyColors, customAttributes = geometry.__webglCustomAttributesList, i, il, a, ca, cal, value, customAttribute; if ( dirtyVertices ) { for ( v = 0; v < vl; v ++ ) { vertex = vertices[ v ].position; offset = v * 3; vertexArray[ offset ] = vertex.x; vertexArray[ offset + 1 ] = vertex.y; vertexArray[ offset + 2 ] = vertex.z; } _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); } if ( dirtyColors ) { for ( c = 0; c < cl; c ++ ) { color = colors[ c ]; offset = c * 3; colorArray[ offset ] = color.r; colorArray[ offset + 1 ] = color.g; colorArray[ offset + 2 ] = color.b; } _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); } if ( customAttributes ) { for ( i = 0, il = customAttributes.length; i < il; i ++ ) { customAttribute = customAttributes[ i ]; if ( customAttribute.needsUpdate && ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) ) { offset = 0; cal = customAttribute.value.length; for ( ca = 0; ca < cal; ca ++ ) { if ( customAttribute.size === 1 ) { customAttribute.array[ offset ] = customAttribute.value[ ca ]; } else { value = customAttribute.value[ ca ]; if ( customAttribute.size === 2 ) { customAttribute.array[ offset ] = value.x; customAttribute.array[ offset + 1 ] = value.y; } else if ( customAttribute.size === 3 ) { if ( customAttribute.type === "c" ) { customAttribute.array[ offset ] = value.r; customAttribute.array[ offset + 1 ] = value.g; customAttribute.array[ offset + 2 ] = value.b; } else { customAttribute.array[ offset ] = value.x; customAttribute.array[ offset + 1 ] = value.y; customAttribute.array[ offset + 2 ] = value.z; } } else { customAttribute.array[ offset ] = value.x; customAttribute.array[ offset + 1 ] = value.y; customAttribute.array[ offset + 2 ] = value.z; customAttribute.array[ offset + 3 ] = value.w; } } offset += customAttribute.size; } _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); } } } }; function setRibbonBuffers ( geometry, hint ) { var v, c, vertex, offset, vertices = geometry.vertices, colors = geometry.colors, vl = vertices.length, cl = colors.length, vertexArray = geometry.__vertexArray, colorArray = geometry.__colorArray, dirtyVertices = geometry.__dirtyVertices, dirtyColors = geometry.__dirtyColors; if ( dirtyVertices ) { for ( v = 0; v < vl; v ++ ) { vertex = vertices[ v ].position; offset = v * 3; vertexArray[ offset ] = vertex.x; vertexArray[ offset + 1 ] = vertex.y; vertexArray[ offset + 2 ] = vertex.z; } _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); } if ( dirtyColors ) { for ( c = 0; c < cl; c ++ ) { color = colors[ c ]; offset = c * 3; colorArray[ offset ] = color.r; colorArray[ offset + 1 ] = color.g; colorArray[ offset + 2 ] = color.b; } _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); } }; function setParticleBuffers ( geometry, hint, object ) { var v, c, vertex, offset, vertices = geometry.vertices, vl = vertices.length, colors = geometry.colors, cl = colors.length, vertexArray = geometry.__vertexArray, colorArray = geometry.__colorArray, sortArray = geometry.__sortArray, dirtyVertices = geometry.__dirtyVertices, dirtyElements = geometry.__dirtyElements, dirtyColors = geometry.__dirtyColors, customAttributes = geometry.__webglCustomAttributesList, i, il, a, ca, cal, value, customAttribute; if ( object.sortParticles ) { _projScreenMatrix.multiplySelf( object.matrixWorld ); for ( v = 0; v < vl; v ++ ) { vertex = vertices[ v ].position; _vector3.copy( vertex ); _projScreenMatrix.multiplyVector3( _vector3 ); sortArray[ v ] = [ _vector3.z, v ]; } sortArray.sort( function( a, b ) { return b[ 0 ] - a[ 0 ]; } ); for ( v = 0; v < vl; v ++ ) { vertex = vertices[ sortArray[v][1] ].position; offset = v * 3; vertexArray[ offset ] = vertex.x; vertexArray[ offset + 1 ] = vertex.y; vertexArray[ offset + 2 ] = vertex.z; } for ( c = 0; c < cl; c ++ ) { offset = c * 3; color = colors[ sortArray[c][1] ]; colorArray[ offset ] = color.r; colorArray[ offset + 1 ] = color.g; colorArray[ offset + 2 ] = color.b; } if ( customAttributes ) { for ( i = 0, il = customAttributes.length; i < il; i ++ ) { customAttribute = customAttributes[ i ]; if ( ! ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) ) continue; offset = 0; cal = customAttribute.value.length; for ( ca = 0; ca < cal; ca ++ ) { index = sortArray[ ca ][ 1 ]; if ( customAttribute.size === 1 ) { customAttribute.array[ offset ] = customAttribute.value[ index ]; } else { value = customAttribute.value[ index ]; if ( customAttribute.size === 2 ) { customAttribute.array[ offset ] = value.x; customAttribute.array[ offset + 1 ] = value.y; } else if ( customAttribute.size === 3 ) { if ( customAttribute.type === "c" ) { customAttribute.array[ offset ] = value.r; customAttribute.array[ offset + 1 ] = value.g; customAttribute.array[ offset + 2 ] = value.b; } else { customAttribute.array[ offset ] = value.x; customAttribute.array[ offset + 1 ] = value.y; customAttribute.array[ offset + 2 ] = value.z; } } else { customAttribute.array[ offset ] = value.x; customAttribute.array[ offset + 1 ] = value.y; customAttribute.array[ offset + 2 ] = value.z; customAttribute.array[ offset + 3 ] = value.w; } } offset += customAttribute.size; } } } } else { if ( dirtyVertices ) { for ( v = 0; v < vl; v ++ ) { vertex = vertices[ v ].position; offset = v * 3; vertexArray[ offset ] = vertex.x; vertexArray[ offset + 1 ] = vertex.y; vertexArray[ offset + 2 ] = vertex.z; } } if ( dirtyColors ) { for ( c = 0; c < cl; c ++ ) { color = colors[ c ]; offset = c * 3; colorArray[ offset ] = color.r; colorArray[ offset + 1 ] = color.g; colorArray[ offset + 2 ] = color.b; } } if ( customAttributes ) { for ( i = 0, il = customAttributes.length; i < il; i ++ ) { customAttribute = customAttributes[ i ]; if ( customAttribute.needsUpdate && ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices") ) { cal = customAttribute.value.length; offset = 0; for ( ca = 0; ca < cal; ca ++ ) { offset_custom = customAttribute.offset; if ( customAttribute.size === 1 ) { customAttribute.array[ offset ] = customAttribute.value[ ca ]; } else { value = customAttribute.value[ ca ]; if ( customAttribute.size === 2 ) { customAttribute.array[ offset ] = value.x; customAttribute.array[ offset + 1 ] = value.y; } else if ( customAttribute.size === 3 ) { if ( customAttribute.type === "c" ) { customAttribute.array[ offset ] = value.r; customAttribute.array[ offset + 1 ] = value.g; customAttribute.array[ offset + 2 ] = value.b; } else { customAttribute.array[ offset ] = value.x; customAttribute.array[ offset + 1 ] = value.y; customAttribute.array[ offset + 2 ] = value.z; } } else { customAttribute.array[ offset ] = value.x; customAttribute.array[ offset + 1 ] = value.y; customAttribute.array[ offset + 2 ] = value.z; customAttribute.array[ offset + 3 ] = value.w; } } offset += customAttribute.size; } } } } } if ( dirtyVertices || object.sortParticles ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); } if ( dirtyColors || object.sortParticles ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); } if ( customAttributes ) { for ( i = 0, il = customAttributes.length; i < il; i ++ ) { customAttribute = customAttributes[ i ]; if ( customAttribute.needsUpdate || object.sortParticles ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); } } } }; function setMaterialShaders( material, shaders ) { material.uniforms = THREE.UniformsUtils.clone( shaders.uniforms ); material.vertexShader = shaders.vertexShader; material.fragmentShader = shaders.fragmentShader; }; function refreshUniformsCommon( uniforms, material ) { uniforms.opacity.value = material.opacity; if ( _this.gammaInput ) { uniforms.diffuse.value.copyGammaToLinear( material.color ); } else { uniforms.diffuse.value = material.color; } uniforms.map.texture = material.map; if ( material.map ) { uniforms.offsetRepeat.value.set( material.map.offset.x, material.map.offset.y, material.map.repeat.x, material.map.repeat.y ); } uniforms.lightMap.texture = material.lightMap; uniforms.envMap.texture = material.envMap; uniforms.flipEnvMap.value = ( material.envMap instanceof THREE.WebGLRenderTargetCube ) ? 1 : -1; if ( _this.gammaInput ) { //uniforms.reflectivity.value = material.reflectivity * material.reflectivity; uniforms.reflectivity.value = material.reflectivity; } else { uniforms.reflectivity.value = material.reflectivity; } uniforms.refractionRatio.value = material.refractionRatio; uniforms.combine.value = material.combine; uniforms.useRefract.value = material.envMap && material.envMap.mapping instanceof THREE.CubeRefractionMapping; }; function refreshUniformsLine( uniforms, material ) { uniforms.diffuse.value = material.color; uniforms.opacity.value = material.opacity; }; function refreshUniformsParticle( uniforms, material ) { uniforms.psColor.value = material.color; uniforms.opacity.value = material.opacity; uniforms.size.value = material.size; uniforms.scale.value = _canvas.height / 2.0; // TODO: Cache this. uniforms.map.texture = material.map; }; function refreshUniformsFog( uniforms, fog ) { uniforms.fogColor.value = fog.color; if ( fog instanceof THREE.Fog ) { uniforms.fogNear.value = fog.near; uniforms.fogFar.value = fog.far; } else if ( fog instanceof THREE.FogExp2 ) { uniforms.fogDensity.value = fog.density; } }; function refreshUniformsPhong( uniforms, material ) { uniforms.shininess.value = material.shininess; if ( _this.gammaInput ) { uniforms.ambient.value.copyGammaToLinear( material.ambient ); uniforms.specular.value.copyGammaToLinear( material.specular ); } else { uniforms.ambient.value = material.ambient; uniforms.specular.value = material.specular; } }; function refreshUniformsLambert( uniforms, material ) { if ( _this.gammaInput ) { uniforms.ambient.value.copyGammaToLinear( material.ambient ); } else { uniforms.ambient.value = material.ambient; } }; function refreshUniformsLights( uniforms, lights ) { uniforms.enableLighting.value = lights.directional.length + lights.point.length; uniforms.ambientLightColor.value = lights.ambient; uniforms.directionalLightColor.value = lights.directional.colors; uniforms.directionalLightDirection.value = lights.directional.positions; uniforms.pointLightColor.value = lights.point.colors; uniforms.pointLightPosition.value = lights.point.positions; uniforms.pointLightDistance.value = lights.point.distances; }; function refreshUniformsShadow( uniforms, material ) { if ( uniforms.shadowMatrix ) { for ( var i = 0; i < _shadowMatrix.length; i ++ ) { uniforms.shadowMatrix.value[ i ] = _shadowMatrix[ i ]; uniforms.shadowMap.texture[ i ] = _this.shadowMap[ i ]; } uniforms.shadowDarkness.value = _this.shadowMapDarkness; uniforms.shadowBias.value = _this.shadowMapBias; } }; this.initMaterial = function ( material, lights, fog, object ) { var u, a, identifiers, i, parameters, maxLightCount, maxBones, maxShadows, shaderID; if ( material instanceof THREE.MeshDepthMaterial ) { shaderID = 'depth'; } else if ( material instanceof THREE.MeshNormalMaterial ) { shaderID = 'normal'; } else if ( material instanceof THREE.MeshBasicMaterial ) { shaderID = 'basic'; } else if ( material instanceof THREE.MeshLambertMaterial ) { shaderID = 'lambert'; } else if ( material instanceof THREE.MeshPhongMaterial ) { shaderID = 'phong'; } else if ( material instanceof THREE.LineBasicMaterial ) { shaderID = 'basic'; } else if ( material instanceof THREE.ParticleBasicMaterial ) { shaderID = 'particle_basic'; } if ( shaderID ) { setMaterialShaders( material, THREE.ShaderLib[ shaderID ] ); } // heuristics to create shader parameters according to lights in the scene // (not to blow over maxLights budget) maxLightCount = allocateLights( lights ); maxShadows = allocateShadows( lights ); maxBones = allocateBones( object ); parameters = { map: !!material.map, envMap: !!material.envMap, lightMap: !!material.lightMap, vertexColors: material.vertexColors, fog: fog, useFog: material.fog, sizeAttenuation: material.sizeAttenuation, skinning: material.skinning, morphTargets: material.morphTargets, maxMorphTargets: this.maxMorphTargets, maxDirLights: maxLightCount.directional, maxPointLights: maxLightCount.point, maxBones: maxBones, shadowMapEnabled: this.shadowMapEnabled && object.receiveShadow, shadowMapSoft: this.shadowMapSoft, shadowMapWidth: this.shadowMapWidth, shadowMapHeight: this.shadowMapHeight, maxShadows: maxShadows, alphaTest: material.alphaTest, metal: material.metal, perPixel: material.perPixel }; material.program = buildProgram( shaderID, material.fragmentShader, material.vertexShader, material.uniforms, material.attributes, parameters ); var attributes = material.program.attributes; if ( attributes.position >= 0 ) _gl.enableVertexAttribArray( attributes.position ); if ( attributes.color >= 0 ) _gl.enableVertexAttribArray( attributes.color ); if ( attributes.normal >= 0 ) _gl.enableVertexAttribArray( attributes.normal ); if ( attributes.tangent >= 0 ) _gl.enableVertexAttribArray( attributes.tangent ); if ( material.skinning && attributes.skinVertexA >=0 && attributes.skinVertexB >= 0 && attributes.skinIndex >= 0 && attributes.skinWeight >= 0 ) { _gl.enableVertexAttribArray( attributes.skinVertexA ); _gl.enableVertexAttribArray( attributes.skinVertexB ); _gl.enableVertexAttribArray( attributes.skinIndex ); _gl.enableVertexAttribArray( attributes.skinWeight ); } if ( material.attributes ) { for ( a in material.attributes ) { if( attributes[ a ] !== undefined && attributes[ a ] >= 0 ) _gl.enableVertexAttribArray( attributes[ a ] ); } } if ( material.morphTargets ) { material.numSupportedMorphTargets = 0; var id, base = "morphTarget"; for ( i = 0; i < this.maxMorphTargets; i ++ ) { id = base + i; if ( attributes[ id ] >= 0 ) { _gl.enableVertexAttribArray( attributes[ id ] ); material.numSupportedMorphTargets ++; } } } material.uniformsList = []; for ( u in material.uniforms ) { material.uniformsList.push( [ material.uniforms[ u ], u ] ); } }; function setProgram( camera, lights, fog, material, object ) { if ( ! material.program ) { _this.initMaterial( material, lights, fog, object ); } if ( material.morphTargets ) { if ( ! object.__webglMorphTargetInfluences ) { object.__webglMorphTargetInfluences = new Float32Array( _this.maxMorphTargets ); for ( var i = 0, il = _this.maxMorphTargets; i < il; i ++ ) { object.__webglMorphTargetInfluences[ i ] = 0; } } } var refreshMaterial = false; var program = material.program, p_uniforms = program.uniforms, m_uniforms = material.uniforms; if ( program !== _currentProgram ) { _gl.useProgram( program ); _currentProgram = program; refreshMaterial = true; } if ( material.id !== _currentMaterialId ) { _currentMaterialId = material.id; refreshMaterial = true; } if ( refreshMaterial ) { _gl.uniformMatrix4fv( p_uniforms.projectionMatrix, false, _projectionMatrixArray ); // refresh uniforms common to several materials if ( fog && material.fog ) { refreshUniformsFog( m_uniforms, fog ); } if ( material instanceof THREE.MeshPhongMaterial || material instanceof THREE.MeshLambertMaterial || material.lights ) { setupLights( program, lights ); refreshUniformsLights( m_uniforms, _lights ); } if ( material instanceof THREE.MeshBasicMaterial || material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) { refreshUniformsCommon( m_uniforms, material ); } // refresh single material specific uniforms if ( material instanceof THREE.LineBasicMaterial ) { refreshUniformsLine( m_uniforms, material ); } else if ( material instanceof THREE.ParticleBasicMaterial ) { refreshUniformsParticle( m_uniforms, material ); } else if ( material instanceof THREE.MeshPhongMaterial ) { refreshUniformsPhong( m_uniforms, material ); } else if ( material instanceof THREE.MeshLambertMaterial ) { refreshUniformsLambert( m_uniforms, material ); } else if ( material instanceof THREE.MeshDepthMaterial ) { m_uniforms.mNear.value = camera.near; m_uniforms.mFar.value = camera.far; m_uniforms.opacity.value = material.opacity; } else if ( material instanceof THREE.MeshNormalMaterial ) { m_uniforms.opacity.value = material.opacity; } if ( object.receiveShadow && ! material._shadowPass ) { refreshUniformsShadow( m_uniforms, material ); } // load common uniforms loadUniformsGeneric( program, material.uniformsList ); // load material specific uniforms // (shader material also gets them for the sake of genericity) if ( material instanceof THREE.ShaderMaterial || material instanceof THREE.MeshPhongMaterial || material.envMap ) { if( p_uniforms.cameraPosition !== null ) { _gl.uniform3f( p_uniforms.cameraPosition, camera.position.x, camera.position.y, camera.position.z ); } } if ( material instanceof THREE.MeshPhongMaterial || material instanceof THREE.MeshLambertMaterial || material instanceof THREE.ShaderMaterial || material.skinning ) { if( p_uniforms.viewMatrix !== null ) { _gl.uniformMatrix4fv( p_uniforms.viewMatrix, false, _viewMatrixArray ); } } if ( material.skinning ) { loadUniformsSkinning( p_uniforms, object ); } } loadUniformsMatrices( p_uniforms, object ); if ( material instanceof THREE.ShaderMaterial || material.envMap || material.skinning || object.receiveShadow ) { if ( p_uniforms.objectMatrix !== null ) { _gl.uniformMatrix4fv( p_uniforms.objectMatrix, false, object._objectMatrixArray ); } } return program; }; function renderBuffer( camera, lights, fog, material, geometryGroup, object ) { if ( material.opacity === 0 ) return; var program, attributes, linewidth, primitives, a, attribute, i, il; program = setProgram( camera, lights, fog, material, object ); attributes = program.attributes; var updateBuffers = false, wireframeBit = material.wireframe ? 1 : 0, geometryGroupHash = ( geometryGroup.id * 0xffffff ) + ( program.id * 2 ) + wireframeBit; if ( geometryGroupHash !== _currentGeometryGroupHash ) { _currentGeometryGroupHash = geometryGroupHash; updateBuffers = true; } // vertices if ( !material.morphTargets && attributes.position >= 0 ) { if ( updateBuffers ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); } } else { if ( object.morphTargetBase ) { setupMorphTargets( material, geometryGroup, object ); } } if ( updateBuffers ) { // custom attributes // Use the per-geometryGroup custom attribute arrays which are setup in initMeshBuffers if ( geometryGroup.__webglCustomAttributesList ) { for ( i = 0, il = geometryGroup.__webglCustomAttributesList.length; i < il; i ++ ) { attribute = geometryGroup.__webglCustomAttributesList[ i ]; if( attributes[ attribute.buffer.belongsToAttribute ] >= 0 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, attribute.buffer ); _gl.vertexAttribPointer( attributes[ attribute.buffer.belongsToAttribute ], attribute.size, _gl.FLOAT, false, 0, 0 ); } } } // colors if ( attributes.color >= 0 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer ); _gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 ); } // normals if ( attributes.normal >= 0 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer ); _gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); } // tangents if ( attributes.tangent >= 0 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer ); _gl.vertexAttribPointer( attributes.tangent, 4, _gl.FLOAT, false, 0, 0 ); } // uvs if ( attributes.uv >= 0 ) { if ( geometryGroup.__webglUVBuffer ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer ); _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); _gl.enableVertexAttribArray( attributes.uv ); } else { _gl.disableVertexAttribArray( attributes.uv ); } } if ( attributes.uv2 >= 0 ) { if ( geometryGroup.__webglUV2Buffer ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer ); _gl.vertexAttribPointer( attributes.uv2, 2, _gl.FLOAT, false, 0, 0 ); _gl.enableVertexAttribArray( attributes.uv2 ); } else { _gl.disableVertexAttribArray( attributes.uv2 ); } } if ( material.skinning && attributes.skinVertexA >= 0 && attributes.skinVertexB >= 0 && attributes.skinIndex >= 0 && attributes.skinWeight >= 0 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinVertexABuffer ); _gl.vertexAttribPointer( attributes.skinVertexA, 4, _gl.FLOAT, false, 0, 0 ); _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinVertexBBuffer ); _gl.vertexAttribPointer( attributes.skinVertexB, 4, _gl.FLOAT, false, 0, 0 ); _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer ); _gl.vertexAttribPointer( attributes.skinIndex, 4, _gl.FLOAT, false, 0, 0 ); _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer ); _gl.vertexAttribPointer( attributes.skinWeight, 4, _gl.FLOAT, false, 0, 0 ); } } // render mesh if ( object instanceof THREE.Mesh ) { // wireframe if ( material.wireframe ) { _gl.lineWidth( material.wireframeLinewidth ); if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer ); _gl.drawElements( _gl.LINES, geometryGroup.__webglLineCount, _gl.UNSIGNED_SHORT, 0 ); // triangles } else { if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer ); _gl.drawElements( _gl.TRIANGLES, geometryGroup.__webglFaceCount, _gl.UNSIGNED_SHORT, 0 ); } _this.info.render.calls ++; _this.info.render.vertices += geometryGroup.__webglFaceCount; _this.info.render.faces += geometryGroup.__webglFaceCount / 3; // render lines } else if ( object instanceof THREE.Line ) { primitives = ( object.type === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES; _gl.lineWidth( material.linewidth ); _gl.drawArrays( primitives, 0, geometryGroup.__webglLineCount ); _this.info.render.calls ++; // render particles } else if ( object instanceof THREE.ParticleSystem ) { _gl.drawArrays( _gl.POINTS, 0, geometryGroup.__webglParticleCount ); _this.info.render.calls ++; // render ribbon } else if ( object instanceof THREE.Ribbon ) { _gl.drawArrays( _gl.TRIANGLE_STRIP, 0, geometryGroup.__webglVertexCount ); _this.info.render.calls ++; } }; function setupMorphTargets( material, geometryGroup, object ) { // set base var attributes = material.program.attributes; if ( object.morphTargetBase !== - 1 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ object.morphTargetBase ] ); _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); } else if ( attributes.position >= 0 ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); } if ( object.morphTargetForcedOrder.length ) { // set forced order var m = 0; var order = object.morphTargetForcedOrder; var influences = object.morphTargetInfluences; while ( m < material.numSupportedMorphTargets && m < order.length ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ order[ m ] ] ); _gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 ); object.__webglMorphTargetInfluences[ m ] = influences[ order[ m ] ]; m ++; } } else { // find most influencing var used = []; var candidateInfluence = - 1; var candidate = 0; var influences = object.morphTargetInfluences; var i, il = influences.length; var m = 0; if ( object.morphTargetBase !== - 1 ) { used[ object.morphTargetBase ] = true; } while ( m < material.numSupportedMorphTargets ) { for ( i = 0; i < il; i ++ ) { if ( !used[ i ] && influences[ i ] > candidateInfluence ) { candidate = i; candidateInfluence = influences[ candidate ]; } } _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ candidate ] ); _gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 ); object.__webglMorphTargetInfluences[ m ] = candidateInfluence; used[ candidate ] = 1; candidateInfluence = -1; m ++; } } // load updated influences uniform if( material.program.uniforms.morphTargetInfluences !== null ) { _gl.uniform1fv( material.program.uniforms.morphTargetInfluences, object.__webglMorphTargetInfluences ); } } function renderBufferImmediate( object, program, shading ) { if ( ! object.__webglVertexBuffer ) object.__webglVertexBuffer = _gl.createBuffer(); if ( ! object.__webglNormalBuffer ) object.__webglNormalBuffer = _gl.createBuffer(); if ( object.hasPos ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglVertexBuffer ); _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW ); _gl.enableVertexAttribArray( program.attributes.position ); _gl.vertexAttribPointer( program.attributes.position, 3, _gl.FLOAT, false, 0, 0 ); } if ( object.hasNormal ) { _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglNormalBuffer ); if ( shading === THREE.FlatShading ) { var nx, ny, nz, nax, nbx, ncx, nay, nby, ncy, naz, nbz, ncz, normalArray, i, il = object.count * 3; for( i = 0; i < il; i += 9 ) { normalArray = object.normalArray; nax = normalArray[ i ]; nay = normalArray[ i + 1 ]; naz = normalArray[ i + 2 ]; nbx = normalArray[ i + 3 ]; nby = normalArray[ i + 4 ]; nbz = normalArray[ i + 5 ]; ncx = normalArray[ i + 6 ]; ncy = normalArray[ i + 7 ]; ncz = normalArray[ i + 8 ]; nx = ( nax + nbx + ncx ) / 3; ny = ( nay + nby + ncy ) / 3; nz = ( naz + nbz + ncz ) / 3; normalArray[ i ] = nx; normalArray[ i + 1 ] = ny; normalArray[ i + 2 ] = nz; normalArray[ i + 3 ] = nx; normalArray[ i + 4 ] = ny; normalArray[ i + 5 ] = nz; normalArray[ i + 6 ] = nx; normalArray[ i + 7 ] = ny; normalArray[ i + 8 ] = nz; } } _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW ); _gl.enableVertexAttribArray( program.attributes.normal ); _gl.vertexAttribPointer( program.attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); } _gl.drawArrays( _gl.TRIANGLES, 0, object.count ); object.count = 0; }; function setObjectFaces( object ) { if ( _oldDoubleSided !== object.doubleSided ) { if( object.doubleSided ) { _gl.disable( _gl.CULL_FACE ); } else { _gl.enable( _gl.CULL_FACE ); } _oldDoubleSided = object.doubleSided; } if ( _oldFlipSided !== object.flipSided ) { if( object.flipSided ) { _gl.frontFace( _gl.CW ); } else { _gl.frontFace( _gl.CCW ); } _oldFlipSided = object.flipSided; } }; function setDepthTest( depthTest ) { if ( _oldDepthTest !== depthTest ) { if( depthTest ) { _gl.enable( _gl.DEPTH_TEST ); } else { _gl.disable( _gl.DEPTH_TEST ); } _oldDepthTest = depthTest; } }; function setDepthWrite( depthWrite ) { if ( _oldDepthWrite !== depthWrite ) { _gl.depthMask( depthWrite ); _oldDepthWrite = depthWrite; } }; function setPolygonOffset ( polygonoffset, factor, units ) { if ( _oldPolygonOffset !== polygonoffset ) { if ( polygonoffset ) { _gl.enable( _gl.POLYGON_OFFSET_FILL ); } else { _gl.disable( _gl.POLYGON_OFFSET_FILL ); } _oldPolygonOffset = polygonoffset; } if ( polygonoffset && ( _oldPolygonOffsetFactor !== factor || _oldPolygonOffsetUnits !== units ) ) { _gl.polygonOffset( factor, units ); _oldPolygonOffsetFactor = factor; _oldPolygonOffsetUnits = units; } }; function computeFrustum( m ) { _frustum[ 0 ].set( m.n41 - m.n11, m.n42 - m.n12, m.n43 - m.n13, m.n44 - m.n14 ); _frustum[ 1 ].set( m.n41 + m.n11, m.n42 + m.n12, m.n43 + m.n13, m.n44 + m.n14 ); _frustum[ 2 ].set( m.n41 + m.n21, m.n42 + m.n22, m.n43 + m.n23, m.n44 + m.n24 ); _frustum[ 3 ].set( m.n41 - m.n21, m.n42 - m.n22, m.n43 - m.n23, m.n44 - m.n24 ); _frustum[ 4 ].set( m.n41 - m.n31, m.n42 - m.n32, m.n43 - m.n33, m.n44 - m.n34 ); _frustum[ 5 ].set( m.n41 + m.n31, m.n42 + m.n32, m.n43 + m.n33, m.n44 + m.n34 ); var i, plane; for ( i = 0; i < 6; i ++ ) { plane = _frustum[ i ]; plane.divideScalar( Math.sqrt( plane.x * plane.x + plane.y * plane.y + plane.z * plane.z ) ); } }; function isInFrustum( object ) { var distance, matrix = object.matrixWorld, radius = - object.geometry.boundingSphere.radius * Math.max( object.scale.x, Math.max( object.scale.y, object.scale.z ) ); for ( var i = 0; i < 6; i ++ ) { distance = _frustum[ i ].x * matrix.n14 + _frustum[ i ].y * matrix.n24 + _frustum[ i ].z * matrix.n34 + _frustum[ i ].w; if ( distance <= radius ) return false; } return true; }; function addToFixedArray( where, what ) { where.list[ where.count ] = what; where.count += 1; }; function unrollImmediateBufferMaterial( globject ) { var object = globject.object, material = object.material; if ( material.transparent ) { globject.transparent = material; globject.opaque = null; } else { globject.opaque = material; globject.transparent = null; } }; function unrollBufferMaterial( globject ) { var object = globject.object, buffer = globject.buffer, material, materialIndex, meshMaterial; meshMaterial = object.material; if ( meshMaterial instanceof THREE.MeshFaceMaterial ) { materialIndex = buffer.materialIndex; if ( materialIndex >= 0 ) { material = object.geometry.materials[ materialIndex ]; if ( material.transparent ) { globject.transparent = material; globject.opaque = null; } else { globject.opaque = material; globject.transparent = null; } } } else { material = meshMaterial; if ( material ) { if ( material.transparent ) { globject.transparent = material; globject.opaque = null; } else { globject.opaque = material; globject.transparent = null; } } } }; function painterSort( a, b ) { return b.z - a.z; }; function renderShadowMap( scene, camera ) { var i, il, light, j = 0, shadowMap, shadowMatrix, oil, material, o, ol, webglObject, object, lights = scene.lights, fog = null; if ( ! _cameraLight ) { _cameraLight = new THREE.PerspectiveCamera( _this.shadowCameraFov, _this.shadowMapWidth / _this.shadowMapHeight, _this.shadowCameraNear, _this.shadowCameraFar ); } for ( i = 0, il = lights.length; i < il; i ++ ) { light = lights[ i ]; if ( light instanceof THREE.SpotLight && light.castShadow ) { _currentMaterialId = -1; if ( ! _this.shadowMap[ j ] ) { var pars = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBAFormat }; _this.shadowMap[ j ] = new THREE.WebGLRenderTarget( _this.shadowMapWidth, _this.shadowMapHeight, pars ); } if ( ! _shadowMatrix[ j ] ) { _shadowMatrix[ j ] = new THREE.Matrix4(); } shadowMap = _this.shadowMap[ j ]; shadowMatrix = _shadowMatrix[ j ]; _cameraLight.position.copy( light.position ); _cameraLight.lookAt( light.target.position ); if ( _cameraLight.parent == null ) { console.warn( "Camera is not on the Scene. Adding it..." ); scene.add( _cameraLight ); } if ( this.autoUpdateScene ) scene.updateMatrixWorld(); _cameraLight.matrixWorldInverse.getInverse( _cameraLight.matrixWorld ); // compute shadow matrix shadowMatrix.set( 0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0 ); shadowMatrix.multiplySelf( _cameraLight.projectionMatrix ); shadowMatrix.multiplySelf( _cameraLight.matrixWorldInverse ); // render shadow map _cameraLight.matrixWorldInverse.flattenToArray( _viewMatrixArray ); _cameraLight.projectionMatrix.flattenToArray( _projectionMatrixArray ); _projScreenMatrix.multiply( _cameraLight.projectionMatrix, _cameraLight.matrixWorldInverse ); computeFrustum( _projScreenMatrix ); setRenderTarget( shadowMap ); // using arbitrary clear color in depth pass // creates variance in shadows _gl.clearColor( 1, 1, 1, 1 ); //_gl.clearColor( 0, 0, 0, 1 ); _this.clear(); _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); // set matrices & frustum culling ol = scene.__webglObjects.length; oil = scene.__webglObjectsImmediate.length; for ( o = 0; o < ol; o ++ ) { webglObject = scene.__webglObjects[ o ]; object = webglObject.object; if ( object.visible && object.castShadow ) { if ( ! ( object instanceof THREE.Mesh ) || ! ( object.frustumCulled ) || isInFrustum( object ) ) { object.matrixWorld.flattenToArray( object._objectMatrixArray ); setupMatrices( object, _cameraLight, false ); webglObject.render = true; } else { webglObject.render = false; } } else { webglObject.render = false; } } setDepthTest( true ); setBlending( THREE.NormalBlending ); // maybe blending should be just disabled? //_gl.cullFace( _gl.FRONT ); for ( o = 0; o < ol; o ++ ) { webglObject = scene.__webglObjects[ o ]; if ( webglObject.render ) { object = webglObject.object; buffer = webglObject.buffer; setObjectFaces( object ); if ( object.customDepthMaterial ) { material = object.customDepthMaterial; } else if ( object.geometry.morphTargets.length ) { material = _depthMaterialMorph; } else { material = _depthMaterial; } renderBuffer( _cameraLight, lights, fog, material, buffer, object ); } } for ( o = 0; o < oil; o ++ ) { webglObject = scene.__webglObjectsImmediate[ o ]; object = webglObject.object; if ( object.visible && object.castShadow ) { if( object.matrixAutoUpdate ) { object.matrixWorld.flattenToArray( object._objectMatrixArray ); } _currentGeometryGroupHash = -1; setupMatrices( object, _cameraLight, false ); setObjectFaces( object ); program = setProgram( _cameraLight, lights, fog, _depthMaterial, object ); if ( object.immediateRenderCallback ) { object.immediateRenderCallback( program, _gl, _frustum ); } else { object.render( function( object ) { renderBufferImmediate( object, program, _depthMaterial.shading ); } ); } } } //_gl.cullFace( _gl.BACK ); j ++; } } }; this.clearTarget = function ( renderTarget, color, depth, stencil ) { setRenderTarget( renderTarget ); this.clear( color, depth, stencil ); }; this.updateShadowMap = function ( scene, camera ) { renderShadowMap( scene, camera ); }; this.render = function( scene, camera, renderTarget, forceClear ) { var i, program, material, o, ol, oil, webglObject, object, buffer, lights = scene.lights, fog = scene.fog; _currentMaterialId = -1; if ( this.autoUpdateObjects ) this.initWebGLObjects( scene ); if ( this.shadowMapEnabled && this.shadowMapAutoUpdate ) renderShadowMap( scene, camera ); _this.info.render.calls = 0; _this.info.render.vertices = 0; _this.info.render.faces = 0; if ( camera.parent === undefined ) { console.warn( 'DEPRECATED: Camera hasn\'t been added to a Scene. Adding it...' ); scene.add( camera ); } if ( this.autoUpdateScene ) scene.updateMatrixWorld(); camera.matrixWorldInverse.getInverse( camera.matrixWorld ); camera.matrixWorldInverse.flattenToArray( _viewMatrixArray ); camera.projectionMatrix.flattenToArray( _projectionMatrixArray ); _projScreenMatrix.multiply( camera.projectionMatrix, camera.matrixWorldInverse ); computeFrustum( _projScreenMatrix ); setRenderTarget( renderTarget ); if ( this.autoClear || forceClear ) { this.clear( this.autoClearColor, this.autoClearDepth, this.autoClearStencil ); } // set matrices ol = scene.__webglObjects.length; for ( o = 0; o < ol; o ++ ) { webglObject = scene.__webglObjects[ o ]; object = webglObject.object; if ( object.visible ) { if ( ! ( object instanceof THREE.Mesh ) || ! ( object.frustumCulled ) || isInFrustum( object ) ) { object.matrixWorld.flattenToArray( object._objectMatrixArray ); setupMatrices( object, camera, true ); unrollBufferMaterial( webglObject ); webglObject.render = true; if ( this.sortObjects ) { if ( object.renderDepth ) { webglObject.z = object.renderDepth; } else { _vector3.copy( object.position ); _projScreenMatrix.multiplyVector3( _vector3 ); webglObject.z = _vector3.z; } } } else { webglObject.render = false; } } else { webglObject.render = false; } } if ( this.sortObjects ) { scene.__webglObjects.sort( painterSort ); } oil = scene.__webglObjectsImmediate.length; for ( o = 0; o < oil; o ++ ) { webglObject = scene.__webglObjectsImmediate[ o ]; object = webglObject.object; if ( object.visible ) { if( object.matrixAutoUpdate ) { object.matrixWorld.flattenToArray( object._objectMatrixArray ); } setupMatrices( object, camera, true ); unrollImmediateBufferMaterial( webglObject ); } } if ( scene.overrideMaterial ) { setDepthTest( scene.overrideMaterial.depthTest ); setBlending( scene.overrideMaterial.blending ); for ( o = 0; o < ol; o ++ ) { webglObject = scene.__webglObjects[ o ]; if ( webglObject.render ) { object = webglObject.object; buffer = webglObject.buffer; setObjectFaces( object ); renderBuffer( camera, lights, fog, scene.overrideMaterial, buffer, object ); } } for ( o = 0; o < oil; o ++ ) { webglObject = scene.__webglObjectsImmediate[ o ]; object = webglObject.object; if ( object.visible ) { _currentGeometryGroupHash = -1; setObjectFaces( object ); program = setProgram( camera, lights, fog, scene.overrideMaterial, object ); if ( object.immediateRenderCallback ) { object.immediateRenderCallback( program, _gl, _frustum ); } else { object.render( function( object ) { renderBufferImmediate( object, program, scene.overrideMaterial.shading ); } ); } } } } else { // opaque pass // (front-to-back order) setBlending( THREE.NormalBlending ); for ( o = ol - 1; o >= 0; o -- ) { webglObject = scene.__webglObjects[ o ]; if ( webglObject.render ) { object = webglObject.object; buffer = webglObject.buffer; material = webglObject.opaque; if ( ! material ) continue; setObjectFaces( object ); setDepthTest( material.depthTest ); setDepthWrite( material.depthWrite ); setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); renderBuffer( camera, lights, fog, material, buffer, object ); } } // opaque pass (immediate simulator) for ( o = 0; o < oil; o ++ ) { webglObject = scene.__webglObjectsImmediate[ o ]; object = webglObject.object; if ( object.visible ) { _currentGeometryGroupHash = -1; material = webglObject.opaque; if ( ! material ) continue; setObjectFaces( object ); setDepthTest( material.depthTest ); setDepthWrite( material.depthWrite ); setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); program = setProgram( camera, lights, fog, material, object ); if ( object.immediateRenderCallback ) { object.immediateRenderCallback( program, _gl, _frustum ); } else { object.render( function( object ) { renderBufferImmediate( object, program, material.shading ); } ); } } } // transparent pass // (back-to-front order) for ( o = 0; o < ol; o ++ ) { webglObject = scene.__webglObjects[ o ]; if ( webglObject.render ) { object = webglObject.object; buffer = webglObject.buffer; material = webglObject.transparent; if ( ! material ) continue; setObjectFaces( object ); setBlending( material.blending ); setDepthTest( material.depthTest ); setDepthWrite( material.depthWrite ); setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); renderBuffer( camera, lights, fog, material, buffer, object ); } } // transparent pass (immediate simulator) for ( o = 0; o < oil; o ++ ) { webglObject = scene.__webglObjectsImmediate[ o ]; object = webglObject.object; if ( object.visible ) { _currentGeometryGroupHash = -1; material = webglObject.transparent; if ( ! material ) continue; setObjectFaces( object ); setBlending( material.blending ); setDepthTest( material.depthTest ); setDepthWrite( material.depthWrite ); setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); program = setProgram( camera, lights, fog, material, object ); if ( object.immediateRenderCallback ) { object.immediateRenderCallback( program, _gl, _frustum ); } else { object.render( function( object ) { renderBufferImmediate( object, program, material.shading ); } ); } } } } // render 2d if ( scene.__webglSprites.length ) { renderSprites( scene, camera ); } // Generate mipmap if we're using any kind of mipmap filtering if ( renderTarget && renderTarget.minFilter !== THREE.NearestFilter && renderTarget.minFilter !== THREE.LinearFilter ) { updateRenderTargetMipmap( renderTarget ); } //_gl.finish(); }; /* * Render sprites * */ function renderSprites( scene, camera ) { var o, ol, object; var attributes = _sprite.attributes; var uniforms = _sprite.uniforms; var invAspect = _viewportHeight / _viewportWidth; var size, scale = []; var screenPosition; var halfViewportWidth = _viewportWidth * 0.5; var halfViewportHeight = _viewportHeight * 0.5; var mergeWith3D = true; // setup gl _gl.useProgram( _sprite.program ); _currentProgram = _sprite.program; _oldBlending = -1; _oldDepthTest = -1; _currentGeometryGroupHash = -1; if ( !_spriteAttributesEnabled ) { _gl.enableVertexAttribArray( _sprite.attributes.position ); _gl.enableVertexAttribArray( _sprite.attributes.uv ); _spriteAttributesEnabled = true; } _gl.disable( _gl.CULL_FACE ); _gl.enable( _gl.BLEND ); _gl.depthMask( true ); _gl.bindBuffer( _gl.ARRAY_BUFFER, _sprite.vertexBuffer ); _gl.vertexAttribPointer( attributes.position, 2, _gl.FLOAT, false, 2 * 8, 0 ); _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 2 * 8, 8 ); _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _sprite.elementBuffer ); _gl.uniformMatrix4fv( uniforms.projectionMatrix, false, _projectionMatrixArray ); _gl.activeTexture( _gl.TEXTURE0 ); _gl.uniform1i( uniforms.map, 0 ); // update positions and sort for( o = 0, ol = scene.__webglSprites.length; o < ol; o ++ ) { object = scene.__webglSprites[ o ]; if ( !object.visible || object.opacity === 0 ) continue; if( !object.useScreenCoordinates ) { object._modelViewMatrix.multiplyToArray( camera.matrixWorldInverse, object.matrixWorld, object._modelViewMatrixArray ); object.z = -object._modelViewMatrix.n34; } else { object.z = -object.position.z; } } scene.__webglSprites.sort( painterSort ); // render all sprites for ( o = 0, ol = scene.__webglSprites.length; o < ol; o ++ ) { object = scene.__webglSprites[ o ]; if ( !object.visible || object.opacity === 0 ) continue; if ( object.map && object.map.image && object.map.image.width ) { if ( object.useScreenCoordinates ) { _gl.uniform1i( uniforms.useScreenCoordinates, 1 ); _gl.uniform3f( uniforms.screenPosition, ( object.position.x - halfViewportWidth ) / halfViewportWidth, ( halfViewportHeight - object.position.y ) / halfViewportHeight, Math.max( 0, Math.min( 1, object.position.z ))); } else { _gl.uniform1i( uniforms.useScreenCoordinates, 0 ); _gl.uniform1i( uniforms.affectedByDistance, object.affectedByDistance ? 1 : 0 ); _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, object._modelViewMatrixArray ); } size = object.map.image.width / ( object.scaleByViewport ? _viewportHeight : 1 ); scale[ 0 ] = size * invAspect * object.scale.x; scale[ 1 ] = size * object.scale.y; _gl.uniform2f( uniforms.uvScale, object.uvScale.x, object.uvScale.y ); _gl.uniform2f( uniforms.uvOffset, object.uvOffset.x, object.uvOffset.y ); _gl.uniform2f( uniforms.alignment, object.alignment.x, object.alignment.y ); _gl.uniform1f( uniforms.opacity, object.opacity ); _gl.uniform3f( uniforms.color, object.color.r, object.color.g, object.color.b ); _gl.uniform1f( uniforms.rotation, object.rotation ); _gl.uniform2fv( uniforms.scale, scale ); if ( object.mergeWith3D && !mergeWith3D ) { _gl.enable( _gl.DEPTH_TEST ); mergeWith3D = true; } else if ( !object.mergeWith3D && mergeWith3D ) { _gl.disable( _gl.DEPTH_TEST ); mergeWith3D = false; } setBlending( object.blending ); setTexture( object.map, 0 ); _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); } } // restore gl _gl.enable( _gl.CULL_FACE ); _gl.enable( _gl.DEPTH_TEST ); _gl.depthMask( _oldDepthWrite ); } function setupMatrices( object, camera, computeNormalMatrix ) { object._modelViewMatrix.multiplyToArray( camera.matrixWorldInverse, object.matrixWorld, object._modelViewMatrixArray ); if ( computeNormalMatrix ) { THREE.Matrix4.makeInvert3x3( object._modelViewMatrix ).transposeIntoArray( object._normalMatrixArray ); } } this.initWebGLObjects = function ( scene ) { if ( !scene.__webglObjects ) { scene.__webglObjects = []; scene.__webglObjectsImmediate = []; scene.__webglSprites = []; } 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 ); } }; function addObject( object, scene ) { var g, geometry, geometryGroup; if ( ! object.__webglInit ) { object.__webglInit = true; object._modelViewMatrix = new THREE.Matrix4(); object._normalMatrixArray = new Float32Array( 9 ); object._modelViewMatrixArray = new Float32Array( 16 ); object._objectMatrixArray = new Float32Array( 16 ); object.matrixWorld.flattenToArray( object._objectMatrixArray ); if ( object instanceof THREE.Mesh ) { geometry = object.geometry; if ( geometry.geometryGroups === undefined ) { sortFacesByMaterial( geometry ); } // create separate VBOs per geometry chunk for ( g in geometry.geometryGroups ) { geometryGroup = geometry.geometryGroups[ g ]; // initialise VBO on the first access if ( ! geometryGroup.__webglVertexBuffer ) { createMeshBuffers( geometryGroup ); initMeshBuffers( geometryGroup, object ); geometry.__dirtyVertices = true; geometry.__dirtyMorphTargets = true; geometry.__dirtyElements = true; geometry.__dirtyUvs = true; geometry.__dirtyNormals = true; geometry.__dirtyTangents = true; geometry.__dirtyColors = true; } } } else if ( object instanceof THREE.Ribbon ) { geometry = object.geometry; if( ! geometry.__webglVertexBuffer ) { createRibbonBuffers( geometry ); initRibbonBuffers( geometry ); geometry.__dirtyVertices = true; geometry.__dirtyColors = true; } } else if ( object instanceof THREE.Line ) { geometry = object.geometry; if( ! geometry.__webglVertexBuffer ) { createLineBuffers( geometry ); initLineBuffers( geometry, object ); geometry.__dirtyVertices = true; geometry.__dirtyColors = true; } } else if ( object instanceof THREE.ParticleSystem ) { geometry = object.geometry; if ( ! geometry.__webglVertexBuffer ) { createParticleBuffers( geometry ); initParticleBuffers( geometry, object ); geometry.__dirtyVertices = true; geometry.__dirtyColors = true; } } } if ( ! object.__webglActive ) { if ( object instanceof THREE.Mesh ) { geometry = object.geometry; for ( g in geometry.geometryGroups ) { geometryGroup = geometry.geometryGroups[ g ]; addBuffer( scene.__webglObjects, geometryGroup, object ); } } else if ( object instanceof THREE.Ribbon || object instanceof THREE.Line || object instanceof THREE.ParticleSystem ) { geometry = object.geometry; addBuffer( scene.__webglObjects, geometry, object ); } else if ( THREE.MarchingCubes !== undefined && object instanceof THREE.MarchingCubes || object.immediateRenderCallback ) { addBufferImmediate( scene.__webglObjectsImmediate, object ); } else if ( object instanceof THREE.Sprite ) { scene.__webglSprites.push( object ); } object.__webglActive = true; } }; 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; } }; function updateObject( object ) { var geometry = object.geometry, geometryGroup, customAttributesDirty, material; if ( object instanceof THREE.Mesh ) { // check all geometry groups for( var i = 0, il = geometry.geometryGroupsList.length; i < il; i ++ ) { geometryGroup = geometry.geometryGroupsList[ i ]; material = getBufferMaterial( object, geometryGroup ); customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); if ( geometry.__dirtyVertices || geometry.__dirtyMorphTargets || geometry.__dirtyElements || geometry.__dirtyUvs || geometry.__dirtyNormals || geometry.__dirtyColors || geometry.__dirtyTangents || customAttributesDirty ) { setMeshBuffers( geometryGroup, object, _gl.DYNAMIC_DRAW, !geometry.dynamic ); } } geometry.__dirtyVertices = false; geometry.__dirtyMorphTargets = false; geometry.__dirtyElements = false; geometry.__dirtyUvs = false; geometry.__dirtyNormals = false; geometry.__dirtyColors = false; geometry.__dirtyTangents = false; material.attributes && clearCustomAttributes( material ); } else if ( object instanceof THREE.Ribbon ) { if ( geometry.__dirtyVertices || geometry.__dirtyColors ) { setRibbonBuffers( geometry, _gl.DYNAMIC_DRAW ); } geometry.__dirtyVertices = false; geometry.__dirtyColors = false; } else if ( object instanceof THREE.Line ) { material = getBufferMaterial( object, geometryGroup ); customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); if ( geometry.__dirtyVertices || geometry.__dirtyColors || customAttributesDirty ) { setLineBuffers( geometry, _gl.DYNAMIC_DRAW ); } geometry.__dirtyVertices = false; geometry.__dirtyColors = false; material.attributes && clearCustomAttributes( material ); } else if ( object instanceof THREE.ParticleSystem ) { material = getBufferMaterial( object, geometryGroup ); customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); if ( geometry.__dirtyVertices || geometry.__dirtyColors || object.sortParticles || customAttributesDirty ) { setParticleBuffers( geometry, _gl.DYNAMIC_DRAW, object ); } geometry.__dirtyVertices = false; geometry.__dirtyColors = false; material.attributes && clearCustomAttributes( material ); } }; 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 ); } } }; 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.MarchingCubes || object.immediateRenderCallback ) { removeInstances( scene.__webglObjectsImmediate, object ); } object.__webglActive = false; }; function sortFacesByMaterial( geometry ) { var f, fl, face, materialIndex, vertices, materialHash, groupHash, hash_map = {}; var numMorphTargets = geometry.morphTargets.length; geometry.geometryGroups = {}; for ( f = 0, fl = geometry.faces.length; f < fl; f ++ ) { face = geometry.faces[ f ]; materialIndex = face.materialIndex; materialHash = ( materialIndex !== undefined ) ? materialIndex : -1; if ( hash_map[ materialHash ] === undefined ) { hash_map[ materialHash ] = { 'hash': materialHash, 'counter': 0 }; } groupHash = hash_map[ materialHash ].hash + '_' + hash_map[ materialHash ].counter; if ( geometry.geometryGroups[ groupHash ] === undefined ) { geometry.geometryGroups[ groupHash ] = { 'faces3': [], 'faces4': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets }; } vertices = face instanceof THREE.Face3 ? 3 : 4; if ( geometry.geometryGroups[ groupHash ].vertices + vertices > 65535 ) { hash_map[ materialHash ].counter += 1; groupHash = hash_map[ materialHash ].hash + '_' + hash_map[ materialHash ].counter; if ( geometry.geometryGroups[ groupHash ] === undefined ) { geometry.geometryGroups[ groupHash ] = { 'faces3': [], 'faces4': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets }; } } 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 ] ); } }; function addBuffer( objlist, buffer, object ) { objlist.push( { buffer: buffer, object: object, opaque: null, transparent: null } ); }; function addBufferImmediate( objlist, object ) { objlist.push( { object: object, opaque: null, transparent: null } ); }; this.setFaceCulling = function ( cullFace, frontFace ) { if ( cullFace ) { if ( !frontFace || frontFace === "ccw" ) { _gl.frontFace( _gl.CCW ); } else { _gl.frontFace( _gl.CW ); } if( cullFace === "back" ) { _gl.cullFace( _gl.BACK ); } else if( cullFace === "front" ) { _gl.cullFace( _gl.FRONT ); } else { _gl.cullFace( _gl.FRONT_AND_BACK ); } _gl.enable( _gl.CULL_FACE ); } else { _gl.disable( _gl.CULL_FACE ); } }; this.supportsVertexTextures = function () { return _supportsVertexTextures; }; function maxVertexTextures() { return _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ); }; function buildProgram( shaderID, fragmentShader, vertexShader, uniforms, attributes, parameters ) { var p, pl, program, code; var chunks = []; // Generate code if ( shaderID ) { chunks.push( shaderID ); } else { chunks.push( fragmentShader ); chunks.push( vertexShader ); } for ( p in parameters ) { chunks.push( p ); chunks.push( parameters[ p ] ); } code = chunks.join(); // Check if code has been already compiled for ( p = 0, pl = _programs.length; p < pl; p ++ ) { if ( _programs[ p ].code === code ) { // console.log( "Code already compiled." /*: \n\n" + code*/ ); return _programs[ p ].program; } } //console.log( "building new program " ); // program = _gl.createProgram(); var prefix_vertex = [ _supportsVertexTextures ? "#define VERTEX_TEXTURES" : "", _this.gammaInput ? "#define GAMMA_INPUT" : "", _this.gammaOutput ? "#define GAMMA_OUTPUT" : "", _this.physicallyBasedShading ? "#define PHYSICALLY_BASED_SHADING" : "", "#define MAX_DIR_LIGHTS " + parameters.maxDirLights, "#define MAX_POINT_LIGHTS " + parameters.maxPointLights, "#define MAX_SHADOWS " + parameters.maxShadows, "#define MAX_BONES " + parameters.maxBones, parameters.map ? "#define USE_MAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.vertexColors ? "#define USE_COLOR" : "", parameters.skinning ? "#define USE_SKINNING" : "", parameters.morphTargets ? "#define USE_MORPHTARGETS" : "", parameters.perPixel ? "#define PHONG_PER_PIXEL" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapSoft ? "#define SHADOWMAP_SOFT" : "", parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "", "uniform mat4 objectMatrix;", "uniform mat4 modelViewMatrix;", "uniform mat4 projectionMatrix;", "uniform mat4 viewMatrix;", "uniform mat3 normalMatrix;", "uniform vec3 cameraPosition;", "uniform mat4 cameraInverseMatrix;", "attribute vec3 position;", "attribute vec3 normal;", "attribute vec2 uv;", "attribute vec2 uv2;", "#ifdef USE_COLOR", "attribute vec3 color;", "#endif", "#ifdef USE_MORPHTARGETS", "attribute vec3 morphTarget0;", "attribute vec3 morphTarget1;", "attribute vec3 morphTarget2;", "attribute vec3 morphTarget3;", "attribute vec3 morphTarget4;", "attribute vec3 morphTarget5;", "attribute vec3 morphTarget6;", "attribute vec3 morphTarget7;", "#endif", "#ifdef USE_SKINNING", "attribute vec4 skinVertexA;", "attribute vec4 skinVertexB;", "attribute vec4 skinIndex;", "attribute vec4 skinWeight;", "#endif", "" ].join("\n"); var prefix_fragment = [ "#ifdef GL_ES", "precision highp float;", "#endif", "#define MAX_DIR_LIGHTS " + parameters.maxDirLights, "#define MAX_POINT_LIGHTS " + parameters.maxPointLights, "#define MAX_SHADOWS " + parameters.maxShadows, parameters.alphaTest ? "#define ALPHATEST " + parameters.alphaTest: "", _this.gammaInput ? "#define GAMMA_INPUT" : "", _this.gammaOutput ? "#define GAMMA_OUTPUT" : "", _this.physicallyBasedShading ? "#define PHYSICALLY_BASED_SHADING" : "", ( parameters.useFog && parameters.fog ) ? "#define USE_FOG" : "", ( parameters.useFog && parameters.fog instanceof THREE.FogExp2 ) ? "#define FOG_EXP2" : "", parameters.map ? "#define USE_MAP" : "", parameters.envMap ? "#define USE_ENVMAP" : "", parameters.lightMap ? "#define USE_LIGHTMAP" : "", parameters.vertexColors ? "#define USE_COLOR" : "", parameters.metal ? "#define METAL" : "", parameters.perPixel ? "#define PHONG_PER_PIXEL" : "", parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", parameters.shadowMapSoft ? "#define SHADOWMAP_SOFT" : "", parameters.shadowMapSoft ? "#define SHADOWMAP_WIDTH " + parameters.shadowMapWidth.toFixed( 1 ) : "", parameters.shadowMapSoft ? "#define SHADOWMAP_HEIGHT " + parameters.shadowMapHeight.toFixed( 1 ) : "", "uniform mat4 viewMatrix;", "uniform vec3 cameraPosition;", "" ].join("\n"); _gl.attachShader( program, getShader( "fragment", prefix_fragment + fragmentShader ) ); _gl.attachShader( program, getShader( "vertex", prefix_vertex + vertexShader ) ); _gl.linkProgram( program ); if ( !_gl.getProgramParameter( program, _gl.LINK_STATUS ) ) { console.error( "Could not initialise shader\n" + "VALIDATE_STATUS: " + _gl.getProgramParameter( program, _gl.VALIDATE_STATUS ) + ", gl error [" + _gl.getError() + "]" ); } //console.log( prefix_fragment + fragmentShader ); //console.log( prefix_vertex + vertexShader ); program.uniforms = {}; program.attributes = {}; var identifiers, u, a, i; // cache uniform locations identifiers = [ 'viewMatrix', 'modelViewMatrix', 'projectionMatrix', 'normalMatrix', 'objectMatrix', 'cameraPosition', 'cameraInverseMatrix', 'boneGlobalMatrices', 'morphTargetInfluences' ]; for ( u in uniforms ) { identifiers.push( u ); } cacheUniformLocations( program, identifiers ); // cache attributes locations identifiers = [ "position", "normal", "uv", "uv2", "tangent", "color", "skinVertexA", "skinVertexB", "skinIndex", "skinWeight" ]; for ( i = 0; i < parameters.maxMorphTargets; i ++ ) { identifiers.push( "morphTarget" + i ); } for ( a in attributes ) { identifiers.push( a ); } cacheAttributeLocations( program, identifiers ); program.id = _programs.length; _programs.push( { program: program, code: code } ); _this.info.memory.programs = _programs.length; return program; }; function loadUniformsSkinning( uniforms, object ) { _gl.uniformMatrix4fv( uniforms.cameraInverseMatrix, false, _viewMatrixArray ); _gl.uniformMatrix4fv( uniforms.boneGlobalMatrices, false, object.boneMatrices ); }; function loadUniformsMatrices( uniforms, object ) { _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, object._modelViewMatrixArray ); if ( uniforms.normalMatrix ) { _gl.uniformMatrix3fv( uniforms.normalMatrix, false, object._normalMatrixArray ); } }; function loadUniformsGeneric( program, uniforms ) { var uniform, value, type, location, texture, i, il, j, jl, offset; for( j = 0, jl = uniforms.length; j < jl; j ++ ) { location = program.uniforms[ uniforms[ j ][ 1 ] ]; if ( !location ) continue; uniform = uniforms[ j ][ 0 ]; type = uniform.type; value = uniform.value; // single integer if( type === "i" ) { _gl.uniform1i( location, value ); // single float } else if( type === "f" ) { _gl.uniform1f( location, value ); // single THREE.Vector2 } else if( type === "v2" ) { _gl.uniform2f( location, value.x, value.y ); // single THREE.Vector3 } else if( type === "v3" ) { _gl.uniform3f( location, value.x, value.y, value.z ); // single THREE.Vector4 } else if( type === "v4" ) { _gl.uniform4f( location, value.x, value.y, value.z, value.w ); // single THREE.Color } else if( type === "c" ) { _gl.uniform3f( location, value.r, value.g, value.b ); // flat array of floats (JS or typed array) } else if( type === "fv1" ) { _gl.uniform1fv( location, value ); // flat array of floats with 3 x N size (JS or typed array) } else if( type === "fv" ) { _gl.uniform3fv( location, value ); // array of THREE.Vector3 } else if( type === "v3v" ) { if ( ! uniform._array ) { uniform._array = new Float32Array( 3 * value.length ); } for ( i = 0, il = value.length; i < il; i ++ ) { offset = i * 3; uniform._array[ offset ] = value[ i ].x; uniform._array[ offset + 1 ] = value[ i ].y; uniform._array[ offset + 2 ] = value[ i ].z; } _gl.uniform3fv( location, uniform._array ); // single THREE.Matrix4 } else if( type === "m4" ) { if ( ! uniform._array ) { uniform._array = new Float32Array( 16 ); } value.flattenToArray( uniform._array ); _gl.uniformMatrix4fv( location, false, uniform._array ); // array of THREE.Matrix4 } else if( type === "m4v" ) { if ( ! uniform._array ) { uniform._array = new Float32Array( 16 * value.length ); } for ( i = 0, il = value.length; i < il; i ++ ) { value[ i ].flattenToArrayOffset( uniform._array, i * 16 ); } _gl.uniformMatrix4fv( location, false, uniform._array ); // single THREE.Texture (2d or cube) } else if( type === "t" ) { _gl.uniform1i( location, value ); texture = uniform.texture; if ( !texture ) continue; if ( texture.image instanceof Array && texture.image.length === 6 ) { setCubeTexture( texture, value ); } else if ( texture instanceof THREE.WebGLRenderTargetCube ) { setCubeTextureDynamic( texture, value ); } else { setTexture( texture, value ); } // array of THREE.Texture (2d) } else if( type === "tv" ) { if ( ! uniform._array ) { uniform._array = []; for( i = 0, il = uniform.texture.length; i < il; i ++ ) { uniform._array[ i ] = value + i; } } _gl.uniform1iv( location, uniform._array ); for( i = 0, il = uniform.texture.length; i < il; i ++ ) { texture = uniform.texture[ i ]; if ( !texture ) continue; setTexture( texture, uniform._array[ i ] ); } } } }; function setBlending( blending ) { if ( blending !== _oldBlending ) { switch ( blending ) { case THREE.AdditiveBlending: _gl.blendEquation( _gl.FUNC_ADD ); _gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE ); break; case THREE.SubtractiveBlending: // TODO: Find blendFuncSeparate() combination _gl.blendEquation( _gl.FUNC_ADD ); _gl.blendFunc( _gl.ZERO, _gl.ONE_MINUS_SRC_COLOR ); break; case THREE.MultiplyBlending: // TODO: Find blendFuncSeparate() combination _gl.blendEquation( _gl.FUNC_ADD ); _gl.blendFunc( _gl.ZERO, _gl.SRC_COLOR ); break; default: _gl.blendEquationSeparate( _gl.FUNC_ADD, _gl.FUNC_ADD ); _gl.blendFuncSeparate( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA, _gl.ONE, _gl.ONE_MINUS_SRC_ALPHA ); break; } _oldBlending = blending; } }; function setTextureParameters( textureType, texture, image ) { if ( isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ) ) { _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 ) ); _gl.generateMipmap( textureType ); } else { _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) ); _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) ); } }; function setTexture( texture, slot ) { if ( texture.needsUpdate ) { if ( ! texture.__webglInit ) { texture.__webglInit = true; texture.__webglTexture = _gl.createTexture(); _this.info.memory.textures ++; } _gl.activeTexture( _gl.TEXTURE0 + slot ); _gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture ); if ( texture instanceof THREE.DataTexture ) { _gl.texImage2D( _gl.TEXTURE_2D, 0, paramThreeToGL( texture.format ), texture.image.width, texture.image.height, 0, paramThreeToGL( texture.format ), _gl.UNSIGNED_BYTE, texture.image.data ); } else { _gl.texImage2D( _gl.TEXTURE_2D, 0, _gl.RGBA, _gl.RGBA, _gl.UNSIGNED_BYTE, texture.image ); } setTextureParameters( _gl.TEXTURE_2D, texture, texture.image ); texture.needsUpdate = false; } else { _gl.activeTexture( _gl.TEXTURE0 + slot ); _gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture ); } }; function setCubeTexture( texture, slot ) { if ( texture.image.length === 6 ) { if ( texture.needsUpdate ) { if ( ! texture.image.__webglTextureCube ) { texture.image.__webglTextureCube = _gl.createTexture(); } _gl.activeTexture( _gl.TEXTURE0 + slot ); _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube ); for ( var i = 0; i < 6; i ++ ) { _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, _gl.RGBA, _gl.RGBA, _gl.UNSIGNED_BYTE, texture.image[ i ] ); } setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, texture.image[ 0 ] ); texture.needsUpdate = false; } 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 ); }; function setupFrameBuffer( framebuffer, renderTarget, textureTarget ) { _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureTarget, renderTarget.__webglTexture, 0 ); }; function setupRenderBuffer( renderbuffer, renderTarget ) { _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer ); if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height ); _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); /* For some reason this is not working. Defaulting to RGBA4. } else if( ! renderTarget.depthBuffer && renderTarget.stencilBuffer ) { _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.STENCIL_INDEX8, renderTarget.width, renderTarget.height ); _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); */ } else if( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height ); _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); } else { _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height ); } }; function setRenderTarget( renderTarget ) { var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube ); if ( renderTarget && ! renderTarget.__webglFramebuffer ) { if( renderTarget.depthBuffer === undefined ) renderTarget.depthBuffer = true; if( renderTarget.stencilBuffer === undefined ) renderTarget.stencilBuffer = true; renderTarget.__webglTexture = _gl.createTexture(); // Setup texture, create render and frame buffers if ( isCube ) { renderTarget.__webglFramebuffer = []; renderTarget.__webglRenderbuffer = []; _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTarget.__webglTexture ); setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget, renderTarget ); 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, paramThreeToGL( renderTarget.format ), renderTarget.width, renderTarget.height, 0, paramThreeToGL( renderTarget.format ), paramThreeToGL( renderTarget.type ), null ); setupFrameBuffer( renderTarget.__webglFramebuffer[ i ], renderTarget, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i ); setupRenderBuffer( renderTarget.__webglRenderbuffer[ i ], renderTarget ); } } else { renderTarget.__webglFramebuffer = _gl.createFramebuffer(); renderTarget.__webglRenderbuffer = _gl.createRenderbuffer(); _gl.bindTexture( _gl.TEXTURE_2D, renderTarget.__webglTexture ); setTextureParameters( _gl.TEXTURE_2D, renderTarget, renderTarget ); _gl.texImage2D( _gl.TEXTURE_2D, 0, paramThreeToGL( renderTarget.format ), renderTarget.width, renderTarget.height, 0, paramThreeToGL( renderTarget.format ), paramThreeToGL( renderTarget.type ), null ); setupFrameBuffer( renderTarget.__webglFramebuffer, renderTarget, _gl.TEXTURE_2D ); _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderTarget.__webglRenderbuffer ); setupRenderBuffer( renderTarget.__webglRenderbuffer, renderTarget ); } // Release everything if ( isCube ) { _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); } else { _gl.bindTexture( _gl.TEXTURE_2D, null ); } _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); _gl.bindFramebuffer( _gl.FRAMEBUFFER, null); } var framebuffer, width, height, vx, vy; if ( renderTarget ) { if ( isCube ) { framebuffer = renderTarget.__webglFramebuffer[ renderTarget.activeCubeFace ]; } else { framebuffer = renderTarget.__webglFramebuffer; } width = renderTarget.width; height = renderTarget.height; vx = 0; vy = 0; } else { framebuffer = null; width = _viewportWidth; height = _viewportHeight; vx = _viewportX; vy = _viewportY; } if ( framebuffer !== _currentFramebuffer ) { _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); _gl.viewport( vx, vy, width, height ); _currentFramebuffer = framebuffer; } }; function updateRenderTargetMipmap( renderTarget ) { if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) { _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTarget.__webglTexture ); _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); } else { _gl.bindTexture( _gl.TEXTURE_2D, renderTarget.__webglTexture ); _gl.generateMipmap( _gl.TEXTURE_2D ); _gl.bindTexture( _gl.TEXTURE_2D, null ); } }; function cacheUniformLocations( program, identifiers ) { var i, l, id; for( i = 0, l = identifiers.length; i < l; i++ ) { id = identifiers[ i ]; program.uniforms[ id ] = _gl.getUniformLocation( program, id ); } }; function cacheAttributeLocations( program, identifiers ) { var i, l, id; for( i = 0, l = identifiers.length; i < l; i++ ) { id = identifiers[ i ]; program.attributes[ id ] = _gl.getAttribLocation( program, id ); } }; function getShader( type, string ) { var shader; if ( type === "fragment" ) { shader = _gl.createShader( _gl.FRAGMENT_SHADER ); } else if ( type === "vertex" ) { shader = _gl.createShader( _gl.VERTEX_SHADER ); } _gl.shaderSource( shader, string ); _gl.compileShader( shader ); if ( !_gl.getShaderParameter( shader, _gl.COMPILE_STATUS ) ) { console.error( _gl.getShaderInfoLog( shader ) ); console.error( string ); return null; } return shader; }; // fallback filters for non-power-of-2 textures function filterFallback( f ) { switch ( f ) { case THREE.NearestFilter: case THREE.NearestMipMapNearestFilter: case THREE.NearestMipMapLinearFilter: return _gl.NEAREST; break; case THREE.LinearFilter: case THREE.LinearMipMapNearestFilter: case THREE.LinearMipMapLinearFilter: default: return _gl.LINEAR; break; } }; function paramThreeToGL( p ) { switch ( p ) { case THREE.RepeatWrapping: return _gl.REPEAT; break; case THREE.ClampToEdgeWrapping: return _gl.CLAMP_TO_EDGE; break; case THREE.MirroredRepeatWrapping: return _gl.MIRRORED_REPEAT; break; case THREE.NearestFilter: return _gl.NEAREST; break; case THREE.NearestMipMapNearestFilter: return _gl.NEAREST_MIPMAP_NEAREST; break; case THREE.NearestMipMapLinearFilter: return _gl.NEAREST_MIPMAP_LINEAR; break; case THREE.LinearFilter: return _gl.LINEAR; break; case THREE.LinearMipMapNearestFilter: return _gl.LINEAR_MIPMAP_NEAREST; break; case THREE.LinearMipMapLinearFilter: return _gl.LINEAR_MIPMAP_LINEAR; break; case THREE.ByteType: return _gl.BYTE; break; case THREE.UnsignedByteType: return _gl.UNSIGNED_BYTE; break; case THREE.ShortType: return _gl.SHORT; break; case THREE.UnsignedShortType: return _gl.UNSIGNED_SHORT; break; case THREE.IntType: return _gl.INT; break; case THREE.UnsignedShortType: return _gl.UNSIGNED_INT; break; case THREE.FloatType: return _gl.FLOAT; break; case THREE.AlphaFormat: return _gl.ALPHA; break; case THREE.RGBFormat: return _gl.RGB; break; case THREE.RGBAFormat: return _gl.RGBA; break; case THREE.LuminanceFormat: return _gl.LUMINANCE; break; case THREE.LuminanceAlphaFormat: return _gl.LUMINANCE_ALPHA; break; } return 0; }; function isPowerOfTwo( value ) { return ( value & ( value - 1 ) ) === 0; }; function materialNeedsSmoothNormals( material ) { return material && material.shading !== undefined && material.shading === THREE.SmoothShading; }; function bufferGuessVertexColorType( material ) { if ( material.vertexColors ) { return material.vertexColors; } return false; }; function bufferGuessNormalType( material ) { // only MeshBasicMaterial and MeshDepthMaterial don't need normals if ( ( material instanceof THREE.MeshBasicMaterial && !material.envMap ) || material instanceof THREE.MeshDepthMaterial ) { return false; } if ( materialNeedsSmoothNormals( material ) ) { return THREE.SmoothShading; } else { return THREE.FlatShading; } }; function bufferGuessUVType( material ) { // material must use some texture to require uvs if ( material.map || material.lightMap || material instanceof THREE.ShaderMaterial ) { return true; } return false; }; function allocateBones( object ) { // default for when object is not specified // ( for example when prebuilding shader // to be used with multiple objects ) // // - leave some extra space for other uniforms // - limit here is ANGLE's 254 max uniform vectors // (up to 54 should be safe) var maxBones = 50; if ( object !== undefined && object instanceof THREE.SkinnedMesh ) { maxBones = object.bones.length; } return maxBones; }; function allocateLights( lights ) { var l, ll, light, dirLights, pointLights, maxDirLights, maxPointLights; dirLights = pointLights = maxDirLights = maxPointLights = 0; for ( l = 0, ll = lights.length; l < ll; l++ ) { light = lights[ l ]; if ( light instanceof THREE.SpotLight ) dirLights ++; // hack, not a proper spotlight if ( light instanceof THREE.DirectionalLight ) dirLights ++; if ( light instanceof THREE.PointLight ) pointLights ++; } if ( ( pointLights + dirLights ) <= _maxLights ) { maxDirLights = dirLights; maxPointLights = pointLights; } else { maxDirLights = Math.ceil( _maxLights * dirLights / ( pointLights + dirLights ) ); maxPointLights = _maxLights - maxDirLights; } return { 'directional' : maxDirLights, 'point' : maxPointLights }; }; function allocateShadows( lights ) { var l, ll, light, maxShadows = 0; for ( l = 0, ll = lights.length; l < ll; l++ ) { light = lights[ l ]; if ( light instanceof THREE.SpotLight && light.castShadow ) maxShadows ++; } return maxShadows; }; /* DEBUG function getGLParams() { var params = { 'MAX_VARYING_VECTORS': _gl.getParameter( _gl.MAX_VARYING_VECTORS ), 'MAX_VERTEX_ATTRIBS': _gl.getParameter( _gl.MAX_VERTEX_ATTRIBS ), 'MAX_TEXTURE_IMAGE_UNITS': _gl.getParameter( _gl.MAX_TEXTURE_IMAGE_UNITS ), 'MAX_VERTEX_TEXTURE_IMAGE_UNITS': _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ), 'MAX_COMBINED_TEXTURE_IMAGE_UNITS' : _gl.getParameter( _gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS ), 'MAX_VERTEX_UNIFORM_VECTORS': _gl.getParameter( _gl.MAX_VERTEX_UNIFORM_VECTORS ), 'MAX_FRAGMENT_UNIFORM_VECTORS': _gl.getParameter( _gl.MAX_FRAGMENT_UNIFORM_VECTORS ) } return params; }; function dumpObject( obj ) { var p, str = ""; for ( p in obj ) { str += p + ": " + obj[p] + "\n"; } return str; } */ };