/** * Metasequoia.as * * @see http://snippets.libspark.org/ * @see http://www.libspark.org/wiki/nanmo/Metasequoia * * Copyright (c) 2008 nanmo * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ /** * History * Make Metasequoia.as(original) by rch850 * in http://snippets.libspark.org/trac/wiki/rch850/Metasequoia * Modify Metasequoia.as for Away3D by katopz@sleepydesign.com * in http://sleepydesign.blogspot.com/2008/05/away3d-metasequoia-mqo.html * Modify Metasequoia.as for Away3D v2.2 by nanmo * in http://www.libspark.org/wiki/nanmo/Metasequoia */ package org.libspark.away3d { import away3d.containers.ObjectContainer3D; import away3d.core.*; import away3d.core.math.*; import away3d.core.base.* import away3d.core.base.*; import away3d.loaders.utils.MaterialLibrary; import away3d.materials.*; import away3d.core.utils.*; import away3d.core.stats.*; import away3d.loaders.*; import flash.utils.*; public class Metasequoia extends AbstractParser { use namespace arcane; /** @private */ arcane var ini:Init; public var charset:String = "shift_jis"; private var mesh:Mesh; private var scaling:Number; public var materialLibrary:MaterialLibrary; private var _materialNames:Array; private var _filename:String; function Metasequoia (data:ByteArray, init:Object = null) { container = new ObjectContainer3D(ini); container.name = "Metasequoia"; init = Init.parse(init); _filename = init.getString("filename", ""); scaling = init.getNumber("scaling", 1)*100; mesh = new Mesh(init); parseMetasequoia(data); container = mesh; } public static function parse(data:*, init:Object = null, loader:Object3DLoader = null):Mesh { return new Metasequoia(Cast.bytearray(data), init).mesh; } public static function load(url:String, init:Object = null):Object3DLoader { init.filename = url; return Object3DLoader.loadGeometry(url, Metasequoia, true, init); } private function parseMetasequoia(data:ByteArray):void { var byteArray:ByteArray = ByteArray(data); var plainText:String = byteArray.readMultiByte(byteArray.length, charset); var lines:Array = plainText.split("\r\n"); var l:int = 0; l = parseMaterialChunk(lines, 0); while (l != -1) { l = parseObjectChunk(lines, l); } mesh.type = ".Mqo"; } private function getMaterialChunkLine(lines:Array, startLine:int = 0):int { for (var i:uint = startLine; i < lines.length; ++i) { if (lines[i].indexOf("Material") == 0) { return int(i); } } return -1; } private function parseMaterialChunk(lines:Array, startLine:int):int { materialLibrary = new MaterialLibrary(); var l:int = getMaterialChunkLine(lines, startLine); if (l == -1) { return -1; } var line:String = lines[l]; var num:Number = parseInt(line.substr(9)); if (isNaN(num)) { return -1; } ++l; _materialNames = new Array(); var endLine:int = l + int(num); //Get Mqo file directory var path:String = _filename.slice(0, _filename.lastIndexOf("/") + 1); for (; l < endLine; ++l) { var _material:ITriangleMaterial; line = lines[l]; var nameBeginIndex:int = line.indexOf("\""); var nameEndIndex:int = line.indexOf("\"", nameBeginIndex + 1); var name:String = line.substring(nameBeginIndex + 1, nameEndIndex); _materialNames.push(name); var tex:String = getParam(line, "tex"); if (tex) { tex = tex.substr(1, tex.length - 2); _material = new BitmapFileMaterial(path + tex,{repeat:true}); } else { var colorstr:String = getParam(line, "col"); if (colorstr != null) { var color:Array = colorstr.match(/\d+\.\d+/g); var r:int = parseFloat(color[0]) * 255; var g:int = parseFloat(color[1]) * 255; var b:int = parseFloat(color[2]) * 255; var a:Number = parseFloat(color[3]); _material = new ColorMaterial((r << 16) | (g << 8) | b,{alpha:a}); } else { //_material = ITriangleMaterial.DEFAULT; } } materialLibrary[name] = _material; } return endLine; } private function getObjectChunkLine(lines:Array, startLine:int = 0):int { for (var i:uint = startLine; i < lines.length; ++i) { if (lines[i].indexOf("Object") == 0) { return int(i); } } return -1; } private function parseObjectChunk(lines:Array, startLine:int):int { var vertices:Array = []; var faces:Array = []; var l:int = getObjectChunkLine(lines, startLine); if (l == -1) { return -1; } var line:String = lines[l]; var objectName:String = line.substring(8, line.indexOf("\"", 8)); ++l; var vline:int = getChunkLine(lines, "vertex", l); if (vline == -1) { return -1; } var properties:Dictionary = new Dictionary(); for (; l < vline; ++l) { line = lines[l]; var props:Array = RegExp(/^\s*([\w]+)\s+(.*)$/).exec(line); properties[props[1]] = props[2]; } line = lines[l]; l = vline + 1; var numVertices:int = parseInt(line.substring(line.indexOf("vertex") + 7)); var vertexEndLine:int = l + numVertices; var firstVertexIndex:int = vertices.length; for (; l < vertexEndLine; ++l) { line = lines[l]; var coords:Array = line.match(/(-?\d+\.\d+)/g); var x:Number = parseFloat(coords[0]) * scaling; var y:Number = parseFloat(coords[1]) * scaling; var z:Number = -parseFloat(coords[2]) * scaling; vertices.push(new Vertex(x,y,z)); } l = getChunkLine(lines, "face", l); if (l == -1) { return -1; } line = lines[l++]; var numFaces:int = parseInt(line.substring(line.indexOf("face") + 5)); var faceEndLine:int = l + numFaces; for (; l < faceEndLine; ++l) { if (properties["visible"] == "15") { parseFace(faces, lines[l], vertices, firstVertexIndex, properties); } } return faceEndLine; } private function parseFace(faces:Array, line:String, vertices:Array, vertexOffset:int, properties:Dictionary):void { var vstr:String = getParam(line, "V"); var mstr:String = getParam(line, "M"); var uvstr:String = getParam(line, "UV"); var v:Array = (vstr != null) ? vstr.match(/\d+/g) : []; var uv:Array = (uvstr != null) ? uvstr.match(/-?\d+\.\d+/g) : []; var a:Vertex; var b:Vertex; var c:Vertex; var d:Vertex; var _material:ITriangleMaterial; var uvA:UV; var uvB:UV; var uvC:UV; var uvD:UV; var face:Face; if (v.length == 3) { c = vertices[parseInt(v[0]) + vertexOffset]; b = vertices[parseInt(v[1]) + vertexOffset]; a = vertices[parseInt(v[2]) + vertexOffset]; if (mstr != null) { _material = materialLibrary[_materialNames[parseInt(mstr)]]; } if (uv.length != 0) { uvC = new UV(parseFloat(uv[0]), 1 - parseFloat(uv[1])); uvB = new UV(parseFloat(uv[2]), 1 - parseFloat(uv[3])); uvA = new UV(parseFloat(uv[4]), 1 - parseFloat(uv[5])); face = new Face(a, b, c, _material, uvA, uvB, uvC); } else { face = new Face(a, b, c, _material, new UV(0, 0), new UV(1, 0), new UV(0, 1)); } mesh.addFace(face); if (properties["mirror"] == "1") { var mirrorAxis:int = parseInt(properties["mirror_axis"]); a = mirrorVertex(a, mirrorAxis); b = mirrorVertex(b, mirrorAxis); c = mirrorVertex(c, mirrorAxis); vertices.push(a); vertices.push(b); vertices.push(c); face = new Face( c, b, a, _material, uvC, uvB, uvA); mesh.addFace(face); } } else if (v.length == 4) { d = vertices[parseInt(v[0]) + vertexOffset]; c = vertices[parseInt(v[1]) + vertexOffset]; b = vertices[parseInt(v[2]) + vertexOffset]; a = vertices[parseInt(v[3]) + vertexOffset]; if (mstr != null) { _material = materialLibrary[_materialNames[parseInt(mstr)]]; } if (uv.length != 0) { uvD = new UV(parseFloat(uv[0]), 1 - parseFloat(uv[1])); uvC = new UV(parseFloat(uv[2]), 1 - parseFloat(uv[3])); uvB = new UV(parseFloat(uv[4]), 1 - parseFloat(uv[5])); uvA = new UV(parseFloat(uv[6]), 1 - parseFloat(uv[7])); } else { uvD = new UV(0, 0); uvC = new UV(1, 0); uvB = new UV(0, 1); uvA = new UV(1, 1); } face = new Face(a, b, c, _material, uvA, uvB, uvC); mesh.addFace(face); face = new Face(c, d, a, _material, uvC, uvD, uvA); mesh.addFace(face); if (properties["mirror"] == "1") { mirrorAxis = parseInt(properties["mirror_axis"]); a = mirrorVertex(a, mirrorAxis); b = mirrorVertex(b, mirrorAxis); c = mirrorVertex(c, mirrorAxis); d = mirrorVertex(d, mirrorAxis); vertices.push(a); vertices.push(b); vertices.push(c); vertices.push(d); face = new Face(c, b, a, _material, uvC, uvB, uvA); mesh.addFace(face); face = new Face(a, d, c, _material, uvA, uvD, uvC); mesh.addFace(face); } } } private static function mirrorVertex(v:Vertex, axis:int):Vertex { return new Vertex( ((axis & 1) != 0) ? -v.x : v.x, ((axis & 2) != 0) ? -v.y : v.y, ((axis & 4) != 0) ? -v.z : v.z); } private static function getChunkLine(lines:Array, chunkName:String, startLine:int = 0):int { for (var i:uint = startLine; i < lines.length; ++i) { if (lines[i].indexOf(chunkName) != -1) { return int(i); } } return -1; } private static function getParam(line:String, paramName:String):String { var prefix:String = paramName + "("; var prefixLen:int = prefix.length; var begin:int = line.indexOf(prefix, 0); if (begin == -1) { return null; } var end:int = line.indexOf(")", begin + prefixLen); if (end == -1){ return null; } return line.substring(begin + prefixLen, end); } } }