lesson45.py :  » Development » PyObjC » trunk » pyobjc » PyOpenGL-2.0.2.01 » OpenGL » Demo » NeHe » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Development » PyObjC 
PyObjC » trunk » pyobjc » PyOpenGL 2.0.2.01 » OpenGL » Demo » NeHe » lesson45.py
# /*******************************************
# *                                          *
# *   Paul Frazee's Vertex Array Example     *
# *           nehe.gamedev.net               *
# *                2003                      *
# *                                          *
# *******************************************/
#
#
# NeHe Tutorial Lesson: 45 - Vertex Buffer Objects
#
# Ported to PyOpenGL 2.0 by Brian Leair 2004
#
# This code was created by Jeff Molofee 2000
#
# The port was based on the PyOpenGL tutorials and from 
# PyOpenGLContext (tests/glprint.py)
#
# If you've found this code useful, feel free to let me know 
# at (Brian Leair telcom_sage@yahoo.com).
#
# See original source and C based tutorial at http://nehe.gamedev.net
#
# Note:
# -----
# This code is not an ideal example of Pythonic coding or use of OO 
# techniques. It is a simple and direct exposition of how to use the 
# Open GL API in Python via the PyOpenGL package. It also uses GLUT, 
# a high quality platform independent library. Due to using these APIs, 
# this code is more like a C program using procedural programming.
#
# To run this example you will need:
# Python   - www.python.org (v 2.3 as of 1/2004)
# PyOpenGL   - pyopengl.sourceforge.net (v 2.0.1.07 as of 1/2004)
# Numeric Python  - (v.22 of "numpy" as of 1/2004) numpy.sourceforge.net
# Python Image Library  - http://www.pythonware.com/products/pil/
#
# Make sure to get versions of Numeric, PyOpenGL, and PIL to match your
# version of python.
#
#
#
# PyOpenGL extension wrapper for GL_ARB_vertex_buffer_object is currently
# only availabel through cvs (no release build yet). When a new release
# is made of PyOpenGL updating this tutorial should be pretty easy.
# For now, in place of VBOs, vertex/texcoord arrays are used.
#
#


import OpenGL
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
import Numeric

import Image         # PIL
import sys
# import win32api        # GetTickCount
import time        

# Note Yet Supported
# from OpenGL.GL.ARB.vertex_buffer_object import *
# http://oss.sgi.com/projects/ogl-sample/registry/ARB/vertex_buffer_object.txt




# *********************** Globals *********************** 
# Python 2.2 defines these directly
try:
  True
except NameError:
  True = 1==1
  False = 1==0


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

# Number of the glut window.
window = 0

# PyOpenGL doesn't yet have the ARB for vertex_buffer_objects
NO_VBOS = True

g_fVBOSupported = False;              # // ARB_vertex_buffer_object supported?
g_pMesh = None;                    # // Mesh Data
g_flYRot = 0.0;                    # // Rotation
g_nFPS = 0
g_nFrames = 0;                    # // FPS and FPS Counter
g_dwLastFPS = 0;                  # // Last FPS Check Time  




class CVert:
  """ // Vertex Class """
  def __init__ (self, x = 0.0, y = 0.0, z = 0.0):
    self.x = 0                      # // X Component
    self.y = 0                      # // Y Component
    self.z = 0                      # // Z Component

# // The Definitions Are Synonymous
CVec = CVert

class CTexCoord:
  """ // Texture Coordinate Class """
  def __init__ (self, u = 0.0, v = 0.0):
    self.u = u;                      # // U Component
    self.v = v;                      # // V Component

class CMesh:
  """ // Mesh Data """
  MESH_RESOLUTION = 4.0
  MESH_HEIGHTSCALE = 1.0

  def __init__ (self):
    self.m_nVertexCount = 0;                # // Vertex Count

    self.m_pVertices = None # Numeric.array ( (), 'f')     # // Vertex Data array
    self.m_pVertices_as_string = None            # raw memory string for VertexPointer ()

    self.m_pTexCoords = None # Numeric.array ( (), 'f')   # // Texture Coordinates array
    self.m_pTexCoords_as_string = None            # raw memory string for TexPointer ()

    self.m_nTextureId = None;                # // Texture ID

    # // Vertex Buffer Object Names
    self.m_nVBOVertices = None;                # // Vertex VBO Name
    self.m_nVBOTexCoords = None;              # // Texture Coordinate VBO Name

    # // Temporary Data
    self.m_pTextureImage = None;              # // Heightmap Data


  def LoadHeightmap( self, szPath, flHeightScale, flResolution ):
    """ // Heightmap Loader """

    # // Error-Checking
    # // Load Texture Data
    try:
      self.m_pTextureImage = Image.open (szPath)               # // Open The Image
    except:
      return False

    # // Generate Vertex Field
    sizeX = self.m_pTextureImage.size [0]
    sizeY = self.m_pTextureImage.size [1]
    self.m_nVertexCount = int ( sizeX * sizeY * 6 / ( flResolution * flResolution ) );
    # self.m_pVertices = Numeric.zeros ((self.m_nVertexCount * 3), 'f')       # // Vertex Data
    # Non strings approach
    self.m_pVertices = Numeric.zeros ((self.m_nVertexCount, 3), 'f')       # // Vertex Data
    self.m_pTexCoords = Numeric.zeros ((self.m_nVertexCount, 2), 'f')       # // Texture Coordinates

    nZ = 0
    nIndex = 0
    nTIndex = 0
    half_sizeX = float (sizeX) / 2.0
    half_sizeY = float (sizeY) / 2.0
    flResolution_int = int (flResolution)
    while (nZ < sizeY):
      nX = 0
      while (nX < sizeY):
        for nTri in xrange (6):
          # // Using This Quick Hack, Figure The X,Z Position Of The Point
          flX = float (nX)
          if (nTri == 1) or (nTri == 2) or (nTri == 5):
            flX += flResolution
          flZ = float (nZ)
          if (nTri == 2) or (nTri == 4) or (nTri == 5):
            flZ += flResolution
          x = flX - half_sizeX
          y = self.PtHeight (int (flX), int (flZ)) * flHeightScale
          z = flZ - half_sizeY
          self.m_pVertices [nIndex, 0] = x
          self.m_pVertices [nIndex, 1] = y
          self.m_pVertices [nIndex, 2] = z
          self.m_pTexCoords [nTIndex, 0] = flX / sizeX
          self.m_pTexCoords [nTIndex, 1] =  flZ / sizeY
          nIndex += 1
          nTIndex += 1

        nX += flResolution_int
      nZ += flResolution_int

    self.m_pVertices_as_string = self.m_pVertices.tostring () 
    self.m_pTexCoords_as_string = self.m_pTexCoords.tostring () 

    # // Load The Texture Into OpenGL
    self.m_nTextureID = glGenTextures (1)            # // Get An Open ID
    glBindTexture( GL_TEXTURE_2D, self.m_nTextureID );      # // Bind The Texture
    glTexImage2D( GL_TEXTURE_2D, 0, 3, sizeX, sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, 
      self.m_pTextureImage.tostring ("raw", "RGB", 0, -1))
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

    # // Free The Texture Data
    self.m_pTextureImage = None
    return True;

  def PtHeight (self, nX, nY):
    """ // Calculate The Position In The Texture, Careful Not To Overflow """
    sizeX = self.m_pTextureImage.size [0]
    sizeY = self.m_pTextureImage.size [1]
    if (nX >= sizeX or nY >= sizeY):
      return 0

    # Get The Red, Green, and Blue Components 
    # NOTE, Python Image library starts 0 at the top of the image - so to match the windows
    # code we reverse the Y order 
    pixel = self.m_pTextureImage.getpixel ((nX, sizeY - nY - 1))
    flR = float (pixel [0])
    flG = float (pixel [1])
    flB = float (pixel [2])
    pixel = self.m_pTextureImage.getpixel ((nY, nX))

    # // Calculate The Height Using The Luminance Algorithm
    return ( (0.299 * flR) + (0.587 * flG) + (0.114 * flB) );      


  def BuildVBOs (self):
    """ // Generate And Bind The Vertex Buffer """
    if (g_fVBOSupported):
      self.m_nVBOVertices = glGenBuffersARB( 1);            # // Get A Valid Name
      glBindBufferARB( GL_ARRAY_BUFFER_ARB, self.m_nVBOVertices );  # // Bind The Buffer
      # // Load The Data
      glBufferDataARB( GL_ARRAY_BUFFER_ARB, self.m_pVertices, GL_STATIC_DRAW_ARB );

      # // Generate And Bind The Texture Coordinate Buffer
      self.m_nVBOTexCoords = glGenBuffersARB( 1);            # // Get A Valid Name
      glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOTexCoords );    # // Bind The Buffer
      # // Load The Data
      glBufferDataARB( GL_ARRAY_BUFFER_ARB, m_pTexCoords, GL_STATIC_DRAW_ARB );

      # // Our Copy Of The Data Is No Longer Necessary, It Is Safe In The Graphics Card
      self.m_pVertices = None
      self.m_pVertices = None;
      self.m_pTexCoords = None
      self.m_pTexCoords = None;
    return









def IsExtensionSupported (TargetExtension):
  """ Accesses the rendering context to see if it supports an extension.
    Note, that this test only tells you if the OpenGL library supports
    the extension. The PyOpenGL system might not actually support the extension.
  """
  Extensions = glGetString (GL_EXTENSIONS)
  # python 2.3
  # if (not TargetExtension in Extensions):
  #  gl_supports_extension = False
  #  print "OpenGL does not support '%s'" % (TargetExtension)
  #  return False

  # python 2.2
  Extensions = Extensions.split ()
  found_extension = False
  for extension in Extensions:
    if extension == TargetExtension:
      found_extension = True
      break;
  if (found_extension == False):
    gl_supports_extension = False
    print "OpenGL rendering context does not support '%s'" % (TargetExtension)
    return False

  gl_supports_extension = True

  # Now determine if Python supports the extension
  # Exentsion names are in the form GL_<group>_<extension_name>
  # e.g.  GL_EXT_fog_coord 
  # Python divides extension into modules
  # g_fVBOSupported = IsExtensionSupported ("GL_ARB_vertex_buffer_object")
  # from OpenGL.GL.EXT.fog_coord import *
  if (TargetExtension [:3] != "GL_"):
    # Doesn't appear to following extension naming convention.
    # Don't have a means to find a module for this exentsion type.
    return False

  # extension name after GL_
  afterGL = TargetExtension [3:]
  try:
    group_name_end = afterGL.index ("_")
  except:
    # Doesn't appear to following extension naming convention.
    # Don't have a means to find a module for this exentsion type.
    return False

  group_name = afterGL [:group_name_end]
  extension_name = afterGL [len (group_name) + 1:]
  extension_module_name = "OpenGL.GL.%s" % (extension_name)

  try:
    __import__ (extension_module_name)
    print "PyOpenGL supports '%s'" % (TargetExtension)
  except:
    print "OpenGL rendering context supports '%s'" % (TargetExtension),
    print "however PyOpenGL (ver %s) does not." % (OpenGL.__version__)
    return False

  return True



# // Any GL Init Code & User Initialiazation Goes Here
def InitGL(Width, Height):        # We call this right after our OpenGL window is created.
  global g_pMesh

  # // TUTORIAL
  # // Load The Mesh Data
  g_pMesh = CMesh ()
  if (not g_pMesh.LoadHeightmap ("Terrain.bmp",
    CMesh.MESH_HEIGHTSCALE, CMesh.MESH_RESOLUTION)):
    print "Error Loading Heightmap"
    sys.exit(1)
    return False

  # // Check for VBOs Supported
  g_fVBOSupported = IsExtensionSupported ("GL_ARB_vertex_buffer_object")
  if (g_fVBOSupported):
    # // Get Pointers To The GL Functions
    # In python, calling Init for the extension functions will
    # fill in the function pointers (really function objects)
    # so that we call the Extension.

    if (not glInitVertexBufferObjectARB ()):
      print "Help!  No GL_ARB_vertex_buffer_object"
      sys.exit(1)
      return False
    # Now we can call to gl*Buffer* ()
    # glGenBuffersARB
    # glBindBufferARB
    # glBufferDataARB
    # glDeleteBuffersARB
    g_pMesh.BuildVBOs 


  # Setup GL States
  glClearColor (0.0, 0.0, 0.0, 0.5);              # // Black Background
  glClearDepth (1.0);                      # // Depth Buffer Setup
  glDepthFunc (GL_LEQUAL);                  # // The Type Of Depth Testing
  glEnable (GL_DEPTH_TEST);                  # // Enable Depth Testing
  glShadeModel (GL_SMOOTH);                  # // Select Smooth Shading
  glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);      # // Set Perspective Calculations To Most Accurate
  glEnable(GL_TEXTURE_2D);                  # // Enable Texture Mapping
  glColor4f (1.0, 6.0, 6.0, 1.0)

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


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

  glViewport(0, 0, Width, Height)    # Reset The Current Viewport And Perspective Transformation
  glMatrixMode(GL_PROJECTION)
  glLoadIdentity()
  # // field of view, aspect ratio, near and far
  # This will squash and stretch our objects as the window is resized.
  gluPerspective(45.0, float(Width)/float(Height), 1, 1000.0)

  glMatrixMode(GL_MODELVIEW)
  glLoadIdentity()


g_prev_draw_time = 0.0
# The main drawing function. 
def DrawGLScene():
  global g_dwLastFPS, g_nFPS, g_nFrames, g_pMesh, g_fVBOSupported, g_flYRot, g_prev_draw_time

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


  # // Get FPS
  # milliseconds = win32api.GetTickCount() 
  milliseconds = time.clock () * 1000.0
  if (milliseconds - g_dwLastFPS >= 1000):          # // When A Second Has Passed...
    # g_dwLastFPS = win32api.GetTickCount();        # // Update Our Time Variable
    g_dwLastFPS = time.clock () * 1000.0
    g_nFPS = g_nFrames;                    # // Save The FPS
    g_nFrames = 0;                      # // Reset The FPS Counter

    # // Build The Title String
    szTitle = "Lesson 45: NeHe & Paul Frazee's VBO Tut - %d Triangles, %d FPS" % (g_pMesh.m_nVertexCount / 3, g_nFPS );
    if ( g_fVBOSupported ):                  # // Include A Notice About VBOs
      szTitle = szTitle + ", Using VBOs";
    else:
      szTitle = szTitle + ", Not Using VBOs";

    glutSetWindowTitle ( szTitle );              # // Set The Title

  g_nFrames += 1                         # // Increment Our FPS Counter
  rot = (milliseconds - g_prev_draw_time) / 1000.0 * 25
  g_prev_draw_time = milliseconds
  g_flYRot += rot                       # // Consistantly Rotate The Scenery

  # // Move The Camera
  glTranslatef( 0.0, -220.0, 0.0 );              # // Move Above The Terrain
  glRotatef( 10.0, 1.0, 0.0, 0.0 );              # // Look Down Slightly
  glRotatef( g_flYRot, 0.0, 1.0, 0.0 );            # // Rotate The Camera

  # // Enable Pointers
  glEnableClientState( GL_VERTEX_ARRAY );            # // Enable Vertex Arrays
  glEnableClientState( GL_TEXTURE_COORD_ARRAY );        # // Enable Texture Coord Arrays


  # // Set Pointers To Our Data
  if( g_fVBOSupported ):
    glBindBufferARB( GL_ARRAY_BUFFER_ARB, g_pMesh.m_nVBOVertices );
    glVertexPointer( 3, GL_FLOAT, 0, None );        # // Set The Vertex Pointer To The Vertex Buffer
    glBindBufferARB( GL_ARRAY_BUFFER_ARB, g_pMesh.m_nVBOTexCoords );
    glTexCoordPointer( 2, GL_FLOAT, 0, None );        # // Set The TexCoord Pointer To The TexCoord Buffer
  else:
    # You can use the pythonism glVertexPointerf (), which will convert the numarray into 
    # the needed memory for VertexPointer. This has two drawbacks however:
    #  1) This does not work in Python 2.2 with PyOpenGL 2.0.0.44 
    #  2) In Python 2.3 with PyOpenGL 2.0.1.07 this is very slow.
    # See the PyOpenGL documentation. Section "PyOpenGL for OpenGL Programmers" for details
    # regarding glXPointer API.
    # Also see OpenGLContext Working with Numeric Python
    # glVertexPointerf ( g_pMesh.m_pVertices );   # // Set The Vertex Pointer To Our Vertex Data
    # glTexCoordPointerf ( g_pMesh.m_pTexCoords );   # // Set The Vertex Pointer To Our TexCoord Data
    #
    #
    # The faster approach is to make use of an opaque "string" that represents the
    # the data (vertex array and tex coordinates in this case).
    glVertexPointer( 3, GL_FLOAT, 0, g_pMesh.m_pVertices_as_string);    # // Set The Vertex Pointer To Our Vertex Data
    glTexCoordPointer( 2, GL_FLOAT, 0, g_pMesh.m_pTexCoords_as_string);   # // Set The Vertex Pointer To Our TexCoord Data


  # // Render
  glDrawArrays( GL_TRIANGLES, 0, g_pMesh.m_nVertexCount );    # // Draw All Of The Triangles At Once

  # // Disable Pointers
  glDisableClientState( GL_VERTEX_ARRAY );          # // Disable Vertex Arrays
  glDisableClientState( GL_TEXTURE_COORD_ARRAY );        # // Disable Texture Coord Arrays

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





# The function called whenever a key is pressed. Note the use of Python tuples to pass in: (key, x, y)  
def keyPressed(*args):
  global window

  # If escape is pressed, kill everything.
  if args[0] == ESCAPE:
    sys.exit ()
  return

def main():
  global window
  # pass arguments to init
  glutInit(sys.argv)

  # Select type of Display mode:   
  #  Double buffer 
  #  RGBA color
  # Alpha components supported 
  # Depth buffer
  glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)
  
  # get a 640 x 480 window 
  glutInitWindowSize(640, 480)
  
  # the window starts at the upper left corner of the screen 
  glutInitWindowPosition(0, 0)
  
  # Okay, like the C version we retain the window id to use when closing, but for those of you new
  # to Python, remember this assignment would make the variable local and not global
  # if it weren't for the global declaration at the start of main.
  window = glutCreateWindow("Lesson 45: NeHe & Paul Frazee's VBO Tut")

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

  # When we are doing nothing, redraw the scene.
  glutIdleFunc(DrawGLScene)
  
  # Register the function called when our window is resized.
  glutReshapeFunc(ReSizeGLScene)
  
  # Register the function called when the keyboard is pressed.  
  # The call setup glutSpecialFunc () is needed to receive 
  # "keyboard function or directional keys." 
  glutKeyboardFunc(keyPressed)
  glutSpecialFunc(keyPressed)

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

  # Start Event Processing Engine  
  glutMainLoop()

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


www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.