Skip to content

Instantly share code, notes, and snippets.

@noonat
Created February 28, 2011 08:59
Show Gist options
  • Save noonat/847106 to your computer and use it in GitHub Desktop.
Save noonat/847106 to your computer and use it in GitHub Desktop.
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