// forked from romatica's FLARToolkitマーカー位置にイージング移動 (easing motion) // forked from rokubou's FLARToolKit_Sample_Simple_PV3D /** *------------------------------------------------- * * FLARToolkit Butterfly Animation * @auther itoz ( http://www.romatica.com/ ) * * マーカーを認識すると、「蝶」がマーカーに寄って来て、留まります。 * 非認識だと、飛び立ちます。 * 認識中マーカーを極端に動かすと、飛び立ち、また、マーカー位置に留まります。 * * [marker pdf] マーカーをプリントして下さい。 * http://www.romatica.com/dev/resource/flarlogo-marker.pdf * *------------------------------------------------- */ package { import org.libspark.flartoolkit.core.FLARCode; import org.libspark.flartoolkit.core.param.FLARParam; import org.libspark.flartoolkit.core.raster.rgb.FLARRgbRaster_BitmapData; import org.libspark.flartoolkit.core.transmat.FLARTransMatResult; import org.libspark.flartoolkit.detector.FLARSingleMarkerDetector; import org.libspark.flartoolkit.support.pv3d.FLARBaseNode; import org.libspark.flartoolkit.support.pv3d.FLARCamera3D; import org.papervision3d.core.math.Matrix3D; import org.papervision3d.core.math.Number3D; import org.papervision3d.materials.BitmapMaterial; import org.papervision3d.objects.DisplayObject3D; import org.papervision3d.objects.primitives.Plane; import org.papervision3d.render.LazyRenderEngine; import org.papervision3d.scenes.Scene3D; import org.papervision3d.view.Viewport3D; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Loader; import flash.display.PixelSnapping; import flash.display.Sprite; import flash.events.Event; import flash.events.IOErrorEvent; import flash.events.SecurityErrorEvent; import flash.media.Camera; import flash.media.Video; import flash.net.URLLoader; import flash.net.URLLoaderDataFormat; import flash.net.URLRequest; import flash.system.LoaderContext; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.utils.Dictionary; [SWF(width=465, height=465, backgroundColor=0x808080, frameRate=30)] public class FLARToolkit_Butterfly extends Sprite { /**FLAR*/ protected var cameraParamFile : String; private var _canvasWidth : int; private var _canvasHeight : int; private var _captureWidth : int; private var _captureHeight : int; private var _codeWidth : int; private var _markerPatternFile : String; private var _cameraParam : FLARParam; private var _markerPatternCode : FLARCode; private var _webCamera : Camera; private var _video : Video; private var _capture : Bitmap; private var _raster : FLARRgbRaster_BitmapData; private var _detector : FLARSingleMarkerDetector; private var _urlLoader : URLLoader; private var _scene : Scene3D; private var _viewport : Viewport3D; private var _camera3D : FLARCamera3D; private var _renderer : LazyRenderEngine; private var _markerNode : FLARBaseNode; private var _container : DisplayObject3D; private var _modelWRAP : DisplayObject3D; private var _hane1BM : Bitmap; private var _hane2BM : Bitmap; private var _lastRot : Number3D = new Number3D(); private var _loaderList : Dictionary; private var _loadCount : int = 0; private const LOAD_URL_ARRAY : Array = ["http://www.romatica.com/dev/wonderfl/butterfly1.png", "http://www.romatica.com/dev/wonderfl/butterfly2.png"]; private var _butt : DisplayObject3D; private var _hane1 : Plane ; private var _hane2 : Plane; private var _recognizer : TextField ; //認識/非認識 表示 private const STAY_LIMIT : int = 20; //蝶が飛び立つきっかけとなる目標点までの距離 /** * Constructor */ public function FLARToolkit_Butterfly() { Wonderfl.capture_delay(16); // 各種サイズの初期化 _captureWidth = 320; _captureHeight = 240; _canvasWidth = 640; _canvasHeight = 480; _codeWidth = 80; // パラメータファイルの読込み 今回は省略して初期値を用いる _cameraParam = new FLARParam(); _cameraParam.changeScreenSize(_captureWidth, _captureHeight); // マーカーパターンファイルの読込み _markerPatternFile = 'http://assets.wonderfl.net/static/flar/flarlogo.pat'; _urlLoader = new URLLoader(); _urlLoader.dataFormat = URLLoaderDataFormat.TEXT; _urlLoader.addEventListener(Event.COMPLETE, this.onLoadCode); _urlLoader.addEventListener(IOErrorEvent.IO_ERROR, dispatchEvent); _urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, dispatchEvent); _urlLoader.load(new URLRequest(_markerPatternFile)); } // ====================================================================== /** * マーカーパターンを読み込む */ protected function onLoadCode(e : Event) : void { _urlLoader.removeEventListener(Event.COMPLETE, this.onLoadCode); // 分割数(縦・横)、黒枠の幅(縦・横) _markerPatternCode = new FLARCode(16, 16, 50, 50); _markerPatternCode.loadARPatt(this._urlLoader.data); _urlLoader = null; laodImages(); //画像読み込み } // ====================================================================== /** * imege load start */ private function laodImages() : void { _loaderList = new Dictionary(); for (var i : int = 0;i < LOAD_URL_ARRAY.length ;i++) { var loader : Loader = new Loader(); _loaderList[loader] = i; loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler); loader.load(new URLRequest(LOAD_URL_ARRAY[i]), new LoaderContext(true)); } } // ====================================================================== /** * 画像ロードカウント */ private function completeHandler(event : Event) : void { var loader : Loader = event.target.content.parent as Loader; switch(_loaderList[loader]) { case 0: _hane1BM = event.target.content as Bitmap; break; case 1: _hane2BM = event.target.content as Bitmap; break; default: } _loadCount++; if(_loadCount >= LOAD_URL_ARRAY.length) { _allLoadComplete(); } } // ====================================================================== /** * 画像ロード完了 */ private function _allLoadComplete() : void { onInit();// 初期化 } // ====================================================================== /** * Webカメラの設定と、ARToolKitの準備 */ protected function onInit() : void { // setup webcam this._webCamera = Camera.getCamera(); if (!this._webCamera) { throw new Error('No webcamera!'); } this._webCamera.setMode(this._captureWidth, this._captureHeight, 30); this._video = new Video(this._captureWidth, this._captureHeight); this._video.attachCamera(this._webCamera); // setup ARToolKit this._capture = new Bitmap(new BitmapData(this._captureWidth, this._captureHeight, false, 0), PixelSnapping.AUTO, true); // ウェブカメラの解像度と表示サイズが異なる場合は拡大する this._capture.width = this._canvasWidth; this._capture.height = this._canvasHeight; this.addChild(this._capture); this._raster = new FLARRgbRaster_BitmapData(this._capture.bitmapData); // setup Single marker detector this._detector = new FLARSingleMarkerDetector(this._cameraParam, this._markerPatternCode, this._codeWidth); this._detector.setContinueMode(true); // PV3D _viewport = this.addChild(new Viewport3D(this._captureWidth, this._captureHeight)) as Viewport3D; _viewport.scaleX = this._canvasWidth / this._captureWidth; _viewport.scaleY = this._canvasHeight / this._captureHeight; _viewport.x = -4; // なぜかずれるので補正 _scene = new Scene3D(); _markerNode = this._scene.addChild(new FLARBaseNode(FLARBaseNode.AXIS_MODE_PV3D)) as FLARBaseNode; _camera3D = new FLARCamera3D(this._cameraParam); _renderer = new LazyRenderEngine(this._scene, this._camera3D, this._viewport); _container = new DisplayObject3D(); // モデルデータ setModelData(); // モデルデータを登録 _markerNode.addChild(this._container); //認識非認識チェッカー createRecognizer(); // start start(); } // ====================================================================== /** * 認識チェッカー作成 */ private function createRecognizer():void { _recognizer =addChild( new TextField()) as TextField; _recognizer.autoSize = TextFieldAutoSize.LEFT; _recognizer.background = true; _recognizer.backgroundColor = 0xcc0000; _recognizer.x = _recognizer.y = 5; } // ====================================================================== /** * 蝶作成 */ protected function setModelData() : void { _modelWRAP = new DisplayObject3D(); _butt = new DisplayObject3D(); var h1BMM : BitmapMaterial = new BitmapMaterial(_hane1BM.bitmapData); var h2BMM : BitmapMaterial = new BitmapMaterial(_hane2BM.bitmapData); h1BMM.oneSide = false; h2BMM.oneSide = false; _hane1 = new Plane(h1BMM, 43, 61, 4, 6); _hane2 = new Plane(h2BMM, 43, 61, 4, 6); _butt.addChild(_hane1); _butt.addChild(_hane2); _hane1.transformVertices(Matrix3D.translationMatrix(21.5, 0, 0)); _hane2.transformVertices(Matrix3D.translationMatrix(-21.5, 0, 0)); _modelWRAP.addChild(_butt); _scene.addChild(_modelWRAP); } // ====================================================================== /** * マーカーの認識と3次元モデルの描写を開始する */ public function start() : void { // マーカー認識・非認識時用のイベントを登録 this.addEventListener(MarkerEvent.MARKER_ADDED, this.onMarkerAdded); this.addEventListener(MarkerEvent.MARKER_UPDATED, this.onMarkerUpdated); this.addEventListener(MarkerEvent.MARKER_REMOVED, this.onMarkerRemoved); // 処理開始 this.addEventListener(Event.ENTER_FRAME, this.run); } // ====================================================================== /** * 認識したマーカーの情報を格納 */ protected var resultMat : FLARTransMatResult = new FLARTransMatResult(); public function onMarkerAdded(e : Event = null) : void { _recognizer.text = "マーカー認識しています"; _recognizer.backgroundColor = 0xffffff; } public function onMarkerUpdated(e : Event = null) : void { } public function onMarkerRemoved(e : Event = null) : void { _recognizer.text = "マーカー認識していません"; _recognizer.backgroundColor = 0xcc0000; } // ====================================================================== /** * 蝶の動き。ここで処理振り分けを行っている */ private var _ang : Number = 0; private var _r : Number = 60; private var _spd : Number = 60; public function run(e : Event) : void { this._capture.bitmapData.draw(this._video); var rad : Number = _ang / 180 * Math.PI; var rot : Number = Math.sin(rad); // Marker detect var detected : Boolean = false; try { detected = this._detector.detectMarkerLite(this._raster, 80) && this._detector.getConfidence() > 0.5; } catch (e : Error) { } if (detected) { // 認識時 _detector.getTransformMatrix(this.resultMat); _markerNode.setTransformMatrix(this.resultMat); var transform : Matrix3D = _markerNode.transform; _lastRot = Matrix3D.matrix2euler(transform); this.dispatchEvent(new MarkerEvent(MarkerEvent.MARKER_ADDED)); } else { // 非認識時 this.dispatchEvent(new MarkerEvent(MarkerEvent.MARKER_REMOVED)); _markerNode.x += (rot * (Math.random() * 30 + 25)); _markerNode.y += (rot * Math.random() * 10 + 3); _markerNode.z += (rot * (Math.random() * 10 + 10)); if(_markerNode.y>100) _markerNode.y-(Math.random()*10); } //距離の絶対値 var _xAbs : Number = (_markerNode.x - _modelWRAP.x); _xAbs = (_xAbs > 0) ? _xAbs : -_xAbs; var _yAbs : Number = (_markerNode.y - _modelWRAP.y); _yAbs = (_yAbs > 0) ? _yAbs : -_yAbs; var _zAbs : Number = (_markerNode.z - _modelWRAP.z); _zAbs = (_zAbs > 0) ? _zAbs : -_zAbs; if(_xAbs > STAY_LIMIT || _yAbs > STAY_LIMIT || _zAbs > STAY_LIMIT) { _r = 60; _ang += _spd; if(_butt.rotationX < 90)_butt.rotationX += 20; _butt.x += (rot * (Math.random() * 20)); if(_butt.x > 10 )_butt.x = 10; if(_butt.x < -10 )_butt.x = -10; _butt.y += (rot * Math.random() * 25 + 20); if(_butt.y > 90 )_butt.y = 90; if(_butt.y < 0 )_butt.y = 0; _butt.z += (rot * ( Math.random() * 5 + 5)); if(_butt.z > 20 )_butt.z = 20; if(_butt.z < -20 )_butt.z = -20; } else { _r = 30; _ang += _spd / 11; } //羽の動き _hane1.rotationY = rot * _r; _hane2.rotationY = -(rot * _r); //イージング移動 _modelWRAP.x += (_markerNode.x - _modelWRAP.x) * 0.15; _modelWRAP.y += (_markerNode.y - _modelWRAP.y) * 0.15; _modelWRAP.z += (_markerNode.z - _modelWRAP.z) * 0.15; _modelWRAP.rotationX += (_lastRot.x - _modelWRAP.rotationX) * 0.1; _modelWRAP.rotationY += (_lastRot.y - _modelWRAP.rotationY) * 0.1; _modelWRAP.rotationZ += (_lastRot.z - _modelWRAP.rotationZ) * 0.1; // _butt.rotationX += (0 - _butt.rotationX) * 0.3; _butt.x += (0 - _butt.x) * 0.2; _butt.y += (0 - _butt.y) * 0.2; _butt.z += (0 - _butt.z) * 0.2; this._renderer.render(); } } } import flash.events.Event; /** * イベント制御用の簡易クラス */ class MarkerEvent extends Event { /** Markerを認識した時*/ public static const MARKER_ADDED : String = "markerAdded"; /**Marker更新時*/ public static const MARKER_UPDATED : String = "markerUpdated"; /**Markerが認識しなくなった時 */ public static const MARKER_REMOVED : String = "markerRemoved"; public function MarkerEvent(type : String, bubbles : Boolean = false, cancelable : Boolean = false) { super(type, bubbles, cancelable); } } [FLARToolkit] マーカーに「蝶」がとまる (Butterfly Animation)