def _update(self): """ Actual upload of data to GPU memory """ log.debug("GPU: Updating %s" % self.name) # Check active status (mandatory) # if not self._active: # raise RuntimeError("Attribute variable is not active") # if self._data is None: # raise RuntimeError("Attribute variable data is not set") # Generic vertex attribute (all vertices receive the same value) if self._generic: if self._handle >= 0: gl.glDisableVertexAttribArray(self._handle) self._afunction(self._handle, *self._data) # Regular vertex buffer elif self.handle >= 0: #if self._need_update: # self.data._update() # self._need_update = False # Get relevant information from gl_typeinfo size, gtype, dtype = gl_typeinfo[self._gtype] stride = self.data.stride # Make offset a pointer, or it will be interpreted as a small array offset = ctypes.c_void_p(self.data.offset) gl.glEnableVertexAttribArray(self.handle) gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.data.handle) gl.glVertexAttribPointer(self.handle, size, gtype, gl.GL_FALSE, stride, offset)
def _transfer_frame_pbo(self): # generate PBO's if self._video_thread.has_video_loaded and (self._pbo_shape != self._video_thread.frame.shape): if self._pbos is not None: gl.glDeleteBuffers(PBO_COUNT, self._pbos) self._pbo_shape = self._video_thread.frame.shape self._pbos = gl.glGenBuffers(PBO_COUNT) h, w, b = self._pbo_shape num_bytes = h*w*b for pbo in self._pbos: gl.glBindBuffer(gl.GL_PIXEL_UNPACK_BUFFER, pbo) gl.glBufferData(gl.GL_PIXEL_UNPACK_BUFFER, num_bytes, None, gl.GL_STREAM_DRAW) gl.glBindBuffer(gl.GL_PIXEL_UNPACK_BUFFER, 0) # generate/update OpenGL texture if (self._frame is None or self._frame.shape != self._pbo_shape) and self._pbo_shape is not None: self._frame = np.zeros(self._pbo_shape, dtype=np.uint8).view(gloo.Texture2D) self._frame.activate() self._frame.deactivate() # do the transfer of pixel data from cpu to gpu using PBO's # inspired by this: https://gist.github.com/roxlu/4663550 if self._video_thread.has_video_loaded and self._video_thread.frame_changed: self._video_thread.frame_changed = False pbo = self._pbos[self._pbo_index] gl.glBindBuffer(gl.GL_PIXEL_UNPACK_BUFFER, pbo) t = self._frame._handle h, w, b = self._pbo_shape assert t != None and t != 0 gl.glBindTexture(gl.GL_TEXTURE_2D, t) gl.glTexSubImage2D(gl.GL_TEXTURE_2D, 0, 0, 0, w, h, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, None) h, w, b = self._pbo_shape num_bytes = h*w*b self._pbo_index = (self._pbo_index + 1) % PBO_COUNT pbo = self._pbos[self._pbo_index] gl.glBindBuffer(gl.GL_PIXEL_UNPACK_BUFFER, pbo) # this might be an alternative, but not so fast as tested so far #gl.glBufferSubData(gl.GL_PIXEL_UNPACK_BUFFER, 0, num_bytes, ctypes.c_void_p(self._video_thread.frame.ctypes.data)) ptr = gl.glMapBuffer(gl.GL_PIXEL_UNPACK_BUFFER, gl.GL_WRITE_ONLY) if ptr != 0: #start = time.time() ctypes.memmove(ctypes.c_voidp(ptr), ctypes.c_void_p(self._video_thread.frame.ctypes.data), num_bytes) #end = time.time() #elapsed = end - start #print("Took %.2fms, %.2fMB/s" % (elapsed * 1000, (num_bytes / 1000000) / elapsed)) gl.glUnmapBuffer(gl.GL_PIXEL_UNPACK_BUFFER) gl.glBindBuffer(gl.GL_PIXEL_UNPACK_BUFFER, 0)
def set_uniforms(self, program): super().set_uniforms(program) ssbo = self.get("ssbo") if ssbo is None: ssbo = empty_ssbo ssbo.activate() b = ssbo._handle gl.glBindBuffer(gl.GL_SHADER_STORAGE_BUFFER, b) gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 0, b) program["uCount"] = len(ssbo)
def draw(self, projection, view, width, height): model_matrix = self._transform.matrix # Set uniforms self._program["u_projection"] = projection # Note: OpenGL matrix multiplication works on column-major oriented storage (as least for pre-multiplication). # Also glumpy.glm is using column-major assumption for its operations. view_model_matrix = np.transpose( np.matmul(np.transpose(view), np.transpose(model_matrix))) self._program["u_view_model_matrix"] = view_model_matrix # self._program["u_view"] = view # self._program["u_model"] = self._model view_model_normal_matrix = np.transpose( np.linalg.inv(view_model_matrix)) self._program["u_view_model_normal_matrix"] = view_model_normal_matrix self._program["u_color_scale"] = self._color_scale self._program["u_normal_scale"] = self._normal_scale self._program["u_depth_scale"] = self._depth_scale if self._shader_type == self.PHONG: self._program["u_light_position"] = self._light_position self._program[ "u_light_ambient_intensity"] = 0.4 * self._light_intensity self._program[ "u_light_diffuse_intensity"] = 0.4 * self._light_intensity self._program[ "u_light_specular_intensity"] = 0.2 * self._light_intensity self._program["u_material_ambient"] = self._material self._program["u_material_diffuse"] = self._material self._program["u_material_specular"] = self._material self._program["u_material_shininess"] = 32 with self._program.activate(): # Bind index buffer and draw if self.use_depth_test: gl.glEnable(gl.GL_DEPTH_TEST) else: gl.glDisable(gl.GL_DEPTH_TEST) if self.use_face_culling: gl.glFrontFace(gl.GL_CCW) gl.glCullFace(gl.GL_BACK) gl.glEnable(gl.GL_CULL_FACE) else: gl.glDisable(gl.GL_CULL_FACE) gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, self._gl_index_buffer) gl.glDrawElements(gl.GL_TRIANGLES, 3 * len(self._faces), opengl_utils.get_gl_type(self._faces), None) gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, 0)
def draw(self, mode=gl.GL_TRIANGLES, indices=None): #first=0, count=None): """ Draw using the specified mode & indices. :param gl.GLEnum mode: One of * GL_POINTS * GL_LINES * GL_LINE_STRIP * GL_LINE_LOOP, * GL_TRIANGLES * GL_TRIANGLE_STRIP * GL_TRIANGLE_FAN :param IndexBuffer|None indices: Vertex indices to be drawn. If none given, everything is drawn. """ self.activate() attributes = self._attributes.values() # Get buffer size first attribute # We need more tests here # - do we have at least 1 attribute ? # - does all attributes report same count ? # count = (count or attributes[0].size) - first if isinstance(indices, IndexBuffer): indices.activate() gltypes = { np.dtype(np.uint8): gl.GL_UNSIGNED_BYTE, np.dtype(np.uint16): gl.GL_UNSIGNED_SHORT, np.dtype(np.uint32): gl.GL_UNSIGNED_INT } gl.glDrawElements(mode, indices.size, gltypes[indices.dtype], None) indices.deactivate() else: first = 0 # count = (self._count or attributes[0].size) - first count = len(tuple(attributes)[0]) gl.glDrawArrays(mode, first, count) gl.glBindBuffer(gl.GL_ARRAY_BUFFER, 0) self.deactivate()
def draw(self, mode=gl.GL_TRIANGLES, indices=None): # first=0, count=None): """ Draw the attribute arrays in the specified mode. Parameters ---------- mode : GL_ENUM GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_LINE_LOOP, GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN first : int The starting vertex index in the vertex array. Default 0. count : int The number of vertices to draw. Default all. """ self.activate() attributes = self._attributes.values() # Get buffer size first attribute # We need more tests here # - do we have at least 1 attribute ? # - does all attributes report same count ? # count = (count or attributes[0].size) - first if isinstance(indices, IndexBuffer): indices.activate() gltypes = { np.dtype(np.uint8): gl.GL_UNSIGNED_BYTE, np.dtype(np.uint16): gl.GL_UNSIGNED_SHORT, np.dtype(np.uint32): gl.GL_UNSIGNED_INT, } gl.glDrawElements(mode, indices.size, gltypes[indices.dtype], None) indices.deactivate() else: first = 0 # count = (self._count or attributes[0].size) - first count = len(tuple(attributes)[0]) gl.glDrawArrays(mode, first, count) gl.glBindBuffer(gl.GL_ARRAY_BUFFER, 0) self.deactivate()
def draw(self, mode = gl.GL_TRIANGLES, indices=None): #first=0, count=None): """ Draw using the specified mode & indices. :param gl.GLEnum mode: One of * GL_POINTS * GL_LINES * GL_LINE_STRIP * GL_LINE_LOOP, * GL_TRIANGLES * GL_TRIANGLE_STRIP * GL_TRIANGLE_FAN :param IndexBuffer|None indices: Vertex indices to be drawn. If none given, everything is drawn. """ self.activate() attributes = self._attributes.values() # Get buffer size first attribute # We need more tests here # - do we have at least 1 attribute ? # - does all attributes report same count ? # count = (count or attributes[0].size) - first if isinstance(indices, IndexBuffer): indices.activate() gltypes = { np.dtype(np.uint8) : gl.GL_UNSIGNED_BYTE, np.dtype(np.uint16): gl.GL_UNSIGNED_SHORT, np.dtype(np.uint32): gl.GL_UNSIGNED_INT } gl.glDrawElements(mode, indices.size, gltypes[indices.dtype], None) indices.deactivate() else: first = 0 # count = (self._count or attributes[0].size) - first count = len(tuple(attributes)[0]) gl.glDrawArrays(mode, first, count) gl.glBindBuffer( gl.GL_ARRAY_BUFFER, 0 ) self.deactivate()
def _download_texture(self, texture): # generate PBO's if self._pbos is None or self._pbo_shape != texture.shape: if self._pbos is not None: gl.glDeleteBuffers(2, self._pbos) self._pbo_shape = texture.shape self._pbos = gl.glGenBuffers(2) h, w, b = self._pbo_shape num_bytes = h*w*b for pbo in self._pbos: gl.glBindBuffer(gl.GL_PIXEL_PACK_BUFFER, pbo) gl.glBufferData(gl.GL_PIXEL_PACK_BUFFER, num_bytes, None, gl.GL_STREAM_DRAW) gl.glBindBuffer(gl.GL_PIXEL_PACK_BUFFER, 0) # do the transfer of pixel data from gpu to cpu using PBO's # inspired by this: https://gist.github.com/roxlu/4663550 h, w, b = self._pbo_shape b -= 1 num_bytes = h*w*b pbo = self._pbos[self._pbo_index] gl.glBindBuffer(gl.GL_PIXEL_PACK_BUFFER, pbo) t = texture._handle assert t != None and t != 0 gl.glBindTexture(gl.GL_TEXTURE_2D, t) gl.glGetTexImage(gl.GL_TEXTURE_2D, 0, gl.GL_RGB, gl.GL_UNSIGNED_BYTE, ctypes.c_void_p(0)) self._pbo_index = (self._pbo_index + 1) % 2 pbo = self._pbos[self._pbo_index] gl.glBindBuffer(gl.GL_PIXEL_PACK_BUFFER, pbo) ptr = gl.glMapBuffer(gl.GL_PIXEL_PACK_BUFFER, gl.GL_READ_ONLY) data = None if ptr != 0: p = ctypes.cast(ptr, ctypes.POINTER(ctypes.c_ubyte * num_bytes)) data = np.frombuffer(p.contents, dtype=np.uint8).copy().reshape((h, w, b)) gl.glUnmapBuffer(gl.GL_PIXEL_PACK_BUFFER) gl.glBindBuffer(gl.GL_PIXEL_PACK_BUFFER, 0) assert data is not None return data
def draw(self, mode = gl.GL_TRIANGLES, indices=None): #first=0, count=None): """ Draw the attribute arrays in the specified mode. Parameters ---------- mode : GL_ENUM GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_LINE_LOOP, GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN first : int The starting vertex index in the vertex array. Default 0. count : int The number of vertices to draw. Default all. """ self.activate() attributes = self._attributes.values() # Get buffer size first attribute # We need more tests here # - do we have at least 1 attribute ? # - does all attributes report same count ? # count = (count or attributes[0].size) - first if isinstance(indices, IndexBuffer): indices.activate() gltypes = { np.dtype(np.uint8) : gl.GL_UNSIGNED_BYTE, np.dtype(np.uint16): gl.GL_UNSIGNED_SHORT, np.dtype(np.uint32): gl.GL_UNSIGNED_INT } gl.glDrawElements(mode, indices.size, gltypes[indices.dtype], None) indices.deactivate() else: first = 0 # count = (self._count or attributes[0].size) - first count = len(attributes[0]) gl.glDrawArrays(mode, first, count) gl.glBindBuffer( gl.GL_ARRAY_BUFFER, 0 ) self.deactivate()
def _deactivate(self): """ Unbind the current bound buffer """ log.debug("GPU: Deactivating buffer (id=%d)" % self._id) gl.glBindBuffer(self._target, 0)
def _activate(self): """ Bind the buffer to some target """ log.debug("GPU: Activating buffer (id=%d)" % self._id) gl.glBindBuffer(self._target, self._handle)
def render(self, draw_data): # perf: local for faster access io = self.io display_width, display_height = io.display_size fb_width = int(display_width * io.display_fb_scale[0]) fb_height = int(display_height * io.display_fb_scale[1]) if fb_width == 0 or fb_height == 0: return draw_data.scale_clip_rects(*io.display_fb_scale) # backup GL state # todo: provide cleaner version of this backup-restore code last_program = gl.glGetIntegerv(gl.GL_CURRENT_PROGRAM) last_texture = gl.glGetIntegerv(gl.GL_TEXTURE_BINDING_2D) last_active_texture = gl.glGetIntegerv(gl.GL_ACTIVE_TEXTURE) last_array_buffer = gl.glGetIntegerv(gl.GL_ARRAY_BUFFER_BINDING) last_element_array_buffer = gl.glGetIntegerv( gl.GL_ELEMENT_ARRAY_BUFFER_BINDING) last_vertex_array = gl.glGetIntegerv(gl.GL_VERTEX_ARRAY_BINDING) last_blend_src = gl.glGetIntegerv(gl.GL_BLEND_SRC) last_blend_dst = gl.glGetIntegerv(gl.GL_BLEND_DST) last_blend_equation_rgb = gl.glGetIntegerv(gl.GL_BLEND_EQUATION_RGB) last_blend_equation_alpha = gl.glGetIntegerv( gl.GL_BLEND_EQUATION_ALPHA) last_viewport = gl.glGetIntegerv(gl.GL_VIEWPORT) last_scissor_box = gl.glGetIntegerv(gl.GL_SCISSOR_BOX) last_enable_blend = gl.glIsEnabled(gl.GL_BLEND) last_enable_cull_face = gl.glIsEnabled(gl.GL_CULL_FACE) last_enable_depth_test = gl.glIsEnabled(gl.GL_DEPTH_TEST) last_enable_scissor_test = gl.glIsEnabled(gl.GL_SCISSOR_TEST) gl.glEnable(gl.GL_BLEND) gl.glBlendEquation(gl.GL_FUNC_ADD) gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA) gl.glDisable(gl.GL_CULL_FACE) gl.glDisable(gl.GL_DEPTH_TEST) gl.glEnable(gl.GL_SCISSOR_TEST) #gl.glActiveTexture(gl.GL_TEXTURE0) gl.glViewport(0, 0, int(fb_width), int(fb_height)) ortho_projection = (ctypes.c_float * 16)(2.0 / display_width, 0.0, 0.0, 0.0, 0.0, 2.0 / -display_height, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, -1.0, 1.0, 0.0, 1.0) self.prog["ProjMtx"] = ortho_projection for commands in draw_data.commands_lists: idx_buffer_offset = 0 array_type = c_ubyte * commands.vtx_buffer_size * imgui.VERTEX_SIZE data_carray = array_type.from_address(commands.vtx_buffer_data) if not ( imgui.VERTEX_BUFFER_POS_OFFSET == 0 and \ imgui.VERTEX_BUFFER_UV_OFFSET == 8 and \ imgui.VERTEX_BUFFER_COL_OFFSET == 16 ): log.error( "GlumpyRenderer.render(): imgui vertex buffer layout has changed ! notify the developers ..." ) return #TODO: this is a bit convoluted; Imgui delivers uint8 colors, but glumpy wants float32's dtype = [('Position', np.float32, 2), ('UV', np.float32, 2), ('Color', np.uint8, 4)] vao_content = np.frombuffer(data_carray, dtype=dtype) dtype2 = [('Position', np.float32, 2), ('UV', np.float32, 2), ('Color', np.float32, 4)] vao_content_f = np.zeros(vao_content.shape, dtype=dtype2) for i, val in enumerate(vao_content): vao_content_f[i] = vao_content[i] vao_content_f[i]['Color'] /= 255 v_array = vao_content_f.view(gloo.VertexArray) self.prog.bind(v_array) if imgui.INDEX_SIZE == 1: dtype = np.uint8 if imgui.INDEX_SIZE == 2: dtype = np.uint16 if imgui.INDEX_SIZE == 4: dtype = np.uint32 # gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, self._elements_handle) # # todo: check this (sizes) # gl.glBufferData(gl.GL_ELEMENT_ARRAY_BUFFER, commands.idx_buffer_size * imgui.INDEX_SIZE, ctypes.c_void_p(commands.idx_buffer_data), gl.GL_STREAM_DRAW) array_type = c_ubyte * commands.idx_buffer_size * imgui.INDEX_SIZE data_carray = array_type.from_address(commands.idx_buffer_data) idx_content = np.frombuffer(data_carray, dtype=dtype) for command in commands.commands: # TODO: ImGui Images will not work yet, homogenizing texture id # allocation by imgui/glumpy is likely a larger issue # #accessing command.texture_id crashes the prog # #gl.glBindTexture(gl.GL_TEXTURE_2D, command.texture_id ) x, y, z, w = command.clip_rect gl.glScissor(int(x), int(fb_height - w), int(z - x), int(w - y)) idx_array = idx_content[idx_buffer_offset:( idx_buffer_offset + command.elem_count)].view( gloo.IndexBuffer) self.prog.draw(mode=gl.GL_TRIANGLES, indices=idx_array) idx_buffer_offset += command.elem_count # restore modified GL state gl.glUseProgram(last_program) gl.glActiveTexture(last_active_texture) gl.glBindTexture(gl.GL_TEXTURE_2D, last_texture) gl.glBindVertexArray(last_vertex_array) gl.glBindBuffer(gl.GL_ARRAY_BUFFER, last_array_buffer) gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, last_element_array_buffer) gl.glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha) gl.glBlendFunc(last_blend_src, last_blend_dst) if last_enable_blend: gl.glEnable(gl.GL_BLEND) else: gl.glDisable(gl.GL_BLEND) if last_enable_cull_face: gl.glEnable(gl.GL_CULL_FACE) else: gl.glDisable(gl.GL_CULL_FACE) if last_enable_depth_test: gl.glEnable(gl.GL_DEPTH_TEST) else: gl.glDisable(gl.GL_DEPTH_TEST) if last_enable_scissor_test: gl.glEnable(gl.GL_SCISSOR_TEST) else: gl.glDisable(gl.GL_SCISSOR_TEST) gl.glViewport(last_viewport[0], last_viewport[1], last_viewport[2], last_viewport[3]) gl.glScissor(last_scissor_box[0], last_scissor_box[1], last_scissor_box[2], last_scissor_box[3]) log.debug("----------------------end---------------------------------")
def __init__(self, mesh, use_depth_test=True, use_face_culling=True, shader_type=PHONG): self.use_depth_test = use_depth_test self.use_face_culling = use_face_culling # Compile shaders if shader_type == self.FLAT: self._program = Program(self.FLAT_VERTEX, self.FLAT_FRAGMENT, version="330") elif shader_type == self.PHONG: self._program = Program(self.PHONG_VERTEX, self.PHONG_FRAGMENT, version="330") else: raise RuntimeError("Unknown shader type: {}".format(shader_type)) self._shader_type = shader_type # Get mesh attributes self._vertices_vbo = np.core.records.fromarrays( [mesh.vertices], dtype=[('a_vertex', np.float32, (3, ))]).view(gloo.VertexBuffer) if mesh.colors is None: print("Mesh has no color. Using default color [0.5, 0, 0, 1].") mesh_colors = np.zeros((self._vertices_vbo.shape[0], 4), dtype=np.float32) mesh_colors[:, :] = [0.5, 0, 0, 1] else: mesh_colors = mesh.colors self._colors_vbo = np.core.records.fromarrays( [mesh_colors], dtype=[('a_color', np.float32, (4, ))]).view(gloo.VertexBuffer) if mesh.normals is None: print("Mesh has no normals. Using default normals [0, 0, 0].") mesh_normals = np.zeros((self._vertices_vbo.shape[0], 3), dtype=np.float32) mesh_normals[:, :] = [0, 0.5, 0] else: mesh_normals = mesh.normals self._normals_vbo = np.core.records.fromarrays( [mesh_normals], dtype=[('a_normal', np.float32, (3, ))]).view(gloo.VertexBuffer) # self._texcoords_vbo = np.core.records.fromarrays([mesh.texcoords], dtype=[('a_texcoords', np.float32, (2,))]).view(gloo.VertexBuffer) self._faces = mesh.faces.view(gloo.IndexBuffer) # Bind vertex attributes self._program.bind(self._vertices_vbo) self._program.bind(self._colors_vbo) self._program.bind(self._normals_vbo) # self._program.bind(self._texcoords_vbo) # Create and fill index buffer self._gl_index_buffer = gl.glGenBuffers(1) gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, self._gl_index_buffer) gl.glBufferData(gl.GL_ELEMENT_ARRAY_BUFFER, self._faces.nbytes, self._faces, gl.GL_STATIC_DRAW) gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, 0) # Initialize uniforms self._color_scale = np.array([1, 1, 1, 1]) self._normal_scale = np.array([1, 1, 1]) self._depth_scale = 1.0 self._light_position = np.array([-25, -25, 50.]) self._light_intensity = 3 self._material = np.array([0.5, 0.5, 0.5]) self._transform = pvm.ModelTransform()