SVGRenderer.js 13.6 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 64 65 66 67
	// WebGLRenderer compatibility

	this.supportsVertexTextures = function () {};
	this.setFaceCulling = function () {};

68
	this.setSize = function( width, height ) {
69

M
Mr.doob 已提交
70 71
		_svgWidth = width; _svgHeight = height;
		_svgWidthHalf = _svgWidth / 2; _svgHeightHalf = _svgHeight / 2;
72

M
Mr.doob 已提交
73 74 75
		_svg.setAttribute( 'viewBox', ( - _svgWidthHalf ) + ' ' + ( - _svgHeightHalf ) + ' ' + _svgWidth + ' ' + _svgHeight );
		_svg.setAttribute( 'width', _svgWidth );
		_svg.setAttribute( 'height', _svgHeight );
76

M
Mr.doob 已提交
77 78
		_clipBox.min.set( - _svgWidthHalf, - _svgHeightHalf );
		_clipBox.max.set( _svgWidthHalf, _svgHeightHalf );
79

M
Mr.doob 已提交
80
	};
81

M
Mr.doob 已提交
82 83
	this.clear = function () {

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

86
			_svg.removeChild( _svg.childNodes[ 0 ] );
M
Mr.doob 已提交
87 88 89 90 91

		}

	};

92
	this.render = function ( scene, camera ) {
93

M
Mr.doob 已提交
94 95 96 97 98 99 100
		if ( camera instanceof THREE.Camera === false ) {

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

		}

101
		var e, el, element, material;
102

103
		this.autoClear && this.clear();
104

M
Mr.doob 已提交
105 106
		_this.info.render.vertices = 0;
		_this.info.render.faces = 0;
107

108
		_renderData = _projector.projectScene( scene, camera, this.sortObjects, this.sortElements );
109 110
		_elements = _renderData.elements;
		_lights = _renderData.lights;
111

112
		_pathCount = 0; _circleCount = 0; _lineCount = 0;
113

114
		_enableLighting = _lights.length > 0;
115 116 117

		if ( _enableLighting ) {

118
			 calculateLights( _lights );
119 120

		}
121

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

124
			element = _elements[ e ];
125

126 127
			material = element.material;

128
			if ( material === undefined || material.visible === false ) continue;
129

M
Mr.doob 已提交
130
			_elemBox.makeEmpty();
131

132
			if ( element instanceof THREE.RenderableParticle ) {
133

134 135
				_v1 = element;
				_v1.x *= _svgWidthHalf; _v1.y *= -_svgHeightHalf;
136

137
				renderParticle( _v1, element, material, scene );
138

139
			} else if ( element instanceof THREE.RenderableLine ) {
140

141
				_v1 = element.v1; _v2 = element.v2;
142

143 144
				_v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf;
				_v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf;
145

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

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

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

152
				}
153

154
			} else if ( element instanceof THREE.RenderableFace3 ) {
155

156
				_v1 = element.v1; _v2 = element.v2; _v3 = element.v3;
157

158 159 160 161
				if ( _v1.positionScreen.z < -1 || _v1.positionScreen.z > 1 ) continue;
				if ( _v2.positionScreen.z < -1 || _v2.positionScreen.z > 1 ) continue;
				if ( _v3.positionScreen.z < -1 || _v3.positionScreen.z > 1 ) continue;

162 163 164 165
				_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 已提交
166
				_elemBox.setFromPoints( [ _v1.positionScreen, _v2.positionScreen, _v3.positionScreen ] );
167

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

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

172 173
				_v1 = element.v1; _v2 = element.v2; _v3 = element.v3; _v4 = element.v4;

174 175 176 177 178
				if ( _v1.positionScreen.z < -1 || _v1.positionScreen.z > 1 ) continue;
				if ( _v2.positionScreen.z < -1 || _v2.positionScreen.z > 1 ) continue;
				if ( _v3.positionScreen.z < -1 || _v3.positionScreen.z > 1 ) continue;
				if ( _v4.positionScreen.z < -1 || _v4.positionScreen.z > 1 ) continue;

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

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

186
				renderFace4( _v1, _v2, _v3, _v4, element, material, scene );
187

M
Mr.doob 已提交
188
			}
189 190 191

		}

M
Mr.doob 已提交
192
	};
193

194
	function calculateLights( lights ) {
M
Mr.doob 已提交
195

196
		var l, ll, light, lightColor;
M
Mr.doob 已提交
197

M
Mr.doob 已提交
198
		_ambientLight.setRGB( 0, 0, 0 );
199 200
		_directionalLights.setRGB( 0, 0, 0 );
		_pointLights.setRGB( 0, 0, 0 );
M
Mr.doob 已提交
201

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

204 205
			light = lights[ l ];
			lightColor = light.color;
M
Mr.doob 已提交
206

M
Mr.doob 已提交
207 208 209 210 211 212 213
			if ( light instanceof THREE.AmbientLight ) {

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

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

215 216 217
				_directionalLights.r += lightColor.r;
				_directionalLights.g += lightColor.g;
				_directionalLights.b += lightColor.b;
218 219 220

			} else if ( light instanceof THREE.PointLight ) {

221 222 223
				_pointLights.r += lightColor.r;
				_pointLights.g += lightColor.g;
				_pointLights.b += lightColor.b;
224 225 226 227 228 229 230

			}

		}

	}

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

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

235 236
			var light = lights[ l ];
			var lightColor = light.color;
M
Mr.doob 已提交
237 238 239

			if ( light instanceof THREE.DirectionalLight ) {

240
				var lightPosition = _vector3.getPositionFromMatrix( light.matrixWorld ).normalize();
M
Mr.doob 已提交
241

242
				var amount = normal.dot( lightPosition );
M
Mr.doob 已提交
243

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

246 247 248 249 250
				amount *= light.intensity;

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

			} else if ( light instanceof THREE.PointLight ) {

254
				var lightPosition = _vector3.getPositionFromMatrix( light.matrixWorld );
M
Mr.doob 已提交
255

256
				var amount = normal.dot( _vector3.subVectors( lightPosition, position ).normalize() );
M
Mr.doob 已提交
257

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

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

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

			}

		}

	}

276
	function renderParticle( v1, element, material, scene ) {
277

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

		if ( material instanceof THREE.ParticleCircleMaterial ) {

			if ( _enableLighting ) {

288 289 290
				_color.r = _ambientLight.r + _directionalLights.r + _pointLights.r;
				_color.g = _ambientLight.g + _directionalLights.g + _pointLights.g;
				_color.b = _ambientLight.b + _directionalLights.b + _pointLights.b;
291

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

				_color.updateStyleString();

			} else {

				_color = material.color;

			}

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

		}

		_svg.appendChild( _svgNode );
309
		*/
310 311

	}
M
Mr.doob 已提交
312

313
	function renderLine ( v1, v2, element, material, scene ) {
314

315
		_svgNode = getLineNode( _lineCount ++ );
M
Mr.doob 已提交
316

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

322
		if ( material instanceof THREE.LineBasicMaterial ) {
323

324
			_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 );
325

326
			_svg.appendChild( _svgNode );
327

328
		}
329

330
	}
M
Mr.doob 已提交
331

332
	function renderFace3( v1, v2, v3, element, material, scene ) {
333

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

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

M
Mr.doob 已提交
340 341
		if ( material instanceof THREE.MeshBasicMaterial ) {

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

344 345
			if ( material.vertexColors === THREE.FaceColors ) {

346
				_color.multiply( element.color );
347 348 349

			}

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

352 353
			_diffuseColor.copy( material.color );
			_emissiveColor.copy( material.emissive );
354

355 356
			if ( material.vertexColors === THREE.FaceColors ) {

357
				_diffuseColor.multiply( element.color );
358 359 360 361

			}

			if ( _enableLighting ) {
362

363
				_color.copy( _ambientLight );
364

365
				calculateLight( _lights, element.centroidModel, element.normalModel, _color );
366

367
				_color.multiply( _diffuseColor ).add( _emissiveColor );
368

369 370
			} else {

371
				_color.copy( _diffuseColor );
372 373 374

			}

375 376 377
		} else if ( material instanceof THREE.MeshDepthMaterial ) {

			_w = 1 - ( material.__2near / (material.__farPlusNear - element.z * material.__farMinusNear) );
378
			_color.setRGB( _w, _w, _w );
379 380 381

		} else if ( material instanceof THREE.MeshNormalMaterial ) {

382 383 384
			var normal = element.normalModelView;

			_color.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 );
385

M
Mr.doob 已提交
386
		}
387

M
Mr.doob 已提交
388
		if ( material.wireframe ) {
389

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

M
Mr.doob 已提交
392
		} else {
393

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

		}

		_svg.appendChild( _svgNode );

	}

402
	function renderFace4( v1, v2, v3, v4, element, material, scene ) {
403

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

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

M
Mr.doob 已提交
410 411
		if ( material instanceof THREE.MeshBasicMaterial ) {

M
Mr.doob 已提交
412
			_color.copy( material.color );
M
Mr.doob 已提交
413

414 415
			if ( material.vertexColors === THREE.FaceColors ) {

416
				_color.multiply( element.color );
417 418 419

			}

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

422 423
			_diffuseColor.copy( material.color );
			_emissiveColor.copy( material.emissive );
424

425 426
			if ( material.vertexColors === THREE.FaceColors ) {

427
				_diffuseColor.multiply( element.color );
428 429 430 431

			}

			if ( _enableLighting ) {
432

433
				_color.copy( _ambientLight );
434

435
				calculateLight( _lights, element.centroidModel, element.normalModel, _color );
436

437
				_color.multiply( _diffuseColor ).add( _emissiveColor );
438

439 440
			} else {

441
				_color.copy( _diffuseColor );
442 443 444

			}

445 446 447
		} else if ( material instanceof THREE.MeshDepthMaterial ) {

			_w = 1 - ( material.__2near / (material.__farPlusNear - element.z * material.__farMinusNear) );
448
			_color.setRGB( _w, _w, _w );
449 450 451

		} else if ( material instanceof THREE.MeshNormalMaterial ) {

452 453 454
			var normal = element.normalModelView;

			_color.setRGB( normal.x, normal.y, normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 );
455

M
Mr.doob 已提交
456
		}
457

M
Mr.doob 已提交
458
		if ( material.wireframe ) {
459

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

M
Mr.doob 已提交
462
		} else {
463

464
			_svgNode.setAttribute( 'style', 'fill: ' + _color.getStyle() + '; fill-opacity: ' + material.opacity );
465 466 467 468 469 470 471

		}

		_svg.appendChild( _svgNode );

	}

472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
	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 ];

	}
491

492
	function getPathNode( id ) {
493

494
		if ( _svgPathPool[ id ] == null ) {
495 496 497

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

498
			if ( _quality == 0 ) {
499 500 501 502 503 504 505

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

			}

			return _svgPathPool[ id ];

506
		}
507 508 509

		return _svgPathPool[ id ];

M
Mr.doob 已提交
510
	}
511

512
	function getCircleNode( id ) {
513

514
		if ( _svgCirclePool[id] == null ) {
515 516 517

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

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

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

			}

			return _svgCirclePool[ id ];

526
		}
527 528 529 530 531

		return _svgCirclePool[ id ];

	}

532
	function pad( str ) {
533

534 535
		while ( str.length < 6 ) str = '0' + str;
		return str;
536 537 538

	}

M
Mr.doob 已提交
539
};