Statistics
| Revision:

root / PyOpenGL-Demo / NeHe / lesson44 / lesson44.py @ 1

History | View | Annotate | Download (18.2 kB)

1
# NeHe Tutorial Lesson: 44 - Lense Flare
2
#
3
# Ported to PyOpenGL 2.0 by Brian Leair 2004
4
#
5
# This code was created by Jeff Molofee 2000
6
#
7
# The port was based on the PyOpenGL tutorials and from 
8
# PyOpenGLContext (tests/glprint.py)
9
#
10
# If you've found this code useful, feel free to let me know 
11
# at (Brian Leair telcom_sage@yahoo.com).
12
#
13
# See original source and C based tutorial at http://nehe.gamedev.net
14
#
15
# Note:
16
# -----
17
# This code is not an ideal example of Pythonic coding or use of OO 
18
# techniques. It is a simple and direct exposition of how to use the 
19
# Open GL API in Python via the PyOpenGL package. It also uses GLUT, 
20
# a high quality platform independent library. Due to using these APIs, 
21
# this code is more like a C program using procedural programming.
22
#
23
# To run this example you will need:
24
# Python         - www.python.org (v 2.3 as of 1/2004)
25
# PyOpenGL         - pyopengl.sourceforge.net (v 2.0.1.07 as of 1/2004)
26
# Numeric Python        - (v.22 of "numpy" as of 1/2004) numpy.sourceforge.net
27
# Python Image Library        - http://www.pythonware.com/products/pil/
28
#
29
# #########################################################
30
# Please note, don't use PyOpenGL older than 2.0.1.07.
31
# Older PyOpenGL had a bug glGetFloat () that prevents this 
32
# tutorial from working.
33
#
34
#
35

    
36

    
37
from OpenGL.GL import *
38
from OpenGL.GLUT import *
39
from OpenGL.GLU import *
40
import Image                                        # PIL
41
try:
42
        import win32api                                # GetTickCount ()
43
        gHaveWin32 = 1
44
except:
45
        gHaveWin32 = 0
46
import sys
47
import time                                                # clock ()
48
import os
49

    
50
from glCamera import *
51
from glFont import *
52

    
53

    
54
# *********************** Globals *********************** 
55
# Python 2.2 defines these directly
56
try:
57
        True
58
except NameError:
59
        True = 1==1
60
        False = 1==0
61

    
62

    
63
# Some api in the chain is translating the keystrokes to this octal string
64
# so instead of saying: ESCAPE = 27, we use the following.
65
ESCAPE = '\033'
66

    
67
# Number of the glut window.
68
window = 0
69
base = None
70

    
71
gInfoOn = False
72
gFrames = 0
73
gStartTime = -1
74
gCurrentTime = -1
75
gFPS = -1
76
gCamera = None
77

    
78
# //##################  NEW STUFF  ##################################
79

    
80
qobj = None                                        # //the quadric for our cylinder
81
gcylList = None
82

    
83

    
84
def LoadTexture (path):
85
        """ // Load Image And Convert To A Texture
86
        path can be a relative path, or a fully qualified path.
87
        returns tuple of status and ID:
88
        returns False if the requested image couldn't loaded as a texture
89
        returns True and the texture ID if image was loaded
90
        """
91
        # Catch exception here if image file couldn't be loaded
92
        try:
93
                # Note, NYI, path specified as URL's could be access using python url lib
94
                # OleLoadPicturePath () supports url paths, but that capability isn't critcial to this tutorial.
95
                Picture = Image.open (path)
96
        except:
97
                return False, 0
98

    
99
        glMaxTexDim = glGetIntegerv (GL_MAX_TEXTURE_SIZE)
100

    
101
        WidthPixels = Picture.size [0]
102
        HeightPixels = Picture.size [1]
103

    
104
        if ((WidthPixels > glMaxTexDim) or (HeightPixels > glMaxTexDim)):
105
                # The image file is too large. Shrink it to fit within the texture dimensions
106
                # support by our rendering context for a GL texture.
107
                # Note, Feel free to experiemnt and force a resize by placing a small val into
108
                # glMaxTexDim (e.g. 32,64,128).
109
                raise RuntimeError, "Texture image (%d by %d) is larger than supported by GL %d." % (WidthPixels, HeightPixels, glMaxTexDim)
110

    
111
        # Create a raw string from the image data - data will be unsigned bytes
112
        # RGBpad, no stride (0), and first line is top of image (-1)
113
        pBits = Picture.tostring("raw", "RGBX", 0, -1)
114

    
115
        # // Typical Texture Generation Using Data From The Bitmap
116
        texid = glGenTextures(1);                                                                                        # // Create The Texture
117
        glBindTexture(GL_TEXTURE_2D, texid);                                                                # // Bind To The Texture ID
118
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);                # // (Modify This For The Type Of Filtering You Want)
119
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);     # // (Modify This For The Type Of Filtering You Want)
120

    
121
        # // (Modify This If You Want Mipmaps)
122
        glTexImage2D(GL_TEXTURE_2D, 0, 3, WidthPixels, HeightPixels, 0, GL_RGBA, GL_UNSIGNED_BYTE, pBits);
123

    
124
        # Cleanup (this would all happen automatically upon return... just spelling it out)
125
        # // Decrements IPicture Reference Count
126
        Picture = None
127
        return True, texid                                        # // Return True (All Good)
128

    
129

    
130

    
131

    
132

    
133

    
134

    
135
# A general OpenGL initialization function.  Sets all of the initial parameters. 
136
def InitGL(Width, Height):                                # We call this right after our OpenGL window is created.
137
        global gFont, gCamera, gStartTime, gcylList, qobj
138

    
139
        glShadeModel(GL_SMOOTH)                                # Enables Smooth Color Shading
140
        glClearColor(0.0, 0.0, 0.0, 0.5)        # This Will Clear The Background Color To Black
141
        glClearDepth(1.0)                                        # Enables Clearing Of The Depth Buffer
142
        glEnable(GL_DEPTH_TEST)                                # Enables Depth Testing
143
        glDepthFunc(GL_LEQUAL)                                # The Type Of Depth Test To Do
144
        glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST) # Really Nice Perspective Calculations
145

    
146
        status, tex = LoadTexture (os.path.join("Art","Font.bmp"))
147
        if (status):
148
                gFont = glFont ()
149
                gFont.SetFontTexture (tex)
150
                gFont.SetWindowSize (1024, 768)
151
                gFont.BuildFont (1.0)
152
        else:
153
                raise RuntimeError, "Failed to build font 'Art\\Font.bmp'"
154

    
155

    
156
        gCamera = glCamera ()
157
        gCamera.m_MaxHeadingRate = 1.0;                        # // Set our Maximum rates for the camera
158
        gCamera.m_MaxPitchRate = 1.0;                        # // Set our Maximum rates for the camera
159
        gCamera.m_HeadingDegrees = 0.0;                        # // Set our Maximum rates for the camera
160

    
161
        # // Try and load the HardGlow texture tell the user if we can't find it then quit
162
        status, gCamera.m_GlowTexture = LoadTexture(os.path.join("Art","HardGlow2.bmp"));
163
        if (not status):
164
                raise RuntimeError, "Failed to load Hard Glow texture."
165

    
166
        # // Try and load the BigGlow texture tell the user if we can't find it then quit
167
        status, gCamera.m_BigGlowTexture = LoadTexture(os.path.join("Art","BigGlow3.bmp"))
168
        if (not status):
169
                raise RuntimeError, "Failed to load Big Glow texture."
170

    
171
        # // Try and load the Halo texture tell the user if we can't find it then quit
172
        status, gCamera.m_HaloTexture = LoadTexture(os.path.join("Art","Halo3.bmp"))
173
        if (not status):
174
                raise RuntimeError, "Failed to load Halo texture."
175
        
176
        # // Try and load the Streaks texture tell the user if we can't find it then quit
177
        status, gCamera.m_StreakTexture = LoadTexture(os.path.join("Art","Streaks4.bmp"))
178
        if (not status):
179
                raise RuntimeError, "Failed to load Streaks texture."
180

    
181
        # //##################  NEW STUFF  ##################################
182

    
183
        # // Just create a cylinder that will be used as occluder object
184
        gcylList = glGenLists(1);
185
        qobj = gluNewQuadric();
186
        gluQuadricDrawStyle(qobj, GLU_FILL); 
187
        gluQuadricNormals(qobj, GLU_SMOOTH);
188
        glNewList(gcylList, GL_COMPILE);
189
        # List Start
190
        glEnable(GL_COLOR_MATERIAL);
191
        glColor3f(0.0, 0.0, 1.0);
192
        glEnable(GL_LIGHT0);
193
        glEnable(GL_LIGHTING);
194
        glTranslatef(0.0,0.0,-2.0);
195
        gluCylinder(qobj, 0.5, 0.5, 4.0, 15, 5);
196
        glDisable(GL_LIGHTING);
197
        glDisable(GL_LIGHT0);
198
        glDisable(GL_COLOR_MATERIAL);
199
        glEndList();
200
        # List End
201

    
202
        # if (gHaveWin32):
203
        #        gStartTime = win32api.GetTickCount ()         # // Get the time the app started
204
        gStartTime = time.clock ();                                         # // Get the time the app started
205

    
206
        return True                                                                        # // Initialization Went OK
207
        
208

    
209
# The function called when our window is resized (which shouldn't happen if you enable fullscreen, below)
210
def ReSizeGLScene(Width, Height):
211
        if Height == 0:                                                # Prevent A Divide By Zero If The Window Is Too Small 
212
                Height = 1
213

    
214
        glViewport(0, 0, Width, Height)                # Reset The Current Viewport And Perspective Transformation
215
        glMatrixMode(GL_PROJECTION)
216
        glLoadIdentity()
217
        # // field of view, aspect ratio, near and far
218
        # This will squash and stretch our objects as the window is resized.
219
        # Note that the near clip plane is 1 (hither) and the far plane is 1000 (yon)
220
        gluPerspective(45.0, float(Width)/float(Height), 1, 1000.0)
221

    
222
        glMatrixMode(GL_MODELVIEW)
223
        glLoadIdentity()
224

    
225
def DrawGLInfo ():
226
        global gCamera, gFont, gFrames, gCurrentTime, gCurrentTime, gStartTime, gFPS
227

    
228
        projMatrix = glGetFloatv(GL_PROJECTION_MATRIX);                                        # // Grab the projection matrix
229
        modelMatrix = glGetFloatv(GL_MODELVIEW_MATRIX);                                # // Grab the modelview matrix
230

    
231
        # // Print out the cameras position
232
        glColor4f(1.0, 1.0, 1.0, 1.0);
233
        String = "m_Position............. = %.02f, %.02f, %.02f" % (gCamera.m_Position.x, gCamera.m_Position.y, gCamera.m_Position.z)
234
        gFont.glPrintf(10, 720, 1, String);
235
        
236
        # // Print out the cameras direction
237
        String = "m_DirectionVector...... = %.02f, %.02f, %.02f" % (gCamera.m_DirectionVector.i, gCamera.m_DirectionVector.j, gCamera.m_DirectionVector.k);
238
        gFont.glPrintf(10, 700, 1, String);
239
        
240
        # // Print out the light sources position
241
        String = "m_LightSourcePos....... = %.02f, %.02f, %.02f" % (gCamera.m_LightSourcePos.x, gCamera.m_LightSourcePos.y, gCamera.m_LightSourcePos.z);
242
        gFont.glPrintf(10, 680, 1, String);
243

    
244
        # // Print out the intersection point
245
        String = "ptIntersect............ = %.02f, %.02f, %.02f" % (gCamera.m_ptIntersect.x, gCamera.m_ptIntersect.y, gCamera.m_ptIntersect.z);
246
        gFont.glPrintf(10, 660, 1, String);
247

    
248
        # // Print out the vector that points from the light source to the camera
249
        String = "vLightSourceToCamera... = %.02f, %.02f, %.02f" % (gCamera.vLightSourceToCamera.i, gCamera.vLightSourceToCamera.j, gCamera.vLightSourceToCamera.k);
250
        gFont.glPrintf(10, 640, 1, String);
251

    
252
        # // Print out the vector that points from the light source to the intersection point.
253
        String = "vLightSourceToIntersect = %.02f, %.02f, %.02f" % (gCamera.vLightSourceToIntersect.i, gCamera.vLightSourceToIntersect.j, gCamera.vLightSourceToIntersect.k);
254
        gFont.glPrintf(10, 620, 1, String);
255

    
256
        # // Let everyone know the below matrix is the model view matrix
257
        String = "GL_MODELVIEW_MATRIX";
258
        gFont.glPrintf(10, 580, 1, String);
259
        
260
        # // Print out row 1 of the model view matrix
261
        String = "%.02f, %.02f, %.02f, %.02f" % (modelMatrix[0][0], modelMatrix[0][1], modelMatrix[0][2], modelMatrix[0][3]);
262
        gFont.glPrintf(10, 560, 1, String);
263

    
264
        # // Print out row 2 of the model view matrix
265
        String = "%.02f, %.02f, %.02f, %.02f" % (modelMatrix[1][0], modelMatrix[1][1], modelMatrix[1][2], modelMatrix[1][3]);
266
        gFont.glPrintf(10, 540, 1, String);
267

    
268
        # // Print out row 3 of the model view matrix
269
        String = "%.02f, %.02f, %.02f, %.02f" % (modelMatrix[2][0], modelMatrix[2][1], modelMatrix[2][2], modelMatrix[2][3]);
270
        gFont.glPrintf(10, 520, 1, String);
271

    
272
        # // Print out row 4 of the model view matrix
273
        String = "%.02f, %.02f, %.02f, %.02f" % (modelMatrix[3][0], modelMatrix[3][1], modelMatrix[3][2], modelMatrix[3][3]);
274
        gFont.glPrintf(10, 500, 1, String);
275

    
276
        # // Let everyone know the below matrix is the projection matrix
277
        String = "GL_PROJECTION_MATRIX";
278
        gFont.glPrintf(10, 460, 1, String);
279
        
280
        # // Print out row 1 of the projection view matrix
281
        String = "%.02f, %.02f, %.02f, %.02f" % (projMatrix[0][0], projMatrix[0][1], projMatrix[0][2], projMatrix[0][3]);
282
        gFont.glPrintf(10, 440, 1, String);
283

    
284
        # // Print out row 2 of the projection view matrix
285
        String = "%.02f, %.02f, %.02f, %.02f" % (projMatrix[1][0], projMatrix[1][1], projMatrix[1][2], projMatrix[1][3]);
286
        gFont.glPrintf(10, 420, 1, String);
287

    
288
        # // Print out row 3 of the projection view matrix
289
        String = "%.02f, %.02f, %.03f, %.03f" % (projMatrix[2][0], projMatrix[2][1], projMatrix[2][2], projMatrix[2][3]);
290
        gFont.glPrintf(10, 400, 1, String);
291

    
292
        # // Print out row 4 of the projection view matrix
293
        String = "%.02f, %.02f, %.03f, %.03f" % (projMatrix[3][0], projMatrix[3][1], projMatrix[3][2], projMatrix[3][3]);
294
        gFont.glPrintf(10, 380, 1, String);
295

    
296
        # // Let everyone know the below values are the Frustum clipping planes
297
        gFont.glPrintf(10, 320, 1, "FRUSTUM CLIPPING PLANES");
298

    
299
        # // Print out the right clipping plane
300
        String = "%.02f, %.02f, %.02f, %.02f" % (gCamera.m_Frustum[0][0], gCamera.m_Frustum[0][1], gCamera.m_Frustum[0][2], gCamera.m_Frustum[0][3]);
301
        gFont.glPrintf(10, 300, 1, String);
302

    
303
        # // Print out the left clipping plane
304
        String = "%.02f, %.02f, %.02f, %.02f" % (gCamera.m_Frustum[1][0], gCamera.m_Frustum[1][1], gCamera.m_Frustum[1][2], gCamera.m_Frustum[1][3]);
305
        gFont.glPrintf(10, 280, 1, String);
306

    
307
        # // Print out the bottom clipping plane
308
        String = "%.02f, %.02f, %.02f, %.02f" % (gCamera.m_Frustum[2][0], gCamera.m_Frustum[2][1], gCamera.m_Frustum[2][2], gCamera.m_Frustum[2][3]);
309
        gFont.glPrintf(10, 260, 1, String);
310

    
311
        # // Print out the top clipping plane
312
        String = "%.02f, %.02f, %.02f, %.02f" % (gCamera.m_Frustum[3][0], gCamera.m_Frustum[3][1], gCamera.m_Frustum[3][2], gCamera.m_Frustum[3][3]);
313
        gFont.glPrintf(10, 240, 1, String);
314

    
315
        # // Print out the far clipping plane
316
        String = "%.02f, %.02f, %.02f, %.02f" % (gCamera.m_Frustum[4][0], gCamera.m_Frustum[4][1], gCamera.m_Frustum[4][2], gCamera.m_Frustum[4][3]);
317
        gFont.glPrintf(10, 220, 1, String);
318

    
319
        # // Print out the near clipping plane
320
        String = "%.02f, %.02f, %.02f, %.02f" % (gCamera.m_Frustum[5][0], gCamera.m_Frustum[5][1], gCamera.m_Frustum[5][2], gCamera.m_Frustum[5][3]);
321
        gFont.glPrintf(10, 200, 1, String);
322

    
323
        if(gFrames >= 100):                                                                                        # // if we are due for another FPS update
324
                # gCurrentTime = win32api.GetTickCount ();                                        # // Get the current time
325
                gCurrentTime = time.clock ();                                                        # // Get the current time
326
                DiffTime = gCurrentTime - gStartTime;                                        # // Find the difference between the start and end times
327
                # gFPS = (gFrames / float (DiffTime)) * 1000.0;                                        # // Compute the FPS
328
                gFPS = (gFrames / float (DiffTime));                                        # // Compute the FPS
329
                gStartTime = gCurrentTime;                                                                # // Set the current start time to the current time
330
                gFrames = 1;                                                                                        # // Set the number of frames to 1
331
        else:
332
                gFrames += 1;                                                                                        # // We are not due to for another update so add one to the frame count
333
        
334
        # // Print out the FPS
335
        String = "FPS %.02f" % (gFPS);
336
        gFont.glPrintf(10, 160, 1, String);
337
        return
338

    
339

    
340
def DrawGLScene ():
341
        """ // Here's Where We Do All The Drawing """
342
        global gCamera, gcylList, ginfoOn
343

    
344
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);                # // Clear Screen And Depth Buffer
345
        glLoadIdentity();                                                                                # // Reset The Current Modelview Matrix
346

    
347
        # // We want our light source to be 50 units if front 
348
        # // of the camera all the time to make it look like 
349
        # // it is infinately far away from the camera. We only
350
        # // do this to the z coordinate because we want to see
351
        # // the flares adjust if we fly in a straight line.
352
        gCamera.m_LightSourcePos.z = gCamera.m_Position.z - 50.0;
353

    
354

    
355
        # //##################### NEW STUFF ##########################
356
        # // Draw our cylinder and make it "do something"
357
        # // Of course we do that BEFORE testing for occlusion
358
        # // We need our depth buffer to be filled to check against occluder objects
359
        glPushMatrix();
360
        glLoadIdentity();
361
        glTranslatef(0.0, 0.0, -20.0);
362
        # glRotatef(win32api.GetTickCount () / 50.0, 0.3, 0.0, 0.0);
363
        # glRotatef(win32api.GetTickCount () / 50.0, 0.0, 0.5, 0.0);
364
        glRotatef((time.clock () * 1000.0) / 50.0, 0.3, 0.0, 0.0);
365
        glRotatef((time.clock () * 1000.0) / 50.0, 0.0, 0.5, 0.0);
366
        glCallList(gcylList);
367
        glPopMatrix();
368

    
369
        gCamera.SetPrespective();                                                # // Set our perspective/oriention on the world
370
        gCamera.RenderLensFlare();                                                # // Render the lens flare
371
        gCamera.UpdateFrustumFaster();                                        # // Update the frustum as fast as possible.
372
        
373
        # // Check to see if info has been toggled by 1,2
374
        if (gInfoOn):
375
                DrawGLInfo();                                                                # // Info is on so draw the GL information.                                                                
376

    
377
        glutSwapBuffers()
378
        return True
379

    
380

    
381
# The function called whenever a key is pressed. Note the use of Python tuples to pass in: (key, x, y)  
382
def keyPressed(*args):
383
        global window, gCamera, gInfoOn, gFont, gcylList, qobj
384
        # If escape is pressed, kill everything.
385
        key = args [0]
386
        if key == ESCAPE:
387
                gFont.release ()
388
                gCamera.release ()
389
                gluDeleteQuadric (qobj)
390
                glDeleteLists (gcylList, 1)
391
                sys.exit ()
392

    
393
        if key == 'W' or key == 'w':
394
                gCamera.ChangePitch(-0.2);                                                # // Pitch the camera up 0.2 degrees
395

    
396
        if key == 'S' or key == 's':
397
                gCamera.ChangePitch(0.2);                                                # // Pitch the camera down 0.2 degrees
398
        
399
        if key == 'D' or key == 'd':
400
                gCamera.ChangeHeading(0.2);                                                # // Yaw the camera to the left
401
        
402
        if key == 'A' or key == 'a':
403
                gCamera.ChangeHeading(-0.2);                                        # // Yaw the camera to the right
404
        
405
        if key == 'Z' or key == 'z':
406
                gCamera.m_ForwardVelocity = 0.01;                                # // Start moving the camera forward 0.01 units every frame
407

    
408
        if key == 'C' or key == 'c':
409
                gCamera.m_ForwardVelocity = -0.01;                                # // Start moving the camera backwards 0.01 units every frame
410
        
411
        if key == 'X' or key == 'x':
412
                gCamera.m_ForwardVelocity = 0.0;                                # // Stop the camera from moving.
413

    
414
        if args[0] == '1':
415
                gInfoOn = True;                                                                        # // Toggle info on
416
        
417
        if args[0] == '2':
418
                gInfoOn = False;                                                                # // Toggle info off
419

    
420

    
421
def main():
422
        global window
423
        # pass arguments to init
424
        glutInit(sys.argv)
425

    
426
        # Select type of Display mode:   
427
        #  Double buffer 
428
        #  RGBA color
429
        # Alpha components supported 
430
        # Depth buffer
431
        glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)
432
        
433
        # get a 640 x 480 window 
434
        glutInitWindowSize(640, 480)
435
        
436
        # the window starts at the upper left corner of the screen 
437
        glutInitWindowPosition(0, 0)
438
        
439
        # Okay, like the C version we retain the window id to use when closing, but for those of you new
440
        # to Python, remember this assignment would make the variable local and not global
441
        # if it weren't for the global declaration at the start of main.
442
        window = glutCreateWindow("Lens Flare Tutorial")
443

    
444
        # Register the drawing function with glut, BUT in Python land, at least using PyOpenGL, we need to
445
        # set the function pointer and invoke a function to actually register the callback, otherwise it
446
        # would be very much like the C version of the code.        
447
        glutDisplayFunc(DrawGLScene)
448
        
449
        # Uncomment this line to get full screen.
450
        #glutFullScreen()
451

    
452
        # When we are doing nothing, redraw the scene.
453
        glutIdleFunc(DrawGLScene)
454
        
455
        # Register the function called when our window is resized.
456
        glutReshapeFunc(ReSizeGLScene)
457
        
458
        # Register the function called when the keyboard is pressed.  
459
        glutKeyboardFunc(keyPressed)
460

    
461
        # We've told Glut the type of window we want, and we've told glut about
462
        # various functions that we want invoked (idle, resizing, keyboard events).
463
        # Glut has done the hard work of building up thw windows DC context and 
464
        # tying in a rendering context, so we are ready to start making immediate mode
465
        # GL calls.
466
        # Call to perform inital GL setup (the clear colors, enabling modes, and most releveant -
467
        # consturct the displays lists for the bitmap font.
468
        InitGL(640, 480)
469

    
470
        # Start Event Processing Engine        
471
        glutMainLoop()
472

    
473
# Print message to console, and kick off the main to get it rolling.
474
if __name__ == "__main__":
475
        print "Hit ESC key to quit."
476
        main()
477