[PV3D]CircleFlow(はじめてのPapervision3D)

[PV3D]CircleFlow(はじめてのPapervision3D)Flashの3Dにおいて、Papervision3Dって共通語になっているような気がしたので勉強開始。
BasicViewを使っているせいもあるかもしれないけど「めんどくさそうなこと」をずいぶんうまくつつみこんでいるなーというのが第一印象。でもこれ、この簡単そうな印象のまま、実案件で泣いている人が世界中にいそうな気がすけるけど、まあ余計なお世話か。

*注*
筆者及び水玉製作所はPapervision3Dの経験は薄いものの、弊社オリジナルエンジンによるFlashの3D案件は大手ゲームメーカ、イベントホール、研究所ほか複数受注納品しています。視野を広げ、対応力を高めるためにPapervision3Dの勉強をはじめました。

 
とりあえず、弊社立野のエントリーを読みながら入門。
http://www.mztm.jp/wp/2009/05/22/studypapervision3dを使ってみる1/
http://www.mztm.jp/wp/2009/06/01/studypapervision3d-02-360度のパノラマ画像を回してみる/
とりあえずコピペして動かせて第一歩は踏めた。やった!
ありがとう立野君。次は上のエントリーに無駄なことがだいぶあることに気がついてね!(はーと)
まあ、人のこと言えないけどw

さらにGoogle検索して動かしてみる。第二歩目に進んだ。

■Papervision3Dやってみる
http://rainyday.jp/blog/flash/pv3d/75.html

プリミティブを簡単に動かせるのはいいことだ。良くできている。
特に面白いと思ったのは。テクスチャの管理とファイルローダーを一体化させているところ。

var m:BitmapFileMaterial = new BitmapFileMaterial("hoge.jpg");

あとはEvent.ENTER_FRAMEが最初から走っているところ。

override protected function onRenderTick(event:Event=null):void {
	mySphere.rotationX += 0.5;
	super.onRenderTick(event);
}

なるほど。

さらにGoogle検索して動かしてみる。第三歩目。
■WonderflでPapervision3D
http://clockmaker.jp/blog/2009/02/wonderfl_papervision3d/
プリミティブに対してひと味加えて、やってみたい感を増量させている。さすが。

んで、「被写界深度デモ」をフォークして作ってみたのが以下。

▼Wonderfl

[PV3D]CircleFlow forked from: [PV3D] Field of Blur – wonderfl build flash online

先日公開の物欲フローのPV3D版。
とりあえず、動かすことに専念できたのは良かったんだけど、どうしても、かゆいところに手が届かない感がある。

わからないところ
・マウスクリックでどのアイテムがクリックされたかを知る。
・ビットマップにスムージングをかける。

▼ActionScript AS3(FP9)

/**
* Papervision3Dで被写界深度
* 参照: http://clockmaker.jp/blog/2008/07/pv3d_gw_blur/

動き方自体は前に作ったことがあったので、それのPV3D版。

うーん、とりあえずPV3Dが便利なのはわかった。


次の課題
・マウスクリックでどのアイテムがクリックされたかを知る。
・ビットマップにスムージングをかける。


*/
package
{
	import flash.display.*;
	import flash.events.*;
	import flash.filters.*;
	
	import org.papervision3d.view.*;
	import org.papervision3d.materials.*;
	import org.papervision3d.objects.*;
	import org.papervision3d.objects.primitives.*
	
	[SWF(width = "465", height = "465", frameRate = "30", backgroundColor = "#000000")]
	
	public class Test extends BasicView 
	{
		// const vars
		static private const OBJ_LENGTH   :int = 20;
		static private const CIRCLE_RANGE :int = 570;
		private var stageWidth:int = stage.stageWidth;
		private var stageHeight:int = stage.stageHeight;
		private var loadFiles_array:Array;
		private var MultiLoader:MultiLoaderClass = new MultiLoaderClass("http://mztm.jp/crossdomain.xml");
		private var data_xml:XML;
		//private var shadow
		
		// 3d vars
		private var list :Array = []
		private var wrap :DisplayObject3D;
		/**
		 * Constructor
		 */
		public function Test(){
			loadFiles_array = MultiLoader.setLoad(["http://mztm.jp/wonderfl/3d/arss/data.xml"],onXMLComp);
			
		}
		public function onXMLComp():void
		{
			var i:int;
			var jpg_array:Array = new Array();
			data_xml = XML(loadFiles_array[0].data);
			var _length:int = data_xml.items.item.length();
			for (i = 0; i < _length; i++) {
				if(data_xml.items.item[i].jpg != ""){					  
					var _array:Array = String(data_xml.items.item[i].jpg).split('/');  
					var jpg_url:String = "http://mztm.jp/wonderfl/3d/arss/revolution/"+_array.pop();  
					jpg_url = jpg_url.replace(/%/g,'');
					jpg_array.push(jpg_url);  
				}
			}
			
			camera.x		 = 0;
			camera.y		 = 200;
			camera.z		 = 1000;
			camera.rotationX = 5;
			
			// wrap
			wrap = new DisplayObject3D();
			scene.addChild(wrap);
			
			for (i = 0; i < OBJ_LENGTH; i++)
			{
				var rot:Number = 360 * i / OBJ_LENGTH ;
				
				
				var m : BitmapFileMaterial = new BitmapFileMaterial(jpg_array[i+int(Math.random()*120)]);
				//var m:ColorMaterial = new ColorMaterial(0x0066CC*i);
				m.doubleSided = true;
				
				var o:Plane = new Plane(m, 180, 180,2,2);
				o.x = CIRCLE_RANGE * Math.sin(rot * Math.PI / 180);
				o.z = CIRCLE_RANGE * Math.cos(rot * Math.PI / 180);
				
				if(i > OBJ_LENGTH/2){
					o.rotationY = rot-90;
				}else{
					o.rotationY = rot+90;
				}
				o.useOwnContainer = true; //ココ重要
				
				// 配列に追加 & wrapの子供にする
				list.push(wrap.addChild(o));
			}
			
			addEventListener(Event.ENTER_FRAME, loop);
			startRendering();
			//addEventListener(MouseEvent.MOUSE_DOWN,DOUBLE_CLICK);
			//MouseUnderPoint.isNamed = true;
			//function DOUBLE_CLICK(e:MouseEvent):void{
				//trace(MouseUnderPoint.getObject(stage).name);
			//}
		}
		
				// loop
		private function loop(event:Event):void
		{
			var mouseX:Number = Math.min(Math.max(stage.mouseX,0),stageWidth)-stageWidth/2;
			var mouseY:Number = Math.min(Math.max(stage.mouseY,0),stageHeight)-stageHeight/2;
			camera.y = mouseY;
			wrap.yaw(mouseX/200);
			
			// 被写界深度
			for (var i:int = 0; i < list.length; i++)
			{
				var o:DisplayObject3D = list[i] as DisplayObject3D;
				var CIRCLE_RANGE_oSceneZ:Number = (CIRCLE_RANGE - o.sceneZ);
				
				if(CIRCLE_RANGE_oSceneZ< 30){
					if(o.sceneX < 0){
						o.rotationY =360 * i / OBJ_LENGTH + CIRCLE_RANGE_oSceneZ*3-180;
					}else{
						o.rotationY =360 * i / OBJ_LENGTH - CIRCLE_RANGE_oSceneZ*3-180;
					}
				}else if((CIRCLE_RANGE*2-1) < CIRCLE_RANGE_oSceneZ){
					if(o.sceneX < 0){
						o.rotationY =360 * i / OBJ_LENGTH + 90-180;
					}else{
						o.rotationY =360 * i / OBJ_LENGTH - 90-180;
					}
				}
				var blur:Number = Math.round(CIRCLE_RANGE_oSceneZ / 100); 
				o.filters = [new BlurFilter(blur, blur, 2)];
			}
		}
	}
}

class MultiLoaderClass{
	import flash.system.Security;
	import flash.net.URLRequest;
	import flash.net.URLLoader;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.display.Loader;
	//import flash.display.LoaderInfo;

	private var onComplete:Function = function():void{};
	private var loadNum:int;
	private var loadCompNum:int;

	public function MultiLoaderClass(_str:String = null){
		if(_str != null){
			Security.loadPolicyFile(_str);
		}
	}

	public function setLoad(__item_array:Array,_onComp:Function = null):Array{
		loadCompNum = loadNum = 0;
		onComplete = _onComp;
		var _array:Array = new Array();
		var _length:int = __item_array.length;
		for (var i:int = 0; i < _length; i++) {
			if(__item_array[i] == null){continue};
			var _extension:String = __item_array[i].substr(-4,4).toLowerCase();//拡張子を取り出す。
			if(_extension == ".xml"){
				loadNum ++;
				_array[i] = fnURLLoader(__item_array[i]);
			}else if(_extension == ".jpg" || _extension == ".png" || _extension == ".gif"){
				loadNum ++;
				_array[i] = fnLoader(__item_array[i]);
			}else{
				//_array[i] = null;
			}
		}
		return _array;
	}
	private function fnURLLoader(__url:String):URLLoader{
		var _loader : URLLoader = new URLLoader();
		_loader.load(new URLRequest(__url));
		_loader.addEventListener (Event.COMPLETE,completeHandler);
		_loader.addEventListener (IOErrorEvent.IO_ERROR, ioErrorHandler);
		return _loader;
	}

	private function fnLoader(__url:String):Loader{
		var _loader:Loader = new Loader();
		_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
		_loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
		_loader.load(new URLRequest(__url));
		//_loader.name = __url;
		return _loader;
	}

	private function completeHandler(event:Event = null):void {
		loadCompNum ++;
		if(loadCompNum == loadNum){
			onComplete();
		}
		//var loaderInfo:LoaderInfo=event.currentTarget as LoaderInfo;
		//var loader:Loader=loaderInfo.loader;
		//addChild(loader);
	}

	private function ioErrorHandler(event:IOErrorEvent):void {
		//event.text = "Error #2035: URL が見つかりません。 URL: file:///~~~~~";
		//event.text = "Error #2036: 読み込みが未完了です。 URL: http://~~~~~";
		//から、URLのみを取り出す。
		//trace(String(event.text).substr(String(event.text).indexOf(" URL: ")+6),"*****");
		completeHandler();
	}
}