/src/aerys/minko/render/Viewport.as
ActionScript | 450 lines | 316 code | 74 blank | 60 comment | 53 complexity | e17fe920b317aca58f70142c9e185ced MD5 | raw file
1package aerys.minko.render
2{
3 import aerys.minko.ns.minko_render;
4 import aerys.minko.render.resource.Context3DResource;
5 import aerys.minko.type.KeyboardManager;
6 import aerys.minko.type.MouseManager;
7 import aerys.minko.type.Signal;
8
9 import flash.display.DisplayObject;
10 import flash.display.Graphics;
11 import flash.display.Shape;
12 import flash.display.Sprite;
13 import flash.display.Stage;
14 import flash.display.Stage3D;
15 import flash.display.StageAlign;
16 import flash.display.StageScaleMode;
17 import flash.events.Event;
18 import flash.events.MouseEvent;
19 import flash.events.TouchEvent;
20 import flash.geom.Point;
21
22 /**
23 * The Viewport is the display area where a 3D scene can be rendered.
24 *
25 * @author Jean-Marc Le Roux
26 *
27 */
28 public class Viewport extends Sprite
29 {
30 private static const ZERO2 : Point = new Point();
31
32 private var _stage3d : Stage3D = null;
33 private var _context3d : Context3DResource = null;
34
35 private var _width : uint = 0;
36 private var _height : uint = 0;
37 private var _autoResize : Boolean = false;
38 private var _antiAliasing : uint = 0;
39 private var _backgroundColor : uint = 0;
40 private var _backBuffer : RenderTarget = null;
41 private var _invalidBackBuffer : Boolean = false;
42
43 private var _alwaysOnTop : Boolean = false;
44 private var _mask : Shape = new Shape();
45
46 private var _mouseManager : MouseManager = new MouseManager();
47 private var _keyboardManager : KeyboardManager = new KeyboardManager();
48
49 private var _resized : Signal = new Signal('Viewport.resized');
50
51 minko_render function get context3D() : Context3DResource
52 {
53 return _context3d;
54 }
55
56 minko_render function get backBuffer() : RenderTarget
57 {
58 var positionOnStage : Point = localToGlobal(ZERO2);
59
60 if (_stage3d.x != positionOnStage.x || _stage3d.y != positionOnStage.y)
61 updateStage3D()
62
63 return _backBuffer;
64 }
65
66 public function get ready() : Boolean
67 {
68 return _stage3d != null && _stage3d.context3D != null && _backBuffer != null;
69 }
70
71 /**
72 * Whether the viewport is visible or not.
73 * @return
74 *
75 */
76 override public function set visible(value : Boolean) : void
77 {
78 if (_stage3d)
79 _stage3d.visible = value;
80
81 super.visible = value;
82 }
83
84 override public function set x(value : Number) : void
85 {
86 super.x = value;
87 updateStage3D();
88 }
89
90 override public function set y(value : Number) : void
91 {
92 super.y = value;
93 updateStage3D();
94 }
95
96 /**
97 * The width of the display area.
98 * @return
99 *
100 */
101 override public function get width() : Number
102 {
103 return _width;
104 }
105 override public function set width(value : Number) : void
106 {
107 resize(value, _height);
108 }
109
110 /**
111 * The height of the display area.
112 * @return
113 *
114 */
115 override public function get height() : Number
116 {
117 return _height;
118 }
119 override public function set height(value : Number) : void
120 {
121 resize(_width, value);
122 }
123
124 public function get alwaysOnTop() : Boolean
125 {
126 return _alwaysOnTop;
127 }
128 public function set alwaysOnTop(value : Boolean) : void
129 {
130 _alwaysOnTop = value;
131 }
132
133 public function get keyboardManager() : KeyboardManager
134 {
135 return _keyboardManager;
136 }
137
138 public function get mouseManager() : MouseManager
139 {
140 return _mouseManager;
141 }
142
143 /**
144 * The signal executed when the viewport is resized.
145 * Callback functions for this signal should accept the following
146 * arguments:
147 * <ul>
148 * <li>viewport : Viewport, the viewport executing the signal</li>
149 * <li>width : Number, the new width of the viewport</li>
150 * <li>height : Number, the new height of the viewport</li>
151 * </ul>
152 * @return
153 *
154 */
155 public function get resized() : Signal
156 {
157 return _resized;
158 }
159
160 /**
161 * The background color of the display area. This value must use the
162 * RGB format.
163 * @return
164 *
165 */
166 public function get backgroundColor() : uint
167 {
168 return _backgroundColor;
169 }
170 public function set backgroundColor(value : uint) : void
171 {
172 _backgroundColor = value;
173 updateBackBuffer();
174 }
175
176 /**
177 * The anti-aliasing to use (0, 2, 4, 8 or 16). The actual anti-aliasing
178 * used for rendering depends on the hardware capabilities. If the specified
179 * anti-aliasing value is not supported, the value 0 will be used.
180 * @return
181 *
182 */
183 public function get antiAliasing() : uint
184 {
185 return _antiAliasing;
186 }
187 public function set antiAliasing(value : uint) : void
188 {
189 _antiAliasing = value;
190 updateStage3D();
191 }
192
193 /**
194 * The driver informations provided by the Stage3D API.
195 */
196 public function get driverInfo() : String
197 {
198 return _stage3d && _stage3d.context3D
199 ? _stage3d.context3D.driverInfo
200 : null;
201 }
202
203 public function Viewport(antiAliasing : uint = 0,
204 width : uint = 0,
205 height : uint = 0)
206 {
207 _antiAliasing = antiAliasing;
208
209 if (width == 0 && height == 0)
210 _autoResize = true;
211
212 _width = width;
213 _height = height;
214
215 initialize();
216 }
217
218 private function initialize() : void
219 {
220 _mouseManager.bind(this);
221
222 addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
223 addEventListener(Event.REMOVED_FROM_STAGE, removedFromStageHandler);
224 }
225
226 private function addedToStageHandler(event : Event) : void
227 {
228 _keyboardManager.bind(stage);
229
230 parent.addEventListener(Event.RESIZE, parentResizedHandler);
231
232 stage.addEventListener(Event.ADDED_TO_STAGE, displayObjectAddedToStageHandler);
233 stage.addEventListener(Event.REMOVED_FROM_STAGE, displayObjectRemovedFromStageHandler);
234
235 setupOnStage(stage);
236 }
237
238 private function removedFromStageHandler(event : Event) : void
239 {
240 _keyboardManager.unbind(stage);
241
242 parent.removeEventListener(Event.RESIZE, parentResizedHandler);
243
244 stage.removeEventListener(Event.ADDED_TO_STAGE, displayObjectAddedToStageHandler);
245 stage.removeEventListener(Event.REMOVED_FROM_STAGE, displayObjectRemovedFromStageHandler);
246
247 if (_stage3d != null)
248 _stage3d.visible = false;
249 }
250
251 private function setupOnStage(stage : Stage, stage3dId : uint = 0) : void
252 {
253 if (_autoResize)
254 {
255 _width = stage.stageWidth;
256 _height = stage.stageHeight;
257 }
258
259 if (!_stage3d)
260 {
261 _stage3d = stage.stage3Ds[stage3dId];
262 _stage3d.addEventListener(Event.CONTEXT3D_CREATE, context3dCreatedHandler);
263 _stage3d.requestContext3D();
264 }
265 else
266 {
267 _stage3d.visible = visible;
268 }
269
270 stage.scaleMode = StageScaleMode.NO_SCALE;
271 stage.align = StageAlign.TOP_LEFT;
272 }
273
274 /**
275 * Dispose the Viewport and all the Stage3D related objects. After this operation,
276 * the Viewport cannot be used anymore and is ready for garbage collection.
277 */
278 public function dispose():void
279 {
280 if (_stage3d != null)
281 {
282 _stage3d.removeEventListener(Event.CONTEXT3D_CREATE, context3dCreatedHandler);
283 _stage3d = null;
284 }
285
286 if (_context3d != null)
287 {
288 _context3d.dispose();
289 _context3d = null;
290 }
291
292 return ;
293 }
294
295 /**
296 * Resize the display area. The "resized" signal is executed when the new width and
297 * height have be set.
298 * @param width
299 * @param height
300 *
301 */
302 public function resize(width : Number, height : Number) : void
303 {
304 _autoResize = false;
305
306 setSize(width, height);
307 }
308
309 private function setSize(width : Number, height : Number) : void
310 {
311 if (width == _width && _height == height)
312 return ;
313
314 _width = width;
315 _height = height;
316
317 updateStage3D();
318 updateBackBuffer();
319 _resized.execute(this, width, height);
320 }
321
322 private function stageResizedHandler(event : Event) : void
323 {
324 updateMask();
325 }
326
327 private function parentResizedHandler(event : Event) : void
328 {
329 if (_autoResize)
330 {
331 if (parent == stage)
332 setSize(stage.stageWidth, stage.stageHeight);
333 else
334 setSize(parent.width, parent.height);
335 }
336 }
337
338 private function context3dCreatedHandler(event : Event) : void
339 {
340 _context3d = new Context3DResource(_stage3d.context3D);
341
342 updateStage3D();
343 updateBackBuffer();
344
345 dispatchEvent(new Event(Event.INIT));
346 }
347
348 private function updateBackBuffer() : void
349 {
350 if (_width == 0 || _height == 0 || _stage3d == null || _stage3d.context3D == null)
351 return ;
352
353 _invalidBackBuffer = false;
354 _stage3d.context3D.configureBackBuffer(_width, _height, _antiAliasing, true);
355 _backBuffer = new RenderTarget(_width, _height, null, 0, _backgroundColor);
356
357 graphics.clear();
358 graphics.beginFill(0, 0);
359 graphics.drawRect(0, 0, _width, _height);
360 }
361
362 private function updateStage3D() : void
363 {
364 if (_stage3d == null)
365 return ;
366
367 var upperLeft : Point = localToGlobal(ZERO2);
368
369 _stage3d.x = upperLeft.x;
370 _stage3d.y = upperLeft.y;
371
372 if (_width > 2048)
373 {
374 _stage3d.x = (_width - 2048) * 0.5;
375 _width = 2048;
376 }
377 else
378 _stage3d.x = upperLeft.x;
379
380 if (_height > 2048)
381 {
382 _stage3d.y = (_height - 2048) * 0.5;
383 _height = 2048;
384 }
385 else
386 _stage3d.y = upperLeft.y;
387
388 updateMask();
389 }
390
391 private function updateMask() : void
392 {
393 if (!stage)
394 return ;
395
396 var numChildren : uint = stage.numChildren;
397 var i : uint = 0;
398
399 if (_alwaysOnTop)
400 {
401 var gfx : Graphics = _mask.graphics;
402 var stageWidth : int = stage.stageWidth;
403 var stageHeight : int = stage.stageHeight;
404
405 gfx.clear();
406 gfx.beginFill(0);
407 gfx.moveTo(0, 0);
408 gfx.lineTo(stageWidth, 0);
409 gfx.lineTo(stageWidth, stageHeight);
410 gfx.lineTo(0., stageHeight);
411 gfx.lineTo(0, 0);
412 gfx.moveTo(_stage3d.x, _stage3d.y);
413 gfx.lineTo(_stage3d.x, _stage3d.y + height);
414 gfx.lineTo(_stage3d.x + width, _stage3d.y + height);
415 gfx.lineTo(_stage3d.x + width, _stage3d.y);
416 gfx.lineTo(_stage3d.x, _stage3d.y);
417 gfx.endFill();
418
419 for (i = 0; i < numChildren; ++i)
420 stage.getChildAt(i).mask = _mask;
421 }
422 else
423 {
424 for (i = 0; i < numChildren; ++i)
425 {
426 var child : DisplayObject = stage.getChildAt(i);
427
428 if (child.mask == _mask)
429 child.mask = null;
430 }
431 }
432 }
433
434 private function displayObjectAddedToStageHandler(event : Event) : void
435 {
436 var displayObject : DisplayObject = event.target as DisplayObject;
437
438 if (displayObject.parent == stage)
439 updateMask();
440 }
441
442 private function displayObjectRemovedFromStageHandler(event : Event) : void
443 {
444 var displayObject : DisplayObject = event.target as DisplayObject;
445
446 if (_autoResize && displayObject.parent == stage)
447 displayObject.mask = null;
448 }
449 }
450}