Fixed up MeshData and GLMeshItem classes for surface display

This commit is contained in:
Luke Campagnola 2012-03-24 12:17:48 -04:00
parent f6da6e2fd0
commit d2d812c86e
2 changed files with 124 additions and 112 deletions

View File

@ -1,3 +1,5 @@
from pyqtgraph.Qt import QtGui
class MeshData(object): class MeshData(object):
""" """
Class for storing 3D mesh data. May contain: Class for storing 3D mesh data. May contain:
@ -9,15 +11,16 @@ class MeshData(object):
""" """
def __init__(self): def __init__(self):
self.vertexes = [] self._vertexes = []
self.edges = None self._edges = None
self.faces = [] self._faces = []
self.vertexFaces = None ## maps vertex ID to a list of face IDs self._vertexFaces = None ## maps vertex ID to a list of face IDs
self.vertexNormals = None self._vertexNormals = None
self.faceNormals = None self._faceNormals = None
self.vertexColors = None self._vertexColors = None
self.edgeColors = None self._edgeColors = None
self.faceColors = None self._faceColors = None
self._meshColor = (1, 1, 1, 0.1) # default color to use if no face/edge/vertex colors are given
def setFaces(self, faces, vertexes=None): def setFaces(self, faces, vertexes=None):
""" """
@ -30,84 +33,111 @@ class MeshData(object):
""" """
if vertexes is None: if vertexes is None:
self._setUnindexedFaces(self, faces) self._setUnindexedFaces(faces)
else: else:
self._setIndexedFaces(self, faces) self._setIndexedFaces(faces, vertexes)
def _setUnindexedFaces(self, faces): def _setUnindexedFaces(self, faces):
verts = {} verts = {}
self.faces = [] self._faces = []
self.vertexes = [] self._vertexes = []
self.vertexFaces = [] self._vertexFaces = []
self.faceNormals = None self._faceNormals = None
self.vertexNormals = None self._vertexNormals = None
for face in faces: for face in faces:
inds = [] inds = []
for pt in face: for pt in face:
pt2 = tuple([int(x*1e14) for x in pt]) ## quantize to be sure that nearly-identical points will be merged pt2 = tuple([round(x*1e14) for x in pt]) ## quantize to be sure that nearly-identical points will be merged
index = verts.get(pt2, None) index = verts.get(pt2, None)
if index is None: if index is None:
self.vertexes.append(tuple(pt)) self._vertexes.append(QtGui.QVector3D(*pt))
self.vertexFaces.append([]) self._vertexFaces.append([])
index = len(self.vertexes)-1 index = len(self._vertexes)-1
verts[pt2] = index verts[pt2] = index
self.vertexFaces[index].append(face) self._vertexFaces[index].append(len(self._faces))
inds.append(index) inds.append(index)
self.faces.append(tuple(inds)) self._faces.append(tuple(inds))
def _setIndexedFaces(self, faces, vertexes): def _setIndexedFaces(self, faces, vertexes):
self.vertexes = vertexes self._vertexes = [QtGui.QVector3D(*v) for v in vertexes]
self.faces = faces self._faces = faces
self.edges = None self._edges = None
self.vertexFaces = None self._vertexFaces = None
self.faceNormals = None self._faceNormals = None
self.vertexNormals = None self._vertexNormals = None
def getVertexFaces(self): def vertexFaces(self):
""" """
Return list mapping each vertex index to a list of face indexes that use the vertex. Return list mapping each vertex index to a list of face indexes that use the vertex.
""" """
if self.vertexFaces is None: if self._vertexFaces is None:
self.vertexFaces = [[]] * len(self.vertexes) self._vertexFaces = [[]] * len(self._vertexes)
for i, face in enumerate(self.faces): for i, face in enumerate(self._faces):
for ind in face: for ind in face:
if len(self.vertexFaces[ind]) == 0: if len(self._vertexFaces[ind]) == 0:
self.vertexFaces[ind] = [] ## need a unique/empty list to fill self._vertexFaces[ind] = [] ## need a unique/empty list to fill
self.vertexFaces[ind].append(i) self._vertexFaces[ind].append(i)
return self.vertexFaces return self._vertexFaces
def __iter__(self):
"""Iterate over all faces, yielding a list of three tuples [(position, normal, color), ...] for each face."""
vnorms = self.vertexNormals()
vcolors = self.vertexColors()
for i in range(len(self._faces)):
face = []
for j in [0,1,2]:
vind = self._faces[i][j]
pos = self._vertexes[vind]
norm = vnorms[vind]
if vcolors is None:
color = self._meshColor
else:
color = vcolors[vind]
face.append((pos, norm, color))
yield face
def getFaceNormals(self): def faceNormals(self):
""" """
Computes and stores normal of each face. Computes and stores normal of each face.
""" """
if self.faceNormals is None: if self._faceNormals is None:
self.faceNormals = [] self._faceNormals = []
for i, face in enumerate(self.faces): for i, face in enumerate(self._faces):
## compute face normal ## compute face normal
pts = [QtGui.QVector3D(*self.vertexes[vind]) for vind in face] pts = [self._vertexes[vind] for vind in face]
norm = QtGui.QVector3D.crossProduct(pts[1]-pts[0], pts[2]-pts[0]) norm = QtGui.QVector3D.crossProduct(pts[1]-pts[0], pts[2]-pts[0]).normalized()
self.faceNormals.append(norm) self._faceNormals.append(norm)
return self.faceNormals return self._faceNormals
def getVertexNormals(self): def vertexNormals(self):
""" """
Assigns each vertex the average of its connected face normals. Assigns each vertex the average of its connected face normals.
If face normals have not been computed yet, then generateFaceNormals will be called. If face normals have not been computed yet, then generateFaceNormals will be called.
""" """
if self.vertexNormals is None: if self._vertexNormals is None:
faceNorms = self.getFaceNormals() faceNorms = self.faceNormals()
vertFaces = self.getVertexFaces() vertFaces = self.vertexFaces()
self.vertexNormals = [] self._vertexNormals = []
for vindex in xrange(len(self.vertexes)): for vindex in xrange(len(self._vertexes)):
#print vertFaces[vindex]
norms = [faceNorms[findex] for findex in vertFaces[vindex]] norms = [faceNorms[findex] for findex in vertFaces[vindex]]
if len(norms) == 0:
norm = QtGui.QVector3D() norm = QtGui.QVector3D()
else: for fn in norms:
norm = reduce(QtGui.QVector3D.__add__, facenorms) / float(len(norms)) norm += fn
self.vertexNormals.append(norm) norm.normalize()
return self.vertexNormals self._vertexNormals.append(norm)
return self._vertexNormals
def vertexColors(self):
return self._vertexColors
def faceColors(self):
return self._faceColors
def edgeColors(self):
return self._edgeColors
def reverseNormals(self): def reverseNormals(self):
""" """

View File

@ -1,5 +1,6 @@
from OpenGL.GL import * from OpenGL.GL import *
from .. GLGraphicsItem import GLGraphicsItem from .. GLGraphicsItem import GLGraphicsItem
from .. MeshData import MeshData
from pyqtgraph.Qt import QtGui from pyqtgraph.Qt import QtGui
import pyqtgraph as pg import pyqtgraph as pg
from .. import shaders from .. import shaders
@ -10,33 +11,20 @@ import numpy as np
__all__ = ['GLMeshItem'] __all__ = ['GLMeshItem']
class GLMeshItem(GLGraphicsItem): class GLMeshItem(GLGraphicsItem):
def __init__(self, faces): """
self.faces = faces Displays a 3D triangle mesh.
self.normals, self.faceNormals = pg.meshNormals(faces)
"""
def __init__(self, faces, vertexes=None):
"""
See MeshData for initialization arguments.
"""
self.data = MeshData()
self.data.setFaces(faces, vertexes)
GLGraphicsItem.__init__(self) GLGraphicsItem.__init__(self)
def initializeGL(self): def initializeGL(self):
#balloonVertexShader = shaders.compileShader("""
#varying vec3 normal;
#void main() {
#normal = normalize(gl_NormalMatrix * gl_Normal);
#//vec4 color = normal;
#//normal.w = min(color.w + 2.0 * color.w * pow(normal.x*normal.x + normal.y*normal.y, 2.0), 1.0);
#gl_FrontColor = gl_Color;
#gl_BackColor = gl_Color;
#gl_Position = ftransform();
#}""", GL_VERTEX_SHADER)
#balloonFragmentShader = shaders.compileShader("""
#varying vec3 normal;
#void main() {
#vec4 color = gl_Color;
#color.w = min(color.w + 2.0 * color.w * pow(normal.x*normal.x + normal.y*normal.y, 5.0), 1.0);
#gl_FragColor = color;
#}""", GL_FRAGMENT_SHADER)
#self.shader = shaders.compileProgram(balloonVertexShader, balloonFragmentShader)
self.shader = shaders.getShader('balloon') self.shader = shaders.getShader('balloon')
l = glGenLists(1) l = glGenLists(1)
@ -51,39 +39,33 @@ class GLMeshItem(GLGraphicsItem):
glDisable( GL_DEPTH_TEST ) glDisable( GL_DEPTH_TEST )
glColor4f(1, 1, 1, .1) glColor4f(1, 1, 1, .1)
glBegin( GL_TRIANGLES ) glBegin( GL_TRIANGLES )
for i, f in enumerate(self.faces): for face in self.data:
pts = [QtGui.QVector3D(*x) for x in f] for (pos, norm, color) in face:
if pts[0] is None: glColor4f(*color)
print f
continue
#norm = QtGui.QVector3D.crossProduct(pts[1]-pts[0], pts[2]-pts[0])
for j in [0,1,2]:
norm = self.normals[self.faceNormals[i][j]]
glNormal3f(norm.x(), norm.y(), norm.z()) glNormal3f(norm.x(), norm.y(), norm.z())
glVertex3f(pos.x(), pos.y(), pos.z())
glEnd()
glEndList()
#l = glGenLists(1)
#self.meshList = l
#glNewList(l, GL_COMPILE)
#glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
#glEnable( GL_BLEND )
#glEnable( GL_ALPHA_TEST )
##glAlphaFunc( GL_ALWAYS,0.5 )
#glEnable( GL_POINT_SMOOTH )
#glEnable( GL_DEPTH_TEST )
#glColor4f(1, 1, 1, .3)
#glBegin( GL_LINES )
#for f in self.faces:
#for i in [0,1,2]:
#j = (i+1) % 3 #j = (i+1) % 3
glVertex3f(*f[j]) #glVertex3f(*f[i])
glEnd() #glVertex3f(*f[j])
glEndList() #glEnd()
#glEndList()
l = glGenLists(1)
self.meshList = l
glNewList(l, GL_COMPILE)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glEnable( GL_BLEND )
glEnable( GL_ALPHA_TEST )
#glAlphaFunc( GL_ALWAYS,0.5 )
glEnable( GL_POINT_SMOOTH )
glEnable( GL_DEPTH_TEST )
glColor4f(1, 1, 1, .3)
glBegin( GL_LINES )
for f in self.faces:
for i in [0,1,2]:
j = (i+1) % 3
glVertex3f(*f[i])
glVertex3f(*f[j])
glEnd()
glEndList()
def paint(self): def paint(self):