package { import com.adobe.utils.AGALMiniAssembler; import com.adobe.utils.PerspectiveMatrix3D; import com.bit101.components.CheckBox; import com.bit101.components.ComboBox; import com.bit101.components.HSlider; import com.bit101.components.Window; import flash.display.BitmapData; import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.display3D.Context3D; import flash.display3D.Context3DProgramType; import flash.display3D.Context3DVertexBufferFormat; import flash.display3D.IndexBuffer3D; import flash.display3D.Program3D; import flash.display3D.VertexBuffer3D; import flash.events.Event; import flash.geom.Matrix; import flash.geom.Matrix3D; import flash.geom.Vector3D; import flash.utils.ByteArray; import net.hires.debug.Stats; [SWF(width=465, height=465, frameRate=30, backgroundColor=0x000000)] public class ParticleVideo extends Sprite { public var W : int; public var H : int; public var context : Context3D; public var depthSort : Boolean = false; public var smoothLevel : int = 0; public const VIDEO_WIDTH : int = 360; public const VIDEO_HEIGHT : int = 180; public const SCALE : Number = 2; public const INDEX : Vector.<uint> = Vector.<uint>([0, 1, 2]); public const SIZE : Number = 1; public const ROOT3 : Number = 1.73; public const MAX_COLOR_LENGTH : int = 195075; public const MAX : int = 20000; public const MAX_BUFFER : int = 16; public const OFFSET : int = 4; private var fallingSpeed:Number = .3; private var bmpdPlayer:BitmapData; private var mtxDraw:Matrix; private var projectionTransform:PerspectiveMatrix3D; private var m:Matrix3D; private var zFar:Number = 5; private var program:Program3D; private var buffers:Vector.<VertexBuffer3D>; private var vBufferPos:VertexBuffer3D; private var iBuffer:IndexBuffer3D; private var btnDir:CheckBox; private var sliderSpeed:HSlider; private var rx:Number = 0; private var ry:Number = 0; private var offsetY:Number = 0; private var videoList:ComboBox; private var videoURL:String = "SigurRos.flv"; private var player:VideoPlayer; private var stats:Stats; public function ParticleVideo() { W = stage.stageWidth; H = stage.stageHeight; stage.stage3Ds[0].addEventListener(Event.CONTEXT3D_CREATE, _onContext); stage.stage3Ds[0].requestContext3D(); stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; _initPlayer(); stats = new Stats(); addChild(stats).y = 365; Wonderfl.capture_delay(30); } protected function _onContext(e:Event) : void { stage.stage3Ds[0].removeEventListener(Event.CONTEXT3D_CREATE, _onContext); context = stage.stage3Ds[0].context3D; context.configureBackBuffer(W, H, smoothLevel, depthSort); _init(); stage.addEventListener(Event.RESIZE, __onResize); } private function __onResize(e:Event) : void { context.configureBackBuffer(stage.stageWidth, stage.stageHeight, 0, false); stats.y = stage.stageHeight - 100; } protected function _init() : void { _initControl(); _initShader(); _initBuffer(); _initMatrix(); var data:Vector.<Number> = Vector.<Number>([0, -SIZE, 0, 1]); context.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 10, data, 1); data = Vector.<Number>([SIZE*.5*ROOT3, SIZE*.5*ROOT3, 0, 1]); context.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 11, data, 1); data = Vector.<Number>([-SIZE*.5*ROOT3, SIZE*.5*ROOT3, 0, 1]); context.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 12, data, 1); context.setProgram(program); context.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, m, true); addEventListener(Event.ENTER_FRAME, _loop); } private function _initControl() : void { var window:Window = new Window(this, 5, 5, "Controls"); window.width = 150; window.height = 90; btnDir = new CheckBox(window.content, 5, 5, "UP / DOWN"); sliderSpeed = new HSlider(window.content, 5, 20); sliderSpeed.value = 50; videoList = new ComboBox(window.content, 5, 40, "SigurRos.flv", ["SigurRos.flv", "color2_800x450.f4v", "iron.flv"]); videoList.selectedIndex = 0; } private function _loop(e:Event) : void { if(videoURL != videoList.selectedItem.toString() ) { videoURL = videoList.selectedItem.toString(); // trace("Video:" + videoURL); player.setVideoURL("http://www.bongiovi.tw/experiments/particleVideo/" + videoURL); player.play(); } context.clear(0, 0, 0, 1); // RESET THE MATRIX m = new Matrix3D; m.appendScale(2/W, -2/H, 1); var center:Vector3D = new Vector3D(1, 0, 2); var xx:Number = stage.mouseY/stage.stageHeight * 45; var yy:Number = (stage.mouseX/stage.stageWidth - .5)* 90; var off:Number = rx += ( xx-rx ) * .2; ry += ( yy-ry ) * .2; offsetY += (stage.mouseY/stage.stageHeight - offsetY) * .2; m.appendRotation(rx, Vector3D.X_AXIS, center); m.appendRotation(ry, Vector3D.Y_AXIS, center); m.appendTranslation(-1, 1+offsetY, 0); m.append(projectionTransform); context.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, m, true); // CREATE CURRENT FRAME IMAGE var vbuffer:VertexBuffer3D = _getCurrentImageBuffer(); // PUSHING IN TO BUFFERS buffers.unshift(vbuffer); if(buffers.length > MAX_BUFFER) buffers.pop(); // DRAW EACH FRAM // UPDATING TIME DATA var data:Vector.<Number> = Vector.<Number>([0, 10, 0, 1]); var data1:Vector.<Number> = Vector.<Number>([0, 10, 0, 1]); var offset:Number = 0; var buf:VertexBuffer3D; var f:Function; for (var i:int = buffers.length-1; i >=0; i--) { offset = 1 - i/MAX_BUFFER; offset = Math.sin(offset * Math.PI * .5); data = Vector.<Number>([offset, offset, offset, offset]); f = btnDir.selected ? Math.sin : Math.cos; data1 = Vector.<Number>([f(i/MAX_BUFFER * Math.PI * .5)*sliderSpeed.value/100, 0, 0, 0]); context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, data, 1); context.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 5, data1, 1); context.setVertexBufferAt(0, vBufferPos, 0, Context3DVertexBufferFormat.FLOAT_4); context.setVertexBufferAt(1, buffers[i], 0, Context3DVertexBufferFormat.FLOAT_3); context.drawTriangles(iBuffer); } context.present(); } private function _getCurrentImageBuffer() : VertexBuffer3D { var vBufferPos:VertexBuffer3D = context.createVertexBuffer(MAX * 3, 3); bmpdPlayer.draw(player, mtxDraw); var colors:Vector.<uint> = bmpdPlayer.getVector(bmpdPlayer.rect); var i:int, j:int, posIndex:int, color:uint;; var vBufPos:Vector.<Number> = new Vector.<Number>(MAX * 3 * 3); var r:Number, g:Number, b:Number; var oColor:Object; for(j=0; j<bmpdPlayer.height; j++) { for(i=0; i<bmpdPlayer.width; i++) { color = colors[j*bmpdPlayer.width + i]; oColor = getRGB(color); r = oColor.r/255; g = oColor.g/255; b = oColor.b/255; for(var k:int=0; k<3; k++) { vBufPos[posIndex++] = r; vBufPos[posIndex++] = g; vBufPos[posIndex++] = b; } } } vBufferPos.uploadFromVector(vBufPos, 0, MAX * 3); return vBufferPos; } private function _initBuffer() : void { buffers = new Vector.<VertexBuffer3D>(); // INDICES iBuffer = context.createIndexBuffer(MAX * 3); var iBuf:Vector.<uint> = new Vector.<uint>(MAX*3); var t:int = 0, index:int=0, i:int, j:int, posIndex:int; for( i=0; i<MAX; i++) { for(t=0; t<INDEX.length; t++) { iBuf[index++] = INDEX[t] + i*3; } } iBuffer.uploadFromVector(iBuf, 0, MAX * 3); // VERTICES var vBufPos:Vector.<Number> = new Vector.<Number>(MAX * 3 * 4); var tx:Number, ty:Number, tz:Number; var count : int = 0, bufferCount:int = 0; for(j=0; j<bmpdPlayer.height; j++) { for(i=0; i<bmpdPlayer.width; i++) { tx = i*OFFSET + (W - bmpdPlayer.width*OFFSET) * .5; ty = j*OFFSET + (H - bmpdPlayer.height*OFFSET) * .5; tz = 0; for(var k:int=0; k<3; k++) { vBufPos[posIndex++] = tx; vBufPos[posIndex++] = ty; vBufPos[posIndex++] = tz; vBufPos[posIndex++] = 10+k; } } } vBufferPos = context.createVertexBuffer(MAX * 3, 4); vBufferPos.uploadFromVector(vBufPos, 0, MAX * 3); } private function _initShader() : void { var assembler : AGALMiniAssembler = new AGALMiniAssembler; var code:String = ""; code += "mov vt0, vc[va0.w]\n"; code += "add vt1, va0, vt0\n"; code += "mov vt1.w, vt0.w\n"; code += "mov vt2, va1\n"; code += "mul vt3, vt2, vt2\n"; code += "add vt3.x, vt3.x, vt3.y\n"; code += "add vt3.x, vt3.x, vt3.z\n"; code += "mul vt3.x, vt3.x, vc13.w\n"; code += "div vt3.x, vt3.x, vc13.x\n"; code += "mul vt3.x, vt3.x, vc13.y\n"; code += "add vt3.x, vt3.x, vc13.z\n"; code += "add vt3.x, vt3.x, vc5.x\n"; code += "mov vt1.z, vt3.x\n"; code += "m44 op, vt1, vc0\n"; code += "mov v0, vt2\n"; var vertextShader : ByteArray = assembler.assemble(Context3DProgramType.VERTEX, code); code = "mul oc, v0, fc0\n"; var fragmentShader : ByteArray = assembler.assemble(Context3DProgramType.FRAGMENT, code); var far:Number = 3; var data:Vector.<Number> = Vector.<Number>([3, -far*.3, far, .5]); context.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 13, data, 1); program = context.createProgram(); program.upload(vertextShader, fragmentShader); } private function _initMatrix() : void { projectionTransform = new PerspectiveMatrix3D(); var aspect:Number = 4/3; var zNear:Number = 0.1; var fov:Number = 45*Math.PI/180; projectionTransform.perspectiveFieldOfViewLH(fov, aspect, zNear, zFar); m = new Matrix3D; m.appendScale(4/W, -4/H, 1); m.appendTranslation(-2, 2, 0); m.append(projectionTransform); context.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, m, true); } private function _initPlayer() : void { player = new VideoPlayer(VIDEO_WIDTH, VIDEO_HEIGHT); player.setVideoURL("http://www.bongiovi.tw/experiments/particleVideo/" + videoURL); player.play(); player.setVolume(0); bmpdPlayer = new BitmapData(VIDEO_WIDTH/SCALE, VIDEO_HEIGHT/SCALE, false, 0); mtxDraw = new Matrix; mtxDraw.scale(1/SCALE, 1/SCALE); } public static function getRGB(pixelValue:uint) : Object { var red:uint = pixelValue >> 16 & 0xFF; var green:uint = pixelValue >> 8 & 0xFF; var blue:uint = pixelValue & 0xFF; return {r:red, g:green, b:blue}; } } } import flash.display.Sprite; import flash.events.AsyncErrorEvent; import flash.events.ErrorEvent; import flash.events.Event; import flash.events.NetStatusEvent; import flash.events.SecurityErrorEvent; import flash.media.SoundTransform; import flash.media.Video; import flash.net.NetConnection; import flash.net.NetStream; class VideoPlayer extends Sprite { private var _nc:NetConnection; private var _ns:NetStream; private var _video:Video; private var _url:String; public function VideoPlayer(width:int, height:int) { _nc = new NetConnection(); _nc.addEventListener(NetStatusEvent.NET_STATUS, onStatus); _nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError); _nc.connect(null); _ns = new NetStream(_nc); _ns.client = this; _ns.addEventListener(NetStatusEvent.NET_STATUS, onStatus); _ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, onError); _ns.checkPolicyFile = true; _video = new Video(width, height); _video.smoothing = true; _video.attachNetStream(_ns); addChild(_video); } public function setVideoURL(videoURL:String):void { _url = videoURL; stop(); } public function play():void { if (!_url) { stop(); trace("No URL set. Please use the 'setVideoURL' method."); } else { _ns.play(_url); } } public function stop():void { _ns.close(); _video.clear(); } public function setVolume(volume:Number):void { var st:SoundTransform = _ns.soundTransform; st.volume = volume; _ns.soundTransform = st; } private function onError(e:ErrorEvent) : void { trace(e.text); } private function onStatus(e:NetStatusEvent) : void { trace(e.info.code); } public function onMetaData(o:Object):void { } public function onXMPData(info:Object):void { } } Particle Video