root/as3/Metasequoia/src/org/libspark/pv3d/Metasequoia.as

リビジョン 330, 17.4 kB (コミッタ: Mk-10, コミット時期: 2 年 前)

Metasequoia に勝手に機能追加
nondelion さんが BMP を読めるようにしていたので本体自体も修正してみたよ

Line 
1 /**
2  * Metasequoia.as
3  *
4  * @see http://snippets.libspark.org/
5  * @see http://snippets.libspark.org/trac/wiki/rch850/Metasequoia
6  *
7  * Copyright (c) 2007-2008 rch850
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a copy
10  * of this software and associated documentation files (the "Software"), to deal
11  * in the Software without restriction, including without limitation the rights
12  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13  * copies of the Software, and to permit persons to whom the Software is
14  * furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be included in
17  * all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25  * THE SOFTWARE.
26  */
27
28 package org.libspark.pv3d {
29         import flash.display.BitmapData;
30         import flash.events.*;
31         import flash.net.URLLoader;
32         import flash.net.URLLoaderDataFormat;
33         import flash.net.URLRequest;
34         import flash.utils.ByteArray;
35         import flash.utils.Dictionary;
36         import org.libspark.pv3d.decoders.TGADecoder;
37         import org.papervision3d.core.geom.TriangleMesh3D;
38         import org.papervision3d.core.geom.renderables.Triangle3D;
39         import org.papervision3d.core.geom.renderables.Vertex3D;
40         import org.papervision3d.core.math.Matrix3D;
41         import org.papervision3d.core.math.NumberUV;
42         import org.papervision3d.core.proto.DisplayObjectContainer3D;
43         import org.papervision3d.core.proto.GeometryObject3D;
44         import org.papervision3d.core.proto.MaterialObject3D;
45         import org.papervision3d.events.FileLoadEvent;
46         import org.papervision3d.events.InteractiveScene3DEvent;
47         import org.papervision3d.materials.BitmapFileMaterial;
48         import org.papervision3d.materials.BitmapMaterial;
49         import org.papervision3d.materials.ColorMaterial;
50         import org.papervision3d.materials.utils.MaterialsList;
51         import org.papervision3d.objects.DisplayObject3D;
52
53         import com.voidelement.images.BMPDecoder;       
54         /**
55         * メタセコイアのファイル(.mqo)を読み込むためのクラス。
56         *
57         * var mqo = new Metasequoia();
58         * mqo.addEventListener(...);
59         * mqo.load("hoge.mqo");
60         */
61         public class Metasequoia extends TriangleMesh3D {
62                 /**
63                 * コンストラクタ
64                 */
65                 function Metasequoia() {
66                         this.materials = new MaterialsList();
67                         super(null, new Array(), new Array(), null);
68                 }
69                
70                 /**
71                 * @param file 読み込むファイルの URL。絶対パスで指定してください。
72                 * @param scale 読み込むときの拡大率。1 が原寸大です。
73                 */
74                 public function load(file:String, scale:Number = 1):void {
75                         _filename = file;
76                         _scale = scale;
77                         loadMetasequoia();
78                 }
79                
80                 /**
81                  * ファイルの文字コード。よほどのことが無い限り shift_jis だと思います。
82                  */
83                 public var charset:String = "shift_jis";
84                
85                 /**
86                  * 面の両側にマテリアルを貼るかどうかを指定します。
87                  */
88                 public var doubleSided:Boolean = false;
89                
90                 /**
91                  * インタラクティビティを設定します。
92                  */
93                 public var interactive:Boolean = false;
94                
95                 private var _loader:URLLoader;
96                 private var _filename:String;
97                 private var _materialsToLoad:int =0;
98                 private var _materialNames:Array;
99                 private var _scale:Number = 1;
100                 private var _prevMesh:DisplayObject3D;
101                 private var _prevDepth:int;
102                
103                 private function loadMetasequoia():void {
104                         _loader = new URLLoader();
105                         _loader.dataFormat = URLLoaderDataFormat.BINARY;
106                         _loader.addEventListener(Event.COMPLETE, completeHandler);
107                         _loader.addEventListener(IOErrorEvent.IO_ERROR, defaultHandler);
108                         _loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, defaultHandler);
109                         _loader.addEventListener(ProgressEvent.PROGRESS, defaultHandler);
110                         _loader.load(new URLRequest(_filename));
111                 }
112                
113                 private function completeHandler(evt:Event):void {
114                         var byteArray:ByteArray = ByteArray(_loader.data);
115                         buildMetasequoia(byteArray.readMultiByte(byteArray.length, charset));
116                         dispatchEvent(evt.clone());
117                 }
118                
119                 private function defaultHandler(evt:Event):void {
120                         dispatchEvent(evt.clone());
121                 }
122                
123                 private function buildMetasequoia(plainText:String):void {
124                         var lines:Array = plainText.split("\r\n");
125                         //trace("num lines = " + lines.length);
126                         var l:int = 0;
127                        
128                         // Material チャンクを読み込む
129                         l = parseMaterialChunk(lines, 0);
130                        
131                         _prevDepth = 0;
132                         _prevMesh = this;
133                        
134                         // Object チャンクを読み込めなくなるまで読み込む
135                         while (l != -1) {
136                                 l = parseObjectChunk(lines, l);
137                         }
138                        
139                         geometry.ready = true;
140                 }
141                
142                 /**
143                 * Material チャンクの開始行を返します。
144                 * 見つからなかった場合には -1 を返します。
145                 */
146                 private function getMaterialChunkLine(lines:Array, startLine:int = 0):int {
147                         for (var i:uint = startLine; i < lines.length; ++i) {
148                                 if (lines[i].indexOf("Material") == 0) {
149                                         return int(i);
150                                 }
151                         }
152                         return -1;
153                 }
154                
155                 /**
156                 * Material チャンクを読み込み、その最後の行番号を返します。
157                 * エラーが起こった場合は -1 を返します。
158                 */
159                 private function parseMaterialChunk(lines:Array, startLine:int):int {
160                         var l:int = getMaterialChunkLine(lines, startLine);
161                         if (l == -1) {
162                                 return -1;
163                         }
164                        
165                         // 解析中の行の文字列
166                         var line:String = lines[l];
167                        
168                         // マテリアル数を取得
169                         var num:Number = parseInt(line.substr(9));
170                         if (isNaN(num)) {
171                                 return -1;
172                         }
173                         ++l;
174                         _materialNames = new Array();
175                        
176                         // } で閉じているところの行番号
177                         var endLine:int = l + int(num);
178                        
179                         // mqo ファイルのあるディレクトリのパス
180                         var path:String = _filename.slice(0, _filename.lastIndexOf("/") + 1);
181                        
182                         for (; l < endLine; ++l) {
183                                 var material:MaterialObject3D;
184                                 line = lines[l];
185                                
186                                 // マテリアルの名前を取得
187                                 var nameBeginIndex:int = line.indexOf("\"");
188                                 var nameEndIndex:int = line.indexOf("\"", nameBeginIndex + 1);
189                                 var name:String = line.substring(nameBeginIndex + 1, nameEndIndex);
190                                 _materialNames.push(name);
191                                
192                                 // テクスチャファイル名
193                                 var tex:String = getParam(line, "tex");
194                                
195                                 if (tex) {
196                                         // テクスチャファイル名を取り囲む " " を取り除く
197                                         tex = tex.substr(1, tex.length - 2);
198                                         _materialsToLoad++;
199                                         if (tex.toLowerCase().search(/\.tga$/) != -1) {
200                                                 material = loadTGAMaterial(path + tex);
201                                         } else if (tex.toLowerCase().search(/\.bmp$/) != -1) {
202                                                 material = loadBMPMaterial(path + tex);
203                                         } else {
204                                                 // テクスチャの URL を絶対にして読み込む
205                                                 material = new BitmapFileMaterial(path + tex);
206                                                 material.addEventListener(FileLoadEvent.LOAD_COMPLETE, materialLoadCompleteHandler);
207                                                 material.addEventListener(FileLoadEvent.LOAD_ERROR, materialLoadErrorHandler);
208                                         }
209                                         // あまり重さが変わらないのでせっかくだからスムージング
210                                         material.smooth = true;
211                                 } else {
212                                         // 形式 - col(1.000 1.000 0.000 1.000)
213                                         var colorstr:String = getParam(line, "col");
214                                         if (colorstr != null) {
215                                                 var color:Array = colorstr.match(/\d+\.\d+/g);
216                                                 var r:int = parseFloat(color[0]) * 255;
217                                                 var g:int = parseFloat(color[1]) * 255;
218                                                 var b:int = parseFloat(color[2]) * 255;
219                                                 var a:Number = parseFloat(color[3]) * 100;
220                                                 //trace("rgb = " + r + "," + g + "," + b);
221                                                 material = new ColorMaterial((r << 16) | (g << 8) | b);
222                                         } else {
223                                                 material = MaterialObject3D.DEFAULT;
224                                         }
225                                 }
226                                
227                                 material.doubleSided = this.doubleSided;
228                                 material.interactive = this.interactive;
229                                 material.name = name;
230                                
231                                 materials.addMaterial(material, name);
232                         }
233                        
234                         return endLine;
235                 }
236                
237                 /**
238                  * Creates a BitmapMaterial from TGA file and returns it.
239                  */
240                 private function loadTGAMaterial(url:String):BitmapMaterial {
241                         var material:BitmapMaterial = new BitmapMaterial();
242                         var loader:URLLoader = new URLLoader();
243                         loader.dataFormat = URLLoaderDataFormat.BINARY;
244                         loader.addEventListener(Event.COMPLETE, function(event:Event):void {
245                                 var tga:TGADecoder = new TGADecoder(loader.data);
246                                 material.bitmap = tga.bitmap;
247                                 material.maxU = material.maxV = 1;
248                                 material.resetMapping();
249                         });
250                         loader.load(new URLRequest(url));
251                         return material;
252                 }
253                
254                 private function loadBMPMaterial(url:String):BitmapMaterial
255                 {
256                         var material:BitmapMaterial = new BitmapMaterial();
257                         var loader:URLLoader = new URLLoader();
258                         loader.dataFormat = URLLoaderDataFormat.BINARY;
259                         loader.addEventListener(Event.COMPLETE, function(event:Event):void {
260                             var bmpLoader:URLLoader = event.target as URLLoader;
261                             var decoder:BMPDecoder = new BMPDecoder();
262                             material.bitmap = decoder.decode(bmpLoader.data);
263                             material.maxU = material.maxV = 1;
264                             material.resetMapping();
265                         });
266                         loader.load(new URLRequest(url));
267                         return material;
268                 }
269                
270                 private function materialLoadCompleteHandler(evt:FileLoadEvent):void {
271                         _materialsToLoad--;
272                         if(_materialsToLoad == 0){
273                                 //COLLADA のソースにあった謎の一行。不具合の元になるのでコメントアウト
274                                 //materials = new MaterialsList();
275                                 dispatchEvent(new FileLoadEvent(FileLoadEvent.COLLADA_MATERIALS_DONE));
276                         }
277                 }
278                
279                 private function materialLoadErrorHandler(evt:FileLoadEvent):void {
280                         _materialsToLoad--;
281                         if(_materialsToLoad == 0){
282                                 dispatchEvent(new FileLoadEvent(FileLoadEvent.COLLADA_MATERIALS_DONE));
283                         }
284                 }
285                
286                 /**
287                 * Object チャンクの開始行を返します。
288                 * 見つからなかった場合には -1 を返します。
289                 */
290                 private function getObjectChunkLine(lines:Array, startLine:int = 0):int {
291                         for (var i:uint = startLine; i < lines.length; ++i) {
292                                 if (lines[i].indexOf("Object") == 0) {
293                                         return int(i);
294                                 }
295                         }
296                         return -1;
297                 }
298                
299                 /**
300                 * Object チャンクを読み込み、その最後の行番号を返します。
301                 * エラーが起こった場合は -1 を返します。
302                 */
303                 private function parseObjectChunk(lines:Array, startLine:int):int {
304                         var l:int = getObjectChunkLine(lines, startLine);
305                         if (l == -1) {
306                                 return -1;
307                         }
308                        
309                         // 解析中の行の文字列
310                         var line:String = lines[l];
311                        
312                         // オブジェクト名を取得
313                         var objectName:String = line.substring(8, line.indexOf("\"", 8));
314                         ++l;
315                        
316                         var mesh:TriangleMesh3D = new TriangleMesh3D(null, new Array(), new Array(), objectName);
317                         var vertices:Array = mesh.geometry.vertices;
318                         var faces:Array = mesh.geometry.faces;
319                        
320                         // vertex チャンクを検索
321                         var vline:int = getChunkLine(lines, "vertex", l);
322                         if (vline == -1) {
323                                 return -1;
324                         }
325                        
326                         // プロパティを読み込む
327                         var properties:Dictionary = new Dictionary();
328                         for (; l < vline; ++l) {
329                                 line = lines[l];
330                                 var props:Array = RegExp(/^\s*([\w]+)\s+(.*)$/).exec(line);
331                                 properties[props[1]] = props[2];
332                         }
333                        
334                         line = lines[l];
335                         l = vline + 1;
336                        
337                         // 頂点数を取得
338                         var numVertices:int = parseInt(line.substring(line.indexOf("vertex") + 7));
339                         var vertexEndLine:int = l + numVertices;
340                         var firstVertexIndex:int = vertices.length;
341                        
342                         // vertex チャンクを読み込む
343                         for (; l < vertexEndLine; ++l) {
344                                 line = lines[l];
345                                 var coords:Array = line.match(/(-?\d+\.\d+)/g);
346                                 var x:Number = parseFloat(coords[0]) * _scale;
347                                 var y:Number = parseFloat(coords[1]) * _scale;
348                                 var z:Number = -parseFloat(coords[2]) * _scale;
349                                 vertices.push(new Vertex3D(x, y, z));
350                         }
351                        
352                         // face チャンクを検索
353                         l = getChunkLine(lines,  "face", l);
354                         if (l == -1) {
355                                 return -1;
356                         }
357                         line = lines[l++];
358                        
359                         // 面数を取得
360                         var numFaces:int = parseInt(line.substring(line.indexOf("face") + 5));
361                         var faceEndLine:int = l + numFaces;
362                        
363                         // face チャンクを読み込む
364                         for (; l < faceEndLine; ++l) {
365                                 if (properties["visible"] == "15") {
366                                         parseFace(faces, lines[l], vertices, firstVertexIndex, properties);
367                                 }
368                         }
369                        
370                         // Resolve parent-child relationship.
371                         var depth:int;
372                         try {
373                                 depth = parseInt(properties["depth"]);
374                         } catch (e:Error) {
375                                 depth = 0;
376                         }
377                         var parentMesh:DisplayObjectContainer3D = _prevMesh;
378                         if (depth <= 0) {
379                                 parentMesh = this;
380                                 depth = 0;
381                         } else {
382                                 while (depth <= _prevDepth) {
383                                         parentMesh = DisplayObject3D(parentMesh).parent;
384                                         --_prevDepth;
385                                 }
386                         }
387                         parentMesh.addChild(mesh);
388                         _prevMesh = mesh;
389                         _prevDepth = depth;
390                        
391                         return l;
392                 }
393                
394                 private function parseFace(faces:Array, line:String, vertices:Array, vertexOffset:int,
395                                 properties:Dictionary):void {
396                         var vstr:String = getParam(line, "V");
397                         var mstr:String = getParam(line, "M");
398                         var uvstr:String = getParam(line, "UV");
399                        
400                         var v:Array = (vstr != null) ? vstr.match(/\d+/g) : [];
401                         var uv:Array = (uvstr != null) ? uvstr.match(/-?\d+\.\d+/g) : [];
402                         var a:Vertex3D;
403                         var b:Vertex3D;
404                         var c:Vertex3D;
405                         var d:Vertex3D;
406                         var material:MaterialObject3D;
407                         var uvA:NumberUV;
408                         var uvB:NumberUV;
409                         var uvC:NumberUV;
410                         var uvD:NumberUV;
411                         var face:Triangle3D;
412                        
413                         if (v.length == 3) {
414                                 c = vertices[parseInt(v[0]) + vertexOffset];
415                                 b = vertices[parseInt(v[1]) + vertexOffset];
416                                 a = vertices[parseInt(v[2]) + vertexOffset];
417                                
418                                 if (mstr != null) {
419                                         material = materials.getMaterialByName(_materialNames[parseInt(mstr)]);
420                                 }
421                                
422                                 if (uv.length != 0) {
423                                         uvC = new NumberUV(parseFloat(uv[0]), 1 - parseFloat(uv[1]));
424                                         uvB = new NumberUV(parseFloat(uv[2]), 1 - parseFloat(uv[3]));
425                                         uvA = new NumberUV(parseFloat(uv[4]), 1 - parseFloat(uv[5]));
426                                         face = new Triangle3D(this, [a, b, c], material, [uvA, uvB, uvC]);
427                                 } else {
428                                         face = new Triangle3D(this, [a, b, c], material,
429                                                 [new NumberUV(0, 0), new NumberUV(1, 0), new NumberUV(0, 1)]);
430                                 }
431                                
432                                 faces.push(face);
433                                
434                                 if (properties["mirror"] == "1") {
435                                         var mirrorAxis:int = parseInt(properties["mirror_axis"]);
436                                         a = mirrorVertex(a, mirrorAxis);
437                                         b = mirrorVertex(b, mirrorAxis);
438                                         c = mirrorVertex(c, mirrorAxis);
439                                         vertices.push(a);
440                                         vertices.push(b);
441                                         vertices.push(c);
442                                         face = new Triangle3D(this, [c, b, a], material, face.uv.reverse());
443                                         faces.push(face);
444                                 }
445                         } else if (v.length == 4) {
446                                 d = vertices[parseInt(v[0]) + vertexOffset];
447                                 c = vertices[parseInt(v[1]) + vertexOffset];
448                                 b = vertices[parseInt(v[2]) + vertexOffset];
449                                 a = vertices[parseInt(v[3]) + vertexOffset];
450                                
451                                 if (mstr != null) {
452                                         material = materials.getMaterialByName(_materialNames[parseInt(mstr)]);
453                                 }
454                                
455                                 if (uv.length != 0) {
456                                         uvD = new NumberUV(parseFloat(uv[0]), 1 - parseFloat(uv[1]));
457                                         uvC = new NumberUV(parseFloat(uv[2]), 1 - parseFloat(uv[3]));
458                                         uvB = new NumberUV(parseFloat(uv[4]), 1 - parseFloat(uv[5]));
459                                         uvA = new NumberUV(parseFloat(uv[6]), 1 - parseFloat(uv[7]));
460                                 } else {
461                                         uvD = new NumberUV(0, 0);
462                                         uvC = new NumberUV(1, 0);
463                                         uvB = new NumberUV(0, 1);
464                                         uvA = new NumberUV(1, 1);
465                                 }
466                                 face = new Triangle3D(this, [a, b, c], material, [uvA, uvB, uvC]);
467                                 faces.push(face);
468                                 face = new Triangle3D(this, [c, d, a], material, [uvC, uvD, uvA]);
469                                 faces.push(face);
470                                
471                                 if (properties["mirror"] == "1") {
472                                         mirrorAxis = parseInt(properties["mirror_axis"]);
473                                         a = mirrorVertex(a, mirrorAxis);
474                                         b = mirrorVertex(b, mirrorAxis);
475                                         c = mirrorVertex(c, mirrorAxis);
476                                         d = mirrorVertex(d, mirrorAxis);
477                                         vertices.push(a);
478                                         vertices.push(b);
479                                         vertices.push(c);
480                                         vertices.push(d);
481                                         face = new Triangle3D(this, [c, b, a], material, [uvC, uvB, uvA]);
482                                         faces.push(face);
483                                         face = new Triangle3D(this, [a, d, c], material, [uvA, uvD, uvC]);
484                                         faces.push(face);
485                                 }
486                         }
487                 }
488                
489                 /**
490                 * 頂点を軸に沿って反転させたものを返します。
491                 */
492                 private static function mirrorVertex(v:Vertex3D, axis:int):Vertex3D {
493                         return new Vertex3D(
494                                 ((axis & 1) != 0) ? -v.x : v.x,
495                                 ((axis & 2) != 0) ? -v.y : v.y,
496                                 ((axis & 4) != 0) ? -v.z : v.z);
497                 }
498                
499                 /**
500                 * Object チャンクの開始行を返します。
501                 */
502                 private static function getChunkLine(lines:Array, chunkName:String, startLine:int = 0):int {
503                         for (var i:uint = startLine; i < lines.length; ++i) {
504                                 if (lines[i].indexOf(chunkName) != -1) {
505                                         return int(i);
506                                 }
507                         }
508                         return -1;
509                 }
510                
511                 /**
512                 * line 内で paramName(...) という形式で指定されているパラメータを返します。
513                 */
514                 private static function getParam(line:String, paramName:String):String {
515                         var prefix:String = paramName + "(";
516                         var prefixLen:int = prefix.length;
517                        
518                         var begin:int = line.indexOf(prefix, 0);
519                         if (begin == -1) {
520                                 return null;
521                         }
522                         var end:int = line.indexOf(")", begin + prefixLen);
523                         if (end == -1){
524                                 return null;
525                         }
526                         return line.substring(begin + prefixLen, end);
527                 }
528         }
529 }
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。