Molehill Example: Spinning Cube
/** | |
* Spinning cube in Molehill. | |
* http://ltslashgt.com/2011/02/28/molehill-spinning-cube/ | |
*/ | |
package { | |
import com.adobe.utils.AGALMiniAssembler; | |
import flash.display.Sprite; | |
import flash.display.Stage3D; | |
import flash.display.StageAlign; | |
import flash.display.StageScaleMode; | |
import flash.display3D.Context3D; | |
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.Rectangle; | |
import flash.geom.Vector3D; | |
import flash.utils.getTimer; | |
public class Cube extends Sprite { | |
protected var _width:Number; | |
protected var _height:Number; | |
protected var _stage:Stage3D; | |
protected var _context:Context3D; | |
protected var _indexBuffer:IndexBuffer3D; | |
protected var _vertexBuffer:VertexBuffer3D; | |
protected var _program:Program3D; | |
protected var _modelView:Matrix3D; | |
protected var _modelViewProjection:Matrix3D; | |
protected var _projection:Matrix3D; | |
protected var _time:Number = 0; | |
protected var _deltaTime:Number = 0; | |
protected var _tweenTime:Number = 0; | |
protected var _tweenPitch:Number = 0; | |
protected var _tweenYaw:Number = 0; | |
protected var _pitch:Number = 0; | |
protected var _yaw:Number = 0; | |
// Vertexes for the cube. Format is (x, y, z, r, g, b), counter-clockwise | |
// winding, with normal OpenGL axes (-x/+x = left/right, -y/+y = bottom/top, | |
// -z/+z = far/near). This is also a bit overkill on vertexes, but the | |
// extras are needed to make each side a solid color. | |
protected var _cubeVertexes:Vector.<Number> = Vector.<Number>([ | |
// near face | |
-1.0, -1.0, 1.0, 1.0, 0.0, 0.0, | |
-1.0, 1.0, 1.0, 1.0, 0.0, 0.0, | |
1.0, 1.0, 1.0, 1.0, 0.0, 0.0, | |
1.0, -1.0, 1.0, 1.0, 0.0, 0.0, | |
// left face | |
-1.0, -1.0, -1.0, 0.0, 1.0, 0.0, | |
-1.0, 1.0, -1.0, 0.0, 1.0, 0.0, | |
-1.0, 1.0, 1.0, 0.0, 1.0, 0.0, | |
-1.0, -1.0, 1.0, 0.0, 1.0, 0.0, | |
// far face | |
1.0, -1.0, -1.0, 0.0, 0.0, 1.0, | |
1.0, 1.0, -1.0, 0.0, 0.0, 1.0, | |
-1.0, 1.0, -1.0, 0.0, 0.0, 1.0, | |
-1.0, -1.0, -1.0, 0.0, 0.0, 1.0, | |
// right face | |
1.0, -1.0, 1.0, 1.0, 1.0, 0.0, | |
1.0, 1.0, 1.0, 1.0, 1.0, 0.0, | |
1.0, 1.0, -1.0, 1.0, 1.0, 0.0, | |
1.0, -1.0, -1.0, 1.0, 1.0, 0.0, | |
// top face | |
-1.0, 1.0, 1.0, 1.0, 0.0, 1.0, | |
-1.0, 1.0, -1.0, 1.0, 0.0, 1.0, | |
1.0, 1.0, -1.0, 1.0, 0.0, 1.0, | |
1.0, 1.0, 1.0, 1.0, 0.0, 1.0, | |
// bottom face | |
-1.0, -1.0, -1.0, 0.0, 1.0, 1.0, | |
-1.0, -1.0, 1.0, 0.0, 1.0, 1.0, | |
1.0, -1.0, 1.0, 0.0, 1.0, 1.0, | |
1.0, -1.0, -1.0, 0.0, 1.0, 1.0, | |
]); | |
// Indexes into the vertex buffer above for each of the cube's triangles. | |
protected var _cubeIndexes:Vector.<uint> = Vector.<uint>([ | |
0, 1, 2, | |
0, 2, 3, | |
4, 5, 6, | |
4, 6, 7, | |
8, 9, 10, | |
8, 10, 11, | |
12, 13, 14, | |
12, 14, 15, | |
16, 17, 18, | |
16, 18, 19, | |
20, 21, 22, | |
20, 22, 23 | |
]); | |
protected var _vertexShader:String = [ | |
"m44 op, va0, vc0", // multiply vertex by modelViewProjection | |
"mov v0, va1" // copy the vertex color | |
].join("\n"); | |
protected var _fragmentShader:String = [ | |
"mov oc, v0" // output the fragment color | |
].join("\n"); | |
function Cube() { | |
addEventListener(Event.ADDED_TO_STAGE, onAddedToStage); | |
} | |
protected function onAddedToStage(event:Event):void { | |
stage.align = StageAlign.TOP_LEFT; | |
stage.scaleMode = StageScaleMode.NO_SCALE; | |
_width = stage.stageWidth; | |
_height = stage.stageHeight; | |
_stage = stage.stage3Ds[0]; | |
_stage.addEventListener(Event.CONTEXT3D_CREATE, onContext); | |
_stage.requestContext3D(Context3DRenderMode.AUTO); | |
_stage.viewPort = new Rectangle(0, 0, _width, _height); | |
} | |
// Called once the context we requested has been created. | |
protected function onContext(event:Event):void { | |
_context = _stage.context3D; | |
_context.configureBackBuffer(_width, _height, 2, true); | |
_context.enableErrorChecking = true; | |
// Discard triangles pointing away from the camera, and ones | |
// behind things that we've already drawn. | |
_context.setCulling(Context3DTriangleFace.BACK); | |
_context.setDepthTest(true, Context3DCompareMode.LESS_EQUAL); | |
// Setup our initial matrices. | |
_modelView = new Matrix3D; | |
_modelViewProjection = new Matrix3D; | |
_projection = perspectiveProjection(60, _width / _height, 0.1, 2048); | |
// Create a program from the two shaders. | |
var vsAssembler:AGALMiniAssembler = new AGALMiniAssembler; | |
vsAssembler.assemble(Context3DProgramType.VERTEX, _vertexShader); | |
var fsAssembler:AGALMiniAssembler = new AGALMiniAssembler; | |
fsAssembler.assemble(Context3DProgramType.FRAGMENT, _fragmentShader); | |
_program = _context.createProgram(); | |
_program.upload(vsAssembler.agalcode, fsAssembler.agalcode); | |
_context.setProgram(_program); | |
// Upload all the vertex data and create two streams. Position data | |
// will be available to the vertex program as `va0`, color data as `va1`. | |
_vertexBuffer = _context.createVertexBuffer(_cubeVertexes.length / 6, 6); | |
_vertexBuffer.uploadFromVector(_cubeVertexes, 0, _cubeVertexes.length / 6); | |
_context.setVertexBufferAt(0, _vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_3); | |
_context.setVertexBufferAt(1, _vertexBuffer, 3, Context3DVertexBufferFormat.FLOAT_3); | |
// Upload the index data. This is used by drawTriangles (below). | |
_indexBuffer = _context.createIndexBuffer(_cubeIndexes.length); | |
_indexBuffer.uploadFromVector(_cubeIndexes, 0, _cubeIndexes.length); | |
_time = getTimer() / 1000.0; | |
_tweenTime = _time + 1; | |
addEventListener(Event.ENTER_FRAME, onEnterFrame); | |
} | |
protected function onEnterFrame(event:Event):void { | |
_context.clear(0, 0, 0); | |
var newTime:Number = getTimer() / 1000.0; | |
_deltaTime = Math.min(newTime - _time, 0.1); | |
_time = newTime; | |
updateRotation(); | |
// Update the modelView matrix given the current rotation | |
_modelView.identity(); | |
_modelView.appendRotation(_tweenPitch, Vector3D.X_AXIS); | |
_modelView.appendRotation(_tweenYaw, Vector3D.Y_AXIS); | |
_modelView.appendTranslation(0, 0, -4); | |
// Concatenate the modelView and projection matrices and set it as | |
// a vertex program constant. It will be available as `vc0`. | |
_modelViewProjection.identity(); | |
_modelViewProjection.append(_modelView); | |
_modelViewProjection.append(_projection); | |
_context.setProgramConstantsFromMatrix( | |
Context3DProgramType.VERTEX, 0, _modelViewProjection, true); | |
// Tell the GPU to draw all the triangles in indexBuffer. You can also | |
// specify an offset and limit within the index buffer, if desired. | |
_context.drawTriangles(_indexBuffer); | |
_context.present(); | |
} | |
protected function perspectiveProjection(fov:Number=90, | |
aspect:Number=1, near:Number=1, far:Number=2048):Matrix3D { | |
var y2:Number = near * Math.tan(fov * Math.PI / 360); | |
var y1:Number = -y2; | |
var x1:Number = y1 * aspect; | |
var x2:Number = y2 * aspect; | |
var a:Number = 2 * near / (x2 - x1); | |
var b:Number = 2 * near / (y2 - y1); | |
var c:Number = (x2 + x1) / (x2 - x1); | |
var d:Number = (y2 + y1) / (y2 - y1); | |
var q:Number = -(far + near) / (far - near); | |
var qn:Number = -2 * (far * near) / (far - near); | |
return new Matrix3D(Vector.<Number>([ | |
a, 0, 0, 0, | |
0, b, 0, 0, | |
c, d, q, -1, | |
0, 0, qn, 0 | |
])); | |
} | |
protected function updateRotation():void { | |
while (_tweenTime < _time) { | |
_tweenTime += 1; | |
_pitch = (_pitch + 60) % 360; | |
_yaw = (_yaw + 40) % 360; | |
} | |
var factor:Number = _tweenTime - _time; | |
if (factor < 0.0) factor = 0.0; | |
else if (factor > 1.0) factor = 1.0; | |
factor = 1.0 - Math.pow(factor, 4); | |
_tweenPitch = _pitch + (60 * factor); | |
_tweenYaw = _yaw + (40 * factor); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment