Statistics
| Revision:

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

History | View | Annotate | Download (13.2 kB)

1
# NeHe Tutorial Lesson: 41 - Volumetric Fog
2
#
3
# Ported to PyOpenGL 2.0 by Brian Leair 18 Jan 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 techniques.  
18
# It is a simple and direct exposition of how to use the Open GL API in 
19
# Python via the PyOpenGL package. It also uses GLUT, a high quality 
20
# platform independent library. Due to using these APIs, this code is 
21
# more like a C program using procedural based 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
# Make sure to get versions of Numeric, PyOpenGL, and PIL to match your
30
# version of python.
31
#
32
#
33
#
34
#
35
# Topics demonstrated in this tutorial:
36
#        using PIL (Python Image Library) to load a texture from an image file 
37
#                 (see doc - http://www.pythonware.com/library/pil/handbook/index.htm)
38
#        accessing the extension FOG_COORDINATE_EXTENSION
39
#                 (see doc - http://pyopengl.sourceforge.net/documentation/opengl_diffs.html)
40
#
41

    
42
from OpenGL.GL import *
43
from OpenGL.GLUT import *
44
from OpenGL.GLU import *
45
import Image                                 # PIL
46
import sys
47
from OpenGL.GL.EXT.fog_coord import *
48

    
49

    
50

    
51
# *********************** Globals *********************** 
52
# Python 2.2 defines these directly
53
try:
54
        True
55
except NameError:
56
        True = 1==1
57
        False = 1==0
58

    
59

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

    
64
# Number of the glut window.
65
window = 0
66
fogColor = (0.6, 0.3, 0.0, 1.0);                                                                # // Fog Colour 
67
camz = None                                                                                                                # // Camera Z Depth
68
lastTickCount = 0.0
69

    
70
texture = None
71

    
72
def next_p2 (num):
73
        """ If num isn't a power of 2, will return the next higher power of two """
74
        rval = 1
75
        while (rval<num):
76
                rval <<= 1
77
        return rval
78

    
79

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

    
95
        glMaxTexDim = glGetIntegerv (GL_MAX_TEXTURE_SIZE)
96

    
97
        WidthPixels = Picture.size [0]
98
        HeightPixels = Picture.size [1]
99

    
100
        if ((WidthPixels > glMaxTexDim) or (HeightPixels > glMaxTexDim)):
101
                # The image file is too large. Shrink it to fit within the texture dimensions
102
                # support by our rendering context for a GL texture.
103
                # Note, Feel free to experiemnt and force a resize by placing a small val into
104
                # glMaxTexDim (e.g. 32,64,128).
105
                if (WidthPixels > HeightPixels):
106
                        # Width is the domainant dimension.
107
                        resizeWidthPixels = glMaxTexDim
108
                        squash = float (resizeWidthPixels) / float (WidthPixels)
109
                        resizeHeightPixels = int (HeighPixels * squash)
110
                else:
111
                        resizeHeightPixels = glMaxTexDim
112
                        squash = float (resizeHeightPixels) / float (HeightPixels)
113
                        resizeWidthPixels = int (WidthPixels * squash)
114
        else:
115
                # // Resize Image To Closest Power Of Two
116
                if (WidthPixels > HeightPixels):
117
                        # Width is the domainant dimension.
118
                        resizeWidthPixels = next_p2 (WidthPixels)
119
                        squash = float (resizeWidthPixels) / float (WidthPixels)
120
                        resizeHeightPixels = int (HeighPixels * squash)
121
                else:
122
                        resizeHeightPixels = next_p2 (HeightPixels)
123
                        squash = float (resizeHeightPixels) / float (HeightPixels)
124
                        resizeWidthPixels = int (WidthPixels * squash)
125
                # 
126
        # Resize the image to be used as a texture.
127
        # The Python image library provides a handy method resize (). 
128
        # Several filtering options are available.
129
        # If you don't specify a filtering option will default NEAREST
130
        Picture = Picture.resize ((resizeWidthPixels, resizeHeightPixels), Image.BICUBIC)
131
        lWidthPixels = next_p2 (resizeWidthPixels)
132
        lHeightPixels = next_p2 (resizeWidthPixels)
133
        # Now we create an image that has the padding needed
134
        newpicture = Image.new ("RGB", (lWidthPixels, lHeightPixels), (0, 0, 0))
135
        newpicture.paste (Picture)
136

    
137
        # Create a raw string from the image data - data will be unsigned bytes
138
        # RGBpad, no stride (0), and first line is top of image (-1)
139
        pBits = newpicture.tostring("raw", "RGBX", 0, -1)
140

    
141
        # // Typical Texture Generation Using Data From The Bitmap
142
        texid = glGenTextures(1);                                                                                        # // Create The Texture
143
        glBindTexture(GL_TEXTURE_2D, texid);                                                                # // Bind To The Texture ID
144
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);                # // (Modify This For The Type Of Filtering You Want)
145
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);     # // (Modify This For The Type Of Filtering You Want)
146

    
147
        # // (Modify This If You Want Mipmaps)
148
        glTexImage2D(GL_TEXTURE_2D, 0, 3, lWidthPixels, lHeightPixels, 0, GL_RGBA, GL_UNSIGNED_BYTE, pBits);
149

    
150
        # Cleanup (python actually handles all memory for you, so this isn't necessary)
151
        # // Decrements IPicture Reference Count
152
        Picture = None
153
        newpicture = None
154
        return True, texid                                        # // Return True (All Good)
155

    
156

    
157
def Extension_Init ():
158
        """ Determine if the fog coord extentsion is availble """
159

    
160
        # After calling this, we will be able to invoke glFogCoordEXT ()
161
        if (not glInitFogCoordEXT ()):
162
                print "Help!  No GL_EXT_ForCoord"
163
                sys.exit(1)
164
                return False
165
        return True
166

    
167
# // Any GL Init Code & User Initialiazation Goes Here
168
def InitGL(Width, Height):                                # We call this right after our OpenGL window is created.
169
        global fogColor
170
        global camz
171

    
172
        if (not Extension_Init ()):                        # // Check And Enable Fog Extension If Available
173
                return False;                                        # // Return False If Extension Not Supported
174

    
175
        if (not BuildTexture("Wall.bmp")):                                                # // Load The Wall Texture
176
                return False;                                                                                        # // Return False If Loading Failed
177

    
178
        glEnable(GL_TEXTURE_2D);                                                                        # // Enable Texture Mapping
179
        glClearColor (0.0, 0.0, 0.0, 0.5);                                                        # // Black Background
180
        glClearDepth (1.0);                                                                                        # // Depth Buffer Setup
181
        glDepthFunc (GL_LEQUAL);                                                                        # // The Type Of Depth Testing
182
        glEnable (GL_DEPTH_TEST);                                                                        # // Enable Depth Testing
183
        glShadeModel (GL_SMOOTH);                                                                        # // Select Smooth Shading
184
        glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);                        # // Set Perspective Calculations To Most Accurate
185

    
186
        # // Set Up Fog 
187
        glEnable(GL_FOG);                                                                                        # // Enable Fog
188
        glFogi(GL_FOG_MODE, GL_LINEAR);                                                                # // Fog Fade Is Linear
189
        glFogfv(GL_FOG_COLOR, fogColor);                                                        # // Set The Fog Color
190
        glFogf(GL_FOG_START,  0.0);                                                                        # // Set The Fog Start
191
        glFogf(GL_FOG_END,    1.0);                                                                        # // Set The Fog End
192
        glHint(GL_FOG_HINT, GL_NICEST);                                                                # // Per-Pixel Fog Calculation
193
        glFogi(GL_FOG_COORDINATE_SOURCE_EXT, GL_FOG_COORDINATE_EXT) # // Set Fog Based On Vertice Coordinates
194

    
195
        camz =        -19.0;                                                                                                # // Set Camera Z Position To -19.0f
196

    
197
        return True;                                                                                                # // Return TRUE (Initialization Successful)
198

    
199

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

    
205
        glViewport(0, 0, Width, Height)                # Reset The Current Viewport And Perspective Transformation
206
        glMatrixMode(GL_PROJECTION)
207
        glLoadIdentity()
208
        # // field of view, aspect ratio, near and far
209
        # This will squash and stretch our objects as the window is resized.
210
        gluPerspective(45.0, float(Width)/float(Height), 0.1, 100.0)
211

    
212
        glMatrixMode(GL_MODELVIEW)
213
        glLoadIdentity()
214

    
215

    
216
# The main drawing function. 
217
def DrawGLScene():
218
        global camz
219

    
220
        glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);                                # // Clear Screen And Depth Buffer
221
        glLoadIdentity ();                                                                                                        # // Reset The Modelview Matrix
222

    
223
        glTranslatef(0.0, 0.0, camz);                                                                                # // Move To Our Camera Z Position
224

    
225
        glBegin(GL_QUADS);                                                                                                        # // Back Wall
226
        glFogCoordfEXT( 1.0);        glTexCoord2f(0.0, 0.0);        glVertex3f(-2.5,-2.5,-15.0);
227
        glFogCoordfEXT( 1.0);        glTexCoord2f(1.0, 0.0);        glVertex3f( 2.5,-2.5,-15.0);
228
        glFogCoordfEXT( 1.0);        glTexCoord2f(1.0, 1.0);        glVertex3f( 2.5, 2.5,-15.0);
229
        glFogCoordfEXT( 1.0);        glTexCoord2f(0.0, 1.0);        glVertex3f(-2.5, 2.5,-15.0);
230
        # PyOpenGL 2.0.1.07 has a bug. Swig generated code for FogCoordfExt ()
231
        # uses wrong error check macro. Macro falsely sets exception due to
232
        # call during glBegin (). Fixed in later versions.
233
        try:
234
                glEnd();
235
        except:
236
                pass
237

    
238
        glBegin(GL_QUADS);                                                                                                        # // Floor
239
        glFogCoordfEXT( 1.0);        glTexCoord2f(0.0, 0.0);        glVertex3f(-2.5,-2.5,-15.0);
240
        glFogCoordfEXT( 1.0);        glTexCoord2f(1.0, 0.0);        glVertex3f( 2.5,-2.5,-15.0);
241
        glFogCoordfEXT( 0.0);        glTexCoord2f(1.0, 1.0);        glVertex3f( 2.5,-2.5, 15.0);
242
        glFogCoordfEXT( 0.0);        glTexCoord2f(0.0, 1.0);        glVertex3f(-2.5,-2.5, 15.0);
243
        try:
244
                glEnd();
245
        except:
246
                pass
247

    
248
        glBegin(GL_QUADS);                                                                                                        # // Roof
249
        glFogCoordfEXT( 1.0);        glTexCoord2f(0.0, 0.0);        glVertex3f(-2.5, 2.5,-15.0);
250
        glFogCoordfEXT( 1.0);        glTexCoord2f(1.0, 0.0);        glVertex3f( 2.5, 2.5,-15.0);
251
        glFogCoordfEXT( 0.0);        glTexCoord2f(1.0, 1.0);        glVertex3f( 2.5, 2.5, 15.0);
252
        glFogCoordfEXT( 0.0);        glTexCoord2f(0.0, 1.0);        glVertex3f(-2.5, 2.5, 15.0);
253
        try:
254
                glEnd();
255
        except:
256
                pass
257

    
258
        glBegin(GL_QUADS);                                                                                                        # // Right Wall
259
        glFogCoordfEXT( 0.0);        glTexCoord2f(0.0, 0.0);        glVertex3f( 2.5,-2.5, 15.0);
260
        glFogCoordfEXT( 0.0);        glTexCoord2f(0.0, 1.0);        glVertex3f( 2.5, 2.5, 15.0);
261
        glFogCoordfEXT( 1.0);        glTexCoord2f(1.0, 1.0);        glVertex3f( 2.5, 2.5,-15.0);
262
        glFogCoordfEXT( 1.0);        glTexCoord2f(1.0, 0.0);        glVertex3f( 2.5,-2.5,-15.0);
263
        try:
264
                glEnd();
265
        except:
266
                pass
267

    
268
        glBegin(GL_QUADS);                                                                                                        # // Left Wall
269
        glFogCoordfEXT( 0.0);        glTexCoord2f(0.0, 0.0);        glVertex3f(-2.5,-2.5, 15.0);
270
        glFogCoordfEXT( 0.0);        glTexCoord2f(0.0, 1.0);        glVertex3f(-2.5, 2.5, 15.0);
271
        glFogCoordfEXT( 1.0);        glTexCoord2f(1.0, 1.0);        glVertex3f(-2.5, 2.5,-15.0);
272
        glFogCoordfEXT( 1.0);        glTexCoord2f(1.0, 0.0);        glVertex3f(-2.5,-2.5,-15.0);
273
        try:
274
                glEnd();
275
        except:
276
                pass
277

    
278
        glutSwapBuffers()                                                                                                         # // Flush The GL Rendering Pipeline
279
        return True
280

    
281

    
282
# The function called whenever a key is pressed. Note the use of Python tuples to pass in: (key, x, y)  
283
def keyPressed(*args):
284
        global window
285
        global camz
286
        global lastTickCount
287

    
288
        tickCount = glutGet (GLUT_ELAPSED_TIME)
289
        milliseconds = (tickCount - lastTickCount) 
290
        lastTickCount = tickCount
291
        if (milliseconds > 200):
292
                lastTickCount = tickCount
293
                milliseconds = 20
294

    
295
        # If escape is pressed, kill everything.
296
        if args[0] == ESCAPE:
297
                sys.exit ()
298
        if ((args[0] == GLUT_KEY_UP) and (camz < 14.0)):
299
                        camz += milliseconds / 100.0                 # // Move Object Closer (Move Forwards Through Hallway)
300
        if (args[0] == GLUT_KEY_DOWN and camz > -19.0):
301
                        camz -= milliseconds / 100.0                 # // Move Object Closer (Move Backwards Through Hallway)
302

    
303
        return
304

    
305
def main():
306
        global window, lastTickCount
307

    
308
        # pass arguments to init
309
        glutInit(sys.argv)
310

    
311
        # Select type of Display mode:   
312
        #  Double buffer 
313
        #  RGBA color
314
        # Alpha components supported 
315
        # Depth buffer
316
        glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)
317
        
318
        # get a 640 x 480 window 
319
        glutInitWindowSize(640, 480)
320
        
321
        # the window starts at the upper left corner of the screen 
322
        glutInitWindowPosition(0, 0)
323
        
324
        # Okay, like the C version we retain the window id to use when closing, but for those of you new
325
        # to Python, remember this assignment would make the variable local and not global
326
        # if it weren't for the global declaration at the start of main.
327
        window = glutCreateWindow("NeHe's Volumetric Fog & IPicture Image Loading Tutorial")
328

    
329
        # Register the drawing function with glut, BUT in Python land, at least using PyOpenGL, we need to
330
        # set the function pointer and invoke a function to actually register the callback, otherwise it
331
        # would be very much like the C version of the code.        
332
        glutDisplayFunc(DrawGLScene)
333
        
334
        # Uncomment this line to get full screen.
335
        #glutFullScreen()
336

    
337
        # When we are doing nothing, redraw the scene.
338
        glutIdleFunc(DrawGLScene)
339
        
340
        # Register the function called when our window is resized.
341
        glutReshapeFunc(ReSizeGLScene)
342
        
343
        # Register the function called when the keyboard is pressed.  
344
        # The call setup glutSpecialFunc () is needed to receive 
345
        # "keyboard function or directional keys." 
346
        glutKeyboardFunc(keyPressed)
347
        glutSpecialFunc(keyPressed)
348

    
349
        # We've told Glut the type of window we want, and we've told glut about
350
        # various functions that we want invoked (idle, resizing, keyboard events).
351
        # Glut has done the hard work of building up thw windows DC context and 
352
        # tying in a rendering context, so we are ready to start making immediate mode
353
        # GL calls.
354
        # Call to perform inital GL setup (the clear colors, enabling modes, and most releveant -
355
        # consturct the displays lists for the bitmap font.
356
        InitGL(640, 480)
357

    
358
        # Start Event Processing Engine        
359
        glutMainLoop()
360

    
361
# Print message to console, and kick off the main to get it rolling.
362
if __name__ == "__main__":
363
        print "Hit ESC key to quit."
364
        main()