Answer to StackOverflow question Using offsets into the buffer in PyOpenGL calls
In the following examples a list of 6 indices is used, which may form a quad which consists of 2 triangle primitives.
indices = [0, 1, 2, 0, 2, 3]
Since the data which is passed to the OpenGL functions has to be consist of fixed size units in a coherent buffer, the list has to be transformed to an array of floats either by import ctypes ornumpy.array
.
The type of the array elements has to match value type enumerator constant, which is set, at the call of glDrawElements
or glMultiDrawElements
:
ctypes.c_uint8 / numpy.uint8 <-> GL_UNSIGNED_BYTE
ctypes.c_uint16 / numpy.uint16 <-> GL_UNSIGNED_SHORT
ctypes.c_uint32 / numpy.uint32 <-> GL_UNSIGNED_INT
Using ctypes
:
import ctypes
indexArray = (ctypes.c_uint32 * 6)(*indices)
Using numpy
:
import numpy
indexArray = numpy.array(indices, dtype=numpy.uint32)
For the use of the index buffer with glDrawElements
there are different opportunities.
Using Legacy OpenGL (compatibility profile xontext), the buffer can be directly passed to glDrawElements
. The pointer to the array data is passed to the function.
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, indexArray)
If named element array buffer object is stated in the vertex array object, the the last parameter of glDrawElements
is treated as a byte offset into the buffer object’s data store.
glBindVertexArray(vao)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexArray, GL_STATIC_DRAW)
The last parameter can be None
, which is equivalent to ctypes.c_void_p(0)
, if the indices should be drawn, starting by the 1st element of the buffer:
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, None)
If the drawing should not start, with the first index, then the byte offset of the start index has to be calculated. e.g. 3*4
sets the start to the 3 index, for a buffer of type GL_UNSIGNED_INT
:
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, ctypes.c_void_p(3 * 4))
The use of glMultiDrawElements
is very similar.
Using a compatibility profile xontext, the buffer pointers can be directly passed to the OpenGL function.
To arrays of indices have to be generated:
Using ctypes
:
indexArray1 = (ctypes.c_uint32 * 3)(0, 1, 2)
indexArray2 = (ctypes.c_uint32 * 3)(0, 2, 3)
Using numpy
:
indexArray1 = numpy.array([0, 1, 2], dtype=numpy.uint32)
indexArray2 = numpy.array([0, 2, 3], dtype=numpy.uint32)
The pointers to the buffers have to be arranged to an array of pointers:
Using ctypes
the pointer to the index data arrays is get by ctypes.addressof()
:
indexPtr = (ctypes.c_void_p * 2)(ctypes.addressof(indexArray1),ctypes.addressof(indexArray2))
Using numpy
the pointer to the index data arrays is get by numpy.ndarray.ctypes
:
indexPtr = numpy.array([indexArray1.ctypes.data, indexArray2.ctypes.data], dtype=numpy.intp)
This array of pointer can be passed to the OpenGL function
counts = [3, 3]
glMultiDrawElements(GL_TRIANGLES, counts, GL_UNSIGNED_INT, indexPtr, 2)
If a vertex array object with an named element array buffer is used,
glBindVertexArray(vao)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo)
then the index parameter is treated as an array of byte offsets. In the following an array with 2 offset is passed to the function. 0 identifies the 1st index in the array and 3*4 the 3rd index.
Using ctypes
:
indexPtr = (ctypes.c_void_p * 2)(0, 3 * 4)
counts = [3, 3]
glMultiDrawElements(GL_TRIANGLES, counts, GL_UNSIGNED_INT, indexPtr, 2)
Using numpy
:
indexPtr = np.array([0, 3*4], dtype=numpy.intp)
counts = [3, 3]
glMultiDrawElements(GL_TRIANGLES, counts, GL_UNSIGNED_INT, indexPtr, 2)