SVGRenderer.js 13.7 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,

M
Mr.doob 已提交
17 18
	_clipBox = new THREE.Box2(),
	_elemBox = new THREE.Box2(),
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 73
		_clipBox.min.set( - _svgWidthHalf, - _svgHeightHalf );
		_clipBox.max.set( _svgWidthHalf, _svgHeightHalf );
74

M
Mr.doob 已提交
75
	};
76

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

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

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

		}

	};

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

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

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

		}

96
		var e, el, element, material;
97

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

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

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

107
		_pathCount = 0; _circleCount = 0; _lineCount = 0;
108

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

		if ( _enableLighting ) {

113
			 calculateLights( _lights );
114 115

		}
116

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

119
			element = _elements[ e ];
120

121 122
			material = element.material;

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

M
Mr.doob 已提交
125
			_elemBox.makeEmpty();
126

127
			if ( element instanceof THREE.RenderableParticle ) {
128

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

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

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

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

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

M
Mr.doob 已提交
141
				_elemBox.setFromPoints( [ _v1.positionScreen, _v2.positionScreen ] );
142

M
Mr.doob 已提交
143
				if ( _clipBox.isIntersectionBox( _elemBox ) === true ) {
144

M
Mr.doob 已提交
145
					renderLine( _v1, _v2, element, material, scene );
146

147
				}
148

149
			} else if ( element instanceof THREE.RenderableFace3 ) {
150

151
				_v1 = element.v1; _v2 = element.v2; _v3 = element.v3;
152

153 154 155 156
				_v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf;
				_v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf;
				_v3.positionScreen.x *= _svgWidthHalf; _v3.positionScreen.y *= - _svgHeightHalf;

M
Mr.doob 已提交
157
				_elemBox.setFromPoints( [ _v1.positionScreen, _v2.positionScreen, _v3.positionScreen ] );
158

M
Mr.doob 已提交
159
				if ( _clipBox.isIntersectionBox( _elemBox ) === true ) {
160

M
Mr.doob 已提交
161
					renderFace3( _v1, _v2, _v3, element, material, scene );
162

M
Mr.doob 已提交
163
				}
164

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

167 168 169 170 171 172
				_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;
173

M
Mr.doob 已提交
174
				_elemBox.setFromPoints( [ _v1.positionScreen, _v2.positionScreen, _v3.positionScreen, _v4.positionScreen ] );
175

M
Mr.doob 已提交
176
				if ( _clipBox.isIntersectionBox( _elemBox ) === true ) {
177

M
Mr.doob 已提交
178
					renderFace4( _v1, _v2, _v3, _v4, element, material, scene );
179

180
				}
181

M
Mr.doob 已提交
182
			}
183 184 185

		}

M
Mr.doob 已提交
186
	};
187

188
	function calculateLights( lights ) {
M
Mr.doob 已提交
189

190
		var l, ll, light, lightColor;
M
Mr.doob 已提交
191

M
Mr.doob 已提交
192
		_ambientLight.setRGB( 0, 0, 0 );
193 194
		_directionalLights.setRGB( 0, 0, 0 );
		_pointLights.setRGB( 0, 0, 0 );
M
Mr.doob 已提交
195

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

198 199
			light = lights[ l ];
			lightColor = light.color;
M
Mr.doob 已提交
200

M
Mr.doob 已提交
201 202 203 204 205 206 207
			if ( light instanceof THREE.AmbientLight ) {

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

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

209 210 211
				_directionalLights.r += lightColor.r;
				_directionalLights.g += lightColor.g;
				_directionalLights.b += lightColor.b;
212 213 214

			} else if ( light instanceof THREE.PointLight ) {

215 216 217
				_pointLights.r += lightColor.r;
				_pointLights.g += lightColor.g;
				_pointLights.b += lightColor.b;
218 219 220 221 222 223 224

			}

		}

	}

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

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

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

231 232
			light = lights[ l ];
			lightColor = light.color;
M
Mr.doob 已提交
233 234 235

			if ( light instanceof THREE.DirectionalLight ) {

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

238
				amount = normal.dot( lightPosition );
M
Mr.doob 已提交
239

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

242 243 244 245 246
				amount *= light.intensity;

				color.r += lightColor.r * amount;
				color.g += lightColor.g * amount;
				color.b += lightColor.b * amount;
M
Mr.doob 已提交
247 248 249

			} else if ( light instanceof THREE.PointLight ) {

250
				lightPosition = light.matrixWorld.getPosition();
M
Mr.doob 已提交
251

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

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

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

258 259 260 261 262 263 264
				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 已提交
265 266 267 268 269 270 271

			}

		}

	}

272
	function renderParticle( v1, element, material, scene ) {
273

274
		/*
275
		_svgNode = getCircleNode( _circleCount++ );
276 277
		_svgNode.setAttribute( 'cx', v1.x );
		_svgNode.setAttribute( 'cy', v1.y );
M
Mr.doob 已提交
278
		_svgNode.setAttribute( 'r', element.scale.x * _svgWidthHalf );
279 280 281 282 283

		if ( material instanceof THREE.ParticleCircleMaterial ) {

			if ( _enableLighting ) {

284 285 286
				_color.r = _ambientLight.r + _directionalLights.r + _pointLights.r;
				_color.g = _ambientLight.g + _directionalLights.g + _pointLights.g;
				_color.b = _ambientLight.b + _directionalLights.b + _pointLights.b;
287

288 289 290
				_color.r = material.color.r * _color.r;
				_color.g = material.color.g * _color.g;
				_color.b = material.color.b * _color.b;
291 292 293 294 295 296 297 298 299 300 301 302 303 304

				_color.updateStyleString();

			} else {

				_color = material.color;

			}

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

		}

		_svg.appendChild( _svgNode );
305
		*/
306 307

	}
M
Mr.doob 已提交
308

309
	function renderLine ( v1, v2, element, material, scene ) {
310

311
		_svgNode = getLineNode( _lineCount ++ );
M
Mr.doob 已提交
312

313 314 315 316 317
		_svgNode.setAttribute( 'x1', v1.positionScreen.x );
		_svgNode.setAttribute( 'y1', v1.positionScreen.y );
		_svgNode.setAttribute( 'x2', v2.positionScreen.x );
		_svgNode.setAttribute( 'y2', v2.positionScreen.y );

318
		if ( material instanceof THREE.LineBasicMaterial ) {
319

320
			_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 );
321

322
			_svg.appendChild( _svgNode );
323

324
		}
325

326
	}
M
Mr.doob 已提交
327

328
	function renderFace3( v1, v2, v3, element, material, scene ) {
329

M
Mr.doob 已提交
330 331
		_this.info.render.vertices += 3;
		_this.info.render.faces ++;
332

333
		_svgNode = getPathNode( _pathCount ++ );
334
		_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' );
335

M
Mr.doob 已提交
336 337
		if ( material instanceof THREE.MeshBasicMaterial ) {

M
Mr.doob 已提交
338
			_color.copy( material.color );
M
Mr.doob 已提交
339

340 341 342 343 344 345 346 347
			if ( material.vertexColors === THREE.FaceColors ) {

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

			}

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

350 351
			_diffuseColor.copy( material.color );
			_emissiveColor.copy( material.emissive );
352

353 354 355 356 357 358 359 360 361
			if ( material.vertexColors === THREE.FaceColors ) {

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

			}

			if ( _enableLighting ) {
362

363 364 365
				_color.r = _ambientLight.r;
				_color.g = _ambientLight.g;
				_color.b = _ambientLight.b;
366

367
				calculateLight( _lights, element.centroidModel, element.normalModel, _color );
368

369 370 371
				_color.r = _color.r * _diffuseColor.r + _emissiveColor.r;
				_color.g = _color.g * _diffuseColor.g + _emissiveColor.g;
				_color.b = _color.b * _diffuseColor.b + _emissiveColor.b;
372

373 374
			} else {

375
				_color.copy( _diffuseColor );
376 377 378

			}

379 380 381
		} else if ( material instanceof THREE.MeshDepthMaterial ) {

			_w = 1 - ( material.__2near / (material.__farPlusNear - element.z * material.__farMinusNear) );
382
			_color.setRGB( _w, _w, _w );
383 384 385

		} else if ( material instanceof THREE.MeshNormalMaterial ) {

386
			_color.setRGB( 0.5 * element.normalModelView.x + 0.5, 0.5 * element.normalModelView.y + 0.5, 0.5 * element.normalModelView.z + 0.5 );
387

M
Mr.doob 已提交
388
		}
389

M
Mr.doob 已提交
390
		if ( material.wireframe ) {
391

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

M
Mr.doob 已提交
394
		} else {
395

396
			_svgNode.setAttribute( 'style', 'fill: ' + _color.getStyle() + '; fill-opacity: ' + material.opacity );
397 398 399 400 401 402 403

		}

		_svg.appendChild( _svgNode );

	}

404
	function renderFace4( v1, v2, v3, v4, element, material, scene ) {
405

M
Mr.doob 已提交
406 407
		_this.info.render.vertices += 4;
		_this.info.render.faces ++;
408

409
		_svgNode = getPathNode( _pathCount ++ );
410
		_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' );
411

M
Mr.doob 已提交
412 413
		if ( material instanceof THREE.MeshBasicMaterial ) {

M
Mr.doob 已提交
414
			_color.copy( material.color );
M
Mr.doob 已提交
415

416 417 418 419 420 421 422 423
			if ( material.vertexColors === THREE.FaceColors ) {

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

			}

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

426 427
			_diffuseColor.copy( material.color );
			_emissiveColor.copy( material.emissive );
428

429 430 431 432 433 434 435 436 437
			if ( material.vertexColors === THREE.FaceColors ) {

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

			}

			if ( _enableLighting ) {
438

439 440 441
				_color.r = _ambientLight.r;
				_color.g = _ambientLight.g;
				_color.b = _ambientLight.b;
442

443
				calculateLight( _lights, element.centroidModel, element.normalModel, _color );
444

445 446 447
				_color.r = _color.r * _diffuseColor.r + _emissiveColor.r;
				_color.g = _color.g * _diffuseColor.g + _emissiveColor.g;
				_color.b = _color.b * _diffuseColor.b + _emissiveColor.b;
448

449 450
			} else {

451
				_color.copy( _diffuseColor );
452 453 454

			}

455 456 457
		} else if ( material instanceof THREE.MeshDepthMaterial ) {

			_w = 1 - ( material.__2near / (material.__farPlusNear - element.z * material.__farMinusNear) );
458
			_color.setRGB( _w, _w, _w );
459 460 461

		} else if ( material instanceof THREE.MeshNormalMaterial ) {

462
			_color.setRGB( 0.5 * element.normalModelView.x + 0.5, 0.5 * element.normalModelView.y + 0.5, 0.5 * element.normalModelView.z + 0.5 );
463

M
Mr.doob 已提交
464
		}
465

M
Mr.doob 已提交
466
		if ( material.wireframe ) {
467

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

M
Mr.doob 已提交
470
		} else {
471

472
			_svgNode.setAttribute( 'style', 'fill: ' + _color.getStyle() + '; fill-opacity: ' + material.opacity );
473 474 475 476 477 478 479

		}

		_svg.appendChild( _svgNode );

	}

480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
	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 ];

	}
499

500
	function getPathNode( id ) {
501

502
		if ( _svgPathPool[ id ] == null ) {
503 504 505

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

506
			if ( _quality == 0 ) {
507 508 509 510 511 512 513

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

			}

			return _svgPathPool[ id ];

514
		}
515 516 517

		return _svgPathPool[ id ];

M
Mr.doob 已提交
518
	}
519

520
	function getCircleNode( id ) {
521

522
		if ( _svgCirclePool[id] == null ) {
523 524 525

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

526
			if ( _quality == 0 ) {
527 528 529 530 531 532 533

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

			}

			return _svgCirclePool[ id ];

534
		}
535 536 537 538 539

		return _svgCirclePool[ id ];

	}

540
	function pad( str ) {
541

542 543
		while ( str.length < 6 ) str = '0' + str;
		return str;
544 545 546

	}

M
Mr.doob 已提交
547
};