SVGRenderer.js 14.2 KB
Newer Older
M
Mr.doob 已提交
1
/**
2
 * @author mrdoob / http://mrdoob.com/
M
Mr.doob 已提交
3 4
 */

M
Mr.doob 已提交
5
THREE.SVGRenderer = function () {
6

M
Mr.doob 已提交
7
	console.log( 'THREE.SVGRenderer', THREE.REVISION );
8

9
	var _this = this,
10
	_renderData, _elements, _lights,
M
Mr.doob 已提交
11 12
	_projector = new THREE.Projector(),
	_svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'),
M
Mr.doob 已提交
13
	_svgWidth, _svgHeight, _svgWidthHalf, _svgHeightHalf,
14 15 16

	_v1, _v2, _v3, _v4,

17 18
	_clipRect = new THREE.Rectangle(),
	_bboxRect = new THREE.Rectangle(),
M
Mr.doob 已提交
19

20
	_enableLighting = false,
21
	_color = new THREE.Color(),
22 23
	_diffuseColor = new THREE.Color(),
	_emissiveColor = new THREE.Color(),
24 25 26
	_ambientLight = new THREE.Color(),
	_directionalLights = new THREE.Color(),
	_pointLights = new THREE.Color(),
M
Mr.doob 已提交
27

M
Mr.doob 已提交
28
	_w, // z-buffer to w-buffer
M
Mr.doob 已提交
29 30
	_vector3 = new THREE.Vector3(), // Needed for PointLight

31 32
	_svgPathPool = [], _svgCirclePool = [], _svgLinePool = [],
	_svgNode, _pathCount, _circleCount, _lineCount,
33 34
	_quality = 1;

35
	this.domElement = _svg;
M
Mr.doob 已提交
36

M
Mr.doob 已提交
37
	this.autoClear = true;
M
Mr.doob 已提交
38 39
	this.sortObjects = true;
	this.sortElements = true;
M
Mr.doob 已提交
40

M
Mr.doob 已提交
41
	this.info = {
42

M
Mr.doob 已提交
43 44 45 46 47 48
		render: {

			vertices: 0,
			faces: 0

		}
49 50 51

	}

52 53 54 55 56 57 58 59
	this.setQuality = function( quality ) {

		switch(quality) {

			case "high": _quality = 1; break;
			case "low": _quality = 0; break;

		}
M
Mr.doob 已提交
60 61

	};
62

63
	this.setSize = function( width, height ) {
64

M
Mr.doob 已提交
65 66
		_svgWidth = width; _svgHeight = height;
		_svgWidthHalf = _svgWidth / 2; _svgHeightHalf = _svgHeight / 2;
67

M
Mr.doob 已提交
68 69 70
		_svg.setAttribute( 'viewBox', ( - _svgWidthHalf ) + ' ' + ( - _svgHeightHalf ) + ' ' + _svgWidth + ' ' + _svgHeight );
		_svg.setAttribute( 'width', _svgWidth );
		_svg.setAttribute( 'height', _svgHeight );
71

M
Mr.doob 已提交
72
		_clipRect.set( - _svgWidthHalf, - _svgHeightHalf, _svgWidthHalf, _svgHeightHalf );
73

M
Mr.doob 已提交
74
	};
75

M
Mr.doob 已提交
76 77
	this.clear = function () {

78
		while ( _svg.childNodes.length > 0 ) {
M
Mr.doob 已提交
79

80
			_svg.removeChild( _svg.childNodes[ 0 ] );
M
Mr.doob 已提交
81 82 83 84 85

		}

	};

86
	this.render = function ( scene, camera ) {
87

M
Mr.doob 已提交
88 89 90 91 92 93 94
		if ( camera instanceof THREE.Camera === false ) {

			console.error( 'THREE.SVGRenderer.render: camera is not an instance of THREE.Camera.' );
			return;

		}

95
		var e, el, element, material;
96

97
		this.autoClear && this.clear();
98

M
Mr.doob 已提交
99 100
		_this.info.render.vertices = 0;
		_this.info.render.faces = 0;
101

102
		_renderData = _projector.projectScene( scene, camera, this.sortObjects, this.sortElements );
103 104
		_elements = _renderData.elements;
		_lights = _renderData.lights;
105

106
		_pathCount = 0; _circleCount = 0; _lineCount = 0;
107

108
		_enableLighting = _lights.length > 0;
109 110 111

		if ( _enableLighting ) {

112
			 calculateLights( _lights );
113 114

		}
115

116
		for ( e = 0, el = _elements.length; e < el; e ++ ) {
M
Mr.doob 已提交
117

118
			element = _elements[ e ];
119

120 121
			material = element.material;

122
			if ( material === undefined || material.visible === false ) continue;
123

124
			_bboxRect.empty();
125

126
			if ( element instanceof THREE.RenderableParticle ) {
127

128 129
				_v1 = element;
				_v1.x *= _svgWidthHalf; _v1.y *= -_svgHeightHalf;
130

131
				renderParticle( _v1, element, material, scene );
132

133
			} else if ( element instanceof THREE.RenderableLine ) {
134

135
				_v1 = element.v1; _v2 = element.v2;
136

137 138
				_v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf;
				_v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf;
139

140 141
				_bboxRect.addPoint( _v1.positionScreen.x, _v1.positionScreen.y );
				_bboxRect.addPoint( _v2.positionScreen.x, _v2.positionScreen.y );
142

M
Mr.doob 已提交
143
				if ( !_clipRect.intersects( _bboxRect ) ) {
144

145
					continue;
146

147
				}
148

149
				renderLine( _v1, _v2, element, material, scene );
150

151
			} else if ( element instanceof THREE.RenderableFace3 ) {
152

153
				_v1 = element.v1; _v2 = element.v2; _v3 = element.v3;
154

155 156 157 158 159 160 161
				_v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf;
				_v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf;
				_v3.positionScreen.x *= _svgWidthHalf; _v3.positionScreen.y *= - _svgHeightHalf;

				_bboxRect.addPoint( _v1.positionScreen.x, _v1.positionScreen.y );
				_bboxRect.addPoint( _v2.positionScreen.x, _v2.positionScreen.y );
				_bboxRect.addPoint( _v3.positionScreen.x, _v3.positionScreen.y );
162

M
Mr.doob 已提交
163
				if ( !_clipRect.intersects( _bboxRect ) ) {
164

165
					continue;
166

M
Mr.doob 已提交
167
				}
168

169
				renderFace3( _v1, _v2, _v3, element, material, scene );
170

171
			} else if ( element instanceof THREE.RenderableFace4 ) {
M
Mr.doob 已提交
172

173 174 175 176 177 178
				_v1 = element.v1; _v2 = element.v2; _v3 = element.v3; _v4 = element.v4;

				_v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= -_svgHeightHalf;
				_v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= -_svgHeightHalf;
				_v3.positionScreen.x *= _svgWidthHalf; _v3.positionScreen.y *= -_svgHeightHalf;
				_v4.positionScreen.x *= _svgWidthHalf; _v4.positionScreen.y *= -_svgHeightHalf;
179

180 181 182 183
				_bboxRect.addPoint( _v1.positionScreen.x, _v1.positionScreen.y );
				_bboxRect.addPoint( _v2.positionScreen.x, _v2.positionScreen.y );
				_bboxRect.addPoint( _v3.positionScreen.x, _v3.positionScreen.y );
				_bboxRect.addPoint( _v4.positionScreen.x, _v4.positionScreen.y );
184

M
Mr.doob 已提交
185
				if ( !_clipRect.intersects( _bboxRect) ) {
186

187
					continue;
188

189
				}
190

191
				renderFace4( _v1, _v2, _v3, _v4, element, material, scene );
192

M
Mr.doob 已提交
193
			}
194 195 196

		}

M
Mr.doob 已提交
197
	};
198

199
	function calculateLights( lights ) {
M
Mr.doob 已提交
200

201
		var l, ll, light, lightColor;
M
Mr.doob 已提交
202

M
Mr.doob 已提交
203
		_ambientLight.setRGB( 0, 0, 0 );
204 205
		_directionalLights.setRGB( 0, 0, 0 );
		_pointLights.setRGB( 0, 0, 0 );
M
Mr.doob 已提交
206

207
		for ( l = 0, ll = lights.length; l < ll; l++ ) {
M
Mr.doob 已提交
208

209 210
			light = lights[ l ];
			lightColor = light.color;
M
Mr.doob 已提交
211

M
Mr.doob 已提交
212 213 214 215 216 217 218
			if ( light instanceof THREE.AmbientLight ) {

				_ambientLight.r += lightColor.r;
				_ambientLight.g += lightColor.g;
				_ambientLight.b += lightColor.b;

			} else if ( light instanceof THREE.DirectionalLight ) {
219

220 221 222
				_directionalLights.r += lightColor.r;
				_directionalLights.g += lightColor.g;
				_directionalLights.b += lightColor.b;
223 224 225

			} else if ( light instanceof THREE.PointLight ) {

226 227 228
				_pointLights.r += lightColor.r;
				_pointLights.g += lightColor.g;
				_pointLights.b += lightColor.b;
229 230 231 232 233 234 235

			}

		}

	}

236
	function calculateLight( lights, position, normal, color ) {
M
Mr.doob 已提交
237

238
		var l, ll, light, lightColor, lightPosition, amount;
M
Mr.doob 已提交
239

240
		for ( l = 0, ll = lights.length; l < ll; l ++ ) {
M
Mr.doob 已提交
241

242 243
			light = lights[ l ];
			lightColor = light.color;
M
Mr.doob 已提交
244 245 246

			if ( light instanceof THREE.DirectionalLight ) {

247
				lightPosition = light.matrixWorld.getPosition().normalize();
M
Mr.doob 已提交
248

249
				amount = normal.dot( lightPosition );
M
Mr.doob 已提交
250

251
				if ( amount <= 0 ) continue;
M
Mr.doob 已提交
252

253 254 255 256 257
				amount *= light.intensity;

				color.r += lightColor.r * amount;
				color.g += lightColor.g * amount;
				color.b += lightColor.b * amount;
M
Mr.doob 已提交
258 259 260

			} else if ( light instanceof THREE.PointLight ) {

261
				lightPosition = light.matrixWorld.getPosition();
M
Mr.doob 已提交
262

263
				amount = normal.dot( _vector3.sub( lightPosition, position ).normalize() );
M
Mr.doob 已提交
264

265
				if ( amount <= 0 ) continue;
M
Mr.doob 已提交
266

267
				amount *= light.distance == 0 ? 1 : 1 - Math.min( position.distanceTo( lightPosition ) / light.distance, 1 );
M
Mr.doob 已提交
268

269 270 271 272 273 274 275
				if ( amount == 0 ) continue;

				amount *= light.intensity;

				color.r += lightColor.r * amount;
				color.g += lightColor.g * amount;
				color.b += lightColor.b * amount;
M
Mr.doob 已提交
276 277 278 279 280 281 282

			}

		}

	}

283
	function renderParticle( v1, element, material, scene ) {
284

285
		/*
286
		_svgNode = getCircleNode( _circleCount++ );
287 288
		_svgNode.setAttribute( 'cx', v1.x );
		_svgNode.setAttribute( 'cy', v1.y );
M
Mr.doob 已提交
289
		_svgNode.setAttribute( 'r', element.scale.x * _svgWidthHalf );
290 291 292 293 294

		if ( material instanceof THREE.ParticleCircleMaterial ) {

			if ( _enableLighting ) {

295 296 297
				_color.r = _ambientLight.r + _directionalLights.r + _pointLights.r;
				_color.g = _ambientLight.g + _directionalLights.g + _pointLights.g;
				_color.b = _ambientLight.b + _directionalLights.b + _pointLights.b;
298

299 300 301
				_color.r = material.color.r * _color.r;
				_color.g = material.color.g * _color.g;
				_color.b = material.color.b * _color.b;
302 303 304 305 306 307 308 309 310 311 312 313 314 315

				_color.updateStyleString();

			} else {

				_color = material.color;

			}

			_svgNode.setAttribute( 'style', 'fill: ' + _color.__styleString );

		}

		_svg.appendChild( _svgNode );
316
		*/
317 318

	}
M
Mr.doob 已提交
319

320
	function renderLine ( v1, v2, element, material, scene ) {
321

322
		_svgNode = getLineNode( _lineCount ++ );
M
Mr.doob 已提交
323

324 325 326 327 328
		_svgNode.setAttribute( 'x1', v1.positionScreen.x );
		_svgNode.setAttribute( 'y1', v1.positionScreen.y );
		_svgNode.setAttribute( 'x2', v2.positionScreen.x );
		_svgNode.setAttribute( 'y2', v2.positionScreen.y );

329
		if ( material instanceof THREE.LineBasicMaterial ) {
330

331
			_svgNode.setAttribute( 'style', 'fill: none; stroke: ' + material.color.getStyle() + '; stroke-width: ' + material.linewidth + '; stroke-opacity: ' + material.opacity + '; stroke-linecap: ' + material.linecap + '; stroke-linejoin: ' + material.linejoin );
332

333
			_svg.appendChild( _svgNode );
334

335
		}
336

337
	}
M
Mr.doob 已提交
338

339
	function renderFace3( v1, v2, v3, element, material, scene ) {
340

M
Mr.doob 已提交
341 342
		_this.info.render.vertices += 3;
		_this.info.render.faces ++;
343

344
		_svgNode = getPathNode( _pathCount ++ );
345
		_svgNode.setAttribute( 'd', 'M ' + v1.positionScreen.x + ' ' + v1.positionScreen.y + ' L ' + v2.positionScreen.x + ' ' + v2.positionScreen.y + ' L ' + v3.positionScreen.x + ',' + v3.positionScreen.y + 'z' );
346

M
Mr.doob 已提交
347 348
		if ( material instanceof THREE.MeshBasicMaterial ) {

M
Mr.doob 已提交
349
			_color.copy( material.color );
M
Mr.doob 已提交
350

351 352 353 354 355 356 357 358
			if ( material.vertexColors === THREE.FaceColors ) {

				_color.r *= element.color.r;
				_color.g *= element.color.g;
				_color.b *= element.color.b;

			}

M
Mr.doob 已提交
359
		} else if ( material instanceof THREE.MeshLambertMaterial ) {
360

361 362
			_diffuseColor.copy( material.color );
			_emissiveColor.copy( material.emissive );
363

364 365 366 367 368 369 370 371 372
			if ( material.vertexColors === THREE.FaceColors ) {

				_diffuseColor.r *= element.color.r;
				_diffuseColor.g *= element.color.g;
				_diffuseColor.b *= element.color.b;

			}

			if ( _enableLighting ) {
373

374 375 376
				_color.r = _ambientLight.r;
				_color.g = _ambientLight.g;
				_color.b = _ambientLight.b;
377

378
				calculateLight( _lights, element.centroidWorld, element.normalWorld, _color );
379

380 381 382
				_color.r = _color.r * _diffuseColor.r + _emissiveColor.r;
				_color.g = _color.g * _diffuseColor.g + _emissiveColor.g;
				_color.b = _color.b * _diffuseColor.b + _emissiveColor.b;
383

384 385
			} else {

386
				_color.copy( _diffuseColor );
387 388 389

			}

390 391 392
		} else if ( material instanceof THREE.MeshDepthMaterial ) {

			_w = 1 - ( material.__2near / (material.__farPlusNear - element.z * material.__farMinusNear) );
393
			_color.setRGB( _w, _w, _w );
394 395 396

		} else if ( material instanceof THREE.MeshNormalMaterial ) {

397
			_color.setRGB( normalToComponent( element.normalWorld.x ), normalToComponent( element.normalWorld.y ), normalToComponent( element.normalWorld.z ) );
398

M
Mr.doob 已提交
399
		}
400

M
Mr.doob 已提交
401
		if ( material.wireframe ) {
402

403
			_svgNode.setAttribute( 'style', 'fill: none; stroke: ' + _color.getStyle() + '; stroke-width: ' + material.wireframeLinewidth + '; stroke-opacity: ' + material.opacity + '; stroke-linecap: ' + material.wireframeLinecap + '; stroke-linejoin: ' + material.wireframeLinejoin );
404

M
Mr.doob 已提交
405
		} else {
406

407
			_svgNode.setAttribute( 'style', 'fill: ' + _color.getStyle() + '; fill-opacity: ' + material.opacity );
408 409 410 411 412 413 414

		}

		_svg.appendChild( _svgNode );

	}

415
	function renderFace4( v1, v2, v3, v4, element, material, scene ) {
416

M
Mr.doob 已提交
417 418
		_this.info.render.vertices += 4;
		_this.info.render.faces ++;
419

420
		_svgNode = getPathNode( _pathCount ++ );
421
		_svgNode.setAttribute( 'd', 'M ' + v1.positionScreen.x + ' ' + v1.positionScreen.y + ' L ' + v2.positionScreen.x + ' ' + v2.positionScreen.y + ' L ' + v3.positionScreen.x + ',' + v3.positionScreen.y + ' L ' + v4.positionScreen.x + ',' + v4.positionScreen.y + 'z' );
422

M
Mr.doob 已提交
423 424
		if ( material instanceof THREE.MeshBasicMaterial ) {

M
Mr.doob 已提交
425
			_color.copy( material.color );
M
Mr.doob 已提交
426

427 428 429 430 431 432 433 434
			if ( material.vertexColors === THREE.FaceColors ) {

				_color.r *= element.color.r;
				_color.g *= element.color.g;
				_color.b *= element.color.b;

			}

M
Mr.doob 已提交
435
		} else if ( material instanceof THREE.MeshLambertMaterial ) {
436

437 438
			_diffuseColor.copy( material.color );
			_emissiveColor.copy( material.emissive );
439

440 441 442 443 444 445 446 447 448
			if ( material.vertexColors === THREE.FaceColors ) {

				_diffuseColor.r *= element.color.r;
				_diffuseColor.g *= element.color.g;
				_diffuseColor.b *= element.color.b;

			}

			if ( _enableLighting ) {
449

450 451 452
				_color.r = _ambientLight.r;
				_color.g = _ambientLight.g;
				_color.b = _ambientLight.b;
453

454
				calculateLight( _lights, element.centroidWorld, element.normalWorld, _color );
455

456 457 458
				_color.r = _color.r * _diffuseColor.r + _emissiveColor.r;
				_color.g = _color.g * _diffuseColor.g + _emissiveColor.g;
				_color.b = _color.b * _diffuseColor.b + _emissiveColor.b;
459

460 461
			} else {

462
				_color.copy( _diffuseColor );
463 464 465

			}

466 467 468
		} else if ( material instanceof THREE.MeshDepthMaterial ) {

			_w = 1 - ( material.__2near / (material.__farPlusNear - element.z * material.__farMinusNear) );
469
			_color.setRGB( _w, _w, _w );
470 471 472

		} else if ( material instanceof THREE.MeshNormalMaterial ) {

473
			_color.setRGB( normalToComponent( element.normalWorld.x ), normalToComponent( element.normalWorld.y ), normalToComponent( element.normalWorld.z ) );
474

M
Mr.doob 已提交
475
		}
476

M
Mr.doob 已提交
477
		if ( material.wireframe ) {
478

479
			_svgNode.setAttribute( 'style', 'fill: none; stroke: ' + _color.getStyle() + '; stroke-width: ' + material.wireframeLinewidth + '; stroke-opacity: ' + material.opacity + '; stroke-linecap: ' + material.wireframeLinecap + '; stroke-linejoin: ' + material.wireframeLinejoin );
480

M
Mr.doob 已提交
481
		} else {
482

483
			_svgNode.setAttribute( 'style', 'fill: ' + _color.getStyle() + '; fill-opacity: ' + material.opacity );
484 485 486 487 488 489 490

		}

		_svg.appendChild( _svgNode );

	}

491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
	function getLineNode( id ) {

		if ( _svgLinePool[ id ] == null ) {

			_svgLinePool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'line' );

			if ( _quality == 0 ) {

				_svgLinePool[ id ].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed

			}

			return _svgLinePool[ id ];

		}

		return _svgLinePool[ id ];

	}
510

511
	function getPathNode( id ) {
512

513
		if ( _svgPathPool[ id ] == null ) {
514 515 516

			_svgPathPool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' );

517
			if ( _quality == 0 ) {
518 519 520 521 522 523 524

				_svgPathPool[ id ].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed

			}

			return _svgPathPool[ id ];

525
		}
526 527 528

		return _svgPathPool[ id ];

M
Mr.doob 已提交
529
	}
530

531
	function getCircleNode( id ) {
532

533
		if ( _svgCirclePool[id] == null ) {
534 535 536

			_svgCirclePool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'circle' );

537
			if ( _quality == 0 ) {
538 539 540 541 542 543 544

				_svgCirclePool[id].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed

			}

			return _svgCirclePool[ id ];

545
		}
546 547 548 549 550

		return _svgCirclePool[ id ];

	}

551 552
	function normalToComponent( normal ) {

553 554 555 556 557 558
		var component = ( normal + 1 ) * 0.5;
		return component < 0 ? 0 : ( component > 1 ? 1 : component );

	}

	function pad( str ) {
559

560 561
		while ( str.length < 6 ) str = '0' + str;
		return str;
562 563 564

	}

M
Mr.doob 已提交
565
};