Created
February 28, 2011 08:59
-
-
Save noonat/847106 to your computer and use it in GitHub Desktop.
Molehill Example: Spinning Cube
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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