SVGRenderer.js 13.4 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 22 23 24
	_color = new THREE.Color(),
	_ambientLight = new THREE.Color(),
	_directionalLights = new THREE.Color(),
	_pointLights = new THREE.Color(),
M
Mr.doob 已提交
25

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

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

33
	this.domElement = _svg;
M
Mr.doob 已提交
34

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

M
Mr.doob 已提交
39
	this.info = {
40

M
Mr.doob 已提交
41 42 43 44 45 46
		render: {

			vertices: 0,
			faces: 0

		}
47 48 49

	}

50 51 52 53 54 55 56 57
	this.setQuality = function( quality ) {

		switch(quality) {

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

		}
M
Mr.doob 已提交
58 59

	};
60

61
	this.setSize = function( width, height ) {
62

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

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

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

M
Mr.doob 已提交
72
	};
73

M
Mr.doob 已提交
74 75
	this.clear = function () {

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

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

		}

	};

84
	this.render = function ( scene, camera ) {
85

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

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

		}

93
		var e, el, element, material;
94

95
		this.autoClear && this.clear();
96

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

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

104
		_pathCount = 0; _circleCount = 0; _lineCount = 0;
105

106
		_enableLighting = _lights.length > 0;
107 108 109

		if ( _enableLighting ) {

110
			 calculateLights( _lights );
111 112

		}
113

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

116
			element = _elements[ e ];
117

118 119
			material = element.material;

120
			if ( material === undefined || material.visible === false ) continue;
121

122
			_bboxRect.empty();
123

124
			if ( element instanceof THREE.RenderableParticle ) {
125

126 127
				_v1 = element;
				_v1.x *= _svgWidthHalf; _v1.y *= -_svgHeightHalf;
128

129
				renderParticle( _v1, element, material, scene );
130

131
			} else if ( element instanceof THREE.RenderableLine ) {
132

133
				_v1 = element.v1; _v2 = element.v2;
134

135 136
				_v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf;
				_v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf;
137

138 139
				_bboxRect.addPoint( _v1.positionScreen.x, _v1.positionScreen.y );
				_bboxRect.addPoint( _v2.positionScreen.x, _v2.positionScreen.y );
140

M
Mr.doob 已提交
141
				if ( !_clipRect.intersects( _bboxRect ) ) {
142

143
					continue;
144

145
				}
146

147
				renderLine( _v1, _v2, element, material, scene );
148

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

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

153 154 155 156 157 158 159
				_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 );
160

M
Mr.doob 已提交
161
				if ( !_clipRect.intersects( _bboxRect ) ) {
162

163
					continue;
164

M
Mr.doob 已提交
165
				}
166

167
				renderFace3( _v1, _v2, _v3, element, material, scene );
168

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

171 172 173 174 175 176
				_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;
177

178 179 180 181
				_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 );
182

M
Mr.doob 已提交
183
				if ( !_clipRect.intersects( _bboxRect) ) {
184

185
					continue;
186

187
				}
188

189
				renderFace4( _v1, _v2, _v3, _v4, element, material, scene );
190

M
Mr.doob 已提交
191
			}
192 193 194

		}

M
Mr.doob 已提交
195
	};
196

197
	function calculateLights( lights ) {
M
Mr.doob 已提交
198

199
		var l, ll, light, lightColor;
M
Mr.doob 已提交
200

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

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

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

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

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

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

218 219 220
				_directionalLights.r += lightColor.r;
				_directionalLights.g += lightColor.g;
				_directionalLights.b += lightColor.b;
221 222 223

			} else if ( light instanceof THREE.PointLight ) {

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

			}

		}

	}

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

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

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

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

			if ( light instanceof THREE.DirectionalLight ) {

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

247
				amount = normal.dot( lightPosition );
M
Mr.doob 已提交
248

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

251 252 253 254 255
				amount *= light.intensity;

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

			} else if ( light instanceof THREE.PointLight ) {

259
				lightPosition = light.matrixWorld.getPosition();
M
Mr.doob 已提交
260

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

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

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

267 268 269 270 271 272 273
				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 已提交
274 275 276 277 278 279 280

			}

		}

	}

281
	function renderParticle( v1, element, material, scene ) {
282

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

		if ( material instanceof THREE.ParticleCircleMaterial ) {

			if ( _enableLighting ) {

293 294 295
				_color.r = _ambientLight.r + _directionalLights.r + _pointLights.r;
				_color.g = _ambientLight.g + _directionalLights.g + _pointLights.g;
				_color.b = _ambientLight.b + _directionalLights.b + _pointLights.b;
296

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

				_color.updateStyleString();

			} else {

				_color = material.color;

			}

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

		}

		_svg.appendChild( _svgNode );
314
		*/
315 316

	}
M
Mr.doob 已提交
317

318
	function renderLine ( v1, v2, element, material, scene ) {
319

320
		_svgNode = getLineNode( _lineCount ++ );
M
Mr.doob 已提交
321

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

327
		if ( material instanceof THREE.LineBasicMaterial ) {
328

M
Mr.doob 已提交
329
			_svgNode.setAttribute( 'style', 'fill: none; stroke: ' + material.color.getContextStyle() + '; stroke-width: ' + material.linewidth + '; stroke-opacity: ' + material.opacity + '; stroke-linecap: ' + material.linecap + '; stroke-linejoin: ' + material.linejoin );
330

331
			_svg.appendChild( _svgNode );
332

333
		}
334

335
	}
M
Mr.doob 已提交
336

337
	function renderFace3( v1, v2, v3, element, material, scene ) {
338

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

342
		_svgNode = getPathNode( _pathCount ++ );
343
		_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' );
344

M
Mr.doob 已提交
345 346
		if ( material instanceof THREE.MeshBasicMaterial ) {

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

		} else if ( material instanceof THREE.MeshLambertMaterial ) {
350 351 352

			if ( _enableLighting ) {

353 354 355
				var diffuse = material.color;
				var emissive = material.emissive;

356 357 358
				_color.r = _ambientLight.r;
				_color.g = _ambientLight.g;
				_color.b = _ambientLight.b;
359

360
				calculateLight( _lights, element.centroidWorld, element.normalWorld, _color );
361

M
Mr.doob 已提交
362 363 364
				_color.r = diffuse.r * _color.r + emissive.r;
				_color.g = diffuse.g * _color.g + emissive.g;
				_color.b = diffuse.b * _color.b + emissive.b;
365

366 367
			} else {

M
Mr.doob 已提交
368
				_color.copy( material.color );
369 370 371

			}

372 373 374
		} else if ( material instanceof THREE.MeshDepthMaterial ) {

			_w = 1 - ( material.__2near / (material.__farPlusNear - element.z * material.__farMinusNear) );
375
			_color.setRGB( _w, _w, _w );
376 377 378

		} else if ( material instanceof THREE.MeshNormalMaterial ) {

379
			_color.setRGB( normalToComponent( element.normalWorld.x ), normalToComponent( element.normalWorld.y ), normalToComponent( element.normalWorld.z ) );
380

M
Mr.doob 已提交
381
		}
382

M
Mr.doob 已提交
383
		if ( material.wireframe ) {
384

M
Mr.doob 已提交
385
			_svgNode.setAttribute( 'style', 'fill: none; stroke: ' + _color.getContextStyle() + '; stroke-width: ' + material.wireframeLinewidth + '; stroke-opacity: ' + material.opacity + '; stroke-linecap: ' + material.wireframeLinecap + '; stroke-linejoin: ' + material.wireframeLinejoin );
386

M
Mr.doob 已提交
387
		} else {
388

M
Mr.doob 已提交
389
			_svgNode.setAttribute( 'style', 'fill: ' + _color.getContextStyle() + '; fill-opacity: ' + material.opacity );
390 391 392 393 394 395 396

		}

		_svg.appendChild( _svgNode );

	}

397
	function renderFace4( v1, v2, v3, v4, element, material, scene ) {
398

M
Mr.doob 已提交
399 400
		_this.info.render.vertices += 4;
		_this.info.render.faces ++;
401

402
		_svgNode = getPathNode( _pathCount ++ );
403
		_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' );
404

M
Mr.doob 已提交
405 406
		if ( material instanceof THREE.MeshBasicMaterial ) {

M
Mr.doob 已提交
407
			_color.copy( material.color );
M
Mr.doob 已提交
408 409

		} else if ( material instanceof THREE.MeshLambertMaterial ) {
410 411 412

			if ( _enableLighting ) {

413 414 415
				var diffuse = material.color;
				var emissive = material.emissive;

416 417 418
				_color.r = _ambientLight.r;
				_color.g = _ambientLight.g;
				_color.b = _ambientLight.b;
419

420
				calculateLight( _lights, element.centroidWorld, element.normalWorld, _color );
421

M
Mr.doob 已提交
422 423 424
				_color.r = diffuse.r * _color.r + emissive.r;
				_color.g = diffuse.g * _color.g + emissive.g;
				_color.b = diffuse.b * _color.b + emissive.b;
425

426 427
			} else {

M
Mr.doob 已提交
428
				_color.copy( material.color );
429 430 431

			}

432 433 434
		} else if ( material instanceof THREE.MeshDepthMaterial ) {

			_w = 1 - ( material.__2near / (material.__farPlusNear - element.z * material.__farMinusNear) );
435
			_color.setRGB( _w, _w, _w );
436 437 438

		} else if ( material instanceof THREE.MeshNormalMaterial ) {

439
			_color.setRGB( normalToComponent( element.normalWorld.x ), normalToComponent( element.normalWorld.y ), normalToComponent( element.normalWorld.z ) );
440

M
Mr.doob 已提交
441
		}
442

M
Mr.doob 已提交
443
		if ( material.wireframe ) {
444

M
Mr.doob 已提交
445
			_svgNode.setAttribute( 'style', 'fill: none; stroke: ' + _color.getContextStyle() + '; stroke-width: ' + material.wireframeLinewidth + '; stroke-opacity: ' + material.opacity + '; stroke-linecap: ' + material.wireframeLinecap + '; stroke-linejoin: ' + material.wireframeLinejoin );
446

M
Mr.doob 已提交
447
		} else {
448

M
Mr.doob 已提交
449
			_svgNode.setAttribute( 'style', 'fill: ' + _color.getContextStyle() + '; fill-opacity: ' + material.opacity );
450 451 452 453 454 455 456

		}

		_svg.appendChild( _svgNode );

	}

457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475
	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 ];

	}
476

477
	function getPathNode( id ) {
478

479
		if ( _svgPathPool[ id ] == null ) {
480 481 482

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

483
			if ( _quality == 0 ) {
484 485 486 487 488 489 490

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

			}

			return _svgPathPool[ id ];

491
		}
492 493 494

		return _svgPathPool[ id ];

M
Mr.doob 已提交
495
	}
496

497
	function getCircleNode( id ) {
498

499
		if ( _svgCirclePool[id] == null ) {
500 501 502

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

503
			if ( _quality == 0 ) {
504 505 506 507 508 509 510

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

			}

			return _svgCirclePool[ id ];

511
		}
512 513 514 515 516

		return _svgCirclePool[ id ];

	}

517 518
	function normalToComponent( normal ) {

519 520 521 522 523 524
		var component = ( normal + 1 ) * 0.5;
		return component < 0 ? 0 : ( component > 1 ? 1 : component );

	}

	function pad( str ) {
525

526 527
		while ( str.length < 6 ) str = '0' + str;
		return str;
528 529 530

	}

M
Mr.doob 已提交
531
};