<gh_stars>1-10
# /*******************************************
# *                                          *
# *   <NAME>'s Vertex Array Example     *
# *           nehe.gamedev.net               *
# *                2003                      *
# *                                          *
# *******************************************/
#
#
# NeHe Tutorial Lesson: 45 - Vertex Buffer Objects
#
# Ported to PyOpenGL 2.0 by <NAME> 2004
#
# This code was created by <NAME> 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 (<NAME> <EMAIL>).
#
# 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 *


from PIL import Image
import sys
# import win32api                # GetTickCount
import time
from numpy import zeros
# Note Yet Supported
from OpenGL.GL.ARB.vertex_buffer_object import *
# http://oss.sgi.com/projects/ogl-sample/registry/ARB/vertex_buffer_object.txt


# Some api in the chain is translating the keystrokes to this octal string
# so instead of saying: ESCAPE = 27, we use the following.
ESCAPE = b'\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

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

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

        self.m_nTextureId = None                                # // Texture ID

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

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

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

        # // Error-Checking
        # // Load Texture Data
        try:
            # // Open The Image
            self.m_pTextureImage = Image.open(szPath)
        except:
            traceback.print_exc()
            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 = zeros ((self.m_nVertexCount * 3), 'f')             # // Vertex Data
        # Non strings approach
        self.m_pVertices = zeros(
            (self.m_nVertexCount, 3), 'f')             # // Vertex Data
        # // Texture Coordinates
        self.m_pTexCoords = zeros((self.m_nVertexCount, 2), 'f')

        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 range(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.tobytes()
        self.m_pTexCoords_as_string = self.m_pTexCoords.tobytes()

        # // Load The Texture Into OpenGL
        self.m_nTextureID = glGenTextures(
            1)                        # // Get An Open ID
        # // Bind The Texture
        glBindTexture(GL_TEXTURE_2D, self.m_nTextureID)
        glTexImage2D(GL_TEXTURE_2D, 0, 3, sizeX, sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
                     self.m_pTextureImage.tobytes("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):
            # // Get A Valid Name
            self.m_nVBOVertices = int(glGenBuffersARB(1))
            # // Bind The Buffer
            glBindBufferARB(GL_ARRAY_BUFFER_ARB, self.m_nVBOVertices)
            # // Load The Data
            glBufferDataARB(GL_ARRAY_BUFFER_ARB,
                            self.m_pVertices, GL_STATIC_DRAW_ARB)

            # // Generate And Bind The Texture Coordinate Buffer
            # // Get A Valid Name
            self.m_nVBOTexCoords = int(glGenBuffersARB(1))
            # // Bind The Buffer
            glBindBufferARB(GL_ARRAY_BUFFER_ARB, self.m_nVBOTexCoords)
            # // Load The Data
            glBufferDataARB(GL_ARRAY_BUFFER_ARB,
                            self.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)

    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.ARB.%s" % (extension_name)

    import traceback
    try:
        __import__(extension_module_name)
        print("PyOpenGL supports '%s'" % (TargetExtension))
    except:
        traceback.print_exc()
        print('Failed to import', extension_module_name)
        print("OpenGL rendering context supports '%s'" % TargetExtension)
        return False

    return True


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

    # // Check for VBOs Supported
    g_fVBOSupported = IsExtensionSupported("GL_ARB_vertex_buffer_object")
    # // 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')
        glutDestroyWindow(window)
        return False

    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')
            glutDestroyWindow(window)
            return False
        # Now we can call to gl*Buffer* ()
        # glGenBuffersARB
        # glBindBufferARB
        # glBufferDataARB
        # glDeleteBuffersARB
        g_pMesh.BuildVBOs()

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

    # // Return TRUE (Initialization Successful)
    return True


# 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

    # Reset The Current Viewport And Perspective Transformation
    glViewport(0, 0, Width, Height)
    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

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

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

        # // Build The Title String
        szTitle = "Lesson 45: NeHe & <NAME>'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"

        # // Set The Title
        glutSetWindowTitle(szTitle)

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

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

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

    # // Set Pointers To Our Data
    if(g_fVBOSupported):
        #        import pdb
        #        pdb.set_trace()
        glBindBufferARB(GL_ARRAY_BUFFER_ARB, g_pMesh.m_nVBOVertices)
        # // Set The Vertex Pointer To The Vertex Buffer
        glVertexPointer(3, GL_FLOAT, 0, None)
        glBindBufferARB(GL_ARRAY_BUFFER_ARB, g_pMesh.m_nVBOTexCoords)
        # // Set The TexCoord Pointer To The TexCoord Buffer
        glTexCoordPointer(2, GL_FLOAT, 0, None)
    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 numpy 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).
        # // Set The Vertex Pointer To Our Vertex Data
        glVertexPointer(3, GL_FLOAT, 0, g_pMesh.m_pVertices_as_string)
        # // Set The Vertex Pointer To Our TexCoord Data
        glTexCoordPointer(2, GL_FLOAT, 0, g_pMesh.m_pTexCoords_as_string)

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

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

    # // Flush The GL Rendering Pipeline
    glutSwapBuffers()
    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:
        glutDestroyWindow(window)


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 & <NAME>'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)

    # Print message to console, and kick off the main to get it rolling.
    print('Hit ESC key to quit.')

    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()


if __name__ == "__main__":
    main()

# /*******************************************
# *                                          *
# *   <NAME>'s Vertex Array Example     *
# *           nehe.gamedev.net               *
# *                2003                      *
# *                                          *
# *******************************************/
#
#
# NeHe Tutorial Lesson: 45 - Vertex Buffer Objects
#
# Ported to PyOpenGL 2.0 by <NAME> 2004
#
# This code was created by <NAME> 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 (<NAME> <EMAIL>).
#
# 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 *


from PIL import Image
import sys
# import win32api                # GetTickCount
import time
from numpy import zeros
# Note Yet Supported
from OpenGL.GL.ARB.vertex_buffer_object import *
# http://oss.sgi.com/projects/ogl-sample/registry/ARB/vertex_buffer_object.txt


# Some api in the chain is translating the keystrokes to this octal string
# so instead of saying: ESCAPE = 27, we use the following.
ESCAPE = b'\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

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

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

        self.m_nTextureId = None                                # // Texture ID

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

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

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

        # // Error-Checking
        # // Load Texture Data
        try:
            # // Open The Image
            self.m_pTextureImage = Image.open(szPath)
        except:
            traceback.print_exc()
            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 = zeros ((self.m_nVertexCount * 3), 'f')             # // Vertex Data
        # Non strings approach
        self.m_pVertices = zeros(
            (self.m_nVertexCount, 3), 'f')             # // Vertex Data
        # // Texture Coordinates
        self.m_pTexCoords = zeros((self.m_nVertexCount, 2), 'f')

        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 range(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.tobytes()
        self.m_pTexCoords_as_string = self.m_pTexCoords.tobytes()

        # // Load The Texture Into OpenGL
        self.m_nTextureID = glGenTextures(
            1)                        # // Get An Open ID
        # // Bind The Texture
        glBindTexture(GL_TEXTURE_2D, self.m_nTextureID)
        glTexImage2D(GL_TEXTURE_2D, 0, 3, sizeX, sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
                     self.m_pTextureImage.tobytes("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):
            # // Get A Valid Name
            self.m_nVBOVertices = int(glGenBuffersARB(1))
            # // Bind The Buffer
            glBindBufferARB(GL_ARRAY_BUFFER_ARB, self.m_nVBOVertices)
            # // Load The Data
            glBufferDataARB(GL_ARRAY_BUFFER_ARB,
                            self.m_pVertices, GL_STATIC_DRAW_ARB)

            # // Generate And Bind The Texture Coordinate Buffer
            # // Get A Valid Name
            self.m_nVBOTexCoords = int(glGenBuffersARB(1))
            # // Bind The Buffer
            glBindBufferARB(GL_ARRAY_BUFFER_ARB, self.m_nVBOTexCoords)
            # // Load The Data
            glBufferDataARB(GL_ARRAY_BUFFER_ARB,
                            self.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)

    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.ARB.%s" % (extension_name)

    import traceback
    try:
        __import__(extension_module_name)
        print("PyOpenGL supports '%s'" % (TargetExtension))
    except:
        traceback.print_exc()
        print('Failed to import', extension_module_name)
        print("OpenGL rendering context supports '%s'" % TargetExtension)
        return False

    return True


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

    # // Check for VBOs Supported
    g_fVBOSupported = IsExtensionSupported("GL_ARB_vertex_buffer_object")
    # // 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')
        glutDestroyWindow(window)
        return False

    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')
            glutDestroyWindow(window)
            return False
        # Now we can call to gl*Buffer* ()
        # glGenBuffersARB
        # glBindBufferARB
        # glBufferDataARB
        # glDeleteBuffersARB
        g_pMesh.BuildVBOs()

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

    # // Return TRUE (Initialization Successful)
    return True


# 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

    # Reset The Current Viewport And Perspective Transformation
    glViewport(0, 0, Width, Height)
    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

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

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

        # // Build The Title String
        szTitle = "Lesson 45: NeHe & <NAME>'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"

        # // Set The Title
        glutSetWindowTitle(szTitle)

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

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

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

    # // Set Pointers To Our Data
    if(g_fVBOSupported):
        #        import pdb
        #        pdb.set_trace()
        glBindBufferARB(GL_ARRAY_BUFFER_ARB, g_pMesh.m_nVBOVertices)
        # // Set The Vertex Pointer To The Vertex Buffer
        glVertexPointer(3, GL_FLOAT, 0, None)
        glBindBufferARB(GL_ARRAY_BUFFER_ARB, g_pMesh.m_nVBOTexCoords)
        # // Set The TexCoord Pointer To The TexCoord Buffer
        glTexCoordPointer(2, GL_FLOAT, 0, None)
    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 numpy 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).
        # // Set The Vertex Pointer To Our Vertex Data
        glVertexPointer(3, GL_FLOAT, 0, g_pMesh.m_pVertices_as_string)
        # // Set The Vertex Pointer To Our TexCoord Data
        glTexCoordPointer(2, GL_FLOAT, 0, g_pMesh.m_pTexCoords_as_string)

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

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

    # // Flush The GL Rendering Pipeline
    glutSwapBuffers()
    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:
        glutDestroyWindow(window)


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 & <NAME>'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)

    # Print message to console, and kick off the main to get it rolling.
    print('Hit ESC key to quit.')

    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()


if __name__ == "__main__":
    main()
# /******************************************* # *                                          * # *   <NAME>'s Vertex Array Example     * # *           nehe.gamedev.net               * # *                2003                      * # *                                          * # *******************************************/ # # # NeHe Tutorial Lesson: 45 - Vertex Buffer Objects # # Ported to PyOpenGL 2.0 by <NAME> 2004 # # This code was created by <NAME> 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 (<NAME> <EMAIL>). # # 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 win32api                # GetTickCount # Note Yet Supported # http://oss.sgi.com/projects/ogl-sample/registry/ARB/vertex_buffer_object.txt # Some api in the chain is translating the keystrokes to this octal string # so instead of saying: ESCAPE = 27, we use the following. # Number of the glut window. # PyOpenGL doesn't yet have the ARB for vertex_buffer_objects # ARB_vertex_buffer_object supported? # Mesh Data # Rotation # FPS and FPS Counter # Last FPS Check Time Vertex Class # X Component # Y Component # Z Component # The Definitions Are Synonymous // Texture Coordinate Class # U Component # V Component Mesh Data # // Vertex Count # Numeric.array ( (), 'f')         # // Vertex Data array # raw memory string for VertexPointer () # Numeric.array ( (), 'f')     # // Texture Coordinates array # raw memory string for TexPointer () # // Texture ID # // Vertex Buffer Object Names # // Vertex VBO Name # // Texture Coordinate VBO Name # // Temporary Data # // Heightmap Data Heightmap Loader # // Error-Checking # // Load Texture Data # // Open The Image # // Generate Vertex Field # self.m_pVertices = zeros ((self.m_nVertexCount * 3), 'f')             # // Vertex Data # Non strings approach # // Vertex Data # // Texture Coordinates # Using This Quick Hack, Figure The X,Z Position Of The #  Point. # // Load The Texture Into OpenGL # // Get An Open ID # // Bind The Texture # // Free The Texture Data Calculate The Position In The Texture, Careful Not To Overflow # 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 # // Calculate The Height Using The Luminance Algorithm Generate And Bind The Vertex Buffer # // Get A Valid Name # // Bind The Buffer # // Load The Data # // Generate And Bind The Texture Coordinate Buffer # // Get A Valid Name # // Bind The Buffer # // Load The Data # // Our Copy Of The Data Is No Longer Necessary, It Is Safe In The Graphics Card 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. # 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 * # Doesn't appear to following extension naming convention. # Don't have a means to find a module for this exentsion type. # extension name after GL_ # Doesn't appear to following extension naming convention. # Don't have a means to find a module for this exentsion type. # // Any GL Init Code & User Initialiazation Goes Here # We call this right after our OpenGL window is created. # // Check for VBOs Supported # // TUTORIAL # // Load The Mesh Data # // 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. # Now we can call to gl*Buffer* () # glGenBuffersARB # glBindBufferARB # glBufferDataARB # glDeleteBuffersARB # Setup GL States # // Black Background # // Depth Buffer Setup # // The Type Of Depth Testing # // Enable Depth Testing # // Select Smooth Shading # // Set Perspective Calculations To Most Accurate # // Enable Texture Mapping # // Return TRUE (Initialization Successful) # The function called when our window is resized (which shouldn't happen if you enable fullscreen, below) # Prevent A Divide By Zero If The Window Is Too Small # Reset The Current Viewport And Perspective Transformation # // field of view, aspect ratio, near and far # This will squash and stretch our objects as the window is resized. # The main drawing function. # // Clear Screen And Depth Buffer # Reset The Modelview Matrix # // Get FPS # milliseconds = win32api.GetTickCount() # When A Second Has Passed... # g_dwLastFPS = win32api.GetTickCount()  # Update Our Time Variable # Save The FPS # Reset The FPS Counter # // Build The Title String # Include A Notice About VBOs # // Set The Title # // Increment Our FPS Counter # // Consistantly Rotate The Scenery # // Move The Camera # // Move Above The Terrain # // Look Down Slightly # // Rotate The Camera # // Enable Pointers # // Enable Vertex Arrays # // Enable Texture Coord Arrays # // Set Pointers To Our Data #        import pdb #        pdb.set_trace() # // Set The Vertex Pointer To The Vertex Buffer # // Set The TexCoord Pointer To The TexCoord Buffer # 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 numpy 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). # // Set The Vertex Pointer To Our Vertex Data # // Set The Vertex Pointer To Our TexCoord Data # // Render # // Draw All Of The Triangles At Once # // Disable Pointers # // Disable Vertex Arrays # // Disable Texture Coord Arrays # // Flush The GL Rendering Pipeline # The function called whenever a key is pressed. Note the use of Python tuples to pass in: (key, x, y) # If escape is pressed, kill everything. # pass arguments to init # Select type of Display mode: #  Double buffer #  RGBA color # Alpha components supported # Depth buffer # get a 640 x 480 window # the window starts at the upper left corner of the screen # 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. # 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. # Uncomment this line to get full screen. # glutFullScreen() # When we are doing nothing, redraw the scene. # Register the function called when our window is resized. # Register the function called when the keyboard is pressed. # The call setup glutSpecialFunc () is needed to receive # "keyboard function or directional keys." # Print message to console, and kick off the main to get it rolling. # 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. # Start Event Processing Engine