2009年11月29日 星期日

PV3D 練習 - Facebook 好友大頭照

DEMO:(需登入fb)
http://www.asn.com.tw/flex/20091129.htm
20110827更新連結…
http://www.vercenter.nknu.edu.tw/flex/FacebookFriends/LongfellowHelloFlex.html

在 facebook 上登入,讀取好友名單及大頭照,再將大頭照轉成 BitmapMaterial,用這個材質生成 PLANE,再置入 PV3D 場景中。

效果跟效能,並不是絕對的反比關係。




SOURCE:
package study
{
    import game.FacebookAP;

    import com.facebook.data.users.FacebookUser;
   
    import flash.display.Bitmap;
    import flash.display.Loader;
    import flash.display.LoaderInfo;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.filters.BlurFilter;
    import flash.net.URLRequest;
    import flash.system.LoaderContext;
       
    import org.papervision3d.core.effects.view.ReflectionView;
    import org.papervision3d.core.math.Quaternion;
    import org.papervision3d.cameras.CameraType;
    import org.papervision3d.materials.BitmapMaterial;
    import org.papervision3d.objects.DisplayObject3D;
    import org.papervision3d.objects.primitives.Plane;

    public class study21 extends ReflectionView
    {
        private var currentQuat:Quaternion = new Quaternion();
        private var targetQuat:Quaternion = new Quaternion();
        private var slerp:Number = 0;
        private var fbRadius:Number;
        private var carousel:DisplayObject3D = new DisplayObject3D();
        private var isMouseDown:Boolean;
        private var fb:FacebookAP;
        private var save_mouseX:Number;
        private var save_rotationY:Number;
        private static var planeWidth:Number = 50;
        private static var planeHeight:Number = 50;
       
        public function study21()
        {
            super(0,0,true,false,CameraType.FREE);
            this.addEventListener(Event.ADDED_TO_STAGE, init0);
        }
       
        private function init0( event:Event ):void
        {
            this.removeEventListener(Event.ADDED_TO_STAGE, init0);
            fb = new FacebookAP();
            fb.addEventListener( Event.COMPLETE, init1 );
            fb.init();
        }

        private function init1( event:Event ):void
        {
            fb.removeEventListener( Event.COMPLETE, init1 );
           
             opaqueBackground=0;
            surfaceHeight = -5;
            viewport.interactive=true;
            viewportReflection.filters = [new BlurFilter(3,3,3)];
            setReflectionColor(.7, .7, .7);

            fbRadius = ( fb.friends.length * (planeWidth+planeWidth/2) )/(2*Math.PI);

             camera.target = carousel;
            camera.position = carousel.position;
            camera.y = planeHeight/2;
             camera.moveBackward( fbRadius+150 );
           
             for( var i:int=0; i
             {
                  var user:FacebookUser = fb.friends.getItemAt(i) as FacebookUser;
                var lc:LoaderContext = new LoaderContext(true);       
                var loader:Loader = new Loader();
                loader.contentLoaderInfo.addEventListener( Event.COMPLETE, onLoadBitmap );
                if( user.pic_square == "" )
                    loader.load(new URLRequest( "images/q_silhouette.gif" ), lc );
                else
                    loader.load(new URLRequest( user.pic_square ), lc);
            }

            scene.addChild(carousel);
            this.addEventListener(Event.ENTER_FRAME, onENTER_FRAME );
            this.stage.addEventListener( MouseEvent.MOUSE_DOWN, onMOUSE_DOWN );
            this.stage.addEventListener( MouseEvent.MOUSE_UP, onMOUSE_UP );
        }

        private function onLoadBitmap( event:Event ):void
        {
            var bodyMaterial:BitmapMaterial = new BitmapMaterial( Bitmap(LoaderInfo(event.target).content).bitmapData );
            bodyMaterial.interactive=true;
            bodyMaterial.doubleSided=true
           
            var plane:DisplayObject3D = new Plane( bodyMaterial,planeWidth,planeHeight,1,1 );
           
            plane.rotationY = ( 360 / fb.friends.length ) * carousel.numChildren;
            plane.moveForward( fbRadius );
            plane.y = planeHeight/2;
           
            carousel.addChild(plane);
        }      

        private function onMOUSE_DOWN(e:Event):void
        {
            isMouseDown = true;
            save_mouseX = this.stage.mouseX;
            save_rotationY = carousel.rotationY;
        }
       
        private function onMOUSE_UP(e:Event):void
        {
            isMouseDown = false;
        }
       
        private function onENTER_FRAME(e:Event):void
        {
            if( isMouseDown )
            {
                carousel.rotationY = save_rotationY + (180/Math.PI)*Math.atan((save_mouseX-this.stage.mouseX)/(fbRadius+150));
            } else {
                carousel.rotationY -= ( this.stage.mouseX - this.stage.stageWidth/2 )/200;

                slerp += (1 - slerp) * .05;
                var quat:Quaternion = Quaternion.slerp( currentQuat, targetQuat, slerp );
                carousel.transform = quat.matrix;
             }
        
            singleRender();
        }
    }
}

2009年11月27日 星期五

PV3D 練習 - 加入影片

現在可以邊開車邊看電視了 ^ ^"

http://www.asn.com.tw/flex/cardrive/CarDriver4.html
20110827連結更新…
http://www.vercenter.nknu.edu.tw/flex/CarDriver/CarDriver.html

不過所使用的影片檔是伺服端的 flv 檔,若要使用外部的連結(如 youtube),則會有 security domain 的問題,還要再研究。

幾個問題要注意…

一、DAE 物件有可能是很多個小物件組成的,在 3DS 裡會有不同的名稱及材質名稱,若需要讓 DAE 物件能產生事件 ( 譬如這個例子中液晶電視的畫面會接收滑鼠按下的事件來停止、撥放影片 ),必須從讀入的 DAE 物件中抽取出子物件 ( getChildByName ),再把這個子物件加上 InteractiveScene3DEvent (材質亦需事先設定好 interactive = true,才能正確接收到事件。

二、同樣是 DAE 物件,由於物件的建立順序是先建立 DisplayObject3D 再 LOAD 進模型及上材質,因此在 Render 的時候要特別注意,別在還沒 FileLoadEvent.LOAD_COMPLETE 之前就先調用子物件 ( 例如本例中的車承軸及車輪 ),否則會誤用 NULL 物件。 解決的辦法是確認全部 LOAD 完再 startRendering 或者在每次使用前作 NULL check。

三、材質使用貼圖 bitmap 時要特別小心原 bitmap 的檔案大小也會嚴重影響到 render 的效能 ( 例如被我消去的草坪 = =",跟原本很大張的木箱圖 )。


 PV3D 的效能確實不是很理想,但想必隨著 ADOBE 日後支援 3D 硬體 GPU 加速 (希望早點實現 ),PV3D (或其它 WEB 3D 引擎 ) 應該也會更讚才是,在此之前,先把效率最優化的技巧學好吧~ ^ ^


另外順便介紹一個超驚人的 Web 3D 引擎 UNITY,底下是官網 DEMO…
http://unity3d.com/gallery/live-demos/tropical-paradise
需要安裝個小小元件才能看到,不過效果驚人。

2009年11月26日 星期四

PV3D 練習-幾種加入光影材質的方式

開車加入光影材質…
http://www.asn.com.tw/flex/cardrive/CarDriver3.html



幾種加入光影材質的方式…

在PV3D 裡面有所謂的 shadematerial,用來產生會按照光源反應出明暗變化的效果的材質,在使用上很方便,只需產生一個點光源,再利用它來 new 出一個 ShadeMaterial,再賦予物件使用。

最簡單的像是 org.papervision3d.materials.shadematerials 底下的 FlatShadeMaterial …

         private function foo():void
         {
            var light:PointLight3D = new PointLight3D();
            var shaderMaterial:FlatShadeMaterial =  new FlatShadeMaterial( light, 0x123456, 0x000000 );
            var plane:Plane = new Plane(shaderMaterial,500,500);
         }

但有時會需要用貼圖的方式來生成物件的表面紋理,比較像真,這時要透過 shader 來重新製作俱備貼圖的 ShadedMaterial…

[Embed(source="assets/photo.jpg")]
private var bmpAsset:Class;
         private function foo():void
         {
            var light:PointLight3D = new PointLight3D();
             var yourBitmap:Bitmap = new bmpAsset() as Bitmap;
            var bitmapMaterial:BitmapMaterial = new BitmapMaterial(yourBitmap.bitmapData);
            var shader:FlatShader= new FlatShader( light, 0xffffff, 0x333333);
            var shaderMaterial:ShadedMaterial = new ShadedMaterial(bitmapMaterial, shader);
            plane = new Plane(shaderMaterial,500,500);
         }

上面是靜態讀圖的作法,但是若是透過 BitmapFileMaterial 動態即時讀圖檔,卻會失敗…

         private function foo():void
         {
            var fileMaterial:BitmapFileMaterial = new BitmapFileMaterial("assets/FocusBody.jpg");
            /// after FileLoadEvent.LOAD_COMPLETE ....
            var light:PointLight3D = new PointLight3D();
            var shader:FlatShader = new FlatShader(light, 0xFFFFFF, 0x333333);
            var shaderMaterial:ShadedMaterial = new ShadedMaterial(fileMaterial, shader);
            var plane:Plane = new Plane(shaderMaterial,500,500);
       }

因為 BitmapFileMaterial 並不是 BitmapMaterial 的子類別  ~"~,所以要稍微轉一下…


         private function foo():void
         {
            var fileMaterial:BitmapFileMaterial = new BitmapFileMaterial("assets/FocusBody.jpg");
            /// after FileLoadEvent.LOAD_COMPLETE ....
            var light:PointLight3D = new PointLight3D();
            var shader:FlatShader = new FlatShader(light, 0xFFFFFF, 0x333333);
            var bitmapMaterial:BitmapMaterial = new BitmapMaterial( fileMaterial.bitmap );
            var shaderMaterial:ShadedMaterial = new ShadedMaterial(bitmapMaterial, shader);
            var plane:Plane = new Plane(shaderMaterial,500,500);
        }

另外要注意,Cube 六面體若是要使用 ShadedMaterial 的話,六面都要 new 個新 shader 給它,否則會有三角形黑色塊產生 = =",很麻煩,而且跑起來非常秏效能。

org.papervision3d.materials.shader 底下還有很多種不同的 shader 可以使用,效果皆不同…
FlatShader
PhongShader
CellShader
EnvMapShader
GouraudShader

2009年11月24日 星期二

多圖 BitmapFileMaterial 讀取方式

上次提到了 BitmapFileMaterial load 的時候使用一個 array,利用事件傳遞的方式來檢測什麼時候所有的圖片讀取完畢,好進行下一個有前後相依的步驟。

結果,今天 trace 了一下 BitmapFileMaterial 這個類別,發現原來它早就有把類似的機制作在裡面了,在BitmapFileMaterial.as 的這行…

        static public var callback :Function;

於是,要讀取一連串的圖檔就變的更容易了…

        public function init():void
        {
            BitmapFileMaterial.callback = loadComplete;
            floorMaterial = new BitmapFileMaterial("assets/grassTexture.jpg");
            bodyMaterial = new BitmapFileMaterial("assets/FocusBody.jpg");
            wheelMaterial = new BitmapFileMaterial("assets/FocusWheel.jpg");
            boxMaterial = new BitmapFileMaterial("assets/box.jpg");
        }

        private function loadComplete():void
        {
            BitmapFileMaterial.callback = null;
            ///......
        }


雖然如此,但使用上也要特別小心,因為 BitmapFileMaterial.callback 是 public static 的,意即所有其它程序也是有可能在你讀取一堆圖檔的時候,使用同樣的方式設定了 callback 的函式,那就會出錯嘍(潛藏的BUG)。

2009年11月23日 星期一

PV3D 防止破圖的幾種方法

一、使用 QuadrantRenderEngine…

新版的 PV3D 可以使用 QuadrantRenderEngine 代替原來的 BasicRenderEngine

renderer = new QuadrantRenderEngine(QuadrantRenderEngine.ALL_FILTERS);

但是效能很差,可能還需要細部的參數設定。

二、自己手動作 ViewportLayer…

            viewport.containerSprite.sortMode = ViewportLayerSortMode.INDEX_SORT;
            objViewportLayer = new ViewportLayer(viewport, null);
            objViewportLayer.layerIndex = 1;
            viewport.containerSprite.addLayer(objViewportLayer);
            objViewportLayer.addDisplayObject3D(YOUR_OBJECT, true);

同一層的物件依然存在破圖的問題,並且,上層物件絕對會遮蓋住下層物件,所以在分配上要自己作好控制,效能很好。

三、在近距離的情況下,多邊形會被省略不畫而導致的缺面…

            renderer.clipping = new FrustumClipping(FrustumClipping.BOTTOM);

2009年11月21日 星期六

PV3D 練習 - 開車

開車兜兜風…
http://www.asn.com.tw/flex/cardrive/cardrive.html

http://www.asn.com.tw/flex/cardrive/cardrive2.html with BOX2D

程式參考…
http://pv3d.org/2009/01/23/springcamera3d-and-driving-a-car/
重點在 driveCar() 及 updateCar() 兩個函式。

汽車控制幾乎都使用原作的,看別人的程式可以學到很多,沒想到原來不難,缺的大概就是經驗跟創意。

另一個重點是汽車的模型,在建模的時候就要先規畫好,要能配合程式運作,包括物件軸心、面向、四個輪子的軸心跟方向、子物件的名稱…。

圖檔及資源的讀取部份改寫如下…

使用一個陣列 push 所有需要讀取資源的 loader,並加入事件監聽,在收到完成的事件後再移出陣例,最後判斷是否己全數讀取完成,再進行其它的初始化工作。

        private var loadAssets:Array = new Array();
        private function initAssets():void
        {
            removeEventListener(Event.ADDED_TO_STAGE, initAssets);

            floorMaterial = new BitmapFileMaterial("assets/grassTexture.jpg");
            floorMaterial.addEventListener(FileLoadEvent.LOAD_COMPLETE,onLoadAssets);
            loadAssets.push(floorMaterial);

            bodyMaterial = new BitmapFileMaterial("assets/FocusBody.jpg");
            bodyMaterial.addEventListener(FileLoadEvent.LOAD_COMPLETE,onLoadAssets);
            loadAssets.push(bodyMaterial);

            wheelMaterial = new BitmapFileMaterial("assets/FocusWheel.jpg");
            wheelMaterial.addEventListener(FileLoadEvent.LOAD_COMPLETE,onLoadAssets);
            loadAssets.push(wheelMaterial);
        }
       
        private function onLoadAssets(e:FileLoadEvent):void
        {
            var idx:Number = loadAssets.indexOf(e.target);
            if( idx != -1 )
            {
                if( e.target is BitmapFileMaterial )
                    (e.target as BitmapFileMaterial).removeEventListener(FileLoadEvent.LOAD_COMPLETE,onLoadAssets);
                loadAssets.splice( idx, 1 );
                if( loadAssets.length == 0 )
                    initOther();
            }
        }

2009年11月20日 星期五

PV3D DAEMC2 練習

DAEMC2 是一個讓 PV3D 可以讀取 dae 動作模型的組件,藉由它,可以讓你在 PV3D 中控制撥放預先制作好的動作。

http://www.asn.com.tw/flex/BoxMan/BoxMan.html

程式參考 這裡,但改成了 DAEMC2 版 frame 的新的操作方式。

DAEMC2 教學可以參考 影片

VirtualBox 空間減肥

sdelete64 -z c: VBoxManage  modifymedium  disk  "/Users/fellow/VirtualBox VMs/Win10/Win10.vdi"  --compact *.vdi 路徑可以在 VirtualBox 儲...