Fixed up MeshData and GLMeshItem classes for surface display
This commit is contained in:
parent
f6da6e2fd0
commit
d2d812c86e
@ -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):
|
||||||
"""
|
"""
|
||||||
|
@ -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):
|
||||||
|
Loading…
Reference in New Issue
Block a user