package
{
import com.adobe.utils.*;
import flash.display.*;
import flash.display3D.*;
import flash.events.*;
import flash.geom.*;
import flash.text.*;
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 mDown:Boolean = false;
private var mx:Number;
private var my:Number;
// camera
private var camDist:Number = 20;
private var camPitch:int = 0;
private var camYaw:int = 180;
// button
private var btnArray:Vector.<TextField>;
private var btnSelect:int;
private var firstIndex:Vector.<int> = new Vector.<int>;
private var numTriangles:Vector.<int> = new Vector.<int>;
private var prevIndex:int = 0;
private var pos:Vector.<Vector3D>;
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);
//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(30), stage.stageWidth / stage.stageHeight, 1, 100);
//
generate();
btnArray = new Vector.<TextField>;
createButton(10, 10, 200, 50, "六方最密充填構造\nhexagonal close-packed");
createButton(10, 70, 200, 50, "面心立方格子構造\nface-centered cubic");
selectButton(0);
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 createButton(x:Number, y:Number, width:Number, height:Number, text:String):void
{
var tf:TextField = new TextField;
tf.background = true;
tf.border = true;
tf.defaultTextFormat = new TextFormat(null, 20);
tf.name = btnArray.length.toString();
tf.selectable = false;
tf.x = x;
tf.y = y;
tf.width = width;
tf.height = height;
tf.text = text;
tf.addEventListener(MouseEvent.CLICK, onButtonClick);
addChild(tf);
btnArray.push(tf);
}
private function onButtonClick(e:MouseEvent):void
{
var select:int = parseInt((e.target as TextField).name);
selectButton(select);
}
private function selectButton(select:int):void
{
for (var i:int = 0; i < btnArray.length; i++)
{
var tf:TextField = btnArray[i];
tf.backgroundColor = (i == select) ? 0xffff00 : 0x7f7f7f;
}
btnSelect = select;
pos = new Vector.<Vector3D>;
switch (btnSelect)
{
case 0: hcp(); break;
case 1: fcc(); break;
}
}
private function hcp():void
{
const h:Number = Math.sqrt(2 / 3) * 2;
const r3:Number = Math.sqrt(3);
// x, y, z, c(0:青 1:赤 2:緑 3:黄)
var y:Number = -h;
var z:Number = 0;
var c:Number = 0;
pos.push(new Vector3D( -1, y, z - r3, c));
pos.push(new Vector3D(1, y, z - r3, c));
pos.push(new Vector3D(3, y, z - r3, 3));
pos.push(new Vector3D( -2, y, z, c));
pos.push(new Vector3D(0, y, z, c));
pos.push(new Vector3D(2, y, z, c));
pos.push(new Vector3D( -1, y, z + r3, c));
pos.push(new Vector3D(1, y, z + r3, c));
pos.push(new Vector3D(3, y, z + r3, 3));
y = 0;
z = r3 * (2 / 3);
c = 1;
pos.push(new Vector3D( -1, y, z - r3, c));
pos.push(new Vector3D(1, y, z - r3, c));
pos.push(new Vector3D(3, y, z - r3, 3));
pos.push(new Vector3D( -2, y, z, 3));
pos.push(new Vector3D(0, y, z, c));
pos.push(new Vector3D(2, y, z, 3));
pos.push(new Vector3D( -1, y, z + r3, 3));
pos.push(new Vector3D(1, y, z + r3, 3));
pos.push(new Vector3D(3, y, z + r3, 3));
y = h;
z = 0;
c = 0;
pos.push(new Vector3D( -1, y, z - r3, c));
pos.push(new Vector3D(1, y, z - r3, c));
pos.push(new Vector3D(3, y, z - r3, 3));
pos.push(new Vector3D( -2, y, z, c));
pos.push(new Vector3D(0, y, z, c));
pos.push(new Vector3D(2, y, z, c));
pos.push(new Vector3D( -1, y, z + r3, c));
pos.push(new Vector3D(1, y, z + r3, c));
pos.push(new Vector3D(3, y, z + r3, 3));
}
private function fcc():void
{
const h:Number = Math.sqrt(2 / 3) * 2;
const r3:Number = Math.sqrt(3);
// x, y, z, c(0:青 1:赤 2:緑 3:黄)
var y:Number = -h;
var z:Number = 0;
var c:Number = 0;
pos.push(new Vector3D( -1, y, z - r3, c));
pos.push(new Vector3D(1, y, z - r3, 3));
pos.push(new Vector3D(3, y, z - r3, c));
pos.push(new Vector3D( -2, y, z, c));
pos.push(new Vector3D(0, y, z, c));
pos.push(new Vector3D(2, y, z, c));
pos.push(new Vector3D( -1, y, z + r3, c));
pos.push(new Vector3D(1, y, z + r3, c));
pos.push(new Vector3D(3, y, z + r3, c));
y = 0;
z = r3 * (2 / 3);
c = 1;
pos.push(new Vector3D( -1, y, z - r3, 3));
pos.push(new Vector3D(1, y, z - r3, 3));
pos.push(new Vector3D(3, y, z - r3, 3));
pos.push(new Vector3D( -2, y, z, c));
pos.push(new Vector3D(0, y, z, c));
pos.push(new Vector3D(2, y, z, c));
pos.push(new Vector3D( -1, y, z + r3, c));
pos.push(new Vector3D(1, y, z + r3, c));
pos.push(new Vector3D(3, y, z + r3, c));
y = h;
z = r3 * (4 / 3);
c = 2;
pos.push(new Vector3D( -2, y, z - 2 * r3, c));
pos.push(new Vector3D(0, y, z - 2 * r3, c));
pos.push(new Vector3D(2, y, z - 2 * r3, c));
pos.push(new Vector3D( -1, y, z - r3, c));
pos.push(new Vector3D(1, y, z - r3, 3));
pos.push(new Vector3D(3, y, z - r3, c));
pos.push(new Vector3D( -2, y, z, c));
pos.push(new Vector3D(0, y, z, c));
pos.push(new Vector3D(2, y, z, c));
}
private function generate():void
{
vertexData = new Vector.<Number>;
indexData = new Vector.<uint>;
numVertices = 0;
genSphere(0.5, 0.5, 1);
genSphere(1, 0.5, 0.5);
genSphere(0.5, 1, 0.5);
genSphere(1, 1, 0.5);
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 genSphere(red:Number, green:Number, blue:Number):void
{
const stackNum:int = 32; // 輪切りの数。2の倍数。最小2。
// vertex
var vertices:Vector.<Number> = new Vector.<Number>;
vertices.push(0, 1, 0); // x, y, z
for (var stack:int = 1; stack < stackNum; stack++)
{
var lat:Number = Math.PI * (stack / stackNum);
var y:Number = Math.cos(lat);
var r:Number = Math.sin(lat);
var sliceNum:int = 4 * Math.min(stack, stackNum - stack); // 放射頂点の数
for (var slice:int = 0; slice <= sliceNum; slice++)
{
var lon:Number = 2 * Math.PI * (slice / sliceNum);
var z:Number = Math.cos(lon) * r;
var x:Number = Math.sin(lon) * r;
vertices.push(x, y, z);
}
}
vertices.push(0, -1, 0);
// index
var indices:Vector.<uint> = new Vector.<uint>;
var prevHead:int = 0; // 前の先頭頂点番号
var prevVert:int = 1; // 前の頂点数
for (stack = 0; stack < stackNum / 2; stack++)
{
var currHead:int = prevHead + prevVert; // 現在の先頭頂点番号
var currVert:int = 4 * (stack + 1) + 1; // 現在の頂点数
for (var quad:int = 0; quad < 4; quad++) // 4象限
{
var prevQuad:int = quad * stack; // 前の象限オフセット
var currQuad:int = quad * (stack + 1); // 現在の象限オフセット
for (var i:int = 0; ; i++)
{
indices.push(prevHead + prevQuad + i);
indices.push(currHead + currQuad + i);
indices.push(currHead + currQuad + i + 1);
if (stack <= i) break;
indices.push(prevHead + prevQuad + i);
indices.push(currHead + currQuad + i + 1);
indices.push(prevHead + prevQuad + i + 1);
}
}
prevHead = currHead;
prevVert = currVert;
}
for (stack = stackNum / 2 - 1; 0 <= stack; stack--)
{
currHead = prevHead + prevVert;
currVert = 4 * stack + 1;
for (quad = 0; quad < 4; quad++) // 4象限
{
prevQuad = quad * (stack + 1);
currQuad = quad * stack;
for (i = 0; ; i++)
{
indices.push(currHead + currQuad + i);
indices.push(prevHead + prevQuad + i + 1);
indices.push(prevHead + prevQuad + i);
if (stack <= i) break;
indices.push(currHead + currQuad + i);
indices.push(currHead + currQuad + i + 1);
indices.push(prevHead + prevQuad + i + 1);
}
}
prevHead = currHead;
prevVert = currVert;
}
//
var num:int = vertices.length / 3;
for (i = 0; i < num; i++)
{
var base:int = i * 3;
var c:Number = (1 + vertices[base + 1]) / 2;
vertexData.push(vertices[base + 0]);
vertexData.push(vertices[base + 1]);
vertexData.push(vertices[base + 2]);
vertexData.push(c * red, c * green, c * blue);
}
for (i = 0; i < indices.length; i++)
{
indexData.push(numVertices + indices[i]);
}
numVertices += num;
firstIndex.push(prevIndex);
numTriangles.push((indexData.length - prevIndex) / 3);
prevIndex = indexData.length;
}
private function render(e:Event):void
{
updateView();
context3D.clear(0.39, 0.58, 0.93);
for each (var v:Vector3D in pos)
{
var world:Matrix3D = new Matrix3D;
world.appendTranslation(v.x, v.y, v.z);
var m:Matrix3D = new Matrix3D;
m.append(world);
m.append(view);
m.append(projection);
context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, m, true);
context3D.drawTriangles(indexBuffer, firstIndex[v.w], numTriangles[v.w]);
}
context3D.present();
}
private function updateView():void
{
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 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;
}
}
}