瀏覽代碼

Merge pull request #13616 from mrdoob/svgloader

Added basic functionality to SVGLoader.
Mr.doob 7 年之前
父節點
當前提交
206aa14a0e
共有 4 個文件被更改,包括 391 次插入6 次删除
  1. 1 0
      examples/files.js
  2. 188 6
      examples/js/loaders/SVGLoader.js
  3. 44 0
      examples/models/svg/tiger.svg
  4. 158 0
      examples/webgl_loader_svg.html

+ 1 - 0
examples/files.js

@@ -124,6 +124,7 @@ var files = {
 		"webgl_loader_sea3d_skinning",
 		"webgl_loader_sea3d_sound",
 		"webgl_loader_stl",
+		"webgl_loader_svg",
 		"webgl_loader_texture_dds",
 		"webgl_loader_texture_exr",
 		"webgl_loader_texture_hdr",

+ 188 - 6
examples/js/loaders/SVGLoader.js

@@ -17,17 +17,199 @@ THREE.SVGLoader.prototype = {
 
 		var scope = this;
 
-		var parser = new DOMParser();
-
 		var loader = new THREE.FileLoader( scope.manager );
-		loader.load( url, function ( svgString ) {
-
-			var doc = parser.parseFromString( svgString, 'image/svg+xml' ); // application/xml
+		loader.load( url, function ( text ) {
 
-			onLoad( doc.documentElement );
+			onLoad( scope.parse( text ) );
 
 		}, onProgress, onError );
 
+	},
+
+	parse: function ( text ) {
+
+		function parseNodes( nodes ) {
+
+			for ( var i = 0; i < nodes.length; i ++ ) {
+
+				parseNode( nodes[ i ] );
+
+			}
+
+		}
+
+		function parseNode( node ) {
+
+			if ( node.nodeType !== 1 ) return;
+
+			switch ( node.nodeName ) {
+
+				case 'svg':
+					break;
+
+				case 'g':
+					break;
+
+				case 'path':
+					paths.push( parsePathNode( node ) );
+					break;
+
+				case 'rect':
+					paths.push( parseRectNode( node ) );
+					break;
+
+				default:
+					console.log( node );
+					break;
+
+			}
+
+			parseNodes( node.childNodes );
+
+		}
+
+		function parsePathNode( node ) {
+
+			var path = new THREE.ShapePath();
+			var point = new THREE.Vector2();
+
+			var d = node.getAttribute( 'd' );
+
+			console.log( d );
+
+			var commands = d.match( /[a-df-z][^a-df-z]*/ig );
+
+			for ( var i = 0; i < commands.length; i ++ ) {
+
+				var command = commands[ i ];
+
+				var type = command.charAt( 0 );
+				var data = command.substr( 1 );
+
+				switch ( type ) {
+
+					case 'M':
+						var numbers = parseFloats( data );
+						point.fromArray( numbers );
+						path.moveTo( point.x, point.y );
+						break;
+
+					case 'H':
+						var numbers = parseFloats( data );
+						point.x = numbers[ 0 ];
+						path.lineTo( point.x, point.y );
+						break;
+
+					case 'V':
+						var numbers = parseFloats( data );
+						point.y = numbers[ 0 ];
+						path.lineTo( point.x, point.y );
+						break;
+
+					case 'L':
+						var numbers = parseFloats( data );
+						point.x = numbers[ 0 ];
+						point.y = numbers[ 1 ];
+						path.lineTo( point.x, point.y );
+						break;
+
+					case 'C':
+						var numbers = parseFloats( data );
+						path.bezierCurveTo(
+							numbers[ 0 ],
+							numbers[ 1 ],
+							numbers[ 2 ],
+							numbers[ 3 ],
+							numbers[ 4 ],
+							numbers[ 5 ],
+						);
+						point.x = numbers[ 4 ];
+						point.y = numbers[ 5 ];
+						break;
+
+					case 'h':
+						var numbers = parseFloats( data );
+						point.x += numbers[ 0 ];
+						path.lineTo( point.x, point.y );
+						break;
+
+					case 'v':
+						var numbers = parseFloats( data );
+						point.y += numbers[ 0 ];
+						path.lineTo( point.x, point.y );
+						break;
+
+					case 'l':
+						var numbers = parseFloats( data );
+						point.x += numbers[ 0 ];
+						point.y += numbers[ 1 ];
+						path.lineTo( point.x, point.y );
+						break;
+
+					case 'c':
+						var numbers = parseFloats( data );
+						path.bezierCurveTo(
+							point.x + numbers[ 0 ],
+							point.y + numbers[ 1 ],
+							point.x + numbers[ 2 ],
+							point.y + numbers[ 3 ],
+							point.x + numbers[ 4 ],
+							point.y + numbers[ 5 ],
+						);
+						point.x += numbers[ 4 ];
+						point.y += numbers[ 5 ];
+						break;
+
+				}
+
+			}
+
+			return path;
+
+		}
+
+		function parseRectNode( node ) {
+
+			var x = parseFloat( node.getAttribute( 'x' ) );
+			var y = parseFloat( node.getAttribute( 'y' ) );
+			var w = parseFloat( node.getAttribute( 'width' ) );
+			var h = parseFloat( node.getAttribute( 'height' ) );
+
+			var path = new THREE.ShapePath();
+			path.moveTo( x, y );
+			path.lineTo( x + w, y );
+			path.lineTo( x + w, y + h );
+			path.lineTo( x, y + h );
+			return path;
+
+		}
+
+		function parseFloats( string ) {
+
+			var array = string.split( /[\s,]+|(?=\s?[+\-])/ );
+
+			for ( var i = 0; i < array.length; i ++ ) {
+
+				array[ i ] = parseFloat( array[ i ] );
+
+			}
+
+			return array;
+
+		}
+
+		//
+
+		var xml = new DOMParser().parseFromString( text, 'image/svg+xml' ); // application/xml
+
+		var svg = xml.documentElement;
+
+		var paths = [];
+
+		parseNode( svg );
+
+		return paths;
+
 	}
 
 };

文件差異過大導致無法顯示
+ 44 - 0
examples/models/svg/tiger.svg


+ 158 - 0
examples/webgl_loader_svg.html

@@ -0,0 +1,158 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+		body {
+			color: #ffffff;
+			font-family: Monospace;
+			font-size: 13px;
+			text-align: center;
+			font-weight: bold;
+			background-color: #000000;
+			margin: 0px;
+			overflow: hidden;
+		}
+
+		#info {
+			position: absolute;
+			top: 0px;
+			width: 100%;
+			padding: 5px;
+		}
+
+		a {
+			color: #ffffff;
+		}
+		</style>
+	</head>
+
+	<body>
+
+		<div id="container"></div>
+		<div id="info">
+			<a href="http://threejs.org" target="_blank" rel="noopener">three.js</a>
+		</div>
+
+		<script type="text/javascript" src="../build/three.js"></script>
+		<script src="js/controls/OrbitControls.js"></script>
+		<script src="js/loaders/SVGLoader.js"></script>
+		<script src="js/libs/stats.min.js"></script>
+
+		<script>
+
+			var renderer, stats, scene, camera;
+
+			init();
+			animate();
+
+			//
+
+			function init() {
+
+				var container = document.getElementById( 'container' );
+
+				//
+
+				scene = new THREE.Scene();
+				scene.background = new THREE.Color( 0xb0b0b0 );
+
+				//
+
+				camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 1000 );
+				camera.position.set( 0, 0, 200 );
+
+				//
+
+				var helper = new THREE.GridHelper( 160, 10 );
+				helper.rotation.x = Math.PI / 2;
+				scene.add( helper );
+
+				//
+
+				var loader = new THREE.SVGLoader();
+				loader.load( 'models/svg/tiger.svg', function ( paths ) {
+
+					console.log( paths );
+
+					var group = new THREE.Group();
+					group.scale.multiplyScalar( 0.1 );
+					group.scale.y *= -1;
+
+					for ( var i = 0; i < paths.length; i ++ ) {
+
+						var path = paths[ i ];
+
+						var material = new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff } );
+
+						var shapes = path.toShapes( true );
+
+						for ( var j = 0; j < shapes.length; j ++ ) {
+
+							var shape = shapes[ j ];
+
+							var geometry = new THREE.ShapeBufferGeometry( shape );
+							var mesh = new THREE.Mesh( geometry, material );
+
+							group.add( mesh );
+
+						}
+
+					}
+
+					scene.add( group );
+
+				} );
+
+				//
+
+				renderer = new THREE.WebGLRenderer( { antialias: true } );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				container.appendChild( renderer.domElement );
+
+				//
+
+				var controls = new THREE.OrbitControls( camera, renderer.domElement );
+
+				//
+
+				stats = new Stats();
+				container.appendChild( stats.dom );
+
+				//
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+
+				render();
+				stats.update();
+
+			}
+
+			function render() {
+
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+
+	</body>
+</html>

部分文件因文件數量過多而無法顯示