So I'm having problem with the opengl function glBeginTransformFeedback
When I uncomment the lines, an error is occurring. It says 1282 invalid operation though that's quite vague.
My question is what should be done with the source code to uncomment these three lines:
#glBeginTransformFeedback(GL_POINTS)
#glDrawArrays(GL_POINTS, 0, POINTS_TOTAL)
#glEndTransformFeedback()
A broader question is any suggestions on how to get it to render correctly?
Update: With the help of Rabbid76 excellent code the program is now working!!! Thank you very much!
Success!!!! Current output same as expected output:
supporting files: springmass_support.zip
source code:
#!/usr/bin/python3
import sys
import time
import ctypes
fullscreen = True
sys.path.append("./shared")
from sbmloader import SBMObject # location of sbm file format loader
from sbmath import m3dDegToRad, m3dRadToDeg, m3dTranslateMatrix44, m3dRotationMatrix44, m3dMultiply, m3dOrtho, m3dPerspective, rotation_matrix, translate, m3dScaleMatrix44, \
scale, m3dLookAt, normalize
try:
from OpenGL.GLUT import *
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.raw.GL.ARB.vertex_array_object import glGenVertexArrays, glBindVertexArray
except:
print ('''
ERROR: PyOpenGL not installed properly.
''')
sys.exit()
from array import array
from enum import Enum
import numpy as np
import glm
from math import cos, sin
identityMatrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]
render_program = GLuint(0)
myobject = SBMObject()
POSITION_A = 0
POSITION_B = 1
VELOCITY_A = 2
VELOCITY_B = 3
CONNECTION = 4
POINTS_X = 50
POINTS_Y = 50
POINTS_TOTAL = POINTS_X * POINTS_Y
CONNECTIONS_TOTAL = (POINTS_X - 1) * POINTS_Y + (POINTS_Y - 1) * POINTS_X
m_vao = [GLuint(0) for _ in range(2)]
m_vbo = [GLuint(0) for _ in range(5)]
m_index_buffer = GLuint(0)
m_pos_tbo = [GLuint(0), GLuint(0)]
m_update_program = GLuint(0)
m_render_program = GLuint(0)
m_C_loc = GLuint(0)
m_iteration_index = 0
draw_points = True
draw_lines = True
iterations_per_frame = 16
def shader_load(filename, shader_type):
result = GLuint(0)
with open ( filename, "rb") as data:
result = glCreateShader(shader_type)
glShaderSource(result, data.read() )
glCompileShader(result)
return result
def load_shaders():
global m_update_program
global m_render_program
vs = GLuint(0)
fs = GLuint(0)
buffer = ''
vs = shader_load("update.vs.glsl", GL_VERTEX_SHADER)
if (m_update_program):
glDeleteProgram(m_update_program)
m_update_program = glCreateProgram()
glAttachShader(m_update_program, vs)
# static const char * tf_varyings[] =
# {
# "tf_position_mass",
# "tf_velocity"
# }
# tricky to convert string array to string pointer
tf_varyings = ["tf_position_mass", "tf_velocity"]
# Prepare ctypes data containing the list tf_varyings of strings
array_type = ctypes.c_char_p * len(tf_varyings)
buff = array_type()
for i, e in enumerate(tf_varyings):
buff[i] = e.encode()
# ctypes.c_char
tf_varyings_chrpp = ctypes.cast(ctypes.pointer(buff), ctypes.POINTER(ctypes.POINTER(GLchar)))
glTransformFeedbackVaryings(m_update_program, 2, tf_varyings_chrpp, GL_SEPARATE_ATTRIBS)
glLinkProgram(m_update_program)
glGetShaderInfoLog(vs)
glGetProgramInfoLog(m_update_program)
glDeleteShader(vs)
vs = shader_load("render.vs.glsl", GL_VERTEX_SHADER)
fs = shader_load("render.fs.glsl", GL_FRAGMENT_SHADER)
if (m_render_program):
glDeleteProgram(m_render_program)
m_render_program = glCreateProgram()
glAttachShader(m_render_program, vs)
glAttachShader(m_render_program, fs)
glLinkProgram(m_render_program)
class Scene:
def __init__(self, width, height):
global m_vao
global m_vbo
i = 0
j = 0
load_shaders()
initial_positions = [glm.vec4() for _ in range(POINTS_TOTAL)]
initial_velocities = [glm.vec3() for _ in range(POINTS_TOTAL)]
connection_vectors = [glm.ivec3() for _ in range(POINTS_TOTAL)]
n=0
for j in range(0, POINTS_Y):
fj = float(j) / float(POINTS_Y)
for i in range(0, POINTS_X):
fi = float(i) / float(POINTS_X)
initial_positions[n] = glm.vec4((fi - 0.5) * float(POINTS_X), (fj - 0.5) * float(POINTS_Y), 0.6 * sin(fi) * cos(fj), 1.0)
initial_velocities[n] = glm.vec3(0.0)
connection_vectors[n] = glm.ivec4(-1)
if (j != (POINTS_Y - 1)):
if (i != 0):
connection_vectors[n][0] = n - 1
if (j != 0):
connection_vectors[n][1] = n - POINTS_X
if (i != (POINTS_X - 1)):
connection_vectors[n][2] = n + 1
if (j != (POINTS_Y - 1)):
connection_vectors[n][3] = n + POINTS_X
n+=1
for i in range(0, 2):
glGenVertexArrays(1, m_vao[i])
for i in range(0, 5):
glGenBuffers(i+1, m_vbo[i])
for i in range(0, 2):
glBindVertexArray(m_vao[i])
glBindBuffer(GL_ARRAY_BUFFER, m_vbo[POSITION_A + i])
# POSITION_A
glBindBuffer(GL_ARRAY_BUFFER, m_vbo[POSITION_A + i])
ar_position = np.empty([POINTS_TOTAL, 4], dtype='float32')
for j, e in enumerate(initial_positions):
ar_position[j] = e
glBufferData(GL_ARRAY_BUFFER, POINTS_TOTAL * glm.sizeof(glm.vec4()), ar_position, GL_DYNAMIC_COPY)
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, None)
glEnableVertexAttribArray(0)
# VELOCITY_A
glBindBuffer(GL_ARRAY_BUFFER, m_vbo[VELOCITY_A + i])
ar_velocities = np.empty([POINTS_TOTAL, 3], dtype='float32')
for j, e in enumerate(initial_velocities):
ar_velocities[j] = e
glBufferData(GL_ARRAY_BUFFER, POINTS_TOTAL * glm.sizeof(glm.vec3()), ar_velocities, GL_DYNAMIC_COPY)
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, None)
glEnableVertexAttribArray(1)
# CONNECTION
glBindBuffer(GL_ARRAY_BUFFER, m_vbo[CONNECTION])
ar_connection = np.empty([POINTS_TOTAL, 4], dtype='uint32')
for j, e in enumerate(connection_vectors):
ar_connection[j] = e
glBufferData(GL_ARRAY_BUFFER, POINTS_TOTAL * glm.sizeof(glm.ivec4()), ar_connection, GL_STATIC_DRAW)
glVertexAttribIPointer(2, 4, GL_INT, 0, None)
glEnableVertexAttribArray(2)
glGenTextures(2, m_pos_tbo)
glBindTexture(GL_TEXTURE_BUFFER, m_pos_tbo[0])
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, m_vbo[POSITION_A])
glBindTexture(GL_TEXTURE_BUFFER, m_pos_tbo[1])
glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, m_vbo[POSITION_B])
lines = (POINTS_X - 1) * POINTS_Y + (POINTS_Y - 1) * POINTS_X
glGenBuffers(1, m_index_buffer)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_index_buffer)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, lines * 2 * ctypes.sizeof(ctypes.c_int), None, GL_STATIC_DRAW)
e = glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 0, lines * 2 * ctypes.sizeof(ctypes.c_int), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT)
int_array = (ctypes.c_int * (4 * lines * 2)).from_address(e)
n = 0
for j in range(0, POINTS_Y):
for i in range(0, POINTS_X - 1):
int_array[n] = i + j * POINTS_X
n+=1
int_array[n] = 1 + i + j * POINTS_X
n+=1
for i in range(0, POINTS_X):
for j in range(0, POINTS_Y - 1):
int_array[n] = i + j * POINTS_X
n+=1
int_array[n] = POINTS_X + i + j * POINTS_X
n+=1
glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER)
def display(self):
global m_iteration_index
glUseProgram(m_update_program)
glEnable(GL_RASTERIZER_DISCARD)
for i in range( iterations_per_frame, 0, -1):
glBindVertexArray(m_vao[m_iteration_index & 1])
glBindTexture(GL_TEXTURE_BUFFER, m_pos_tbo[m_iteration_index & 1])
m_iteration_index +=1
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_vbo[POSITION_A + (m_iteration_index & 1)])
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, m_vbo[VELOCITY_A + (m_iteration_index & 1)])
glBeginTransformFeedback(GL_POINTS)
glDrawArrays(GL_POINTS, 0, POINTS_TOTAL)
glEndTransformFeedback()
glDisable(GL_RASTERIZER_DISCARD)
black = [ 0.0, 0.0, 0.0, 0.0 ]
glViewport(0, 0, self.width, self.height)
glClearBufferfv(GL_COLOR, 0, black)
glUseProgram(m_render_program)
if (draw_points):
glPointSize(4.0)
glDrawArrays(GL_POINTS, 0, POINTS_TOTAL)
if (draw_lines):
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_index_buffer)
glDrawElements(GL_LINES, CONNECTIONS_TOTAL * 2, GL_UNSIGNED_INT, None)
glutSwapBuffers()
def reshape(self, width, height):
self.width = width
self.height = height
def keyboard(self, key, x, y ):
global fullscreen
print ('key:' , key)
if key == b'\x1b': # ESC
sys.exit()
elif key == b'f' or key == b'F': #fullscreen toggle
if (fullscreen == True):
glutReshapeWindow(512, 512)
glutPositionWindow(int((1360/2)-(512/2)), int((768/2)-(512/2)))
fullscreen = False
else:
glutFullScreen()
fullscreen = True
print('done')
def init(self):
pass
def timer(self, blah):
glutPostRedisplay()
glutTimerFunc( int(1/60), self.timer, 0)
time.sleep(1/60.0)
if __name__ == '__main__':
start = time.time()
glutInit()
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)
glutInitWindowSize(512, 512)
w1 = glutCreateWindow('OpenGL SuperBible - Spring-Mass Simulator')
glutInitWindowPosition(int((1360/2)-(512/2)), int((768/2)-(512/2)))
fullscreen = False
many_cubes = False
#glutFullScreen()
scene = Scene(512,512)
glutReshapeFunc(scene.reshape)
glutDisplayFunc(scene.display)
glutKeyboardFunc(scene.keyboard)
glutIdleFunc(scene.display)
#glutTimerFunc( int(1/60), scene.timer, 0)
scene.init()
glutMainLoop()
I ported this program to python from chapter 7 of Superbible OpenGL 7ed.
ported from: springmass.cpp
sys.getsizeof()
doesn't return the size of a buffer meanged by an object, it returns the size of the object.
If you wan to get the size of a the buffer manged by an ctypes
object, then you've to us ctypes.sizeof
. e.g:
glBufferData(GL_ELEMENT_ARRAY_BUFFER, lines * 2 * sys.getsizeof(int), None, GL_STATIC_DRAW)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, lines * 2 * ctypes.sizeof(ctypes.c_int), None, GL_STATIC_DRAW)
PyGLM
alos provides a glm.sizeof()
function. e.g.:
glBufferData(GL_ARRAY_BUFFER, POINTS_TOTAL * sys.getsizeof(glm.vec4()), ar, GL_DYNAMIC_COPY)
glBufferData(GL_ARRAY_BUFFER, POINTS_TOTAL * glm.sizeof(glm.vec4), ar, GL_DYNAMIC_COPY)
NumPy provides the .itmesize
property. e.g.:
np.dtype('float32').itemsize
A list of object can be generated by one line of code:
initial_positions = [glm.vec4() for _ in range(POINTS_TOTAL)]
initial_velocities = [glm.vec3() for _ in range(POINTS_TOTAL)]
connection_vectors = [glm.ivec3() for _ in range(POINTS_TOTAL)]
To generate a array form the list you can create an empty array by numpy.empty
.
Create a 2 dimensional arrays with the shape ([POINTS_TOTAL, 4]
) respectively ([POINTS_TOTAL, 3]
). The glm vector objects can be assigned in a loop.
Note, the array type for the integral attribute has to be 'uint32' rather than float32
:
# POSITION_A
glBindBuffer(GL_ARRAY_BUFFER, m_vbo[POSITION_A + i])
ar = np.empty([POINTS_TOTAL, 4], dtype='float32')
for j, e in enumerate(initial_positions):
ar[j] = e
glBufferData(GL_ARRAY_BUFFER, POINTS_TOTAL * glm.sizeof(glm.vec4), ar, GL_DYNAMIC_COPY)
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, None)
glEnableVertexAttribArray(0)
# VELOCITY_A
glBindBuffer(GL_ARRAY_BUFFER, m_vbo[VELOCITY_A + i])
ar = np.empty([POINTS_TOTAL, 3], dtype='float32')
for j, e in enumerate(initial_velocities):
ar[j] = e
glBufferData(GL_ARRAY_BUFFER, POINTS_TOTAL * glm.sizeof(glm.vec3), ar, GL_DYNAMIC_COPY)
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, None)
glEnableVertexAttribArray(1)
# CONNECTION
glBindBuffer(GL_ARRAY_BUFFER, m_vbo[CONNECTION])
ar = np.empty([POINTS_TOTAL, 4], dtype='uint32')
for j, e in enumerate(connection_vectors):
ar[j] = e
glBufferData(GL_ARRAY_BUFFER, POINTS_TOTAL * glm.sizeof(glm.ivec4), ar, GL_STATIC_DRAW)
glVertexAttribIPointer(2, 4, GL_INT, 0, None)
glEnableVertexAttribArray(2)
The instrucction
int_array = ((ctypes.c_int * 4) * lines * 2).from_address(e)
would generate a 3 dimensional array. A 2 dimensional array can be generated by using the *
- operator once, withe the size of all the elements (e.g. (ctypes.c_int * (4 * lines * 2))
):
int_array = (ctypes.c_int * (4 * lines * 2)).from_address(e)
n = 0
for j in range(0, POINTS_Y):
for i in range(0, POINTS_X - 1):
int_array[n] = i + j * POINTS_X
n+=1
int_array[n] = 1 + i + j * POINTS_X
n+=1
for i in range(0, POINTS_X):
for j in range(0, POINTS_Y - 1):
int_array[n] = i + j * POINTS_X
n+=1
int_array[n] = POINTS_X + i + j * POINTS_X
n+=1
Further there is an issue when you generate the vertex array objects and buffer objects.
Create list for the objects:
m_vao = [GLuint(0) for _ in range(2)]
m_vbo = [GLuint(0) for _ in range(5)]
Create 1 single object in the for
loops:
for i in range(0, 5):
glGenBuffers(1, m_vbo[i])
for i in range(0, 2):
glGenVertexArrays(1, m_vao[i])
Finally glBeginTransformFeedback
can be used:
for i in range( iterations_per_frame, 0, -1):
glBindVertexArray(m_vao[m_iteration_index & 1])
glBindTexture(GL_TEXTURE_BUFFER, m_pos_tbo[m_iteration_index & 1])
m_iteration_index +=1
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_vbo[POSITION_A + (m_iteration_index & 1)])
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, m_vbo[VELOCITY_A + (m_iteration_index & 1)])
glBeginTransformFeedback(GL_POINTS)
glDrawArrays(GL_POINTS, 0, POINTS_TOTAL)
glEndTransformFeedback()
I changed iterations_per_frame = 1
and I skipped glutIdleFunc
, but activated glutIdleFunc
class Scene:
# [...]
def timer(self, blah):
glutPostRedisplay()
glutTimerFunc( 50, self.timer, 0)
if __name__ == '__main__':
# [...]
#glutIdleFunc(scene.display)
glutTimerFunc( 1000, scene.timer, 0)
And got the following result: