/** * @author mikael emtinger / http://gomo.se/ * @author alteredq / http://alteredqualia.com/ */ THREE.SpritePlugin = function ( ) { var _gl, _renderer, _sprite = {}; this.init = function ( renderer ) { _gl = renderer.context; _renderer = renderer; _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 = createProgram( THREE.ShaderSprite[ "sprite" ] ); _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" ); _sprite.attributesEnabled = false; }; this.render = function ( scene, camera, viewportWidth, viewportHeight ) { var sprites = scene.__webglSprites, nSprites = sprites.length; if ( ! nSprites ) return; var attributes = _sprite.attributes, uniforms = _sprite.uniforms; var invAspect = viewportHeight / viewportWidth; var halfViewportWidth = viewportWidth * 0.5, halfViewportHeight = viewportHeight * 0.5; var mergeWith3D = true; // setup gl _gl.useProgram( _sprite.program ); if ( ! _sprite.attributesEnabled ) { _gl.enableVertexAttribArray( attributes.position ); _gl.enableVertexAttribArray( attributes.uv ); _sprite.attributesEnabled = 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, camera._projectionMatrixArray ); _gl.activeTexture( _gl.TEXTURE0 ); _gl.uniform1i( uniforms.map, 0 ); // update positions and sort var i, sprite, screenPosition, size, scale = []; for( i = 0; i < nSprites; i ++ ) { sprite = sprites[ i ]; if ( ! sprite.visible || sprite.opacity === 0 ) continue; if( ! sprite.useScreenCoordinates ) { sprite._modelViewMatrix.multiplyToArray( camera.matrixWorldInverse, sprite.matrixWorld, sprite._modelViewMatrixArray ); sprite.z = - sprite._modelViewMatrix.n34; } else { sprite.z = - sprite.position.z; } } sprites.sort( painterSort ); // render all sprites for( i = 0; i < nSprites; i ++ ) { sprite = sprites[ i ]; if ( ! sprite.visible || sprite.opacity === 0 ) continue; if ( sprite.map && sprite.map.image && sprite.map.image.width ) { if ( sprite.useScreenCoordinates ) { _gl.uniform1i( uniforms.useScreenCoordinates, 1 ); _gl.uniform3f( uniforms.screenPosition, ( sprite.position.x - halfViewportWidth ) / halfViewportWidth, ( halfViewportHeight - sprite.position.y ) / halfViewportHeight, Math.max( 0, Math.min( 1, sprite.position.z ) ) ); } else { _gl.uniform1i( uniforms.useScreenCoordinates, 0 ); _gl.uniform1i( uniforms.affectedByDistance, sprite.affectedByDistance ? 1 : 0 ); _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite._modelViewMatrixArray ); } size = sprite.map.image.width / ( sprite.scaleByViewport ? viewportHeight : 1 ); scale[ 0 ] = size * invAspect * sprite.scale.x; scale[ 1 ] = size * sprite.scale.y; _gl.uniform2f( uniforms.uvScale, sprite.uvScale.x, sprite.uvScale.y ); _gl.uniform2f( uniforms.uvOffset, sprite.uvOffset.x, sprite.uvOffset.y ); _gl.uniform2f( uniforms.alignment, sprite.alignment.x, sprite.alignment.y ); _gl.uniform1f( uniforms.opacity, sprite.opacity ); _gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b ); _gl.uniform1f( uniforms.rotation, sprite.rotation ); _gl.uniform2fv( uniforms.scale, scale ); if ( sprite.mergeWith3D && !mergeWith3D ) { _gl.enable( _gl.DEPTH_TEST ); mergeWith3D = true; } else if ( ! sprite.mergeWith3D && mergeWith3D ) { _gl.disable( _gl.DEPTH_TEST ); mergeWith3D = false; } _renderer.setBlending( sprite.blending ); _renderer.setTexture( sprite.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( true ); }; function createProgram ( shader ) { var program = _gl.createProgram(); var fragmentShader = _gl.createShader( _gl.FRAGMENT_SHADER ); var vertexShader = _gl.createShader( _gl.VERTEX_SHADER ); _gl.shaderSource( fragmentShader, shader.fragmentShader ); _gl.shaderSource( vertexShader, shader.vertexShader ); _gl.compileShader( fragmentShader ); _gl.compileShader( vertexShader ); _gl.attachShader( program, fragmentShader ); _gl.attachShader( program, vertexShader ); _gl.linkProgram( program ); return program; }; function painterSort ( a, b ) { return b.z - a.z; }; };