package
{
import com.adobe.utils.*;
import flash.display.*;
import flash.display3D.*;
import flash.events.*;
import flash.geom.*;
import flash.text.*;
import flash.ui.Keyboard;
import mx.utils.StringUtil;
public class Main extends Sprite
{
private var context3D:Context3D;
private var vertexBuffer:VertexBuffer3D;
private var indexBuffer:IndexBuffer3D;
private var vertexData:Vector.<Number> = new Vector.<Number>;
private var indexData:Vector.<uint> = new Vector.<uint>;
private var numVertices:int = 0;
private var view:Matrix3D;
private var projection:PerspectiveMatrix3D;
private var keyDown:Array = new Array;
private var tf:TextField = new TextField;
private var cameraPos:Vector3D = new Vector3D(0, 2, 0);
private var cameraFront:Vector3D = new Vector3D(0, 0, 1);
private var cameraUp:Vector3D = new Vector3D(0, 1, 0);
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();
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;
});
tf.defaultTextFormat = new TextFormat(null, 20, 0xffffff);
tf.autoSize = TextFieldAutoSize.LEFT;
addChild(tf);
}
private function onContext3DCreate(e:Event):void
{
context3D = (e.target as Stage3D).context3D;
context3D.configureBackBuffer(stage.stageWidth, stage.stageHeight, 2);
context3D.setCulling(Context3DTriangleFace.BACK);
generateSurface();
generateCloud(0, 5, 0, 1, 1, 1);
vertexBuffer = context3D.createVertexBuffer(numVertices, 6);
vertexBuffer.uploadFromVector(vertexData, 0, numVertices);
indexBuffer = context3D.createIndexBuffer(indexData.length);
indexBuffer.uploadFromVector(indexData, 0, indexData.length);
// 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.setVertexBufferAt(0, vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
context3D.setVertexBufferAt(1, vertexBuffer, 3, Context3DVertexBufferFormat.FLOAT_3);
context3D.setProgram(program);
//
projection = new PerspectiveMatrix3D;
projection.perspectiveFieldOfViewLH(
radians(45), stage.stageWidth / stage.stageHeight, 0.1, 100);
stage.addEventListener(Event.ENTER_FRAME, render);
}
private function generateSurface():void
{
const vertices:Vector.<Number> = Vector.<Number>([
// x, y, z,
0, 0, 0,
0, 0, 1,
1, 0, 1,
1, 0, 0,
]);
const indices:Vector.<uint> = Vector.<uint>([
0, 1, 2, 2, 3, 0,
]);
for (var z:int = 0; z < 10; z++)
{
for (var x:int = 0; x < 10; x++)
{
for (var i:int = 0; i < 4; i++)
{
var base:int = i * 3;
vertexData.push(vertices[base + 0] + x);
vertexData.push(vertices[base + 1]);
vertexData.push(vertices[base + 2] + z);
vertexData.push(0, (x + z) / 18, 0);
}
for (i = 0; i < indices.length; i++)
{
indexData.push(numVertices + indices[i]);
}
numVertices += 4;
}
}
}
private function generateCloud(
x0:Number, y0:Number, z0:Number, x1:Number, y1:Number, z1:Number):void
{
const vertices:Vector.<Number> = Vector.<Number>([
// x, y, z, r, g, b,
0, 0, 0, 0.5, 0.5, 0.5,
1, 0, 0, 0.5, 0.5, 0.5,
1, 0, 1, 0.5, 0.5, 0.5,
0, 0, 1, 0.5, 0.5, 0.5,
0, 1, 0, 1, 1, 1,
1, 1, 0, 1, 1, 1,
1, 1, 1, 1, 1, 1,
0, 1, 1, 1, 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 * 6;
vertexData.push(x0 + vertices[base + 0] * x1);
vertexData.push(y0 + vertices[base + 1] * y1);
vertexData.push(z0 + vertices[base + 2] * z1);
vertexData.push(vertices[base + 3]);
vertexData.push(vertices[base + 4]);
vertexData.push(vertices[base + 5]);
}
for (i = 0; i < indices.length; i++)
{
indexData.push(numVertices + indices[i]);
}
numVertices += 8;
}
private function render(e:Event):void
{
updateView();
context3D.clear(0.39, 0.58, 0.93);
var curx:int = Math.floor(cameraPos.x / 10);
var curz:int = Math.floor(cameraPos.z / 10);
for (var z:int = -1; z <= 1; z++)
{
for (var x:int = -1; x <= 1; x++)
{
var world:Matrix3D = new Matrix3D;
world.appendTranslation((curx + x) * 10, 0, (curz + z) * 10);
var m:Matrix3D = new Matrix3D;
m.append(world);
m.append(view);
m.append(projection);
context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, m, true);
context3D.drawTriangles(indexBuffer, 0, -1);
}
}
context3D.present();
}
private function updateView():void
{
var roll:Number = 0;
var pitch:Number = 0;
var yaw:Number = 0;
var velocity:Number = 0;
if (keyDown[Keyboard.Q]) velocity += .1;
if (keyDown[Keyboard.A]) velocity += -.1;
if (keyDown[Keyboard.Z]) yaw += -1;
if (keyDown[Keyboard.X]) yaw += 1;
if (keyDown[Keyboard.LEFT]) roll += 1;
if (keyDown[Keyboard.RIGHT]) roll += -1;
if (keyDown[Keyboard.UP]) pitch += -1;
if (keyDown[Keyboard.DOWN]) pitch += 1;
var cameraCross:Vector3D = cameraFront.crossProduct(cameraUp); // Left
var m:Matrix3D = new Matrix3D;
m.identity();
m.prependRotation(yaw, cameraUp);
cameraFront = m.transformVector(cameraFront);
cameraCross = m.transformVector(cameraCross);
m.identity();
m.prependRotation(pitch, cameraCross);
cameraFront = m.transformVector(cameraFront);
cameraUp = m.transformVector(cameraUp);
m.identity();
m.prependRotation(roll, cameraFront);
cameraCross = m.transformVector(cameraCross);
cameraUp = m.transformVector(cameraUp);
var forward:Vector3D = cameraFront.clone();
forward.scaleBy(velocity);
cameraPos = cameraPos.add(forward);
view = lookAt(cameraPos, cameraPos.add(cameraFront), cameraUp);
displayParams();
}
private function displayParams():void
{
var camPitch:Number = degrees(Math.asin(cameraFront.y));
var camYaw:Number = degrees(Math.atan2(cameraFront.x, cameraFront.z));
if (camYaw < 0) camYaw += 360;
var camCross:Vector3D = new Vector3D(-1, 0, 0); // Left
var camUp:Vector3D = Vector3D.Y_AXIS;
var m:Matrix3D = new Matrix3D;
m.identity();
m.prependRotation(camYaw, Vector3D.Y_AXIS);
camCross = m.transformVector(camCross);
m.identity();
m.prependRotation(camPitch, camCross);
camUp = m.transformVector(camUp);
var camRoll:Number = degrees(Math.acos(clamp(camUp.dotProduct(cameraUp), -1, 1)));
if (0 < camCross.dotProduct(cameraUp)) camRoll = -camRoll;
tf.text = StringUtil.substitute(
"Pos:{0} {1} {2}\nRoll:{3} Pitch:{4} Yaw:{5}",
cameraPos.x.toFixed(1), cameraPos.y.toFixed(1), cameraPos.z.toFixed(1),
camRoll.toFixed(0), camPitch.toFixed(0), camYaw.toFixed(0));
}
private function lookAt(
cameraPosition:Vector3D, cameraTarget:Vector3D, cameraUpVector:Vector3D):Matrix3D
{
var zaxis:Vector3D = cameraTarget.subtract(cameraPos);
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 degrees(x:Number):Number
{
return x * 180 / Math.PI;
}
private function clamp(x:Number, min:Number, max:Number):Number
{
if (x <= min) return min;
if (max <= x) return max;
return x;
}
}
}