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

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

86
		var e, el, element, material;
87

88
		this.autoClear && this.clear();
89

M
Mr.doob 已提交
90 91
		_this.info.render.vertices = 0;
		_this.info.render.faces = 0;
92

93 94 95
		_renderData = _projector.projectScene( scene, camera, this.sortElements );
		_elements = _renderData.elements;
		_lights = _renderData.lights;
96

97
		_pathCount = 0; _circleCount = 0; _lineCount = 0;
98

99
		_enableLighting = _lights.length > 0;
100 101 102

		if ( _enableLighting ) {

103
			 calculateLights( _lights );
104 105

		}
106

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

109
			element = _elements[ e ];
110

111 112
			material = element.material;

113
			if ( material === undefined || material.visible === false ) continue;
114

115
			_bboxRect.empty();
116

117
			if ( element instanceof THREE.RenderableParticle ) {
118

119 120
				_v1 = element;
				_v1.x *= _svgWidthHalf; _v1.y *= -_svgHeightHalf;
121

122
				renderParticle( _v1, element, material, scene );
123

124
			} else if ( element instanceof THREE.RenderableLine ) {
125

126
				_v1 = element.v1; _v2 = element.v2;
127

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

131 132
				_bboxRect.addPoint( _v1.positionScreen.x, _v1.positionScreen.y );
				_bboxRect.addPoint( _v2.positionScreen.x, _v2.positionScreen.y );
133

M
Mr.doob 已提交
134
				if ( !_clipRect.intersects( _bboxRect ) ) {
135

136
					continue;
137

138
				}
139

140
				renderLine( _v1, _v2, element, material, scene );
141

142
			} else if ( element instanceof THREE.RenderableFace3 ) {
143

144
				_v1 = element.v1; _v2 = element.v2; _v3 = element.v3;
145

146 147 148 149 150 151 152
				_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 );
153

M
Mr.doob 已提交
154
				if ( !_clipRect.intersects( _bboxRect ) ) {
155

156
					continue;
157

M
Mr.doob 已提交
158
				}
159

160
				renderFace3( _v1, _v2, _v3, element, material, scene );
161

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

164 165 166 167 168 169
				_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;
170

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

M
Mr.doob 已提交
176
				if ( !_clipRect.intersects( _bboxRect) ) {
177

178
					continue;
179

180
				}
181

182
				renderFace4( _v1, _v2, _v3, _v4, element, material, scene );
183

M
Mr.doob 已提交
184
			}
185 186 187

		}

M
Mr.doob 已提交
188
	};
189

190
	function calculateLights( lights ) {
M
Mr.doob 已提交
191

192
		var l, ll, light, lightColor;
M
Mr.doob 已提交
193

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

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

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

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

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

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

211 212 213
				_directionalLights.r += lightColor.r;
				_directionalLights.g += lightColor.g;
				_directionalLights.b += lightColor.b;
214 215 216

			} else if ( light instanceof THREE.PointLight ) {

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

			}

		}

	}

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

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

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

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

			if ( light instanceof THREE.DirectionalLight ) {

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

240
				amount = normal.dot( lightPosition );
M
Mr.doob 已提交
241

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

244 245 246 247 248
				amount *= light.intensity;

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

			} else if ( light instanceof THREE.PointLight ) {

252
				lightPosition = light.matrixWorld.getPosition();
M
Mr.doob 已提交
253

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

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

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

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

			}

		}

	}

274
	function renderParticle( v1, element, material, scene ) {
275

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

		if ( material instanceof THREE.ParticleCircleMaterial ) {

			if ( _enableLighting ) {

286 287 288
				_color.r = _ambientLight.r + _directionalLights.r + _pointLights.r;
				_color.g = _ambientLight.g + _directionalLights.g + _pointLights.g;
				_color.b = _ambientLight.b + _directionalLights.b + _pointLights.b;
289

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

				_color.updateStyleString();

			} else {

				_color = material.color;

			}

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

		}

		_svg.appendChild( _svgNode );
307
		*/
308 309

	}
M
Mr.doob 已提交
310

311
	function renderLine ( v1, v2, element, material, scene ) {
312

313
		_svgNode = getLineNode( _lineCount ++ );
M
Mr.doob 已提交
314

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

320
		if ( material instanceof THREE.LineBasicMaterial ) {
321

M
Mr.doob 已提交
322
			_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 );
323

324
			_svg.appendChild( _svgNode );
325

326
		}
327

328
	}
M
Mr.doob 已提交
329

330
	function renderFace3( v1, v2, v3, element, material, scene ) {
331

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

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

M
Mr.doob 已提交
338 339
		if ( material instanceof THREE.MeshBasicMaterial ) {

M
Mr.doob 已提交
340
			_color.copy( material.color );
M
Mr.doob 已提交
341 342

		} else if ( material instanceof THREE.MeshLambertMaterial ) {
343 344 345

			if ( _enableLighting ) {

346 347 348
				_color.r = _ambientLight.r;
				_color.g = _ambientLight.g;
				_color.b = _ambientLight.b;
349

350
				calculateLight( _lights, element.centroidWorld, element.normalWorld, _color );
351

352 353 354
				_color.r = Math.max( 0, Math.min( material.color.r * _color.r, 1 ) );
				_color.g = Math.max( 0, Math.min( material.color.g * _color.g, 1 ) );
				_color.b = Math.max( 0, Math.min( material.color.b * _color.b, 1 ) );
355

356 357
			} else {

M
Mr.doob 已提交
358
				_color.copy( material.color );
359 360 361

			}

362 363 364
		} else if ( material instanceof THREE.MeshDepthMaterial ) {

			_w = 1 - ( material.__2near / (material.__farPlusNear - element.z * material.__farMinusNear) );
365
			_color.setRGB( _w, _w, _w );
366 367 368

		} else if ( material instanceof THREE.MeshNormalMaterial ) {

369
			_color.setRGB( normalToComponent( element.normalWorld.x ), normalToComponent( element.normalWorld.y ), normalToComponent( element.normalWorld.z ) );
370

M
Mr.doob 已提交
371
		}
372

M
Mr.doob 已提交
373
		if ( material.wireframe ) {
374

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

M
Mr.doob 已提交
377
		} else {
378

M
Mr.doob 已提交
379
			_svgNode.setAttribute( 'style', 'fill: ' + _color.getContextStyle() + '; fill-opacity: ' + material.opacity );
380 381 382 383 384 385 386

		}

		_svg.appendChild( _svgNode );

	}

387
	function renderFace4( v1, v2, v3, v4, element, material, scene ) {
388

M
Mr.doob 已提交
389 390
		_this.info.render.vertices += 4;
		_this.info.render.faces ++;
391

392
		_svgNode = getPathNode( _pathCount ++ );
393
		_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' );
394

M
Mr.doob 已提交
395 396
		if ( material instanceof THREE.MeshBasicMaterial ) {

M
Mr.doob 已提交
397
			_color.copy( material.color );
M
Mr.doob 已提交
398 399

		} else if ( material instanceof THREE.MeshLambertMaterial ) {
400 401 402

			if ( _enableLighting ) {

403 404 405
				_color.r = _ambientLight.r;
				_color.g = _ambientLight.g;
				_color.b = _ambientLight.b;
406

407
				calculateLight( _lights, element.centroidWorld, element.normalWorld, _color );
408

409 410 411
				_color.r = Math.max( 0, Math.min( material.color.r * _color.r, 1 ) );
				_color.g = Math.max( 0, Math.min( material.color.g * _color.g, 1 ) );
				_color.b = Math.max( 0, Math.min( material.color.b * _color.b, 1 ) );
412

413 414
			} else {

M
Mr.doob 已提交
415
				_color.copy( material.color );
416 417 418

			}

419 420 421
		} else if ( material instanceof THREE.MeshDepthMaterial ) {

			_w = 1 - ( material.__2near / (material.__farPlusNear - element.z * material.__farMinusNear) );
422
			_color.setRGB( _w, _w, _w );
423 424 425

		} else if ( material instanceof THREE.MeshNormalMaterial ) {

426
			_color.setRGB( normalToComponent( element.normalWorld.x ), normalToComponent( element.normalWorld.y ), normalToComponent( element.normalWorld.z ) );
427

M
Mr.doob 已提交
428
		}
429

M
Mr.doob 已提交
430
		if ( material.wireframe ) {
431

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

M
Mr.doob 已提交
434
		} else {
435

M
Mr.doob 已提交
436
			_svgNode.setAttribute( 'style', 'fill: ' + _color.getContextStyle() + '; fill-opacity: ' + material.opacity );
437 438 439 440 441 442 443

		}

		_svg.appendChild( _svgNode );

	}

444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
	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 ];

	}
463

464
	function getPathNode( id ) {
465

466
		if ( _svgPathPool[ id ] == null ) {
467 468 469

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

470
			if ( _quality == 0 ) {
471 472 473 474 475 476 477

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

			}

			return _svgPathPool[ id ];

478
		}
479 480 481

		return _svgPathPool[ id ];

M
Mr.doob 已提交
482
	}
483

484
	function getCircleNode( id ) {
485

486
		if ( _svgCirclePool[id] == null ) {
487 488 489

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

490
			if ( _quality == 0 ) {
491 492 493 494 495 496 497

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

			}

			return _svgCirclePool[ id ];

498
		}
499 500 501 502 503

		return _svgCirclePool[ id ];

	}

504 505
	function normalToComponent( normal ) {

506 507 508 509 510 511
		var component = ( normal + 1 ) * 0.5;
		return component < 0 ? 0 : ( component > 1 ? 1 : component );

	}

	function pad( str ) {
512

513 514
		while ( str.length < 6 ) str = '0' + str;
		return str;
515 516 517

	}

M
Mr.doob 已提交
518
};