pyqtgraph/opengl/items/GLVolumeItem.py
Luke Campagnola 872fcb17ff Added basic OpenGL scenegraph system
- rotate/scalable view widget
 - volumetric data item
2012-03-06 01:22:02 -05:00

180 lines
5.3 KiB
Python

from OpenGL.GL import *
from .. GLGraphicsItem import GLGraphicsItem
from pyqtgraph.Qt import QtGui
import numpy as np
__all__ = ['GLVolumeItem']
class GLVolumeItem(GLGraphicsItem):
def initializeGL(self):
n = 128
self.data = np.random.randint(0, 255, size=4*n**3).astype(np.uint8).reshape((n,n,n,4))
self.data[...,3] *= 0.1
for i in range(n):
self.data[i,:,:,0] = i*256./n
glEnable(GL_TEXTURE_3D)
self.texture = glGenTextures(1)
glBindTexture(GL_TEXTURE_3D, self.texture)
#glTexImage3D( GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, void *data );
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER)
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER)
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER)
#glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_BORDER_COLOR, ) ## black/transparent by default
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, n, n, n, 0, GL_RGBA, GL_UNSIGNED_BYTE, self.data)
glDisable(GL_TEXTURE_3D)
self.lists = {}
for ax in [0,1,2]:
for d in [-1, 1]:
l = glGenLists(1)
self.lists[(ax,d)] = l
glNewList(l, GL_COMPILE)
self.drawVolume(ax, d)
glEndList()
def paint(self):
glEnable(GL_TEXTURE_3D)
glBindTexture(GL_TEXTURE_3D, self.texture)
glDisable(GL_DEPTH_TEST)
#glDisable(GL_CULL_FACE)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glEnable( GL_BLEND )
glEnable( GL_ALPHA_TEST )
view = self.view()
cam = view.cameraPosition()
cam = np.array([cam.x(), cam.y(), cam.z()])
ax = np.argmax(abs(cam))
d = 1 if cam[ax] > 0 else -1
glCallList(self.lists[(ax,d)]) ## draw axes
glDisable(GL_TEXTURE_3D)
def drawVolume(self, ax, d):
slices = 256
N = 5
imax = [0,1,2]
imax.remove(ax)
tp = [[0,0,0],[0,0,0],[0,0,0],[0,0,0]]
vp = [[0,0,0],[0,0,0],[0,0,0],[0,0,0]]
tp[0][imax[0]] = 0
tp[0][imax[1]] = 0
tp[1][imax[0]] = 1
tp[1][imax[1]] = 0
tp[2][imax[0]] = 1
tp[2][imax[1]] = 1
tp[3][imax[0]] = 0
tp[3][imax[1]] = 1
vp[0][imax[0]] = -N
vp[0][imax[1]] = -N
vp[1][imax[0]] = N
vp[1][imax[1]] = -N
vp[2][imax[0]] = N
vp[2][imax[1]] = N
vp[3][imax[0]] = -N
vp[3][imax[1]] = N
r = range(slices)
if d == -1:
r = r[::-1]
glBegin(GL_QUADS)
for i in r:
z = float(i)/(slices-1.)
w = float(i)*10./(slices-1.) - 5.
tp[0][ax] = z
tp[1][ax] = z
tp[2][ax] = z
tp[3][ax] = z
vp[0][ax] = w
vp[1][ax] = w
vp[2][ax] = w
vp[3][ax] = w
glTexCoord3f(*tp[0])
glVertex3f(*vp[0])
glTexCoord3f(*tp[1])
glVertex3f(*vp[1])
glTexCoord3f(*tp[2])
glVertex3f(*vp[2])
glTexCoord3f(*tp[3])
glVertex3f(*vp[3])
glEnd()
## Interesting idea:
## remove projection/modelview matrixes, recreate in texture coords.
## it _sorta_ works, but needs tweaking.
#mvm = glGetDoublev(GL_MODELVIEW_MATRIX)
#pm = glGetDoublev(GL_PROJECTION_MATRIX)
#m = QtGui.QMatrix4x4(mvm.flatten()).inverted()[0]
#p = QtGui.QMatrix4x4(pm.flatten()).inverted()[0]
#glMatrixMode(GL_PROJECTION)
#glPushMatrix()
#glLoadIdentity()
#N=1
#glOrtho(-N,N,-N,N,-100,100)
#glMatrixMode(GL_MODELVIEW)
#glLoadIdentity()
#glMatrixMode(GL_TEXTURE)
#glLoadIdentity()
#glMultMatrixf(m.copyDataTo())
#view = self.view()
#w = view.width()
#h = view.height()
#dist = view.opts['distance']
#fov = view.opts['fov']
#nearClip = dist * .1
#farClip = dist * 5.
#r = nearClip * np.tan(fov)
#t = r * h / w
#p = QtGui.QMatrix4x4()
#p.frustum( -r, r, -t, t, nearClip, farClip)
#glMultMatrixf(p.inverted()[0].copyDataTo())
#glBegin(GL_QUADS)
#M=1
#for i in range(500):
#z = i/500.
#w = -i/500.
#glTexCoord3f(-M, -M, z)
#glVertex3f(-N, -N, w)
#glTexCoord3f(M, -M, z)
#glVertex3f(N, -N, w)
#glTexCoord3f(M, M, z)
#glVertex3f(N, N, w)
#glTexCoord3f(-M, M, z)
#glVertex3f(-N, N, w)
#glEnd()
#glDisable(GL_TEXTURE_3D)
#glMatrixMode(GL_PROJECTION)
#glPopMatrix()