package away3d.loaders { import away3d.arcane; import away3d.containers.*; import away3d.core.base.*; import away3d.core.utils.*; import away3d.materials.*; import away3d.loaders.utils.*; import flash.geom.*; use namespace arcane; /** * File loader for the AC3D file format (.ac). * This parser was written from AC3D 6.715 outputs. .ac file format version 11. * Untested in prior versions * Note: the "texture" tag url's might require some edits as AC3D doesn't offer a relative export * * If all textures are set into same folder, the parser supports a sourcespath variable. * url textures becomes then "sourcesPath/+[skiped part url found in file]filename.jpg" example: private function loadACFile():void { var loader:Loader3D = new Loader3D(); loader.addOnSuccess(onLoaderSuccess); loader.addOnError(onLoaderError); var ACParser:AC3D = new AC3D({scaling:100, sourcesPath:"myImages"}); loader.loadGeometry("exportedACfiles/bigroom.ac", ACParser); } private function onLoaderSuccess(e:Loader3DEvent):void { var obj:Object3D = e.loader.handle; _scene.addChild(obj); } private function onLoaderError(e:Event):void { trace("Error loading .ac file"); } * * unsupported tags: "MATERIAL", "numsurf","kids","crease","texrep","refs lines of","url","data" and "numvert lines of": */ public class AC3D extends AbstractParser { public function getVersionFromHex(char:String):int { switch (char) { case "A": case "a": return 10; case "B": case "b": return 11; case "C": case "c": return 12; case "D": case "d": return 13; case "E": case "e": return 14; case "F": case "f": return 15; default: return new Number(char); } } private var mainContainer:ObjectContainer3D; private var activeContainer:ObjectContainer3D; /** @private */ arcane override function prepareData(data:*):void { mainContainer = activeContainer = null; var ac:String = Cast.string(data); var lines:Array = ac.split('\n'); if(lines.length == 1) lines = ac.split(String.fromCharCode(13)); var trunk:Array; var materialList:Array = []; var materialIndexList:Array = []; var textureList:Array = []; var meshList:Array = []; var containersList:Array = []; var activeMesh:Mesh; var tmpos:Vector3D; var vertexes:Array; var uvs:Array; var nameid:String; var parsesV:Boolean; var isQuad:Boolean; var quadCount:int; var refscount:int; var invalidPoly:Boolean; var lastType:String = ""; var tUrl:String = ""; var matrix:Matrix3D; var kidsCount:int = 0; var m:Mesh; var cont:ObjectContainer3D; //version ac3d --> AC3D[b] --> hex value for file format var version:String = lines[0].substring(lines[0].length-1, lines[0].length); trace("AC3D file version: "+getVersionFromHex(version)); lines.shift(); for each (var line:String in lines) { trunk = line.replace(" "," ").replace(" "," ").replace(" "," ").split(" "); switch (trunk[0]) { //XX Not used for now XX case "MATERIAL"://MATERIAL "ac3dmat1" rgb 1 1 1 amb 0.2 0.2 0.2 emis 0 0 0 spec 0.2 0.2 0.2 shi 128 trans 0 materialList.push(line);//push the whole line for now break; case "numsurf"://integer case "crease"://45.000000. case "texrep":// %f %f tiling case "refs lines of": case "url": case "data": case "numvert lines of": break; case "kids"://howmany children in the upcomming object. Probably need it later on, to couple with container/group generation kidsCount = parseInt(trunk[1]); break; case "OBJECT": if(activeMesh != null){ CentralMaterialLibrary.addMaterial(activeMesh.material, activeMesh, activeMesh.name, textureList[textureList.length-1]); buildMeshGeometry(activeMesh, vertexes, uvs , tmpos); tmpos = null; activeMesh = null; } if(trunk[1] == "world"){ if(mainContainer == null){ mainContainer = new ObjectContainer3D(); _container = mainContainer; mainContainer.name = "ac_"+containersList.length; containersList.push(mainContainer); lastType = "world"; } activeContainer = mainContainer; } if(trunk[1] == "poly"){ m = new Mesh(); //lets add geom+matrix, probably useless, but now I know for sure its there... m.geometry = new Geometry(); matrix = new Matrix3D(); matrix.rawData = Vector.([ 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]); m.transform = matrix; vertexes = []; uvs = []; activeMesh = m; activeContainer.addChild(activeMesh); //add default name in case none is provided few lines down -->todo check uniqueNames m.name = "m_"+meshList.length; meshList.push(m); parsesV = true;//in case of groups, numvert might not be there lastType = "poly"; } if(trunk[1] == "group"){ cont = new ObjectContainer3D(); activeContainer.addChild(cont); cont.name = "c_"+containersList.length; containersList.push(cont); activeContainer = cont; lastType = "group"; } break; case "name": nameid = line.substring(6, line.length-1); if(lastType == "poly"){ activeMesh.name = nameid; } else{ activeContainer.name = nameid; } break; case "numvert":// count vertices in mesh parsesV = true; break; case "SURF"://0x30 if(invalidPoly) invalidPoly = false; break; case "refs": refscount = parseInt(trunk[1]); if(refscount == 4){ isQuad = true; quadCount = 0; } else if( refscount<3 || refscount > 4){ trace("Unsupported polygon type with "+refscount+" sides. Triangulate in AC3D"); invalidPoly = true; } else{ isQuad = false; } parsesV = false; break; case "mat": materialIndexList.push(trunk[1]); break; case "texture": //removing the quotes tUrl = trunk[1].replace("\"", ""); tUrl = unescape(tUrl); if(sourcesPath != "") tUrl = resolveUrl(tUrl); textureList.push(tUrl);//once we know more, we can register to centralLib activeMesh.material = new BitmapFileMaterial(tUrl); break; case "loc"://%f %f %f /* The translation of the object. Effectively the definition of the centre of the object. This is relative to the parent - i.e. not a global position. If this is not found then the default centre of the object will be 0, 0, 0. */ tmpos = new Vector3D(parseFloat(trunk[1])*scaling, parseFloat(trunk[2])*scaling, parseFloat(trunk[3])*scaling); case "rot"://%f %f %f %f %f %f %f %f %f /*The 3x3 rotation matrix for this objects vertices. Note that the rotation is relative to the object's parent i.e. it is not a global rotation matrix. If this token is not specified then the default rotation matrix is 1 0 0, 0 1 0, 0 0 1 */ //Not required as ac 3d applys rotation to vertexes during export //Might be required for containers later on //matrix = new Matrix3D(); //3.5.3 & > /*matrix.rawData = Vector.([parseFloat(trunk[1]),parseFloat(trunk[2]),parseFloat(trunk[3]),0, parseFloat(trunk[4]),parseFloat(trunk[5]),parseFloat(trunk[6]),0, parseFloat(trunk[7]),parseFloat(trunk[8]),parseFloat(trunk[9]),0, 0,0,0,1]);*/ //to do : <3.5.3 and Prefab version /*matrix.sxx = parseFloat(trunk[1]); matrix.sxy = parseFloat(trunk[2]); matrix.sxz = parseFloat(trunk[3]); matrix.tx = 0; matrix.syx = parseFloat(trunk[4]); matrix.syy = parseFloat(trunk[5]); matrix.syz = parseFloat(trunk[6]); matrix.ty = 0; matrix.szx = parseFloat(trunk[7]); matrix.szy = parseFloat(trunk[8]); matrix.szz = parseFloat(trunk[9]); matrix.tz = 1;*/ //activeMesh.transform = matrix; break; default: if(trunk[0] == "" || invalidPoly) break; if(parsesV){ //v0, v1, v2 --> x mirrored for right to left handed system vertexes.push(new Vertex(-(parseFloat(trunk[0]))* scaling, parseFloat(trunk[1])* scaling, parseFloat(trunk[2])* scaling)); } else{ if(isQuad){ quadCount++; if(quadCount == 4){ uvs.push(uvs[uvs.length-2], uvs[uvs.length-1]); uvs.push(parseInt(trunk[0]), new UV(parseFloat(trunk[1]), parseFloat(trunk[2]))); uvs.push(uvs[uvs.length-10], uvs[uvs.length-9]); } else{ uvs.push(parseInt(trunk[0]), new UV(parseFloat(trunk[1]), parseFloat(trunk[2]))); } } else { uvs.push(parseInt(trunk[0]), new UV(parseFloat(trunk[1]), parseFloat(trunk[2]))); } } } } if(activeMesh != null){ CentralMaterialLibrary.addMaterial(activeMesh.material, activeMesh, activeMesh.name, textureList[textureList.length-1]); buildMeshGeometry(activeMesh, vertexes, uvs, tmpos); tmpos = null; activeMesh = null; } materialList = materialIndexList = textureList = meshList = containersList = []; } private function resolveUrl(fileurl:String):String { var split:Array = fileurl.split("/"); var extra:String = (sourcesPath.substring(sourcesPath.length-1, sourcesPath.length) == "/")? "" : "/" ; return sourcesPath+extra+split[split.length-1]; } private function buildMeshGeometry(activeMesh:Mesh, vertexes:Array, uvs:Array, tmpos:Vector3D = null):void { var uv0:UV; var uv1:UV; var uv2:UV; for (var i:int = 0;iAC3D object. * * @param init [optional] An initialisation object for specifying default instance properties. * * @see away3d.loaders.AC3D#parse() * @see away3d.loaders.AC3D#load() */ public function AC3D(init:Object = null) { super(init); sourcesPath = ini.getString("sourcesPath", ""); scaling = ini.getNumber("scaling", 1); binary = false; } /** * Creates a 3d mesh object from the raw ascii data of a .ac file (AC3D). * * @param data The ascii data of a loaded file. * @param init [optional] An initialisation object for specifying default instance properties. * * @return An Object3D representating the ac file. */ public static function parse(data:*, init:Object = null):Object3D { return Loader3D.parse(data, AC3D, init).handle; } /** * Loads and parses a .ac file into an Object3D object. * * @param url The url location of the file to load. * @param init [optional] An initialisation object for specifying default instance properties. * * @return A 3d loader object that can be used as a placeholder in a scene while the file is loading. */ public static function load(url:String, init:Object = null):Loader3D { return Loader3D.load(url, Obj, init); } } }