package
{
import com.adobe.utils.*;
import flash.display.*;
import flash.display3D.*;
import flash.events.*;
import flash.geom.*;
import flash.ui.*;
public class Main extends Sprite
{
// 3D
private var context3D:Context3D;
private var vertexBuffer:VertexBuffer3D;
private var indexBuffer:IndexBuffer3D;
// model
private var vertexData:Vector.<Number> = new Vector.<Number>;
private var indexData:Vector.<uint> = new Vector.<uint>;
private var numVertices:int = 0;
private const colorFixed:Vector.<Number> = Vector.<Number>([1, 1, 1, 1]);
private var numFixed:int;
private var indexSphere:int;
private var numSphere: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;
//
private const au:Number = 149597870.700; // 天文単位(km)
private var planet:Vector.<Object>;
// camera
private var camDist:Number = 2 * au;
private var camLat:Number = 0;
private var camLon:Number = 180;
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 = stage.stage3Ds[0].context3D;
context3D.configureBackBuffer(stage.stageWidth, stage.stageHeight, 2);
context3D.setCulling(Context3DTriangleFace.BACK);
// program
var vertexProgram:AGALMiniAssembler = new AGALMiniAssembler;
vertexProgram.assemble(Context3DProgramType.VERTEX,
"m44 op va0 vc0"
);
var fragmentProgram:AGALMiniAssembler = new AGALMiniAssembler;
fragmentProgram.assemble(Context3DProgramType.FRAGMENT,
"mov oc fc0"
);
var program:Program3D = context3D.createProgram();
program.upload(vertexProgram.agalcode, fragmentProgram.agalcode);
context3D.setProgram(program);
//
projection = new PerspectiveMatrix3D;
projection.perspectiveFieldOfViewLH(
toRadians(45), stage.stageWidth / stage.stageHeight, 0.1 * au, 10 * au);
//
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 * au * 0.1;
});
stage.addEventListener(Event.ENTER_FRAME, render);
}
private function generate():void
{
// R:直径(km) a:軌道長半径(au) E:公転周期(year)
planet = new Vector.<Object>(4);
planet[0] = { R:1392038, color:Vector.<Number>([1, 1, 0, 1]) };
planet[1] = { R:4879.4, a:0.38709927, E:0.241, color:Vector.<Number>([0, 1, 1, 1]) };
planet[2] = { R:12103.6, a:0.72333566, E:0.615, color:Vector.<Number>([1, 0.5, 0, 1]) };
planet[3] = { R:12756.3, a:1.00000261, E:1.000, color:Vector.<Number>([0, 0, 1, 1]) };
for each (var p:Object in planet)
{
p.lon = 0;
genCircle(p.a * au, p.R * 50, 64);
}
numFixed = indexData.length / 3;
indexSphere = indexData.length;
genSphere();
numSphere = (indexData.length - indexSphere) / 3;
vertexBuffer = context3D.createVertexBuffer(numVertices, 3);
vertexBuffer.uploadFromVector(vertexData, 0, numVertices);
indexBuffer = context3D.createIndexBuffer(indexData.length);
indexBuffer.uploadFromVector(indexData, 0, indexData.length);
context3D.setVertexBufferAt(0, vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
}
// a=軌道長半径 R=直径
private function genCircle(a:Number, R:Number, slice:int):void
{
const vertices:Vector.<int> = Vector.<int>([
// x, y, z, x=start/end z=in/out
0, 0, 0,
0, 1, 0,
0, 1, 1,
0, 0, 1,
1, 0, 0,
1, 1, 0,
1, 1, 1,
1, 0, 1,
]);
const indices:Vector.<uint> = Vector.<uint>([
0, 1, 5, 5, 4, 0, // in
1, 2, 6, 6, 5, 1, // +y
2, 3, 7, 7, 6, 2, // out
3, 0, 4, 4, 7, 3, // -y
]);
for (var s:int = 0; s < slice; s++)
{
for (var i:int = 0; i < 8; i++)
{
var base:int = i * 3;
var lon:Number = 2 * Math.PI * (s + vertices[base]) / slice;
var y:Number = (vertices[base + 1] - 0.5) * R;
var r:Number = a + (vertices[base + 2] - 0.5) * R;
vertexData.push(Math.sin(lon) * r);
vertexData.push(y);
vertexData.push(Math.cos(lon) * r);
}
for (i = 0; i < indices.length; i++)
{
indexData.push(numVertices + indices[i]);
}
numVertices += 8;
}
}
// r=半径
private function genSphere():void
{
const vertices:Vector.<int> = Vector.<int>([
// x, y, z,
0, 1, 0,
0, 0, 1,
1, 0, 0,
0, 0, -1,
-1, 0, 0,
0, -1, 0,
]);
const indices:Vector.<uint> = Vector.<uint>([
0, 1, 2,
0, 2, 3,
0, 3, 4,
0, 4, 1,
1, 5, 2,
2, 5, 3,
3, 5, 4,
4, 5, 1,
]);
for (var i:int = 0; i < 6; i++)
{
var base:int = i * 3;
vertexData.push(vertices[base + 0]);
vertexData.push(vertices[base + 1]);
vertexData.push(vertices[base + 2]);
}
for (i = 0; i < indices.length; i++)
{
indexData.push(numVertices + indices[i]);
}
numVertices += 6;
}
private function render(e:Event):void
{
updateView();
context3D.clear(0.39, 0.58, 0.93);
var m:Matrix3D = new Matrix3D;
m.append(view);
m.append(projection);
context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, m, true);
context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, colorFixed);
context3D.drawTriangles(indexBuffer, 0, numFixed);
for each (var p:Object in planet)
{
m.identity();
m.append(calcWorld(p));
m.append(view);
m.append(projection);
context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, m, true);
context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, p.color);
context3D.drawTriangles(indexBuffer, indexSphere, numSphere);
}
context3D.present();
}
private function updateView():void
{
if (keyDown[Keyboard.W]) camLat += 1;
if (keyDown[Keyboard.S]) camLat -= 1;
if (keyDown[Keyboard.A]) camLon += 1;
if (keyDown[Keyboard.D]) camLon -= 1;
if (keyDown[Keyboard.PAGE_UP]) camDist -= 0.01 * au;
if (keyDown[Keyboard.PAGE_DOWN]) camDist += 0.01 * au;
if (mDown)
{
camLon += mouseX - mX;
camLat += mouseY - mY;
}
mX = mouseX;
mY = mouseY;
if (camLat < -90) camLat = -90;
if (90 < camLat) camLat = 90;
while (camLon < 0) camLon += 360;
while (360 <= camLon) camLon -= 360;
if (camDist < 0) camDist = 0;
var lat:Number = toRadians(camLat);
var y:Number = Math.sin(lat) * camDist;
var r:Number = Math.cos(lat) * camDist;
var lon:Number = toRadians(camLon);
var z:Number = Math.cos(lon) * r;
var x:Number = Math.sin(lon) * r;
view = lookAt(new Vector3D(x, y, z), new Vector3D, Vector3D.Y_AXIS);
}
private function calcWorld(p:Object):Matrix3D
{
var s:Number;
var x:Number;
var z:Number;
if (p.hasOwnProperty("E"))
{
s = p.R * 300;
var lon:Number = p.lon + 1 / p.E;
if (360 <= lon) lon -= 360;
p.lon = lon;
var r:Number = p.a * au;
var rad:Number = toRadians(lon);
x = Math.cos(rad) * r;
z = Math.sin(rad) * r;
}
else
{
s = p.R * 5;
x = z = 0;
}
var world:Matrix3D = new Matrix3D;
world.appendScale(s, s, s);
world.appendTranslation(x, 0, z);
return world;
}
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 toRadians(degrees:Number):Number
{
return degrees * Math.PI / 180;
}
}
}