瀏覽代碼

Fix OBJExporter exported file format.

OBJExporter produced invalid OBJ file in case of missing texture coordinates or vertex normals. Now I fixed it, and write dummy texture coordinates if there aren't any, and write face normals if vertex normals are not exist. In addition I created an example to test the code.
kovacsv 10 年之前
父節點
當前提交
940a60884a
共有 2 個文件被更改,包括 264 次插入12 次删除
  1. 41 12
      examples/js/exporters/OBJExporter.js
  2. 223 0
      examples/webgl_exporter_obj.html

+ 41 - 12
examples/js/exporters/OBJExporter.js

@@ -13,7 +13,7 @@ THREE.OBJExporter.prototype = {
 		var output = '';
 
 		var indexVertex = 0;
-		var indexVertexUvs = 0
+		var indexVertexUvs = 0;
 		var indexNormals = 0;
 
 		var parseObject = function ( child ) {
@@ -41,21 +41,34 @@ THREE.OBJExporter.prototype = {
 
 				// uvs
 
-				for ( var i = 0, l = geometry.faceVertexUvs[ 0 ].length; i < l; i ++ ) {
+				if (geometry.faceVertexUvs[ 0 ].length == geometry.faces.length) {
+					
+					for ( var i = 0, l = geometry.faceVertexUvs[ 0 ].length; i < l; i ++ ) {
 
-					var vertexUvs = geometry.faceVertexUvs[ 0 ][ i ];
+						var vertexUvs = geometry.faceVertexUvs[ 0 ][ i ];
 
-					for ( var j = 0; j < vertexUvs.length; j ++ ) {
+						for ( var j = 0; j < vertexUvs.length; j ++ ) {
 
-						var uv = vertexUvs[ j ];
-						vertex.applyMatrix4( child.matrixWorld );
+							var uv = vertexUvs[ j ];
+							vertex.applyMatrix4( child.matrixWorld );
 
-						output += 'vt ' + uv.x + ' ' + uv.y + '\n';
+							output += 'vt ' + uv.x + ' ' + uv.y + '\n';
 
-						nbVertexUvs ++;
+							nbVertexUvs ++;
+
+						}
 
 					}
+					
+				} else {
+					
+					for ( var i = 0, l = geometry.faces.length * 3; i < l; i ++ ) {
 
+						output += 'vt 0 0\n';
+						nbVertexUvs ++;
+						
+					}
+					
 				}
 
 				// normals
@@ -64,13 +77,29 @@ THREE.OBJExporter.prototype = {
 
 					var normals = geometry.faces[ i ].vertexNormals;
 
-					for ( var j = 0; j < normals.length; j ++ ) {
+					if (normals.length == 3) {
+
+						for ( var j = 0; j < normals.length; j ++ ) {
+
+							var normal = normals[ j ];
+							output += 'vn ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n';
+
+							nbNormals ++;
+
+						}
+						
+					} else {
+						
+						var normal = geometry.faces[ i ].normal;
+						
+						for ( var j = 0; j < 3; j ++ ) {
 
-						var normal = normals[ j ];
-						output += 'vn ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n';
+							output += 'vn ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n';
 
-						nbNormals ++;
+							nbNormals ++;
 
+						}										
+						
 					}
 
 				}

+ 223 - 0
examples/webgl_exporter_obj.html

@@ -0,0 +1,223 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - exporter - obj</title>
+		<meta charset="utf-8">
+		<style>
+			body {
+				font-family: Monospace;
+				background-color: #000000;
+				margin: 0px;
+				overflow: hidden;
+			}
+			
+			#info {
+				color: #fff;
+				position: absolute;
+				top: 10px;
+				width: 100%;
+				text-align: center;
+				z-index: 100;
+				display: block;
+			}
+
+			div.floating {
+				color : #ffffff;
+				background : #000000;
+				opacity : 0.8;
+				width : 80%;
+				height : 80%;
+				position : absolute;
+				left : 10%;
+				top : 10%;
+				border : 1px solid #555555;
+				padding : 10px;
+				display : none;
+				overflow : auto;
+				z-index: 100;
+			}
+			
+			span.link {
+				color: skyblue;
+				cursor: pointer;
+				text-decoration : underline;
+			}
+			
+			a {
+				color: skyblue
+			}
+		</style>
+	</head>
+	<body>
+		<div id="info">
+			<a href="http://threejs.org" target="_blank">three.js</a>
+			- geometries:
+			<span class="link" id="triangle">triangle</span>,
+			<span class="link" id="cube">cube</span>,
+			<span class="link" id="cylinder">cylinder</span>,
+			<span class="link" id="both">both</span>
+			- <span class="link" id="export">export to obj</span>
+		</div>
+		
+		<script src="../build/three.min.js"></script>
+		<script src="js/exporters/OBJExporter.js"></script>
+
+		<script>
+
+			var camera, scene, light, renderer;
+			var exportButton, floatingDiv;
+			var mouseX = 0, mouseY = 0;
+
+			function exportToObj ()
+			{
+				var exporter = new THREE.OBJExporter ();
+				var result = exporter.parse (scene);
+				floatingDiv.style.display = 'block';
+				floatingDiv.innerHTML = result.split ('\n').join ('<br />');
+			}
+			
+			function addGeometry (type)
+			{
+				for (var i = 0; i < scene.children.length; i++) {
+					var current = scene.children[i];
+					if (current instanceof THREE.Mesh) {
+						scene.remove (current);
+						i--;
+					}
+				}
+	
+				if (type == 1) {
+					var material = new THREE.MeshLambertMaterial ( { color : 0x00cc00 } );
+					var geometry = new THREE.Geometry ();
+					geometry.vertices.push (new THREE.Vector3 (-50, -50, 0));
+					geometry.vertices.push (new THREE.Vector3 (50, -50, 0));
+					geometry.vertices.push (new THREE.Vector3 (50, 50, 0));
+					var face = new THREE.Face3 (0, 1, 2);
+					geometry.faces.push (face);	
+					geometry.computeFaceNormals ();
+					scene.add( new THREE.Mesh( geometry, material ) );
+				} else if (type == 2) {
+					var material = new THREE.MeshLambertMaterial ( { color : 0x00cc00 } );
+					var geometry = new THREE.BoxGeometry( 100, 100, 100 );
+					scene.add( new THREE.Mesh( geometry, material ) );
+				} else if (type == 3) {
+					var material = new THREE.MeshLambertMaterial ( { color : 0x00cc00 } );
+					var geometry = new THREE.CylinderGeometry( 50, 50, 100, 30, 1 );
+					scene.add( new THREE.Mesh( geometry, material ) );
+				} else if (type == 4) {
+					var material = new THREE.MeshLambertMaterial ( { color : 0x00cc00 } );
+
+					var geometry = new THREE.Geometry ();
+					geometry.vertices.push (new THREE.Vector3 (-50, -50, 0));
+					geometry.vertices.push (new THREE.Vector3 (50, -50, 0));
+					geometry.vertices.push (new THREE.Vector3 (50, 50, 0));
+					geometry.faces.push (new THREE.Face3 (0, 1, 2));	
+					geometry.computeFaceNormals ();
+					var mesh = new THREE.Mesh( geometry, material );
+					mesh.position.x = -200;
+					scene.add( mesh );
+
+					var geometry2 = new THREE.BoxGeometry( 100, 100, 100 );
+					var mesh2 = new THREE.Mesh( geometry2, material );
+					scene.add( mesh2 );
+
+					var geometry3 = new THREE.CylinderGeometry( 50, 50, 100, 30, 1 );
+					var mesh3 = new THREE.Mesh( geometry3, material );
+					mesh3.position.x = 200;
+
+					scene.add( mesh3 );
+				}
+			}
+			
+			function init() {
+
+				renderer = new THREE.WebGLRenderer();
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				document.body.appendChild( renderer.domElement );
+
+				camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 1000 );
+				camera.position.set (0, 0, 400);
+
+				scene = new THREE.Scene();
+
+				light = new THREE.DirectionalLight( 0xffffff );
+				scene.add( light );
+
+				addGeometry (1);
+				
+				window.addEventListener( 'click', onWindowClick, false );
+				window.addEventListener( 'resize', onWindowResize, false );
+				document.addEventListener( 'mousemove', onDocumentMouseMove, false );
+				document.addEventListener( 'mouseover', onDocumentMouseMove, false );
+				
+				document.getElementById( 'triangle' ).addEventListener( 'click', function() { addGeometry (1); });
+				document.getElementById( 'cube' ).addEventListener( 'click', function() { addGeometry (2); });
+				document.getElementById( 'cylinder' ).addEventListener( 'click', function() { addGeometry (3); });
+				document.getElementById( 'both' ).addEventListener( 'click', function() { addGeometry (4); });
+				
+				exportButton = document.getElementById( 'export' );
+				exportButton.addEventListener( 'click', function() { exportToObj (); });
+				
+				floatingDiv = document.createElement ('div');
+				floatingDiv.className = 'floating';
+				document.body.appendChild (floatingDiv);
+			}
+
+			function onWindowClick(event) {
+
+				var needToClose = true;
+				var target = event.target;
+				while (target !== null) {
+					if (target === floatingDiv || target == exportButton) {
+						needToClose = false;
+						break;
+					}
+					target = target.parentElement;
+				}
+				
+				if (needToClose) {
+					floatingDiv.style.display = 'none';
+				}
+
+			}			
+			
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function onDocumentMouseMove( event ) {
+
+				var windowHalfX = window.innerWidth / 2;
+				var windowHalfY = window.innerHeight / 2;
+				mouseX = ( event.clientX - windowHalfX ) / 2;
+				mouseY = ( event.clientY - windowHalfY ) / 2;
+
+			}
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+
+				camera.position.x += ( mouseX - camera.position.x ) * .05;
+				camera.position.y += ( -mouseY - camera.position.y ) * .05;
+				camera.lookAt( new THREE.Vector3 (0.0, 0.0, 0.0) );
+
+				light.position.set( camera.position.x, camera.position.y, camera.position.z ).normalize ();
+				renderer.render( scene, camera );
+
+			}
+
+			init();
+			animate();
+
+
+		</script>
+
+	</body>
+</html>