def convert_meshdata(vertices=None, faces=None, normals=None, meshdata=None, invert_normals=False, transform=None): """Convert mesh data to be compatible with visbrain. Parameters ---------- vertices : array_like | None Vertices of the template of shape (N, 3) or (N, 3, 3) if indexed by faces. faces : array_like | None Faces of the template of shape (M, 3) normals : array_like | None The normals to each vertex, with the same shape as vertices. meshdata : VisPy.MeshData | None VisPy MeshData object. invert_normals : bool | False If the brain appear to be black, use this parameter to invert normals. transform : visPy.transform | None VisPy transformation to apply to vertices ans normals. Returns ------- vertices : array_like Vertices of shape (N, 3) faces : array_like Faces of the template of shape (M, 3) normals : array_like The normals of shape (M, 3, 3). """ # Priority to meshdata : if meshdata is not None: vertices = meshdata.get_vertices() faces = meshdata.get_faces() normals = meshdata.get_vertex_normals() logger.debug('Indexed faces normals converted // extracted') else: # Check if faces index start at zero (Matlab like): if faces.min() != 0: faces -= faces.min() # Get normals if None : if (normals is None) or (normals.ndim != 2): md = MeshData(vertices=vertices, faces=faces) normals = md.get_vertex_normals() logger.debug('Indexed faces normals converted // extracted') assert vertices.ndim == 2 # Invert normals : norm_coef = -1. if invert_normals else 1. normals *= norm_coef # Apply transformation : if transform is not None: vertices = transform.map(vertices)[..., 0:-1] normals = transform.map(normals)[..., 0:-1] # Type checking : vertices = vispy_array(vertices) faces = vispy_array(faces, np.uint32) normals = vispy_array(normals) return vertices, faces, normals
class TriangleMeshVisual(Visual): def __init__(self, vertices, faces, colors=None, texture=None, textureCoordinates=None, light=None, ambientMaterialConstant=0.5, diffuseMaterialConstant=0.4, specularMaterialConstant=0.3, vertexShader=defaultVertexShader, fragmentShader=defaultFragmentShader, camera=None): Visual.__init__(self, vertexShader, fragmentShader) # Culling mode (for debugging) #wrappers.set_cull_face(mode='back') # Default arguments if colors is None and texture is None: colors = np.array([(0.5, 0.5, 0.5) for index in range(vertices.shape[0])], dtype=np.float32) elif colors is not None and len(colors) is 3 and (isinstance( colors[0], float) or isinstance(colors[0], int)): colors = np.array([colors for index in range(len(vertices))], dtype=np.float32) if light is None: light = SimpleLight(self, color=(1.0, 1.0, 1.0)) else: light = light(self) # Geometry setup self._meshData = MeshData(vertices=vertices, faces=faces, vertex_colors=colors) self._vertices = None self._colors = None self._texture = None self._textureCoordinates = None self._normals = None self._draw_mode = 'triangles' self.set_gl_state('opaque', depth_test=True, cull_face=True) # Light setup self._light = light self._camera = camera self._ambientMaterialConstant = ambientMaterialConstant self._diffuseMaterialConstant = diffuseMaterialConstant self._specularMaterialConstant = specularMaterialConstant # Initial updates self.updateMesh(vertices=vertices, faces=faces, colors=colors, texture=texture, textureCoordinates=textureCoordinates) self.updateLight() # --- Function override --- def _prepare_transforms(self, view): view.view_program.vert['transform'] = view.get_transform() def _prepare_draw(self, view): #self.updateMesh() self.updateLight() # --- State update --- def updateMesh(self, vertices=None, faces=None, colors=None, texture=None, textureCoordinates=None): # Set if vertices is not None: self._meshData.set_vertices(verts=vertices) if faces is not None: self._meshData.set_faces(faces) if colors is not None: self._meshData.set_vertex_colors(colors) # Update if vertices is not None or faces is not None: self._vertices = VertexBuffer( self._meshData.get_vertices(indexed='faces').astype( np.float32)) if faces is not None and vertices is not None: self._normals = VertexBuffer( self._meshData.get_vertex_normals(indexed='faces').astype( np.float32)) if colors is not None: self._colors = VertexBuffer( self._meshData.get_vertex_colors(indexed='faces').astype( np.float32)) if texture is not None: self._texture = Texture2D(np.array(texture, dtype=np.float32), interpolation='linear', wrapping='mirrored_repeat') if textureCoordinates is not None: textureMesh = MeshData(vertices=textureCoordinates, faces=self._meshData.get_faces()) self._textureCoordinates = VertexBuffer( textureMesh.get_vertices(indexed='faces').astype(np.float32)) # Update GPU - geometry self.shared_program.vert['position'] = self._vertices self.shared_program.vert['normal'] = self._normals # Update GPU - color if self._texture is None: self.shared_program.vert['color'] = self._colors else: self.shared_program.vert[ 'textureCoordinates'] = self._textureCoordinates self.shared_program['u_objectTexture'] = self._texture def updateLight(self): self.shared_program.frag['ambientLight'] = self._light._ambient self.shared_program.frag[ 'ambientMaterialConstant'] = self._ambientMaterialConstant self.shared_program.frag[ 'diffuseMaterialConstant'] = self._diffuseMaterialConstant self.shared_program.frag[ 'specularMaterialConstant'] = self._specularMaterialConstant self.shared_program.frag['lightPos'] = self._light._pos self.shared_program.frag['lightColor'] = self._light._color self.shared_program.frag['cameraPos'] = np.array( self._camera._quaternion.rotate_point([0.0, 3.0, 0.0]), dtype=np.float32) self.update()