예제 #1
0
 def __init__(self, block, index):
     assert type(block) == UniformBlock, "Must be a UniformBlock instance"
     self.block = block
     self.buffer = BufferObject(self.block.size, GL_UNIFORM_BUFFER)
     self.buffer.bind()
     self.view = self._introspect_uniforms()
     self._view_ptr = pointer(self.view)
     self.index = index
예제 #2
0
    def __init__(self, program, attribute_meta, index_gl_type=GL_UNSIGNED_INT):
        super(IndexedVertexDomain, self).__init__(program, attribute_meta)

        self.index_allocator = allocation.Allocator(self._initial_index_count)

        self.index_gl_type = index_gl_type
        self.index_c_type = vertexattribute._c_types[index_gl_type]
        self.index_element_size = ctypes.sizeof(self.index_c_type)
        self.index_buffer = BufferObject(
            self.index_allocator.capacity * self.index_element_size,
            GL_ELEMENT_ARRAY_BUFFER)
예제 #3
0
def draw(size, mode, **kwargs):
    """Draw a primitive immediately.

    :Parameters:
        `size` : int
            Number of vertices given
        `mode` : gl primitive type 
            OpenGL drawing mode, e.g. ``GL_TRIANGLES``, 
            avoiding quotes.
        `**data` : keyword arguments for passing vertex attribute data.
            The keyword should be the vertex attribute name, and the
            argument should be a tuple of (format, data). For example:
            `position=('f', array)`

    """
    # Create and bind a throwaway VAO
    vao_id = GLuint()
    glGenVertexArrays(1, vao_id)
    glBindVertexArray(vao_id)
    # Activate shader program:
    program = get_default_shader()
    program.use()

    buffers = []
    for name, (fmt, array) in kwargs.items():
        location = program.attributes[name]['location']
        count = program.attributes[name]['count']
        gl_type = vertexdomain._gl_types[fmt[0]]
        normalize = 'n' in fmt
        attribute = vertexattribute.VertexAttribute(name, location, count,
                                                    gl_type, normalize)
        assert size == len(
            array) // attribute.count, 'Data for %s is incorrect length' % fmt

        buffer = BufferObject(size * attribute.stride, GL_ARRAY_BUFFER)
        attribute.set_region(buffer, 0, size, array)
        attribute.enable()
        attribute.set_pointer(buffer.ptr)

        buffers.append(buffer)  # Don't garbage collect it.

    glDrawArrays(mode, 0, size)

    # Deactivate shader program:
    program.stop()
    # Discard everything after drawing:
    del buffers
    glBindVertexArray(0)
    glDeleteVertexArrays(1, vao_id)
예제 #4
0
def draw_indexed(size, mode, indices, **data):
    """Draw a primitive with indexed vertices immediately.

    :Parameters:
        `size` : int
            Number of vertices given
        `mode` : int
            OpenGL drawing mode, e.g. ``GL_TRIANGLES``
        `indices` : sequence of int
            Sequence of integers giving indices into the vertex list.
        `**data` : keyword arguments for passing vertex attribute data.
            The keyword should be the vertex attribute name, and the
            argument should be a tuple of (format, data). For example:
            `position=('f', array)`

    """
    # Create and bind a throwaway VAO
    vao_id = GLuint()
    glGenVertexArrays(1, vao_id)
    glBindVertexArray(vao_id)
    # Activate shader program:
    program = get_default_shader()
    program.use()

    buffers = []
    for name, (fmt, array) in data.items():
        location = program.attributes[name]['location']
        count = program.attributes[name]['count']
        gl_type = vertexdomain._gl_types[fmt[0]]
        normalize = 'n' in fmt
        attribute = vertexattribute.VertexAttribute(name, location, count, gl_type, normalize)
        assert size == len(array) // attribute.count, 'Data for %s is incorrect length' % fmt

        buffer = BufferObject(size * attribute.stride, GL_ARRAY_BUFFER)
        attribute.set_region(buffer, 0, size, array)
        attribute.enable()
        attribute.set_pointer(buffer.ptr)
        buffers.append(buffer)

    if size <= 0xff:
        index_type = GL_UNSIGNED_BYTE
        index_c_type = ctypes.c_ubyte
    elif size <= 0xffff:
        index_type = GL_UNSIGNED_SHORT
        index_c_type = ctypes.c_ushort
    else:
        index_type = GL_UNSIGNED_INT
        index_c_type = ctypes.c_uint

    # With GL 3.3 vertex arrays indices needs to be in a buffer
    # bound to the ELEMENT_ARRAY slot
    index_array = (index_c_type * len(indices))(*indices)
    index_buffer = BufferObject(ctypes.sizeof(index_array), GL_ELEMENT_ARRAY_BUFFER)
    index_buffer.set_data(index_array)

    glDrawElements(mode, len(indices), index_type, 0)
    glFlush()

    # Deactivate shader program:
    program.stop()
    # Discard everything after drawing:
    del buffers
    del index_buffer
    glBindVertexArray(0)
    glDeleteVertexArrays(1, vao_id)
예제 #5
0
class UniformBufferObject:
    __slots__ = 'block', 'buffer', 'view', '_view', '_view_ptr', 'index'

    def __init__(self, block, index):
        assert type(block) == UniformBlock, "Must be a UniformBlock instance"
        self.block = block
        self.buffer = BufferObject(self.block.size, GL_UNIFORM_BUFFER)
        self.buffer.bind()
        self.view = self._introspect_uniforms()
        self._view_ptr = pointer(self.view)
        self.index = index
        # glUniformBlockBinding(self.block.program.id, self.block.index, self.index)

    @property
    def id(self):
        return self.buffer.id

    def _introspect_uniforms(self):
        p_id = self.block.program.id
        index = self.block.index

        # Query the number of active Uniforms:
        num_active = GLint()
        glGetActiveUniformBlockiv(p_id, index,
                                  GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, num_active)

        # Query the uniform index order and each uniform's offset:
        indices = (GLuint * num_active.value)()
        offsets = (GLint * num_active.value)()
        indices_ptr = cast(addressof(indices), POINTER(GLint))
        offsets_ptr = cast(addressof(offsets), POINTER(GLint))
        glGetActiveUniformBlockiv(p_id, index,
                                  GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES,
                                  indices_ptr)
        glGetActiveUniformsiv(p_id, num_active.value, indices,
                              GL_UNIFORM_OFFSET, offsets_ptr)

        # Offsets may be returned in non-ascending order, sort them with the corresponding index:
        _oi = sorted(zip(offsets, indices), key=lambda x: x[0])
        offsets = [x[0] for x in _oi] + [self.block.size]
        indices = (GLuint * num_active.value)(*(x[1] for x in _oi))

        # Query other uniform information:
        gl_types = (GLint * num_active.value)()
        mat_stride = (GLint * num_active.value)()
        gl_types_ptr = cast(addressof(gl_types), POINTER(GLint))
        stride_ptr = cast(addressof(mat_stride), POINTER(GLint))
        glGetActiveUniformsiv(p_id, num_active.value, indices, GL_UNIFORM_TYPE,
                              gl_types_ptr)
        glGetActiveUniformsiv(p_id, num_active.value, indices,
                              GL_UNIFORM_MATRIX_STRIDE, stride_ptr)

        args = []

        for i in range(num_active.value):
            u_name, gl_type, length = self.block.uniforms[indices[i]]
            size = offsets[i + 1] - offsets[i]
            c_type_size = sizeof(gl_type)
            actual_size = c_type_size * length
            padding = size - actual_size

            # TODO: handle stride for multiple matrixes in the same UBO (crashes now)
            m_stride = mat_stride[i]

            arg = (u_name, gl_type * length) if length > 1 else (u_name,
                                                                 gl_type)
            args.append(arg)

            if padding > 0:
                padding_bytes = padding // c_type_size
                args.append((f'_padding{i}', gl_type * padding_bytes))

        # Custom ctypes Structure for Uniform access:
        class View(Structure):
            _fields_ = args
            __repr__ = lambda self: str(dict(self._fields_))

        return View()

    def bind(self, index=None):
        glUniformBlockBinding(self.block.program.id, self.block.index, index
                              or self.index)
        glBindBufferBase(GL_UNIFORM_BUFFER, index or self.index,
                         self.buffer.id)

    def read(self):
        """Read the byte contents of the buffer"""
        glBindBuffer(GL_UNIFORM_BUFFER, self.buffer.id)
        ptr = glMapBufferRange(GL_UNIFORM_BUFFER, 0, self.buffer.size,
                               GL_MAP_READ_BIT)
        data = string_at(ptr, size=self.buffer.size)
        glUnmapBuffer(GL_UNIFORM_BUFFER)
        return data

    def __enter__(self):
        # Return the view to the user in a `with` context:
        glUniformBlockBinding(self.block.program.id, self.block.index,
                              self.index)
        glBindBufferBase(GL_UNIFORM_BUFFER, self.index, self.buffer.id)
        return self.view

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.buffer.set_data(self._view_ptr)

    def __repr__(self):
        return "{0}(id={1})".format(self.block.name + 'Buffer', self.buffer.id)
예제 #6
0
class IndexedVertexDomain(VertexDomain):
    """Management of a set of indexed vertex lists.

    Construction of an indexed vertex domain is usually done with the
    :py:func:`create_domain` function.
    """
    _initial_index_count = 16

    def __init__(self, program, attribute_meta, index_gl_type=GL_UNSIGNED_INT):
        super(IndexedVertexDomain, self).__init__(program, attribute_meta)

        self.index_allocator = allocation.Allocator(self._initial_index_count)

        self.index_gl_type = index_gl_type
        self.index_c_type = vertexattribute._c_types[index_gl_type]
        self.index_element_size = ctypes.sizeof(self.index_c_type)
        self.index_buffer = BufferObject(
            self.index_allocator.capacity * self.index_element_size,
            GL_ELEMENT_ARRAY_BUFFER)

    def safe_index_alloc(self, count):
        """Allocate indices, resizing the buffers if necessary."""
        try:
            return self.index_allocator.alloc(count)
        except allocation.AllocatorMemoryException as e:
            capacity = _nearest_pow2(e.requested_capacity)
            self.version += 1
            self.index_buffer.resize(capacity * self.index_element_size)
            self.index_allocator.set_capacity(capacity)
            return self.index_allocator.alloc(count)

    def safe_index_realloc(self, start, count, new_count):
        """Reallocate indices, resizing the buffers if necessary."""
        try:
            return self.index_allocator.realloc(start, count, new_count)
        except allocation.AllocatorMemoryException as e:
            capacity = _nearest_pow2(e.requested_capacity)
            self.version += 1
            self.index_buffer.resize(capacity * self.index_element_size)
            self.index_allocator.set_capacity(capacity)
            return self.index_allocator.realloc(start, count, new_count)

    def create(self, count, index_count):
        """Create an :py:class:`IndexedVertexList` in this domain.

        :Parameters:
            `count` : int
                Number of vertices to create
            `index_count`
                Number of indices to create

        """
        start = self.safe_alloc(count)
        index_start = self.safe_index_alloc(index_count)
        return IndexedVertexList(self, start, count, index_start, index_count)

    def get_index_region(self, start, count):
        """Get a data from a region of the index buffer.

        :Parameters:
            `start` : int
                Start of the region to map.
            `count` : int
                Number of indices to map.

        :rtype: Array of int
        """
        byte_start = self.index_element_size * start
        byte_count = self.index_element_size * count
        ptr_type = ctypes.POINTER(self.index_c_type * count)
        map_ptr = self.index_buffer.map_range(byte_start, byte_count, ptr_type)
        data = map_ptr[:]
        self.index_buffer.unmap()
        return data

    def set_index_region(self, start, count, data):
        byte_start = self.index_element_size * start
        byte_count = self.index_element_size * count
        ptr_type = ctypes.POINTER(self.index_c_type * count)
        map_ptr = self.index_buffer.map_range(byte_start, byte_count, ptr_type)
        map_ptr[:] = data
        self.index_buffer.unmap()

    def draw(self, mode):
        """Draw all vertices in the domain.

        All vertices in the domain are drawn at once. This is the
        most efficient way to render primitives.

        :Parameters:
            `mode` : int
                OpenGL drawing mode, e.g. ``GL_POINTS``, ``GL_LINES``, etc.

        """
        self.vao.bind()

        for buffer, attributes in self.buffer_attributes:
            buffer.bind()
            for attribute in attributes:
                attribute.enable()
                attribute.set_pointer(attribute.buffer.ptr)
        self.index_buffer.bind()

        starts, sizes = self.index_allocator.get_allocated_regions()
        primcount = len(starts)
        if primcount == 0:
            pass
        elif primcount == 1:
            # Common case
            glDrawElements(
                mode, sizes[0], self.index_gl_type,
                self.index_buffer.ptr + starts[0] * self.index_element_size)
        else:
            starts = [
                s * self.index_element_size + self.index_buffer.ptr
                for s in starts
            ]
            starts = (ctypes.POINTER(GLvoid) *
                      primcount)(*(GLintptr * primcount)(*starts))
            sizes = (GLsizei * primcount)(*sizes)
            glMultiDrawElements(mode, sizes, self.index_gl_type, starts,
                                primcount)

        self.index_buffer.unbind()
        for buffer, _ in self.buffer_attributes:
            buffer.unbind()

    def draw_subset(self, mode, vertex_list):
        """Draw a specific IndexedVertexList in the domain.

        The `vertex_list` parameter specifies a :py:class:`IndexedVertexList`
        to draw. Only primitives in that list will be drawn.

        :Parameters:
            `mode` : int
                OpenGL drawing mode, e.g. ``GL_POINTS``, ``GL_LINES``, etc.
            `vertex_list` : `IndexedVertexList`
                Vertex list to draw.

        """
        self.vao.bind()

        for buffer, attributes in self.buffer_attributes:
            buffer.bind()
            for attribute in attributes:
                attribute.enable()
                attribute.set_pointer(attribute.buffer.ptr)
        self.index_buffer.bind()

        glDrawElements(
            mode, vertex_list.index_count, self.index_gl_type,
            self.index_buffer.ptr +
            vertex_list.index_start * self.index_element_size)

        self.index_buffer.unbind()
        for buffer, _ in self.buffer_attributes:
            buffer.unbind()