Stage3D 3D Webcam Video
/**
* Copyright devon_o ( http://wonderfl.net/user/devon_o )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/ctT7
*/
package {
/**
* 3D Webcam video using Stage3D
*
* great inspiration and some source code from this joint: http://blog.bongiovi.tw/?p=265
* @author Devon O.
*/
import com.adobe.utils.AGALMiniAssembler;
import com.bit101.components.HUISlider;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.display3D.Context3D;
import flash.display3D.Context3DBlendFactor;
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.media.Camera;
import flash.media.Video;
import flash.text.AntiAliasType;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.utils.ByteArray;
[SWF(width='465', height='465', backgroundColor='#000000', frameRate='60')]
public class Main extends Sprite {
private static const INDEX:Vector.<uint> = Vector.<uint>([0, 1, 2, 0, 2, 3]);
private static const PIXEL_SIZE:int = 4;
private var mVideoData:BitmapData;
private var mVBuffer:VertexBuffer3D;
private var mIBuffer:IndexBuffer3D;
private var mProgram:Program3D;
private var mNumVerts:int;
private var mNumRows:int;
private var mNumCols:int;
private var mSliderSize:HUISlider;
private var mSliderDepth:HUISlider;
private var mContext:Context3D;
private var mVerticies:Vector.<Number>;
private var mAppWidth:int;
private var mAppHeight:int;
private var mWebCam:Camera;
private var mVideo:Video;
public function Main() {
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(event:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
initStage();
}
private function initStage():void
{
mAppWidth = stage.stageWidth;
mAppHeight = stage.stageHeight;
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.stage3Ds[0].addEventListener(Event.CONTEXT3D_CREATE, onContext);
stage.stage3Ds[0].requestContext3D();
stage.addEventListener(Event.RESIZE, onStageResize);
}
private function onStageResize(event:Event):void{
if (!mContext) return;
mAppWidth = stage.stageWidth;
mAppHeight = stage.stageHeight;
mContext.configureBackBuffer(mAppWidth, mAppHeight, 4, true);
}
protected function onContext(event:Event):void{
stage.stage3Ds[0].removeEventListener(Event.CONTEXT3D_CREATE, onContext);
mContext = stage.stage3Ds[0].context3D;
mContext.configureBackBuffer(mAppWidth, mAppHeight, 4, true);
initApp();
}
private function initApp():void {
if (!initCam())
{
exit();
return;
}
initBuffers();
initShader();
initUI();
start();
}
private function initUI():void
{
mSliderSize = new HUISlider(this, 10, 5, "Size");
mSliderSize.value = 40;
mSliderDepth = new HUISlider(this, 10, 25, "Depth");
mSliderDepth.maximum = 2.5;
mSliderDepth.minimum = -2.5;
mSliderDepth.tick = .1;
}
private function exit():void
{
var tf:TextField = new TextField();
tf.selectable = false;
tf.mouseEnabled = false;
tf.mouseWheelEnabled = false;
tf.autoSize = TextFieldAutoSize.LEFT;
tf.defaultTextFormat = new TextFormat("_sans", 12, 0xFFFFFF);
tf.antiAliasType = AntiAliasType.ADVANCED;
tf.text = "No Web Cam detected.";
tf.x = (mAppWidth - tf.width) >> 1;
tf.y = (mAppHeight - tf.height) >> 1;
addChild(tf);
}
private function initCam():Boolean
{
var camIndex:int = 0;
for ( var i:int = 0 ; i < Camera.names.length ; i++ ) {
if ( Camera.names[ i ] == "USB Video Class Video" ) {
camIndex = i;
break;
}
}
mWebCam = Camera.getCamera(String(camIndex));
if (!mWebCam) return false;
mVideo = new Video();
mVideo.attachCamera(mWebCam);
mVideoMat = new Matrix( -1, 0, 0, 1, mVideo.width, 0)
mVideoData = new BitmapData(mVideo.width, mVideo.height, false, 0x000000);
mVideoData.draw(mVideo);
return true;
}
public function start():void{
addEventListener(Event.ENTER_FRAME, onFrame);
}
private function initBuffers():void {
var i:int, j:int, k:int;
var idx:int = 0;
mNumRows = mVideoData.width / PIXEL_SIZE;
mNumCols = mVideoData.height / PIXEL_SIZE;
var pixelCount:int = mNumRows * mNumCols;
mNumVerts = pixelCount * 4;
var numInds:int = pixelCount * 6;
mVBuffer = mContext.createVertexBuffer(mNumVerts, 7);
mContext.setVertexBufferAt(0, mVBuffer, 0, Context3DVertexBufferFormat.FLOAT_4);
mContext.setVertexBufferAt(1, mVBuffer, 4, Context3DVertexBufferFormat.FLOAT_3);
mVerticies = new Vector.<Number>(mNumVerts * 7, true);
var dat:Vector.<Number>;
dat = Vector.<Number>([-1, -1, 0, 1]);
mContext.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 10, dat, 1);
dat = Vector.<Number>([1, -1, 0, 1]);
mContext.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 11, dat, 1);
dat = Vector.<Number>([1, 1, 0, 1]);
mContext.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 12, dat, 1);
dat = Vector.<Number>([-1, 1, 0, 1]);
mContext.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 13, dat, 1);
var indicies:Vector.<uint> = new Vector.<uint>(numInds, true);
idx = 0;
i = 0;
while (i < pixelCount)
{
j = 0;
while (j < INDEX.length)
{
indicies[idx++] = (INDEX[j] + (i * PIXEL_SIZE));
j++;
}
i++;
}
mIBuffer = mContext.createIndexBuffer(numInds);
mIBuffer.uploadFromVector(indicies, 0, numInds);
}
private function getColorObj(color:uint):Object
{
return { r:color >> 16, g:color >> 8 & 0xFF, b:color & 0xFF };
}
private function initShader():void {
var assembler:AGALMiniAssembler = new AGALMiniAssembler();
var agal:String = "";
agal += "mov vt0, vc[va0.w]\n";
agal += "mul vt0, vt0, vc9\n";
agal += "div vt2, va1, vc7\n";
agal += "mul vt3, vt2, vt2\n";
agal += "add vt3.x, vt3.x, vt3.y\n";
agal += "add vt3.x, vt3.x, vt3.z\n";
agal += "div vt3.x, vt3.x, vc8.y\n";
agal += "mul vt3.x, vt3.x, vc8.z\n";
agal += "sub vt3.x, vc8.w, vt3.x\n";
agal += "div vt3.y, vc9.w, vt3.x\n";
agal += "mul vt0.xyz, vt0.xyz, vt3.y\n";
agal += "add vt1, va0, vt0\n";
agal += "mov vt1.z, vt3.x\n";
agal += "mov vt1.w, vt0.w\n";
agal += "m44 op, vt1, vc0\n";
agal += "mov v0, vt2\n";
var vertShader:ByteArray = assembler.assemble(Context3DProgramType.VERTEX, agal);
agal = "mov oc, v0";
var fragShader:ByteArray = assembler.assemble(Context3DProgramType.FRAGMENT, agal);
mContext.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 7, Vector.<Number>([0xFF, 0xFF, 0xFF, 1]));
mProgram = mContext.createProgram();
mProgram.upload(vertShader, fragShader);
mContext.setProgram(mProgram);
}
private var mAng:Number = 1 / Math.tan(45 / 2);
private var mCenter:Matrix3D = new Matrix3D(Vector.<Number>([mAng, 0, 0, 0, 0, mAng, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]));
private var mVideoMat:Matrix;
private var mCam:Matrix3D = new Matrix3D();
private var mRotation:Matrix3D = new Matrix3D();
private var mRotationX:Number = 0.0;
private var mRotationY:Number = 0.0;
private var mPivot:Vector3D = new Vector3D(0.0, 0.0, 2)
public function onFrame(event:Event = null):void {
mVideoData.draw(mVideo, mVideoMat);
var color:uint;
var colorObj:Object;
var i:int = 0, j:int, k:int;
var idx:int = 0;
while (i < mNumCols)
{
j = 0;
while (j < mNumRows)
{
color = mVideoData.getPixel(j * PIXEL_SIZE, i * PIXEL_SIZE);
colorObj = getColorObj(color);
k = 0;
while (k < 4) {
mVerticies[idx++] = j * PIXEL_SIZE;
mVerticies[idx++] = mVideoData.height - (i * PIXEL_SIZE);
mVerticies[idx++] = 0;
mVerticies[idx++] = 10 + k;
mVerticies[idx++] = colorObj.r;
mVerticies[idx++] = colorObj.g;
mVerticies[idx++] = colorObj.b;
k++;
}
j++;
}
i++;
}
mVBuffer.uploadFromVector(mVerticies, 0, mNumVerts);
var w:int = mVideoData.width / PIXEL_SIZE;
var h:int = mVideoData.height / PIXEL_SIZE;
mCam.identity();
mCam.appendTranslation( -w * PIXEL_SIZE * 0.5, -h * PIXEL_SIZE * 0.5, 1);
mCam.appendScale(3.5 / mAppWidth, 3.5 / mAppHeight, 1);
mRotation.identity();
var rx:Number = -((stage.mouseX / mAppWidth) - 0.5) * 180;
var ry:Number = -((stage.mouseY / mAppHeight) - 0.5) * 180;
mRotationX += (rx - mRotationX) / 10;
mRotationY += (ry - mRotationY) / 10;
mRotation.appendRotation( mRotationY, Vector3D.X_AXIS, mPivot );
mRotation.appendRotation( mRotationX, Vector3D.Y_AXIS, mPivot );
mCam.append(mRotation);
mCam.append(mCenter);
mContext.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, mCam, true);
var dat:Vector.<Number>;
var size:Number = (mSliderSize.value / mSliderSize.maximum) * PIXEL_SIZE;
dat = Vector.<Number>([size, size, size, 1]);
mContext.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 9, dat, 1);
dat = Vector.<Number>([1, 8, mSliderDepth.value, 1]);
mContext.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 8, dat, 1);
mContext.setBlendFactors(Context3DBlendFactor.SOURCE_COLOR, Context3DBlendFactor.ONE);
mContext.clear(0.0, 0.0, 0.0, 1.0);
mContext.drawTriangles(mIBuffer);
mContext.present();
}
}
}