├── README.md ├── index.html └── js ├── 3d-lines-animation.js ├── canvas-renderer.js ├── color.js ├── projector.js └── three.min.js /README.md: -------------------------------------------------------------------------------- 1 | 3D lines animation with three.js 2 | ======================================== 3 | ...and an animated background color 4 | 5 | 6 | Tags 7 | ----------- 8 | HTML5, javascript, canvas, three.js, animation, colors. 9 | 10 | 11 | What is? 12 | ----------- 13 | 14 | I took a prebuild three.js [example](http://threejs.org/examples/#canvas_lines) and put a fancy animated background color, adjusted some parameters like number of lines, perspective and colors. Now is ready to use for everyone. 15 | 16 | Online demo 17 | ----------- 18 | [Check out the online demo](http://joanclaret.github.io/html5-canvas-animation/) 19 | 20 | 21 | Preview 22 | ----------- 23 | 24 | ![html5 canvas animation preview](http://joanclaret.github.io/html5-canvas-animation/preview.png) 25 | 26 | 27 | 28 | How it works? 29 | ----------- 30 | 31 | ### Javascript initialization 32 | 33 | ```html 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | ``` 48 | 49 | ### Layout 50 | 51 | ```html 52 |
53 |
54 |

Hello world

55 |
56 |
57 |
58 | ``` 59 | 60 | ### Options 61 | 62 | * The script is fully configurable (colors, lines, opacities, perspectives...) but you'll need to dive in to the code to adjust them. Download the files and start checking out the file 3d-lines-animation.js 63 | 64 | ### Follow the repository 65 | ★ Star and watch [this repo](https://github.com/JoanClaret/html5-canvas-animation) in order to stay updated with news about this plugin 66 | 67 | 68 | License 69 | ------- 70 | 71 | The MIT License (MIT) 72 | 73 | Copyright (c) 2015 Joan Claret 74 | 75 | Permission is hereby granted, free of charge, to any person obtaining a copy 76 | of this software and associated documentation files (the "Software"), to deal 77 | in the Software without restriction, including without limitation the rights 78 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 79 | copies of the Software, and to permit persons to whom the Software is 80 | furnished to do so, subject to the following conditions: 81 | 82 | The above copyright notice and this permission notice shall be included in 83 | all copies or substantial portions of the Software. 84 | 85 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 86 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 87 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 88 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 89 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 90 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 91 | THE SOFTWARE. 92 | 93 | Other useful plugins 94 | ---------------------- 95 | * [Maximum Characters limit warning](https://github.com/JoanClaret/max-char-limit-warning): Get a warning when the max char limit has been exceeded with a jQuery plugin 96 | * [jcSlider](http://joanclaret.github.io/jcSlider): A responsive slider jQuery plugin with CSS animations 97 | * [slide and swipe menu](http://joanclaret.github.io/slide-and-swipe-menu): A sliding swipe menu that works with touchSwipe library. 98 | * [jquery dynamic max height](http://joanclaret.github.io/jquery-dynamic-max-height) : Dynamic max height plugin for jQuery with CSS animation 99 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 3D lines animation with three.js 6 | 7 | 8 | 44 | 45 | 46 |
47 |
48 |

3D lines animation with three.js

49 |
50 |
51 | 52 |
53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /js/3d-lines-animation.js: -------------------------------------------------------------------------------- 1 | var mouseX = 0, mouseY = 0, 2 | 3 | windowHalfX = window.innerWidth / 2, 4 | windowHalfY = window.innerHeight / 2, 5 | 6 | SEPARATION = 200, 7 | AMOUNTX = 1, 8 | AMOUNTY = 1, 9 | 10 | camera, scene, renderer; 11 | 12 | init(); 13 | animate(); 14 | 15 | 16 | 17 | function init() { 18 | 19 | 20 | /* 21 | * Define variables 22 | */ 23 | var container, separation = 1000, amountX = 50, amountY = 50, color = 0xffffff, 24 | particles, particle; 25 | 26 | container = document.getElementById("canvas"); 27 | 28 | 29 | camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 ); 30 | camera.position.z = 100; 31 | 32 | scene = new THREE.Scene(); 33 | 34 | renderer = new THREE.CanvasRenderer({ alpha: true }); 35 | renderer.setPixelRatio( window.devicePixelRatio ); 36 | renderer.setClearColor( 0x000000, 0 ); // canvas background color 37 | renderer.setSize( window.innerWidth, window.innerHeight ); 38 | container.appendChild( renderer.domElement ); 39 | 40 | 41 | 42 | var PI2 = Math.PI * 2; 43 | var material = new THREE.SpriteCanvasMaterial( { 44 | 45 | color: color, 46 | opacity: 0.5, 47 | program: function ( context ) { 48 | 49 | context.beginPath(); 50 | context.arc( 0, 0, 0.5, 0, PI2, true ); 51 | context.fill(); 52 | 53 | } 54 | 55 | } ); 56 | 57 | var geometry = new THREE.Geometry(); 58 | 59 | /* 60 | * Number of particles 61 | */ 62 | for ( var i = 0; i < 150; i ++ ) { 63 | 64 | particle = new THREE.Sprite( material ); 65 | particle.position.x = Math.random() * 2 - 1; 66 | particle.position.y = Math.random() * 2 - 1; 67 | particle.position.z = Math.random() * 2 - 1; 68 | particle.position.normalize(); 69 | particle.position.multiplyScalar( Math.random() * 10 + 600 ); 70 | particle.scale.x = particle.scale.y = 5; 71 | 72 | scene.add( particle ); 73 | 74 | geometry.vertices.push( particle.position ); 75 | 76 | } 77 | 78 | /* 79 | * Lines 80 | */ 81 | 82 | var line = new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: color, opacity: 0.2 } ) ); 83 | scene.add( line ); 84 | 85 | document.addEventListener( 'mousemove', onDocumentMouseMove, false ); 86 | document.addEventListener( 'touchstart', onDocumentTouchStart, false ); 87 | document.addEventListener( 'touchmove', onDocumentTouchMove, false ); 88 | 89 | // 90 | 91 | window.addEventListener( 'resize', onWindowResize, false ); 92 | 93 | } 94 | 95 | function onWindowResize() { 96 | 97 | windowHalfX = window.innerWidth / 2; 98 | windowHalfY = window.innerHeight / 2; 99 | 100 | camera.aspect = window.innerWidth / window.innerHeight; 101 | camera.updateProjectionMatrix(); 102 | 103 | renderer.setSize( window.innerWidth, window.innerHeight ); 104 | 105 | } 106 | 107 | // 108 | 109 | function onDocumentMouseMove(event) { 110 | 111 | mouseX = (event.clientX - windowHalfX) * 0.05; 112 | mouseY = (event.clientY - windowHalfY) * 0.2; 113 | 114 | } 115 | 116 | function onDocumentTouchStart( event ) { 117 | 118 | if ( event.touches.length > 1 ) { 119 | 120 | event.preventDefault(); 121 | 122 | mouseX = (event.touches[ 0 ].pageX - windowHalfX) * 0.7; 123 | mouseY = (event.touches[ 0 ].pageY - windowHalfY) * 0.7; 124 | 125 | } 126 | 127 | } 128 | 129 | function onDocumentTouchMove( event ) { 130 | 131 | if ( event.touches.length == 1 ) { 132 | 133 | event.preventDefault(); 134 | 135 | mouseX = event.touches[ 0 ].pageX - windowHalfX; 136 | mouseY = event.touches[ 0 ].pageY - windowHalfY; 137 | 138 | } 139 | 140 | } 141 | 142 | // 143 | 144 | function animate() { 145 | 146 | requestAnimationFrame( animate ); 147 | 148 | render(); 149 | 150 | } 151 | 152 | function render() { 153 | 154 | camera.position.x += ( mouseX - camera.position.x ) * 0.1; 155 | camera.position.y += ( - mouseY + 200 - camera.position.y ) * 0.05; 156 | camera.lookAt( scene.position ); 157 | 158 | renderer.render( scene, camera ); 159 | 160 | } -------------------------------------------------------------------------------- /js/canvas-renderer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | */ 4 | 5 | THREE.SpriteCanvasMaterial = function ( parameters ) { 6 | 7 | THREE.Material.call( this ); 8 | 9 | this.type = 'SpriteCanvasMaterial'; 10 | 11 | this.color = new THREE.Color( 0xffffff ); 12 | this.program = function ( context, color ) {}; 13 | 14 | this.setValues( parameters ); 15 | 16 | }; 17 | 18 | THREE.SpriteCanvasMaterial.prototype = Object.create( THREE.Material.prototype ); 19 | THREE.SpriteCanvasMaterial.prototype.constructor = THREE.SpriteCanvasMaterial; 20 | 21 | THREE.SpriteCanvasMaterial.prototype.clone = function () { 22 | 23 | var material = new THREE.SpriteCanvasMaterial(); 24 | 25 | THREE.Material.prototype.clone.call( this, material ); 26 | 27 | material.color.copy( this.color ); 28 | material.program = this.program; 29 | 30 | return material; 31 | 32 | }; 33 | 34 | // 35 | 36 | THREE.CanvasRenderer = function ( parameters ) { 37 | 38 | console.log( 'THREE.CanvasRenderer', THREE.REVISION ); 39 | 40 | var smoothstep = THREE.Math.smoothstep; 41 | 42 | parameters = parameters || {}; 43 | 44 | var _this = this, 45 | _renderData, _elements, _lights, 46 | _projector = new THREE.Projector(), 47 | 48 | _canvas = parameters.canvas !== undefined 49 | ? parameters.canvas 50 | : document.createElement( 'canvas' ), 51 | 52 | _canvasWidth = _canvas.width, 53 | _canvasHeight = _canvas.height, 54 | _canvasWidthHalf = Math.floor( _canvasWidth / 2 ), 55 | _canvasHeightHalf = Math.floor( _canvasHeight / 2 ), 56 | 57 | _viewportX = 0, 58 | _viewportY = 0, 59 | _viewportWidth = _canvasWidth, 60 | _viewportHeight = _canvasHeight, 61 | 62 | pixelRatio = 1, 63 | 64 | _context = _canvas.getContext( '2d', { 65 | alpha: parameters.alpha === true 66 | } ), 67 | 68 | _clearColor = new THREE.Color( 0x000000 ), 69 | _clearAlpha = parameters.alpha === true ? 0 : 1, 70 | 71 | _contextGlobalAlpha = 1, 72 | _contextGlobalCompositeOperation = 0, 73 | _contextStrokeStyle = null, 74 | _contextFillStyle = null, 75 | _contextLineWidth = null, 76 | _contextLineCap = null, 77 | _contextLineJoin = null, 78 | _contextLineDash = [], 79 | 80 | _camera, 81 | 82 | _v1, _v2, _v3, _v4, 83 | _v5 = new THREE.RenderableVertex(), 84 | _v6 = new THREE.RenderableVertex(), 85 | 86 | _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, 87 | _v4x, _v4y, _v5x, _v5y, _v6x, _v6y, 88 | 89 | _color = new THREE.Color(), 90 | _color1 = new THREE.Color(), 91 | _color2 = new THREE.Color(), 92 | _color3 = new THREE.Color(), 93 | _color4 = new THREE.Color(), 94 | 95 | _diffuseColor = new THREE.Color(), 96 | _emissiveColor = new THREE.Color(), 97 | 98 | _lightColor = new THREE.Color(), 99 | 100 | _patterns = {}, 101 | 102 | _image, _uvs, 103 | _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, 104 | 105 | _clipBox = new THREE.Box2(), 106 | _clearBox = new THREE.Box2(), 107 | _elemBox = new THREE.Box2(), 108 | 109 | _ambientLight = new THREE.Color(), 110 | _directionalLights = new THREE.Color(), 111 | _pointLights = new THREE.Color(), 112 | 113 | _vector3 = new THREE.Vector3(), // Needed for PointLight 114 | _centroid = new THREE.Vector3(), 115 | _normal = new THREE.Vector3(), 116 | _normalViewMatrix = new THREE.Matrix3(); 117 | 118 | // dash+gap fallbacks for Firefox and everything else 119 | 120 | if ( _context.setLineDash === undefined ) { 121 | 122 | _context.setLineDash = function () {} 123 | 124 | } 125 | 126 | this.domElement = _canvas; 127 | 128 | this.autoClear = true; 129 | this.sortObjects = true; 130 | this.sortElements = true; 131 | 132 | this.info = { 133 | 134 | render: { 135 | 136 | vertices: 0, 137 | faces: 0 138 | 139 | } 140 | 141 | } 142 | 143 | // WebGLRenderer compatibility 144 | 145 | this.supportsVertexTextures = function () {}; 146 | this.setFaceCulling = function () {}; 147 | 148 | // 149 | 150 | this.getPixelRatio = function () { 151 | 152 | return pixelRatio; 153 | 154 | }; 155 | 156 | this.setPixelRatio = function ( value ) { 157 | 158 | pixelRatio = value; 159 | 160 | }; 161 | 162 | this.setSize = function ( width, height, updateStyle ) { 163 | 164 | _canvasWidth = width * pixelRatio; 165 | _canvasHeight = height * pixelRatio; 166 | 167 | _canvas.width = _canvasWidth; 168 | _canvas.height = _canvasHeight; 169 | 170 | _canvasWidthHalf = Math.floor( _canvasWidth / 2 ); 171 | _canvasHeightHalf = Math.floor( _canvasHeight / 2 ); 172 | 173 | if ( updateStyle !== false ) { 174 | 175 | _canvas.style.width = width + 'px'; 176 | _canvas.style.height = height + 'px'; 177 | 178 | } 179 | 180 | _clipBox.min.set( -_canvasWidthHalf, -_canvasHeightHalf ), 181 | _clipBox.max.set( _canvasWidthHalf, _canvasHeightHalf ); 182 | 183 | _clearBox.min.set( - _canvasWidthHalf, - _canvasHeightHalf ); 184 | _clearBox.max.set( _canvasWidthHalf, _canvasHeightHalf ); 185 | 186 | _contextGlobalAlpha = 1; 187 | _contextGlobalCompositeOperation = 0; 188 | _contextStrokeStyle = null; 189 | _contextFillStyle = null; 190 | _contextLineWidth = null; 191 | _contextLineCap = null; 192 | _contextLineJoin = null; 193 | 194 | this.setViewport( 0, 0, width, height ); 195 | 196 | }; 197 | 198 | this.setViewport = function ( x, y, width, height ) { 199 | 200 | _viewportX = x * pixelRatio; 201 | _viewportY = y * pixelRatio; 202 | 203 | _viewportWidth = width * pixelRatio; 204 | _viewportHeight = height * pixelRatio; 205 | 206 | }; 207 | 208 | this.setScissor = function () {}; 209 | this.enableScissorTest = function () {}; 210 | 211 | this.setClearColor = function ( color, alpha ) { 212 | 213 | _clearColor.set( color ); 214 | _clearAlpha = alpha !== undefined ? alpha : 1; 215 | 216 | _clearBox.min.set( - _canvasWidthHalf, - _canvasHeightHalf ); 217 | _clearBox.max.set( _canvasWidthHalf, _canvasHeightHalf ); 218 | 219 | }; 220 | 221 | this.setClearColorHex = function ( hex, alpha ) { 222 | 223 | console.warn( 'THREE.CanvasRenderer: .setClearColorHex() is being removed. Use .setClearColor() instead.' ); 224 | this.setClearColor( hex, alpha ); 225 | 226 | }; 227 | 228 | this.getClearColor = function () { 229 | 230 | return _clearColor; 231 | 232 | }; 233 | 234 | this.getClearAlpha = function () { 235 | 236 | return _clearAlpha; 237 | 238 | }; 239 | 240 | this.getMaxAnisotropy = function () { 241 | 242 | return 0; 243 | 244 | }; 245 | 246 | this.clear = function () { 247 | 248 | if ( _clearBox.empty() === false ) { 249 | 250 | _clearBox.intersect( _clipBox ); 251 | _clearBox.expandByScalar( 2 ); 252 | 253 | _clearBox.min.x = _clearBox.min.x + _canvasWidthHalf; 254 | _clearBox.min.y = - _clearBox.min.y + _canvasHeightHalf; // higher y value ! 255 | _clearBox.max.x = _clearBox.max.x + _canvasWidthHalf; 256 | _clearBox.max.y = - _clearBox.max.y + _canvasHeightHalf; // lower y value ! 257 | 258 | if ( _clearAlpha < 1 ) { 259 | 260 | _context.clearRect( 261 | _clearBox.min.x | 0, 262 | _clearBox.max.y | 0, 263 | ( _clearBox.max.x - _clearBox.min.x ) | 0, 264 | ( _clearBox.min.y - _clearBox.max.y ) | 0 265 | ); 266 | 267 | } 268 | 269 | if ( _clearAlpha > 0 ) { 270 | 271 | setBlending( THREE.NormalBlending ); 272 | setOpacity( 1 ); 273 | 274 | setFillStyle( 'rgba(' + Math.floor( _clearColor.r * 255 ) + ',' + Math.floor( _clearColor.g * 255 ) + ',' + Math.floor( _clearColor.b * 255 ) + ',' + _clearAlpha + ')' ); 275 | 276 | _context.fillRect( 277 | _clearBox.min.x | 0, 278 | _clearBox.max.y | 0, 279 | ( _clearBox.max.x - _clearBox.min.x ) | 0, 280 | ( _clearBox.min.y - _clearBox.max.y ) | 0 281 | ); 282 | 283 | } 284 | 285 | _clearBox.makeEmpty(); 286 | 287 | } 288 | 289 | }; 290 | 291 | // compatibility 292 | 293 | this.clearColor = function () {}; 294 | this.clearDepth = function () {}; 295 | this.clearStencil = function () {}; 296 | 297 | this.render = function ( scene, camera ) { 298 | 299 | if ( camera instanceof THREE.Camera === false ) { 300 | 301 | console.error( 'THREE.CanvasRenderer.render: camera is not an instance of THREE.Camera.' ); 302 | return; 303 | 304 | } 305 | 306 | if ( this.autoClear === true ) this.clear(); 307 | 308 | _this.info.render.vertices = 0; 309 | _this.info.render.faces = 0; 310 | 311 | _context.setTransform( _viewportWidth / _canvasWidth, 0, 0, - _viewportHeight / _canvasHeight, _viewportX, _canvasHeight - _viewportY ); 312 | _context.translate( _canvasWidthHalf, _canvasHeightHalf ); 313 | 314 | _renderData = _projector.projectScene( scene, camera, this.sortObjects, this.sortElements ); 315 | _elements = _renderData.elements; 316 | _lights = _renderData.lights; 317 | _camera = camera; 318 | 319 | _normalViewMatrix.getNormalMatrix( camera.matrixWorldInverse ); 320 | 321 | /* DEBUG 322 | setFillStyle( 'rgba( 0, 255, 255, 0.5 )' ); 323 | _context.fillRect( _clipBox.min.x, _clipBox.min.y, _clipBox.max.x - _clipBox.min.x, _clipBox.max.y - _clipBox.min.y ); 324 | */ 325 | 326 | calculateLights(); 327 | 328 | for ( var e = 0, el = _elements.length; e < el; e ++ ) { 329 | 330 | var element = _elements[ e ]; 331 | 332 | var material = element.material; 333 | 334 | if ( material === undefined || material.opacity === 0 ) continue; 335 | 336 | _elemBox.makeEmpty(); 337 | 338 | if ( element instanceof THREE.RenderableSprite ) { 339 | 340 | _v1 = element; 341 | _v1.x *= _canvasWidthHalf; _v1.y *= _canvasHeightHalf; 342 | 343 | renderSprite( _v1, element, material ); 344 | 345 | } else if ( element instanceof THREE.RenderableLine ) { 346 | 347 | _v1 = element.v1; _v2 = element.v2; 348 | 349 | _v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf; 350 | _v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf; 351 | 352 | _elemBox.setFromPoints( [ 353 | _v1.positionScreen, 354 | _v2.positionScreen 355 | ] ); 356 | 357 | if ( _clipBox.isIntersectionBox( _elemBox ) === true ) { 358 | 359 | renderLine( _v1, _v2, element, material ); 360 | 361 | } 362 | 363 | } else if ( element instanceof THREE.RenderableFace ) { 364 | 365 | _v1 = element.v1; _v2 = element.v2; _v3 = element.v3; 366 | 367 | if ( _v1.positionScreen.z < - 1 || _v1.positionScreen.z > 1 ) continue; 368 | if ( _v2.positionScreen.z < - 1 || _v2.positionScreen.z > 1 ) continue; 369 | if ( _v3.positionScreen.z < - 1 || _v3.positionScreen.z > 1 ) continue; 370 | 371 | _v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf; 372 | _v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf; 373 | _v3.positionScreen.x *= _canvasWidthHalf; _v3.positionScreen.y *= _canvasHeightHalf; 374 | 375 | if ( material.overdraw > 0 ) { 376 | 377 | expand( _v1.positionScreen, _v2.positionScreen, material.overdraw ); 378 | expand( _v2.positionScreen, _v3.positionScreen, material.overdraw ); 379 | expand( _v3.positionScreen, _v1.positionScreen, material.overdraw ); 380 | 381 | } 382 | 383 | _elemBox.setFromPoints( [ 384 | _v1.positionScreen, 385 | _v2.positionScreen, 386 | _v3.positionScreen 387 | ] ); 388 | 389 | if ( _clipBox.isIntersectionBox( _elemBox ) === true ) { 390 | 391 | renderFace3( _v1, _v2, _v3, 0, 1, 2, element, material ); 392 | 393 | } 394 | 395 | } 396 | 397 | /* DEBUG 398 | setLineWidth( 1 ); 399 | setStrokeStyle( 'rgba( 0, 255, 0, 0.5 )' ); 400 | _context.strokeRect( _elemBox.min.x, _elemBox.min.y, _elemBox.max.x - _elemBox.min.x, _elemBox.max.y - _elemBox.min.y ); 401 | */ 402 | 403 | _clearBox.union( _elemBox ); 404 | 405 | } 406 | 407 | /* DEBUG 408 | setLineWidth( 1 ); 409 | setStrokeStyle( 'rgba( 255, 0, 0, 0.5 )' ); 410 | _context.strokeRect( _clearBox.min.x, _clearBox.min.y, _clearBox.max.x - _clearBox.min.x, _clearBox.max.y - _clearBox.min.y ); 411 | */ 412 | 413 | _context.setTransform( 1, 0, 0, 1, 0, 0 ); 414 | 415 | }; 416 | 417 | // 418 | 419 | function calculateLights() { 420 | 421 | _ambientLight.setRGB( 0, 0, 0 ); 422 | _directionalLights.setRGB( 0, 0, 0 ); 423 | _pointLights.setRGB( 0, 0, 0 ); 424 | 425 | for ( var l = 0, ll = _lights.length; l < ll; l ++ ) { 426 | 427 | var light = _lights[ l ]; 428 | var lightColor = light.color; 429 | 430 | if ( light instanceof THREE.AmbientLight ) { 431 | 432 | _ambientLight.add( lightColor ); 433 | 434 | } else if ( light instanceof THREE.DirectionalLight ) { 435 | 436 | // for sprites 437 | 438 | _directionalLights.add( lightColor ); 439 | 440 | } else if ( light instanceof THREE.PointLight ) { 441 | 442 | // for sprites 443 | 444 | _pointLights.add( lightColor ); 445 | 446 | } 447 | 448 | } 449 | 450 | } 451 | 452 | function calculateLight( position, normal, color ) { 453 | 454 | for ( var l = 0, ll = _lights.length; l < ll; l ++ ) { 455 | 456 | var light = _lights[ l ]; 457 | 458 | _lightColor.copy( light.color ); 459 | 460 | if ( light instanceof THREE.DirectionalLight ) { 461 | 462 | var lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld ).normalize(); 463 | 464 | var amount = normal.dot( lightPosition ); 465 | 466 | if ( amount <= 0 ) continue; 467 | 468 | amount *= light.intensity; 469 | 470 | color.add( _lightColor.multiplyScalar( amount ) ); 471 | 472 | } else if ( light instanceof THREE.PointLight ) { 473 | 474 | var lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld ); 475 | 476 | var amount = normal.dot( _vector3.subVectors( lightPosition, position ).normalize() ); 477 | 478 | if ( amount <= 0 ) continue; 479 | 480 | amount *= light.distance == 0 ? 1 : 1 - Math.min( position.distanceTo( lightPosition ) / light.distance, 1 ); 481 | 482 | if ( amount == 0 ) continue; 483 | 484 | amount *= light.intensity; 485 | 486 | color.add( _lightColor.multiplyScalar( amount ) ); 487 | 488 | } 489 | 490 | } 491 | 492 | } 493 | 494 | function renderSprite( v1, element, material ) { 495 | 496 | setOpacity( material.opacity ); 497 | setBlending( material.blending ); 498 | 499 | var scaleX = element.scale.x * _canvasWidthHalf; 500 | var scaleY = element.scale.y * _canvasHeightHalf; 501 | 502 | var dist = 0.5 * Math.sqrt( scaleX * scaleX + scaleY * scaleY ); // allow for rotated sprite 503 | _elemBox.min.set( v1.x - dist, v1.y - dist ); 504 | _elemBox.max.set( v1.x + dist, v1.y + dist ); 505 | 506 | if ( material instanceof THREE.SpriteMaterial ) { 507 | 508 | var texture = material.map; 509 | 510 | if ( texture !== null && texture.image !== undefined ) { 511 | 512 | if ( texture.hasEventListener( 'update', onTextureUpdate ) === false ) { 513 | 514 | if ( texture.image.width > 0 ) { 515 | 516 | textureToPattern( texture ); 517 | 518 | } 519 | 520 | texture.addEventListener( 'update', onTextureUpdate ); 521 | 522 | } 523 | 524 | var pattern = _patterns[ texture.id ]; 525 | 526 | if ( pattern !== undefined ) { 527 | 528 | setFillStyle( pattern ); 529 | 530 | } else { 531 | 532 | setFillStyle( 'rgba( 0, 0, 0, 1 )' ); 533 | 534 | } 535 | 536 | // 537 | 538 | var bitmap = texture.image; 539 | 540 | var ox = bitmap.width * texture.offset.x; 541 | var oy = bitmap.height * texture.offset.y; 542 | 543 | var sx = bitmap.width * texture.repeat.x; 544 | var sy = bitmap.height * texture.repeat.y; 545 | 546 | var cx = scaleX / sx; 547 | var cy = scaleY / sy; 548 | 549 | _context.save(); 550 | _context.translate( v1.x, v1.y ); 551 | if ( material.rotation !== 0 ) _context.rotate( material.rotation ); 552 | _context.translate( - scaleX / 2, - scaleY / 2 ); 553 | _context.scale( cx, cy ); 554 | _context.translate( - ox, - oy ); 555 | _context.fillRect( ox, oy, sx, sy ); 556 | _context.restore(); 557 | 558 | } else { 559 | 560 | // no texture 561 | 562 | setFillStyle( material.color.getStyle() ); 563 | 564 | _context.save(); 565 | _context.translate( v1.x, v1.y ); 566 | if ( material.rotation !== 0 ) _context.rotate( material.rotation ); 567 | _context.scale( scaleX, - scaleY ); 568 | _context.fillRect( - 0.5, - 0.5, 1, 1 ); 569 | _context.restore(); 570 | 571 | } 572 | 573 | } else if ( material instanceof THREE.SpriteCanvasMaterial ) { 574 | 575 | setStrokeStyle( material.color.getStyle() ); 576 | setFillStyle( material.color.getStyle() ); 577 | 578 | _context.save(); 579 | _context.translate( v1.x, v1.y ); 580 | if ( material.rotation !== 0 ) _context.rotate( material.rotation ); 581 | _context.scale( scaleX, scaleY ); 582 | 583 | material.program( _context ); 584 | 585 | _context.restore(); 586 | 587 | } 588 | 589 | /* DEBUG 590 | setStrokeStyle( 'rgb(255,255,0)' ); 591 | _context.beginPath(); 592 | _context.moveTo( v1.x - 10, v1.y ); 593 | _context.lineTo( v1.x + 10, v1.y ); 594 | _context.moveTo( v1.x, v1.y - 10 ); 595 | _context.lineTo( v1.x, v1.y + 10 ); 596 | _context.stroke(); 597 | */ 598 | 599 | } 600 | 601 | function renderLine( v1, v2, element, material ) { 602 | 603 | setOpacity( material.opacity ); 604 | setBlending( material.blending ); 605 | 606 | _context.beginPath(); 607 | _context.moveTo( v1.positionScreen.x, v1.positionScreen.y ); 608 | _context.lineTo( v2.positionScreen.x, v2.positionScreen.y ); 609 | 610 | if ( material instanceof THREE.LineBasicMaterial ) { 611 | 612 | setLineWidth( material.linewidth ); 613 | setLineCap( material.linecap ); 614 | setLineJoin( material.linejoin ); 615 | 616 | if ( material.vertexColors !== THREE.VertexColors ) { 617 | 618 | setStrokeStyle( material.color.getStyle() ); 619 | 620 | } else { 621 | 622 | var colorStyle1 = element.vertexColors[ 0 ].getStyle(); 623 | var colorStyle2 = element.vertexColors[ 1 ].getStyle(); 624 | 625 | if ( colorStyle1 === colorStyle2 ) { 626 | 627 | setStrokeStyle( colorStyle1 ); 628 | 629 | } else { 630 | 631 | try { 632 | 633 | var grad = _context.createLinearGradient( 634 | v1.positionScreen.x, 635 | v1.positionScreen.y, 636 | v2.positionScreen.x, 637 | v2.positionScreen.y 638 | ); 639 | grad.addColorStop( 0, colorStyle1 ); 640 | grad.addColorStop( 1, colorStyle2 ); 641 | 642 | } catch ( exception ) { 643 | 644 | grad = colorStyle1; 645 | 646 | } 647 | 648 | setStrokeStyle( grad ); 649 | 650 | } 651 | 652 | } 653 | 654 | _context.stroke(); 655 | _elemBox.expandByScalar( material.linewidth * 2 ); 656 | 657 | } else if ( material instanceof THREE.LineDashedMaterial ) { 658 | 659 | setLineWidth( material.linewidth ); 660 | setLineCap( material.linecap ); 661 | setLineJoin( material.linejoin ); 662 | setStrokeStyle( material.color.getStyle() ); 663 | setLineDash( [ material.dashSize, material.gapSize ] ); 664 | 665 | _context.stroke(); 666 | 667 | _elemBox.expandByScalar( material.linewidth * 2 ); 668 | 669 | setLineDash( [] ); 670 | 671 | } 672 | 673 | } 674 | 675 | function renderFace3( v1, v2, v3, uv1, uv2, uv3, element, material ) { 676 | 677 | _this.info.render.vertices += 3; 678 | _this.info.render.faces ++; 679 | 680 | setOpacity( material.opacity ); 681 | setBlending( material.blending ); 682 | 683 | _v1x = v1.positionScreen.x; _v1y = v1.positionScreen.y; 684 | _v2x = v2.positionScreen.x; _v2y = v2.positionScreen.y; 685 | _v3x = v3.positionScreen.x; _v3y = v3.positionScreen.y; 686 | 687 | drawTriangle( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y ); 688 | 689 | if ( ( material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) && material.map === null ) { 690 | 691 | _diffuseColor.copy( material.color ); 692 | _emissiveColor.copy( material.emissive ); 693 | 694 | if ( material.vertexColors === THREE.FaceColors ) { 695 | 696 | _diffuseColor.multiply( element.color ); 697 | 698 | } 699 | 700 | _color.copy( _ambientLight ); 701 | 702 | _centroid.copy( v1.positionWorld ).add( v2.positionWorld ).add( v3.positionWorld ).divideScalar( 3 ); 703 | 704 | calculateLight( _centroid, element.normalModel, _color ); 705 | 706 | _color.multiply( _diffuseColor ).add( _emissiveColor ); 707 | 708 | material.wireframe === true 709 | ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) 710 | : fillPath( _color ); 711 | 712 | } else if ( material instanceof THREE.MeshBasicMaterial || 713 | material instanceof THREE.MeshLambertMaterial || 714 | material instanceof THREE.MeshPhongMaterial ) { 715 | 716 | if ( material.map !== null ) { 717 | 718 | var mapping = material.map.mapping; 719 | 720 | if ( mapping === THREE.UVMapping ) { 721 | 722 | _uvs = element.uvs; 723 | patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uvs[ uv1 ].x, _uvs[ uv1 ].y, _uvs[ uv2 ].x, _uvs[ uv2 ].y, _uvs[ uv3 ].x, _uvs[ uv3 ].y, material.map ); 724 | 725 | } 726 | 727 | } else if ( material.envMap !== null ) { 728 | 729 | if ( material.envMap.mapping === THREE.SphericalReflectionMapping ) { 730 | 731 | _normal.copy( element.vertexNormalsModel[ uv1 ] ).applyMatrix3( _normalViewMatrix ); 732 | _uv1x = 0.5 * _normal.x + 0.5; 733 | _uv1y = 0.5 * _normal.y + 0.5; 734 | 735 | _normal.copy( element.vertexNormalsModel[ uv2 ] ).applyMatrix3( _normalViewMatrix ); 736 | _uv2x = 0.5 * _normal.x + 0.5; 737 | _uv2y = 0.5 * _normal.y + 0.5; 738 | 739 | _normal.copy( element.vertexNormalsModel[ uv3 ] ).applyMatrix3( _normalViewMatrix ); 740 | _uv3x = 0.5 * _normal.x + 0.5; 741 | _uv3y = 0.5 * _normal.y + 0.5; 742 | 743 | patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, material.envMap ); 744 | 745 | } 746 | 747 | } else { 748 | 749 | _color.copy( material.color ); 750 | 751 | if ( material.vertexColors === THREE.FaceColors ) { 752 | 753 | _color.multiply( element.color ); 754 | 755 | } 756 | 757 | material.wireframe === true 758 | ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) 759 | : fillPath( _color ); 760 | 761 | } 762 | 763 | } else if ( material instanceof THREE.MeshDepthMaterial ) { 764 | 765 | _color.r = _color.g = _color.b = 1 - smoothstep( v1.positionScreen.z * v1.positionScreen.w, _camera.near, _camera.far ); 766 | 767 | material.wireframe === true 768 | ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) 769 | : fillPath( _color ); 770 | 771 | } else if ( material instanceof THREE.MeshNormalMaterial ) { 772 | 773 | _normal.copy( element.normalModel ).applyMatrix3( _normalViewMatrix ); 774 | 775 | _color.setRGB( _normal.x, _normal.y, _normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 ); 776 | 777 | material.wireframe === true 778 | ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) 779 | : fillPath( _color ); 780 | 781 | } else { 782 | 783 | _color.setRGB( 1, 1, 1 ); 784 | 785 | material.wireframe === true 786 | ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) 787 | : fillPath( _color ); 788 | 789 | } 790 | 791 | } 792 | 793 | // 794 | 795 | function drawTriangle( x0, y0, x1, y1, x2, y2 ) { 796 | 797 | _context.beginPath(); 798 | _context.moveTo( x0, y0 ); 799 | _context.lineTo( x1, y1 ); 800 | _context.lineTo( x2, y2 ); 801 | _context.closePath(); 802 | 803 | } 804 | 805 | function strokePath( color, linewidth, linecap, linejoin ) { 806 | 807 | setLineWidth( linewidth ); 808 | setLineCap( linecap ); 809 | setLineJoin( linejoin ); 810 | setStrokeStyle( color.getStyle() ); 811 | 812 | _context.stroke(); 813 | 814 | _elemBox.expandByScalar( linewidth * 2 ); 815 | 816 | } 817 | 818 | function fillPath( color ) { 819 | 820 | setFillStyle( color.getStyle() ); 821 | _context.fill(); 822 | 823 | } 824 | 825 | function onTextureUpdate ( event ) { 826 | 827 | textureToPattern( event.target ); 828 | 829 | } 830 | 831 | function textureToPattern( texture ) { 832 | 833 | if ( texture instanceof THREE.CompressedTexture ) return; 834 | 835 | var repeatX = texture.wrapS === THREE.RepeatWrapping; 836 | var repeatY = texture.wrapT === THREE.RepeatWrapping; 837 | 838 | var image = texture.image; 839 | 840 | var canvas = document.createElement( 'canvas' ); 841 | canvas.width = image.width; 842 | canvas.height = image.height; 843 | 844 | var context = canvas.getContext( '2d' ); 845 | context.setTransform( 1, 0, 0, - 1, 0, image.height ); 846 | context.drawImage( image, 0, 0 ); 847 | 848 | _patterns[ texture.id ] = _context.createPattern( 849 | canvas, repeatX === true && repeatY === true 850 | ? 'repeat' 851 | : repeatX === true && repeatY === false 852 | ? 'repeat-x' 853 | : repeatX === false && repeatY === true 854 | ? 'repeat-y' 855 | : 'no-repeat' 856 | ); 857 | 858 | } 859 | 860 | function patternPath( x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2, texture ) { 861 | 862 | if ( texture instanceof THREE.DataTexture ) return; 863 | 864 | if ( texture.hasEventListener( 'update', onTextureUpdate ) === false ) { 865 | 866 | if ( texture.image !== undefined && texture.image.width > 0 ) { 867 | 868 | textureToPattern( texture ); 869 | 870 | } 871 | 872 | texture.addEventListener( 'update', onTextureUpdate ); 873 | 874 | } 875 | 876 | var pattern = _patterns[ texture.id ]; 877 | 878 | if ( pattern !== undefined ) { 879 | 880 | setFillStyle( pattern ); 881 | 882 | } else { 883 | 884 | setFillStyle( 'rgba(0,0,0,1)' ); 885 | _context.fill(); 886 | 887 | return; 888 | 889 | } 890 | 891 | // http://extremelysatisfactorytotalitarianism.com/blog/?p=2120 892 | 893 | var a, b, c, d, e, f, det, idet, 894 | offsetX = texture.offset.x / texture.repeat.x, 895 | offsetY = texture.offset.y / texture.repeat.y, 896 | width = texture.image.width * texture.repeat.x, 897 | height = texture.image.height * texture.repeat.y; 898 | 899 | u0 = ( u0 + offsetX ) * width; 900 | v0 = ( v0 + offsetY ) * height; 901 | 902 | u1 = ( u1 + offsetX ) * width; 903 | v1 = ( v1 + offsetY ) * height; 904 | 905 | u2 = ( u2 + offsetX ) * width; 906 | v2 = ( v2 + offsetY ) * height; 907 | 908 | x1 -= x0; y1 -= y0; 909 | x2 -= x0; y2 -= y0; 910 | 911 | u1 -= u0; v1 -= v0; 912 | u2 -= u0; v2 -= v0; 913 | 914 | det = u1 * v2 - u2 * v1; 915 | 916 | if ( det === 0 ) return; 917 | 918 | idet = 1 / det; 919 | 920 | a = ( v2 * x1 - v1 * x2 ) * idet; 921 | b = ( v2 * y1 - v1 * y2 ) * idet; 922 | c = ( u1 * x2 - u2 * x1 ) * idet; 923 | d = ( u1 * y2 - u2 * y1 ) * idet; 924 | 925 | e = x0 - a * u0 - c * v0; 926 | f = y0 - b * u0 - d * v0; 927 | 928 | _context.save(); 929 | _context.transform( a, b, c, d, e, f ); 930 | _context.fill(); 931 | _context.restore(); 932 | 933 | } 934 | 935 | function clipImage( x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2, image ) { 936 | 937 | // http://extremelysatisfactorytotalitarianism.com/blog/?p=2120 938 | 939 | var a, b, c, d, e, f, det, idet, 940 | width = image.width - 1, 941 | height = image.height - 1; 942 | 943 | u0 *= width; v0 *= height; 944 | u1 *= width; v1 *= height; 945 | u2 *= width; v2 *= height; 946 | 947 | x1 -= x0; y1 -= y0; 948 | x2 -= x0; y2 -= y0; 949 | 950 | u1 -= u0; v1 -= v0; 951 | u2 -= u0; v2 -= v0; 952 | 953 | det = u1 * v2 - u2 * v1; 954 | 955 | idet = 1 / det; 956 | 957 | a = ( v2 * x1 - v1 * x2 ) * idet; 958 | b = ( v2 * y1 - v1 * y2 ) * idet; 959 | c = ( u1 * x2 - u2 * x1 ) * idet; 960 | d = ( u1 * y2 - u2 * y1 ) * idet; 961 | 962 | e = x0 - a * u0 - c * v0; 963 | f = y0 - b * u0 - d * v0; 964 | 965 | _context.save(); 966 | _context.transform( a, b, c, d, e, f ); 967 | _context.clip(); 968 | _context.drawImage( image, 0, 0 ); 969 | _context.restore(); 970 | 971 | } 972 | 973 | // Hide anti-alias gaps 974 | 975 | function expand( v1, v2, pixels ) { 976 | 977 | var x = v2.x - v1.x, y = v2.y - v1.y, 978 | det = x * x + y * y, idet; 979 | 980 | if ( det === 0 ) return; 981 | 982 | idet = pixels / Math.sqrt( det ); 983 | 984 | x *= idet; y *= idet; 985 | 986 | v2.x += x; v2.y += y; 987 | v1.x -= x; v1.y -= y; 988 | 989 | } 990 | 991 | // Context cached methods. 992 | 993 | function setOpacity( value ) { 994 | 995 | if ( _contextGlobalAlpha !== value ) { 996 | 997 | _context.globalAlpha = value; 998 | _contextGlobalAlpha = value; 999 | 1000 | } 1001 | 1002 | } 1003 | 1004 | function setBlending( value ) { 1005 | 1006 | if ( _contextGlobalCompositeOperation !== value ) { 1007 | 1008 | if ( value === THREE.NormalBlending ) { 1009 | 1010 | _context.globalCompositeOperation = 'source-over'; 1011 | 1012 | } else if ( value === THREE.AdditiveBlending ) { 1013 | 1014 | _context.globalCompositeOperation = 'lighter'; 1015 | 1016 | } else if ( value === THREE.SubtractiveBlending ) { 1017 | 1018 | _context.globalCompositeOperation = 'darker'; 1019 | 1020 | } 1021 | 1022 | _contextGlobalCompositeOperation = value; 1023 | 1024 | } 1025 | 1026 | } 1027 | 1028 | function setLineWidth( value ) { 1029 | 1030 | if ( _contextLineWidth !== value ) { 1031 | 1032 | _context.lineWidth = value; 1033 | _contextLineWidth = value; 1034 | 1035 | } 1036 | 1037 | } 1038 | 1039 | function setLineCap( value ) { 1040 | 1041 | // "butt", "round", "square" 1042 | 1043 | if ( _contextLineCap !== value ) { 1044 | 1045 | _context.lineCap = value; 1046 | _contextLineCap = value; 1047 | 1048 | } 1049 | 1050 | } 1051 | 1052 | function setLineJoin( value ) { 1053 | 1054 | // "round", "bevel", "miter" 1055 | 1056 | if ( _contextLineJoin !== value ) { 1057 | 1058 | _context.lineJoin = value; 1059 | _contextLineJoin = value; 1060 | 1061 | } 1062 | 1063 | } 1064 | 1065 | function setStrokeStyle( value ) { 1066 | 1067 | if ( _contextStrokeStyle !== value ) { 1068 | 1069 | _context.strokeStyle = value; 1070 | _contextStrokeStyle = value; 1071 | 1072 | } 1073 | 1074 | } 1075 | 1076 | function setFillStyle( value ) { 1077 | 1078 | if ( _contextFillStyle !== value ) { 1079 | 1080 | _context.fillStyle = value; 1081 | _contextFillStyle = value; 1082 | 1083 | } 1084 | 1085 | } 1086 | 1087 | function setLineDash( value ) { 1088 | 1089 | if ( _contextLineDash.length !== value.length ) { 1090 | 1091 | _context.setLineDash( value ); 1092 | _contextLineDash = value; 1093 | 1094 | } 1095 | 1096 | } 1097 | 1098 | }; 1099 | -------------------------------------------------------------------------------- /js/color.js: -------------------------------------------------------------------------------- 1 | 2 | var colors = new Array( 3 | [62,35,255], 4 | [60,255,60], 5 | [255,35,98], 6 | [45,175,230], 7 | [255,0,255], 8 | [255,128,0]); 9 | 10 | 11 | // 12 | 13 | var step = 0; 14 | //color table indices for: 15 | // current color left 16 | // next color left 17 | // current color right 18 | // next color right 19 | var colorIndices = [0,1,2,3]; 20 | 21 | //transition speed 22 | var gradientSpeed = 0.002; 23 | 24 | function updateGradient() 25 | { 26 | 27 | if ( $===undefined ) return; 28 | 29 | var c0_0 = colors[colorIndices[0]]; 30 | var c0_1 = colors[colorIndices[1]]; 31 | var c1_0 = colors[colorIndices[2]]; 32 | var c1_1 = colors[colorIndices[3]]; 33 | 34 | var istep = 1 - step; 35 | var r1 = Math.round(istep * c0_0[0] + step * c0_1[0]); 36 | var g1 = Math.round(istep * c0_0[1] + step * c0_1[1]); 37 | var b1 = Math.round(istep * c0_0[2] + step * c0_1[2]); 38 | var color1 = "rgb("+r1+","+g1+","+b1+")"; 39 | 40 | var r2 = Math.round(istep * c1_0[0] + step * c1_1[0]); 41 | var g2 = Math.round(istep * c1_0[1] + step * c1_1[1]); 42 | var b2 = Math.round(istep * c1_0[2] + step * c1_1[2]); 43 | var color2 = "rgb("+r2+","+g2+","+b2+")"; 44 | 45 | $('.gradient').css({ 46 | background: "-webkit-gradient(linear, left top, right top, from("+color1+"), to("+color2+"))"}).css({ 47 | background: "-moz-linear-gradient(left, "+color1+" 0%, "+color2+" 100%)"}); 48 | 49 | step += gradientSpeed; 50 | if ( step >= 1 ) 51 | { 52 | step %= 1; 53 | colorIndices[0] = colorIndices[1]; 54 | colorIndices[2] = colorIndices[3]; 55 | 56 | //pick two new target color indices 57 | //do not pick the same as the current one 58 | colorIndices[1] = ( colorIndices[1] + Math.floor( 1 + Math.random() * (colors.length - 1))) % colors.length; 59 | colorIndices[3] = ( colorIndices[3] + Math.floor( 1 + Math.random() * (colors.length - 1))) % colors.length; 60 | 61 | } 62 | } 63 | 64 | setInterval(updateGradient,10); -------------------------------------------------------------------------------- /js/projector.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | * @author supereggbert / http://www.paulbrunt.co.uk/ 4 | * @author julianwa / https://github.com/julianwa 5 | */ 6 | 7 | THREE.RenderableObject = function () { 8 | 9 | this.id = 0; 10 | 11 | this.object = null; 12 | this.z = 0; 13 | 14 | }; 15 | 16 | // 17 | 18 | THREE.RenderableFace = function () { 19 | 20 | this.id = 0; 21 | 22 | this.v1 = new THREE.RenderableVertex(); 23 | this.v2 = new THREE.RenderableVertex(); 24 | this.v3 = new THREE.RenderableVertex(); 25 | 26 | this.normalModel = new THREE.Vector3(); 27 | 28 | this.vertexNormalsModel = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ]; 29 | this.vertexNormalsLength = 0; 30 | 31 | this.color = new THREE.Color(); 32 | this.material = null; 33 | this.uvs = [ new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() ]; 34 | 35 | this.z = 0; 36 | 37 | }; 38 | 39 | // 40 | 41 | THREE.RenderableVertex = function () { 42 | 43 | this.position = new THREE.Vector3(); 44 | this.positionWorld = new THREE.Vector3(); 45 | this.positionScreen = new THREE.Vector4(); 46 | 47 | this.visible = true; 48 | 49 | }; 50 | 51 | THREE.RenderableVertex.prototype.copy = function ( vertex ) { 52 | 53 | this.positionWorld.copy( vertex.positionWorld ); 54 | this.positionScreen.copy( vertex.positionScreen ); 55 | 56 | }; 57 | 58 | // 59 | 60 | THREE.RenderableLine = function () { 61 | 62 | this.id = 0; 63 | 64 | this.v1 = new THREE.RenderableVertex(); 65 | this.v2 = new THREE.RenderableVertex(); 66 | 67 | this.vertexColors = [ new THREE.Color(), new THREE.Color() ]; 68 | this.material = null; 69 | 70 | this.z = 0; 71 | 72 | }; 73 | 74 | // 75 | 76 | THREE.RenderableSprite = function () { 77 | 78 | this.id = 0; 79 | 80 | this.object = null; 81 | 82 | this.x = 0; 83 | this.y = 0; 84 | this.z = 0; 85 | 86 | this.rotation = 0; 87 | this.scale = new THREE.Vector2(); 88 | 89 | this.material = null; 90 | 91 | }; 92 | 93 | // 94 | 95 | THREE.Projector = function () { 96 | 97 | var _object, _objectCount, _objectPool = [], _objectPoolLength = 0, 98 | _vertex, _vertexCount, _vertexPool = [], _vertexPoolLength = 0, 99 | _face, _faceCount, _facePool = [], _facePoolLength = 0, 100 | _line, _lineCount, _linePool = [], _linePoolLength = 0, 101 | _sprite, _spriteCount, _spritePool = [], _spritePoolLength = 0, 102 | 103 | _renderData = { objects: [], lights: [], elements: [] }, 104 | 105 | _vector3 = new THREE.Vector3(), 106 | _vector4 = new THREE.Vector4(), 107 | 108 | _clipBox = new THREE.Box3( new THREE.Vector3( - 1, - 1, - 1 ), new THREE.Vector3( 1, 1, 1 ) ), 109 | _boundingBox = new THREE.Box3(), 110 | _points3 = new Array( 3 ), 111 | _points4 = new Array( 4 ), 112 | 113 | _viewMatrix = new THREE.Matrix4(), 114 | _viewProjectionMatrix = new THREE.Matrix4(), 115 | 116 | _modelMatrix, 117 | _modelViewProjectionMatrix = new THREE.Matrix4(), 118 | 119 | _normalMatrix = new THREE.Matrix3(), 120 | 121 | _frustum = new THREE.Frustum(), 122 | 123 | _clippedVertex1PositionScreen = new THREE.Vector4(), 124 | _clippedVertex2PositionScreen = new THREE.Vector4(); 125 | 126 | // 127 | 128 | this.projectVector = function ( vector, camera ) { 129 | 130 | console.warn( 'THREE.Projector: .projectVector() is now vector.project().' ); 131 | vector.project( camera ); 132 | 133 | }; 134 | 135 | this.unprojectVector = function ( vector, camera ) { 136 | 137 | console.warn( 'THREE.Projector: .unprojectVector() is now vector.unproject().' ); 138 | vector.unproject( camera ); 139 | 140 | }; 141 | 142 | this.pickingRay = function ( vector, camera ) { 143 | 144 | console.error( 'THREE.Projector: .pickingRay() is now raycaster.setFromCamera().' ); 145 | 146 | }; 147 | 148 | // 149 | 150 | var RenderList = function () { 151 | 152 | var normals = []; 153 | var uvs = []; 154 | 155 | var object = null; 156 | var material = null; 157 | 158 | var normalMatrix = new THREE.Matrix3(); 159 | 160 | var setObject = function ( value ) { 161 | 162 | object = value; 163 | material = object.material; 164 | 165 | normalMatrix.getNormalMatrix( object.matrixWorld ); 166 | 167 | normals.length = 0; 168 | uvs.length = 0; 169 | 170 | }; 171 | 172 | var projectVertex = function ( vertex ) { 173 | 174 | var position = vertex.position; 175 | var positionWorld = vertex.positionWorld; 176 | var positionScreen = vertex.positionScreen; 177 | 178 | positionWorld.copy( position ).applyMatrix4( _modelMatrix ); 179 | positionScreen.copy( positionWorld ).applyMatrix4( _viewProjectionMatrix ); 180 | 181 | var invW = 1 / positionScreen.w; 182 | 183 | positionScreen.x *= invW; 184 | positionScreen.y *= invW; 185 | positionScreen.z *= invW; 186 | 187 | vertex.visible = positionScreen.x >= - 1 && positionScreen.x <= 1 && 188 | positionScreen.y >= - 1 && positionScreen.y <= 1 && 189 | positionScreen.z >= - 1 && positionScreen.z <= 1; 190 | 191 | }; 192 | 193 | var pushVertex = function ( x, y, z ) { 194 | 195 | _vertex = getNextVertexInPool(); 196 | _vertex.position.set( x, y, z ); 197 | 198 | projectVertex( _vertex ); 199 | 200 | }; 201 | 202 | var pushNormal = function ( x, y, z ) { 203 | 204 | normals.push( x, y, z ); 205 | 206 | }; 207 | 208 | var pushUv = function ( x, y ) { 209 | 210 | uvs.push( x, y ); 211 | 212 | }; 213 | 214 | var checkTriangleVisibility = function ( v1, v2, v3 ) { 215 | 216 | if ( v1.visible === true || v2.visible === true || v3.visible === true ) return true; 217 | 218 | _points3[ 0 ] = v1.positionScreen; 219 | _points3[ 1 ] = v2.positionScreen; 220 | _points3[ 2 ] = v3.positionScreen; 221 | 222 | return _clipBox.isIntersectionBox( _boundingBox.setFromPoints( _points3 ) ); 223 | 224 | }; 225 | 226 | var checkBackfaceCulling = function ( v1, v2, v3 ) { 227 | 228 | return ( ( v3.positionScreen.x - v1.positionScreen.x ) * 229 | ( v2.positionScreen.y - v1.positionScreen.y ) - 230 | ( v3.positionScreen.y - v1.positionScreen.y ) * 231 | ( v2.positionScreen.x - v1.positionScreen.x ) ) < 0; 232 | 233 | }; 234 | 235 | var pushLine = function ( a, b ) { 236 | 237 | var v1 = _vertexPool[ a ]; 238 | var v2 = _vertexPool[ b ]; 239 | 240 | _line = getNextLineInPool(); 241 | 242 | _line.id = object.id; 243 | _line.v1.copy( v1 ); 244 | _line.v2.copy( v2 ); 245 | _line.z = ( v1.positionScreen.z + v2.positionScreen.z ) / 2; 246 | 247 | _line.material = object.material; 248 | 249 | _renderData.elements.push( _line ); 250 | 251 | }; 252 | 253 | var pushTriangle = function ( a, b, c ) { 254 | 255 | var v1 = _vertexPool[ a ]; 256 | var v2 = _vertexPool[ b ]; 257 | var v3 = _vertexPool[ c ]; 258 | 259 | if ( checkTriangleVisibility( v1, v2, v3 ) === false ) return; 260 | 261 | if ( material.side === THREE.DoubleSide || checkBackfaceCulling( v1, v2, v3 ) === true ) { 262 | 263 | _face = getNextFaceInPool(); 264 | 265 | _face.id = object.id; 266 | _face.v1.copy( v1 ); 267 | _face.v2.copy( v2 ); 268 | _face.v3.copy( v3 ); 269 | _face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3; 270 | 271 | for ( var i = 0; i < 3; i ++ ) { 272 | 273 | var offset = arguments[ i ] * 3; 274 | var normal = _face.vertexNormalsModel[ i ]; 275 | 276 | normal.set( normals[ offset ], normals[ offset + 1 ], normals[ offset + 2 ] ); 277 | normal.applyMatrix3( normalMatrix ).normalize(); 278 | 279 | var offset2 = arguments[ i ] * 2; 280 | 281 | var uv = _face.uvs[ i ]; 282 | uv.set( uvs[ offset2 ], uvs[ offset2 + 1 ] ); 283 | 284 | } 285 | 286 | _face.vertexNormalsLength = 3; 287 | 288 | _face.material = object.material; 289 | 290 | _renderData.elements.push( _face ); 291 | 292 | } 293 | 294 | }; 295 | 296 | return { 297 | setObject: setObject, 298 | projectVertex: projectVertex, 299 | checkTriangleVisibility: checkTriangleVisibility, 300 | checkBackfaceCulling: checkBackfaceCulling, 301 | pushVertex: pushVertex, 302 | pushNormal: pushNormal, 303 | pushUv: pushUv, 304 | pushLine: pushLine, 305 | pushTriangle: pushTriangle 306 | } 307 | 308 | }; 309 | 310 | var renderList = new RenderList(); 311 | 312 | this.projectScene = function ( scene, camera, sortObjects, sortElements ) { 313 | 314 | _faceCount = 0; 315 | _lineCount = 0; 316 | _spriteCount = 0; 317 | 318 | _renderData.elements.length = 0; 319 | 320 | if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); 321 | if ( camera.parent === undefined ) camera.updateMatrixWorld(); 322 | 323 | _viewMatrix.copy( camera.matrixWorldInverse.getInverse( camera.matrixWorld ) ); 324 | _viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix ); 325 | 326 | _frustum.setFromMatrix( _viewProjectionMatrix ); 327 | 328 | // 329 | 330 | _objectCount = 0; 331 | 332 | _renderData.objects.length = 0; 333 | _renderData.lights.length = 0; 334 | 335 | scene.traverseVisible( function ( object ) { 336 | 337 | if ( object instanceof THREE.Light ) { 338 | 339 | _renderData.lights.push( object ); 340 | 341 | } else if ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Sprite ) { 342 | 343 | if ( object.material.visible === false ) return; 344 | 345 | if ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) { 346 | 347 | _object = getNextObjectInPool(); 348 | _object.id = object.id; 349 | _object.object = object; 350 | 351 | _vector3.setFromMatrixPosition( object.matrixWorld ); 352 | _vector3.applyProjection( _viewProjectionMatrix ); 353 | _object.z = _vector3.z; 354 | 355 | _renderData.objects.push( _object ); 356 | 357 | } 358 | 359 | } 360 | 361 | } ); 362 | 363 | if ( sortObjects === true ) { 364 | 365 | _renderData.objects.sort( painterSort ); 366 | 367 | } 368 | 369 | // 370 | 371 | for ( var o = 0, ol = _renderData.objects.length; o < ol; o ++ ) { 372 | 373 | var object = _renderData.objects[ o ].object; 374 | var geometry = object.geometry; 375 | 376 | renderList.setObject( object ); 377 | 378 | _modelMatrix = object.matrixWorld; 379 | 380 | _vertexCount = 0; 381 | 382 | if ( object instanceof THREE.Mesh ) { 383 | 384 | if ( geometry instanceof THREE.BufferGeometry ) { 385 | 386 | var attributes = geometry.attributes; 387 | var offsets = geometry.offsets; 388 | 389 | if ( attributes.position === undefined ) continue; 390 | 391 | var positions = attributes.position.array; 392 | 393 | for ( var i = 0, l = positions.length; i < l; i += 3 ) { 394 | 395 | renderList.pushVertex( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); 396 | 397 | } 398 | 399 | if ( attributes.normal !== undefined ) { 400 | 401 | var normals = attributes.normal.array; 402 | 403 | for ( var i = 0, l = normals.length; i < l; i += 3 ) { 404 | 405 | renderList.pushNormal( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ); 406 | 407 | } 408 | 409 | } 410 | 411 | if ( attributes.uv !== undefined ) { 412 | 413 | var uvs = attributes.uv.array; 414 | 415 | for ( var i = 0, l = uvs.length; i < l; i += 2 ) { 416 | 417 | renderList.pushUv( uvs[ i ], uvs[ i + 1 ] ); 418 | 419 | } 420 | 421 | } 422 | 423 | if ( attributes.index !== undefined ) { 424 | 425 | var indices = attributes.index.array; 426 | 427 | if ( offsets.length > 0 ) { 428 | 429 | for ( var o = 0; o < offsets.length; o ++ ) { 430 | 431 | var offset = offsets[ o ]; 432 | var index = offset.index; 433 | 434 | for ( var i = offset.start, l = offset.start + offset.count; i < l; i += 3 ) { 435 | 436 | renderList.pushTriangle( indices[ i ] + index, indices[ i + 1 ] + index, indices[ i + 2 ] + index ); 437 | 438 | } 439 | 440 | } 441 | 442 | } else { 443 | 444 | for ( var i = 0, l = indices.length; i < l; i += 3 ) { 445 | 446 | renderList.pushTriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] ); 447 | 448 | } 449 | 450 | } 451 | 452 | } else { 453 | 454 | for ( var i = 0, l = positions.length / 3; i < l; i += 3 ) { 455 | 456 | renderList.pushTriangle( i, i + 1, i + 2 ); 457 | 458 | } 459 | 460 | } 461 | 462 | } else if ( geometry instanceof THREE.Geometry ) { 463 | 464 | var vertices = geometry.vertices; 465 | var faces = geometry.faces; 466 | var faceVertexUvs = geometry.faceVertexUvs[ 0 ]; 467 | 468 | _normalMatrix.getNormalMatrix( _modelMatrix ); 469 | 470 | var material = object.material; 471 | 472 | var isFaceMaterial = material instanceof THREE.MeshFaceMaterial; 473 | var objectMaterials = isFaceMaterial === true ? object.material : null; 474 | 475 | for ( var v = 0, vl = vertices.length; v < vl; v ++ ) { 476 | 477 | var vertex = vertices[ v ]; 478 | 479 | _vector3.copy( vertex ); 480 | 481 | if ( material.morphTargets === true ) { 482 | 483 | var morphTargets = geometry.morphTargets; 484 | var morphInfluences = object.morphTargetInfluences; 485 | 486 | for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) { 487 | 488 | var influence = morphInfluences[ t ]; 489 | 490 | if ( influence === 0 ) continue; 491 | 492 | var target = morphTargets[ t ]; 493 | var targetVertex = target.vertices[ v ]; 494 | 495 | _vector3.x += ( targetVertex.x - vertex.x ) * influence; 496 | _vector3.y += ( targetVertex.y - vertex.y ) * influence; 497 | _vector3.z += ( targetVertex.z - vertex.z ) * influence; 498 | 499 | } 500 | 501 | } 502 | 503 | renderList.pushVertex( _vector3.x, _vector3.y, _vector3.z ); 504 | 505 | } 506 | 507 | for ( var f = 0, fl = faces.length; f < fl; f ++ ) { 508 | 509 | var face = faces[ f ]; 510 | 511 | var material = isFaceMaterial === true 512 | ? objectMaterials.materials[ face.materialIndex ] 513 | : object.material; 514 | 515 | if ( material === undefined ) continue; 516 | 517 | var side = material.side; 518 | 519 | var v1 = _vertexPool[ face.a ]; 520 | var v2 = _vertexPool[ face.b ]; 521 | var v3 = _vertexPool[ face.c ]; 522 | 523 | if ( renderList.checkTriangleVisibility( v1, v2, v3 ) === false ) continue; 524 | 525 | var visible = renderList.checkBackfaceCulling( v1, v2, v3 ); 526 | 527 | if ( side !== THREE.DoubleSide ) { 528 | if ( side === THREE.FrontSide && visible === false ) continue; 529 | if ( side === THREE.BackSide && visible === true ) continue; 530 | } 531 | 532 | _face = getNextFaceInPool(); 533 | 534 | _face.id = object.id; 535 | _face.v1.copy( v1 ); 536 | _face.v2.copy( v2 ); 537 | _face.v3.copy( v3 ); 538 | 539 | _face.normalModel.copy( face.normal ); 540 | 541 | if ( visible === false && ( side === THREE.BackSide || side === THREE.DoubleSide ) ) { 542 | 543 | _face.normalModel.negate(); 544 | 545 | } 546 | 547 | _face.normalModel.applyMatrix3( _normalMatrix ).normalize(); 548 | 549 | var faceVertexNormals = face.vertexNormals; 550 | 551 | for ( var n = 0, nl = Math.min( faceVertexNormals.length, 3 ); n < nl; n ++ ) { 552 | 553 | var normalModel = _face.vertexNormalsModel[ n ]; 554 | normalModel.copy( faceVertexNormals[ n ] ); 555 | 556 | if ( visible === false && ( side === THREE.BackSide || side === THREE.DoubleSide ) ) { 557 | 558 | normalModel.negate(); 559 | 560 | } 561 | 562 | normalModel.applyMatrix3( _normalMatrix ).normalize(); 563 | 564 | } 565 | 566 | _face.vertexNormalsLength = faceVertexNormals.length; 567 | 568 | var vertexUvs = faceVertexUvs[ f ]; 569 | 570 | if ( vertexUvs !== undefined ) { 571 | 572 | for ( var u = 0; u < 3; u ++ ) { 573 | 574 | _face.uvs[ u ].copy( vertexUvs[ u ] ); 575 | 576 | } 577 | 578 | } 579 | 580 | _face.color = face.color; 581 | _face.material = material; 582 | 583 | _face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3; 584 | 585 | _renderData.elements.push( _face ); 586 | 587 | } 588 | 589 | } 590 | 591 | } else if ( object instanceof THREE.Line ) { 592 | 593 | if ( geometry instanceof THREE.BufferGeometry ) { 594 | 595 | var attributes = geometry.attributes; 596 | 597 | if ( attributes.position !== undefined ) { 598 | 599 | var positions = attributes.position.array; 600 | 601 | for ( var i = 0, l = positions.length; i < l; i += 3 ) { 602 | 603 | renderList.pushVertex( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); 604 | 605 | } 606 | 607 | if ( attributes.index !== undefined ) { 608 | 609 | var indices = attributes.index.array; 610 | 611 | for ( var i = 0, l = indices.length; i < l; i += 2 ) { 612 | 613 | renderList.pushLine( indices[ i ], indices[ i + 1 ] ); 614 | 615 | } 616 | 617 | } else { 618 | 619 | var step = object.mode === THREE.LinePieces ? 2 : 1; 620 | 621 | for ( var i = 0, l = ( positions.length / 3 ) - 1; i < l; i += step ) { 622 | 623 | renderList.pushLine( i, i + 1 ); 624 | 625 | } 626 | 627 | } 628 | 629 | } 630 | 631 | } else if ( geometry instanceof THREE.Geometry ) { 632 | 633 | _modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix ); 634 | 635 | var vertices = object.geometry.vertices; 636 | 637 | if ( vertices.length === 0 ) continue; 638 | 639 | v1 = getNextVertexInPool(); 640 | v1.positionScreen.copy( vertices[ 0 ] ).applyMatrix4( _modelViewProjectionMatrix ); 641 | 642 | // Handle LineStrip and LinePieces 643 | var step = object.mode === THREE.LinePieces ? 2 : 1; 644 | 645 | for ( var v = 1, vl = vertices.length; v < vl; v ++ ) { 646 | 647 | v1 = getNextVertexInPool(); 648 | v1.positionScreen.copy( vertices[ v ] ).applyMatrix4( _modelViewProjectionMatrix ); 649 | 650 | if ( ( v + 1 ) % step > 0 ) continue; 651 | 652 | v2 = _vertexPool[ _vertexCount - 2 ]; 653 | 654 | _clippedVertex1PositionScreen.copy( v1.positionScreen ); 655 | _clippedVertex2PositionScreen.copy( v2.positionScreen ); 656 | 657 | if ( clipLine( _clippedVertex1PositionScreen, _clippedVertex2PositionScreen ) === true ) { 658 | 659 | // Perform the perspective divide 660 | _clippedVertex1PositionScreen.multiplyScalar( 1 / _clippedVertex1PositionScreen.w ); 661 | _clippedVertex2PositionScreen.multiplyScalar( 1 / _clippedVertex2PositionScreen.w ); 662 | 663 | _line = getNextLineInPool(); 664 | 665 | _line.id = object.id; 666 | _line.v1.positionScreen.copy( _clippedVertex1PositionScreen ); 667 | _line.v2.positionScreen.copy( _clippedVertex2PositionScreen ); 668 | 669 | _line.z = Math.max( _clippedVertex1PositionScreen.z, _clippedVertex2PositionScreen.z ); 670 | 671 | _line.material = object.material; 672 | 673 | if ( object.material.vertexColors === THREE.VertexColors ) { 674 | 675 | _line.vertexColors[ 0 ].copy( object.geometry.colors[ v ] ); 676 | _line.vertexColors[ 1 ].copy( object.geometry.colors[ v - 1 ] ); 677 | 678 | } 679 | 680 | _renderData.elements.push( _line ); 681 | 682 | } 683 | 684 | } 685 | 686 | } 687 | 688 | } else if ( object instanceof THREE.Sprite ) { 689 | 690 | _vector4.set( _modelMatrix.elements[ 12 ], _modelMatrix.elements[ 13 ], _modelMatrix.elements[ 14 ], 1 ); 691 | _vector4.applyMatrix4( _viewProjectionMatrix ); 692 | 693 | var invW = 1 / _vector4.w; 694 | 695 | _vector4.z *= invW; 696 | 697 | if ( _vector4.z >= - 1 && _vector4.z <= 1 ) { 698 | 699 | _sprite = getNextSpriteInPool(); 700 | _sprite.id = object.id; 701 | _sprite.x = _vector4.x * invW; 702 | _sprite.y = _vector4.y * invW; 703 | _sprite.z = _vector4.z; 704 | _sprite.object = object; 705 | 706 | _sprite.rotation = object.rotation; 707 | 708 | _sprite.scale.x = object.scale.x * Math.abs( _sprite.x - ( _vector4.x + camera.projectionMatrix.elements[ 0 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 12 ] ) ); 709 | _sprite.scale.y = object.scale.y * Math.abs( _sprite.y - ( _vector4.y + camera.projectionMatrix.elements[ 5 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 13 ] ) ); 710 | 711 | _sprite.material = object.material; 712 | 713 | _renderData.elements.push( _sprite ); 714 | 715 | } 716 | 717 | } 718 | 719 | } 720 | 721 | if ( sortElements === true ) { 722 | 723 | _renderData.elements.sort( painterSort ); 724 | 725 | } 726 | 727 | return _renderData; 728 | 729 | }; 730 | 731 | // Pools 732 | 733 | function getNextObjectInPool() { 734 | 735 | if ( _objectCount === _objectPoolLength ) { 736 | 737 | var object = new THREE.RenderableObject(); 738 | _objectPool.push( object ); 739 | _objectPoolLength ++; 740 | _objectCount ++; 741 | return object; 742 | 743 | } 744 | 745 | return _objectPool[ _objectCount ++ ]; 746 | 747 | } 748 | 749 | function getNextVertexInPool() { 750 | 751 | if ( _vertexCount === _vertexPoolLength ) { 752 | 753 | var vertex = new THREE.RenderableVertex(); 754 | _vertexPool.push( vertex ); 755 | _vertexPoolLength ++; 756 | _vertexCount ++; 757 | return vertex; 758 | 759 | } 760 | 761 | return _vertexPool[ _vertexCount ++ ]; 762 | 763 | } 764 | 765 | function getNextFaceInPool() { 766 | 767 | if ( _faceCount === _facePoolLength ) { 768 | 769 | var face = new THREE.RenderableFace(); 770 | _facePool.push( face ); 771 | _facePoolLength ++; 772 | _faceCount ++; 773 | return face; 774 | 775 | } 776 | 777 | return _facePool[ _faceCount ++ ]; 778 | 779 | 780 | } 781 | 782 | function getNextLineInPool() { 783 | 784 | if ( _lineCount === _linePoolLength ) { 785 | 786 | var line = new THREE.RenderableLine(); 787 | _linePool.push( line ); 788 | _linePoolLength ++; 789 | _lineCount ++ 790 | return line; 791 | 792 | } 793 | 794 | return _linePool[ _lineCount ++ ]; 795 | 796 | } 797 | 798 | function getNextSpriteInPool() { 799 | 800 | if ( _spriteCount === _spritePoolLength ) { 801 | 802 | var sprite = new THREE.RenderableSprite(); 803 | _spritePool.push( sprite ); 804 | _spritePoolLength ++; 805 | _spriteCount ++ 806 | return sprite; 807 | 808 | } 809 | 810 | return _spritePool[ _spriteCount ++ ]; 811 | 812 | } 813 | 814 | // 815 | 816 | function painterSort( a, b ) { 817 | 818 | if ( a.z !== b.z ) { 819 | 820 | return b.z - a.z; 821 | 822 | } else if ( a.id !== b.id ) { 823 | 824 | return a.id - b.id; 825 | 826 | } else { 827 | 828 | return 0; 829 | 830 | } 831 | 832 | } 833 | 834 | function clipLine( s1, s2 ) { 835 | 836 | var alpha1 = 0, alpha2 = 1, 837 | 838 | // Calculate the boundary coordinate of each vertex for the near and far clip planes, 839 | // Z = -1 and Z = +1, respectively. 840 | bc1near = s1.z + s1.w, 841 | bc2near = s2.z + s2.w, 842 | bc1far = - s1.z + s1.w, 843 | bc2far = - s2.z + s2.w; 844 | 845 | if ( bc1near >= 0 && bc2near >= 0 && bc1far >= 0 && bc2far >= 0 ) { 846 | 847 | // Both vertices lie entirely within all clip planes. 848 | return true; 849 | 850 | } else if ( ( bc1near < 0 && bc2near < 0 ) || ( bc1far < 0 && bc2far < 0 ) ) { 851 | 852 | // Both vertices lie entirely outside one of the clip planes. 853 | return false; 854 | 855 | } else { 856 | 857 | // The line segment spans at least one clip plane. 858 | 859 | if ( bc1near < 0 ) { 860 | 861 | // v1 lies outside the near plane, v2 inside 862 | alpha1 = Math.max( alpha1, bc1near / ( bc1near - bc2near ) ); 863 | 864 | } else if ( bc2near < 0 ) { 865 | 866 | // v2 lies outside the near plane, v1 inside 867 | alpha2 = Math.min( alpha2, bc1near / ( bc1near - bc2near ) ); 868 | 869 | } 870 | 871 | if ( bc1far < 0 ) { 872 | 873 | // v1 lies outside the far plane, v2 inside 874 | alpha1 = Math.max( alpha1, bc1far / ( bc1far - bc2far ) ); 875 | 876 | } else if ( bc2far < 0 ) { 877 | 878 | // v2 lies outside the far plane, v2 inside 879 | alpha2 = Math.min( alpha2, bc1far / ( bc1far - bc2far ) ); 880 | 881 | } 882 | 883 | if ( alpha2 < alpha1 ) { 884 | 885 | // The line segment spans two boundaries, but is outside both of them. 886 | // (This can't happen when we're only clipping against just near/far but good 887 | // to leave the check here for future usage if other clip planes are added.) 888 | return false; 889 | 890 | } else { 891 | 892 | // Update the s1 and s2 vertices to match the clipped line segment. 893 | s1.lerp( s2, alpha1 ); 894 | s2.lerp( s1, 1 - alpha2 ); 895 | 896 | return true; 897 | 898 | } 899 | 900 | } 901 | 902 | } 903 | 904 | }; 905 | --------------------------------------------------------------------------------