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

リビジョン 511, 14.7 kB (コミッタ: rch850, コミット時期: 2 年 前)

Modified class name because it was different from its file name.

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.events.*;
30         import flash.net.URLLoader;
31         import flash.net.URLLoaderDataFormat;
32         import flash.net.URLRequest;
33         import flash.utils.ByteArray;
34         import flash.utils.Dictionary;
35         import org.papervision3d.core.geom.Face3D;
36         import org.papervision3d.core.geom.Mesh3D;
37         import org.papervision3d.core.geom.Vertex3D;
38         import org.papervision3d.core.Matrix3D;
39         import org.papervision3d.core.NumberUV;
40         import org.papervision3d.core.proto.GeometryObject3D;
41         import org.papervision3d.core.proto.MaterialObject3D;
42         import org.papervision3d.events.FileLoadEvent;
43         import org.papervision3d.materials.BitmapFileMaterial;
44         import org.papervision3d.materials.ColorMaterial;
45         import org.papervision3d.materials.MaterialsList;
46         import org.papervision3d.objects.DisplayObject3D;
47        
48         /**
49         * メタセコイアのファイル(.mqo)を読み込むためのクラス。
50         *
51         * var mqo = new Metasequoia();
52         * mqo.addEventListener(...);
53         * mqo.load("hoge.mqo");
54         */
55         public class Metasequoia_1_7 extends Mesh3D {
56                 /**
57                 * コンストラクタ
58                 */
59                 function Metasequoia() {
60                         this.materials = new MaterialsList();
61                         super(null, new Array(), new Array(), null);
62                 }
63                
64                 /**
65                 * @param file 読み込むファイルの URL。絶対パスで指定してください。
66                 * @param scale 読み込むときの拡大率。1 が原寸大です。
67                 */
68                 public function load(file:String, scale:Number = 1):void {
69                         _filename = file;
70                         _scale = scale;
71                         loadMetasequoia();
72                 }
73                
74                 /**
75                  * ファイルの文字コード。よほどのことが無い限り shift_jis だと思います。
76                  */
77                 public var charset:String = "shift_jis";
78                
79                 /**
80                  * 面の両側にマテリアルを貼るかどうかを指定します。
81                  */
82                 public var doubleSided:Boolean = false;
83                
84                 private var _loader:URLLoader;
85                 private var _filename:String;
86                 private var _materialsToLoad:int =0;
87                 private var _materialNames:Array;
88                 private var _scale:Number = 1;
89                
90                 private function loadMetasequoia():void {
91                         _loader = new URLLoader();
92                         _loader.dataFormat = URLLoaderDataFormat.BINARY;
93                         _loader.addEventListener(Event.COMPLETE, completeHandler);
94                         _loader.addEventListener(IOErrorEvent.IO_ERROR, defaultHandler);
95                         _loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, defaultHandler);
96                         _loader.addEventListener(ProgressEvent.PROGRESS, defaultHandler);
97                         _loader.load(new URLRequest(_filename));
98                 }
99                
100                 private function completeHandler(evt:Event):void {
101                         var byteArray:ByteArray = ByteArray(_loader.data);
102                         buildMetasequoia(byteArray.readMultiByte(byteArray.length, charset));
103                         dispatchEvent(evt.clone());
104                 }
105                
106                 private function defaultHandler(evt:Event):void {
107                         dispatchEvent(evt.clone());
108                 }
109                
110                 private function buildMetasequoia(plainText:String):void {
111                         var lines:Array = plainText.split("\r\n");
112                         trace("num lines = " + lines.length);
113                         var l:int = 0;
114                        
115                         // Material チャンクを読み込む
116                         l = parseMaterialChunk(lines, 0);
117                        
118                         // Object チャンクを読み込めなくなるまで読み込む
119                         while (l != -1) {
120                                 l = parseObjectChunk(lines, l);
121                         }
122                        
123                         geometry.ready = true;
124                 }
125                
126                 /**
127                 * Material チャンクの開始行を返します。
128                 * 見つからなかった場合には -1 を返します。
129                 */
130                 private function getMaterialChunkLine(lines:Array, startLine:int = 0):int {
131                         for (var i:uint = startLine; i < lines.length; ++i) {
132                                 if (lines[i].indexOf("Material") == 0) {
133                                         return int(i);
134                                 }
135                         }
136                         return -1;
137                 }
138                
139                 /**
140                 * Material チャンクを読み込み、その最後の行番号を返します。
141                 * エラーが起こった場合は -1 を返します。
142                 */
143                 private function parseMaterialChunk(lines:Array, startLine:int):int {
144                         var l:int = getMaterialChunkLine(lines, startLine);
145                         if (l == -1) {
146                                 return -1;
147                         }
148                        
149                         // 解析中の行の文字列
150                         var line:String = lines[l];
151                        
152                         // マテリアル数を取得
153                         var num:Number = parseInt(line.substr(9));
154                         if (isNaN(num)) {
155                                 return -1;
156                         }
157                         ++l;
158                         _materialNames = new Array();
159                        
160                         // } で閉じているところの行番号
161                         var endLine:int = l + int(num);
162                        
163                         // mqo ファイルのあるディレクトリのパス
164                         var path:String = _filename.slice(0, _filename.lastIndexOf("/") + 1);
165                        
166                         for (; l < endLine; ++l) {
167                                 var material:MaterialObject3D;
168                                 line = lines[l];
169                                
170                                 // マテリアルの名前を取得
171                                 var nameBeginIndex:int = line.indexOf("\"");
172                                 var nameEndIndex:int = line.indexOf("\"", nameBeginIndex + 1);
173                                 var name:String = line.substring(nameBeginIndex + 1, nameEndIndex);
174                                 _materialNames.push(name);
175                                
176                                 // テクスチャファイル名
177                                 var tex:String = getParam(line, "tex");
178                                
179                                 if (tex) {
180                                         // テクスチャファイル名を取り囲む " " を取り除く
181                                         tex = tex.substr(1, tex.length - 2);
182                                         _materialsToLoad++;
183                                         // テクスチャの URL を絶対にして読み込む
184                                         material = new BitmapFileMaterial(path + tex);
185                                         material.addEventListener(FileLoadEvent.LOAD_COMPLETE, materialLoadCompleteHandler);
186                                         material.addEventListener(FileLoadEvent.LOAD_ERROR, materialLoadErrorHandler);
187                                         // あまり重さが変わらないのでせっかくだからスムージング
188                                         material.smooth = true;
189                                 } else {
190                                         // 形式 - col(1.000 1.000 0.000 1.000)
191                                         var colorstr:String = getParam(line, "col");
192                                         if (colorstr != null) {
193                                                 var color:Array = colorstr.match(/\d+\.\d+/g);
194                                                 var r:int = parseFloat(color[0]) * 255;
195                                                 var g:int = parseFloat(color[1]) * 255;
196                                                 var b:int = parseFloat(color[2]) * 255;
197                                                 var a:Number = parseFloat(color[3]) * 100;
198                                                 trace("rgb = " + r + "," + g + "," + b);
199                                                 material = new ColorMaterial((r << 16) | (g << 8) | b);
200                                         } else {
201                                                 material = MaterialObject3D.DEFAULT;
202                                         }
203                                 }
204                                
205                                 material.doubleSided = this.doubleSided;
206                                 material.name = name;
207                                
208                                 materials.addMaterial(material, name);
209                         }
210                        
211                         return endLine;
212                 }
213                
214                 private function materialLoadCompleteHandler(evt:FileLoadEvent):void {
215                         _materialsToLoad--;
216                         if(_materialsToLoad == 0){
217                                 //COLLADA のソースにあった謎の一行。不具合の元になるのでコメントアウト
218                                 //materials = new MaterialsList();
219                                 dispatchEvent(new FileLoadEvent(FileLoadEvent.COLLADA_MATERIALS_DONE));
220                         }
221                 }
222                
223                 private function materialLoadErrorHandler(evt:FileLoadEvent):void {
224                         _materialsToLoad--;
225                         if(_materialsToLoad == 0){
226                                 dispatchEvent(new FileLoadEvent(FileLoadEvent.COLLADA_MATERIALS_DONE));
227                         }
228                 }
229                
230                 /**
231                 * Object チャンクの開始行を返します。
232                 * 見つからなかった場合には -1 を返します。
233                 */
234                 private function getObjectChunkLine(lines:Array, startLine:int = 0):int {
235                         for (var i:uint = startLine; i < lines.length; ++i) {
236                                 if (lines[i].indexOf("Object") == 0) {
237                                         return int(i);
238                                 }
239                         }
240                         return -1;
241                 }
242                
243                 /**
244                 * Object チャンクを読み込み、その最後の行番号を返します。
245                 * エラーが起こった場合は -1 を返します。
246                 */
247                 private function parseObjectChunk(lines:Array, startLine:int):int {
248                         var vertices:Array = geometry.vertices;
249                         var faces:Array = geometry.faces;
250                        
251                         var l:int = getObjectChunkLine(lines, startLine);
252                         if (l == -1) {
253                                 return -1;
254                         }
255                        
256                         // 解析中の行の文字列
257                         var line:String = lines[l];
258                        
259                         // オブジェクト名を取得
260                         var objectName:String = line.substring(8, line.indexOf("\"", 8));
261                         ++l;
262                        
263                         // vertex チャンクを検索
264                         var vline:int = getChunkLine(lines, "vertex", l);
265                         if (vline == -1) {
266                                 return -1;
267                         }
268                        
269                         // プロパティを読み込む
270                         var properties:Dictionary = new Dictionary();
271                         for (; l < vline; ++l) {
272                                 line = lines[l];
273                                 var props:Array = RegExp(/^\s*([\w]+)\s+(.*)$/).exec(line);
274                                 properties[props[1]] = props[2];
275                         }
276                        
277                         line = lines[l];
278                         l = vline + 1;
279                        
280                         // 頂点数を取得
281                         var numVertices:int = parseInt(line.substring(line.indexOf("vertex") + 7));
282                         var vertexEndLine:int = l + numVertices;
283                         var firstVertexIndex:int = vertices.length;
284                        
285                         // vertex チャンクを読み込む
286                         for (; l < vertexEndLine; ++l) {
287                                 line = lines[l];
288                                 var coords:Array = line.match(/(-?\d+\.\d+)/g);
289                                 var x:Number = parseFloat(coords[0]) * _scale;
290                                 var y:Number = parseFloat(coords[1]) * _scale;
291                                 var z:Number = parseFloat(coords[2]) * _scale;
292                                 vertices.push(new Vertex3D(x, y, z));
293                         }
294                        
295                         // face チャンクを検索
296                         l = getChunkLine(lines,  "face", l);
297                         if (l == -1) {
298                                 return -1;
299                         }
300                         line = lines[l++];
301                        
302                         // 面数を取得
303                         var numFaces:int = parseInt(line.substring(line.indexOf("face") + 5));
304                         var faceEndLine:int = l + numFaces;
305                        
306                         // face チャンクを読み込む
307                         for (; l < faceEndLine; ++l) {
308                                 if (properties["visible"] == "15") {
309                                         parseFace(faces, lines[l], vertices, firstVertexIndex, properties);
310                                 }
311                         }
312                        
313                         return l;
314                 }
315                
316                 private function parseFace(faces:Array, line:String, vertices:Array, vertexOffset:int,
317                                 properties:Dictionary):void {
318                         var vstr:String = getParam(line, "V");
319                         var mstr:String = getParam(line, "M");
320                         var uvstr:String = getParam(line, "UV");
321                        
322                         var v:Array = (vstr != null) ? vstr.match(/\d+/g) : [];
323                         var uv:Array = (uvstr != null) ? uvstr.match(/-?\d+\.\d+/g) : [];
324                         var a:Vertex3D;
325                         var b:Vertex3D;
326                         var c:Vertex3D;
327                         var d:Vertex3D;
328                         var material:MaterialObject3D;
329                         var uvA:NumberUV;
330                         var uvB:NumberUV;
331                         var uvC:NumberUV;
332                         var uvD:NumberUV;
333                         var face:Face3D;
334                        
335                         if (v.length == 3) {
336                                 a = vertices[parseInt(v[0]) + vertexOffset];
337                                 b = vertices[parseInt(v[1]) + vertexOffset];
338                                 c = vertices[parseInt(v[2]) + vertexOffset];
339                                
340                                 if (mstr != null) {
341                                         material = materials.getMaterialByName(_materialNames[parseInt(mstr)]);
342                                 }
343                                
344                                 if (uv.length != 0) {
345                                         uvA = new NumberUV(parseFloat(uv[0]), 1 - parseFloat(uv[1]));
346                                         uvB = new NumberUV(parseFloat(uv[2]), 1 - parseFloat(uv[3]));
347                                         uvC = new NumberUV(parseFloat(uv[4]), 1 - parseFloat(uv[5]));
348                                         face = new Face3D([a, b, c], material, [uvA, uvB, uvC]);
349                                 } else {
350                                         face = new Face3D([a, b, c], material,
351                                                 [new NumberUV(0, 0), new NumberUV(1, 0), new NumberUV(0, 1)]);
352                                 }
353                                
354                                 faces.push(face);
355                                
356                                 if (properties["mirror"] == "1") {
357                                         var mirrorAxis:int = parseInt(properties["mirror_axis"]);
358                                         a = mirrorVertex(a, mirrorAxis);
359                                         b = mirrorVertex(b, mirrorAxis);
360                                         c = mirrorVertex(c, mirrorAxis);
361                                         vertices.push(a);
362                                         vertices.push(b);
363                                         vertices.push(c);
364                                         face = new Face3D([a, b, c], material, face.uv);
365                                         faces.push(face);
366                                 }
367                         } else if (v.length == 4) {
368                                 a = vertices[parseInt(v[0]) + vertexOffset];
369                                 b = vertices[parseInt(v[1]) + vertexOffset];
370                                 c = vertices[parseInt(v[2]) + vertexOffset];
371                                 d = vertices[parseInt(v[3]) + vertexOffset];
372                                
373                                 if (mstr != null) {
374                                         material = materials.getMaterialByName(_materialNames[parseInt(mstr)]);
375                                 }
376                                
377                                 if (uv.length != 0) {
378                                         uvA = new NumberUV(parseFloat(uv[0]), 1 - parseFloat(uv[1]));
379                                         uvB = new NumberUV(parseFloat(uv[2]), 1 - parseFloat(uv[3]));
380                                         uvC = new NumberUV(parseFloat(uv[4]), 1 - parseFloat(uv[5]));
381                                         uvD = new NumberUV(parseFloat(uv[6]), 1 - parseFloat(uv[7]));
382                                 } else {
383                                         uvA = new NumberUV(0, 0);
384                                         uvB = new NumberUV(1, 0);
385                                         uvC = new NumberUV(0, 1);
386                                         uvD = new NumberUV(1, 1);
387                                 }
388                                 face = new Face3D([a, b, c], material, [uvA, uvB, uvC]);
389                                 faces.push(face);
390                                 face = new Face3D([c, d, a], material, [uvC, uvD, uvA]);
391                                 faces.push(face);
392                                
393                                 if (properties["mirror"] == "1") {
394                                         mirrorAxis = parseInt(properties["mirror_axis"]);
395                                         a = mirrorVertex(a, mirrorAxis);
396                                         b = mirrorVertex(b, mirrorAxis);
397                                         c = mirrorVertex(c, mirrorAxis);
398                                         d = mirrorVertex(d, mirrorAxis);
399                                         vertices.push(a);
400                                         vertices.push(b);
401                                         vertices.push(c);
402                                         vertices.push(d);
403                                         face = new Face3D([a, b, c], material, [uvA, uvB, uvC]);
404                                         faces.push(face);
405                                         face = new Face3D([c, d, a], material, [uvC, uvD, uvA]);
406                                         faces.push(face);
407                                 }
408                         }
409                 }
410                
411                 /**
412                 * 頂点を軸に沿って反転させたものを返します。
413                 */
414                 private static function mirrorVertex(v:Vertex3D, axis:int):Vertex3D {
415                         return new Vertex3D(
416                                 ((axis & 1) != 0) ? -v.x : v.x,
417                                 ((axis & 2) != 0) ? -v.y : v.y,
418                                 ((axis & 4) != 0) ? -v.z : v.z);
419                 }
420                
421                 /**
422                 * Object チャンクの開始行を返します。
423                 */
424                 private static function getChunkLine(lines:Array, chunkName:String, startLine:int = 0):int {
425                         for (var i:uint = startLine; i < lines.length; ++i) {
426                                 if (lines[i].indexOf(chunkName) != -1) {
427                                         return int(i);
428                                 }
429                         }
430                         return -1;
431                 }
432                
433                 /**
434                 * line 内で paramName(...) という形式で指定されているパラメータを返します。
435                 */
436                 private static function getParam(line:String, paramName:String):String {
437                         var prefix:String = paramName + "(";
438                         var prefixLen:int = prefix.length;
439                        
440                         var begin:int = line.indexOf(prefix, 0);
441                         if (begin == -1) {
442                                 return null;
443                         }
444                         var end:int = line.indexOf(")", begin + prefixLen);
445                         if (end == -1){
446                                 return null;
447                         }
448                         return line.substring(begin + prefixLen, end);
449                 }
450         }
451 }
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。