/src/Stage3dGame5.as
ActionScript | 543 lines | 364 code | 60 blank | 119 comment | 23 complexity | 6969721079e752bfd7781139dca6816e MD5 | raw file
1//
2// Stage3D Game Template - Chapter Five
3//
4package
5{
6[SWF(width="640", height="480", frameRate="60",
7backgroundColor="#FFFFFF")]
8
9import com.adobe.utils.*;
10import flash.display.*;
11import flash.display3D.*;
12import flash.display3D.textures.*;
13import flash.events.*;
14import flash.geom.*;
15import flash.utils.*;
16import flash.text.*;
17
18public class Stage3dGame5 extends Sprite
19{
20// used by the GUI
21private var fpsLast:uint = getTimer();
22private var fpsTicks:uint = 0;
23private var fpsTf:TextField;
24private var scoreTf:TextField;
25private var score:uint = 0;
26
27// constants used during inits
28private const swfWidth:int = 640;
29private const swfHeight:int = 480;
30// for this demo, ensure ALL textures are 512x512
31private const textureSize:int = 512;
32
33// the 3d graphics window on the stage
34private var context3D:Context3D;
35// the compiled shaders used to render our mesh
36private var shaderProgram1:Program3D;
37private var shaderProgram2:Program3D;
38private var shaderProgram3:Program3D;
39private var shaderProgram4:Program3D;
40
41// matrices that affect the mesh location and camera angles
42private var projectionmatrix:PerspectiveMatrix3D =
43 new PerspectiveMatrix3D();
44private var modelmatrix:Matrix3D = new Matrix3D();
45private var viewmatrix:Matrix3D = new Matrix3D();
46private var terrainviewmatrix:Matrix3D = new Matrix3D();
47private var modelViewProjection:Matrix3D = new Matrix3D();
48
49// a simple frame counter used for animation
50private var t:Number = 0;
51// a reusable loop counter
52private var looptemp:int = 0;
53
54/* TEXTURES: Pure AS3 and Flex version:
55 * if you are using Adobe Flash CS5
56 * comment out the following: */
57[Embed (source = "art/spaceship_texture.jpg")]
58private var myTextureBitmap:Class;
59private var myTextureData:Bitmap = new myTextureBitmap();
60[Embed (source = "art/terrain_texture.jpg")]
61private var terrainTextureBitmap:Class;
62private var terrainTextureData:Bitmap = new terrainTextureBitmap();
63
64/* TEXTURE: Flash CS5 version:
65 * add the jpgs to your library (F11)
66 * right click and edit the advanced properties
67 * so it is exported for use in Actionscript
68 * and call them myTextureBitmap and terrainTextureBitmap
69 * if you are using Flex/FlashBuilder/FlashDevelop/FDT
70 * comment out the following: */
71//private var myBitmapDataObject:myTextureBitmapData =
72// new myTextureBitmapData(textureSize, textureSize);
73//private var myTextureData:Bitmap =
74// new Bitmap(myBitmapDataObject);
75//private var terrainBitmapDataObject:terrainTextureBitmapData =
76// new terrainTextureBitmapData(textureSize, textureSize);
77//private var terrainTextureData:Bitmap =
78// new Bitmap(terrainBitmapDataObject);
79
80// The Stage3d Texture that uses the above myTextureData
81private var myTexture:Texture;
82private var terrainTexture:Texture;
83
84// The spaceship mesh data
85[Embed (source = "art/spaceship.obj",
86 mimeType = "application/octet-stream")]
87private var myObjData:Class;
88private var myMesh:Stage3dObjParser;
89
90// The terrain mesh data
91[Embed (source = "art/terrain.obj",
92 mimeType = "application/octet-stream")]
93private var terrainObjData:Class;
94private var terrainMesh:Stage3dObjParser;
95
96public function Stage3dGame5()
97{
98 if (stage != null)
99 init();
100 else
101 addEventListener(Event.ADDED_TO_STAGE, init);
102}
103
104private function init(e:Event = null):void
105{
106 if (hasEventListener(Event.ADDED_TO_STAGE))
107 removeEventListener(Event.ADDED_TO_STAGE, init);
108
109 stage.scaleMode = StageScaleMode.NO_SCALE;
110 stage.align = StageAlign.TOP_LEFT;
111
112 // add some text labels
113 initGUI();
114
115 // and request a context3D from Stage3d
116 stage.stage3Ds[0].addEventListener(
117 Event.CONTEXT3D_CREATE, onContext3DCreate);
118 stage.stage3Ds[0].requestContext3D();
119}
120
121private function updateScore():void
122{
123 // for now, you earn points over time
124 score++;
125 // padded with zeroes
126 if (score < 10) scoreTf.text = 'Score: 00000' + score;
127 else if (score < 100) scoreTf.text = 'Score: 0000' + score;
128 else if (score < 1000) scoreTf.text = 'Score: 000' + score;
129 else if (score < 10000) scoreTf.text = 'Score: 00' + score;
130 else if (score < 100000) scoreTf.text = 'Score: 0' + score;
131 else scoreTf.text = 'Score: ' + score;
132}
133
134private function initGUI():void
135{
136 // a text format descriptor used by all gui labels
137 var myFormat:TextFormat = new TextFormat();
138 myFormat.color = 0xFFFFFF;
139 myFormat.size = 13;
140
141 // create an FPSCounter that displays the framerate on screen
142 fpsTf = new TextField();
143 fpsTf.x = 0;
144 fpsTf.y = 0;
145 fpsTf.selectable = false;
146 fpsTf.autoSize = TextFieldAutoSize.LEFT;
147 fpsTf.defaultTextFormat = myFormat;
148 fpsTf.text = "Initializing Stage3d...";
149 addChild(fpsTf);
150
151 // create a score display
152 scoreTf = new TextField();
153 scoreTf.x = 560;
154 scoreTf.y = 0;
155 scoreTf.selectable = false;
156 scoreTf.autoSize = TextFieldAutoSize.LEFT;
157 scoreTf.defaultTextFormat = myFormat;
158 scoreTf.text = "000000";
159 addChild(scoreTf);
160
161 // add some labels to describe each shader
162 var label1:TextField = new TextField();
163 label1.x = 100;
164 label1.y = 180;
165 label1.selectable = false;
166 label1.autoSize = TextFieldAutoSize.LEFT;
167 label1.defaultTextFormat = myFormat;
168 label1.text = "Shader 1: Textured";
169 addChild(label1);
170
171 var label2:TextField = new TextField();
172 label2.x = 400;
173 label2.y = 180;
174 label2.selectable = false;
175 label2.autoSize = TextFieldAutoSize.LEFT;
176 label2.defaultTextFormat = myFormat;
177 label2.text = "Shader 2: Vertex RGB";
178 addChild(label2);
179
180 var label3:TextField = new TextField();
181 label3.x = 80;
182 label3.y = 440;
183 label3.selectable = false;
184 label3.autoSize = TextFieldAutoSize.LEFT;
185 label3.defaultTextFormat = myFormat;
186 label3.text = "Shader 3: Vertex RGB + Textured";
187 addChild(label3);
188
189 var label4:TextField = new TextField();
190 label4.x = 340;
191 label4.y = 440;
192 label4.selectable = false;
193 label4.autoSize = TextFieldAutoSize.LEFT;
194 label4.defaultTextFormat = myFormat;
195 label4.text = "Shader 4: Textured + setProgramConstants";
196 addChild(label4);
197}
198
199public function uploadTextureWithMipmaps(
200 dest:Texture, src:BitmapData):void
201{
202 var ws:int = src.width;
203 var hs:int = src.height;
204 var level:int = 0;
205 var tmp:BitmapData;
206 var transform:Matrix = new Matrix();
207 var tmp2:BitmapData;
208
209 tmp = new BitmapData( src.width, src.height, true, 0x00000000);
210
211 while ( ws >= 1 && hs >= 1 )
212 {
213 tmp.draw(src, transform, null, null, null, true);
214 dest.uploadFromBitmapData(tmp, level);
215 transform.scale(0.5, 0.5);
216 level++;
217 ws >>= 1;
218 hs >>= 1;
219 if (hs && ws)
220 {
221 tmp.dispose();
222 tmp = new BitmapData(ws, hs, true, 0x00000000);
223 }
224 }
225 tmp.dispose();
226}
227
228private function onContext3DCreate(event:Event):void
229{
230 // Remove existing frame handler. Note that a context
231 // loss can occur at any time which will force you
232 // to recreate all objects we create here.
233 // A context loss occurs for instance if you hit
234 // CTRL-ALT-DELETE on Windows.
235 // It takes a while before a new context is available
236 // hence removing the enterFrame handler is important!
237
238 if (hasEventListener(Event.ENTER_FRAME))
239 removeEventListener(Event.ENTER_FRAME,enterFrame);
240
241 // Obtain the current context
242 var t:Stage3D = event.target as Stage3D;
243 context3D = t.context3D;
244
245 if (context3D == null)
246 {
247 // Currently no 3d context is available (error!)
248 return;
249 }
250
251 // Disabling error checking will drastically improve performance.
252 // If set to true, Flash sends helpful error messages regarding
253 // AGAL compilation errors, uninitialized program constants, etc.
254 context3D.enableErrorChecking = true;
255
256 // Initialize our mesh data
257 initData();
258
259 // The 3d back buffer size is in pixels (2=antialiased)
260 context3D.configureBackBuffer(swfWidth, swfHeight, 2, true);
261
262 // assemble all the shaders we need
263 initShaders();
264
265 myTexture = context3D.createTexture(
266 textureSize, textureSize,
267 Context3DTextureFormat.BGRA, false);
268 uploadTextureWithMipmaps(
269 myTexture, myTextureData.bitmapData);
270
271 terrainTexture = context3D.createTexture(
272 textureSize, textureSize,
273 Context3DTextureFormat.BGRA, false);
274 uploadTextureWithMipmaps(
275 terrainTexture, terrainTextureData.bitmapData);
276
277 // create projection matrix for our 3D scene
278 projectionmatrix.identity();
279 // 45 degrees FOV, 640/480 aspect ratio, 0.1=near, 100=far
280 projectionmatrix.perspectiveFieldOfViewRH(
281 45.0, swfWidth / swfHeight, 0.01, 5000.0);
282
283 // create a matrix that defines the camera location
284 viewmatrix.identity();
285 // move the camera back a little so we can see the mesh
286 viewmatrix.appendTranslation(0,0,-3);
287
288 // tilt the terrain a little so it is coming towards us
289 terrainviewmatrix.identity();
290 terrainviewmatrix.appendRotation(-60,Vector3D.X_AXIS);
291
292 // start the render loop!
293 addEventListener(Event.ENTER_FRAME,enterFrame);
294}
295
296// create four different shaders
297private function initShaders():void
298{
299 // A simple vertex shader which does a 3D transformation
300 // for simplicity, it is used by all four shaders
301 var vertexShaderAssembler:AGALMiniAssembler =
302 new AGALMiniAssembler();
303 vertexShaderAssembler.assemble
304 (
305 Context3DProgramType.VERTEX,
306 // 4x4 matrix multiply to get camera angle
307 "m44 op, va0, vc0\n" +
308 // tell fragment shader about XYZ
309 "mov v0, va0\n" +
310 // tell fragment shader about UV
311 "mov v1, va1\n" +
312 // tell fragment shader about RGBA
313 "mov v2, va2\n"
314 );
315
316 // textured using UV coordinates
317 var fragmentShaderAssembler1:AGALMiniAssembler
318 = new AGALMiniAssembler();
319 fragmentShaderAssembler1.assemble
320 (
321 Context3DProgramType.FRAGMENT,
322 // grab the texture color from texture 0
323 // and uv coordinates from varying register 1
324 // and store the interpolated value in ft0
325 "tex ft0, v1, fs0 <2d,linear,repeat,miplinear>\n"+
326 // move this value to the output color
327 "mov oc, ft0\n"
328 );
329
330 // no texture, RGBA from the vertex buffer data
331 var fragmentShaderAssembler2:AGALMiniAssembler
332 = new AGALMiniAssembler();
333 fragmentShaderAssembler2.assemble
334 (
335 Context3DProgramType.FRAGMENT,
336 // grab the color from the v2 register
337 // which was set in the vertex program
338 "sub ft0, v2, fc1\n" +
339 "mov oc, v2\n"
340 );
341
342 // textured using UV coordinates AND colored by vertex RGB
343 var fragmentShaderAssembler3:AGALMiniAssembler
344 = new AGALMiniAssembler();
345 fragmentShaderAssembler3.assemble
346 (
347 Context3DProgramType.FRAGMENT,
348 // grab the texture color from texture 0
349 // and uv coordinates from varying register 1
350 "tex ft0, v1, fs0 <2d,linear,repeat,miplinear>\n" +
351 // multiply by the value stored in v2 (the vertex rgb)
352 "mul ft1, v2, ft0\n" +
353 // move this value to the output color
354 "mov oc, ft1\n"
355 );
356
357 // textured using UV coordinates and
358 // tinted using a fragment constant
359 var fragmentShaderAssembler4:AGALMiniAssembler
360 = new AGALMiniAssembler();
361 fragmentShaderAssembler4.assemble
362 (
363 Context3DProgramType.FRAGMENT,
364 // grab the texture color from texture 0
365 // and uv coordinates from varying register 1
366 "tex ft0, v1, fs0 <2d,linear,repeat,miplinear>\n" +
367 // multiply by the value stored in fc0
368 "mul ft1, fc0, ft0\n" +
369 // move this value to the output color
370 "mov oc, ft1\n"
371 );
372
373 // combine shaders into a program which we then upload to the GPU
374 shaderProgram1 = context3D.createProgram();
375 shaderProgram1.upload(
376 vertexShaderAssembler.agalcode,
377 fragmentShaderAssembler1.agalcode);
378
379 shaderProgram2 = context3D.createProgram();
380 shaderProgram2.upload(
381 vertexShaderAssembler.agalcode,
382 fragmentShaderAssembler2.agalcode);
383
384 shaderProgram3 = context3D.createProgram();
385 shaderProgram3.upload(
386 vertexShaderAssembler.agalcode,
387 fragmentShaderAssembler3.agalcode);
388
389 shaderProgram4 = context3D.createProgram();
390 shaderProgram4.upload(
391 vertexShaderAssembler.agalcode,
392 fragmentShaderAssembler4.agalcode);
393}
394
395private function initData():void
396{
397 // parse the OBJ file and create buffers
398 myMesh = new Stage3dObjParser(
399 myObjData, context3D, 1, true, true);
400 // parse the terrain mesh as well
401 terrainMesh = new Stage3dObjParser(
402 terrainObjData, context3D, 1, true, true);
403}
404
405private function renderTerrain():void
406{
407 context3D.setTextureAt(0, terrainTexture);
408 // simple textured shader
409 context3D.setProgram ( shaderProgram1 );
410 // position
411 context3D.setVertexBufferAt(0, terrainMesh.positionsBuffer,
412 0, Context3DVertexBufferFormat.FLOAT_3);
413 // tex coord
414 context3D.setVertexBufferAt(1, terrainMesh.uvBuffer,
415 0, Context3DVertexBufferFormat.FLOAT_2);
416 // vertex rgba
417 context3D.setVertexBufferAt(2, terrainMesh.colorsBuffer,
418 0, Context3DVertexBufferFormat.FLOAT_4);
419 // set up camera angle
420 modelmatrix.identity();
421 // make the terrain face the right way
422 modelmatrix.appendRotation( -90, Vector3D.Y_AXIS);
423 // slowly move the terrain around
424 modelmatrix.appendTranslation(
425 Math.cos(t/300)*1000,Math.cos(t/200)*1000 + 500,-130);
426 // clear the matrix and append new angles
427 modelViewProjection.identity();
428 modelViewProjection.append(modelmatrix);
429 modelViewProjection.append(terrainviewmatrix);
430 modelViewProjection.append(projectionmatrix);
431 // pass our matrix data to the shader program
432 context3D.setProgramConstantsFromMatrix(
433 Context3DProgramType.VERTEX,
434 0, modelViewProjection, true );
435 context3D.drawTriangles(terrainMesh.indexBuffer,
436 0, terrainMesh.indexBufferCount);
437}
438
439private function enterFrame(e:Event):void
440{
441 // clear scene before rendering is mandatory
442 context3D.clear(0,0,0);
443 // move or rotate more each frame
444 t += 2.0;
445 // scroll and render the terrain once
446 renderTerrain();
447 // how far apart each of the 4 spaceships is
448 var dist:Number = 0.8;
449 // loop through each mesh we want to draw
450 for (looptemp = 0; looptemp < 4; looptemp++)
451 {
452 // clear the transformation matrix to 0,0,0
453 modelmatrix.identity();
454 // each mesh has a different texture,
455 // shader, position and spin speed
456 switch(looptemp)
457 {
458 case 0:
459 context3D.setTextureAt(0, myTexture);
460 context3D.setProgram ( shaderProgram1 );
461 modelmatrix.appendRotation(t*0.7, Vector3D.Y_AXIS);
462 modelmatrix.appendRotation(t*0.6, Vector3D.X_AXIS);
463 modelmatrix.appendRotation(t*1.0, Vector3D.Y_AXIS);
464 modelmatrix.appendTranslation(-dist, dist, 0);
465 break;
466 case 1:
467 context3D.setTextureAt(0, null);
468 context3D.setProgram ( shaderProgram2 );
469 modelmatrix.appendRotation(t*-0.2, Vector3D.Y_AXIS);
470 modelmatrix.appendRotation(t*0.4, Vector3D.X_AXIS);
471 modelmatrix.appendRotation(t*0.7, Vector3D.Y_AXIS);
472 modelmatrix.appendTranslation(dist, dist, 0);
473 break;
474 case 2:
475 context3D.setTextureAt(0, myTexture);
476 context3D.setProgram ( shaderProgram3 );
477 modelmatrix.appendRotation(t*1.0, Vector3D.Y_AXIS);
478 modelmatrix.appendRotation(t*-0.2, Vector3D.X_AXIS);
479 modelmatrix.appendRotation(t*0.3, Vector3D.Y_AXIS);
480 modelmatrix.appendTranslation(-dist, -dist, 0);
481 break;
482 case 3:
483 context3D.setProgramConstantsFromVector(
484 Context3DProgramType.FRAGMENT, 0, Vector.<Number>
485 ([ 1, Math.abs(Math.cos(t/50)), 0, 1 ]) );
486 context3D.setTextureAt(0, myTexture);
487 context3D.setProgram ( shaderProgram4 );
488 modelmatrix.appendRotation(t*0.3, Vector3D.Y_AXIS);
489 modelmatrix.appendRotation(t*0.3, Vector3D.X_AXIS);
490 modelmatrix.appendRotation(t*-0.3, Vector3D.Y_AXIS);
491 modelmatrix.appendTranslation(dist, -dist, 0);
492 break;
493 }
494
495 // clear the matrix and append new angles
496 modelViewProjection.identity();
497 modelViewProjection.append(modelmatrix);
498 modelViewProjection.append(viewmatrix);
499 modelViewProjection.append(projectionmatrix);
500
501 // pass our matrix data to the shader program
502 context3D.setProgramConstantsFromMatrix(
503 Context3DProgramType.VERTEX,
504 0, modelViewProjection, true );
505
506 // draw a spaceship mesh
507 // position
508 context3D.setVertexBufferAt(0, myMesh.positionsBuffer,
509 0, Context3DVertexBufferFormat.FLOAT_3);
510 // tex coord
511 context3D.setVertexBufferAt(1, myMesh.uvBuffer,
512 0, Context3DVertexBufferFormat.FLOAT_2);
513 // vertex rgba
514 context3D.setVertexBufferAt(2, myMesh.colorsBuffer,
515 0, Context3DVertexBufferFormat.FLOAT_4);
516 // render it
517 context3D.drawTriangles(myMesh.indexBuffer,
518 0, myMesh.indexBufferCount);
519 }
520
521 // present/flip back buffer
522 // now that all meshes have been drawn
523 context3D.present();
524
525 // update the FPS display
526 fpsTicks++;
527 var now:uint = getTimer();
528 var delta:uint = now - fpsLast;
529 // only update the display once a second
530 if (delta >= 1000)
531 {
532 var fps:Number = fpsTicks / delta * 1000;
533 fpsTf.text = fps.toFixed(1) + " fps";
534 fpsTicks = 0;
535 fpsLast = now;
536 }
537
538 // update the rest of the GUI
539 updateScore();
540}
541
542} // end of class
543} // end of package