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

/starling/src/starling/core/Starling.as

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