package org.papervision3d.objects.parsers { import org.papervision3d.Papervision3D; import org.papervision3d.objects.DisplayObject3D; import org.papervision3d.events.FileLoadEvent; import flash.events.*; import flash.net.*; import mx.controls.*; import org.papervision3d.core.geom.renderables.Vertex3D; import org.papervision3d.core.geom.renderables.Triangle3D; import org.papervision3d.core.proto.MaterialObject3D; import org.papervision3d.core.proto.GeometryObject3D; import org.papervision3d.core.math.NumberUV; import org.papervision3d.materials.ColorMaterial; import org.papervision3d.core.geom.TriangleMesh3D; import org.papervision3d.core.math.Number3D; import org.papervision3d.materials.utils.MaterialsList; import org.papervision3d.materials.BitmapFileMaterial; /** * The OBJ class let you parse and load object files (.obj). * * The parser supports: * * * Basic example (loading both the object file and materials): * * scene.addChild(new OBJ("fish.obj", "fish.mtl"), "Fish"); * * * Always remember to triangulate the geometry in the exported OBJ-file. * * @author Knut Urdalen * @see http://people.scs.fsu.edu/~burkardt/txt/obj_format.txt */ public class OBJ extends TriangleMesh3D { private var _loaderObj:URLLoader; private var _loaderMtl:URLLoader; private var _obj:String; private var _mtl:String; private var _filename:String; private var _loader:URLLoader; private var _childrenCount:int = 0; private var _materials:MaterialsList; /** * * @param obj * @param mtl */ public function OBJ(obj:String, mtl:* = null, scale:Number = 1, initObject:Object = null) { super(material, new Array(), new Array(), null, initObject); this._obj = obj; if(mtl is String) { this._mtl = mtl; loadMtl(); } else if(mtl is MaterialsList) { this._materials = mtl; loadObj(); } //this._materials = materials || new MaterialsList(); }; private function loadMtl() : void { this._filename = this._mtl; this._loaderMtl = new URLLoader(); this._loaderMtl.addEventListener(Event.COMPLETE, parseMtl); this._loaderMtl.addEventListener(IOErrorEvent.IO_ERROR, handleIOError); this._loaderMtl.addEventListener(SecurityErrorEvent.SECURITY_ERROR, handleSecurityLoadError); this._loaderMtl.addEventListener(ProgressEvent.PROGRESS, handleLoadProgress); this._loaderMtl.load(new URLRequest(this._mtl)); } private function loadObj() : void { this._filename = this._obj; this._loaderObj = new URLLoader(); this._loaderObj.addEventListener(Event.COMPLETE, parseObj); this._loaderObj.addEventListener(IOErrorEvent.IO_ERROR, handleIOError); this._loaderObj.addEventListener(SecurityErrorEvent.SECURITY_ERROR, handleSecurityLoadError); this._loaderObj.addEventListener(ProgressEvent.PROGRESS, handleLoadProgress); this._loaderObj.load(new URLRequest(this._obj)); } private function handleLoadProgress( e:ProgressEvent ) : void { var progressEvent:FileLoadEvent = new FileLoadEvent( FileLoadEvent.LOAD_PROGRESS, this._filename, e.bytesLoaded, e.bytesTotal); dispatchEvent( progressEvent ); } private function handleIOError(e:IOErrorEvent) : void { trace("OBJ file load error", e.text); dispatchEvent(new FileLoadEvent(FileLoadEvent.LOAD_ERROR, this._filename,0,0,e.text)); } private function handleSecurityLoadError(e:SecurityErrorEvent) : void { trace("OBJ file security load error", e.text); dispatchEvent(new FileLoadEvent(FileLoadEvent.SECURITY_LOAD_ERROR, this._filename, 0, 0, e.text)); } /** * Material Library File (.mtl) * * Material library files contain one or more material definitions, each * of which includes the color, texture, and reflection map of individual * materials. These are applied to the surfaces and vertices of objects. * Material files are stored in ASCII format and have the .mtl extension. */ private function parseMtl(event:Event) : void { var mtl:String = this._loaderMtl.data; this._loaderMtl.close(); trace(mtl); var lines:Array = mtl.split("\n"); var parts:Array; var keyword:String; var currentMaterial:MaterialObject3D; var currentName:String; var mtls:Object = {}; for(var i:int = 0; i 0) { keyword = parts[0]; } switch(keyword) { case "newmtl": { // assigns a name to the material and designates the start of a material description currentName = parts[1]; //currentMaterial = this.materials.addMaterial(new MaterialObject3D(), parts[1]); break; } // Ka r g b case "Ka": { // defines the ambient color of the material to be (r,g,b). The default is (0.2,0.2,0.2); break; } // Kd r g b case "Kd": { // defines the diffuse color of the material to be (r,g,b). The default is (0.8,0.8,0.8); break; } // Ks r g b case "Ks": { // defines the specular color of the material to be (r,g,b). This color shows up in highlights. The default is (1.0,1.0,1.0); break; } // Ke r g b case "Ke": { break; } // d alpha case "d": { // defines the transparency of the material to be alpha. The default is 1.0 (not transparent at all) Some formats use Tr instead of d; break; } // Tr alpha case "Tr": { // defines the transparency of the material to be alpha. The default is 1.0 (not transparent at all). Some formats use d instead of Tr; break; } // Ns s case "Ns": { // defines the shininess of the material to be s. The default is 0.0; break; } // illum n case "illum": { // denotes the illumination model used by the material. illum = 1 indicates a flat material with no specular highlights, so the value of Ks is not used. illum = 2 denotes the presence of specular highlights, and so a specification for Ks is required. break; } // map_Ka filename case "map_Ka": { // names a file containing a texture map, which should just be an ASCII dump of RGB values; break; } // map_Kd filename case "map_Kd": { //var material:MaterialObject3D = this.materials.getMaterialByName(currentName); //this.materials.removeMaterial(currentMaterial); mtls[currentName] = new BitmapFileMaterial("image/" + parts[1]); //mtls.push({currentName:new BitmapFileMaterial(parts[1])}); // new MaterialsList({currentName:new BitmapFileMaterial(parts[1])}); //currentMaterial.url = parts[1]; break; } // map_Bump filename case "map_Bump": { break; } } } //this.materials = new MaterialsList(mtls); _materials = new MaterialsList(mtls); loadObj(); } private function parseObj(event:Event) : void { var obj:String = this._loaderObj.data; this._loaderObj.close(); var lines:Array = obj.split("\n"); var parts:Array; var keyword:String; var verticesList:Array = []; var uvList:Array = []; var normalList:Array = []; var currentObject:DisplayObject3D = this; var _currentMaterial:String; for(var i:int = 0; i 0) { keyword = parts[0]; } switch(keyword) { // Vertex data case "v": { // Geometric vertices verticesList.push(new Vertex3D(parts[1], parts[2], parts[3])); currentObject.geometry.vertices.push(verticesList[verticesList.length-1]); break; } case "vt": { // Texture vertices uvList.push(new NumberUV(parts[1], parts[2])); break; } case "vn": { // Vertex normal normalList.push(new Number3D(parts[1], parts[2], parts[3])); break; } case "vp": { // Parameter space vertices break; } // Elements case "p": { // Point break; } case "l": { // Line break; } case "f": { // Face if(parts.length != 4) { // not a triangle continue; // skip } var v0:Vertex3D, v1:Vertex3D, v2:Vertex3D; var i0:Array, i1:Array, i2:Array; var uv0:NumberUV, uv1:NumberUV, uv2:NumberUV; // split info in each parameter i0 = parts[1].split("/"); i1 = parts[2].split("/"); i2 = parts[3].split("/"); v0 = verticesList[i0[0]-1]; v1 = verticesList[i1[0]-1]; v2 = verticesList[i2[0]-1]; //trace("v0: " + v0 + ", v1: " + v1 + ", v2: " + v2); if(i0[1] != "") { // texture data may not be present (this happens if normals are exported, but not texture coordinates) uv0 = uvList[i0[1]-1]; uv1 = uvList[i1[1]-1]; uv2 = uvList[i2[1]-1]; } else { uv0 = new NumberUV(0, 0); uv1 = new NumberUV(0, 1); uv2 = new NumberUV(1, 0); } if(i0[2] != "") { // add normals v0.normal = normalList[i0[2]-1]; v1.normal = normalList[i1[2]-1]; v2.normal = normalList[i2[2]-1]; } currentObject.geometry.faces.push(new Triangle3D(this, [v0, v1, v2], _materials.getMaterialByName(_currentMaterial), [uv0, uv1, uv2])); break; } case "curv": { // Curve break; } case "curv2": { // 2D curve break; } case "surf": { // Surface break; } // Free-form curve/surface attributes case "deg": { // Degree break; } case "bmat": { // Basis matrix break; } case "step": { // Step size break; } case "cstype": { // Curve or surface type break; } // Free-form curve/surface body statements case "parm": { // Parameter values break; } case "trim": { // Outer trimming loop break; } case "hole": { // Inner trimming loop break; } case "scrv": { // Special curve break; } case "sp": { // Special point break; } case "end": { // End statement break; } // Connectivity between free-form surfaces case "con": { // Connect break; } // Grouping case "g": { // Group name currentObject = this.addChild(new TriangleMesh3D(null, null, null, parts[1]), parts[1]); break; } case "s": { // Smoothing group break; } case "mg": { // Merging group break; } case "o": { // Object name //currentObject = this.addChild(new TriangleMesh3D(null, null, null, parts[1]), parts[1]); break; } // Display/render attributes case "bevel": { // Bevel interpolation break; } case "c_interp": { // Color interpolation break; } case "d_interp": { // Dissolve interpolation break; } case "lod": { // Level of detail break; } case "usemtl": { // Material name currentObject.material = this._materials.getMaterialByName(parts[1]); //var material:MaterialObject3D = MaterialObject3D.DEFAULT; //material.name = parts[1]; //this._materials.addMaterial(material, parts[1]); break; } case "mtllib": { // Material library var filename:String = parts[1]; break; } case "shadow_obj": { // Shadow casting break; } case "trace_obj": { // Ray tracing break; } case "ctech": { // Curve approximation technique break; } case "stech": { // Surface approximation technique break; } default: { // unknown keyword break; } } } this.geometry.ready = true; // Activate object } } }