PageRenderTime 47ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/starling/core/Starling.as

http://citrus-engine.googlecode.com/
ActionScript | 524 lines | 326 code | 80 blank | 118 comment | 42 complexity | 816679657c5756a2112b0f59741f2d2f MD5 | raw file
  1. // =================================================================================================
  2. //
  3. // Starling Framework
  4. // Copyright 2011 Gamua OG. All Rights Reserved.
  5. //
  6. // This program is free software. You can redistribute and/or modify it
  7. // in accordance with the terms of the accompanying license agreement.
  8. //
  9. // =================================================================================================
  10. package starling.core
  11. {
  12. import flash.display.Sprite;
  13. import flash.display.Stage3D;
  14. import flash.display3D.Context3D;
  15. import flash.display3D.Program3D;
  16. import flash.events.ErrorEvent;
  17. import flash.events.Event;
  18. import flash.events.KeyboardEvent;
  19. import flash.events.MouseEvent;
  20. import flash.events.TouchEvent;
  21. import flash.geom.Point;
  22. import flash.geom.Rectangle;
  23. import flash.text.TextField;
  24. import flash.text.TextFieldAutoSize;
  25. import flash.text.TextFormat;
  26. import flash.text.TextFormatAlign;
  27. import flash.ui.Multitouch;
  28. import flash.ui.MultitouchInputMode;
  29. import flash.utils.ByteArray;
  30. import flash.utils.Dictionary;
  31. import flash.utils.getTimer;
  32. import starling.animation.Juggler;
  33. import starling.display.DisplayObject;
  34. import starling.display.Stage;
  35. import starling.events.ResizeEvent;
  36. import starling.events.TouchPhase;
  37. import starling.events.TouchProcessor;
  38. /** The Starling class represents the core of the Starling framework.
  39. *
  40. * <p>The Starling framework makes it possible to create 2D applications and games that make
  41. * use of the Stage3D architecture introduced in Flash Player 11. It implements a display tree
  42. * system that is very similar to that of conventional Flash, while leveraging modern GPUs
  43. * to speed up rendering.</p>
  44. *
  45. * <p>The Starling class represents the link between the conventional Flash display tree and
  46. * the Starling display tree. To create a Starling-powered application, you have to create
  47. * an instance of the Starling class:</p>
  48. *
  49. * <pre>var starling:Starling = new Starling(Game, stage);</pre>
  50. *
  51. * <p>The first parameter has to be a Starling display object class, e.g. a subclass of
  52. * <code>starling.display.Sprite</code>. In the sample above, the class "Game" is the
  53. * application root. An instance of "Game" will be created as soon as Starling is initialized.
  54. * The second parameter is the conventional (Flash) stage object. Per default, Starling will
  55. * display its contents directly below the stage.</p>
  56. *
  57. * <p>It is recommended to store the starling instance as a member variable, to make sure
  58. * that the Garbage Collector does not destroy it. After creating the Starling object, you
  59. * have to start it up like this:</p>
  60. *
  61. * <pre>starling.start();</pre>
  62. *
  63. * <p>It will now render the contents of the "Game" class in the frame rate that is set up for
  64. * the application (as defined in the Flash stage).</p>
  65. *
  66. * <strong>Accessing the Starling object</strong>
  67. *
  68. * <p>From within your application, you can access the current Starling object anytime
  69. * through the static method <code>Starling.current</code>. It will return the active Starling
  70. * instance (most applications will only have one Starling object, anyway).</p>
  71. *
  72. * <strong>Viewport</strong>
  73. *
  74. * <p>The area the Starling content is rendered into is, per default, the complete size of the
  75. * stage. You can, however, use the "viewPort" property to change it. This can be useful
  76. * when you want to render only into a part of the screen, or if the player size changes. For
  77. * the latter, you can listen to the RESIZE-event dispatched by the Starling
  78. * stage.</p>
  79. *
  80. * <strong>Native overlay</strong>
  81. *
  82. * <p>Sometimes you will want to display native Flash content on top of Starling. That's what the
  83. * <code>nativeOverlay</code> property is for. It returns a Flash Sprite lying directly
  84. * on top of the Starling content. You can add conventional Flash objects to that overlay.</p>
  85. *
  86. * <p>Beware, though, that conventional Flash content on top of 3D content can lead to
  87. * performance penalties on some (mobile) platforms. For that reason, always remove all child
  88. * objects from the overlay when you don't need them any longer. Starling will remove the
  89. * overlay from the display list when it's empty.</p>
  90. *
  91. * <strong>Multitouch</strong>
  92. *
  93. * <p>Starling supports multitouch input on devices that provide it. During development,
  94. * where most of us are working with a conventional mouse and keyboard, Starling can simulate
  95. * multitouch events with the help of the "Shift" and "Ctrl" (Mac: "Cmd") keys. Activate
  96. * this feature by enabling the <code>simulateMultitouch</code> property.</p>
  97. *
  98. */
  99. public class Starling
  100. {
  101. /** The version of the Starling framework. */
  102. public static const VERSION:String = "0.9.1";
  103. // members
  104. private var mStage3D:Stage3D;
  105. private var mStage:Stage; // starling.display.stage!
  106. private var mRootClass:Class;
  107. private var mJuggler:Juggler;
  108. private var mStarted:Boolean;
  109. private var mSupport:RenderSupport;
  110. private var mTouchProcessor:TouchProcessor;
  111. private var mAntiAliasing:int;
  112. private var mSimulateMultitouch:Boolean;
  113. private var mEnableErrorChecking:Boolean;
  114. private var mLastFrameTimestamp:Number;
  115. private var mViewPort:Rectangle;
  116. private var mNativeStage:flash.display.Stage;
  117. private var mNativeOverlay:flash.display.Sprite;
  118. private var mContext:Context3D;
  119. private var mPrograms:Dictionary;
  120. private static var sCurrent:Starling;
  121. // construction
  122. /** Creates a new Starling instance.
  123. * @param rootClass A subclass of a Starling display object. It will be created as soon as
  124. * initialization is finished and will become the first child of the
  125. * Starling stage.
  126. * @param stage The Flash (2D) stage.
  127. * @param viewPort A rectangle describing the area into which the content will be
  128. * rendered. @default stage size
  129. * @param stage3D The Stage3D object into which the content will be rendered.
  130. * @default the first available Stage3D.
  131. * @param renderMode Use this parameter to force "software" rendering.
  132. */
  133. public function Starling(rootClass:Class, stage:flash.display.Stage,
  134. viewPort:Rectangle=null, stage3D:Stage3D=null,
  135. renderMode:String="auto")
  136. {
  137. if (stage == null) throw new ArgumentError("Stage must not be null");
  138. if (rootClass == null) throw new ArgumentError("Root class must not be null");
  139. if (viewPort == null) viewPort = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight);
  140. if (stage3D == null) stage3D = stage.stage3Ds[0];
  141. mRootClass = rootClass;
  142. mViewPort = viewPort;
  143. mStage3D = stage3D;
  144. mStage = new Stage(viewPort.width, viewPort.height, stage.color);
  145. mNativeStage = stage;
  146. mTouchProcessor = new TouchProcessor(mStage);
  147. mJuggler = new Juggler();
  148. mAntiAliasing = 0;
  149. mSimulateMultitouch = false;
  150. mEnableErrorChecking = false;
  151. mLastFrameTimestamp = getTimer() / 1000.0;
  152. mPrograms = new Dictionary();
  153. if (sCurrent == null)
  154. makeCurrent();
  155. // register touch/mouse event handlers
  156. var touchEventTypes:Array = Multitouch.supportsTouchEvents ?
  157. [ TouchEvent.TOUCH_BEGIN, TouchEvent.TOUCH_MOVE, TouchEvent.TOUCH_END ] :
  158. [ MouseEvent.MOUSE_DOWN, MouseEvent.MOUSE_MOVE, MouseEvent.MOUSE_UP ];
  159. for each (var touchEventType:String in touchEventTypes)
  160. stage.addEventListener(touchEventType, onTouch, false, 0, true);
  161. // register other event handlers
  162. stage.addEventListener(Event.ENTER_FRAME, onEnterFrame, false, 0, true);
  163. stage.addEventListener(KeyboardEvent.KEY_DOWN, onKey, false, 0, true);
  164. stage.addEventListener(KeyboardEvent.KEY_UP, onKey, false, 0, true);
  165. stage.addEventListener(Event.RESIZE, onResize, false, 0, true);
  166. mStage3D.addEventListener(Event.CONTEXT3D_CREATE, onContextCreated, false, 0, true);
  167. mStage3D.addEventListener(ErrorEvent.ERROR, onStage3DError, false, 0, true);
  168. try { mStage3D.requestContext3D(renderMode); }
  169. catch (e:Error) { showFatalError("Context3D error: " + e.message); }
  170. }
  171. /** Disposes Shader programs and render context. */
  172. public function dispose():void
  173. {
  174. for each (var program:Program3D in mPrograms)
  175. program.dispose();
  176. if (mContext) mContext.dispose();
  177. if (mTouchProcessor) mTouchProcessor.dispose();
  178. if (mSupport) mSupport.dispose();
  179. }
  180. // functions
  181. private function initializeGraphicsAPI():void
  182. {
  183. if (mContext) return;
  184. mContext = mStage3D.context3D;
  185. mContext.enableErrorChecking = mEnableErrorChecking;
  186. updateViewPort();
  187. mSupport = new RenderSupport();
  188. trace("[Starling] Initialization complete.");
  189. trace("[Starling] Display Driver:" + mContext.driverInfo);
  190. }
  191. private function initializeRoot():void
  192. {
  193. if (mStage.numChildren > 0) return;
  194. var rootObject:DisplayObject = new mRootClass();
  195. if (rootObject == null) throw new Error("Invalid root class: " + mRootClass);
  196. mStage.addChild(rootObject);
  197. }
  198. private function updateViewPort():void
  199. {
  200. if (mContext)
  201. mContext.configureBackBuffer(mViewPort.width, mViewPort.height, mAntiAliasing, false);
  202. mStage3D.x = mViewPort.x;
  203. mStage3D.y = mViewPort.y;
  204. }
  205. private function render():void
  206. {
  207. if (mContext == null) return;
  208. var now:Number = getTimer() / 1000.0;
  209. var passedTime:Number = now - mLastFrameTimestamp;
  210. mLastFrameTimestamp = now;
  211. mStage.advanceTime(passedTime);
  212. mJuggler.advanceTime(passedTime);
  213. mTouchProcessor.advanceTime(passedTime);
  214. mSupport.setOrthographicProjection(mStage.stageWidth, mStage.stageHeight);
  215. mSupport.clear(mStage.color, 1.0);
  216. mStage.render(mSupport, 1.0);
  217. mSupport.finishQuadBatch();
  218. mSupport.nextFrame();
  219. mContext.present();
  220. }
  221. private function updateNativeOverlay():void
  222. {
  223. mNativeOverlay.x = mViewPort.x;
  224. mNativeOverlay.y = mViewPort.y;
  225. mNativeOverlay.scaleX = mViewPort.width / mStage.stageWidth;
  226. mNativeOverlay.scaleY = mViewPort.height / mStage.stageHeight;
  227. // Having a native overlay on top of Stage3D content can cause a performance hit on
  228. // some environments. For that reason, we add it only to the stage while it's not empty.
  229. var numChildren:int = mNativeOverlay.numChildren;
  230. var parent:flash.display.DisplayObject = mNativeOverlay.parent;
  231. if (numChildren != 0 && parent == null)
  232. mNativeStage.addChild(mNativeOverlay);
  233. else if (numChildren == 0 && parent)
  234. mNativeStage.removeChild(mNativeOverlay);
  235. }
  236. private function showFatalError(message:String):void
  237. {
  238. var textField:TextField = new TextField();
  239. var textFormat:TextFormat = new TextFormat("Verdana", 12, 0xFFFFFF);
  240. textFormat.align = TextFormatAlign.CENTER;
  241. textField.defaultTextFormat = textFormat;
  242. textField.wordWrap = true;
  243. textField.width = mStage.stageWidth * 0.75;
  244. textField.autoSize = TextFieldAutoSize.CENTER;
  245. textField.text = message;
  246. textField.x = (mStage.stageWidth - textField.width) / 2;
  247. textField.y = (mStage.stageHeight - textField.height) / 2;
  248. textField.background = true;
  249. textField.backgroundColor = 0x440000;
  250. nativeOverlay.addChild(textField);
  251. }
  252. /** Make this Starling instance the <code>current</code> one. */
  253. public function makeCurrent():void
  254. {
  255. sCurrent = this;
  256. }
  257. /** Starts rendering and dispatching of <code>ENTER_FRAME</code> events. */
  258. public function start():void { mStarted = true; }
  259. /** Stops rendering. */
  260. public function stop():void { mStarted = false; }
  261. // event handlers
  262. private function onStage3DError(event:ErrorEvent):void
  263. {
  264. showFatalError("This application is not correctly embedded (wrong wmode value)");
  265. }
  266. private function onContextCreated(event:Event):void
  267. {
  268. initializeGraphicsAPI();
  269. initializeRoot();
  270. mTouchProcessor.simulateMultitouch = mSimulateMultitouch;
  271. }
  272. private function onEnterFrame(event:Event):void
  273. {
  274. if (mNativeOverlay) updateNativeOverlay();
  275. if (mStarted) render();
  276. }
  277. private function onKey(event:KeyboardEvent):void
  278. {
  279. mStage.dispatchEvent(new starling.events.KeyboardEvent(
  280. event.type, event.charCode, event.keyCode, event.keyLocation,
  281. event.ctrlKey, event.altKey, event.shiftKey));
  282. }
  283. private function onResize(event:flash.events.Event):void
  284. {
  285. var stage:flash.display.Stage = event.target as flash.display.Stage;
  286. mStage.dispatchEvent(new ResizeEvent(Event.RESIZE, stage.stageWidth, stage.stageHeight));
  287. }
  288. private function onTouch(event:Event):void
  289. {
  290. var position:Point;
  291. var phase:String;
  292. var touchID:int;
  293. if (event is MouseEvent)
  294. {
  295. var mouseEvent:MouseEvent = event as MouseEvent;
  296. position = convertPosition(mouseEvent.stageX, mouseEvent.stageY);
  297. phase = getPhaseFromMouseEvent(mouseEvent);
  298. touchID = 0;
  299. }
  300. else
  301. {
  302. var touchEvent:TouchEvent = event as TouchEvent;
  303. position = convertPosition(touchEvent.stageX, touchEvent.stageY);
  304. phase = getPhaseFromTouchEvent(touchEvent);
  305. touchID = touchEvent.touchPointID;
  306. }
  307. mTouchProcessor.enqueue(touchID, phase, position.x, position.y);
  308. function convertPosition(globalX:Number, globalY:Number):Point
  309. {
  310. return new Point(
  311. mStage.stageWidth * (globalX - mViewPort.x) / mViewPort.width,
  312. mStage.stageHeight * (globalY - mViewPort.y) / mViewPort.height);
  313. }
  314. function getPhaseFromMouseEvent(event:MouseEvent):String
  315. {
  316. switch (event.type)
  317. {
  318. case MouseEvent.MOUSE_DOWN: return TouchPhase.BEGAN; break;
  319. case MouseEvent.MOUSE_UP: return TouchPhase.ENDED; break;
  320. case MouseEvent.MOUSE_MOVE:
  321. return mouseEvent.buttonDown ? TouchPhase.MOVED : TouchPhase.HOVER;
  322. break;
  323. default: return null;
  324. }
  325. }
  326. function getPhaseFromTouchEvent(event:TouchEvent):String
  327. {
  328. switch (event.type)
  329. {
  330. case TouchEvent.TOUCH_BEGIN: return TouchPhase.BEGAN; break;
  331. case TouchEvent.TOUCH_MOVE: return TouchPhase.MOVED; break;
  332. case TouchEvent.TOUCH_END: return TouchPhase.ENDED; break;
  333. default: return null;
  334. }
  335. }
  336. }
  337. // program management
  338. /** Registers a vertex- and fragment-program under a certain name. */
  339. public function registerProgram(name:String, vertexProgram:ByteArray, fragmentProgram:ByteArray):void
  340. {
  341. if (name in mPrograms)
  342. throw new Error("Another program with this name is already registered");
  343. var program:Program3D = mContext.createProgram();
  344. program.upload(vertexProgram, fragmentProgram);
  345. mPrograms[name] = program;
  346. }
  347. /** Deletes the vertex- and fragment-programs of a certain name. */
  348. public function deleteProgram(name:String):void
  349. {
  350. var program:Program3D = getProgram(name);
  351. if (program)
  352. {
  353. program.dispose();
  354. delete mPrograms[name];
  355. }
  356. }
  357. /** Returns the vertex- and fragment-programs registered under a certain name. */
  358. public function getProgram(name:String):Program3D
  359. {
  360. return mPrograms[name] as Program3D;
  361. }
  362. /** Indicates if a set of vertex- and fragment-programs is registered under a certain name. */
  363. public function hasProgram(name:String):Boolean
  364. {
  365. return name in mPrograms;
  366. }
  367. // properties
  368. /** Indicates if this Starling instance is started. */
  369. public function get isStarted():Boolean { return mStarted; }
  370. /** The default juggler of this instance. Will be advanced once per frame. */
  371. public function get juggler():Juggler { return mJuggler; }
  372. /** The render context of this instance. */
  373. public function get context():Context3D { return mContext; }
  374. /** Indicates if multitouch simulation with "Shift" and "Ctrl"/"Cmd"-keys is enabled.
  375. * @default false */
  376. public function get simulateMultitouch():Boolean { return mSimulateMultitouch; }
  377. public function set simulateMultitouch(value:Boolean):void
  378. {
  379. mSimulateMultitouch = value;
  380. if (mContext) mTouchProcessor.simulateMultitouch = value;
  381. }
  382. /** Indicates if Stage3D render methods will report errors. Activate only when needed,
  383. * as this has a negative impact on performance. @default false */
  384. public function get enableErrorChecking():Boolean { return mEnableErrorChecking; }
  385. public function set enableErrorChecking(value:Boolean):void
  386. {
  387. mEnableErrorChecking = value;
  388. if (mContext) mContext.enableErrorChecking = value;
  389. }
  390. /** The antialiasing level. 0 - no antialasing, 16 - maximum antialiasing. @default 0 */
  391. public function get antiAliasing():int { return mAntiAliasing; }
  392. public function set antiAliasing(value:int):void
  393. {
  394. mAntiAliasing = value;
  395. updateViewPort();
  396. }
  397. /** The viewport into which Starling contents will be rendered. */
  398. public function get viewPort():Rectangle { return mViewPort.clone(); }
  399. public function set viewPort(value:Rectangle):void
  400. {
  401. mViewPort = value.clone();
  402. updateViewPort();
  403. }
  404. /** A Flash Sprite placed directly on top of the Starling content. Use it to display native
  405. * Flash components. */
  406. public function get nativeOverlay():Sprite
  407. {
  408. if (mNativeOverlay == null)
  409. {
  410. mNativeOverlay = new Sprite();
  411. mNativeStage.addChild(mNativeOverlay);
  412. updateNativeOverlay();
  413. }
  414. return mNativeOverlay;
  415. }
  416. /** The Starling stage object, which is the root of the display tree that is rendered. */
  417. public function get stage():Stage
  418. {
  419. return mStage;
  420. }
  421. /** The Flash (2D) stage Starling renders beneath. */
  422. public function get nativeStage():flash.display.Stage
  423. {
  424. return mNativeStage;
  425. }
  426. // static properties
  427. /** The currently active Starling instance. */
  428. public static function get current():Starling { return sCurrent; }
  429. /** The render context of the currently active Starling instance. */
  430. public static function get context():Context3D { return sCurrent.context; }
  431. /** The default juggler of the currently active Starling instance. */
  432. public static function get juggler():Juggler { return sCurrent.juggler; }
  433. /** Indicates if multitouch input should be supported. */
  434. public static function get multitouchEnabled():Boolean
  435. {
  436. return Multitouch.inputMode == MultitouchInputMode.TOUCH_POINT;
  437. }
  438. public static function set multitouchEnabled(value:Boolean):void
  439. {
  440. Multitouch.inputMode = value ? MultitouchInputMode.TOUCH_POINT :
  441. MultitouchInputMode.NONE;
  442. }
  443. }
  444. }