Source code for Elements.pyGLV.GL.VertexArray

"""
VertexArray class
    
The VertexArray Compoment class is the dedicated to a specific type of data container Component
that of assembling, using and destroying OpenGL API vertex array and buffer objects

Based on the Composite and Iterator design patterns:

* https://refactoring.guru/design-patterns/composite
* https://github.com/faif/python-patterns/blob/master/patterns/structural/composite.py
* https://refactoring.guru/design-patterns/iterator
* https://github.com/faif/python-patterns/blob/master/patterns/behavioral/iterator.py

"""

from __future__         import annotations
from abc                import ABC, abstractmethod
from typing             import List

import OpenGL.GL as gl
import numpy as np

import Elements.pyECSS.System
from Elements.pyECSS.Component import Component, CompNullIterator
import atexit

[docs]class VertexArray(Component): """ A concrete VertexArray class """
[docs] def __init__(self, name=None, type=None, id=None, attributes=None, index=None, primitive = gl.GL_TRIANGLES, usage=gl.GL_STATIC_DRAW): """ Initializes a VertexArray class """ super().__init__(name, type, id) self._parent = self self._glid = None self._buffers = [] #store all GL buffers self._draw_command = None self._arguments = (0,0) self._attributes = attributes self._index = index self._usage = usage self._primitive = primitive #e.g. GL.GL_TRIANGLES atexit.register(self.__del__)
#self.init(attributes, index, usage) #init after a valid GL context is active @property def glid(self): return self._glid @property def attributes(self): # vertex positions, colors, normals, texcoords lists return self._attributes @attributes.setter def attributes(self, value): self._attributes = value @property def index(self): return self._index @index.setter def index(self, value): self._index = value @property def usage(self): return self._usage @usage.setter def usage(self, value): self._usage = value @property def primitive(self): return self._primitive @primitive.setter def primitive(self, value): self._primitive = value def __del__(self): # using atexit to ensure correct destruction order and to avoid yielding errors and exceptions # see https://stackoverflow.com/questions/72238460/python-importerror-sys-meta-path-is-none-python-is-likely-shutting-down gl.glDeleteVertexArrays(1, [self._glid]) gl.glDeleteBuffers(len(self._buffers), self._buffers) def draw(self): gl.glBindVertexArray(self._glid) self._draw_command(self._primitive, *self._arguments) gl.glBindVertexArray(0) def update(self): self.draw() def accept(self, system: Elements.pyECSS.System): """ Accepts a class object to operate on the Component, based on the Visitor pattern. :param system: [a System object] :type system: [System] """ system.apply2VertexArray(self) def init(self): """ Extra method for extra initialisation pf VertexArray Vertex array from attributes and optional index array. Vertex Attributes should be list of arrays with one row per vertex. """ # create and bind(use) a vertex array object self._glid = gl.glGenVertexArrays(1) gl.glBindVertexArray(self._glid) nb_primitives, size = 0, 0 # load buffer per vertex attribute (in a list with index = shader layout) for loc, data in enumerate(self._attributes): if data is not None and len(data) : #check if it is empty # bind a new VBO, upload it to GPU, declare size and type self._buffers.append(gl.glGenBuffers(1)) data = np.array(data, np.float32, copy=False) nb_primitives, size = data.shape gl.glEnableVertexAttribArray(loc) gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self._buffers[-1]) gl.glBufferData(gl.GL_ARRAY_BUFFER, data, self._usage) gl.glVertexAttribPointer(loc, size, gl.GL_FLOAT, False, 0, None) #optionally create and upload an index buffer for this VBO self._draw_command = gl.glDrawArrays self._arguments = (0, nb_primitives) if self._index is not None and len(self._index): #check if list is empty self._buffers += [gl.glGenBuffers(1)] index_buffer = np.array(self._index, np.int32, copy=False) gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, self._buffers[-1]) gl.glBufferData(gl.GL_ELEMENT_ARRAY_BUFFER, index_buffer, self._usage) self._draw_command = gl.glDrawElements self._arguments = (index_buffer.size, gl.GL_UNSIGNED_INT, None) # cleanup and unbind so no accidental subsequent state update gl.glBindVertexArray(0) gl.glBindBuffer(gl.GL_ARRAY_BUFFER, 0) def __iter__(self) ->CompNullIterator: """ A component does not have children to iterate, thus a NULL iterator """ return CompNullIterator(self)