Browse Source

Merge pull request #11359 from linev/fastsvg

Improve performance of SVGRenderer
Mr.doob 8 years ago
parent
commit
e7621bfea0
1 changed files with 62 additions and 57 deletions
  1. 62 57
      examples/js/renderers/SVGRenderer.js

+ 62 - 57
examples/js/renderers/SVGRenderer.js

@@ -44,9 +44,9 @@ THREE.SVGRenderer = function () {
 	_viewMatrix = new THREE.Matrix4(),
 	_viewMatrix = new THREE.Matrix4(),
 	_viewProjectionMatrix = new THREE.Matrix4(),
 	_viewProjectionMatrix = new THREE.Matrix4(),
 
 
-	_svgPathPool = [], _svgLinePool = [], _svgRectPool = [],
-	_svgNode, _pathCount = 0, _lineCount = 0, _rectCount = 0,
-	_quality = 1;
+	_svgPathPool = [],
+	_svgNode, _pathCount = 0, _currPath, _currStyle,
+	_quality = 1, _precision = null;
 
 
 	this.domElement = _svg;
 	this.domElement = _svg;
 
 
@@ -104,11 +104,15 @@ THREE.SVGRenderer = function () {
 
 
 	};
 	};
 
 
+	this.setPrecision = function ( precision ) {
+
+		_precision = precision;
+
+	};
+
 	function removeChildNodes() {
 	function removeChildNodes() {
 
 
 		_pathCount = 0;
 		_pathCount = 0;
-		_lineCount = 0;
-		_rectCount = 0;
 
 
 		while ( _svg.childNodes.length > 0 ) {
 		while ( _svg.childNodes.length > 0 ) {
 
 
@@ -118,10 +122,26 @@ THREE.SVGRenderer = function () {
 
 
 	}
 	}
 
 
+	function getSvgColor ( color, opacity ) {
+
+		var arg = Math.floor( color.r * 255 ) + ',' + Math.floor( color.g * 255 ) + ',' + Math.floor( color.b * 255 );
+
+		if ( opacity === undefined || opacity === 1 ) return 'rgb(' + arg + ')';
+
+		return 'rgba(' + arg + ',' + opacity + ')';
+
+	}
+
+	function convert ( c ) {
+
+		return _precision !== null ? c.toFixed(_precision) : c;
+
+	}
+
 	this.clear = function () {
 	this.clear = function () {
 
 
 		removeChildNodes();
 		removeChildNodes();
-		_svg.style.backgroundColor = 'rgba(' + Math.floor( _clearColor.r * 255 ) + ',' + Math.floor( _clearColor.g * 255 ) + ',' + Math.floor( _clearColor.b * 255 ) + ',' + _clearAlpha + ')';
+		_svg.style.backgroundColor = getSvgColor( _clearColor, _clearAlpha );
 
 
 	};
 	};
 
 
@@ -139,7 +159,7 @@ THREE.SVGRenderer = function () {
 		if ( background && background.isColor ) {
 		if ( background && background.isColor ) {
 
 
 			removeChildNodes();
 			removeChildNodes();
-			_svg.style.backgroundColor = 'rgb(' + Math.floor( background.r * 255 ) + ',' + Math.floor( background.g * 255 ) + ',' + Math.floor( background.b * 255 ) + ')';
+			_svg.style.backgroundColor = getSvgColor( background );
 
 
 		} else if ( this.autoClear === true ) {
 		} else if ( this.autoClear === true ) {
 
 
@@ -160,6 +180,7 @@ THREE.SVGRenderer = function () {
 		_normalViewMatrix.getNormalMatrix( camera.matrixWorldInverse );
 		_normalViewMatrix.getNormalMatrix( camera.matrixWorldInverse );
 
 
 		calculateLights( _lights );
 		calculateLights( _lights );
+		_currPath = _currStyle = ""; // reset accumulated path
 
 
 		for ( var e = 0, el = _elements.length; e < el; e ++ ) {
 		for ( var e = 0, el = _elements.length; e < el; e ++ ) {
 
 
@@ -220,6 +241,8 @@ THREE.SVGRenderer = function () {
 
 
 		}
 		}
 
 
+		flushPath(); // just to flush last svg:path
+
 		scene.traverseVisible( function ( object ) {
 		scene.traverseVisible( function ( object ) {
 
 
 			 if ( object instanceof THREE.SVGObject ) {
 			 if ( object instanceof THREE.SVGObject ) {
@@ -331,37 +354,28 @@ THREE.SVGRenderer = function () {
 			scaleY *= material.size;
 			scaleY *= material.size;
 		}
 		}
 
 
-		_svgNode = getRectNode( _rectCount ++ );
-
-		_svgNode.setAttribute( 'x', v1.x - ( scaleX * 0.5 ) );
-		_svgNode.setAttribute( 'y', v1.y - ( scaleY * 0.5 ) );
-		_svgNode.setAttribute( 'width', scaleX );
-		_svgNode.setAttribute( 'height', scaleY );
+		var path = 'M' + convert( v1.x - scaleX * 0.5 ) + ',' + convert( v1.y - scaleY * 0.5 ) + 'h' + convert( scaleX ) + 'v' + convert( scaleY ) + 'h' + convert(-scaleX) + 'z';
+		var style = "";
 
 
 		if ( material.isSpriteMaterial || material.isPointsMaterial ) {
 		if ( material.isSpriteMaterial || material.isPointsMaterial ) {
 
 
-			_svgNode.setAttribute( 'style', 'fill: ' + material.color.getStyle() );
+			style = 'fill:' + getSvgColor( material.color, material.opacity );
 
 
 		}
 		}
 
 
-		_svg.appendChild( _svgNode );
+		addPath( style, path );
 
 
 	}
 	}
 
 
 	function renderLine( v1, v2, element, material ) {
 	function renderLine( v1, v2, element, material ) {
 
 
-		_svgNode = getLineNode( _lineCount ++ );
-
-		_svgNode.setAttribute( 'x1', v1.positionScreen.x );
-		_svgNode.setAttribute( 'y1', v1.positionScreen.y );
-		_svgNode.setAttribute( 'x2', v2.positionScreen.x );
-		_svgNode.setAttribute( 'y2', v2.positionScreen.y );
+		var path = 'M' + convert( v1.positionScreen.x ) + ',' + convert( v1.positionScreen.y ) + 'L' + convert( v2.positionScreen.x ) + ',' + convert( v2.positionScreen.y );
 
 
 		if ( material instanceof THREE.LineBasicMaterial ) {
 		if ( material instanceof THREE.LineBasicMaterial ) {
 
 
-			_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 );
+			var style = 'fill:none;stroke:' + getSvgColor( material.color, material.opacity ) + ';stroke-width:' + material.linewidth + ';stroke-linecap:' + material.linecap + ';stroke-linejoin:' + material.linejoin;
 
 
-			_svg.appendChild( _svgNode );
+			addPath( style, path );
 
 
 		}
 		}
 
 
@@ -372,8 +386,8 @@ THREE.SVGRenderer = function () {
 		_this.info.render.vertices += 3;
 		_this.info.render.vertices += 3;
 		_this.info.render.faces ++;
 		_this.info.render.faces ++;
 
 
-		_svgNode = getPathNode( _pathCount ++ );
-		_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' );
+		var path = 'M' + convert( v1.positionScreen.x ) + ',' + convert( v1.positionScreen.y ) + 'L' + convert( v2.positionScreen.x ) + ',' + convert( v2.positionScreen.y ) + 'L' + convert( v3.positionScreen.x ) + ',' + convert( v3.positionScreen.y ) + 'z';
+		var style = '';
 
 
 		if ( material instanceof THREE.MeshBasicMaterial ) {
 		if ( material instanceof THREE.MeshBasicMaterial ) {
 
 
@@ -413,75 +427,66 @@ THREE.SVGRenderer = function () {
 
 
 		if ( material.wireframe ) {
 		if ( material.wireframe ) {
 
 
-			_svgNode.setAttribute( 'style', 'fill: none; stroke: ' + _color.getStyle() + '; stroke-width: ' + material.wireframeLinewidth + '; stroke-opacity: ' + material.opacity + '; stroke-linecap: ' + material.wireframeLinecap + '; stroke-linejoin: ' + material.wireframeLinejoin );
+			style = 'fill:none;stroke:' + getSvgColor( _color, material.opacity ) + ';stroke-width:' + material.wireframeLinewidth + ';stroke-linecap:' + material.wireframeLinecap + ';stroke-linejoin:' + material.wireframeLinejoin;
 
 
 		} else {
 		} else {
 
 
-			_svgNode.setAttribute( 'style', 'fill: ' + _color.getStyle() + '; fill-opacity: ' + material.opacity );
+			style = 'fill:' + getSvgColor( _color, material.opacity );
 
 
 		}
 		}
 
 
-		_svg.appendChild( _svgNode );
+		addPath( style, path );
 
 
 	}
 	}
 
 
-	function getLineNode( id ) {
-
-		if ( _svgLinePool[ id ] == null ) {
+	function addPath ( style, path ) {
 
 
-			_svgLinePool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'line' );
+		if ( _currStyle == style ) {
 
 
-			if ( _quality == 0 ) {
+			_currPath += path
 
 
-				_svgLinePool[ id ].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed
+		} else {
 
 
-			}
+			flushPath();
 
 
-			return _svgLinePool[ id ];
+			_currStyle = style;
+			_currPath = path;
 
 
 		}
 		}
 
 
-		return _svgLinePool[ id ];
-
 	}
 	}
 
 
-	function getPathNode( id ) {
-
-		if ( _svgPathPool[ id ] == null ) {
-
-			_svgPathPool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' );
+	function flushPath() {
 
 
-			if ( _quality == 0 ) {
-
-				_svgPathPool[ id ].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed
-
-			}
+		if ( _currPath ) {
 
 
-			return _svgPathPool[ id ];
+			_svgNode = getPathNode( _pathCount ++ );
+			_svgNode.setAttribute( 'd', _currPath );
+			_svgNode.setAttribute( 'style', _currStyle );
+			_svg.appendChild( _svgNode );
 
 
 		}
 		}
 
 
-		return _svgPathPool[ id ];
-
+		_currPath = _currStyle = "";
 	}
 	}
 
 
-	function getRectNode( id ) {
+	function getPathNode( id ) {
 
 
-		if ( _svgRectPool[ id ] == null ) {
+		if ( _svgPathPool[ id ] == null ) {
 
 
-			_svgRectPool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'rect' );
+			_svgPathPool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' );
 
 
 			if ( _quality == 0 ) {
 			if ( _quality == 0 ) {
 
 
-				_svgRectPool[ id ].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed
+				_svgPathPool[ id ].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed
 
 
 			}
 			}
 
 
-			return _svgRectPool[ id ];
+			return _svgPathPool[ id ];
 
 
 		}
 		}
 
 
-		return _svgRectPool[ id ];
+		return _svgPathPool[ id ];
 
 
 	}
 	}