package
{
import com.adobe.utils.*;
import flash.display.*;
import flash.display3D.*;
import flash.events.*;
import flash.external.*;
import flash.geom.*;
import flash.text.*;
import flash.ui.*;
import mx.utils.StringUtil;
public class Main extends Sprite
{
// 3D
private var context3D:Context3D;
private var vertexBuffer:VertexBuffer3D;
private var indexBuffer:IndexBuffer3D;
// model
private var vertexData:Vector.<Number>;
private var indexData:Vector.<uint>;
private var numVertices:int;
// matrix
private var view:Matrix3D;
private var projection:PerspectiveMatrix3D;
// input
private var keyDown:Array = new Array;
private var mDown:Boolean = false;
private var mx:Number;
private var my:Number;
// camera
private var camDist:Number = 10;
private var camPitch:int = 0;
private var camYaw:int = 180;
// display
private var tfParams:TextField;
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
stage.stage3Ds[0].addEventListener(Event.CONTEXT3D_CREATE, onContext3DCreate);
stage.stage3Ds[0].requestContext3D();
}
private function onContext3DCreate(e:Event):void
{
context3D = (e.target as Stage3D).context3D;
context3D.configureBackBuffer(stage.stageWidth, stage.stageHeight, 2);
context3D.setCulling(Context3DTriangleFace.BACK);
//context3D.setBlendFactors(
//Context3DBlendFactor.SOURCE_COLOR, Context3DBlendFactor.DESTINATION_COLOR);
// program
var vertexProgram:AGALMiniAssembler = new AGALMiniAssembler;
vertexProgram.assemble(Context3DProgramType.VERTEX,
"m44 op va0 vc0\n" +
"mov v0 va1"
);
var fragmentProgram:AGALMiniAssembler = new AGALMiniAssembler;
fragmentProgram.assemble(Context3DProgramType.FRAGMENT,
"mov oc v0"
);
var program:Program3D = context3D.createProgram();
program.upload(vertexProgram.agalcode, fragmentProgram.agalcode);
context3D.setProgram(program);
//
projection = new PerspectiveMatrix3D;
projection.perspectiveFieldOfViewLH(
radians(45), stage.stageWidth / stage.stageHeight, 1, 100);
tfParams = addTextField(0, 0);
//
ExternalInterface.addCallback("generate", generate);
stage.addEventListener(KeyboardEvent.KEY_DOWN, function(e:KeyboardEvent):void {
keyDown[e.keyCode] = true;
});
stage.addEventListener(KeyboardEvent.KEY_UP, function(e:KeyboardEvent):void {
keyDown[e.keyCode] = false;
});
stage.addEventListener(Event.DEACTIVATE, function(e:Event):void {
keyDown = new Array;
});
stage.addEventListener(MouseEvent.MOUSE_DOWN, function(e:MouseEvent):void {
mDown = true;
});
stage.addEventListener(MouseEvent.MOUSE_UP, function(e:MouseEvent):void {
mDown = false;
});
stage.addEventListener(MouseEvent.MOUSE_WHEEL, function(e:MouseEvent):void {
camDist -= e.delta;
});
stage.addEventListener(Event.ENTER_FRAME, render);
}
private function addTextField(x:Number, y:Number):TextField
{
var tf:TextField = new TextField;
tf.defaultTextFormat = new TextFormat(null, 20, 0xffffff);
tf.autoSize = TextFieldAutoSize.LEFT;
tf.x = x;
tf.y = y;
addChild(tf);
return tf;
}
private function render(e:Event):void
{
updateView();
context3D.clear(0.39, 0.58, 0.93);
if (numVertices != 0)
{
var m:Matrix3D = new Matrix3D;
m.append(view);
m.append(projection);
context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, m, true);
context3D.drawTriangles(indexBuffer);
}
context3D.present();
}
private function updateView():void
{
if (keyDown[Keyboard.W]) camPitch += 1;
if (keyDown[Keyboard.S]) camPitch -= 1;
if (keyDown[Keyboard.A]) camYaw += 1;
if (keyDown[Keyboard.D]) camYaw -= 1;
if (keyDown[Keyboard.PAGE_UP]) camDist -= 0.1;
if (keyDown[Keyboard.PAGE_DOWN]) camDist += 0.1;
if (mDown)
{
camYaw += mouseX - mx;
camPitch += mouseY - my;
}
mx = mouseX;
my = mouseY;
if (camPitch < -90) camPitch = -90;
if (90 < camPitch) camPitch = 90;
if (camYaw < 0) camYaw += 360;
if (360 <= camYaw) camYaw -= 360;
if (camDist < 1) camDist = 1;
var pitch:Number = radians(camPitch);
var y:Number = Math.sin(pitch) * camDist;
var r:Number = Math.cos(pitch) * camDist;
var yaw:Number = radians(camYaw);
var z:Number = Math.cos(yaw) * r;
var x:Number = Math.sin(yaw) * r;
view = lookAt(new Vector3D(x, y, z), new Vector3D, Vector3D.Y_AXIS);
}
private function generate(csv:String):void
{
var tbl:Array = parseCSV(csv);
vertexData = new Vector.<Number>;
indexData = new Vector.<uint>;
numVertices = 0;
for each (var fields:Array in tbl)
{
switch (fields[0])
{
case "b":
genBox(fields);
break;
case "t":
genTriangle(fields);
break;
}
}
vertexBuffer = context3D.createVertexBuffer(numVertices, 6);
vertexBuffer.uploadFromVector(vertexData, 0, numVertices);
indexBuffer = context3D.createIndexBuffer(indexData.length);
indexBuffer.uploadFromVector(indexData, 0, indexData.length);
//
context3D.setVertexBufferAt(0, vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
context3D.setVertexBufferAt(1, vertexBuffer, 3, Context3DVertexBufferFormat.FLOAT_3);
}
private function genBox(f:Array):void
{
const vertices:Vector.<int> = Vector.<int>([
// x, y, z,
0, 0, 0,
1, 0, 0,
1, 0, 1,
0, 0, 1,
0, 1, 0,
1, 1, 0,
1, 1, 1,
0, 1, 1,
]);
const indices:Vector.<uint> = Vector.<uint>([
0, 4, 5, 5, 1, 0, // z-
1, 5, 6, 6, 2, 1, // x+
2, 6, 7, 7, 3, 2, // z+
3, 7, 4, 4, 0, 3, // x-
0, 1, 2, 2, 3, 0, // y-
4, 7, 6, 6, 5, 4, // y+
]);
for (var i:int = 0; i < 8; i++)
{
var base:int = i * 3;
vertexData.push(vertices[base + 0] == 0 ? f[1] : f[2]);
vertexData.push(vertices[base + 1] == 0 ? f[3] : f[4]);
vertexData.push(vertices[base + 2] == 0 ? f[5] : f[6]);
vertexData.push(color(f[10]), color(f[11]), color(f[12]));
}
for (i = 0; i < indices.length; i++)
{
indexData.push(numVertices + indices[i]);
}
numVertices += 8;
}
private function genTriangle(f:Array):void
{
for (var i:int = 0; i < 3; i++)
{
var base:int = 1 + i * 3;
vertexData.push(f[base + 0]);
vertexData.push(f[base + 1]);
vertexData.push(f[base + 2]);
vertexData.push(color(f[10]), color(f[11]), color(f[12]));
indexData.push(numVertices++);
}
}
private function parseCSV(dat:String):Array
{
dat = dat.replace(/\s+$/);
var csvDat:Array = new Array;
var tmp:Array = dat.split("\n");
var len:Number = tmp.length;
for (var i:int = 0; i < len; i++)
{
csvDat.push(tmp[i].split("\t"));
}
return csvDat;
}
private function lookAt(
cameraPosition:Vector3D, cameraTarget:Vector3D, cameraUpVector:Vector3D):Matrix3D
{
var zaxis:Vector3D = cameraTarget.subtract(cameraPosition);
zaxis.normalize();
var xaxis:Vector3D = cameraUpVector.crossProduct(zaxis);
xaxis.normalize();
var yaxis:Vector3D = zaxis.crossProduct(xaxis);
return new Matrix3D(Vector.<Number>([
xaxis.x, yaxis.x, zaxis.x, 0,
xaxis.y, yaxis.y, zaxis.y, 0,
xaxis.z, yaxis.z, zaxis.z, 0,
-xaxis.dotProduct(cameraPosition), -yaxis.dotProduct(cameraPosition),
-zaxis.dotProduct(cameraPosition), 1
]));
}
private function radians(x:Number):Number
{
return x * Math.PI / 180;
}
private function color(s:String):Number
{
return parseInt(s, 16) / 0xff;
}
}
}