1/** 2 * @author mrdoob / http://mrdoob.com/ 3 */ 4 5THREE.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 () {}; 13 14 this.setValues( parameters ); 15 16}; 17 18THREE.SpriteCanvasMaterial.prototype = Object.create( THREE.Material.prototype ); 19THREE.SpriteCanvasMaterial.prototype.constructor = THREE.SpriteCanvasMaterial; 20THREE.SpriteCanvasMaterial.prototype.isSpriteCanvasMaterial = true; 21 22THREE.SpriteCanvasMaterial.prototype.clone = function () { 23 24 var material = new THREE.SpriteCanvasMaterial(); 25 26 material.copy( this ); 27 material.color.copy( this.color ); 28 material.program = this.program; 29 30 return material; 31 32}; 33 34// 35 36THREE.CanvasRenderer = function ( parameters ) { 37 38 console.log( 'THREE.CanvasRenderer', THREE.REVISION ); 39 40 parameters = parameters || {}; 41 42 var _this = this, 43 _renderData, _elements, _lights, 44 _projector = new THREE.Projector(), 45 46 _canvas = parameters.canvas !== undefined 47 ? parameters.canvas 48 : document.createElement( 'canvas' ), 49 50 _canvasWidth = _canvas.width, 51 _canvasHeight = _canvas.height, 52 _canvasWidthHalf = Math.floor( _canvasWidth / 2 ), 53 _canvasHeightHalf = Math.floor( _canvasHeight / 2 ), 54 55 _viewportX = 0, 56 _viewportY = 0, 57 _viewportWidth = _canvasWidth, 58 _viewportHeight = _canvasHeight, 59 60 _pixelRatio = 1, 61 62 _context = _canvas.getContext( '2d', { 63 alpha: parameters.alpha === true 64 } ), 65 66 _clearColor = new THREE.Color( 0x000000 ), 67 _clearAlpha = parameters.alpha === true ? 0 : 1, 68 69 _contextGlobalAlpha = 1, 70 _contextGlobalCompositeOperation = 0, 71 _contextStrokeStyle = null, 72 _contextFillStyle = null, 73 _contextLineWidth = null, 74 _contextLineCap = null, 75 _contextLineJoin = null, 76 _contextLineDash = [], 77 78 _v1, _v2, _v3, 79 80 _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, 81 82 _color = new THREE.Color(), 83 84 _diffuseColor = new THREE.Color(), 85 _emissiveColor = new THREE.Color(), 86 87 _lightColor = new THREE.Color(), 88 89 _patterns = {}, 90 91 _uvs, 92 _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, 93 94 _clipBox = new THREE.Box2(), 95 _clearBox = new THREE.Box2(), 96 _elemBox = new THREE.Box2(), 97 98 _ambientLight = new THREE.Color(), 99 _directionalLights = new THREE.Color(), 100 _pointLights = new THREE.Color(), 101 102 _vector3 = new THREE.Vector3(), // Needed for PointLight 103 _centroid = new THREE.Vector3(), 104 _normal = new THREE.Vector3(), 105 _normalViewMatrix = new THREE.Matrix3(); 106 107 /* TODO 108 _canvas.mozImageSmoothingEnabled = false; 109 _canvas.webkitImageSmoothingEnabled = false; 110 _canvas.msImageSmoothingEnabled = false; 111 _canvas.imageSmoothingEnabled = false; 112 */ 113 114 // dash+gap fallbacks for Firefox and everything else 115 116 if ( _context.setLineDash === undefined ) { 117 118 _context.setLineDash = function () {}; 119 120 } 121 122 this.domElement = _canvas; 123 124 this.autoClear = true; 125 this.sortObjects = true; 126 this.sortElements = true; 127 128 this.info = { 129 130 render: { 131 132 vertices: 0, 133 faces: 0 134 135 } 136 137 }; 138 139 // WebGLRenderer compatibility 140 141 this.supportsVertexTextures = function () {}; 142 this.setFaceCulling = function () {}; 143 144 // API 145 146 this.getContext = function () { 147 148 return _context; 149 150 }; 151 152 this.getContextAttributes = function () { 153 154 return _context.getContextAttributes(); 155 156 }; 157 158 this.getPixelRatio = function () { 159 160 return _pixelRatio; 161 162 }; 163 164 this.setPixelRatio = function ( value ) { 165 166 if ( value !== undefined ) _pixelRatio = value; 167 168 }; 169 170 this.setSize = function ( width, height, updateStyle ) { 171 172 _canvasWidth = width * _pixelRatio; 173 _canvasHeight = height * _pixelRatio; 174 175 _canvas.width = _canvasWidth; 176 _canvas.height = _canvasHeight; 177 178 _canvasWidthHalf = Math.floor( _canvasWidth / 2 ); 179 _canvasHeightHalf = Math.floor( _canvasHeight / 2 ); 180 181 if ( updateStyle !== false ) { 182 183 _canvas.style.width = width + 'px'; 184 _canvas.style.height = height + 'px'; 185 186 } 187 188 _clipBox.min.set( - _canvasWidthHalf, - _canvasHeightHalf ); 189 _clipBox.max.set( _canvasWidthHalf, _canvasHeightHalf ); 190 191 _clearBox.min.set( - _canvasWidthHalf, - _canvasHeightHalf ); 192 _clearBox.max.set( _canvasWidthHalf, _canvasHeightHalf ); 193 194 _contextGlobalAlpha = 1; 195 _contextGlobalCompositeOperation = 0; 196 _contextStrokeStyle = null; 197 _contextFillStyle = null; 198 _contextLineWidth = null; 199 _contextLineCap = null; 200 _contextLineJoin = null; 201 202 this.setViewport( 0, 0, width, height ); 203 204 }; 205 206 this.setViewport = function ( x, y, width, height ) { 207 208 _viewportX = x * _pixelRatio; 209 _viewportY = y * _pixelRatio; 210 211 _viewportWidth = width * _pixelRatio; 212 _viewportHeight = height * _pixelRatio; 213 214 }; 215 216 this.setScissor = function () {}; 217 this.setScissorTest = function () {}; 218 219 this.setClearColor = function ( color, alpha ) { 220 221 _clearColor.set( color ); 222 _clearAlpha = alpha !== undefined ? alpha : 1; 223 224 _clearBox.min.set( - _canvasWidthHalf, - _canvasHeightHalf ); 225 _clearBox.max.set( _canvasWidthHalf, _canvasHeightHalf ); 226 227 }; 228 229 this.setClearColorHex = function ( hex, alpha ) { 230 231 console.warn( 'THREE.CanvasRenderer: .setClearColorHex() is being removed. Use .setClearColor() instead.' ); 232 this.setClearColor( hex, alpha ); 233 234 }; 235 236 this.getClearColor = function () { 237 238 return _clearColor; 239 240 }; 241 242 this.getClearAlpha = function () { 243 244 return _clearAlpha; 245 246 }; 247 248 this.getMaxAnisotropy = function () { 249 250 return 0; 251 252 }; 253 254 this.clear = function () { 255 256 if ( _clearBox.isEmpty() === false ) { 257 258 _clearBox.intersect( _clipBox ); 259 _clearBox.expandByScalar( 2 ); 260 261 _clearBox.min.x = _clearBox.min.x + _canvasWidthHalf; 262 _clearBox.min.y = - _clearBox.min.y + _canvasHeightHalf; // higher y value ! 263 _clearBox.max.x = _clearBox.max.x + _canvasWidthHalf; 264 _clearBox.max.y = - _clearBox.max.y + _canvasHeightHalf; // lower y value ! 265 266 if ( _clearAlpha < 1 ) { 267 268 _context.clearRect( 269 _clearBox.min.x | 0, 270 _clearBox.max.y | 0, 271 ( _clearBox.max.x - _clearBox.min.x ) | 0, 272 ( _clearBox.min.y - _clearBox.max.y ) | 0 273 ); 274 275 } 276 277 if ( _clearAlpha > 0 ) { 278 279 setOpacity( 1 ); 280 setBlending( THREE.NormalBlending ); 281 282 setFillStyle( 'rgba(' + Math.floor( _clearColor.r * 255 ) + ',' + Math.floor( _clearColor.g * 255 ) + ',' + Math.floor( _clearColor.b * 255 ) + ',' + _clearAlpha + ')' ); 283 284 _context.fillRect( 285 _clearBox.min.x | 0, 286 _clearBox.max.y | 0, 287 ( _clearBox.max.x - _clearBox.min.x ) | 0, 288 ( _clearBox.min.y - _clearBox.max.y ) | 0 289 ); 290 291 } 292 293 _clearBox.makeEmpty(); 294 295 } 296 297 }; 298 299 // compatibility 300 301 this.clearColor = function () {}; 302 this.clearDepth = function () {}; 303 this.clearStencil = function () {}; 304 305 this.render = function ( scene, camera ) { 306 307 if ( camera.isCamera === undefined ) { 308 309 console.error( 'THREE.CanvasRenderer.render: camera is not an instance of THREE.Camera.' ); 310 return; 311 312 } 313 314 var background = scene.background; 315 316 if ( background && background.isColor ) { 317 318 setOpacity( 1 ); 319 setBlending( THREE.NormalBlending ); 320 321 setFillStyle( background.getStyle() ); 322 _context.fillRect( 0, 0, _canvasWidth, _canvasHeight ); 323 324 } else if ( this.autoClear === true ) { 325 326 this.clear(); 327 328 } 329 330 _this.info.render.vertices = 0; 331 _this.info.render.faces = 0; 332 333 _context.setTransform( _viewportWidth / _canvasWidth, 0, 0, - _viewportHeight / _canvasHeight, _viewportX, _canvasHeight - _viewportY ); 334 _context.translate( _canvasWidthHalf, _canvasHeightHalf ); 335 336 _renderData = _projector.projectScene( scene, camera, this.sortObjects, this.sortElements ); 337 _elements = _renderData.elements; 338 _lights = _renderData.lights; 339 340 _normalViewMatrix.getNormalMatrix( camera.matrixWorldInverse ); 341 342 /* DEBUG 343 setFillStyle( 'rgba( 0, 255, 255, 0.5 )' ); 344 _context.fillRect( _clipBox.min.x, _clipBox.min.y, _clipBox.max.x - _clipBox.min.x, _clipBox.max.y - _clipBox.min.y ); 345 */ 346 347 calculateLights(); 348 349 for ( var e = 0, el = _elements.length; e < el; e ++ ) { 350 351 var element = _elements[ e ]; 352 353 var material = element.material; 354 355 if ( material === undefined || material.opacity === 0 ) continue; 356 357 _elemBox.makeEmpty(); 358 359 if ( element instanceof THREE.RenderableSprite ) { 360 361 _v1 = element; 362 _v1.x *= _canvasWidthHalf; _v1.y *= _canvasHeightHalf; 363 364 renderSprite( _v1, element, material ); 365 366 } else if ( element instanceof THREE.RenderableLine ) { 367 368 _v1 = element.v1; _v2 = element.v2; 369 370 _v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf; 371 _v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf; 372 373 _elemBox.setFromPoints( [ 374 _v1.positionScreen, 375 _v2.positionScreen 376 ] ); 377 378 if ( _clipBox.intersectsBox( _elemBox ) === true ) { 379 380 renderLine( _v1, _v2, element, material ); 381 382 } 383 384 } else if ( element instanceof THREE.RenderableFace ) { 385 386 _v1 = element.v1; _v2 = element.v2; _v3 = element.v3; 387 388 if ( _v1.positionScreen.z < - 1 || _v1.positionScreen.z > 1 ) continue; 389 if ( _v2.positionScreen.z < - 1 || _v2.positionScreen.z > 1 ) continue; 390 if ( _v3.positionScreen.z < - 1 || _v3.positionScreen.z > 1 ) continue; 391 392 _v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf; 393 _v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf; 394 _v3.positionScreen.x *= _canvasWidthHalf; _v3.positionScreen.y *= _canvasHeightHalf; 395 396 if ( material.overdraw > 0 ) { 397 398 expand( _v1.positionScreen, _v2.positionScreen, material.overdraw ); 399 expand( _v2.positionScreen, _v3.positionScreen, material.overdraw ); 400 expand( _v3.positionScreen, _v1.positionScreen, material.overdraw ); 401 402 } 403 404 _elemBox.setFromPoints( [ 405 _v1.positionScreen, 406 _v2.positionScreen, 407 _v3.positionScreen 408 ] ); 409 410 if ( _clipBox.intersectsBox( _elemBox ) === true ) { 411 412 renderFace3( _v1, _v2, _v3, 0, 1, 2, element, material ); 413 414 } 415 416 } 417 418 /* DEBUG 419 setLineWidth( 1 ); 420 setStrokeStyle( 'rgba( 0, 255, 0, 0.5 )' ); 421 _context.strokeRect( _elemBox.min.x, _elemBox.min.y, _elemBox.max.x - _elemBox.min.x, _elemBox.max.y - _elemBox.min.y ); 422 */ 423 424 _clearBox.union( _elemBox ); 425 426 } 427 428 /* DEBUG 429 setLineWidth( 1 ); 430 setStrokeStyle( 'rgba( 255, 0, 0, 0.5 )' ); 431 _context.strokeRect( _clearBox.min.x, _clearBox.min.y, _clearBox.max.x - _clearBox.min.x, _clearBox.max.y - _clearBox.min.y ); 432 */ 433 434 _context.setTransform( 1, 0, 0, 1, 0, 0 ); 435 436 }; 437 438 // 439 440 function calculateLights() { 441 442 _ambientLight.setRGB( 0, 0, 0 ); 443 _directionalLights.setRGB( 0, 0, 0 ); 444 _pointLights.setRGB( 0, 0, 0 ); 445 446 for ( var l = 0, ll = _lights.length; l < ll; l ++ ) { 447 448 var light = _lights[ l ]; 449 var lightColor = light.color; 450 451 if ( light.isAmbientLight ) { 452 453 _ambientLight.add( lightColor ); 454 455 } else if ( light.isDirectionalLight ) { 456 457 // for sprites 458 459 _directionalLights.add( lightColor ); 460 461 } else if ( light.isPointLight ) { 462 463 // for sprites 464 465 _pointLights.add( lightColor ); 466 467 } 468 469 } 470 471 } 472 473 function calculateLight( position, normal, color ) { 474 475 for ( var l = 0, ll = _lights.length; l < ll; l ++ ) { 476 477 var light = _lights[ l ]; 478 479 _lightColor.copy( light.color ); 480 481 if ( light.isDirectionalLight ) { 482 483 var lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld ).normalize(); 484 485 var amount = normal.dot( lightPosition ); 486 487 if ( amount <= 0 ) continue; 488 489 amount *= light.intensity; 490 491 color.add( _lightColor.multiplyScalar( amount ) ); 492 493 } else if ( light.isPointLight ) { 494 495 var lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld ); 496 497 var amount = normal.dot( _vector3.subVectors( lightPosition, position ).normalize() ); 498 499 if ( amount <= 0 ) continue; 500 501 amount *= light.distance == 0 ? 1 : 1 - Math.min( position.distanceTo( lightPosition ) / light.distance, 1 ); 502 503 if ( amount == 0 ) continue; 504 505 amount *= light.intensity; 506 507 color.add( _lightColor.multiplyScalar( amount ) ); 508 509 } 510 511 } 512 513 } 514 515 function renderSprite( v1, element, material ) { 516 517 setOpacity( material.opacity ); 518 setBlending( material.blending ); 519 520 var scaleX = element.scale.x * _canvasWidthHalf; 521 var scaleY = element.scale.y * _canvasHeightHalf; 522 523 var dist = Math.sqrt( scaleX * scaleX + scaleY * scaleY ); // allow for rotated sprite 524 _elemBox.min.set( v1.x - dist, v1.y - dist ); 525 _elemBox.max.set( v1.x + dist, v1.y + dist ); 526 527 if ( material.isSpriteMaterial ) { 528 529 var texture = material.map; 530 531 if ( texture !== null ) { 532 533 var pattern = _patterns[ texture.id ]; 534 535 if ( pattern === undefined || pattern.version !== texture.version ) { 536 537 pattern = textureToPattern( texture ); 538 _patterns[ texture.id ] = pattern; 539 540 } 541 542 if ( pattern.canvas !== undefined ) { 543 544 setFillStyle( pattern.canvas ); 545 546 var bitmap = texture.image; 547 548 var ox = bitmap.width * texture.offset.x; 549 var oy = bitmap.height * texture.offset.y; 550 551 var sx = bitmap.width * texture.repeat.x; 552 var sy = bitmap.height * texture.repeat.y; 553 554 var cx = scaleX / sx; 555 var cy = scaleY / sy; 556 557 _context.save(); 558 _context.translate( v1.x, v1.y ); 559 if ( material.rotation !== 0 ) _context.rotate( material.rotation ); 560 _context.translate( - scaleX / 2, - scaleY / 2 ); 561 _context.scale( cx, cy ); 562 _context.translate( - ox, - oy ); 563 _context.fillRect( ox, oy, sx, sy ); 564 _context.restore(); 565 566 } 567 568 } else { 569 570 // no texture 571 572 setFillStyle( material.color.getStyle() ); 573 574 _context.save(); 575 _context.translate( v1.x, v1.y ); 576 if ( material.rotation !== 0 ) _context.rotate( material.rotation ); 577 _context.scale( scaleX, - scaleY ); 578 _context.fillRect( - 0.5, - 0.5, 1, 1 ); 579 _context.restore(); 580 581 } 582 583 } else if ( material.isSpriteCanvasMaterial ) { 584 585 setStrokeStyle( material.color.getStyle() ); 586 setFillStyle( material.color.getStyle() ); 587 588 _context.save(); 589 _context.translate( v1.x, v1.y ); 590 if ( material.rotation !== 0 ) _context.rotate( material.rotation ); 591 _context.scale( scaleX, scaleY ); 592 593 material.program( _context ); 594 595 _context.restore(); 596 597 } else if ( material.isPointsMaterial ) { 598 599 setFillStyle( material.color.getStyle() ); 600 601 _context.save(); 602 _context.translate( v1.x, v1.y ); 603 if ( material.rotation !== 0 ) _context.rotate( material.rotation ); 604 _context.scale( scaleX * material.size, - scaleY * material.size ); 605 _context.fillRect( - 0.5, - 0.5, 1, 1 ); 606 _context.restore(); 607 608 } 609 610 /* DEBUG 611 setStrokeStyle( 'rgb(255,255,0)' ); 612 _context.beginPath(); 613 _context.moveTo( v1.x - 10, v1.y ); 614 _context.lineTo( v1.x + 10, v1.y ); 615 _context.moveTo( v1.x, v1.y - 10 ); 616 _context.lineTo( v1.x, v1.y + 10 ); 617 _context.stroke(); 618 */ 619 620 } 621 622 function renderLine( v1, v2, element, material ) { 623 624 setOpacity( material.opacity ); 625 setBlending( material.blending ); 626 627 _context.beginPath(); 628 _context.moveTo( v1.positionScreen.x, v1.positionScreen.y ); 629 _context.lineTo( v2.positionScreen.x, v2.positionScreen.y ); 630 631 if ( material.isLineBasicMaterial ) { 632 633 setLineWidth( material.linewidth ); 634 setLineCap( material.linecap ); 635 setLineJoin( material.linejoin ); 636 637 if ( material.vertexColors !== THREE.VertexColors ) { 638 639 setStrokeStyle( material.color.getStyle() ); 640 641 } else { 642 643 var colorStyle1 = element.vertexColors[ 0 ].getStyle(); 644 var colorStyle2 = element.vertexColors[ 1 ].getStyle(); 645 646 if ( colorStyle1 === colorStyle2 ) { 647 648 setStrokeStyle( colorStyle1 ); 649 650 } else { 651 652 try { 653 654 var grad = _context.createLinearGradient( 655 v1.positionScreen.x, 656 v1.positionScreen.y, 657 v2.positionScreen.x, 658 v2.positionScreen.y 659 ); 660 grad.addColorStop( 0, colorStyle1 ); 661 grad.addColorStop( 1, colorStyle2 ); 662 663 } catch ( exception ) { 664 665 grad = colorStyle1; 666 667 } 668 669 setStrokeStyle( grad ); 670 671 } 672 673 } 674 675 if ( material.isLineDashedMaterial ) { 676 677 setLineDash( [ material.dashSize, material.gapSize ] ); 678 679 } 680 681 _context.stroke(); 682 _elemBox.expandByScalar( material.linewidth * 2 ); 683 684 if ( material.isLineDashedMaterial ) { 685 686 setLineDash( [] ); 687 688 } 689 690 } 691 692 } 693 694 function renderFace3( v1, v2, v3, uv1, uv2, uv3, element, material ) { 695 696 _this.info.render.vertices += 3; 697 _this.info.render.faces ++; 698 699 setOpacity( material.opacity ); 700 setBlending( material.blending ); 701 702 _v1x = v1.positionScreen.x; _v1y = v1.positionScreen.y; 703 _v2x = v2.positionScreen.x; _v2y = v2.positionScreen.y; 704 _v3x = v3.positionScreen.x; _v3y = v3.positionScreen.y; 705 706 drawTriangle( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y ); 707 708 if ( ( material.isMeshLambertMaterial || material.isMeshPhongMaterial || material.isMeshStandardMaterial ) && material.map === null ) { 709 710 _diffuseColor.copy( material.color ); 711 _emissiveColor.copy( material.emissive ); 712 713 if ( material.vertexColors === THREE.FaceColors ) { 714 715 _diffuseColor.multiply( element.color ); 716 717 } 718 719 _color.copy( _ambientLight ); 720 721 _centroid.copy( v1.positionWorld ).add( v2.positionWorld ).add( v3.positionWorld ).divideScalar( 3 ); 722 723 calculateLight( _centroid, element.normalModel, _color ); 724 725 _color.multiply( _diffuseColor ).add( _emissiveColor ); 726 727 material.wireframe === true 728 ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) 729 : fillPath( _color ); 730 731 } else if ( material.isMeshBasicMaterial || material.isMeshLambertMaterial || material.isMeshPhongMaterial || material.isMeshStandardMaterial ) { 732 733 if ( material.map !== null ) { 734 735 var mapping = material.map.mapping; 736 737 if ( mapping === THREE.UVMapping ) { 738 739 _uvs = element.uvs; 740 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 ); 741 742 } 743 744 } else if ( material.envMap !== null ) { 745 746 if ( material.envMap.mapping === THREE.SphericalReflectionMapping ) { 747 748 _normal.copy( element.vertexNormalsModel[ uv1 ] ).applyMatrix3( _normalViewMatrix ); 749 _uv1x = 0.5 * _normal.x + 0.5; 750 _uv1y = 0.5 * _normal.y + 0.5; 751 752 _normal.copy( element.vertexNormalsModel[ uv2 ] ).applyMatrix3( _normalViewMatrix ); 753 _uv2x = 0.5 * _normal.x + 0.5; 754 _uv2y = 0.5 * _normal.y + 0.5; 755 756 _normal.copy( element.vertexNormalsModel[ uv3 ] ).applyMatrix3( _normalViewMatrix ); 757 _uv3x = 0.5 * _normal.x + 0.5; 758 _uv3y = 0.5 * _normal.y + 0.5; 759 760 patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, material.envMap ); 761 762 } 763 764 } else { 765 766 _color.copy( material.color ); 767 768 if ( material.vertexColors === THREE.FaceColors ) { 769 770 _color.multiply( element.color ); 771 772 } 773 774 material.wireframe === true 775 ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) 776 : fillPath( _color ); 777 778 } 779 780 } else if ( material.isMeshNormalMaterial ) { 781 782 _normal.copy( element.normalModel ).applyMatrix3( _normalViewMatrix ); 783 784 _color.setRGB( _normal.x, _normal.y, _normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 ); 785 786 material.wireframe === true 787 ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) 788 : fillPath( _color ); 789 790 } else { 791 792 _color.setRGB( 1, 1, 1 ); 793 794 material.wireframe === true 795 ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) 796 : fillPath( _color ); 797 798 } 799 800 } 801 802 // 803 804 function drawTriangle( x0, y0, x1, y1, x2, y2 ) { 805 806 _context.beginPath(); 807 _context.moveTo( x0, y0 ); 808 _context.lineTo( x1, y1 ); 809 _context.lineTo( x2, y2 ); 810 _context.closePath(); 811 812 } 813 814 function strokePath( color, linewidth, linecap, linejoin ) { 815 816 setLineWidth( linewidth ); 817 setLineCap( linecap ); 818 setLineJoin( linejoin ); 819 setStrokeStyle( color.getStyle() ); 820 821 _context.stroke(); 822 823 _elemBox.expandByScalar( linewidth * 2 ); 824 825 } 826 827 function fillPath( color ) { 828 829 setFillStyle( color.getStyle() ); 830 _context.fill(); 831 832 } 833 834 function textureToPattern( texture ) { 835 836 if ( texture.version === 0 || 837 texture instanceof THREE.CompressedTexture || 838 texture instanceof THREE.DataTexture ) { 839 840 return { 841 canvas: undefined, 842 version: texture.version 843 }; 844 845 } 846 847 var image = texture.image; 848 849 if ( image.complete === false ) { 850 851 return { 852 canvas: undefined, 853 version: 0 854 }; 855 856 } 857 858 var repeatX = texture.wrapS === THREE.RepeatWrapping || texture.wrapS === THREE.MirroredRepeatWrapping; 859 var repeatY = texture.wrapT === THREE.RepeatWrapping || texture.wrapT === THREE.MirroredRepeatWrapping; 860 861 var mirrorX = texture.wrapS === THREE.MirroredRepeatWrapping; 862 var mirrorY = texture.wrapT === THREE.MirroredRepeatWrapping; 863 864 // 865 866 var canvas = document.createElement( 'canvas' ); 867 canvas.width = image.width * ( mirrorX ? 2 : 1 ); 868 canvas.height = image.height * ( mirrorY ? 2 : 1 ); 869 870 var context = canvas.getContext( '2d' ); 871 context.setTransform( 1, 0, 0, - 1, 0, image.height ); 872 context.drawImage( image, 0, 0 ); 873 874 if ( mirrorX === true ) { 875 876 context.setTransform( - 1, 0, 0, - 1, image.width, image.height ); 877 context.drawImage( image, - image.width, 0 ); 878 879 } 880 881 if ( mirrorY === true ) { 882 883 context.setTransform( 1, 0, 0, 1, 0, 0 ); 884 context.drawImage( image, 0, image.height ); 885 886 } 887 888 if ( mirrorX === true && mirrorY === true ) { 889 890 context.setTransform( - 1, 0, 0, 1, image.width, 0 ); 891 context.drawImage( image, - image.width, image.height ); 892 893 } 894 895 var repeat = 'no-repeat'; 896 897 if ( repeatX === true && repeatY === true ) { 898 899 repeat = 'repeat'; 900 901 } else if ( repeatX === true ) { 902 903 repeat = 'repeat-x'; 904 905 } else if ( repeatY === true ) { 906 907 repeat = 'repeat-y'; 908 909 } 910 911 var pattern = _context.createPattern( canvas, repeat ); 912 913 if ( texture.onUpdate ) texture.onUpdate( texture ); 914 915 return { 916 canvas: pattern, 917 version: texture.version 918 }; 919 920 } 921 922 function patternPath( x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2, texture ) { 923 924 var pattern = _patterns[ texture.id ]; 925 926 if ( pattern === undefined || pattern.version !== texture.version ) { 927 928 pattern = textureToPattern( texture ); 929 _patterns[ texture.id ] = pattern; 930 931 } 932 933 if ( pattern.canvas !== undefined ) { 934 935 setFillStyle( pattern.canvas ); 936 937 } else { 938 939 setFillStyle( 'rgba( 0, 0, 0, 1)' ); 940 _context.fill(); 941 return; 942 943 } 944 945 // http://extremelysatisfactorytotalitarianism.com/blog/?p=2120 946 947 var a, b, c, d, e, f, det, idet, 948 offsetX = texture.offset.x / texture.repeat.x, 949 offsetY = texture.offset.y / texture.repeat.y, 950 width = texture.image.width * texture.repeat.x, 951 height = texture.image.height * texture.repeat.y; 952 953 u0 = ( u0 + offsetX ) * width; 954 v0 = ( v0 + offsetY ) * height; 955 956 u1 = ( u1 + offsetX ) * width; 957 v1 = ( v1 + offsetY ) * height; 958 959 u2 = ( u2 + offsetX ) * width; 960 v2 = ( v2 + offsetY ) * height; 961 962 x1 -= x0; y1 -= y0; 963 x2 -= x0; y2 -= y0; 964 965 u1 -= u0; v1 -= v0; 966 u2 -= u0; v2 -= v0; 967 968 det = u1 * v2 - u2 * v1; 969 970 if ( det === 0 ) return; 971 972 idet = 1 / det; 973 974 a = ( v2 * x1 - v1 * x2 ) * idet; 975 b = ( v2 * y1 - v1 * y2 ) * idet; 976 c = ( u1 * x2 - u2 * x1 ) * idet; 977 d = ( u1 * y2 - u2 * y1 ) * idet; 978 979 e = x0 - a * u0 - c * v0; 980 f = y0 - b * u0 - d * v0; 981 982 _context.save(); 983 _context.transform( a, b, c, d, e, f ); 984 _context.fill(); 985 _context.restore(); 986 987 } 988 989 /* 990 function clipImage( x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2, image ) { 991 992 // http://extremelysatisfactorytotalitarianism.com/blog/?p=2120 993 994 var a, b, c, d, e, f, det, idet, 995 width = image.width - 1, 996 height = image.height - 1; 997 998 u0 *= width; v0 *= height; 999 u1 *= width; v1 *= height; 1000 u2 *= width; v2 *= height; 1001 1002 x1 -= x0; y1 -= y0; 1003 x2 -= x0; y2 -= y0; 1004 1005 u1 -= u0; v1 -= v0; 1006 u2 -= u0; v2 -= v0; 1007 1008 det = u1 * v2 - u2 * v1; 1009 1010 idet = 1 / det; 1011 1012 a = ( v2 * x1 - v1 * x2 ) * idet; 1013 b = ( v2 * y1 - v1 * y2 ) * idet; 1014 c = ( u1 * x2 - u2 * x1 ) * idet; 1015 d = ( u1 * y2 - u2 * y1 ) * idet; 1016 1017 e = x0 - a * u0 - c * v0; 1018 f = y0 - b * u0 - d * v0; 1019 1020 _context.save(); 1021 _context.transform( a, b, c, d, e, f ); 1022 _context.clip(); 1023 _context.drawImage( image, 0, 0 ); 1024 _context.restore(); 1025 1026 } 1027 */ 1028 1029 // Hide anti-alias gaps 1030 1031 function expand( v1, v2, pixels ) { 1032 1033 var x = v2.x - v1.x, y = v2.y - v1.y, 1034 det = x * x + y * y, idet; 1035 1036 if ( det === 0 ) return; 1037 1038 idet = pixels / Math.sqrt( det ); 1039 1040 x *= idet; y *= idet; 1041 1042 v2.x += x; v2.y += y; 1043 v1.x -= x; v1.y -= y; 1044 1045 } 1046 1047 // Context cached methods. 1048 1049 function setOpacity( value ) { 1050 1051 if ( _contextGlobalAlpha !== value ) { 1052 1053 _context.globalAlpha = value; 1054 _contextGlobalAlpha = value; 1055 1056 } 1057 1058 } 1059 1060 function setBlending( value ) { 1061 1062 if ( _contextGlobalCompositeOperation !== value ) { 1063 1064 if ( value === THREE.NormalBlending ) { 1065 1066 _context.globalCompositeOperation = 'source-over'; 1067 1068 } else if ( value === THREE.AdditiveBlending ) { 1069 1070 _context.globalCompositeOperation = 'lighter'; 1071 1072 } else if ( value === THREE.SubtractiveBlending ) { 1073 1074 _context.globalCompositeOperation = 'darker'; 1075 1076 } else if ( value === THREE.MultiplyBlending ) { 1077 1078 _context.globalCompositeOperation = 'multiply'; 1079 1080 } 1081 1082 _contextGlobalCompositeOperation = value; 1083 1084 } 1085 1086 } 1087 1088 function setLineWidth( value ) { 1089 1090 if ( _contextLineWidth !== value ) { 1091 1092 _context.lineWidth = value; 1093 _contextLineWidth = value; 1094 1095 } 1096 1097 } 1098 1099 function setLineCap( value ) { 1100 1101 // "butt", "round", "square" 1102 1103 if ( _contextLineCap !== value ) { 1104 1105 _context.lineCap = value; 1106 _contextLineCap = value; 1107 1108 } 1109 1110 } 1111 1112 function setLineJoin( value ) { 1113 1114 // "round", "bevel", "miter" 1115 1116 if ( _contextLineJoin !== value ) { 1117 1118 _context.lineJoin = value; 1119 _contextLineJoin = value; 1120 1121 } 1122 1123 } 1124 1125 function setStrokeStyle( value ) { 1126 1127 if ( _contextStrokeStyle !== value ) { 1128 1129 _context.strokeStyle = value; 1130 _contextStrokeStyle = value; 1131 1132 } 1133 1134 } 1135 1136 function setFillStyle( value ) { 1137 1138 if ( _contextFillStyle !== value ) { 1139 1140 _context.fillStyle = value; 1141 _contextFillStyle = value; 1142 1143 } 1144 1145 } 1146 1147 function setLineDash( value ) { 1148 1149 if ( _contextLineDash.length !== value.length ) { 1150 1151 _context.setLineDash( value ); 1152 _contextLineDash = value; 1153 1154 } 1155 1156 } 1157 1158};