~facundo/enjuewemela/trunk

« back to all changes in this revision

Viewing changes to cocos/director.py

  • Committer: facundo at com
  • Date: 2010-11-20 01:41:24 UTC
  • mto: This revision was merged to the branch mainline in revision 63.
  • Revision ID: facundo@taniquetil.com.ar-20101120014124-zjyxkchmvili5m2u
Project reorder!

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# ----------------------------------------------------------------------------
 
2
# cocos2d
 
3
# Copyright (c) 2008 Daniel Moisset, Ricardo Quesada, Rayentray Tappa, Lucio Torre
 
4
# All rights reserved.
 
5
#
 
6
# Redistribution and use in source and binary forms, with or without
 
7
# modification, are permitted provided that the following conditions are met:
 
8
#
 
9
#   * Redistributions of source code must retain the above copyright
 
10
#     notice, this list of conditions and the following disclaimer.
 
11
#   * Redistributions in binary form must reproduce the above copyright
 
12
#     notice, this list of conditions and the following disclaimer in
 
13
#     the documentation and/or other materials provided with the
 
14
#     distribution.
 
15
#   * Neither the name of cocos2d nor the names of its
 
16
#     contributors may be used to endorse or promote products
 
17
#     derived from this software without specific prior written
 
18
#     permission.
 
19
#
 
20
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 
21
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 
22
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 
23
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 
24
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 
25
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 
26
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 
27
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 
28
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 
29
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 
30
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 
31
# POSSIBILITY OF SUCH DAMAGE.
 
32
# ----------------------------------------------------------------------------
 
33
'''Singleton that handles the logic behind the Scenes
 
34
 
 
35
Director
 
36
========
 
37
 
 
38
Initializing
 
39
------------
 
40
 
 
41
The director is the singleton that creates and handles the main ``Window``
 
42
and manages the logic behind the ``Scenes``.
 
43
 
 
44
The first thing to do, is to initialize the ``director``::
 
45
 
 
46
    from cocos.director import *
 
47
    director.init( list_of_arguments )
 
48
 
 
49
This will initialize the director, and will create a display area
 
50
(a 640x480 window by default).
 
51
The parameters that are supported by director.init() are the same
 
52
parameters that are supported by pyglet.window.Window(), plus a few
 
53
cocos exclusive ones.
 
54
 
 
55
director.init cocos exclusive parameters:
 
56
    * ``do_not_scale``: Boleean. Defaults to False, when your app can think
 
57
        that the windows size dont change despite resize events.
 
58
        When True, your app must include logic to deal with diferent window
 
59
        sizes along the session.
 
60
    * ``audio_backend``: one in ['pyglet','sdl']. Defaults to 'pyglet' for
 
61
        legacy support.
 
62
    * ``audio``: None or a dict providing parameters for the sdl audio backend.
 
63
        * None: in this case a "null" audio system will be used, where all the
 
64
          sdl sound operations will be no-ops. This may be useful if you do not
 
65
          want to depend on SDL_mixer
 
66
        * A dictionary with string keys; these are the arguments for setting up
 
67
          the audio output (sample rate and bit-width, channels, buffer size).
 
68
          The key names/values should match the positional arguments of
 
69
          http://www.pygame.org/docs/ref/mixer.html#pygame.mixer.init
 
70
        * The default value is {}, which means sound enabled with default
 
71
          settings
 
72
 
 
73
director.init parameters passes to pyglet.window.Window (partial list):
 
74
 
 
75
    * ``fullscreen``: Boolean. Window is created in fullscreen. Default is False
 
76
    * ``resizable``: Boolean. Window is resizable. Default is False
 
77
    * ``vsync``: Boolean. Sync with the vertical retrace. Default is True
 
78
    * ``width``: Integer. Window width size. Default is 640
 
79
    * ``height``: Integer. Window height size. Default is 480
 
80
    * ``caption``: String. Window title.
 
81
    * ``visible``: Boolean. Window is visible or not. Default is True.
 
82
 
 
83
The full list of valid video arguments can be found at the pyglet Window
 
84
documentation:
 
85
 
 
86
    - http://www.pyglet.org/doc/1.1/api/pyglet.window.Window-class.html
 
87
 
 
88
Example::
 
89
 
 
90
    director.init( caption="Hello World", fullscreen=True )
 
91
 
 
92
Running a Scene
 
93
----------------
 
94
 
 
95
Once you have initialized the director, you can run your first ``Scene``::
 
96
 
 
97
    director.run( Scene( MyLayer() ) )
 
98
 
 
99
This will run a scene that has only 1 layer: ``MyLayer()``. You can run a scene
 
100
that has multiple layers. For more information about ``Layers`` and ``Scenes``
 
101
refer to the ``Layers`` and ``Scene`` documentation.
 
102
 
 
103
`cocos.director.Director`
 
104
 
 
105
Once a scene is running you can do the following actions:
 
106
 
 
107
    * ``director.replace( new_scene ):``
 
108
        Replaces the running scene with the new_scene
 
109
        You could also use a transition. For example:
 
110
        director.replace( SplitRowsTransition( new_scene, duration=2 ) )
 
111
 
 
112
    * ``director.push( new_scene ):``
 
113
        The running scene will be pushed to a queue of scenes to run,
 
114
        and new_scene will be executed.
 
115
 
 
116
    * ``director.pop():``
 
117
        Will pop out a scene from the queue, and it will replace the running scene.
 
118
 
 
119
    * ``director.scene.end( end_value ):``
 
120
        Finishes the current scene with an end value of ``end_value``. The next scene
 
121
        to be run will be popped from the queue.
 
122
 
 
123
Other functions you can use are:
 
124
 
 
125
    * ``director.get_window_size():``
 
126
      Returns an (x,y) pair with the _logical_ dimensions of the display.
 
127
      The display might have been resized, but coordinates are always relative
 
128
      to this size. If you need the _physical_ dimensions, check the dimensions
 
129
      of ``director.window``
 
130
 
 
131
 
 
132
    * ``get_virtual_coordinates(self, x, y):``
 
133
      Transforms coordinates that belongs the real (physical) window size, to
 
134
      the coordinates that belongs to the virtual (logical) window. Returns
 
135
      an x,y pair in logical coordinates.
 
136
 
 
137
The director also has some useful attributes:
 
138
 
 
139
    * ``director.return_value``: The value returned by the last scene that
 
140
      called ``director.scene.end``. This is useful to use scenes somewhat like
 
141
      function calls: you push a scene to call it, and check the return value
 
142
      when the director returns control to you.
 
143
 
 
144
    * ``director.window``: This is the pyglet window handled by this director,
 
145
      if you happen to need low level access to it.
 
146
 
 
147
    * ``self.show_FPS``: You can set this to a boolean value to enable, disable
 
148
      the framerate indicator.
 
149
 
 
150
    * ``self.scene``: The scene currently active
 
151
 
 
152
'''
 
153
 
 
154
__docformat__ = 'restructuredtext'
 
155
 
 
156
from os import getenv
 
157
import pyglet
 
158
from pyglet import window, event
 
159
from pyglet import clock
 
160
#from pyglet import media
 
161
from pyglet.gl import *
 
162
 
 
163
import cocos, cocos.audio
 
164
 
 
165
__all__ = ['director', 'DefaultHandler']
 
166
 
 
167
class DefaultHandler( object ):
 
168
    def __init__(self):
 
169
        super(DefaultHandler,self).__init__()
 
170
        self.wired = False
 
171
 
 
172
    def on_key_press( self, symbol, modifiers ):
 
173
        if symbol == pyglet.window.key.F and (modifiers & pyglet.window.key.MOD_ACCEL):
 
174
            director.window.set_fullscreen( not director.window.fullscreen )
 
175
            return True
 
176
 
 
177
        elif symbol == pyglet.window.key.P and (modifiers & pyglet.window.key.MOD_ACCEL):
 
178
            import scenes.pause as pause
 
179
            pause_sc = pause.get_pause_scene()
 
180
            if pause:
 
181
                director.push( pause_sc )
 
182
            return True
 
183
 
 
184
        elif symbol == pyglet.window.key.W and (modifiers & pyglet.window.key.MOD_ACCEL):
 
185
#            import wired
 
186
            if self.wired == False:
 
187
                glDisable(GL_TEXTURE_2D);
 
188
                glPolygonMode(GL_FRONT, GL_LINE);
 
189
                glPolygonMode(GL_BACK, GL_LINE);
 
190
#                wired.wired.install()
 
191
#                wired.wired.uset4F('color', 1.0, 1.0, 1.0, 1.0 )
 
192
                self.wired = True
 
193
            else:
 
194
                glEnable(GL_TEXTURE_2D);
 
195
                glPolygonMode(GL_FRONT, GL_FILL);
 
196
                glPolygonMode(GL_BACK, GL_FILL);
 
197
                self.wired = False
 
198
#                wired.wired.uninstall()
 
199
            return True
 
200
 
 
201
        elif symbol == pyglet.window.key.X and (modifiers & pyglet.window.key.MOD_ACCEL):
 
202
            director.show_FPS = not director.show_FPS
 
203
            return True
 
204
 
 
205
        elif symbol == pyglet.window.key.I and (modifiers & pyglet.window.key.MOD_ACCEL):
 
206
            from layer import PythonInterpreterLayer
 
207
 
 
208
            if not director.show_interpreter:
 
209
                if director.python_interpreter == None:
 
210
                    director.python_interpreter = cocos.scene.Scene( PythonInterpreterLayer() )
 
211
                    director.python_interpreter.enable_handlers( True )
 
212
                director.python_interpreter.on_enter()
 
213
                director.show_interpreter = True
 
214
            else:
 
215
                director.python_interpreter.on_exit()
 
216
                director.show_interpreter= False
 
217
            return True
 
218
 
 
219
        elif symbol == pyglet.window.key.S and (modifiers & pyglet.window.key.MOD_ACCEL):
 
220
            import time
 
221
            pyglet.image.get_buffer_manager().get_color_buffer().save('screenshot-%d.png' % (int( time.time() ) ) )
 
222
            return True
 
223
 
 
224
        if symbol == pyglet.window.key.ESCAPE:
 
225
            director.pop()
 
226
            return True
 
227
 
 
228
class ScreenReaderClock(pyglet.clock.Clock):
 
229
    ''' Make frames happen every 1/framerate and takes screenshots '''
 
230
 
 
231
    def __init__(self, framerate, template, duration):
 
232
        super(ScreenReaderClock, self).__init__()
 
233
        self.framerate = framerate
 
234
        self.template = template
 
235
        self.duration = duration
 
236
        self.frameno = 0
 
237
        self.fake_time = 0
 
238
 
 
239
    def tick(self, poll=False):
 
240
        '''Signify that one frame has passed.
 
241
 
 
242
        '''
 
243
        # take screenshot
 
244
        pyglet.image.get_buffer_manager().get_color_buffer().save(self.template % (self.frameno) )
 
245
        self.frameno += 1
 
246
 
 
247
        # end?
 
248
        if self.duration:
 
249
            if self.fake_time > self.duration:
 
250
                raise SystemExit()
 
251
 
 
252
        # fake time.time
 
253
        ts = self.fake_time
 
254
        self.fake_time += 1.0/self.framerate
 
255
 
 
256
        if self.last_ts is None:
 
257
            delta_t = 0
 
258
        else:
 
259
            delta_t = ts - self.last_ts
 
260
            self.times.insert(0, delta_t)
 
261
            if len(self.times) > self.window_size:
 
262
                self.cumulative_time -= self.times.pop()
 
263
        self.cumulative_time += delta_t
 
264
        self.last_ts = ts
 
265
 
 
266
        # Call functions scheduled for every frame
 
267
        # Dupe list just in case one of the items unchedules itself
 
268
        for item in list(self._schedule_items):
 
269
            item.func(delta_t, *item.args, **item.kwargs)
 
270
 
 
271
        # Call all scheduled interval functions and reschedule for future.
 
272
        need_resort = False
 
273
        # Dupe list just in case one of the items unchedules itself
 
274
        for item in list(self._schedule_interval_items):
 
275
            if item.next_ts > ts:
 
276
                break
 
277
            item.func(ts - item.last_ts, *item.args, **item.kwargs)
 
278
            if item.interval:
 
279
                # Try to keep timing regular, even if overslept this time;
 
280
                # but don't schedule in the past (which could lead to
 
281
                # infinitely-worsing error).
 
282
                item.next_ts = item.last_ts + item.interval
 
283
                item.last_ts = ts
 
284
                if item.next_ts <= ts:
 
285
                    if ts - item.next_ts < 0.05:
 
286
                        # Only missed by a little bit, keep the same schedule
 
287
                        item.next_ts = ts + item.interval
 
288
                    else:
 
289
                        # Missed by heaps, do a soft reschedule to avoid
 
290
                        # lumping everything together.
 
291
                        item.next_ts = self._get_soft_next_ts(ts, item.interval)
 
292
                        # Fake last_ts to avoid repeatedly over-scheduling in
 
293
                        # future.  Unfortunately means the next reported dt is
 
294
                        # incorrect (looks like interval but actually isn't).
 
295
                        item.last_ts = item.next_ts - item.interval
 
296
                need_resort = True
 
297
 
 
298
        # Remove finished one-shots.
 
299
        self._schedule_interval_items = \
 
300
            [item for item in self._schedule_interval_items \
 
301
             if item.next_ts > ts]
 
302
 
 
303
        if need_resort:
 
304
            # TODO bubble up changed items might be faster
 
305
            self._schedule_interval_items.sort(key=lambda a: a.next_ts)
 
306
 
 
307
        return delta_t
 
308
 
 
309
class Director(event.EventDispatcher):
 
310
    """Class that creates and handle the main Window and manages how
 
311
       and when to execute the Scenes"""
 
312
    #: a dict with locals for the interactive python interpreter (fill with what you need)
 
313
    interpreter_locals = {}
 
314
 
 
315
    def init(self, *args, **kwargs):
 
316
        """Initializes the Director creating the main window.
 
317
        Keyword arguments are passed to pyglet.window.Window().
 
318
 
 
319
        All the valid arguments can be found here:
 
320
 
 
321
            - http://www.pyglet.org/doc/1.1/api/pyglet.window.Window-class.html
 
322
 
 
323
        :rtype: pyglet.window.Window
 
324
        :returns: The main window, an instance of pyglet.window.Window class.
 
325
        """
 
326
 
 
327
        # pop out the Cocos-specific flags
 
328
        do_not_scale_window = kwargs.pop('do_not_scale', False)
 
329
        audio_backend = kwargs.pop('audio_backend', 'pyglet')
 
330
        audio_settings = kwargs.pop('audio', {})
 
331
 
 
332
        # Environment variable COCOS2d_NOSOUND=1 overrides audio settings
 
333
        if getenv('COCOS2D_NOSOUND', None) == '1' or audio_backend == 'pyglet':
 
334
            audio_settings = None
 
335
        # if audio is not working, better to not work at all. Except if
 
336
        # explicitely instructed to continue
 
337
        if not cocos.audio._working and audio_settings is not None:
 
338
            from cocos.audio.exceptions import NoAudioError
 
339
            msg = "cocos.audio isn't able to work without needed dependencies. " \
 
340
                  "Try installing pygame for fixing it, or forcing no audio " \
 
341
                  "mode by calling director.init with audio=None, or setting the " \
 
342
                  "COCOS2D_NOSOUND=1 variable in your env."
 
343
            raise NoAudioError(msg)
 
344
 
 
345
        #: pyglet's window object
 
346
        self.window = window.Window( *args, **kwargs )
 
347
 
 
348
        #: whether or not the FPS are displayed
 
349
        self.show_FPS = False
 
350
 
 
351
        #: stack of scenes
 
352
        self.scene_stack = []
 
353
 
 
354
        #: scene that is being run
 
355
        self.scene = None
 
356
 
 
357
        #: this is the next scene that will be shown
 
358
        self.next_scene = None
 
359
 
 
360
        # save resolution and aspect for resize / fullscreen
 
361
        if do_not_scale_window:
 
362
            self.window.push_handlers(on_resize=self.unscaled_resize_window)
 
363
        else:
 
364
            self.window.push_handlers(on_resize=self.scaled_resize_window)
 
365
        self.window.push_handlers(self.on_draw)
 
366
        self._window_virtual_width = self.window.width
 
367
        self._window_virtual_height = self.window.height
 
368
        self._window_aspect =  self.window.width / float( self.window.height )
 
369
        self._offset_x = 0
 
370
        self._offset_y = 0
 
371
 
 
372
        # opengl settings
 
373
        self.set_alpha_blending()
 
374
 
 
375
        # python interpreter
 
376
        self.python_interpreter = None
 
377
 
 
378
        #: whether or not to show the python interpreter
 
379
        self.show_interpreter = False
 
380
 
 
381
        # default handler
 
382
        self.window.push_handlers( DefaultHandler() )
 
383
 
 
384
        # Audio setup:
 
385
        #TODO: reshape audio to not screw unittests
 
386
        import os
 
387
        if not os.environ.get('cocos_utest', False):
 
388
            cocos.audio.initialize(audio_settings)
 
389
 
 
390
        return self.window
 
391
 
 
392
    fps_display = None
 
393
    def set_show_FPS(self, value):
 
394
        if value and self.fps_display is None:
 
395
            self.fps_display = clock.ClockDisplay()
 
396
        elif not value and self.fps_display is not None:
 
397
            self.fps_display.unschedule()
 
398
            self.fps_display.label.delete()
 
399
            self.fps_display = None
 
400
 
 
401
    show_FPS = property(lambda self: self.fps_display is not None,
 
402
        set_show_FPS)
 
403
 
 
404
    def run(self, scene):
 
405
        """Runs a scene, entering in the Director's main loop.
 
406
 
 
407
        :Parameters:
 
408
            `scene` : `Scene`
 
409
                The scene that will be run.
 
410
        """
 
411
 
 
412
        self.scene_stack.append( self.scene )
 
413
        self._set_scene( scene )
 
414
 
 
415
        event_loop.run()
 
416
 
 
417
 
 
418
    def set_recorder(self, framerate, template="frame-%d.png", duration=None):
 
419
        '''Will replace the system clock so that now we can ensure a steady
 
420
        frame rate and save one image per frame
 
421
 
 
422
        :Parameters
 
423
            `framerate`: int
 
424
                the number of frames per second
 
425
            `template`: str
 
426
                the template that will be completed with an in for the name of the files
 
427
            `duration`: float
 
428
                the amount of seconds to record, or 0 for infinite
 
429
        '''
 
430
        pyglet.clock._default = ScreenReaderClock(framerate, template, duration)
 
431
 
 
432
 
 
433
    def on_draw( self ):
 
434
        """Callback to draw the window.
 
435
        It propagates the event to the running scene."""
 
436
 
 
437
        if self.window.width==0 or self.window.height==0: #
 
438
            return
 
439
        self.window.clear()
 
440
 
 
441
        if self.next_scene is not None:
 
442
            self._set_scene( self.next_scene )
 
443
 
 
444
        if not self.scene_stack:
 
445
            pyglet.app.exit()
 
446
 
 
447
        # draw all the objects
 
448
        self.scene.visit()
 
449
 
 
450
        # finally show the FPS
 
451
        if self.show_FPS:
 
452
            self.fps_display.draw()
 
453
 
 
454
        if self.show_interpreter:
 
455
            self.python_interpreter.visit()
 
456
 
 
457
 
 
458
    def push(self, scene):
 
459
        """Suspends the execution of the running scene, pushing it
 
460
        on the stack of suspended scenes. The new scene will be executed.
 
461
 
 
462
        :Parameters:
 
463
            `scene` : `Scene`
 
464
                It is the scene that will be run.
 
465
           """
 
466
        self.dispatch_event("on_push", scene )
 
467
 
 
468
    def on_push( self, scene ):
 
469
        self.next_scene = scene
 
470
        self.scene_stack.append( self.scene )
 
471
 
 
472
    def pop(self):
 
473
        """Pops out a scene from the queue. This scene will replace the running one.
 
474
           The running scene will be deleted. If there are no more scenes in the stack
 
475
           the execution is terminated.
 
476
        """
 
477
        self.dispatch_event("on_pop")
 
478
 
 
479
    def on_pop(self):
 
480
        self.next_scene = self.scene_stack.pop()
 
481
 
 
482
    def replace(self, scene):
 
483
        """Replaces the running scene with a new one. The running scene is terminated.
 
484
 
 
485
        :Parameters:
 
486
            `scene` : `Scene`
 
487
                It is the scene that will be run.
 
488
        """
 
489
        self.next_scene = scene
 
490
 
 
491
    def _set_scene(self, scene ):
 
492
        """Change to a new scene.
 
493
        """
 
494
 
 
495
        self.next_scene = None
 
496
 
 
497
        if self.scene is not None:
 
498
            self.scene.on_exit()
 
499
            self.scene.enable_handlers( False )
 
500
 
 
501
        old = self.scene
 
502
 
 
503
        self.scene = scene
 
504
        self.scene.enable_handlers( True )
 
505
        scene.on_enter()
 
506
 
 
507
        return old
 
508
 
 
509
 
 
510
    #
 
511
    # Window Helper Functions
 
512
    #
 
513
    def get_window_size( self ):
 
514
        """Returns the size of the window when it was created, and not the
 
515
        actual size of the window.
 
516
 
 
517
        Usually you don't want to know the current window size, because the
 
518
        Director() hides the complexity of rescaling your objects when
 
519
        the Window is resized or if the window is made fullscreen.
 
520
 
 
521
        If you created a window of 640x480, the you should continue to place
 
522
        your objects in a 640x480 world, no matter if your window is resized or not.
 
523
        Director will do the magic for you.
 
524
 
 
525
        :rtype: (x,y)
 
526
        :returns: The size of the window when it was created
 
527
        """
 
528
        if not hasattr(self, "_window_virtual_width"):
 
529
            import pdb;pdb.set_trace()
 
530
        return ( self._window_virtual_width, self._window_virtual_height)
 
531
 
 
532
 
 
533
    def get_virtual_coordinates( self, x, y ):
 
534
        """Transforms coordinates that belongs the *real* window size, to the
 
535
        coordinates that belongs to the *virtual* window.
 
536
 
 
537
        For example, if you created a window of 640x480, and it was resized
 
538
        to 640x1000, then if you move your mouse over that window,
 
539
        it will return the coordinates that belongs to the newly resized window.
 
540
        Probably you are not interested in those coordinates, but in the coordinates
 
541
        that belongs to your *virtual* window.
 
542
 
 
543
        :rtype: (x,y)
 
544
        :returns: Transformed coordinates from the *real* window to the *virtual* window
 
545
        """
 
546
 
 
547
        x_diff = self._window_virtual_width / float( self.window.width - self._offset_x * 2 )
 
548
        y_diff = self._window_virtual_height / float( self.window.height - self._offset_y * 2 )
 
549
 
 
550
        adjust_x = (self.window.width * x_diff - self._window_virtual_width ) / 2
 
551
        adjust_y = (self.window.height * y_diff - self._window_virtual_height ) / 2
 
552
 
 
553
        return ( int( x_diff * x) - adjust_x,   int( y_diff * y ) - adjust_y )
 
554
 
 
555
 
 
556
    def scaled_resize_window( self, width, height):
 
557
        """One of two possible methods that are called when the main window is resized.
 
558
 
 
559
        This implementation scales the display such that the initial resolution
 
560
        requested by the programmer (the "logical" resolution) is always retained
 
561
        and the content scaled to fit the physical display.
 
562
 
 
563
        This implementation also sets up a 3D projection for compatibility with the
 
564
        largest set of Cocos transforms.
 
565
 
 
566
        The other implementation is `unscaled_resize_window`.
 
567
 
 
568
        :Parameters:
 
569
            `width` : Integer
 
570
                New width
 
571
            `height` : Integer
 
572
                New height
 
573
        """
 
574
        # physical view size
 
575
        pw, ph = width, height
 
576
        # virtual (desired) view size
 
577
        vw, vh = self.get_window_size()
 
578
        # desired aspect ratio
 
579
        v_ar = vw/float(vh)
 
580
        # usable width, heigh
 
581
        uw = int(min(pw, ph*v_ar))
 
582
        uh = int(min(ph, pw/v_ar))
 
583
        ox = (pw-uw)//2
 
584
        oy = (ph-uh)//2
 
585
        self._offset_x = ox
 
586
        self._offset_y = oy
 
587
        self._usable_width = uw
 
588
        self._usable_height = uh
 
589
        self.set_projection()
 
590
        self.dispatch_event("on_resize", width, height)
 
591
        return pyglet.event.EVENT_HANDLED
 
592
 
 
593
    def unscaled_resize_window(self, width, height):
 
594
        """One of two possible methods that are called when the main window is resized.
 
595
 
 
596
        This implementation does not scale the display but rather forces the logical
 
597
        resolution to match the physical one.
 
598
 
 
599
        This implementation sets up a 2D projection, resulting in the best pixel
 
600
        alignment possible. This is good for text and other detailed 2d graphics
 
601
        rendering.
 
602
 
 
603
        The other implementation is `scaled_resize_window`.
 
604
 
 
605
        :Parameters:
 
606
            `width` : Integer
 
607
                New width
 
608
            `height` : Integer
 
609
                New height
 
610
        """
 
611
        self._usable_width = width
 
612
        self._usable_height = height
 
613
        self.dispatch_event("on_resize", width, height)
 
614
 
 
615
    def set_projection(self):
 
616
        '''Sets a 3D projection mantaining the aspect ratio of the original window size'''
 
617
        # virtual (desired) view size
 
618
        vw, vh = self.get_window_size()
 
619
 
 
620
        glViewport(self._offset_x, self._offset_y, self._usable_width, self._usable_height)
 
621
        glMatrixMode(GL_PROJECTION)
 
622
        glLoadIdentity()
 
623
        gluPerspective(60, self._usable_width/float(self._usable_height), 0.1, 3000.0)
 
624
        glMatrixMode(GL_MODELVIEW)
 
625
 
 
626
        glLoadIdentity()
 
627
        gluLookAt( vw/2.0, vh/2.0, vh/1.1566,   # eye
 
628
                   vw/2.0, vh/2.0, 0,           # center
 
629
                   0.0, 1.0, 0.0                # up vector
 
630
                   )
 
631
 
 
632
 
 
633
    #
 
634
    # Misc functions
 
635
    #
 
636
    def set_alpha_blending( self, on=True ):
 
637
        """
 
638
        Enables/Disables alpha blending in OpenGL
 
639
        using the GL_ONE_MINUS_SRC_ALPHA algorithm.
 
640
        On by default.
 
641
        """
 
642
        if on:
 
643
            glEnable(GL_BLEND)
 
644
            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
 
645
        else:
 
646
            glDisable(GL_BLEND)
 
647
 
 
648
    def set_depth_test( sefl, on=True ):
 
649
        '''Enables z test. On by default
 
650
        '''
 
651
        if on:
 
652
            glClearDepth(1.0)
 
653
            glEnable(GL_DEPTH_TEST)
 
654
            glDepthFunc(GL_LEQUAL)
 
655
            glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)
 
656
        else:
 
657
            glDisable( GL_DEPTH_TEST )
 
658
 
 
659
event_loop = pyglet.app.event_loop
 
660
if not hasattr(event_loop, "event"):
 
661
    event_loop = pyglet.app.EventLoop()
 
662
director = Director()
 
663
director.event = event_loop.event
 
664
"""The singleton; check `cocos.director.Director` for details on usage.
 
665
Don't instantiate Director(). Just use this singleton."""
 
666
 
 
667
director.interpreter_locals["director"] = director
 
668
director.interpreter_locals["cocos"] = cocos
 
669
 
 
670
Director.register_event_type('on_push')
 
671
Director.register_event_type('on_pop')
 
672
Director.register_event_type('on_resize')