From ba56899a365949d77d28327468628b1aa4dacb23 Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Wed, 29 May 2013 14:33:14 -0400 Subject: [PATCH] Added basic wireframe mesh drawing --- examples/GLMeshItem.py | 10 ++++ pyqtgraph/opengl/MeshData.py | 28 ++++++++- pyqtgraph/opengl/items/GLMeshItem.py | 85 ++++++++++++++++++++-------- 3 files changed, 98 insertions(+), 25 deletions(-) diff --git a/examples/GLMeshItem.py b/examples/GLMeshItem.py index 9056fbd6..5ef8eb51 100644 --- a/examples/GLMeshItem.py +++ b/examples/GLMeshItem.py @@ -83,6 +83,16 @@ m3 = gl.GLMeshItem(meshdata=md, smooth=False)#, shader='balloon') w.addItem(m3) +# Example 4: +# wireframe + +md = gl.MeshData.sphere(rows=4, cols=8) +m4 = gl.GLMeshItem(meshdata=md, smooth=False, drawFaces=False, drawEdges=True, edgeColor=(1,1,1,1)) +m4.translate(0,10,0) +w.addItem(m4) + + + diff --git a/pyqtgraph/opengl/MeshData.py b/pyqtgraph/opengl/MeshData.py index 170074b9..12a9b83b 100644 --- a/pyqtgraph/opengl/MeshData.py +++ b/pyqtgraph/opengl/MeshData.py @@ -44,7 +44,7 @@ class MeshData(object): ## mappings between vertexes, faces, and edges self._faces = None # Nx3 array of indexes into self._vertexes specifying three vertexes for each face - self._edges = None + self._edges = None # Nx2 array of indexes into self._vertexes specifying two vertexes per edge self._vertexFaces = None ## maps vertex ID to a list of face IDs (inverse mapping of _faces) self._vertexEdges = None ## maps vertex ID to a list of edge IDs (inverse mapping of _edges) @@ -143,12 +143,19 @@ class MeshData(object): def faces(self): """Return an array (Nf, 3) of vertex indexes, three per triangular face in the mesh.""" return self._faces + + def edges(self): + """Return an array (Nf, 3) of vertex indexes, two per edge in the mesh.""" + if self._edges is None: + self._computeEdges() + return self._edges def setFaces(self, faces): """Set the (Nf, 3) array of faces. Each rown in the array contains three indexes into the vertex array, specifying the three corners of a triangular face.""" self._faces = faces + self._edges = None self._vertexFaces = None self._vertexesIndexedByFaces = None self.resetNormals() @@ -418,6 +425,25 @@ class MeshData(object): #""" #pass + def _computeEdges(self): + ## generate self._edges from self._faces + #print self._faces + nf = len(self._faces) + edges = np.empty(nf*3, dtype=[('i', np.uint, 2)]) + edges['i'][0:nf] = self._faces[:,:2] + edges['i'][nf:2*nf] = self._faces[:,1:3] + edges['i'][-nf:,0] = self._faces[:,2] + edges['i'][-nf:,1] = self._faces[:,0] + + # sort per-edge + mask = edges['i'][:,0] > edges['i'][:,1] + edges['i'][mask] = edges['i'][mask][:,::-1] + + # remove duplicate entries + self._edges = np.unique(edges)['i'] + #print self._edges + + def save(self): """Serialize this mesh to a string appropriate for disk storage""" import pickle diff --git a/pyqtgraph/opengl/items/GLMeshItem.py b/pyqtgraph/opengl/items/GLMeshItem.py index 4222c96b..66d54361 100644 --- a/pyqtgraph/opengl/items/GLMeshItem.py +++ b/pyqtgraph/opengl/items/GLMeshItem.py @@ -22,9 +22,15 @@ class GLMeshItem(GLGraphicsItem): Arguments meshdata MeshData object from which to determine geometry for this item. - color Default color used if no vertex or face colors are - specified. - shader Name of shader program to use (None for no shader) + color Default face color used if no vertex or face colors + are specified. + edgeColor Default edge color to use if no edge colors are + specified in the mesh data. + drawEdges If True, a wireframe mesh will be drawn. + (default=False) + drawFaces If True, mesh faces are drawn. (default=True) + shader Name of shader program to use when drawing faces. + (None for no shader) smooth If True, normal vectors are computed for each vertex and interpolated within each face. computeNormals If False, then computation of normal vectors is @@ -35,6 +41,9 @@ class GLMeshItem(GLGraphicsItem): self.opts = { 'meshdata': None, 'color': (1., 1., 1., 1.), + 'drawEdges': False, + 'drawFaces': True, + 'edgeColor': (0.5, 0.5, 0.5, 1.0), 'shader': None, 'smooth': True, 'computeNormals': True, @@ -100,6 +109,8 @@ class GLMeshItem(GLGraphicsItem): self.faces = None self.normals = None self.colors = None + self.edges = None + self.edgeColors = None self.update() def parseMeshData(self): @@ -137,6 +148,9 @@ class GLMeshItem(GLGraphicsItem): elif md.hasFaceColor(): self.colors = md.faceColors(indexed='faces') + if self.opts['drawEdges']: + self.edges = md.edges() + self.edgeVerts = md.vertexes() return def paint(self): @@ -144,19 +158,52 @@ class GLMeshItem(GLGraphicsItem): self.parseMeshData() - with self.shader(): - verts = self.vertexes - norms = self.normals - color = self.colors - faces = self.faces - if verts is None: - return + if self.opts['drawFaces']: + with self.shader(): + verts = self.vertexes + norms = self.normals + color = self.colors + faces = self.faces + if verts is None: + return + glEnableClientState(GL_VERTEX_ARRAY) + try: + glVertexPointerf(verts) + + if self.colors is None: + color = self.opts['color'] + if isinstance(color, QtGui.QColor): + glColor4f(*pg.glColor(color)) + else: + glColor4f(*color) + else: + glEnableClientState(GL_COLOR_ARRAY) + glColorPointerf(color) + + + if norms is not None: + glEnableClientState(GL_NORMAL_ARRAY) + glNormalPointerf(norms) + + if faces is None: + glDrawArrays(GL_TRIANGLES, 0, np.product(verts.shape[:-1])) + else: + faces = faces.astype(np.uint).flatten() + glDrawElements(GL_TRIANGLES, faces.shape[0], GL_UNSIGNED_INT, faces) + finally: + glDisableClientState(GL_NORMAL_ARRAY) + glDisableClientState(GL_VERTEX_ARRAY) + glDisableClientState(GL_COLOR_ARRAY) + + if self.opts['drawEdges']: + verts = self.edgeVerts + edges = self.edges glEnableClientState(GL_VERTEX_ARRAY) try: glVertexPointerf(verts) - if self.colors is None: - color = self.opts['color'] + if self.edgeColors is None: + color = self.opts['edgeColor'] if isinstance(color, QtGui.QColor): glColor4f(*pg.glColor(color)) else: @@ -164,19 +211,9 @@ class GLMeshItem(GLGraphicsItem): else: glEnableClientState(GL_COLOR_ARRAY) glColorPointerf(color) - - - if norms is not None: - glEnableClientState(GL_NORMAL_ARRAY) - glNormalPointerf(norms) - - if faces is None: - glDrawArrays(GL_TRIANGLES, 0, np.product(verts.shape[:-1])) - else: - faces = faces.astype(np.uint).flatten() - glDrawElements(GL_TRIANGLES, faces.shape[0], GL_UNSIGNED_INT, faces) + edges = edges.flatten() + glDrawElements(GL_LINES, edges.shape[0], GL_UNSIGNED_INT, edges) finally: - glDisableClientState(GL_NORMAL_ARRAY) glDisableClientState(GL_VERTEX_ARRAY) glDisableClientState(GL_COLOR_ARRAY)