summaryrefslogtreecommitdiff
path: root/silx/gui/_glutils/VertexBuffer.py
diff options
context:
space:
mode:
Diffstat (limited to 'silx/gui/_glutils/VertexBuffer.py')
-rw-r--r--silx/gui/_glutils/VertexBuffer.py266
1 files changed, 266 insertions, 0 deletions
diff --git a/silx/gui/_glutils/VertexBuffer.py b/silx/gui/_glutils/VertexBuffer.py
new file mode 100644
index 0000000..689b543
--- /dev/null
+++ b/silx/gui/_glutils/VertexBuffer.py
@@ -0,0 +1,266 @@
+# coding: utf-8
+# /*##########################################################################
+#
+# Copyright (c) 2014-2017 European Synchrotron Radiation Facility
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+#
+# ###########################################################################*/
+"""This module provides a class managing an OpenGL vertex buffer."""
+
+__authors__ = ["T. Vincent"]
+__license__ = "MIT"
+__date__ = "10/01/2017"
+
+
+import logging
+from ctypes import c_void_p
+import numpy
+
+from . import gl
+from .utils import numpyToGLType, sizeofGLType
+
+
+_logger = logging.getLogger(__name__)
+
+
+class VertexBuffer(object):
+ """Object handling an OpenGL vertex buffer object
+
+ :param data: Data used to fill the vertex buffer
+ :type data: numpy.ndarray or None
+ :param int size: Size in bytes of the buffer or None for data size
+ :param usage: OpenGL vertex buffer expected usage pattern:
+ GL_STREAM_DRAW, GL_STATIC_DRAW (default) or GL_DYNAMIC_DRAW
+ :param target: Target buffer:
+ GL_ARRAY_BUFFER (default) or GL_ELEMENT_ARRAY_BUFFER
+ """
+ # OpenGL|ES 2.0 subset:
+ _USAGES = gl.GL_STREAM_DRAW, gl.GL_STATIC_DRAW, gl.GL_DYNAMIC_DRAW
+ _TARGETS = gl.GL_ARRAY_BUFFER, gl.GL_ELEMENT_ARRAY_BUFFER
+
+ def __init__(self,
+ data=None,
+ size=None,
+ usage=None,
+ target=None):
+ if usage is None:
+ usage = gl.GL_STATIC_DRAW
+ assert usage in self._USAGES
+
+ if target is None:
+ target = gl.GL_ARRAY_BUFFER
+ assert target in self._TARGETS
+
+ self._target = target
+ self._usage = usage
+
+ self._name = gl.glGenBuffers(1)
+ self.bind()
+
+ if data is None:
+ assert size is not None
+ self._size = size
+ gl.glBufferData(self._target,
+ self._size,
+ c_void_p(0),
+ self._usage)
+ else:
+ data = numpy.array(data, copy=False, order='C')
+ if size is not None:
+ assert size <= data.nbytes
+
+ self._size = size or data.nbytes
+ gl.glBufferData(self._target,
+ self._size,
+ data,
+ self._usage)
+
+ gl.glBindBuffer(self._target, 0)
+
+ @property
+ def target(self):
+ """The target buffer of the vertex buffer"""
+ return self._target
+
+ @property
+ def usage(self):
+ """The expected usage of the vertex buffer"""
+ return self._usage
+
+ @property
+ def name(self):
+ """OpenGL Vertex Buffer object name (int)"""
+ if self._name is not None:
+ return self._name
+ else:
+ raise RuntimeError("No OpenGL buffer resource, \
+ discard has already been called")
+
+ @property
+ def size(self):
+ """Size in bytes of the Vertex Buffer Object (int)"""
+ if self._size is not None:
+ return self._size
+ else:
+ raise RuntimeError("No OpenGL buffer resource, \
+ discard has already been called")
+
+ def bind(self):
+ """Bind the vertex buffer"""
+ gl.glBindBuffer(self._target, self.name)
+
+ def update(self, data, offset=0, size=None):
+ """Update vertex buffer content.
+
+ :param numpy.ndarray data: The data to put in the vertex buffer
+ :param int offset: Offset in bytes in the buffer where to put the data
+ :param int size: If provided, size of data to copy
+ """
+ data = numpy.array(data, copy=False, order='C')
+ if size is None:
+ size = data.nbytes
+ assert offset + size <= self.size
+ with self:
+ gl.glBufferSubData(self._target, offset, size, data)
+
+ def discard(self):
+ """Delete the vertex buffer"""
+ if self._name is not None:
+ gl.glDeleteBuffers(self._name)
+ self._name = None
+ self._size = None
+ else:
+ _logger.warning("Discard has already been called")
+
+ # with statement
+
+ def __enter__(self):
+ self.bind()
+
+ def __exit__(self, exctype, excvalue, traceback):
+ gl.glBindBuffer(self._target, 0)
+
+
+class VertexBufferAttrib(object):
+ """Describes data stored in a vertex buffer
+
+ Convenient class to store info for glVertexAttribPointer calls
+
+ :param VertexBuffer vbo: The vertex buffer storing the data
+ :param int type_: The OpenGL type of the data
+ :param int size: The number of data elements stored in the VBO
+ :param int dimension: The number of `type_` element(s) in [1, 4]
+ :param int offset: Start offset of data in the vertex buffer
+ :param int stride: Data stride in the vertex buffer
+ """
+
+ _GL_TYPES = gl.GL_UNSIGNED_BYTE, gl.GL_FLOAT, gl.GL_INT
+
+ def __init__(self,
+ vbo,
+ type_,
+ size,
+ dimension=1,
+ offset=0,
+ stride=0,
+ normalisation=False):
+ self.vbo = vbo
+ assert type_ in self._GL_TYPES
+ self.type_ = type_
+ self.size = size
+ assert 1 <= dimension <= 4
+ self.dimension = dimension
+ self.offset = offset
+ self.stride = stride
+ self.normalisation = bool(normalisation)
+
+ @property
+ def itemsize(self):
+ """Size in bytes of a vertex buffer element (int)"""
+ return self.dimension * sizeofGLType(self.type_)
+
+ itemSize = itemsize # Backward compatibility
+
+ def setVertexAttrib(self, attribute):
+ """Call glVertexAttribPointer with objects information"""
+ normalisation = gl.GL_TRUE if self.normalisation else gl.GL_FALSE
+ with self.vbo:
+ gl.glVertexAttribPointer(attribute,
+ self.dimension,
+ self.type_,
+ normalisation,
+ self.stride,
+ c_void_p(self.offset))
+
+ def copy(self):
+ return VertexBufferAttrib(self.vbo,
+ self.type_,
+ self.size,
+ self.dimension,
+ self.offset,
+ self.stride,
+ self.normalisation)
+
+
+def vertexBuffer(arrays, prefix=None, suffix=None, usage=None):
+ """Create a single vertex buffer from multiple 1D or 2D numpy arrays.
+
+ It is possible to reserve memory before and after each array in the VBO
+
+ :param arrays: Arrays of data to store
+ :type arrays: Iterable of numpy.ndarray
+ :param prefix: If given, number of elements to reserve before each array
+ :type prefix: Iterable of int or None
+ :param suffix: If given, number of elements to reserve after each array
+ :type suffix: Iterable of int or None
+ :param int usage: vertex buffer expected usage or None for default
+ :returns: List of VertexBufferAttrib objects sharing the same vertex buffer
+ """
+ info = []
+ vbosize = 0
+
+ if prefix is None:
+ prefix = (0,) * len(arrays)
+ if suffix is None:
+ suffix = (0,) * len(arrays)
+
+ for data, pre, post in zip(arrays, prefix, suffix):
+ data = numpy.array(data, copy=False, order='C')
+ shape = data.shape
+ assert len(shape) <= 2
+ type_ = numpyToGLType(data.dtype)
+ size = shape[0] + pre + post
+ dimension = 1 if len(shape) == 1 else shape[1]
+ sizeinbytes = size * dimension * sizeofGLType(type_)
+ sizeinbytes = 4 * ((sizeinbytes + 3) >> 2) # 4 bytes alignment
+ copyoffset = vbosize + pre * dimension * sizeofGLType(type_)
+ info.append((data, type_, size, dimension,
+ vbosize, sizeinbytes, copyoffset))
+ vbosize += sizeinbytes
+
+ vbo = VertexBuffer(size=vbosize, usage=usage)
+
+ result = []
+ for data, type_, size, dimension, offset, sizeinbytes, copyoffset in info:
+ copysize = data.shape[0] * dimension * sizeofGLType(type_)
+ vbo.update(data, offset=copyoffset, size=copysize)
+ result.append(
+ VertexBufferAttrib(vbo, type_, size, dimension, offset, 0))
+ return result