Search This Blog

2019-11-06

Basic Tutorial

Import Mesh ( .obj, .dae collada)
    1. Copy files to Resource/Externals/Meshes
    2. Run Application then import to the application automatically.
    3. select "Resource List" tab.
    4. You can see the imported mesh file


Load Scene
    1. select "Resource List" tab.
    2. Right click on scene item to open context menu
    3. Click "Load" in context menu
    4. Then loading the scene


Place model to the Scene
    1. select "Resource List" tab.
    2. select Model item what you want to place.
    3. Right Click to open context menu
    4. Click "Action" menu.


Play Game Mode
    1. Just Click "Play" button.

2019-02-19

Export Collada for PyEngine3D

Export Collada for PyEngine3D



Check
    - Include Amatures
    - All keyed curves

2019-02-01

Welcome to PyEngine3D

Welcome to PyEngine3D

Requirements

  • numpy
  • pillow
  • pyglet or pygame
  • PyOpenGL
  • PyOpenGL-accelerate ( optional )
  • Windows, Linux, Mac(not tested)

How to install PyEngine3D

  git clone https://github.com/ubuntunux/PyEngine3D
  cd PyEngine3D
  pip install -r requirements.txt
  python main.py

Features

  • Video : https://youtu.be/x9GVA7tCAdw
  • Import
    • Mesh
      •  .fbx, .obj, .dae ( colada )
      •  gltf, blender
    • Texture
      •  .png, .tga, .bmp etc
      •  Compressed Texture (ETC, DDS)
  • Light
    •  Directional light & Shadow mapping
    •  Spot light
    •  Area light
    •  Point light
      •  shadow map using dual paraboloid mapping
  • Particle System
    •  CPU Based Particle
    •  GPU Based Particle Screen Space Relfection
    •  Vector Field
    •  Particle spawn on polygon surface
    •  Bitonic Sorting
    •  Memory Pool
    •  Attractor
    •  Noise
    •  Curl Noise
  • Object
    •  Select, Move, Modify
    •  Gizmo
    •  Skeleton Mesh
    •  Static Mesh
    •  Tree, Foliage, Grass
    •  Terrain Screen Space Relfection
    •  Atmoshpere & Sky Screen Space RelfectionScreen Space RelfectionScreen Space RelfectionScreen Space RelfectionScreen Space Relfection
    •  Road
    •  Wind
    •  FFT Ocean Screen Space RelfectionScreen Space Relfection
    •  River
  • Rendering
    •  Culling
      •  occlusion culling
      •  distance culling
      •  view frustum culling
    •  VTF Skinning
    •  Calculate the animation in gpu
    •  Distance Field Font
    •  Real time light probe
    •  PBR Screen Space Relfection
    •  Temporal AA
    •  SSAA
    •  MSAA
    •  Temporal Upsacle
    •  Screen Space Relfection Screen Space Relfection
    •  Screen Space Ambient Occlusion
    •  Screen Space Bevel
    •  Screen Space SSS
    •  Depth Of Field
      •  Bokeh
    •  Bloom Screen Space Relfection
    •  Tone mapping
    •  Glare
    •  Film Grain
    •  Color Correction
    •  Color Grading
    •  Light Shaft Screen Space Relfection
    •  Motion Blur
      •  Recursive Velocity
    •  Parallax Occlusion Rendering
    •  Paraboloid environment map
    •  Voxel Based GI
    •  Volumtric Scattering
    •  Fur Rendering
  • Resource Manager
    •  Load / Save / Import / Export
    •  Unload / Reload system
    •  Duplicate resource
    •  Sound Manager
    •  Script Manager
  • Blender3D plugin
    •  transfer geometry, animation, scene datas
    •  edit animation, scene, sequence, particles in blender
  • InGame GUI
    •  input / output
    •  progress bar
    •  button
  • Optimize
    •  Only dynamic shadows are updated on every frame, and static shadows are not updated every time.
    •  SSR ray reuse in compute shader
    •  Postprocessing in compute shader
    •  FFT in compute shader
    •  Precomputed atmosphere in compute shader

References

- https://wickedengine.net/2017/11/07/gpu-based-particle-simulation/
- http://kode80.com/blog/2015/03/11/screen-space-reflections-in-unity-5/index.html
- http://proland.inrialpes.fr/publications.html
- http://casual-effects.com/data/index.html
- https://github.com/TheRealMJP/MSAAFilter
- http://pyopengl.sourceforge.net/
- https://learnopengl.com/
- http://www.songho.ca/index.html

2018-11-11

[Link] PyOpenGL for OpenGL Programmers

source text : http://pyopengl.sourceforge.net/documentation/opengl_diffs.html



PyOpenGL for OpenGL Programmers

This document describes those features of PyOpenGL which are likely to be unfamiliar to OpenGL programmers. It also explains various features of PyOpenGL which are not covered in general OpenGL documentation.

Speed Concerns

PyOpenGL 3.x is far slower than PyOpenGL 2.x, and PyOpenGL 2.x was not fast. Out of the box PyOpenGL 3.x is configured to be as helpful and robust as possible, both traits which make the system much slower than it could be. PyOpenGL is now implemented using ctypes, rather than SWIG, the flexibility of ctypes has a significant cost in terms of performance. PyOpenGL 3.x is also far more flexible with regard to data-types, that flexibility has a noticeable performance cost. Throughout this document you will see descriptions for how to trade off flexibility, helpfulness and robustness in order to claw back speed.
If you are attempting to do per-vertex operations (e.g. glColor, glNormal, glVertex) then you will find that PyOpenGL 3.x is ridiculously slow. Luckily, all of those features are now deprecated. For array-based geometry where the arrays are large and in native-format-compatible formats, PyOpenGL's overhead tends to be amortized over the size of the arrays being processed.

Method Signatures vs. C OpenGL

Most of the functions which appear in PyOpenGL 3 are identical in calling method and functionality to that of the appropriate C specification. There are a few exceptions because of the differences between C and Python. Most of these exceptions are due to the difference between C and Python in the way that they access arrays. For example, a C function like this:
void foo(int count, const int *args);
will normally have the Python binding:
foo(args) -> None
Also C functions which write array data to a function argument like:
void bar(int args[4]);
will normally have the Python binding:
bar() -> args[]
The following sections will document changes other than simple changes like the above examples.

Errors raise Exceptions

PyOpenGL generally uses "strict" OpenGL operation, which is closer to the operation of Python itself, i.e. errors are raised as exceptions, rather than requiring the user to check return values and/or call glCheckError manually.
You can disable PyOpenGL's error checking by setting a module-level flag in the OpenGL package before importing any of the sub-modules, like so:
import OpenGL
OpenGL.ERROR_CHECKING = False
from OpenGL.GL import *
from OpenGL.GLU import *
...
This will tend to cause a huge speed increase in your code, as the number of OpenGL calls issued will roughly halve compared to the error-checking version of the same script. You will normally want to develop with error-checking on and only switch it off for production/release code.  Note, however, that most PyOpenGL code (including helper code in PyOpenGL itself) is written with the assumption that errors raise exceptions, so you will have to review all code you use to be sure you are calling glGetError where appropriate.
The exceptions normally raised by PyOpenGL are:
  • GL.GLerror by all modules except WGL
  • GLU.GLUerror by some GLU functions. Note that GLU can also throw GL.GLerror
  • WindowsError/SystemError by WGL
  • General TypeError/ValueError errors for e.g. wrong-data-type problems
GL.GLerror is a subclass of GL.Error and has a large amount of extra information regarding the call wrapping process:
  • err -- OpenGL error code
  • result -- OpenGL result-code for the operation
  • baseOperation -- the base OpenGL function being called
  • pyArgs -- the set of top-level arguments passed to the function
  • cArgs -- the set of Python-level objects expanded from pyArgs
  • cArguments -- the set of C-level objects converted from cArgs
  • description -- textual OpenGL description of the error (normally pretty terse, useful primarily to look at the documentation)

Logging

By default, PyOpenGL will log errors to the Python logging module. You can disable this by setting a flag before importing the OpenGL sub-modules:
import OpenGL
OpenGL.ERROR_LOGGING = False
from OpenGL.GL import *
...
which will provide a performance improvement for non-development code.
Conversely, if you are finding that your code is crashing and you need to trace through the operation of PyOpenGL then it is possible to instruct PyOpenGL to trace the calls to OpenGL to the logging module:
import OpenGL
OpenGL.FULL_LOGGING = True
from OpenGL.GL import *
...
this is extremely slow, but often useful during the debugging process.

Wrapped Operations

Wrapped Python arguments may go through a number of possible wrapper operations in order to produce a final call from the Python argument-set:
  • takes a set of args and passes a set of converters over them to convert them to acceptable object types (pyArgs)
  • runs a set of cConverters over pyArgs to produce a set of cArgs, representing the Pythonic objects which map 1:1 to the C arguments (cArgs), this will often expand arguments from a single Python argument to mulitple C argument objects
  • runs a set of cResolvers over cArgs to produce a set of final ctypes-compatible arguments (cArguments)
  • runs a set of storeValues converters to save any temporary objects which need to remain in-memory to prevent memory-access failures for later calls (e.g. references to pointers require that the object holding the memory remain in-memory)
  • runs a function to choose what value to return from the function
These operations are implemented by the OpenGL.wrapper.Wrapper class, which is the focus of most of theOpenGL_accelerate module's optimizations (for obvious reasons).

Array Handling

PyOpenGL 3.x supports a significant range of array-compatible data-types, among the default plug-ins are:
  • numpy arrays
  • Python strings (byte-strings)
  • numbers (acting as pointers-to-single-value arrays)
  • ctypes arrays
  • ctypes parameters
  • ctypes pointers
  • lists/tuples
  • vertex buffer objects
when a PyOpenGL 3.x entry point requires an "array" data-type (or a void* data-type), it will use the PyOpenGL FormatHandler plugins (OpenGL.arrays.formathandler) to decide how to convert the array to a format compatible with the low-level OpenGL APIs.
Certain format-types may allow for use as array types while not actually containing a compatible copy of the data. Python lists, tuples and numbers all require the creation of a temporary variable to hold their data. As such you generally should not use them for performance-critical operations. Similarly, if you pass a numpy array with an incompatible data-type the wrapper may have to copy the data in order to pass it into the C-level functions. For numpy arrays, where the behavior is fast enough that you might not realize it, but can negatively impact your performance, you can prevent this behaviour by setting a flag in the OpenGL package before importing any of the OpenGL modules:
import OpenGL
OpenGL.ERROR_ON_COPY = True
from OpenGL.GL import *
which will cause the numpy format handler to raise errors if it finds itself copying an array to another data-type.
Keep in mind that with OpenGL 3.1 it will become required that all non-index arrays be managed as Vertex Buffer Objects.

Type-Specialized Array Functions

Each call which sets an array pointer, such as glVertexPointer, may have many variants. First there will a function which is identical that of the specification. For the pointer argument one should pass a string. Also note that the stride values are used.
Next there will a set of functions named:
glXPointer{ub|b|us|s|ui|i|f|d}
These will usually take as a single argument a multidimensional array of values. The type argument is controlled by the suffix of the function (ub is unsigned byte, b is byte, f is float, d is double etc.) Most other arguments are derived from the dimensions of the array.
So for glColorPointer we have:
glColorPointer(size, type, stride, pointer) -> None
glColorPointerub(pointer[][]) -> None
glColorPointerb(pointer[][]) -> None
glColorPointerus(pointer[][]) -> None
glColorPointers(pointer[][]) -> None
glColorPointerui(pointer[][]) -> None
glColorPointeri(pointer[][]) -> None
glColorPointerf(pointer[][]) -> None
glColorPointerd(pointer[][]) -> None
This same decoration strategy is used for other array functions besides glXPointer.
For instance, glDrawElements has the Python binding:
glDrawElements(mode, count, type, indices) -> None
where indices is expected to be a string. There are also the decorated bindings:
glDrawElementsub(mode, indices[]) -> None
glDrawElementsus(mode, indices[]) -> None
glDrawElementsui(mode, indices[]) -> None
where "indices" is now a single dimensional array.
When calling a glColorPointer, glVertexPointer, etc. Python needs to allocate memory to store the values that OpenGL needs. This memory is reference counted and takes into account function calls like glPushClientAttrib and glPopClientAttrib. To force this memory to be released one just need to make a call glColorPointerub(None).
Currently glPushClientAttrib will always set the GL_CLIENT_VERTEX_ARRAY_BIT flag as glPopClientAttrib has no way of knowing that flag was set and the state of the flag is needed to know whether or not to decrement the pointer locks on the allocated memory.
This may change in the future. That said, surrounding array use glPushClientAttrib/glPopClientAttrib is a good way to force the release of any allocated memory, but make sure that all calls to glXPointer, etc. are within the ClientAttrib block if you chose to use this scheme.
Note that since all the memory allocation is automatic there is no need for glGetPointerv function, so it is excluded.
Note that the function glInterleavedArrays is also present, but it does not have the variants that the others do (i.e., no glInterleavedArraysf). glInterleavedArrays has been unofficially deprecated for a long time, and is officially deprecated as of OpenGL 3.x
Note that for performance you may wish to use the "raw" version of array-handling functions, as these often have less processing applied than the "raw" version.

Image Routines

glDrawPixels and the other image/texturing functions have much the same decoration scheme as the array functions. For glDrawPixels there is the standard function which expects a string as the pixel data:
glDrawPixels(width, height, format, type, pixels) -> None
This function will respect the parameters set by glPixelStore{i|f}.
There is also a collection of variants which take a multidimensional array as the data source and set glPixelStore{i|f} automatically. For example:
glDrawPixelsub(format, pixels) -> None
Notice that width and height are inferred from the pixel data and the type is GLubyte.
PyOpenGL sets up "normal" pixel-transfer mode when using imaging APIs, as almost all Python image-aware modules/extensions assume, for instance, tightly packed data-structures and would potentially cause access errors when operating in standard OpenGL mode.

Extensions and Conditional Functionality

PyOpenGL has support for most OpenGL extensions. Extensions are available as "normal" function pointers by importing the constructed package name for the extension, for instance:
from OpenGL.GL.ARB.vertex_buffer_object import *
buffer = glGenBuffersARB(1)
there is no need to call initialization functions or the like for the extension module. You can, if you like, call the "init" function for the extension to retrieve a boolean indicating whether the local machine supports a given extension, like so:
if glInitVertexBufferObjectARB():
 ...
However, it is normally clearer to test for the boolean truth of the entry points you wish to use:
if (glGenBuffersARB):
 buffers = glGenBuffersARB( 1 )
There are often a number of entry points which implement the same API, for which you would like to use whichever implementation is available (likely with some preference in order). The OpenGL.extensions module provides an easy mechanism to support this:
from OpenGL.extensions import alternate
glCreateProgram = alternate( 'glCreateProgram', glCreateProgram, glCreateProgramObjectARB)
glCreateProgram = alternate( glCreateProgram, glCreateProgramObjectARB)
If the first element is a string it will be used as the name of the alternate object, otherwise the name is taken from the first argument.

Selection and Feedback Buffers

Note that both selection and feedback buffers are deprecated in OpenGL 3.x. You should be replacing selection-buffer usage with either "unique color" selection rendering or mathematical generated selection operations.
Normally in OpenGL to use a selection buffer one would do the following:
GLuint buffer[SIZE];
glSelectBuffer(SIZE, buffer);
glRenderMode(GL_SELECT);
/* draw some stuff */
GLint count = glRenderMode(GL_RENDER);
/* parse the selection buffer */
In Python this accomplished like this:
glSelectBuffer(SIZE) # allocate a selection buffer of SIZE elements
glRenderMode(GL_SELECT)
# draw some stuff
buffer = glRenderMode(GL_RENDER)
for hit_record in buffer:
    min_depth, max_depth, names = hit_record
    # do something with the record
Feedback buffers are used in the same way except that each item in the buffer is tuple (token, value), where value is either a passthrough token or a list of vertices.
Note that if glRenderMode returns a buffer then it also resets OpenGL's pointer for the corresponding buffer. This means that the buffer returned by glRenderMode is independent of future calls to glRenderMode, i.e. it will not be overwritten by any such future calls. This makes the returned buffer somewhat thread-safe. It also means that every call to glRenderMode(GL_SELECT | GL_FEEDBACK) needs to preceded by a call to glSelectBuffer or glFeedbackBuffer first, i.e. the following code will not work:
### THIS DOESN'T WORK!!!
glSelectBuffer(SIZE) # allocate a selection buffer of SIZE elements
glRenderMode(GL_SELECT)
# draw some stuff
buffer = glRenderMode(GL_RENDER)
# do another selection
glRenderMode(GL_SELECT)
# draw some stuff
buffer = glRenderMode(GL_RENDER)
Instead one must do:
glSelectBuffer(SIZE) # allocate a selection buffer of SIZE elements
glRenderMode(GL_SELECT)
# draw some stuff
buffer = glRenderMode(GL_RENDER)
# do another selection
glSelectBuffer(SIZE) allocate a new selection buffer
glRenderMode(GL_SELECT)
# draw some stuff
buffer = glRenderMode(GL_RENDER)

Function Aliases

PyOpenGL has historically provided a number of aliases for functions.  For backwards compatibility, these aliases continue to be provided:
  • glGetBooleanv aliases
    • glGetBoolean
  • glGetDoublev aliases
    • glGetDouble
  • glGetIntegerv aliases
    • glGetInteger
  • glColord aliases
    • glColor
    • glColor3
    • glColor4
  • glEvalCoordd aliases
    • glEvalCoord
    • glEvalCoord1
    • glEvalCoord2
  • glFogfv aliases
    • glFog
  • glIndexd aliases
    • glIndex
  • glLightfv aliases
    • glLight
  • glLightModelfv aliases
    • glLightModel
  • glMaterialfv aliases
    • glMaterial
  • glNormald aliases
    • glNormal
    • glNormal3
    • glNormal4
  • glRasterPosd aliases
    • glRasterPos
    • glRasterPos2
    • glRasterPos3
    • glRasterPos4
  • glRotated aliases
    • glRotate
  • glScaled aliases
    • glScale
  • glTexCoordd aliases
    • glTexCoord
    • glTexCoord1
    • glTexCoord2
    • glTexCoord3
    • glTexCoord4
  • glTexGendv aliases
    • glTexGen
  • glTexParameterfv aliases
    • glTexParameter
  • glTranslated aliases
    • glTranslate
  • glVertexd aliases
    • glVertex
PyOpenGL is a SourceForge Open-Source Project

2018-11-09

[Link] Prototyping OpenGL application with PyOpenGL

1   Introduction

If you have never encountered OpenGL, then you might not be aware of the fact that it is an excellent 3D graphics API and an absolute pleasure to work with. One caveat however is that setting up your application to use OpenGL can be... kind of painful. The struggle comes from the fact that each GUI library handles the OpenGL rendering context slightly differently and some GUIs require various tricks [1]. Also initializing the viewport can be done in several different ways and getting it right (while trying to hack some code together) can also be painful, especially if you have to recompile every time you change something.

2   PyOpenGL

PyOpenGL, as the name implies, is the Pythonic OpenGL API. If you have OpenGL experience but have never used PyOpenGL you should take the time to read the PyOpenGL for OpenGL Programmers tutorial. Although you can pretty much write C OpenGL in Python the PyOpenGL package also provides a nicer more python oriented interface for the OGL API.

2.1   Advantages of PyOpenGL

  • NumPy arrays are natively supported [2], making manipulation of large data sets easy and fast.
  • OpenGL calls are wrapped with glGetError so you automatically get exceptions when things go wrong.
  • You can copy/paste code to and from C/C++ since most of the OpenGL API is the same, or a slightly pythonified version, as the C API. This makes porting code a breeze.
  • It's Python so No need to compile which means faster changes.
  • It's Open Source so it's getting better every day.
  • Contributing is easy since the APIs calls are not very tightly coupled and once you understand the basics of how PyOpenGL and ctypes work you can easily contribute to the project by providing wrappers and pythonifications [3] for OpenGL calls.

2.2   Disadvantages of PyOpenGL

  • The PyOpenGL API consists of auto generated wrapper code with hand written pythonifications, this makes some errors harder to understand.
  • Lesser used / known APIs are not wrapped as nicely making your code ugly (see Using GLSL with PyOpenGL).
  • If you don't initialize your buffers correctly python will segfault, but that's what would happen in C/C++ so I guess it's not a disadvantage.
  • It's easier to write slow applications in PyOpenGL. The reason being that Python is obviously slower then C when running loops and calling methods, so drawing using immediate mode (glBegin/glEnd) tends to be much slower than C (in my superficial tests). However using vertex arrays and being smart about your code will ensure that your performance is close to C/C++ code.

3   Tips

3.1   Use the Pythonic functions

A large portion of the PyOpenGL API has pythonic wrappers, try to use those functions instead of the ones you are used to in C, this will make your code cleaner and therefore easier to understand and debug. Look at PyOpenGL for OpenGL Programmers and the PyOpenGL doc pages to see exactly which calls are pythonic.
For example you can call glGenTexture with one argument, the number of texture IDs you want, and it will return either a single integer based texture id or a list of texture IDs. You can also call it the standard way, initializing an ID and providing it to the function.
Another example is you can call glVertexPointer the C way providing the necessary arguments or use the pythonic glVertexPointer[f|b|i|] set and only providing an array of the given type.

3.2   Zero is not NULL

Some OpenGL calls require a pointer to a data buffer, and sometimes you must call these functions with a NULL pointer. In C/C++ it is perfectly legal to do the following:
# 's
1glBindBuffer(GL_ARRAY_BUFFER, buffer)
2glVertexPointer(3, GL_FLOAT, 0, 0)
but in Python you have to do this:
# 's
1glBindBuffer(GL_ARRAY_BUFFER, buffer)
2glVertexPointer(3, GL_FLOAT, 0, None)

3.3   NumPy array data type

When creating NumPy arrays you don't normally have to specify the data type, it will automatically be inferred from the content. However on a 64bit machine NumPy seems to automatically make floats into float64s which are actually doubles. Surely you can already see the problem with this, you are telling OGL that you are giving it an array of 256 floats, but instead it gets 128 doubles... the result is something like this:



Specifying the data type for a numpy array can be done using the dtype parameter to the array function:That is supposed to be a solid cube, surrounded with a wireframe cube
# 's
1a = array([0.0, 0.5, 1.0], dtype=float32)

3.4   Generating Buffers

Sometimes you would need to generate some buffers, for example using the glGenBuffers VBO call or the old version of the glGenTextures [4] texture generation call.
In this case you must define the variable beforehand and initialize it to the correct type:
# 's
1buffer = 0
2glGenTextures(1, buffer)
3
4# Sometimes you need the exact data type like above:
5from OpenGL.raw import GL
6buffer = GL.GLuint(0)
7glGenBuffers(1, buffer)

3.5   Loading Image Textures

Although there are plenty of good imaging libraries for C/C++ I doubt that any of them are as easy to use as the Python Image Library (PIL). Although PIL's commercially licensed version has an OpenGL Image interface, with the free version in order to load an image as a texture you can do something like:
# 's
 1import Image
 2img = Image.open('some_img.png') # .jpg, .bmp, etc. also work
 3img_data = numpy.array(list(img.getdata()), numpy.int8)
 4
 5texture = glGenTextures(1)
 6glPixelStorei(GL_UNPACK_ALIGNMENT,1)
 7glBindTexture(GL_TEXTURE_2D, texture)
 8glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP)
 9glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP)
10glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
11glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
12glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, img.size[0], img.size[1], 0, GL_RGB, GL_UNSIGNED_BYTE, img_data)

3.6   Geometry Rendering Performance

In general the fewer OpenGL calls you make, the better, true in C but even more so in Python. The performance hierarchy for drawing geometry in OpenGL is as follows (from slowest to fastest):
  1. Immediate mode (glBegin/glEnd) - One of the strengths of OpenGL and one of the main reason OGL is so easy to learn, but it's also the slowest way of drawing primitives.
# 's
1# Somewhere before the drawing code
2circle = [[radius*math.sin(math.radians(deg)), radius*math.cos(math.radians(deg)), 0.0] for deg in xrange(360)]
3
4# In the drawing code
5glBegin(GL_LINE_STRIP)
6for pt in circle:
7    glVertex(pt)
8glEnd()
  1. Vertex arrays - By precalculating vertex coordinates, color, normals and texture values and loading them into buffers you not only reduce the number of calls you make but you also allow the hardware to more efficiently transfer the data to the video card. The buffer data is still sent to the video card on every frame but this time in one giant block.
# 's
1glVertexPointerf(circle)
2glDrawArrays(GL_LINE_STRIP, 0, len(circle))
  1. Interleaved Vertex Arrays - Same as vertex arrays, except that you can place multiple types of data inside one giant buffer and then render it all using the same glDrawArrays/glDrawElements calls as before. Create a buffer with the data then use glInterleavedArrays to prepare it for rendering.
  2. Vertex Buffer Objects (VBOs) - The geometry equivalent of the texture objects, VBOs allow you to move the vertex, color, normal, texture data to the video card and render it when needed, without moving the data to the card on every render call. Depending on your application, VBOs can provide a huge performance boost... although their support in PyOpenGL is a bit rough. Making things a lot easier is Nathan Ostgard's VBO class
# 's
 1# Generate Buffers (inside Init method)
 2circle = array(circle, dtype=float32)
 3vertex_vbo = GL.GLuint(0)
 4glGenBuffers(1, vertex_vbo)
 5glBindBuffer(GL_ARRAY_BUFFER, circle)
 6glBufferData(GL_ARRAY_BUFFER, ADT.arrayByteCount(circle), ADT.voidDataPointer(circle), GL_STATIC_DRAW_ARB)
 7
 8# Draw buffers (inside Render method)
 9glBindBuffer(GL_ARRAY_BUFFER, vertex_vbo)
10glVertexPointer(3, GL_FLOAT, 0, None)
11glDrawArrays(GL_LINE_STRIP, 0, circle.shape[0])

3.7   row/column-major

NumPy by default uses row-major array order, just like C. OpenGL sort of expects/returns column-major arrays which means that you need to use the transpose of the modelview or whatever other matrices you are using. In fact when setting matrices for use by shader code using one of the glUniformMatrix* calls the third argument is whether the matrix should be transposed or not.
Example:
# 's
1# Get the modelview matrix and transform some vertices
2model_view = matrix(glGetFloatv(GL_MODELVIEW_MATRIX))
3vs = model_view.T*vertices
4
5# Save the 'old' model_view inside for use by a vertex/fragment shader
6glUniformMatrix4fv(glGetUniformLocation(program, "oldModelView"), 1, True, model_view)
Note
I am not too clear on this point but don't have the time right now to totally figure it out.

4   Random Errors

4.1   Type Errors

If you ever get strange errors about wrong types, try to manually coerce your data type into whatever type the API expects. Look in the OpenGL.raw package, there you will find all the proper OpenGL typedefs such as GLuint and GLfloat You can create a new variable with the correct type using something like this:
# 's
1from OpenGL.raw import GL as simple
2foo = simple.GLuint( 0 )

4.2   Random Exceptions

The PyOpenGL wrappers call glGetError automatically and throw exceptions when needed. However if you have defined some custom wrappers (see Using GLSL with PyOpenGL) you won't get automatic exception handling. The effect is that if one of your APIs generates an error you will get an exception the first time a proper PyOpenGL API is called. This should remind you of compiler errors from "back in the day" when the actual error is a hundred lines above the error message.

5   GUI Notes

Misc notes about building graphical user interfaces with Python that can use OpenGL

5.1   wxPython

wxPython is the Python API for the wxWidgets cross-platform GUI toolkit.
To use OpenGL in a wxPython application you can either subclass wx.glcanvas.GLCanvas or simply create an instance of the class and then bind the wx.EVT_PAINT to your drawing function.
Warning
By default double buffering is not enabled, which will most likely cause flicker. To enable double buffering pass attribList=[wx.glcanvas.WX_GL_DOUBLEBUFFER] to the GLCanvas constructor.
To further prevent flickering you can bind the erase background event to nothing, like: wx.EVT_ERASE_BACKGROUND(self, lambda e : None).
See Using OpenGL with wxPython for a rudimentary example of wxPython + PyOpenGL

5.4   GLUT

The OpenGL Utility Toolkit is probably the easiest GUI framework to learn and setup, resulting in fastest application development time. However it is also very simple and is not suitable for building large scale GUIs.
Note
If you plan on developing serious GUI based applications, with or without OpenGL, you should invest some time in learning a proper GUI toolkit.
PyOpenGL seems to provide GLUT bindings in the OpenGL.GLUT package.

6   Conclusion

PyOpenGL is not perfect but it's nonetheless a fantastic framework for developing 3D applications, prototyping OpenGL code, or playing with shaders (or other OGL features). Vastly increasing it's RAD capabilities is the fact that PyOpenGL integrates well with the SciPy/NumPy numerical processing library allowing you to develop algorithms and process data with much less code than C or FORTRAN but with very similar performance [5] and then render the data in OpenGL.
Although the title of this article implies that all PyOpenGL is good for is prototyping, that is not true. You can write full blown applications with PyOpenGL combined with pretty much any GUI toolkit you choose.
As a side note, feel free to add more tips, tricks and hacks pertaining to PyOpenGL to this article.

7   Footnotes

[1]For example the wxGLCanvas object from wxWidgets should have it's erase background background event overriden with an empty body to prevent flickering. I am fairly certain this is true for the Win32 API as well.
[2]NumPy is supported in most of the API, but if you run into problems you can always use the numpy.array.ctypes.data method or the OpenGL.arrays.ArrayDatatype class in order to extract the data and manually pass it to the call.
[3]Yes, I am making up words.
[4]glGenTextures is actually pythonic, you only need to provide one argument specifying how many textures you want and either an integer id or a list of texture ids will be returned
[5]NumPy is a set of FORTRAN and C libraries with Python wrappers. If you use the library correctly and avoid Python loops (which you can in most cases) your NumPy code performs most of it's heavy computation inside the Fortran or C libraries resulting in lighting fast speed.