Skip to content
Snippets Groups Projects
Commit 9cef12c2 authored by Paul McCarthy's avatar Paul McCarthy
Browse files

Tensors are now drawn with only the unique vertices, and an index array.

parent dc5cc572
No related branches found
No related tags found
No related merge requests found
......@@ -29,6 +29,7 @@ class TensorOpts(vectoropts.VectorOpts):
# Tensor ellipsoid resolution
tensorResolution = props.Int(default=10)
def __init__(self, *args, **kwargs):
......
......@@ -12,6 +12,7 @@ import numpy as np
import OpenGL.GL as gl
import OpenGL.raw.GL._types as gltypes
import OpenGL.GL.ARB.instanced_arrays as arbia
import OpenGL.GL.ARB.draw_instanced as arbdi
import fsl.utils.transform as transform
import fsl.fsleyes.gl.resources as glresources
......@@ -63,6 +64,7 @@ def init(self):
setattr(self, '{}Texture'.format(name), tex)
self.vertexBuffer = gl.glGenBuffers(1)
self.indexBuffer = gl.glGenBuffers(1)
self.voxelBuffer = gl.glGenBuffers(1)
self.shaders = None
......@@ -73,6 +75,7 @@ def init(self):
def destroy(self):
gl.glDeleteBuffers(1, gltypes.GLuint(self.vertexBuffer))
gl.glDeleteBuffers(1, gltypes.GLuint(self.indexBuffer))
gl.glDeleteBuffers(1, gltypes.GLuint(self.voxelBuffer))
gl.glDeleteProgram(self.shaders)
......@@ -189,14 +192,23 @@ def updateShaderState(self):
# shader will transform these vertices
# into the tensor ellipsoid for each
# voxel.
vertices = glroutines.unitSphere(resolution).ravel('C')
self.nVertices = len(vertices) / 3
vertices, indices = glroutines.unitSphere(resolution)
self.nVertices = len(indices)
vertices = vertices.ravel('C')
gl.glUseProgram(0)
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.vertexBuffer)
gl.glBufferData(
gl.GL_ARRAY_BUFFER, vertices.nbytes, vertices, gl.GL_STATIC_DRAW)
gl.glUseProgram(0)
gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, self.indexBuffer)
gl.glBufferData(
gl.GL_ELEMENT_ARRAY_BUFFER, indices.nbytes, indices, gl.GL_STATIC_DRAW)
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, 0)
gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, 0)
def preDraw(self):
......@@ -239,20 +251,23 @@ def draw(self, zpos, xform=None):
voxels = transform.transform(voxels, d2vMat)
nVoxels = len(voxels)
voxels = np.array(voxels, dtype=np.float32).ravel('C')
voxels = np.array(voxels, dtype=np.float32).ravel('C')
# Copy the voxel coordinates to the voxel buffer
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.voxelBuffer)
gl.glBufferData(
gl.GL_ARRAY_BUFFER, voxels.nbytes, voxels, gl.GL_STATIC_DRAW)
gl.glVertexAttribPointer(
self.voxelPos, 3, gl.GL_FLOAT, gl.GL_FALSE, 0, None)
# Use one set of voxel coordinates for every sphere drawn
arbia.glVertexAttribDivisorARB(self.voxelPos, 1)
# Bind the vertex buffer
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.vertexBuffer)
gl.glVertexAttribPointer(
self.vertexPos, 3, gl.GL_FLOAT, gl.GL_FALSE, 0, None)
self.vertexPos, 3, gl.GL_FLOAT, gl.GL_FALSE, 0, None)
if xform is None: xform = v2dMat
else: xform = transform.concat(v2dMat, xform)
......@@ -260,12 +275,17 @@ def draw(self, zpos, xform=None):
xform = np.array(xform, dtype=np.float32).ravel('C')
gl.glUniformMatrix4fv(self.voxToDisplayMatPos, 1, False, xform)
gl.glDrawArraysInstanced(gl.GL_QUADS, 0, self.nVertices, nVoxels)
# And the vertex index buffer
gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, self.indexBuffer)
arbdi.glDrawElementsInstancedARB(
gl.GL_QUADS, self.nVertices, gl.GL_UNSIGNED_INT, None, nVoxels)
def postDraw(self):
gl.glUseProgram(0)
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, 0)
gl.glBindBuffer(gl.GL_ARRAY_BUFFER, 0)
gl.glBindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, 0)
self.v1Texture.unbindTexture()
self.v2Texture.unbindTexture()
self.v3Texture.unbindTexture()
......
......@@ -648,14 +648,74 @@ def unitSphere(res):
:arg res: Resolution - the number of angles to sample.
:returns: A tuple comprising:
- a ``numpy.float32`` array of size ``((res + 1)**2, 3)``
containing a set of ``(x, y, z)`` vertices which define
the ellipsoid surface.
- A ``numpy.uint32`` array of size ``(4 * res * res)``
containing a list of indices into the vertex array,
defining a vertex ordering that can be used to draw
the ellipsoid using the OpenGL ``GL_QUAD`` primitive type.
"""
ustep = np.pi / res
vstep = np.pi * 2.0 / res
# All angles to be sampled
u = np.linspace(-np.pi / 2, np.pi / 2 + ustep, res + 1, dtype=np.float32)
v = np.linspace(-np.pi, np.pi + vstep, res + 1, dtype=np.float32)
cosu = np.cos(u)
cosv = np.cos(v)
sinu = np.sin(u)
sinv = np.sin(v)
cucv = np.outer(cosu, cosv)
cusv = np.outer(cosu, sinv)
vertices = np.zeros(((res + 1) ** 2, 3), dtype=np.float32)
# All x coordinates are of the form cos(u) * cos(v),
# y coordinates are of the form cos(u) * sin(v),
# and z coordinates of the form sin(u).
vertices[:, 0] = cucv.flatten()
vertices[:, 1] = cusv.flatten()
vertices[:, 2] = sinu.repeat(res + 1)
# Generate a list of indices which join the
# vertices so they can be used to draw the
# sphere as GL_QUADs.
#
# The vertex locations for each quad follow
# this pattern:
#
# 1. (u, v)
# 2. (u + ustep, v)
# 3. (u + ustep, v + vstep)
# 4. (u, v + vstep)
nquads = res * res
quadIdxs = np.array([0, res + 1, res + 2, 1], dtype=np.uint32)
indices = np.tile(quadIdxs, nquads)
indices += np.arange(nquads, dtype=np.uint32).repeat(4)
indices += np.arange(res, dtype=np.uint32).repeat(4 * res)
return vertices, indices
def fullUnitSphere(res):
"""Generates a unit sphere in the same way as :func:`unitSphere`, but
returns all vertices, instead of the unique vertices and an index array.
:arg res: Resolution - the number of angles to sample.
:returns: A ``numpy.float32`` array of size ``(4 * res**2, 3)`` containing
the ``(x, y, z)`` vertices which can be used to draw a unit
sphere (using the ``GL_QUAD`` primitive type).
"""
# TODO Eliminate duplicate vertices,
# and return an index array
ustep = np.pi / res
vstep = np.pi * 2.0 / res
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment