Annular Eclipse Stage3D
時事ネタということで、Stage3Dで金環日食っぽいのを作ってみました。
マウスのY座標で光の強さを変えられます。
/**
* Copyright yuichiroharai ( http://wonderfl.net/user/yuichiroharai )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/eNkj
*/
package
{
import com.adobe.utils.AGALMiniAssembler;
import com.adobe.utils.PerspectiveMatrix3D;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.display.Stage3D;
import flash.display.StageAlign;
import flash.display.StageQuality;
import flash.display.StageScaleMode;
import flash.display3D.Context3D;
import flash.display3D.Context3DBlendFactor;
import flash.display3D.Context3DCompareMode;
import flash.display3D.Context3DProgramType;
import flash.display3D.Context3DRenderMode;
import flash.display3D.Context3DTriangleFace;
import flash.display3D.Context3DVertexBufferFormat;
import flash.display3D.IndexBuffer3D;
import flash.display3D.Program3D;
import flash.display3D.VertexBuffer3D;
import flash.events.Event;
import flash.geom.Matrix3D;
import flash.geom.Vector3D;
import flash.utils.getTimer;
import net.hires.debug.Stats;
[SWF(width="465", height="465", backgroundColor="0x000000")]
public class AnnularEclipse extends Sprite
{
// ----------------------------------------------------------------------------------------------------
// 変数
//
// Stage 関連
private var _stageWidth:uint;
private var _stageHeight:uint;
private var _stageWidthHarf:uint;
private var _stageHeightHarf:uint;
// Stage3D 関連
static private const _SEGMENT:uint = 128;
private var _stage3D:Stage3D;
private var _context3D:Context3D;
private var _stage3DSize:uint;
private var _vertexList:Vector.<Number>;
private var _vertexBuffer:VertexBuffer3D;
private var _indexList:Vector.<uint>;
private var _indexBuffer:IndexBuffer3D;
private var _perspectiveMatrix3D:PerspectiveMatrix3D;
private var _time:uint;
private var _rotationZ:Number = 0;
private var _power:Number = 0;
private var _mouseY:Number = 0;
// AGAL 関連
private var _agalVertex:String;
private var _agalFragment:String;
// スクリーンショット
private var _screenShot:Bitmap;
// ----------------------------------------------------------------------------------------------------
// コンストラクタ
//
// コンストラクタ
public function AnnularEclipse():void
{
Wonderfl.disable_capture();
//addChild(_screenShot = new Bitmap());
// ステージ設定
stage.quality = StageQuality.BEST;
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.frameRate = 60;
// ステージイベント
stage.addEventListener(Event.FULLSCREEN, _atResize);
stage.addEventListener(Event.RESIZE, _atResize);
_atResize();
_initStage3D();
}
// ----------------------------------------------------------------------------------------------------
// ステージイベント
//
// ステージのリサイズ
private function _atResize(e:Event=null):void
{
_stageWidth = stage.stageWidth;
_stageHeight = stage.stageHeight;
_stageWidthHarf = uint(_stageWidth/2);
_stageHeightHarf = uint(_stageHeight/2);
if (_context3D != null) _resizeStage3D();
}
// ----------------------------------------------------------------------------------------------------
// Stage3D
//
// Stage3D の初期化
private function _initStage3D():void
{
_stage3D = stage.stage3Ds[0];
_stage3D.addEventListener(Event.CONTEXT3D_CREATE, _onContext3dCreate);
_stage3D.requestContext3D(Context3DRenderMode.AUTO);
}
// Stage3D の初期化完了
private function _onContext3dCreate(e:Event):void
{
var model:Model;
// Context3D 初期化
_context3D = _stage3D.context3D;
//_context3D.enableErrorChecking = true;
_context3D.setDepthTest(true, Context3DCompareMode.LESS);
_context3D.setCulling(Context3DTriangleFace.NONE);
_context3D.setBlendFactors(Context3DBlendFactor.SOURCE_ALPHA, Context3DBlendFactor.ZERO);
_resizeStage3D();
_initAgal();
model = new Model(0.8, _SEGMENT);
model.color(0xffd700).createIndex();
// VertexBuffer
_vertexBuffer = _createVertexBuffer(model.vertexList, 8);
_context3D.setVertexBufferAt(0, _vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_4);
_context3D.setVertexBufferAt(1, _vertexBuffer, 4, Context3DVertexBufferFormat.FLOAT_4);
// IndexBuffer
_indexBuffer = _createIndexBuffer(model.indexList);
// 透視投影変換用 Matrix3D
_perspectiveMatrix3D = new PerspectiveMatrix3D();
_perspectiveMatrix3D.perspectiveFieldOfViewLH(Math.PI/4, 1, 0.1, 1000);
addEventListener(Event.ENTER_FRAME, _drawStage3D);
}
// 毎フレームの描画処理
private function _drawStage3D(e:Event):void
{
var currentTime:uint, matrix3D:Matrix3D, power:Number;
// 時間で回転角度を決定
_time = (currentTime = getTimer()) - _time;
if ((_rotationZ += 0.01 * _time) > 360) _rotationZ -= 360;
if ((_power += 0.1 * _time) > 360) _power -= 360;
_time = currentTime;
power = Model.sin(_power * Model.PI2 / 360);
power = (power * 0.5 + 0.5) * 0.1;
// マウスで変化
_mouseY += (stage.mouseY/stage.stageHeight - _mouseY)*0.1;
power += 0.5 * _mouseY;
// 回転の Matrix3D を生成
matrix3D = new Matrix3D();
matrix3D.appendRotation(_rotationZ, Vector3D.Z_AXIS);
matrix3D.appendTranslation(0, 0, 3);
matrix3D.append(_perspectiveMatrix3D);
// 定数アップロード
_uploadVertexConstFromMatrix(0, matrix3D);
_uploadVertexConstFromVector(4, Vector.<Number>([power, 0, 0, 1]))
// 描画
_context3D.clear(0, 0, 0);
_context3D.drawTriangles(_indexBuffer, 0);
//_context3D.drawToBitmapData(_screenShot.bitmapData);
_context3D.present();
}
// Stage3D のリサイズ
private function _resizeStage3D():void {
// サイズ
_stage3DSize = (_stageWidth > _stageHeight) ? _stageHeightHarf : _stageWidthHarf;
_stage3DSize = _stage3DSize * 2; // 偶数になるように調整
if (_stage3DSize < 466) _stage3DSize = 466;
// 配置
_stage3D.x = _stageWidthHarf - _stage3DSize/2;
_stage3D.y = _stageHeightHarf - _stage3DSize/2;
// バッファサイズの初期化
_context3D.configureBackBuffer(_stage3DSize, _stage3DSize, 16, true);
/*if (_screenShot.bitmapData != null) _screenShot.bitmapData.dispose();
_screenShot.bitmapData = new BitmapData(_stage3DSize, _stage3DSize, false, 0);
_screenShot.x = _stage3D.x;
_screenShot.y = _stage3D.y;*/
}
// ----------------------------------------------------------------------------------------------------
// Stage3D - AGAL
//
private function _initAgal():void
{
var vertexAssembler:AGALMiniAssembler, fragmentAssembler:AGALMiniAssembler, program3D:Program3D;
// 頂点シェーダー
_agalVertex = "mov vt0, va0 \n"; // 座標をコピー
_agalVertex += "mov vt1.w, va0.w \n"; // wを退避
_agalVertex += "mov vt0.w, vc4.w \n"; // wを1に
_agalVertex += "mul vt1.w, vt1.w, vc4.x \n"; // 取り出したwに定数をかける
_agalVertex += "add vt1.w, vt1.w, vc4.w \n"; // 1を足す
_agalVertex += "mul vt0.xyz, vt0.xyz, vt1.w \n"; // 座標にwをかける
_agalVertex += "m44 op, vt0, vc0 \n"; // 透視投影変換など
_agalVertex += "mov v0, va1";
// 断片シェーダー
_agalFragment = "mov oc, v0";
vertexAssembler = new AGALMiniAssembler();
vertexAssembler.assemble(Context3DProgramType.VERTEX, _agalVertex);
fragmentAssembler = new AGALMiniAssembler();
fragmentAssembler.assemble(Context3DProgramType.FRAGMENT, _agalFragment);
program3D = _context3D.createProgram();
program3D.upload(vertexAssembler.agalcode, fragmentAssembler.agalcode);
_context3D.setProgram(program3D);
}
// ----------------------------------------------------------------------------------------------------
// Stage3D - ユーティリティ
//
// VertexBufferの生成
private function _createVertexBuffer(data:Vector.<Number>, numData:int=8):VertexBuffer3D
{
var vertexBuffer:VertexBuffer3D, vertexNum:uint;
vertexNum = data.length/numData;
vertexBuffer = _context3D.createVertexBuffer(vertexNum, numData);
vertexBuffer.uploadFromVector(data, 0, vertexNum);
return vertexBuffer;
}
// IndexBufferの生成・アップロード
private function _createIndexBuffer(data:Vector.<uint>):IndexBuffer3D
{
var indexBuffer:IndexBuffer3D, indexNum:uint;
indexNum = data.length/3;
indexBuffer = _context3D.createIndexBuffer(data.length);
indexBuffer.uploadFromVector(data, 0, data.length);
return indexBuffer;
}
// Matrix3D から定数のアップロード
private function _uploadVertexConstFromMatrix(i:uint, data:Matrix3D):void
{
_context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, i, data, true);
}
// Vector から定数のアップロード
private function _uploadVertexConstFromVector(i:uint, data:Vector.<Number>):void
{
_context3D.setProgramConstantsFromVector(Context3DProgramType.VERTEX, i, data, data.length/4);
}
}
}
import flash.geom.Matrix3D;
import flash.geom.Rectangle;
import flash.geom.Vector3D;
class Model
{
public static const PI2:Number = Math.PI*2;
public function get vertexList():Vector.<Number> { return _vertexList; }
private var _vertexList:Vector.<Number>;
public function get indexList():Vector.<uint> { return _indexList; }
private var _indexList:Vector.<uint>;
private var _radius:Number;
private var _segment:Number;
private var _segmentPlus:Number;
/**
* インスタンスを生成
*/
public function Model(radius:Number, segment:uint=4) {
_radius = radius;
_segment = (segment >= 4) ? segment : 4;
_segmentPlus = _segment + 1;
}
/**
* 頂点リストを生成
*/
public function color(rgb:uint=0xffffff):Model
{
var i:uint, angle:Number, fluctuation:Number, x:Number, y:Number, r:Number, g:Number, b:Number;
r = ((rgb >> 16) & 255) / 255;
g = ((rgb >> 8) & 255) / 255;
b = (rgb & 255) / 255;
_vertexList = new Vector.<Number>();
for (i = 0; i < _segmentPlus; i++) {
angle = i / _segment * PI2;
fluctuation = sin(i / 4 * PI2);
// 外側
x = _radius*cos(angle);
y = _radius*sin(angle);
_vertexList.push((1.05 + fluctuation * 0.01) * x, (1.05 + fluctuation * 0.01) * y, 0, 1, r, g, b, 0);
// 中心1
_vertexList.push(1.00*x, 1.00*y, 0, 0, r, g, b, 1);
// 中心2
_vertexList.push(0.98*x, 0.98*y, 0, 0, 1, 1, 1, 1);
// 内側
_vertexList.push(0.96*x, 0.96*y, 0, 0, 1, 1, 1, 0);
}
return this;
}
/**
* インデックスリストを生成
*/
public function createIndex():Model
{
var i:uint;
// インデックスリストにデータを追加
_indexList = new Vector.<uint>();
for (i=0;i<_segment;i++) {
_indexList.push(i*4, (i+1)*4, i*4+1);
_indexList.push(i*4+1, (i+1)*4, (i+1)*4+1);
_indexList.push(i*4+1, (i+1)*4+1, i*4+2);
_indexList.push(i*4+2, (i+1)*4+1, (i+1)*4+2);
_indexList.push(i*4+2, (i+1)*4+2, i*4+3);
_indexList.push(i*4+3, (i+1)*4+2, (i+1)*4+3);
}
return this;
}
public static function sin(v:Number):Number {
return Math.round(Math.sin(v) * 1000000000000000) / 1000000000000000;
}
public static function cos(v:Number):Number {
return Math.round(Math.cos(v) * 1000000000000000) / 1000000000000000;
}
}