VAO Classes

AbstractVAO

Bases: ABC

Abstract base class for Vertex Array Objects (VAOs).

Defines the interface for different VAO implementations, including methods for binding, drawing, setting data, and managing the VAO's lifecycle.

Source code in ncca/ngl/opengl/abstract_vao.py
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
class AbstractVAO(abc.ABC):
    """Abstract base class for Vertex Array Objects (VAOs).

    Defines the interface for different VAO implementations, including
    methods for binding, drawing, setting data, and managing the VAO's
    lifecycle.
    """

    def __init__(self, mode: int = gl.GL_TRIANGLES) -> None:
        """Generate a new OpenGL VAO id and initialize default state.

        Args:
            mode: OpenGL primitive drawing mode (e.g. GL_TRIANGLES).
        """
        self.id = gl.glGenVertexArrays(1)
        self.mode = mode
        self.bound = False
        self.allocated = False
        self.indices_count = 0

    def bind(self) -> None:
        """Bind this VAO as the current OpenGL vertex array."""
        gl.glBindVertexArray(self.id)
        self.bound = True

    def unbind(self) -> None:
        """Unbind the current OpenGL vertex array."""
        gl.glBindVertexArray(0)
        self.bound = False

    def __enter__(self) -> "AbstractVAO":
        """Bind the VAO on entering a `with` block."""
        self.bind()
        return self

    def __exit__(self, exc_type: type, exc_val: BaseException, exc_tb: Any) -> None:
        """Unbind the VAO on exiting a `with` block."""
        self.unbind()

    @abc.abstractmethod
    def draw(self) -> None:
        """Draw the contents of this VAO."""
        raise NotImplementedError

    @abc.abstractmethod
    def set_data(self, data: VertexData) -> None:
        """Upload vertex data to the VAO's buffer(s)."""
        raise NotImplementedError

    @abc.abstractmethod
    def remove_vao(self) -> None:
        """Delete the VAO and its associated buffers."""
        raise NotImplementedError

    def set_vertex_attribute_pointer(
        self,
        id: int,
        size: int,
        type: int,
        stride: int,
        offset: int,
        normalize: bool = False,
    ) -> None:
        """Configure and enable a vertex attribute pointer for the bound buffer.

        Args:
            id: Attribute location index.
            size: Number of components per vertex attribute.
            type: OpenGL data type of each component (e.g. GL_FLOAT).
            stride: Byte offset between consecutive vertex attributes.
            offset: Byte offset of the first component in the buffer.
            normalize: Whether integer data should be normalized.
        """
        if not self.bound:
            logger.error("VAO not bound in set_vertex_attribute_pointer")
        gl.glVertexAttribPointer(
            id, size, type, normalize, stride, ctypes.c_void_p(offset)
        )
        gl.glEnableVertexAttribArray(id)

    def set_num_indices(self, count: int) -> None:
        """Set the number of indices/vertices to draw."""
        self.indices_count = count

    def num_indices(self) -> int:
        """Return the number of indices/vertices to draw."""
        return self.indices_count

    def get_mode(self) -> int:
        """Return the OpenGL primitive drawing mode."""
        return self.mode

    def set_mode(self, mode: int) -> None:
        """Set the OpenGL primitive drawing mode."""
        self.mode = mode

    @abc.abstractmethod
    def get_buffer_id(self, index: int = 0) -> int:
        """Return the OpenGL buffer id at the given index."""
        raise NotImplementedError

    @abc.abstractmethod
    def map_buffer(self, index: int = 0, access_mode: int = gl.GL_READ_WRITE) -> Any:
        """Map the buffer at the given index into client memory."""
        raise NotImplementedError

    def unmap_buffer(self) -> None:
        """Unmap the currently mapped GL_ARRAY_BUFFER."""
        gl.glUnmapBuffer(gl.GL_ARRAY_BUFFER)

    def get_id(self) -> int:
        """Return the OpenGL VAO id."""
        return self.id

__enter__()

Bind the VAO on entering a with block.

Source code in ncca/ngl/opengl/abstract_vao.py
64
65
66
67
def __enter__(self) -> "AbstractVAO":
    """Bind the VAO on entering a `with` block."""
    self.bind()
    return self

__exit__(exc_type, exc_val, exc_tb)

Unbind the VAO on exiting a with block.

Source code in ncca/ngl/opengl/abstract_vao.py
69
70
71
def __exit__(self, exc_type: type, exc_val: BaseException, exc_tb: Any) -> None:
    """Unbind the VAO on exiting a `with` block."""
    self.unbind()

__init__(mode=gl.GL_TRIANGLES)

Generate a new OpenGL VAO id and initialize default state.

Parameters:
  • mode (int, default: GL_TRIANGLES ) –

    OpenGL primitive drawing mode (e.g. GL_TRIANGLES).

Source code in ncca/ngl/opengl/abstract_vao.py
42
43
44
45
46
47
48
49
50
51
52
def __init__(self, mode: int = gl.GL_TRIANGLES) -> None:
    """Generate a new OpenGL VAO id and initialize default state.

    Args:
        mode: OpenGL primitive drawing mode (e.g. GL_TRIANGLES).
    """
    self.id = gl.glGenVertexArrays(1)
    self.mode = mode
    self.bound = False
    self.allocated = False
    self.indices_count = 0

bind()

Bind this VAO as the current OpenGL vertex array.

Source code in ncca/ngl/opengl/abstract_vao.py
54
55
56
57
def bind(self) -> None:
    """Bind this VAO as the current OpenGL vertex array."""
    gl.glBindVertexArray(self.id)
    self.bound = True

draw() abstractmethod

Draw the contents of this VAO.

Source code in ncca/ngl/opengl/abstract_vao.py
73
74
75
76
@abc.abstractmethod
def draw(self) -> None:
    """Draw the contents of this VAO."""
    raise NotImplementedError

get_buffer_id(index=0) abstractmethod

Return the OpenGL buffer id at the given index.

Source code in ncca/ngl/opengl/abstract_vao.py
130
131
132
133
@abc.abstractmethod
def get_buffer_id(self, index: int = 0) -> int:
    """Return the OpenGL buffer id at the given index."""
    raise NotImplementedError

get_id()

Return the OpenGL VAO id.

Source code in ncca/ngl/opengl/abstract_vao.py
144
145
146
def get_id(self) -> int:
    """Return the OpenGL VAO id."""
    return self.id

get_mode()

Return the OpenGL primitive drawing mode.

Source code in ncca/ngl/opengl/abstract_vao.py
122
123
124
def get_mode(self) -> int:
    """Return the OpenGL primitive drawing mode."""
    return self.mode

map_buffer(index=0, access_mode=gl.GL_READ_WRITE) abstractmethod

Map the buffer at the given index into client memory.

Source code in ncca/ngl/opengl/abstract_vao.py
135
136
137
138
@abc.abstractmethod
def map_buffer(self, index: int = 0, access_mode: int = gl.GL_READ_WRITE) -> Any:
    """Map the buffer at the given index into client memory."""
    raise NotImplementedError

num_indices()

Return the number of indices/vertices to draw.

Source code in ncca/ngl/opengl/abstract_vao.py
118
119
120
def num_indices(self) -> int:
    """Return the number of indices/vertices to draw."""
    return self.indices_count

remove_vao() abstractmethod

Delete the VAO and its associated buffers.

Source code in ncca/ngl/opengl/abstract_vao.py
83
84
85
86
@abc.abstractmethod
def remove_vao(self) -> None:
    """Delete the VAO and its associated buffers."""
    raise NotImplementedError

set_data(data) abstractmethod

Upload vertex data to the VAO's buffer(s).

Source code in ncca/ngl/opengl/abstract_vao.py
78
79
80
81
@abc.abstractmethod
def set_data(self, data: VertexData) -> None:
    """Upload vertex data to the VAO's buffer(s)."""
    raise NotImplementedError

set_mode(mode)

Set the OpenGL primitive drawing mode.

Source code in ncca/ngl/opengl/abstract_vao.py
126
127
128
def set_mode(self, mode: int) -> None:
    """Set the OpenGL primitive drawing mode."""
    self.mode = mode

set_num_indices(count)

Set the number of indices/vertices to draw.

Source code in ncca/ngl/opengl/abstract_vao.py
114
115
116
def set_num_indices(self, count: int) -> None:
    """Set the number of indices/vertices to draw."""
    self.indices_count = count

set_vertex_attribute_pointer(id, size, type, stride, offset, normalize=False)

Configure and enable a vertex attribute pointer for the bound buffer.

Parameters:
  • id (int) –

    Attribute location index.

  • size (int) –

    Number of components per vertex attribute.

  • type (int) –

    OpenGL data type of each component (e.g. GL_FLOAT).

  • stride (int) –

    Byte offset between consecutive vertex attributes.

  • offset (int) –

    Byte offset of the first component in the buffer.

  • normalize (bool, default: False ) –

    Whether integer data should be normalized.

Source code in ncca/ngl/opengl/abstract_vao.py
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
def set_vertex_attribute_pointer(
    self,
    id: int,
    size: int,
    type: int,
    stride: int,
    offset: int,
    normalize: bool = False,
) -> None:
    """Configure and enable a vertex attribute pointer for the bound buffer.

    Args:
        id: Attribute location index.
        size: Number of components per vertex attribute.
        type: OpenGL data type of each component (e.g. GL_FLOAT).
        stride: Byte offset between consecutive vertex attributes.
        offset: Byte offset of the first component in the buffer.
        normalize: Whether integer data should be normalized.
    """
    if not self.bound:
        logger.error("VAO not bound in set_vertex_attribute_pointer")
    gl.glVertexAttribPointer(
        id, size, type, normalize, stride, ctypes.c_void_p(offset)
    )
    gl.glEnableVertexAttribArray(id)

unbind()

Unbind the current OpenGL vertex array.

Source code in ncca/ngl/opengl/abstract_vao.py
59
60
61
62
def unbind(self) -> None:
    """Unbind the current OpenGL vertex array."""
    gl.glBindVertexArray(0)
    self.bound = False

unmap_buffer()

Unmap the currently mapped GL_ARRAY_BUFFER.

Source code in ncca/ngl/opengl/abstract_vao.py
140
141
142
def unmap_buffer(self) -> None:
    """Unmap the currently mapped GL_ARRAY_BUFFER."""
    gl.glUnmapBuffer(gl.GL_ARRAY_BUFFER)

SimpleVAO

Bases: AbstractVAO

A basic VAO implementation that uses a single buffer for non-indexed drawing.

Source code in ncca/ngl/opengl/simple_vao.py
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
class SimpleVAO(AbstractVAO):
    """A basic VAO implementation that uses a single buffer for non-indexed drawing."""

    def __init__(self, mode: int = gl.GL_TRIANGLES) -> None:
        """Create the VAO and generate its single vertex buffer."""
        super().__init__(mode)
        self.buffer = gl.glGenBuffers(1)

    def draw(self) -> None:
        """Draw the VAO's vertices using glDrawArrays."""
        if self.bound and self.allocated:
            gl.glDrawArrays(self.mode, 0, self.indices_count)
        else:
            logger.error("SimpleVAO not bound or not allocated")

    def set_data(self, data: VertexData) -> None:
        """Upload vertex data to the buffer.

        Raises:
            TypeError: If data is not a VertexData instance.
            RuntimeError: If the VAO is not currently bound.
        """
        if not isinstance(data, VertexData):
            logger.error("SimpleVAO: Invalid data type")
            raise TypeError("data must be of type VertexData")
        if not self.bound:
            logger.error("SimpleVAO not bound")
            raise RuntimeError("SimpleVAO not bound")
        gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.buffer)
        gl.glBufferData(gl.GL_ARRAY_BUFFER, data.data.nbytes, data.data, data.mode)
        self.allocated = True
        self.indices_count = data.size

    def num_indices(self) -> int:
        """Return the number of vertices to draw."""
        return self.indices_count

    def remove_vao(self) -> None:
        """Delete the VAO's buffer and vertex array."""
        gl.glDeleteBuffers(1, [self.buffer])
        gl.glDeleteVertexArrays(1, [self.id])

    def get_buffer_id(self, index: int = 0) -> int:
        """Return the OpenGL buffer id (index is ignored, only one buffer)."""
        return self.buffer

    def map_buffer(self, index: int = 0, access_mode: int = gl.GL_READ_WRITE) -> Any:
        """Map the buffer into client memory (index is ignored, only one buffer)."""
        gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.buffer)
        return gl.glMapBuffer(gl.GL_ARRAY_BUFFER, access_mode)

__init__(mode=gl.GL_TRIANGLES)

Create the VAO and generate its single vertex buffer.

Source code in ncca/ngl/opengl/simple_vao.py
14
15
16
17
def __init__(self, mode: int = gl.GL_TRIANGLES) -> None:
    """Create the VAO and generate its single vertex buffer."""
    super().__init__(mode)
    self.buffer = gl.glGenBuffers(1)

draw()

Draw the VAO's vertices using glDrawArrays.

Source code in ncca/ngl/opengl/simple_vao.py
19
20
21
22
23
24
def draw(self) -> None:
    """Draw the VAO's vertices using glDrawArrays."""
    if self.bound and self.allocated:
        gl.glDrawArrays(self.mode, 0, self.indices_count)
    else:
        logger.error("SimpleVAO not bound or not allocated")

get_buffer_id(index=0)

Return the OpenGL buffer id (index is ignored, only one buffer).

Source code in ncca/ngl/opengl/simple_vao.py
53
54
55
def get_buffer_id(self, index: int = 0) -> int:
    """Return the OpenGL buffer id (index is ignored, only one buffer)."""
    return self.buffer

map_buffer(index=0, access_mode=gl.GL_READ_WRITE)

Map the buffer into client memory (index is ignored, only one buffer).

Source code in ncca/ngl/opengl/simple_vao.py
57
58
59
60
def map_buffer(self, index: int = 0, access_mode: int = gl.GL_READ_WRITE) -> Any:
    """Map the buffer into client memory (index is ignored, only one buffer)."""
    gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.buffer)
    return gl.glMapBuffer(gl.GL_ARRAY_BUFFER, access_mode)

num_indices()

Return the number of vertices to draw.

Source code in ncca/ngl/opengl/simple_vao.py
44
45
46
def num_indices(self) -> int:
    """Return the number of vertices to draw."""
    return self.indices_count

remove_vao()

Delete the VAO's buffer and vertex array.

Source code in ncca/ngl/opengl/simple_vao.py
48
49
50
51
def remove_vao(self) -> None:
    """Delete the VAO's buffer and vertex array."""
    gl.glDeleteBuffers(1, [self.buffer])
    gl.glDeleteVertexArrays(1, [self.id])

set_data(data)

Upload vertex data to the buffer.

Raises:
  • TypeError

    If data is not a VertexData instance.

  • RuntimeError

    If the VAO is not currently bound.

Source code in ncca/ngl/opengl/simple_vao.py
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
def set_data(self, data: VertexData) -> None:
    """Upload vertex data to the buffer.

    Raises:
        TypeError: If data is not a VertexData instance.
        RuntimeError: If the VAO is not currently bound.
    """
    if not isinstance(data, VertexData):
        logger.error("SimpleVAO: Invalid data type")
        raise TypeError("data must be of type VertexData")
    if not self.bound:
        logger.error("SimpleVAO not bound")
        raise RuntimeError("SimpleVAO not bound")
    gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.buffer)
    gl.glBufferData(gl.GL_ARRAY_BUFFER, data.data.nbytes, data.data, data.mode)
    self.allocated = True
    self.indices_count = data.size

SimpleIndexVAO

Bases: AbstractVAO

A VAO implementation that uses an index buffer for indexed drawing.

Source code in ncca/ngl/opengl/simple_index_vao.py
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
class SimpleIndexVAO(AbstractVAO):
    """A VAO implementation that uses an index buffer for indexed drawing."""

    def __init__(self, mode: int = gl.GL_TRIANGLES) -> None:
        """Create the VAO and generate its vertex and index buffers."""
        super().__init__(mode)
        self.buffer = gl.glGenBuffers(1)
        self.idx_buffer = gl.glGenBuffers(1)
        self.index_type = gl.GL_UNSIGNED_INT

    def draw(self) -> None:
        """Draw the VAO's vertices using glDrawElements."""
        if self.bound and self.allocated:
            gl.glDrawElements(self.mode, self.indices_count, self.index_type, None)
        else:
            logger.error("SimpleIndexVAO not bound or not allocated")

    def set_data(self, data: IndexVertexData) -> None:
        """Upload vertex and index data to their respective buffers.

        Raises:
            TypeError: If data is not an IndexVertexData instance.
        """
        if not isinstance(data, IndexVertexData):
            logger.error("SimpleIndexVAO: Unsupported index type")
            raise TypeError("data must be of type IndexVertexData")

        gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.buffer)
        gl.glBufferData(gl.GL_ARRAY_BUFFER, data.data.nbytes, data.data, data.mode)

        gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, self.idx_buffer)
        gl.glBufferData(
            gl.GL_ELEMENT_ARRAY_BUFFER, data.indices.nbytes, data.indices, data.mode
        )

        self.allocated = True
        self.indices_count = len(data.indices)
        self.index_type = data.index_type

    def remove_vao(self) -> None:
        """Delete the VAO's vertex buffer, index buffer, and vertex array."""
        gl.glDeleteBuffers(1, [self.buffer])
        gl.glDeleteBuffers(1, [self.idx_buffer])
        gl.glDeleteVertexArrays(1, [self.id])

    def get_buffer_id(self, index: int = 0) -> int:
        """Return the OpenGL vertex buffer id (index is ignored)."""
        return self.buffer

    def map_buffer(self, index: int = 0, access_mode: int = gl.GL_READ_WRITE) -> Any:
        """Map the vertex buffer into client memory (index is ignored)."""
        gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.buffer)
        return gl.glMapBuffer(gl.GL_ARRAY_BUFFER, access_mode)

__init__(mode=gl.GL_TRIANGLES)

Create the VAO and generate its vertex and index buffers.

Source code in ncca/ngl/opengl/simple_index_vao.py
53
54
55
56
57
58
def __init__(self, mode: int = gl.GL_TRIANGLES) -> None:
    """Create the VAO and generate its vertex and index buffers."""
    super().__init__(mode)
    self.buffer = gl.glGenBuffers(1)
    self.idx_buffer = gl.glGenBuffers(1)
    self.index_type = gl.GL_UNSIGNED_INT

draw()

Draw the VAO's vertices using glDrawElements.

Source code in ncca/ngl/opengl/simple_index_vao.py
60
61
62
63
64
65
def draw(self) -> None:
    """Draw the VAO's vertices using glDrawElements."""
    if self.bound and self.allocated:
        gl.glDrawElements(self.mode, self.indices_count, self.index_type, None)
    else:
        logger.error("SimpleIndexVAO not bound or not allocated")

get_buffer_id(index=0)

Return the OpenGL vertex buffer id (index is ignored).

Source code in ncca/ngl/opengl/simple_index_vao.py
95
96
97
def get_buffer_id(self, index: int = 0) -> int:
    """Return the OpenGL vertex buffer id (index is ignored)."""
    return self.buffer

map_buffer(index=0, access_mode=gl.GL_READ_WRITE)

Map the vertex buffer into client memory (index is ignored).

Source code in ncca/ngl/opengl/simple_index_vao.py
 99
100
101
102
def map_buffer(self, index: int = 0, access_mode: int = gl.GL_READ_WRITE) -> Any:
    """Map the vertex buffer into client memory (index is ignored)."""
    gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.buffer)
    return gl.glMapBuffer(gl.GL_ARRAY_BUFFER, access_mode)

remove_vao()

Delete the VAO's vertex buffer, index buffer, and vertex array.

Source code in ncca/ngl/opengl/simple_index_vao.py
89
90
91
92
93
def remove_vao(self) -> None:
    """Delete the VAO's vertex buffer, index buffer, and vertex array."""
    gl.glDeleteBuffers(1, [self.buffer])
    gl.glDeleteBuffers(1, [self.idx_buffer])
    gl.glDeleteVertexArrays(1, [self.id])

set_data(data)

Upload vertex and index data to their respective buffers.

Raises:
  • TypeError

    If data is not an IndexVertexData instance.

Source code in ncca/ngl/opengl/simple_index_vao.py
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
def set_data(self, data: IndexVertexData) -> None:
    """Upload vertex and index data to their respective buffers.

    Raises:
        TypeError: If data is not an IndexVertexData instance.
    """
    if not isinstance(data, IndexVertexData):
        logger.error("SimpleIndexVAO: Unsupported index type")
        raise TypeError("data must be of type IndexVertexData")

    gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.buffer)
    gl.glBufferData(gl.GL_ARRAY_BUFFER, data.data.nbytes, data.data, data.mode)

    gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, self.idx_buffer)
    gl.glBufferData(
        gl.GL_ELEMENT_ARRAY_BUFFER, data.indices.nbytes, data.indices, data.mode
    )

    self.allocated = True
    self.indices_count = len(data.indices)
    self.index_type = data.index_type

MultiBufferVAO

Bases: AbstractVAO

A VAO implementation that can manage multiple vertex buffers.

Useful for separating different types of vertex attributes (e.g. positions, colors, normals) into different buffers.

Source code in ncca/ngl/opengl/multi_buffer_vao.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
class MultiBufferVAO(AbstractVAO):
    """A VAO implementation that can manage multiple vertex buffers.

    Useful for separating different types of vertex attributes (e.g.
    positions, colors, normals) into different buffers.
    """

    def __init__(self, mode: int = gl.GL_TRIANGLES) -> None:
        """Create the VAO with no buffers allocated yet."""
        super().__init__(mode)
        self.vbo_ids: list[int] = []

    def draw(self) -> None:
        """Draw the VAO's vertices using glDrawArrays."""
        if self.bound and self.allocated:
            gl.glDrawArrays(self.mode, 0, self.indices_count)
        else:
            logger.error("MultiBufferVAO is not bound or not allocated")

    def set_data(self, data: VertexData, index: int | None = None) -> None:
        """Upload vertex data to the buffer at index, creating buffers as needed.

        Args:
            data: The vertex data to upload.
            index: Buffer index to upload to; appends a new buffer if None.

        Raises:
            TypeError: If data is not a VertexData instance.
        """
        if not isinstance(data, VertexData):
            logger.error("MultiBufferVAO: Invalid data type")
            raise TypeError("data must be of type VertexData")
        if index is None:
            index = len(self.vbo_ids)

        if index >= len(self.vbo_ids):
            new_buffers = index - len(self.vbo_ids) + 1
            new_ids = gl.glGenBuffers(new_buffers)
            if isinstance(new_ids, np.ndarray):
                self.vbo_ids.extend(new_ids)
            else:
                self.vbo_ids.append(new_ids)

        gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.vbo_ids[index])
        gl.glBufferData(gl.GL_ARRAY_BUFFER, data.data.nbytes, data.data, data.mode)
        self.allocated = True
        if index == 0:  # Assume first buffer determines the number of indices
            self.indices_count = data.size

    def remove_vao(self) -> None:
        """Delete all of the VAO's buffers and its vertex array."""
        gl.glDeleteBuffers(len(self.vbo_ids), self.vbo_ids)
        gl.glDeleteVertexArrays(1, [self.id])

    def get_buffer_id(self, index: int = 0) -> int:
        """Return the OpenGL buffer id at the given index."""
        return self.vbo_ids[index]

    def map_buffer(self, index: int = 0, access_mode: int = gl.GL_READ_WRITE) -> Any:
        """Map the buffer at the given index into client memory."""
        gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.vbo_ids[index])
        return gl.glMapBuffer(gl.GL_ARRAY_BUFFER, access_mode)

__init__(mode=gl.GL_TRIANGLES)

Create the VAO with no buffers allocated yet.

Source code in ncca/ngl/opengl/multi_buffer_vao.py
19
20
21
22
def __init__(self, mode: int = gl.GL_TRIANGLES) -> None:
    """Create the VAO with no buffers allocated yet."""
    super().__init__(mode)
    self.vbo_ids: list[int] = []

draw()

Draw the VAO's vertices using glDrawArrays.

Source code in ncca/ngl/opengl/multi_buffer_vao.py
24
25
26
27
28
29
def draw(self) -> None:
    """Draw the VAO's vertices using glDrawArrays."""
    if self.bound and self.allocated:
        gl.glDrawArrays(self.mode, 0, self.indices_count)
    else:
        logger.error("MultiBufferVAO is not bound or not allocated")

get_buffer_id(index=0)

Return the OpenGL buffer id at the given index.

Source code in ncca/ngl/opengl/multi_buffer_vao.py
66
67
68
def get_buffer_id(self, index: int = 0) -> int:
    """Return the OpenGL buffer id at the given index."""
    return self.vbo_ids[index]

map_buffer(index=0, access_mode=gl.GL_READ_WRITE)

Map the buffer at the given index into client memory.

Source code in ncca/ngl/opengl/multi_buffer_vao.py
70
71
72
73
def map_buffer(self, index: int = 0, access_mode: int = gl.GL_READ_WRITE) -> Any:
    """Map the buffer at the given index into client memory."""
    gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.vbo_ids[index])
    return gl.glMapBuffer(gl.GL_ARRAY_BUFFER, access_mode)

remove_vao()

Delete all of the VAO's buffers and its vertex array.

Source code in ncca/ngl/opengl/multi_buffer_vao.py
61
62
63
64
def remove_vao(self) -> None:
    """Delete all of the VAO's buffers and its vertex array."""
    gl.glDeleteBuffers(len(self.vbo_ids), self.vbo_ids)
    gl.glDeleteVertexArrays(1, [self.id])

set_data(data, index=None)

Upload vertex data to the buffer at index, creating buffers as needed.

Parameters:
  • data (VertexData) –

    The vertex data to upload.

  • index (int | None, default: None ) –

    Buffer index to upload to; appends a new buffer if None.

Raises:
  • TypeError

    If data is not a VertexData instance.

Source code in ncca/ngl/opengl/multi_buffer_vao.py
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
def set_data(self, data: VertexData, index: int | None = None) -> None:
    """Upload vertex data to the buffer at index, creating buffers as needed.

    Args:
        data: The vertex data to upload.
        index: Buffer index to upload to; appends a new buffer if None.

    Raises:
        TypeError: If data is not a VertexData instance.
    """
    if not isinstance(data, VertexData):
        logger.error("MultiBufferVAO: Invalid data type")
        raise TypeError("data must be of type VertexData")
    if index is None:
        index = len(self.vbo_ids)

    if index >= len(self.vbo_ids):
        new_buffers = index - len(self.vbo_ids) + 1
        new_ids = gl.glGenBuffers(new_buffers)
        if isinstance(new_ids, np.ndarray):
            self.vbo_ids.extend(new_ids)
        else:
            self.vbo_ids.append(new_ids)

    gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.vbo_ids[index])
    gl.glBufferData(gl.GL_ARRAY_BUFFER, data.data.nbytes, data.data, data.mode)
    self.allocated = True
    if index == 0:  # Assume first buffer determines the number of indices
        self.indices_count = data.size

VAOFactory

Factory for creating VAOs, extensible with custom creator functions.

Source code in ncca/ngl/opengl/vao_factory.py
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class VAOFactory:
    """Factory for creating VAOs, extensible with custom creator functions."""

    _creators: Dict[VAOType, Callable[[int], AbstractVAO]] = {}

    @staticmethod
    def register_vao_creator(
        name: VAOType, creator_func: Callable[[int], AbstractVAO]
    ) -> None:
        """Register a creator callable for the given VAO type name."""
        VAOFactory._creators[name] = creator_func

    @staticmethod
    def create_vao(name: VAOType, mode: int) -> AbstractVAO:
        """Create a VAO of the named type with the given draw mode.

        Raises:
            ValueError: If the VAO type is not registered.
        """
        creator = VAOFactory._creators.get(name)
        if not creator:
            logger.warning(f"VAO type '{name}' not found.")
            raise ValueError(name)
        return creator(mode)

create_vao(name, mode) staticmethod

Create a VAO of the named type with the given draw mode.

Raises:
  • ValueError

    If the VAO type is not registered.

Source code in ncca/ngl/opengl/vao_factory.py
33
34
35
36
37
38
39
40
41
42
43
44
@staticmethod
def create_vao(name: VAOType, mode: int) -> AbstractVAO:
    """Create a VAO of the named type with the given draw mode.

    Raises:
        ValueError: If the VAO type is not registered.
    """
    creator = VAOFactory._creators.get(name)
    if not creator:
        logger.warning(f"VAO type '{name}' not found.")
        raise ValueError(name)
    return creator(mode)

register_vao_creator(name, creator_func) staticmethod

Register a creator callable for the given VAO type name.

Source code in ncca/ngl/opengl/vao_factory.py
26
27
28
29
30
31
@staticmethod
def register_vao_creator(
    name: VAOType, creator_func: Callable[[int], AbstractVAO]
) -> None:
    """Register a creator callable for the given VAO type name."""
    VAOFactory._creators[name] = creator_func

VAOType

Bases: Enum

Identifiers for the built-in VAO implementations.

Source code in ncca/ngl/opengl/vao_factory.py
13
14
15
16
17
18
class VAOType(enum.Enum):
    """Identifiers for the built-in VAO implementations."""

    SIMPLE = "simpleVAO"
    MULTI_BUFFER = "multiBufferVAO"
    SIMPLE_INDEX = "simpleIndexVAO"

VertexData

A simple data structure to hold vertex data for a VAO.

Source code in ncca/ngl/opengl/abstract_vao.py
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class VertexData:
    """A simple data structure to hold vertex data for a VAO."""

    def __init__(
        self, data: np.ndarray | list[float], size: int, mode: int = gl.GL_STATIC_DRAW
    ) -> None:
        """Store vertex data as a float32 numpy array along with its size and draw mode.

        Args:
            data: Vertex data, either a numpy array or a list of floats.
            size: Number of vertices represented by this data.
            mode: OpenGL buffer usage hint (e.g. GL_STATIC_DRAW).
        """
        if isinstance(data, np.ndarray):
            self.data = data
        else:
            self.data = np.array(data, dtype=np.float32)
        self.size = size
        self.mode = mode

__init__(data, size, mode=gl.GL_STATIC_DRAW)

Store vertex data as a float32 numpy array along with its size and draw mode.

Parameters:
  • data (ndarray | list[float]) –

    Vertex data, either a numpy array or a list of floats.

  • size (int) –

    Number of vertices represented by this data.

  • mode (int, default: GL_STATIC_DRAW ) –

    OpenGL buffer usage hint (e.g. GL_STATIC_DRAW).

Source code in ncca/ngl/opengl/abstract_vao.py
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
def __init__(
    self, data: np.ndarray | list[float], size: int, mode: int = gl.GL_STATIC_DRAW
) -> None:
    """Store vertex data as a float32 numpy array along with its size and draw mode.

    Args:
        data: Vertex data, either a numpy array or a list of floats.
        size: Number of vertices represented by this data.
        mode: OpenGL buffer usage hint (e.g. GL_STATIC_DRAW).
    """
    if isinstance(data, np.ndarray):
        self.data = data
    else:
        self.data = np.array(data, dtype=np.float32)
    self.size = size
    self.mode = mode

IndexVertexData

Bases: VertexData

Vertex data paired with an index buffer for indexed drawing.

Source code in ncca/ngl/opengl/simple_index_vao.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class IndexVertexData(VertexData):
    """Vertex data paired with an index buffer for indexed drawing."""

    def __init__(
        self,
        data: np.ndarray | list[float],
        size: int,
        indices: np.ndarray | list[int],
        index_type: int,
        mode: int = gl.GL_STATIC_DRAW,
    ) -> None:
        """Store vertex data plus an index array of the given GL index type.

        Args:
            data: Vertex data, either a numpy array or a list of floats.
            size: Number of vertices represented by this data.
            indices: Index values referencing vertices in data.
            index_type: OpenGL index type (e.g. GL_UNSIGNED_INT).
            mode: OpenGL buffer usage hint (e.g. GL_STATIC_DRAW).

        Raises:
            TypeError: If index_type is not a supported GL index type.
        """
        super().__init__(data, size, mode)
        gl.GL_to_numpy_type = {
            gl.GL_UNSIGNED_INT: np.uint32,
            gl.GL_UNSIGNED_SHORT: np.uint16,
            gl.GL_UNSIGNED_BYTE: np.uint8,
        }
        numpy_dtype = gl.GL_to_numpy_type.get(index_type)
        if numpy_dtype is None:
            logger.error("SimpleIndexVAO: Unsupported index type")
            raise TypeError(f"Unsupported index type: {index_type}")

        self.indices = np.array(indices, dtype=numpy_dtype)
        self.index_type = index_type

__init__(data, size, indices, index_type, mode=gl.GL_STATIC_DRAW)

Store vertex data plus an index array of the given GL index type.

Parameters:
  • data (ndarray | list[float]) –

    Vertex data, either a numpy array or a list of floats.

  • size (int) –

    Number of vertices represented by this data.

  • indices (ndarray | list[int]) –

    Index values referencing vertices in data.

  • index_type (int) –

    OpenGL index type (e.g. GL_UNSIGNED_INT).

  • mode (int, default: GL_STATIC_DRAW ) –

    OpenGL buffer usage hint (e.g. GL_STATIC_DRAW).

Raises:
  • TypeError

    If index_type is not a supported GL index type.

Source code in ncca/ngl/opengl/simple_index_vao.py
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
def __init__(
    self,
    data: np.ndarray | list[float],
    size: int,
    indices: np.ndarray | list[int],
    index_type: int,
    mode: int = gl.GL_STATIC_DRAW,
) -> None:
    """Store vertex data plus an index array of the given GL index type.

    Args:
        data: Vertex data, either a numpy array or a list of floats.
        size: Number of vertices represented by this data.
        indices: Index values referencing vertices in data.
        index_type: OpenGL index type (e.g. GL_UNSIGNED_INT).
        mode: OpenGL buffer usage hint (e.g. GL_STATIC_DRAW).

    Raises:
        TypeError: If index_type is not a supported GL index type.
    """
    super().__init__(data, size, mode)
    gl.GL_to_numpy_type = {
        gl.GL_UNSIGNED_INT: np.uint32,
        gl.GL_UNSIGNED_SHORT: np.uint16,
        gl.GL_UNSIGNED_BYTE: np.uint8,
    }
    numpy_dtype = gl.GL_to_numpy_type.get(index_type)
    if numpy_dtype is None:
        logger.error("SimpleIndexVAO: Unsupported index type")
        raise TypeError(f"Unsupported index type: {index_type}")

    self.indices = np.array(indices, dtype=numpy_dtype)
    self.index_type = index_type