Metasequoia改造論(2) 透明マップの読み込み
MetasequoiaMaterialの使用
次に、Metasequoia用のマテリアルを扱う、MetasequoiaMaterialというクラスを準備する。
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.BitmapDataChannel;
import flash.display.Graphics;
import flash.display.Loader;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.utils.Dictionary;
import flash.geom.Point;
import flash.net.URLRequest;
import org.papervision3d.materials.BitmapMaterial;
import org.papervision3d.objects.DisplayObject3D;
import org.papervision3d.events.FileLoadEvent;
public class MetasequoiaMaterial extends BitmapMaterial{
////////////////////////////////////////////////////////////////////////////////////////
//定数定義
////////////////////////////////////////////////////////////////////////////////////////
private static const TEX:String = "tex";
private static const APLANE:String = "aplane";
private static const BUMP:String = "bump";
////////////////////////////////////////////////////////////////////////////////////////
//メンバ変数定義
////////////////////////////////////////////////////////////////////////////////////////
/**
* ロードしたビットマップのキャッシュ
* ロード待ち中:null
* ロード済み:BitmapData
* ロード中:この画像をロードしているローダ
*/
private static var _loadedBitmaps:Dictionary = new Dictionary();
/**
* マップのURLを保持する
*/
private var _maps:Dictionary;
private var _materialsToLoad:Number;
////////////////////////////////////////////////////////////////////////////////////////
//プロパティ定義
////////////////////////////////////////////////////////////////////////////////////////
/**
* 模様マップを変更する
* @param texBmp
*/
public function set tex(map:BitmapData):void {
this.setMaterialMapData(MetasequoiaMaterial.TEX , map)
}
/**
* 模様マップを取得する。
* ここで取得した模様マップを変更しても、マテリアルは変更されない。
* マテリアルを変更する場合、texプロパティにマテリアルを設定する必要がある
* @return
*/
public function get tex():BitmapData {
return this.getMaterialMapData(MetasequoiaMaterial.TEX);
}
/**
* 透明マップを変更する
* @param map
*/
public function set aplane(map:BitmapData):void {
this.setMaterialMapData(MetasequoiaMaterial.APLANE , map)
}
/**
* 透明マップを取得する。
* ここで取得した透明マップを変更しても、マテリアルは変更されない。
* マテリアルを変更する場合、aplaneプロパティにマテリアルを設定する必要がある
* @return
*/
public function get aplane():BitmapData {
return this.getMaterialMapData(MetasequoiaMaterial.APLANE);
}
/**
* バンプマップを変更する
* @param map
*/
public function set bump(map:BitmapData):void {
this.setMaterialMapData(MetasequoiaMaterial.BUMP , map)
}
/**
* バンプマップを取得する。
* ここで取得したバンプマップを変更しても、マテリアルは変更されない。
* マテリアルを変更する場合、bumpプロパティにマテリアルを設定する必要がある
* @return
*/
public function get bump():BitmapData {
return this.getMaterialMapData(MetasequoiaMaterial.BUMP);
}
/**
* マップデータを設定する
* @param type
* @return
*/
private function setMaterialMapData(type:String , map:BitmapData):void {
this._maps[type] = map;
if (this._materialsToLoad == 0) {
this.materialLoadComplete();
}
}
/**
* マップデータを取得する
* @param type
* @return
*/
private function getMaterialMapData(type:String):BitmapData {
var map:BitmapData = this._maps[type] as BitmapData;
if (map != null) {
return map.clone();
} else {
return null;
}
}
////////////////////////////////////////////////////////////////////////////////////////
//コンストラクタ
////////////////////////////////////////////////////////////////////////////////////////
/**
* 新規Metasequoia用マテリアルを作成
*/
public function MetasequoiaMaterial() {
this.init();
}
////////////////////////////////////////////////////////////////////////////////////////
//publicな関数
////////////////////////////////////////////////////////////////////////////////////////
/**
* メタセコイア用のマテリアルをロード
* @param path メタセコイアのモデルデータのパス
* @param line メタセコイアのMaterial チャンク1行分
*/
public function load(path:String , line:String) :void {
this.init();
//色設定
//形式 - col(1.000 1.000 0.000 1.000)
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]);
this.fillColor = ((r <<16) | (g <<8) | b);
this.fillAlpha = a;
}
// マテリアルの名前を取得
var nameBeginIndex:int = line.indexOf("\"");
var nameEndIndex:int = line.indexOf("\"", nameBeginIndex + 1);
this.name = line.substring(nameBeginIndex + 1, nameEndIndex);
//テクスチャ画像のパスを取得
var tex:String = MetasequoiaMaterial.getParam(line , "tex");
var aplane:String = MetasequoiaMaterial.getParam(line , "aplane");
var bump:String = MetasequoiaMaterial.getParam(line , "bump");
//各マップのパスがnullでない場合、キャッシュ探索を行う
if (tex != null) {
tex = path + tex;
this._materialsToLoad++;
this.getImage(tex , MetasequoiaMaterial.TEX);
}
if (aplane != null) {
aplane = path + aplane;
this._materialsToLoad++;
this.getImage(aplane , MetasequoiaMaterial.APLANE);
}
if (bump != null) {
bump = path + bump;
this._materialsToLoad++;
this.getImage(bump , MetasequoiaMaterial.BUMP);
}
if (_materialsToLoad == 0) {
this.materialLoadComplete();
}
}
////////////////////////////////////////////////////////////////////////////////////////
//privateな関数
////////////////////////////////////////////////////////////////////////////////////////
/**
* 初期化関数
* @return
*/
private function init() :void {
//テクスチャの初期化
this.bitmap = null;
this._materialsToLoad = 0;
this._maps = new Dictionary();
this._maps[MetasequoiaMaterial.TEX] = null;
this._maps[MetasequoiaMaterial.APLANE] = null;
this._maps[MetasequoiaMaterial.BUMP] = null;
}
/**
* キャッシュから画像を検索する。<br />
* @param path 画像パス
* @param type 画像をどのマップに使用するか?(TEX , APLANE , BUMP)
* @return キャッシュに画像がある場合はTrue、無い場合はFalse
*/
private function getImage(path:String , type:String):Boolean {
//pathもしくはtypeが設定されてなかったら終了
if (path == null || type == null) {
return false;
}
//キャッシュ探索
var cache:BitmapData = MetasequoiaMaterial._loadedBitmaps[path] as BitmapData;
if (cache == null) {
//キャッシュが無い
this._maps[type] = path;
//ロード中?
var loader:Loader = MetasequoiaMaterial._loadedBitmaps[path] as Loader;
if (loader == null) {
//新しく画像をロードする
loader = new Loader();
loader.name = path;
loader.contentLoaderInfo.addEventListener(Event.COMPLETE , loadImageComplete);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR , loadError);
MetasequoiaMaterial._loadedBitmaps[path] = loader;
loader.load(new URLRequest(path));
} else {
//画像をロード中だったので、イベントリスナーに自分を追加
loader.contentLoaderInfo.addEventListener(Event.COMPLETE , loadImageComplete);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR , loadError);
}
} else {
this._materialsToLoad--;
this[type] = cache;
}
return cache != null;
}
/**
* 画像読み込み完了時
* @param evt
* @return
*/
private function loadImageComplete(evt:Event) : void {
var loader:Loader = evt.target.loader as Loader;
var bmp:Bitmap = loader.content as Bitmap;
if (bmp != null ){
//画像をキャッシュに入れる
MetasequoiaMaterial._loadedBitmaps[loader.name] = bmp.bitmapData;
//画像を設定する
if (this._maps[MetasequoiaMaterial.TEX] == loader.name) {
//模様マップ
this.tex = bmp.bitmapData;
} else if (this._maps[MetasequoiaMaterial.APLANE] == loader.name) {
//透明マップ
this.aplane = bmp.bitmapData;
} else if (this._maps[MetasequoiaMaterial.BUMP] == loader.name) {
//バンプマップ
this.bump = bmp.bitmapData;
}
//画像ロード完了?
if(--this._materialsToLoad == 0) {
this.materialLoadComplete();
}
}
}
/**
* ロード失敗時の処理
* @param evt
*/
private function loadError(evt:Event):void {
var loader:Loader = evt.target.loader as Loader;
this._materialsToLoad--;
if (loader != null) {
trace(" failed to load image[" + loader.name + "]");
} else {
trace(" failed to load image[unknown]");
}
}
/**
* 全マップの読み込み完了時
* @return
*/
private function materialLoadComplete():void {
//透明マップと模様マップから透明部分アリの模様マップを作成
var bmpData:BitmapData;
var t:BitmapData = this.tex;
var a:BitmapData = this.aplane;
var b:BitmapData = this.bump;
if (t != null) {
bmpData = new BitmapData(t.width , t.height , true, this.fillColor);
bmpData.draw(t);
} else {
if (a != null) {
bmpData = new BitmapData(a.width , a.height , true , this.fillColor);
} else {
return;
}
}
if (a != null) {
bmpData.copyChannel(a , a.rect , new Point(0,0) , BitmapDataChannel.RED , BitmapDataChannel.ALPHA);
}
this.texture = bmpData;
var fileEvent:FileLoadEvent = new FileLoadEvent( FileLoadEvent.LOAD_COMPLETE);
this.dispatchEvent( fileEvent );
}
/**
* line内からパラメータを抜き出し、返す。
* 二重引用符(")で囲まれている場合、二重引用符は削除する
* @param line パラメータを探し出す文字列
* @param paramName 対象パラメータ名
* @return 対象パラメータ値
*/
private static function getParam(line:String, paramName:String):String {
var pattern:RegExp = new RegExp(".*" + paramName + "\\(\"?(.*?)\"?\\).*" , "g");
if (pattern.test(line)) {
var result:String = line.replace(pattern , "$1");
return result;
} else {
return null;
}
}
}
}
このクラスは、模様マップと透明マップのロードを行い、完了後に合成して透明部分を持ったBitmapDataを作成する。また、ロードした画像をキャッシュしたりする機能を持っている。




