How to solve invalid value error when calling glBufferSubData() in OpenGL

I have the following code that currently loads a single mesh from an OBJ file and renders it:

import pygame
from pathlib import Path
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GL import shaders
import numpy as np
import pywavefront
from math import sin, cos, tan, atan2

def calcFrustumScale(fFovDeg):
    degToRad = np.pi * 2.0 / 360.0
    fFovRad = fFovDeg * degToRad
    return 1.0 / tan(fFovRad / 2.0)

def calcLerpFactor(fElapsedTime, fLoopDuration):
    fValue = (fElapsedTime % fLoopDuration) / fLoopDuration
    if fValue > 0.5:
        fValue = 1.0 - fValue
    return fValue * 2.0

def computeAngleRad(fElapsedTime, fLoopDuration):
    fScale = np.pi * 2.0 / fLoopDuration
    fCurrTimeThroughLoop = fElapsedTime % fLoopDuration
    return fCurrTimeThroughLoop * fScale

def load_model(single_model_path: Path, color: np.array = np.array([*np.random.uniform(0.0, 1.0, 3), 1.0], dtype='float32')):
    scene = pywavefront.Wavefront(single_model_path, collect_faces=True)

    model = {
        'vertex' : np.array(scene.vertices, dtype='float32'),
        'face' : np.array(scene.mesh_list[0].faces, dtype='uint32')
    }
    model['color'] = np.full((len(model['vertex']), 4), color, dtype='float32')

    return model

def get_size(model: dict, stype='vertex'):
    items = model[stype]
    return items.size * items.itemsize

def get_transform(elapsed_time):
        angle_rad = computeAngleRad(elapsed_time, 2.0)
        _cos = cos(angle_rad)
        _sin = sin(angle_rad)
        
        transform = np.identity(4, dtype='float32')
        transform[0][0] = _cos
        transform[2][0] = _sin
        transform[0][2] = -_sin
        transform[2][2] = _cos
        # offset 
        transform[0][3] = 0.0 #-5.0
        transform[1][3] = 0.0 #5.0
        transform[2][3] = -5
        return transform

def rand_range(start, stop, step):
    return np.random.randint(0, int((stop - start) / step)) * step + start   

color_rnd = np.array([*np.random.uniform(0.0, 1.0, 3), 1.0], dtype='float32')
print(color_rnd)

modelToCameraMatrixUnif = None
cameraToClipMatrixUnif = None

# Global display variables
cameraToClipMatrix = np.zeros((4,4), dtype='float32')
fFrustumScale = calcFrustumScale(45.0)

model = load_model('sample0.obj')
update_enabled = False

print('Model vertex bytesize:t', get_size(model, 'vertex'))
print('Model face bytesize:  t', get_size(model, 'face'))
print('Model color bytesize: t', get_size(model, 'color'))

CUBES_COUNT = 10

VBO_BUFFER_SIZE = CUBES_COUNT * get_size(model, 'vertex')
VBO_SUB_BUFFER_SIZE = get_size(model, 'vertex')

IBO_BUFFER_SIZE = CUBES_COUNT * get_size(model, 'face')
IBO_SUB_BUFFER_SIZE = get_size(model, 'face')

CBO_BUFFER_SIZE = CUBES_COUNT * get_size(model, 'color')
CBO_SUB_BUFFER_SIZE = get_size(model, 'color')

UPDATE_INTERVAL = 10  # Time interval between updates (in frames)

vertex_shader = '''
#version 330

layout(location = 0) in vec4 position;
layout(location = 1) in vec4 color;

smooth out vec4 theColor;

uniform mat4 cameraToClipMatrix;
uniform mat4 modelToCameraMatrix;

void main()
{
    vec4 cameraPos = modelToCameraMatrix * position;
    gl_Position = cameraToClipMatrix * cameraPos;
    theColor = color;
}
'''

fragment_shader = '''
#version 330

smooth in vec4 theColor;
out vec4 outputColor;

void main()
{
    outputColor = theColor;
}
'''

vbo = None
cbo = None
ibo = None
vao = None

program = None

def initialize():
    global model
    global vbo, cbo, ibo, vao
    global program
    global modelToCameraMatrixUnif, cameraToClipMatrixUnif, cameraToClipMatrix

    pygame.init()
    display = (800, 800)
    pygame.display.set_mode(display, DOUBLEBUF | OPENGL)

    vertex_shader_id = shaders.compileShader(vertex_shader, GL_VERTEX_SHADER)
    fragment_shader_id = shaders.compileShader(fragment_shader, GL_FRAGMENT_SHADER)
    program = shaders.compileProgram(vertex_shader_id, fragment_shader_id)
    glUseProgram(program)

    glEnable(GL_CULL_FACE)
    glCullFace(GL_BACK)
    glFrontFace(GL_CW)
    
    glEnable(GL_DEPTH_TEST)
    glDepthMask(GL_TRUE)
    glDepthFunc(GL_LEQUAL)
    glDepthRange(0.0, 1.0)
    
    modelToCameraMatrixUnif = glGetUniformLocation(program, "modelToCameraMatrix")
    cameraToClipMatrixUnif = glGetUniformLocation(program, "cameraToClipMatrix")
    
    fzNear = 1.0
    fzFar = 100.0
    
    # Note that this and the transformation matrix below are both
    # ROW-MAJOR ordered. Thus, it is necessary to pass a transpose
    # of the matrix to the glUniform assignment function.
    cameraToClipMatrix[0][0] = fFrustumScale
    cameraToClipMatrix[1][1] = fFrustumScale
    cameraToClipMatrix[2][2] = (fzFar + fzNear) / (fzNear - fzFar)
    cameraToClipMatrix[2][3] = -1.0
    cameraToClipMatrix[3][2] = (2 * fzFar * fzNear) / (fzNear - fzFar)
    
    glUseProgram(program)
    glUniformMatrix4fv(cameraToClipMatrixUnif, 1, GL_FALSE, cameraToClipMatrix.transpose())
    glUseProgram(0)

    vbo = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, vbo)
    glBufferData(
        GL_ARRAY_BUFFER,
        model['vertex'].flatten(),
        GL_STATIC_DRAW
    )
    glBindBuffer(GL_ARRAY_BUFFER, 0)

    cbo = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, cbo)
    glBufferData(
        GL_ARRAY_BUFFER,
        model['color'].flatten(),
        GL_STATIC_DRAW
    )
    glBindBuffer(GL_ARRAY_BUFFER, 0)

    ibo = glGenBuffers(1)
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo)
    glBufferData(
        GL_ELEMENT_ARRAY_BUFFER,
        model['face'].flatten(),
        GL_STATIC_DRAW
    )
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)

    vao = glGenVertexArrays(1)
    glBindVertexArray(vao)
    vertex_dim = model['vertex'].shape[1]
    glBindBuffer(GL_ARRAY_BUFFER, vbo)
    glEnableVertexAttribArray(0)
    glVertexAttribPointer(0, vertex_dim, GL_FLOAT, GL_FALSE, 0, None)

    color_dim = model['color'].shape[1]
    glBindBuffer(GL_ARRAY_BUFFER, cbo)
    glEnableVertexAttribArray(1)
    glVertexAttribPointer(1, color_dim, GL_FLOAT, GL_FALSE, 0, None)
    
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo)
    
    glBindVertexArray(0)

def update_vbo(offset_prev, offset_curr):
    global vbo
    global model

    #random_offset_in_range = np.random.choice(np.arange(0, VBO_BUFFER_SIZE - VBO_SUB_BUFFER_SIZE, VBO_SUB_BUFFER_SIZE)) 
    print('(VBO) Removing data at ({}:{})'.format(offset_prev, offset_prev + VBO_SUB_BUFFER_SIZE))
    print('(VBO) Adding data at ({}:{})'.format(offset_curr, offset_curr + VBO_SUB_BUFFER_SIZE))

    glBindBuffer(GL_ARRAY_BUFFER, vbo)
    glBufferSubData(GL_ARRAY_BUFFER, offset_prev, VBO_SUB_BUFFER_SIZE, None)
    glBufferSubData(GL_ARRAY_BUFFER, offset_curr, VBO_SUB_BUFFER_SIZE, model['vertex'].flatten())
    #glBufferSubData(GL_ARRAY_BUFFER, 0, VBO_SUB_BUFFER_SIZE, model['vertex'].flatten())
    glBindBuffer(GL_ARRAY_BUFFER, 0)

def update_cbo(offset_prev, offset_curr, color: np.array = np.array([*np.random.uniform(0.0, 1.0, 3), 1.0], dtype='float32')):
    global cbo
    global model

    model['color'] = np.full((len(model['vertex']), 4), color, dtype='float32')

    print('(CBO) Removing data at ({}:{})'.format(offset_prev, offset_prev + CBO_SUB_BUFFER_SIZE))
    print('(CBO) Adding data at ({}:{})'.format(offset_curr, offset_curr + CBO_SUB_BUFFER_SIZE))

    glBindBuffer(GL_ARRAY_BUFFER, cbo)
    glBufferSubData(GL_ARRAY_BUFFER, offset_prev, CBO_SUB_BUFFER_SIZE, None)
    glBufferSubData(GL_ARRAY_BUFFER, offset_curr, CBO_SUB_BUFFER_SIZE, model['color'].flatten())
    #glBufferSubData(GL_ARRAY_BUFFER, 0, CBO_SUB_BUFFER_SIZE, model['color'].flatten())
    glBindBuffer(GL_ARRAY_BUFFER, 0)

def update_ibo(offset_prev, offset_curr):
    global ibo
    global model

    print('(IBO) Removing data at ({}:{})'.format(offset_prev, offset_prev + IBO_SUB_BUFFER_SIZE))
    print('(IBO) Adding data at ({}:{})'.format(offset_curr, offset_curr + IBO_SUB_BUFFER_SIZE))

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo)
    glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset_prev, IBO_SUB_BUFFER_SIZE, None)
    glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset_curr, IBO_SUB_BUFFER_SIZE, model['face'].flatten())
    #glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, IBO_SUB_BUFFER_SIZE, model['face'].flatten())
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)

def render():
    global vao, model
    global modelToCameraMatrixUnif

    glClearColor(0.0, 0.0, 0.0, 0.0)
    glClearDepth(1.0)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

    glUseProgram(program)
    
    elapsed_time = pygame.time.get_ticks() / 1000.0
    transform_func = get_transform
    transformMatrix = transform_func(elapsed_time=elapsed_time)

    glUniformMatrix4fv(modelToCameraMatrixUnif, 1, GL_FALSE, transformMatrix.transpose())

    glBindVertexArray(vao)
    index_count = model['face'].size
    glDrawElements(GL_TRIANGLES, index_count, GL_UNSIGNED_INT, None)
    glBindVertexArray(0)
    pygame.display.flip()

def main():
    global update_enabled
    initialize()

    frame_count = 0
    offsets = {
        'vbo' : {
            'prev' : 0,
            'curr' : 0
        },
        'cbo' : {
            'prev' : 0,
            'curr' : 0
        },
        'ibo' : {
            'prev' : 0,
            'curr' : 0
        }
    }

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                quit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    pygame.quit()
                    quit()

                if event.key == pygame.K_c:
                    #update_color()
                    pass

                if event.key == pygame.K_u:
                    update_enabled = not update_enabled
                    if update_enabled:
                        print('Update triggered')

        if update_enabled: # and frame_count % UPDATE_INTERVAL == 0:
            offsets['vbo']['curr'] = rand_range(start=0, stop=(VBO_BUFFER_SIZE - VBO_SUB_BUFFER_SIZE), step=VBO_SUB_BUFFER_SIZE)
            update_vbo(offsets['vbo']['prev'], offsets['vbo']['curr'])
            offsets['vbo']['prev'] = offsets['vbo']['curr']

            offsets['cbo']['curr'] = rand_range(start=0, stop=(CBO_BUFFER_SIZE - CBO_SUB_BUFFER_SIZE), step=CBO_SUB_BUFFER_SIZE)
            color = np.array([*np.random.uniform(0.0, 1.0, 3), 1.0], dtype='float32')
            update_cbo(offsets['vbo']['prev'], offsets['cbo']['curr'], color)
            offsets['cbo']['prev'] = offsets['cbo']['curr']

            offsets['ibo']['curr'] = rand_range(start=0, stop=(IBO_BUFFER_SIZE - IBO_SUB_BUFFER_SIZE), step=IBO_SUB_BUFFER_SIZE)
            update_ibo(offsets['ibo']['prev'], offsets['ibo']['curr'])
            offsets['ibo']['prev'] = offsets['ibo']['curr']

        update_enabled = False

        render()

        frame_count += 1

if __name__ == '__main__':
    main()

Whenever U key is pressed, the update_*() functions are called to update each type of buffer accordingly. However, I receive

raise self._errorClass(
OpenGL.error.GLError: GLError(
        err = 1281,
        description = b'invalid value',
        baseOperation = glBufferSubData,
        pyArgs = (
                GL_ARRAY_BUFFER,
                576,
                96,
                array([ 1.,  1., -1.,  1., -1., -1.,  1.,  1.,  1.,  1., -1.,  1., -1.,
        1., -1., -1., -1., -1., -1.,  1.,  1....,
        ),
        cArgs = (
                GL_ARRAY_BUFFER,
                576,
                96,
                array([ 1.,  1., -1.,  1., -1., -1.,  1.,  1.,  1.,  1., -1.,  1., -1.,
        1., -1., -1., -1., -1., -1.,  1.,  1....,
        ),
        cArguments = (
                GL_ARRAY_BUFFER,
                576,
                96,
                array([ 1.,  1., -1.,  1., -1., -1.,  1.,  1.,  1.,  1., -1.,  1., -1.,
        1., -1., -1., -1., -1., -1.,  1.,  1....,
        )
)

for the VBO (vertices). Disabling the update of the VBO triggers similar error for the CBO (colors) and IBO (faces).

My goal is to use a single buffer per type (vertices, faces and colors) and update it in some manner. For example in the future I will load a large mesh (constant) representing my terrain and then iterate through a bunch of other meshes that will be loaded and rendered one after the other by updating the data of the remaining buffer, thus not needing to re-load the data that is constant.

I do know that invalid value errors are emitted if the offset or data are incorrectly passed to the glBufferSubData() and indeed, if I replace the offsets with 0, I can at see that it’s working (at least the colors are changing). I can’t figure out how my calculations are incorrect.

The offset is calculated using the rand_range() function, which takes

  • start – here 0
  • end – the total buffer size minus the size of the respective chunk that represents my mesh either as vertices, colors or faces
  • step – the size of the respective chunk that represents my mesh either as vertices, colors or faces

Since I am currently working with the same mesh, step size and end are constant for the respective type of buffer.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật