package
{
import com.adobe.utils.*;
import flash.display.Graphics;
import flash.display.Sprite;
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 projectionTransform:PerspectiveMatrix3D;
private var viewTransform:Matrix3D;
private var cameraPos:Vector3D = new Vector3D(0, 200, 0);
private var cameraFront:Vector3D = new Vector3D(0, 0, 1);
private var cameraUp:Vector3D = new Vector3D(0, 1, 0);
private var velocity:Number = 0; // 速度
private var roll:Number = 0;
private var pitch:Number = 0;
private var yaw:Number = 0;
private var throttle:Number = 0; // 推力
private const throttleMax:Number = 0.03;
private var textFormat:TextFormat = new TextFormat(null, 20, 0xffffff);
private var tfParams:TextField;
private var tfThrottle:TextField;
private var tfVelocity:TextField;
private var sprite:Sprite = new Sprite;
private const barX:Number = 200;
private const barWidth:Number = 550;
private const barHeight:Number = 25;
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();
addEventListener(Event.ENTER_FRAME, onRender);
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
tfParams = addTextField(0, 0);
tfThrottle = addTextField(50, 500);
tfVelocity = addTextField(50, 550);
var g:Graphics = graphics;
g.lineStyle(1, 0xffffff);
g.drawRect(barX, 500, barWidth, barHeight);
g.drawRect(barX, 550, barWidth, barHeight);
for (var i:int = 0; i < 250; i += 50)
{
var x:Number = barX + barWidth * i / 250;
g.moveTo(x, 550);
g.lineTo(x, 550 + barHeight);
}
addChild(sprite);
}
private function addTextField(x:Number, y:Number):TextField
{
var tf:TextField = new TextField;
tf.defaultTextFormat = textFormat;
tf.autoSize = TextFieldAutoSize.LEFT;
tf.x = x;
tf.y = y;
addChild(tf);
return tf;
}
private function onContext3DCreate(e:Event):void
{
context3D = stage.stage3Ds[0].context3D;
context3D.configureBackBuffer(800, 600, 2);
context3D.setCulling(Context3DTriangleFace.BACK);
generateSurface();
generateCloud(-1000, 500, -1000, 100, 100, 100);
generateCloud(1000, 500, -1000, 100, 100, 100);
generateCloud(1000, 500, 1000, 100, 100, 100);
generateCloud(-1000, 500, 1000, 100, 100, 100);
// vertexBuffer
vertexBuffer = context3D.createVertexBuffer(numVertices, 6);
vertexBuffer.uploadFromVector(vertexData, 0, numVertices);
// indexBuffer
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);
//
viewTransform = new Matrix3D;
projectionTransform = new PerspectiveMatrix3D;
projectionTransform.perspectiveFieldOfViewLH(45 * Math.PI / 180, 800 / 600, 1, 5000);
}
private function generateSurface():void
{
const vertices:Vector.<Number> = Vector.<Number>([
// x, y, z, r, g, b,
0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0.5, 0.5,
1, 0, 1, 0, 1, 1,
1, 0, 0, 0, 0.5, 0.5,
]);
const indices:Vector.<uint> = Vector.<uint>([
0, 1, 2, 2, 3, 0,
]);
for (var z:int = -10; z < 10; z++)
{
var green:Number = (0 <= z) ? 1 : 0;
var blue:Number = (0 <= z) ? 0 : 1;
for (var x:int = -10; x < 10; x++)
{
for (var i:int = 0; i < 4; i++)
{
var base:int = i * 6;
vertexData.push((vertices[base + 0] + x) * 100);
vertexData.push(vertices[base + 1]);
vertexData.push((vertices[base + 2] + z) * 100);
vertexData.push(vertices[base + 3]);
vertexData.push(vertices[base + 4] * green);
vertexData.push(vertices[base + 5] * blue);
}
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 onRender(e:Event):void
{
if (!context3D) return;
context3D.clear(0.39, 0.58, 0.93);
updateViewMatrix();
var m:Matrix3D = new Matrix3D;
m.append(viewTransform);
m.append(projectionTransform);
context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, m, true);
context3D.drawTriangles(indexBuffer);
context3D.present();
}
private function updateViewMatrix():void
{
// update
var m:Matrix3D = new Matrix3D;
var cameraCross:Vector3D;
cameraCross = cameraFront.crossProduct(cameraUp);
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 drag:Number; // 抗力
drag = velocity * velocity * 0.01;
velocity += (throttle - drag) * 0.5;
var forward:Vector3D = new Vector3D;
forward.copyFrom(cameraFront);
forward.scaleBy(velocity);
cameraPos = cameraPos.add(forward);
// transform
var cameraTransform:Matrix3D = new Matrix3D;
cameraTransform = lookAt(cameraPos, cameraPos.subtract(cameraFront), cameraUp);
viewTransform.copyFrom(cameraTransform);
//viewTransform.invert();
displayParams(drag);
}
private function displayParams(drag:Number):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 = Vector3D.X_AXIS;
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 (camCross.dotProduct(cameraUp) < 0) camRoll = -camRoll;
tfParams.text = StringUtil.substitute(
"Pos:{0} {1} {2}\nPitch:{3} Yaw:{4} Roll:{5}\nDrag:{6}",
cameraPos.x.toFixed(0), cameraPos.y.toFixed(0), cameraPos.z.toFixed(0),
camPitch.toFixed(0), camYaw.toFixed(0), camRoll.toFixed(0),
drag.toFixed(3));
tfThrottle.text = StringUtil.substitute("Throttle:{0}", throttle.toFixed(3));
tfVelocity.text = StringUtil.substitute("Velocity:{0}km/h", (velocity * 108).toFixed(0));
//
var g:Graphics = sprite.graphics;
g.clear();
g.beginFill(0xffffff, 0.75);
g.drawRect(barX, 500, barWidth * throttle / throttleMax, barHeight);
g.endFill();
g.beginFill(0xffff00, 0.75);
g.drawRect(barX, 550, barWidth * (velocity * 108) / 250, barHeight);
g.endFill();
}
private function onKeyDown(e:KeyboardEvent):void
{
switch (e.keyCode)
{
case Keyboard.LEFT: // aileron
roll = 1;
break;
case Keyboard.RIGHT:
roll = -1;
break;
case Keyboard.UP: // elevator
pitch = -1;
break;
case Keyboard.DOWN:
pitch = 1;
break;
case Keyboard.Z: // rudder
yaw = -1;
break;
case Keyboard.X:
yaw = 1;
break;
case Keyboard.Q: // throttle
throttle = Math.min(throttle + 0.001, throttleMax);
break;
case Keyboard.A:
throttle = Math.max(throttle - 0.001, 0);
break;
}
}
private function onKeyUp(e:KeyboardEvent):void
{
switch (e.keyCode)
{
case Keyboard.LEFT:
case Keyboard.RIGHT:
roll = 0;
break;
case Keyboard.UP:
case Keyboard.DOWN:
pitch = 0;
break;
case Keyboard.Z:
case Keyboard.X:
yaw = 0;
break;
}
}
private function lookAt(eye:Vector3D, at:Vector3D, up:Vector3D):Matrix3D
{
var m:Vector.<Number> = new Vector.<Number>(16);
// z軸 = eye - at
at.x = eye.x - at.x;
at.y = eye.y - at.y;
at.z = eye.z - at.z;
m[2] = at.x / at.length;
m[6] = at.y / at.length;
m[10] = at.z / at.length;
// x軸 = up × z軸
at.x = up.y * m[10] - up.z * m[6];
at.y = up.z * m[2] - up.x * m[10];
at.z = up.x * m[6] - up.y * m[2];
m[0] = at.x / at.length;
m[4] = at.y / at.length;
m[8] = at.z / at.length;
// y軸 = z軸 × x軸
m[1] = m[6] * m[8] - m[10] * m[4];
m[5] = m[10] * m[0] - m[2] * m[8];
m[9] = m[2] * m[4] - m[6] * m[0];
// 平行移動
m[12] = -(eye.x * m[0] + eye.y * m[4] + eye.z * m[8]);
m[13] = -(eye.x * m[1] + eye.y * m[5] + eye.z * m[9]);
m[14] = -(eye.x * m[2] + eye.y * m[6] + eye.z * m[10]);
m[3] = m[7] = m[11] = 0;
m[15] = 1;
var matrix:Matrix3D = new Matrix3D;
matrix.rawData = m;
return matrix;
}
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;
}
}
}