class VolumeObject(object): def __init__(self, stack, spacing): self.active = True self.stack_object = StackObject(stack, spacing) shape = self.stack_object.shape self.vao = glGenVertexArrays(1) glBindVertexArray(self.vao) tl = np.array((shape[2]*spacing[0], # x shape[1]*spacing[1], # y shape[0]*spacing[2])) # z # Vertex buffer: corners of cube. # x, y, z, texture_x, texture_y, texture_z vb = [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0], # Corner 0. [tl[0], 0.0, 0.0, 1.0, 0.0, 0.0], [0.0, tl[1], 0.0, 0.0, 1.0, 0.0], [tl[0], tl[1], 0.0, 1.0, 1.0, 0.0], [0.0, 0.0, tl[2], 0.0, 0.0, 1.0], [tl[0], 0.0, tl[2], 1.0, 0.0, 1.0], [0.0, tl[1], tl[2], 0.0, 1.0, 1.0], [tl[0], tl[1], tl[2], 1.0, 1.0, 1.0]] # Corner 7. vb = np.array(vb, dtype=np.float32) vb = vb.flatten() # Triangles of cube. idx_out = np.array([[0, 2, 1], [2, 3, 1], # Triangle 0, triangle 1. [1, 4, 0], [1, 5, 4], [3, 5, 1], [3, 7, 5], [2, 7, 3], [2, 6, 7], [0, 6, 2], [0, 4, 6], [5, 6, 4], [5, 7, 6]], # Triangle 10, triangle 11. dtype=np.uint32) self.vtVBO = VBO(vb) self.vtVBO.bind() sc = 1.0/la.norm(tl) c = 0.5*tl self.transform = np.array(((sc, 0.0, 0.0, -sc*c[0]), (0.0, sc, 0.0, -sc*c[1]), (0.0, 0.0, sc, -sc*c[2]), (0.0, 0.0, 0.0, 1.0))) self.tex_transform = np.array(((1.0/tl[0], 0.0, 0.0, 0.0), (0.0, 1.0/tl[1], 0.0, 0.0), (0.0, 0.0, 1.0/tl[2], 0.0), (0.0, 0.0, 0.0, 1.0))) glBindVertexArray(0) self.elVBO = VBO(idx_out, target=GL_ELEMENT_ARRAY_BUFFER) self.elCount = len(idx_out.flatten()) def update_stack(self, stack): self.stack_object.update_stack(stack)
class model: ''' class for STL file / 3d model ''' def __init__(self, stl_data, batchh=None): ''' initialise model data''' vert, norm = stl_data self.vertices = numpy.array(vert, dtype=GLfloat) self.normals = numpy.array(norm, dtype=GLfloat) self.vertex_buffer = VBO(self.vertices, 'GL_STATIC_DRAW') self.normal_buffer = VBO(self.normals, 'GL_STATIC_DRAW') # calculate model scale self.corner = self.vertices.transpose().min(1) self.corner2 = self.vertices.transpose().max(1) self.scale = abs(self.corner) + abs(self.corner2) self.scale = max(self.scale[0], self.scale[1], self.scale[2]) print 'STL File loaded in: ', loadtime print 'Object information' print 'corner 1: ', self.corner print 'corner 2: ', self.corner2 print 'object scale: ', self.scale def draw(self): ''' draw model on screen ''' glMatrixMode(GL_MODELVIEW) glPushMatrix() glLoadIdentity() # center the model glTranslate(window.width / 2, window.height / 2, -150) # scale the model glScale(150 / self.scale, 150 / self.scale, 150 / self.scale) # draw grid and coordinate lines draw_grid() draw_xyz(0, 0, 0, -20, -20) # demo rotation glRotate(rot, 1, 0, 0) glRotate(rot2, 0, 1, 0) glRotate(rot3, 0, 0, 1) self.vertex_buffer.bind() glVertexPointer(3, GL_FLOAT, 0, None) self.normal_buffer.bind() glNormalPointer(GL_FLOAT, 0, None) glEnableClientState(GL_VERTEX_ARRAY) glEnableClientState(GL_NORMAL_ARRAY) glDrawArrays(GL_TRIANGLES, 0, len(self.vertices)) glDisableClientState(GL_NORMAL_ARRAY) glDisableClientState(GL_VERTEX_ARRAY) self.normal_buffer.unbind() self.vertex_buffer.unbind() glPopMatrix()
def bind(self): """ bind the vbo to the gl context. needs to be done before accessing the vbo. """ GLVBO.bind(self) for att in self.attributes: gl.glEnableVertexAttribArray( att.location )
class BackGroundImage: vs_source = 'gl/empty.vs.glsl' fs_source = 'gl/image.fs.glsl' def __init__(self, width, height): vertices = [-1, -1, 0, 1, -1, 0, 1, 1, 0, 1, 1, 0, -1, 1, 0, -1, -1, 0] self.num_vertices = len(vertices) // 3 self.vbo = VBO(np.array(vertices, dtype=np.float32), GL_STATIC_DRAW, GL_ARRAY_BUFFER) self.vao = glGenVertexArrays(1) glBindVertexArray(self.vao) self.vbo.bind() glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * 4, self.vbo) glEnableVertexAttribArray(0) glBindVertexArray(0) self.shader = compile_shader(self.vs_source, self.fs_source) self.texture = glGenTextures(1) glBindTexture(GL_TEXTURE_2D, self.texture) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGB8, width, height) # img = Image.open('board.jpg').transpose(Image.FLIP_TOP_BOTTOM) # glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, # img.size[0], img.size[1], 0, # GL_RGB, GL_UNSIGNED_BYTE, img.tobytes()) def set_image(self, image): glBindTexture(GL_TEXTURE_2D, self.texture) np.flipud(image) glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image.shape[1], image.shape[0], GL_BGR, GL_UNSIGNED_BYTE, np.flipud(image).tobytes()) def render(self): glDisable(GL_DEPTH_TEST) glUseProgram(self.shader) glUniform1i(0, 0) glActiveTexture(GL_TEXTURE0) glBindTexture(GL_TEXTURE_2D, self.texture) glBindVertexArray(self.vao) glDrawArrays(GL_TRIANGLES, 0, self.num_vertices)
class RenderBatch(object): def __init__(self, draw_type=GL_QUADS): self.count = 0 self.color_data = [] self.position_data = [] self.color_buffer = VBO(np.array([])) self.position_buffer = VBO(np.array([])) self.draw_type = draw_type def draw2d(self, points, color=(0, 0, 0, 1), rotation=0, center=(0, 0)): n = len(points) self.count += n if not isinstance(color[0], (tuple, list)): color = [color]*n if rotation: transform = psi.calc.rotation_matrix(rotation) temp = np.array(points) - center temp = transform.dot(temp.T).T + center points = temp.tolist() self.color_data.extend(color) self.position_data.extend(points) def clear(self): self.position_data = [] self.color_data = [] self.count = 0 def render(self): self.color_buffer.set_array(np.array(self.color_data, dtype='float32')) self.position_buffer.set_array(np.array(self.position_data, dtype='float32')) self.color_buffer.bind() glColorPointer(4, GL_FLOAT, 0, self.color_buffer) self.position_buffer.bind() glVertexPointer(2, GL_FLOAT, 0, self.position_buffer) glEnableClientState(GL_VERTEX_ARRAY) glEnableClientState(GL_COLOR_ARRAY) glDrawArrays(self.draw_type, 0, self.count) glDisableClientState(GL_COLOR_ARRAY) glDisableClientState(GL_VERTEX_ARRAY)
class RenderBatch(object): def __init__(self, draw_type=GL_QUADS): self.count = 0 self.color_data = [] self.position_data = [] self.color_buffer = VBO(np.array([])) self.position_buffer = VBO(np.array([])) self.draw_type = draw_type def draw2d(self, points, color=(0, 0, 0, 1), rotation=0, center=(0, 0)): n = len(points) self.count += n if not isinstance(color[0], (tuple, list)): color = [color] * n if rotation: transform = psi.calc.rotation_matrix(rotation) temp = np.array(points) - center temp = transform.dot(temp.T).T + center points = temp.tolist() self.color_data.extend(color) self.position_data.extend(points) def clear(self): self.position_data = [] self.color_data = [] self.count = 0 def render(self): self.color_buffer.set_array(np.array(self.color_data, dtype='float32')) self.position_buffer.set_array( np.array(self.position_data, dtype='float32')) self.color_buffer.bind() glColorPointer(4, GL_FLOAT, 0, self.color_buffer) self.position_buffer.bind() glVertexPointer(2, GL_FLOAT, 0, self.position_buffer) glEnableClientState(GL_VERTEX_ARRAY) glEnableClientState(GL_COLOR_ARRAY) glDrawArrays(self.draw_type, 0, self.count) glDisableClientState(GL_COLOR_ARRAY) glDisableClientState(GL_VERTEX_ARRAY)
class OBJModel: vs_source = 'gl/mvp.vs.glsl' fs_source = 'gl/phong.fs.glsl' def __init__(self, obj_file): obj = pywavefront.Wavefront(obj_file, strict=True, collect_faces=True) assert len(obj.mesh_list) == 1 and len(obj.mesh_list[0].materials) == 1 material = obj.mesh_list[0].materials[0] # self.num_vertices = len(material.vertices) // material.vertex_size self.vbo = VBO(np.array(material.vertices, dtype=np.float32), GL_STATIC_DRAW, GL_ARRAY_BUFFER) self.vao = glGenVertexArrays(1) glBindVertexArray(self.vao) self.vbo.bind() glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * 4, self.vbo) glEnableVertexAttribArray(0) glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * 4, self.vbo + 3 * 4) glEnableVertexAttribArray(1) glBindVertexArray(0) self.shader = compile_shader(self.vs_source, self.fs_source) def render(self, model, view, perjection, light_position, color): glEnable(GL_DEPTH_TEST) glUseProgram(self.shader) glUniformMatrix4fv(0, 1, True, model) glUniformMatrix4fv(1, 1, True, view) glUniformMatrix4fv(2, 1, True, perjection) glUniform3fv(3, 1, light_position) glUniform3fv(4, 1, color) glBindVertexArray(self.vao) glDrawArrays(GL_TRIANGLES, 0, self.num_vertices)
class RenderBatchOpt(object): def __init__(self, draw_type=GL_QUADS): self.count = 0 self.color_buffer = VBO(np.array([])) self.vertex_buffer = VBO(np.array([])) self.draw_type = draw_type def draw2d(self, points, color=(0, 0, 0, 1), rotation=0, center=(0, 0)): n = points.shape[0] self.count += n if rotation: transform = psi.calc.rotation_matrix(rotation) temp = points - center temp = transform.dot(temp.T).T + center points = temp.tolist() self.color_buffer.set_array(color) self.vertex_buffer.set_array(points) def clear(self): self.color_buffer.set_array(np.array([])) self.vertex_buffer.set_array(np.array([])) self.count = 0 def render(self): self.color_buffer.bind() glColorPointer(4, GL_FLOAT, 0, self.color_buffer) self.vertex_buffer.bind() glVertexPointer(2, GL_FLOAT, 0, self.vertex_buffer) glEnableClientState(GL_VERTEX_ARRAY) glEnableClientState(GL_COLOR_ARRAY) glDrawArrays(self.draw_type, 0, self.count) glDisableClientState(GL_COLOR_ARRAY) glDisableClientState(GL_VERTEX_ARRAY)
class RenderObject: def __init__(self, program, obj): self.mtl_list = obj.mtl_list self.vao = glGenVertexArrays(1) glBindVertexArray(self.vao) vertices = (GLfloat * len(obj.vertices))(*obj.vertices); self.vbo = VBO(vertices, GL_STATIC_DRAW, GL_ARRAY_BUFFER) self.vbo.bind() vertex_attrib = program.attribs["vertex"] glEnableVertexAttribArray(vertex_attrib) glVertexAttribPointer(vertex_attrib, 3, GL_FLOAT, GL_FALSE, 8 * 4, c_void_p(0)) vertex_tex_attrib = program.attribs["vertex_tex"] glEnableVertexAttribArray(vertex_tex_attrib) glVertexAttribPointer(vertex_tex_attrib, 2, GL_FLOAT, GL_FALSE, 8 * 4, c_void_p(3 * 4)) vertex_normal_attrib = program.attribs["vertex_normal"] glEnableVertexAttribArray(vertex_normal_attrib) glVertexAttribPointer(vertex_normal_attrib, 2, GL_FLOAT, GL_FALSE, 8 * 4, c_void_p(5 * 4))
class MeshObject(object): def __init__(self, fn, spacing): self.active = True m = GLMesh() self.mesh = m sc = m.load_ply(fn) v_out, n_out, col_out, idx_out = m.generate_arrays() vb = np.concatenate((v_out, n_out, col_out), axis=1) self.elVBO = VBO(idx_out, target=GL_ELEMENT_ARRAY_BUFFER) self.elCount = len(idx_out.flatten()) self.vao = glGenVertexArrays(1) glBindVertexArray(self.vao) self.vtVBO = VBO(vb) self.vtVBO.bind() glBindVertexArray(0) c = np.array((0, 0, 0)) self.transform = np.array(((sc, 0.0, 0.0, -sc*c[0]), (0.0, sc, 0.0, -sc*c[1]), (0.0, 0.0, sc, -sc*c[2]), (0.0, 0.0, 0.0, 1.0)))
class GcodeModel(Model): """ Model for displaying Gcode data. """ # vertices for arrow to display the direction of movement arrow = numpy.require([ [0.0, 0.0, 0.0], [0.4, -0.1, 0.0], [0.4, 0.1, 0.0], ], 'f') layer_entry_marker = numpy.require([ [0.23, -0.14, 0.0], [0.0, 0.26, 0.0], [-0.23, -0.14, 0.0], ], 'f') layer_exit_marker = numpy.require([ [-0.23, -0.23, 0.0], [0.23, -0.23, 0.0], [0.23, 0.23, 0.0], [0.23, 0.23, 0.0], [-0.23, 0.23, 0.0], [-0.23, -0.23, 0.0], ], 'f') def load_data(self, model_data, callback=None): t_start = time.time() vertex_list = [] color_list = [] self.layer_stops = [0] self.layer_heights = [] arrow_list = [] layer_markers_list = [] self.layer_marker_stops = [0] num_layers = len(model_data) callback_every = max(1, int(math.floor(num_layers / 100))) # the first movement designates the starting point start = prev = model_data[0][0] del model_data[0][0] for layer_idx, layer in enumerate(model_data): first = layer[0] for movement in layer: vertex_list.append(prev.v) vertex_list.append(movement.v) arrow = self.arrow # position the arrow with respect to movement arrow = vector.rotate(arrow, movement.angle(prev.v), 0.0, 0.0, 1.0) arrow_list.extend(arrow) vertex_color = self.movement_color(movement) color_list.append(vertex_color) prev = movement self.layer_stops.append(len(vertex_list)) self.layer_heights.append(first.v[2]) # add the layer entry marker if layer_idx > 0 and len(model_data[layer_idx - 1]) > 0: layer_markers_list.extend(self.layer_entry_marker + model_data[layer_idx - 1][-1].v) elif layer_idx == 0 and len(layer) > 0: layer_markers_list.extend(self.layer_entry_marker + layer[0].v) # add the layer exit marker if len(layer) > 1: layer_markers_list.extend(self.layer_exit_marker + layer[-1].v) self.layer_marker_stops.append(len(layer_markers_list)) if callback and layer_idx % callback_every == 0: callback(layer_idx + 1, num_layers) self.vertices = numpy.array(vertex_list, 'f') self.colors = numpy.array(color_list, 'f') self.arrows = numpy.array(arrow_list, 'f') self.layer_markers = numpy.array(layer_markers_list, 'f') # by translating the arrow vertices outside of the loop, we achieve a # significant performance gain thanks to numpy. it would be really nice # if we could rotate in a similar fashion... self.arrows = self.arrows + self.vertices[1::2].repeat(3, 0) # for every pair of vertices of the model, there are 3 vertices for the arrow assert len(self.arrows) == ((len(self.vertices) // 2) * 3), \ 'The 2:3 ratio of model vertices to arrow vertices does not hold.' self.max_layers = len(self.layer_stops) - 1 self.num_layers_to_draw = self.max_layers self.arrows_enabled = True self.initialized = False self.vertex_count = len(self.vertices) t_end = time.time() logging.info('Initialized Gcode model in %.2f seconds' % (t_end - t_start)) logging.info('Vertex count: %d' % self.vertex_count) def movement_color(self, move): """ Return the color to use for particular type of movement. """ # default movement color is gray color = (0.6, 0.6, 0.6, 0.6) extruder_on = (move.flags & Movement.FLAG_EXTRUDER_ON or move.delta_e > 0) outer_perimeter = (move.flags & Movement.FLAG_PERIMETER and move.flags & Movement.FLAG_PERIMETER_OUTER) if extruder_on and outer_perimeter: color = (0.0, 0.875, 0.875, 0.6) # cyan elif extruder_on and move.flags & Movement.FLAG_PERIMETER: color = (0.0, 1.0, 0.0, 0.6) # green elif extruder_on and move.flags & Movement.FLAG_LOOP: color = (1.0, 0.875, 0.0, 0.6) # yellow elif extruder_on: color = (1.0, 0.0, 0.0, 0.6) # red return color # ------------------------------------------------------------------------ # DRAWING # ------------------------------------------------------------------------ def init(self): self.vertex_buffer = VBO(self.vertices, 'GL_STATIC_DRAW') self.vertex_color_buffer = VBO(self.colors.repeat( 2, 0), 'GL_STATIC_DRAW') # each pair of vertices shares the color if self.arrows_enabled: self.arrow_buffer = VBO(self.arrows, 'GL_STATIC_DRAW') self.arrow_color_buffer = VBO( self.colors.repeat(3, 0), 'GL_STATIC_DRAW') # each triplet of vertices shares the color self.layer_marker_buffer = VBO(self.layer_markers, 'GL_STATIC_DRAW') self.initialized = True def display(self, elevation=0, eye_height=0, mode_ortho=False, mode_2d=False): glPushMatrix() offset_z = self.offset_z if not mode_2d else 0 glTranslate(self.offset_x, self.offset_y, offset_z) glEnableClientState(GL_VERTEX_ARRAY) glEnableClientState(GL_COLOR_ARRAY) self._display_movements(elevation, eye_height, mode_ortho, mode_2d) if self.arrows_enabled: self._display_arrows() glDisableClientState(GL_COLOR_ARRAY) if self.arrows_enabled: self._display_layer_markers() glDisableClientState(GL_VERTEX_ARRAY) glPopMatrix() def _display_movements(self, elevation=0, eye_height=0, mode_ortho=False, mode_2d=False): self.vertex_buffer.bind() glVertexPointer(3, GL_FLOAT, 0, None) self.vertex_color_buffer.bind() glColorPointer(4, GL_FLOAT, 0, None) if mode_2d: glScale(1.0, 1.0, 0.0) # discard z coordinates start = self.layer_stops[self.num_layers_to_draw - 1] end = self.layer_stops[self.num_layers_to_draw] glDrawArrays(GL_LINES, start, end - start) elif mode_ortho: if elevation >= 0: # draw layers in normal order, bottom to top start = 0 end = self.layer_stops[self.num_layers_to_draw] glDrawArrays(GL_LINES, start, end - start) else: # draw layers in reverse order, top to bottom stop_idx = self.num_layers_to_draw - 1 while stop_idx >= 0: start = self.layer_stops[stop_idx] end = self.layer_stops[stop_idx + 1] glDrawArrays(GL_LINES, start, end - start) stop_idx -= 1 else: # 3d projection mode reverse_threshold_layer = self._layer_up_to_height(eye_height - self.offset_z) if reverse_threshold_layer >= 0: # draw layers up to (and including) the threshold in normal order, bottom to top normal_layers_to_draw = min(self.num_layers_to_draw, reverse_threshold_layer + 1) start = 0 end = self.layer_stops[normal_layers_to_draw] glDrawArrays(GL_LINES, start, end - start) if reverse_threshold_layer + 1 < self.num_layers_to_draw: # draw layers from the threshold in reverse order, top to bottom stop_idx = self.num_layers_to_draw - 1 while stop_idx > reverse_threshold_layer: start = self.layer_stops[stop_idx] end = self.layer_stops[stop_idx + 1] glDrawArrays(GL_LINES, start, end - start) stop_idx -= 1 self.vertex_buffer.unbind() self.vertex_color_buffer.unbind() def _layer_up_to_height(self, height): """Return the index of the last layer lower than height.""" for idx in range(len(self.layer_heights) - 1, -1, -1): if self.layer_heights[idx] < height: return idx return 0 def _display_arrows(self): self.arrow_buffer.bind() glVertexPointer(3, GL_FLOAT, 0, None) self.arrow_color_buffer.bind() glColorPointer(4, GL_FLOAT, 0, None) start = (self.layer_stops[self.num_layers_to_draw - 1] // 2) * 3 end = (self.layer_stops[self.num_layers_to_draw] // 2) * 3 glDrawArrays(GL_TRIANGLES, start, end - start) self.arrow_buffer.unbind() self.arrow_color_buffer.unbind() def _display_layer_markers(self): self.layer_marker_buffer.bind() glVertexPointer(3, GL_FLOAT, 0, None) start = self.layer_marker_stops[self.num_layers_to_draw - 1] end = self.layer_marker_stops[self.num_layers_to_draw] glColor4f(0.6, 0.6, 0.6, 0.6) glDrawArrays(GL_TRIANGLES, start, end - start) self.layer_marker_buffer.unbind()
class TextBatch: __tag = namedtuple("tag", ['mat', 'inf']) def __init__(self, tr): self.__text_render = tr self.__elem_count = 0 self.__color = [1.0, 1.0, 1.0, 1.0] def initializeGL(self): self.vbo = VBO(numpy.ndarray(0, dtype=self.__text_render.buffer_dtype), GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER) self.vao = VAO() with self.vao, self.vbo: self.__text_render.b1.assign() self.__text_render.b2.assign() def restart(self): self.__strs = [] # TEMP def get_string(self, text): """ :param text: :return: :returns: _StringMetrics """ return self.__text_render.getStringMetrics(text) def add(self, mat, str_info): """ Queues a text string to be rendered in the batch :param mat: location matrix :param str_info: :type str_info: _StringMetrics :return: """ self.__strs.append(TextBatch.__tag(mat, str_info)) def prepare(self): self.__text_render.updateTexture() clist = [] for mat, str_info in self.__strs: for (x, y), (u, v) in str_info.arr: newpt = projectPoint(mat, Point2(x, y)) clist.append(((newpt.x, newpt.y), (u, v))) arr = numpy.array(clist, dtype=self.__text_render.buffer_dtype) self.vbo.data = arr self.vbo.size = None self.vbo.copied = False self.vbo.bind() self.__elem_count = len(arr) def render(self, mat): with self.__text_render.sdf_shader, self.__text_render.tex.on( GL.GL_TEXTURE_2D), self.vao: GL.glUniform1i(self.__text_render.sdf_shader.uniforms.tex1, 0) GL.glUniformMatrix3fv(self.__text_render.sdf_shader.uniforms.mat, 1, True, mat.astype(numpy.float32)) GL.glUniform4f(self.__text_render.sdf_shader.uniforms.color, *self.__color) GL.glDrawArrays(GL.GL_TRIANGLES, 0, self.__elem_count) print("Drawing %d elements" % self.__elem_count)
class TextBatch: __tag = namedtuple("__tag", ['mat', 'inf']) def __init__(self, tr: 'TextRender') -> None: self.__text_render = tr self.__elem_count = 0 self.__color = (255, COL_TEXT, 0, 0) def initializeGL(self) -> None: self.vbo = VBO( numpy.ndarray([], dtype=self.__text_render.buffer_dtype), GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER) self.vao = VAO() self._va = VA_tex(1024) with self.vao, self.vbo: self.__text_render.b1.assign() self.__text_render.b2.assign() def restart(self) -> None: self.__strs: List[TextBatch.__tag] = [] # TEMP def get_string(self, text: str) -> '_StringMetrics': """ :param text: :return: :returns: _StringMetrics """ return self.__text_render.getStringMetrics(text) def add(self, mat: 'npt.NDArray[numpy.float64]', str_info: '_StringMetrics') -> None: """ Queues a text string to be rendered in the batch :param mat: location matrix :param str_info: :type str_info: _StringMetrics :return: """ self.__strs.append(TextBatch.__tag(mat, str_info)) def prepare(self) -> None: self.__text_render.updateTexture() self._va.clear() for mat, str_info in self.__strs: self._va.extend_project(mat, str_info.arr) self.vbo.set_array(self._va.buffer()[:]) self.vbo.bind() self.__elem_count = self._va.count() def render(self, mat: 'npt.NDArray[numpy.float64]') -> None: with self.__text_render.sdf_shader.program, self.__text_render.tex.on( GL.GL_TEXTURE_2D), self.vao: GL.glUniform1i(self.__text_render.sdf_shader.uniforms.tex1, 0) GL.glUniformMatrix3fv(self.__text_render.sdf_shader.uniforms.mat, 1, True, mat.astype(numpy.float32)) GL.glUniform4ui(self.__text_render.sdf_shader.uniforms.layer_info, *self.__color) GL.glDrawArrays(GL.GL_TRIANGLES, 0, self.__elem_count)
class _RenderData: def __init__(self, dtype, shader, glhint): self.__dtype = dtype self.vao = VAO() self.batch_vbo = VBO(numpy.array([], dtype=dtype), glhint) with self.vao, self.batch_vbo: vbobind(shader, dtype, "vertex").assign() self.clear() def clear(self): self.__vbo_update = True self.__groups = defaultdict(list) self.__group_lookup = {} self.group_offsets = {} def build_vbo(self): if not self.__vbo_update: return self.__vbo_update = False self.group_offsets = {} built_list = [] for group, points in self.__groups.items(): self.group_offsets[group] = len(built_list) built_list.extend([(i, ) for i in points]) if not built_list: return ar = numpy.array(built_list, dtype=self.__dtype) self.batch_vbo.data = ar self.batch_vbo.size = None self.batch_vbo.copied = False self.batch_vbo.bind() def last_index(self, group): return len(self.__groups[group]) def add_point(self, p1, p2, group): group = self.__groups[group] idx = len(group) // 2 group.append(p1) group.append(p2) self.__vbo_update = True return idx def get_point_index(self, p1, p2, group): key = group, p1.intTuple(), p2.intTuple() try: return self.__group_lookup[key] except KeyError: pass idx = self.__group_lookup[key] = self.add_point(p1, p2, group) return idx
def image_server(evtQueue, resultQueue): # resource-taking objects resObjs = [] # initialize glfw glfw.init() # set glfw config glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3) glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3) glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE) glfw.window_hint(glfw.RESIZABLE, GL_FALSE) if pyPlatform.system().lower() == 'darwin': glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, GL_TRUE) # create window theWindow = glfw.create_window(windowSize[0], windowSize[1], 'Spherical Projection', None, None) # make window the current context glfw.make_context_current(theWindow) # enable z-buffer glEnable(GL_DEPTH_TEST) # set resizing callback function # glfw.set_framebuffer_size_callback(theWindow, window_resize_callback) #glfw.set_key_callback(theWindow, window_keypress_callback) # disable cursor #glfw.set_input_mode(theWindow, glfw.CURSOR, glfw.CURSOR_DISABLED) #glfw.set_cursor_pos_callback(theWindow, window_cursor_callback) # initialize cursor position cursorPos = glfw.get_cursor_pos(theWindow) # glfw.set_scroll_callback(theWindow, window_scroll_callback) vbo = VBO(vertices, 'GL_STATIC_DRAW') vbo.create_buffers() resObjs.append(vbo) vao = glGenVertexArrays(1) glBindVertexArray(vao) vbo.bind() vbo.copy_data() glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(0)) glEnableVertexAttribArray(0) glBindVertexArray(0) # compile program renderProgram = GLProgram(rayTracingVertexShaderSource, rayTracingFragmentShaderSource) renderProgram.compile_and_link() uniformInfos = [ ('backColor', 'vec3f'), ('ambientColor', 'vec3f'), ('o_c', 'vec3f'), ('o_p', 'vec3f'), ('x_c', 'vec3f'), ('y_c', 'vec3f'), ('x_p', 'vec3f'), ('y_p', 'vec3f'), ('c_c', 'vec3f'), ('c_p', 'vec3f'), ('winSize', 'vec2f') ] uniforms = create_uniform(renderProgram.get_program_id(), uniformInfos) # keep rendering until the window should be closed while not glfw.window_should_close(theWindow): # set background color glClearColor(*windowBackgroundColor) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) renderProgram.use() # update uniforms o_c, c_c, x_c, y_c = get_camera_vectors(camera) o_p, c_p, x_p, y_p = get_camera_vectors(projector) uniforms['o_c'].update(o_c) uniforms['x_c'].update(x_c) uniforms['y_c'].update(y_c) uniforms['c_c'].update(c_c) uniforms['o_p'].update(o_p) uniforms['x_p'].update(x_p) uniforms['y_p'].update(y_p) uniforms['c_p'].update(c_p) uniforms['backColor'].update(backColor) uniforms['ambientColor'].update(ambientColor) uniforms['winSize'].update(windowSize.astype(np.float32)) try: newImage = evtQueue.get(timeout=0.05) except Exception as e: # tell glfw to poll and process window events glfw.poll_events() # swap frame buffer glfw.swap_buffers(theWindow) continue texture = create_texture(newImage) glBindVertexArray(vao) glActiveTexture(GL_TEXTURE0) texture.bind() glDrawArrays(GL_TRIANGLES, 0, 6) texture.unbind() glBindVertexArray(0) texture.delete() # respond key press keyboard_respond_func() # tell glfw to poll and process window events glfw.poll_events() # swap frame buffer glfw.swap_buffers(theWindow) result = get_screenshot(windowSize) resultQueue.put(result) for obj in resObjs: obj.delete() # terminate glfw glfw.terminate()
class CompositeManager: def __init__(self) -> None: self.__width = 0 self.__height = 0 self.__layer_fbs: 'List[RenderLayer]' = [] self.__active_count = 0 self.__keys: 'Dict[Any, RenderLayer]' = {} def get(self, key: Any) -> 'RenderLayer': # If we've already got a composite target for this key # return it if key in self.__keys: return self.__keys[key] # Allocate a new layer if required if self.__active_count == len(self.__layer_fbs): self.__layer_fbs.append(RenderLayer(self.__width, self.__height)) # Save new layer fb for reuse self.__keys[key] = self.__layer_fbs[self.__active_count] self.__active_count += 1 return self.__keys[key] def resize(self, width: int, height: int) -> None: if width == self.__width and height == self.__height: return self.__width = width self.__height = height for i in self.__layer_fbs: i.resize(width, height) self.__composite_vbo.set_array(self.__get_vbo_data()) with self.__composite_vbo: self.__composite_vbo.copy_data() def restart(self) -> None: """ Call at the start of rendering. Resets all layers to initial state :return: """ self.__keys = {} self.__active_count = 0 for n, _ in enumerate(self.__layer_fbs): self.reset_layer(n) def reset_layer(self, n: int) -> None: """ Reset a particular layer to an empty state. This implies alpha of 0 (transparent) and type of 0 (undrawn) :param n: :return: """ with self.__layer_fbs[n]: GL.glClearBufferfv(GL.GL_COLOR, 0, (0, 255, 0, 0)) def __get_vbo_data(self) -> 'npt.NDArray[numpy.float64]': if self.__width == 0 or self.__height == 0: assert False # Fullscreen textured quad filled_points = [ ((-1.0, -1.0), (0.0, 0.0)), ((1.0, -1.0), (1.0, 0.0)), ((-1.0, 1.0), (0.0, 1.0)), ((1.0, 1.0), (1.0, 1.0)), ] ar = numpy.array(filled_points, dtype=[("vertex", numpy.float32, 2), ("texpos", numpy.float32, 2)]) sscale = max(self.__width, self.__height) xscale = self.__width / sscale yscale = self.__height / sscale ar["vertex"][:, 0] *= xscale ar["vertex"][:, 1] *= yscale return ar def initializeGL(self, gls: 'GLShared', width: int, height: int) -> None: self.__width = width self.__height = height # Initialize (but don't fill) the Color LUT self.__texture_colors = Texture(debug_name="Layer Color LUT") with self.__texture_colors.on(GL.GL_TEXTURE_1D): GL.glTexParameteri(GL.GL_TEXTURE_1D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST) GL.glTexParameteri(GL.GL_TEXTURE_1D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST) GL.glTexParameteri(GL.GL_TEXTURE_1D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE) GL.glTexParameteri(GL.GL_TEXTURE_1D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE) # Compositing shader and geometry self.__composite_shader = gls.shader_cache.get( "layer_composite_vert", "layer_composite_frag", fragment_bindings={"final_color": 0}) ar = self.__get_vbo_data() self.__composite_vao = VAO(debug_name="Compositor Quad VAO") self.__composite_vbo = VBO(ar, GL.GL_STATIC_DRAW) GL.glObjectLabel(GL.GL_BUFFER, int(self.__composite_vbo), -1, "Compositor Quad VBO") with self.__composite_vao: self.__composite_vbo.bind() VBOBind(self.__composite_shader.program, ar.dtype, "vertex").assign() VBOBind(self.__composite_shader.program, ar.dtype, "texpos").assign() def set_color_table(self, colors: 'Sequence[Tuple[int, int, int, int]]') -> None: array: 'npt.NDArray[numpy.uint8]' = numpy.ndarray((256, 4), dtype=numpy.uint8) # Create a stub array with the color table data array.fill(0) array[:] = (255, 0, 255, 255) array[:len(colors)] = colors with self.__texture_colors.on(GL.GL_TEXTURE_1D): GL.glTexImage1D(GL.GL_TEXTURE_1D, 0, GL.GL_RGBA, 256, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, array) class _PREBIND: def __init__(self, layer_list: 'Dict[Any, RenderLayer]', composite_shader: 'EnhShaderProgram'): self.layer_list = layer_list self.composite_shader = composite_shader def composite(self, n: Any, layer_primary_color: 'Tuple[int,int,int]') -> None: alpha = 0.5 * 255 try: layer = self.layer_list[n] except KeyError: return GL.glActiveTexture(GL.GL_TEXTURE0) GL.glBindTexture(GL.GL_TEXTURE_2D, layer.info_texture.v) GL.glUniform4f(self.composite_shader.uniforms.layer_color, layer_primary_color[0], layer_primary_color[1], layer_primary_color[2], alpha) GL.glDrawArrays(GL.GL_TRIANGLE_STRIP, 0, 4) @contextlib.contextmanager def composite_prebind(self) -> Generator['_PREBIND', None, None]: GL.glActiveTexture(GL.GL_TEXTURE1) GL.glBindTexture(GL.GL_TEXTURE_1D, self.__texture_colors.v) GL.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA) # Composite the layer to the screen with self.__composite_shader.program, self.__composite_vao: GL.glUniform1i(self.__composite_shader.uniforms.layer_info, 0) GL.glUniform1i(self.__composite_shader.uniforms.color_tab, 1) yield self._PREBIND(self.__keys, self.__composite_shader)
class THRenderer: def __init__(self, parent_view: 'BoardViewWidget') -> None: self.parent = parent_view def initializeGL(self, glshared: 'GLShared') -> None: self._filled_shader = glshared.shader_cache.get( "via_filled_vertex_shader", "via_filled_fragment_shader") self._outline_shader = glshared.shader_cache.get( "via_outline_vertex_shader", "basic_fill_frag") # Build geometry for filled rendering using the frag shader for circle borders filled_points = [ ((-1, -1), ), ((1, -1), ), ((-1, 1), ), ((1, 1), ), ] ar = numpy.array(filled_points, dtype=[("vertex", numpy.float32, 2)]) self._sq_vbo = VBO(ar, GL.GL_STATIC_DRAW) GL.glObjectLabel(GL.GL_BUFFER, int(self._sq_vbo), -1, "Via SQ VBO") # Build geometry for outline rendering outline_points = [] for i in numpy.linspace(0, math.pi * 2, N_OUTLINE_SEGMENTS, False): outline_points.append(((math.cos(i), math.sin(i)), )) outline_points_array = numpy.array(outline_points, dtype=[("vertex", numpy.float32, 2) ]) self._outline_vbo = VBO(outline_points_array, GL.GL_STATIC_DRAW) GL.glObjectLabel(GL.GL_BUFFER, int(self._sq_vbo), -1, "Via Outline VBO") self.__dtype = numpy.dtype([ ("pos", numpy.float32, 2), ("r", numpy.float32), ("r_inside_frac_sq", numpy.float32), ]) self.__filled_vao = VAO() self.__outline_vao = VAO() with self.__filled_vao, self._sq_vbo: VBOBind(self._filled_shader.program, self._sq_vbo.data.dtype, "vertex").assign() # Use a fake array to get a zero-length VBO for initial binding filled_instance_array = numpy.ndarray(0, dtype=self.__dtype) self.filled_instance_vbo = VBO(filled_instance_array) GL.glObjectLabel(GL.GL_BUFFER, int(self._sq_vbo), -1, "Via Filled Instance VBO") with self.__filled_vao, self.filled_instance_vbo: VBOBind(self._filled_shader.program, self.__dtype, "pos", div=1).assign() VBOBind(self._filled_shader.program, self.__dtype, "r", div=1).assign() VBOBind(self._filled_shader.program, self.__dtype, "r_inside_frac_sq", div=1).assign() with self.__outline_vao, self._outline_vbo: VBOBind(self._outline_shader.program, self._outline_vbo.data.dtype, "vertex").assign() # Build instance for outline rendering # We don't have an inner 'r' for this because we just do two instances per vertex # Use a fake array to get a zero-length VBO for initial binding outline_instance_array = numpy.ndarray(0, dtype=self.__dtype) self.outline_instance_vbo = VBO(outline_instance_array) GL.glObjectLabel(GL.GL_BUFFER, int(self.outline_instance_vbo), -1, "Via Outline Instance VBO") with self.__outline_vao, self.outline_instance_vbo: VBOBind(self._outline_shader.program, self.__dtype, "pos", div=1).assign() VBOBind(self._outline_shader.program, self.__dtype, "r", div=1).assign() def render_filled(self, mat: 'npt.NDArray[numpy.float64]', va: 'VA_xy', color: Tuple[float, float, float] = COL_VIA) -> None: self.filled_instance_vbo.set_array(va.buffer()[:]) try: with self._filled_shader.program: with self.__filled_vao: with self.filled_instance_vbo: with self._sq_vbo: GL.glUniformMatrix3fv( self._filled_shader.uniforms.mat, 1, True, mat.astype(numpy.float32)) GL.glUniform1ui(self._filled_shader.uniforms.color, color) GL.glDrawArraysInstancedBaseInstance( GL.GL_TRIANGLE_STRIP, 0, 4, va.count(), 0) except OpenGL.error.GLError as e: print("Threw OGL error:", e) def render_outlines(self, mat: 'npt.NDArray[numpy.float64]', va: 'VA_xy') -> None: if not va.count(): return self.outline_instance_vbo.set_array(va.buffer()[:]) self.outline_instance_vbo.bind() with self._outline_shader.program, self.__outline_vao, self.outline_instance_vbo, self._sq_vbo: GL.glUniformMatrix3fv(self._outline_shader.uniforms.mat, 1, True, mat.astype(numpy.float32)) GL.glUniform4ui(self._outline_shader.uniforms.layer_info, 255, COL_SEL, 0, 0) GL.glDrawArraysInstanced(GL.GL_LINE_LOOP, 0, N_OUTLINE_SEGMENTS, va.count())
glEnable(GL_DEPTH_TEST) glEnable(GL_CULL_FACE) glClearColor(1, 1, 1, 0) glPointSize(5) # Set up the shader. prog = shaderutil.createProgram("./shader.vs", "./shader.fs") mvploc = glGetUniformLocation(prog, "mvp") positionloc = glGetAttribLocation(prog, "vs_position") colorloc = glGetAttribLocation(prog, "vs_color") # Setup VAO vertobj = glGenVertexArrays(1) glBindVertexArray(vertobj) # Setup the VBO (using the fancy VBO Object from pyopengl, doing it "manually" would also be a possibility) vertbuf = VBO(cubedata, GL_STATIC_DRAW) vertbuf.bind() glEnableVertexAttribArray(positionloc) glEnableVertexAttribArray(colorloc) glVertexAttribPointer(positionloc, 4, GL_FLOAT, GL_TRUE, 8 * 4, vertbuf+0) # "+0" since we need to create an offset. glVertexAttribPointer(colorloc, 4, GL_FLOAT, GL_TRUE, 8 * 4, vertbuf+16) # 4 * 4 Bytes per float. vertbuf.unbind() # We can unbind the VBO, since it's linked to the VAO # glBindVertexArray(0) running = True t = time.time() rotation = 0.0 while running: glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glUseProgram(prog) glUniformMatrix4fv(mvploc, 1, GL_TRUE, hm.rotation(mvp, rotation,[0,1,0]).tolist()) # glBindVertexArray(vertobj)
class VolumeObject(object): def __init__(self, stack, spacing): self.stack_texture, shape = self.load_stack(stack) self.vao = glGenVertexArrays(1) glBindVertexArray(self.vao) tl = np.array((shape[2]*spacing[2], shape[1]*spacing[1], shape[0]*spacing[0])) # Vertex buffer: corners of cube. # x, y, z, texture_x, texture_y, texture_z vb = [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0], # Corner 0. [tl[0], 0.0, 0.0, 1.0, 0.0, 0.0], [0.0, tl[1], 0.0, 0.0, 1.0, 0.0], [tl[0], tl[1], 0.0, 1.0, 1.0, 0.0], [0.0, 0.0, tl[2], 0.0, 0.0, 1.0], [tl[0], 0.0, tl[2], 1.0, 0.0, 1.0], [0.0, tl[1], tl[2], 0.0, 1.0, 1.0], [tl[0], tl[1], tl[2], 1.0, 1.0, 1.0]] # Corner 7. vb = np.array(vb, dtype=np.float32) vb = vb.flatten() # Triangles of cube. idx_out = np.array([[0, 2, 1], [2, 3, 1], # Triangle 0, triangle 1. [1, 4, 0], [1, 5, 4], [3, 5, 1], [3, 7, 5], [2, 7, 3], [2, 6, 7], [0, 6, 2], [0, 4, 6], [5, 6, 4], [5, 7, 6]], # Triangle 10, triangle 11. dtype=np.uint32) self.vtVBO = VBO(vb) sc = 1.0/la.norm(tl) c = 0.5*tl self.transform = np.array(((0.0, 0.0, sc, -sc*c[2]), (0.0, sc, 0.0, -sc*c[1]), (sc, 0.0, 0.0, -sc*c[0]), (0.0, 0.0, 0.0, 1.0))) self.elVBO = VBO(idx_out, target=GL_ELEMENT_ARRAY_BUFFER) self.elCount = len(idx_out.flatten()) print('made VBO') self.vtVBO.bind() def load_stack(self, stack): print('stack shape', stack.shape) s = np.array(stack, dtype=np.uint8, order='F') print(s.shape) w, h, d = s.shape print('shape', s.shape) stack_texture = glGenTextures(1) print(stack_texture) glActiveTexture(GL_TEXTURE0) glBindTexture(GL_TEXTURE_3D, stack_texture) glTexParameter(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) glTexParameter(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glPixelStorei(GL_UNPACK_ALIGNMENT, 1) # glTexParameter(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) # glTexParameter(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) # glTexParameter(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE) glTexImage3D(GL_TEXTURE_3D, 0, GL_R8, d, h, w, 0, GL_RED, GL_UNSIGNED_BYTE, s) print("made 3D texture") return stack_texture, s.shape
class TraceRender: def __init__(self, parent_view: 'BoardViewWidget') -> None: self.parent = parent_view self.restart() def initializeGL(self, gls: 'GLShared') -> None: # Build trace vertex VBO and associated vertex data dtype = [("vertex", numpy.float32, 2), ("ptid", numpy.uint32)] self.working_array = numpy.zeros(NUM_ENDCAP_SEGMENTS * 2 + 2, dtype=dtype) self.trace_vbo = VBO(self.working_array, GL.GL_DYNAMIC_DRAW) GL.glObjectLabel(GL.GL_BUFFER, int(self.trace_vbo), -1, "Thickline Trace VBO") # Generate geometry for trace and endcaps # ptid is a variable with value 0 or 1 that indicates which endpoint the geometry is associated with self.__build_trace() self.__attribute_shader_vao = VAO( debug_name="Thickline attribute shader VAO") shader = gls.shader_cache.get("line_vertex_shader", "basic_fill_frag", defines={"INPUT_TYPE": "in"}) assert shader is not None self.__attribute_shader: 'EnhShaderProgram' = shader # Now we build an index buffer that allows us to render filled geometry from the same # VBO. arr = [] for i in range(NUM_ENDCAP_SEGMENTS - 1): arr.append(0) arr.append(i + 2) arr.append(i + 3) for i in range(NUM_ENDCAP_SEGMENTS - 1): arr.append(1) arr.append(i + NUM_ENDCAP_SEGMENTS + 2) arr.append(i + NUM_ENDCAP_SEGMENTS + 3) arr.append(2) arr.append(2 + NUM_ENDCAP_SEGMENTS - 1) arr.append(2 + NUM_ENDCAP_SEGMENTS) arr.append(2 + NUM_ENDCAP_SEGMENTS) arr.append(2 + NUM_ENDCAP_SEGMENTS * 2 - 1) arr.append(2) arr2 = numpy.array(arr, dtype=numpy.uint32) self.index_vbo = VBO(arr2, target=GL.GL_ELEMENT_ARRAY_BUFFER) GL.glObjectLabel(GL.GL_BUFFER, int(self.index_vbo), -1, "Thickline Index VBO") self.instance_dtype = numpy.dtype([ ("pos_a", numpy.float32, 2), ("pos_b", numpy.float32, 2), ("thickness", numpy.float32), # ("color", numpy.float32, 4) ]) # Use a fake array to get a zero-length VBO for initial binding instance_array: 'npt.NDArray[Any]' = numpy.ndarray( 0, dtype=self.instance_dtype) self.instance_vbo = VBO(instance_array) GL.glObjectLabel(GL.GL_BUFFER, int(self.instance_vbo), -1, "Thickline Instance VBO") with self.__attribute_shader_vao, self.trace_vbo: VBOBind(self.__attribute_shader.program, self.trace_vbo.dtype, "vertex").assign() VBOBind(self.__attribute_shader.program, self.trace_vbo.dtype, "ptid").assign() with self.__attribute_shader_vao, self.instance_vbo: self.__bind_pos_a = VBOBind(self.__attribute_shader.program, self.instance_dtype, "pos_a", div=1) self.__bind_pos_b = VBOBind(self.__attribute_shader.program, self.instance_dtype, "pos_b", div=1) self.__bind_thickness = VBOBind(self.__attribute_shader.program, self.instance_dtype, "thickness", div=1) # vbobind(self.__attribute_shader, self.instance_dtype, "color", div=1).assign() self.__base_rebind(0) self.index_vbo.bind() def __base_rebind(self, base: int) -> None: self.__bind_pos_a.assign(base) self.__bind_pos_b.assign(base) self.__bind_thickness.assign(base) def restart(self) -> None: pass def __build_trace(self) -> None: # Update trace VBO self.working_array["vertex"][0] = (0, 0) self.working_array["ptid"][0] = 0 self.working_array["vertex"][1] = (0, 0) self.working_array["ptid"][1] = 1 end = Vec2(1, 0) for i in range(0, NUM_ENDCAP_SEGMENTS): theta = math.pi * i / (NUM_ENDCAP_SEGMENTS - 1) + math.pi / 2 m = rotate(theta).dot(end.homol()) self.working_array["vertex"][2 + i] = m[:2] self.working_array["ptid"][2 + i] = 0 self.working_array["vertex"][2 + i + NUM_ENDCAP_SEGMENTS] = -m[:2] self.working_array["ptid"][2 + i + NUM_ENDCAP_SEGMENTS] = 1 # Force data copy self.trace_vbo.bind() self.trace_vbo.set_array(self.working_array) def __render_va_inner(self, col: int, is_outline: bool, first: int, count: int) -> None: GL.glUniform4ui(self.__attribute_shader.uniforms.layer_info, 255, col, 0, 0) if has_base_instance: # Many instances backport glDrawElementsInstancedBaseInstance # This is faster than continually rebinding, so support if possible if not is_outline: # filled traces come first in the array GL.glDrawElementsInstancedBaseInstance(GL.GL_TRIANGLES, TRIANGLES_SIZE, GL.GL_UNSIGNED_INT, ctypes.c_void_p(0), count, first) else: # Then outline traces. We reuse the vertex data for the outside GL.glDrawArraysInstancedBaseInstance(GL.GL_LINE_LOOP, 2, NUM_ENDCAP_SEGMENTS * 2, count, first) else: with self.instance_vbo: if not is_outline: # filled traces come first in the array self.__base_rebind(first) GL.glDrawElementsInstanced(GL.GL_TRIANGLES, TRIANGLES_SIZE, GL.GL_UNSIGNED_INT, ctypes.c_void_p(0), count) else: self.__base_rebind(first) # Then outline traces. We reuse the vertex data for the outside GL.glDrawArraysInstanced(GL.GL_LINE_LOOP, 2, NUM_ENDCAP_SEGMENTS * 2, count) def render_va(self, va: 'VA_thickline', mat: 'npt.NDArray[numpy.float64]', col: int, is_outline: bool = False, first: int = 0, count: 'Optional[int]' = None) -> None: GL.glPushDebugGroup(GL.GL_DEBUG_SOURCE_APPLICATION, 0, -1, "Thickline Draw") assert self.instance_dtype.itemsize == va.stride self.instance_vbo.set_array(va.buffer()[:]) if count is None: count = va.count() - first with self.__attribute_shader.program, self.__attribute_shader_vao, self.instance_vbo: GL.glUniformMatrix3fv(self.__attribute_shader.uniforms.mat, 1, True, mat.astype(numpy.float32)) self.__render_va_inner(col, is_outline, first, count) GL.glPopDebugGroup()
class Mesh(Component): meshes = dict() # cache to reuse meshes @classmethod def fromXMLElement(cls, xmlElement, actor=None): return cls.getMesh(xmlElement.get('src', 'Empty'), actor) # NOTE If src attribute is not found in XML element, special 'Empty' mesh is used @classmethod def getMesh(cls, src, actor=None): if not src in cls.meshes: if src == 'Empty': cls.meshes[src] = EmptyMesh( ) # special empty mesh to be used as a placeholder, and for empty actors else: cls.meshes[src] = Mesh( src ) # NOTE Meshes are currently shared, therefore not linked to individual actors return cls.meshes[src] def __init__(self, src, actor=None): Component.__init__(self, actor) # TODO Include a mesh name (e.g. 'Dragon') as ID as well as src (e.g. '../res/models/Dragon.obj') self.src = src self.filepath = Context.getInstance().getResourcePath('models', src) # OpenGL version-dependent code (NOTE assumes major version = 3) self.vao = None if Context.getInstance().GL_version_minor > 0: # 3.1 (or greater?) self.vao = glGenVertexArrays(1) else: # 3.0 (or less?) self.vao = GLuint(0) glGenVertexArrays(1, self.vao) glBindVertexArray(self.vao) self.loadModel(self.filepath) self.vbo = VBO(self.meshData, GL_STATIC_DRAW) self.vbo.bind() glEnableVertexAttribArray(0) glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * 4, self.vbo + 0) glEnableVertexAttribArray(1) glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * 4, self.vbo + 12) self.ebo = glGenBuffers(1) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.ebo) glBufferData(GL_ELEMENT_ARRAY_BUFFER, len(self.elements) * 4, self.elements, GL_STATIC_DRAW) def loadModel(self, filename): f = open(filename, 'r') self.meshData = [] self.elements = [] vertices = [] normals = [] texCoords = [] for line in f: s = line.split() if len(s) > 0: if s[0] == 'v': v = np.array([s[1], s[2], s[3]], dtype=np.float32) vertices.append(v) elif s[0] == 'vn': v = np.array([s[1], s[2], s[3]], dtype=np.float32) normals.append(v) elif s[0] == 'vt': v = np.array([s[1], s[2]], dtype=np.float32) texCoords.append(v) elif s[0] == 'f': for i in xrange(1, 4): l = s[i].split('/') self.meshData.append(float(vertices[int(l[0]) - 1][0])) self.meshData.append(float(vertices[int(l[0]) - 1][1])) self.meshData.append(float(vertices[int(l[0]) - 1][2])) self.meshData.append(float(normals[int(l[2]) - 1][0])) self.meshData.append(float(normals[int(l[2]) - 1][1])) self.meshData.append(float(normals[int(l[2]) - 1][2])) self.elements.append(len(self.elements)) self.meshData = np.array(self.meshData, dtype=np.float32) self.elements = np.array(self.elements, dtype=np.uint32) def render(self): glBindVertexArray(self.vao) glDrawElements(GL_TRIANGLES, self.elements.size, GL_UNSIGNED_INT, None) def toXMLElement(self): xmlElement = Component.toXMLElement(self) xmlElement.set('src', self.src) return xmlElement def toString(self, indent=""): return indent + "Mesh: { src: \"" + self.src + "\" }" def __str__(self): return self.toString()
class MSGLCanvas2D(QGLWidget): """ Canvas GL plotting in 2 dimensions """ MAX = 100. corner_ = 100.0 zoom_ = 1.5 xrot_ = 220 yrot_ = 220 trans_x_ = 0.0 trans_y_ = 0.0 def __init__(self, data, parent=None, **kw): """ Constructor, initialization """ QGLWidget.__init__(self, parent) self.setFormat(QGLFormat(QGL.SampleBuffers)) self.setMinimumSize(500, 300) #300 self.setMouseTracking(True) self.setFocusPolicy(Qt.StrongFocus) self.data = data vertexes = [] colors = [] from utils.misc import IceAndFire maxY = max(map(max, [log10(el.y_data) for el in data])) maxX = max(map(max, [el.x_data for el in data])) rtmax = max([z.rtmin for z in data]) for el in data: for i, x in enumerate(el.x_data): c = IceAndFire.getQColor(log10(el.y_data[i]) / maxY) colors.append(c) vertexes.append([(x * 2 * self.corner_) / maxX, (el.rt * 2 * self.corner_) / rtmax]) from OpenGL.arrays.vbo import VBO self.vertexes = VBO(array(vertexes, 'f')) self.colors = VBO(array(colors, 'f')) self.mode = "None" # "ZOOMING", "PANNING", "NONE" self.lastpos = QPoint() self.counter_trans_x = 0 self.counter_trans_y = 0 self.defaultColors = { 'ticks': (0., 0., 0., 0.), 'axes': (0., 0., 0., 0.), 'curves': (0., 0., 1., 0.), 'backgroundColor': (1., 1., 1., 1.) } #self.axes=self.drawAxes() self.transformationMatrix = self.setupTransformationMatrix( self.width(), self.height()) def setupTransformationMatrix(self, w, h): """ use a matrix to translate in the gl landmark """ m = QMatrix() m.translate(-w / 2, h / 2) m.scale(300. / w, 300. / h) print w, h, w / 300., 1 - ((h / 300) - 1) #m.scale((self.width()*100)/300, -(self.height()*100)/300) #self.currentSize.x = w #self.currentSize.y = h return m def inGLCoordinate(self, point): return self.transformationMatrix.map(point) def resizeGL(self, w, h): """ called when window is being resized """ glViewport(0, 0, w, h) glMatrixMode(GL_PROJECTION) glLoadIdentity() gluOrtho2D(-self.corner_ * self.zoom_, self.corner_ * self.zoom_, -self.corner_ * self.zoom_, self.corner_ * self.zoom_) #self.transformationMatrix = self.setupTransformationMatrix(w, h) glMatrixMode(GL_MODELVIEW) def initializeGL(self): """needed, initialize GL parameters""" #glClearColor(1.,1.,1.,1.) glDisable(GL_DEPTH_TEST) glEnable(GL_LINE_SMOOTH) glEnable(GL_POINT_SMOOTH) glHint(GL_LINE_SMOOTH_HINT, GL_NICEST) glLoadIdentity() #model view by default # self.grid_lines = self.drawGridLines() # self.ticks =self.drawAxisTick() # self.axes = self.drawAxes() def paintGL(self): """Draw the scene, needed, called each time glDraw""" glClear(GL_COLOR_BUFFER_BIT) glTranslated(self.trans_x_, self.trans_y_, 0.) #addition glMatrixMode(GL_PROJECTION) glLoadIdentity() gluOrtho2D(-self.corner_ * self.zoom_, self.corner_ * self.zoom_, -self.corner_ * self.zoom_, self.corner_ * self.zoom_) glMatrixMode(GL_MODELVIEW) #end addition #glCallList(self.grid_lines) #glCallList(self.ticks) #glCallList(self.axes) glLineWidth(30.0) self.scatterPlot() # if self.flags == "chrom": # self.drawAxisLegend("retention time[s]", "intensity[%]") # glCallList(self.lines) # elif self.flags == "spectrum": # self.drawAxisLegend("m/z", "intensity") def drawAxes(self, width=2., colour=(0., 0., 0.)): """ Draw Axes """ #width must be a float axes = glGenLists(1) glNewList(axes, GL_COMPILE) glLineWidth(width) glColor(colour[0], colour[1], colour[2]) glBegin(GL_LINES) #x_achse glVertex2d(-self.corner_, -self.corner_) glVertex2d(self.corner_, -self.corner_) #y-achse glVertex2d(-self.corner_, -self.corner_) glVertex2d(-self.corner_, self.corner_) glEnd() glEndList() return axes def drawLegends(self, pos): """ draw legend at the specified position """ pass def drawAxisLegend(self, x_label, y_label): """ Draw Axis Legend """ font = QFont("Typewriter") #RT axis legend font.setPixelSize(12) self.renderText(self.corner_, -self.corner_ - 20.0, 0., x_label) # font self.renderText(-self.corner_ - 20.0, self.corner_, 0., y_label, font) def resetTranslations(self): """ reset the different translation to 0 """ self.trans_x_ = 0. self.trans_y_ = 0. self.counter_trans_x = 0. self.counter_trans_y = 0. def normalizeAngle(self, angle): while (angle < 0): angle += 360 * 16 while (angle > 360 * 16): angle -= 360 * 16 ########DRAWING METHODS################################################## def drawLine(self, point_, point): glBegin(GL_LINES) glVertex2d(point_.x(), point_.y()) glVertex2d(point.x(), point.y()) glEnd() def drawRect(self, p_1, p_2, p_3=None, p_4=None): pass def drawOnePoint(self, point, colour=Qt.yellow): pass def scatterPlot(self): """ Draw Data (x, y)""" if self.vertexes is not None and self.colors is not None: self.vertexes.bind() glEnableClientState(GL_VERTEX_ARRAY) glVertexPointerf(self.vertexes) self.colors.bind() glEnableClientState(GL_COLOR_ARRAY) glColorPointerf(self.colors) glDrawArrays(GL_LINES, 0, len(self.vertexes)) self.vertexes.unbind() self.colors.unbind() #self.textures.unbind() glDisableClientState(GL_VERTEX_ARRAY) glDisableClientState(GL_COLOR_ARRAY) def spectrumPlot(self, points): pass def histogramPlot(self, points, bin=5.): pass def barPlot(points, width=2.): pass ########MOUSE AND KEYBOARDS EVENTS########################################################################### def wheelEvent(self, event): if event.delta() > 0: self.zoom_ -= .05 else: self.zoom_ += .05 glMatrixMode(GL_PROJECTION) glLoadIdentity() gluOrtho2D(-self.corner_ * self.zoom_, self.corner_ * self.zoom_, -self.corner_ * self.zoom_, self.corner_ * self.zoom_) self.updateGL() glMatrixMode(GL_MODELVIEW) event.accept() def keyPressEvent(self, event): if event.key() == Qt.Key_Plus: self.zoom_ -= .1 glMatrixMode(GL_PROJECTION) glLoadIdentity() gluOrtho2D(-self.corner_ * self.zoom_, self.corner_ * self.zoom_, -self.corner_ * self.zoom_, self.corner_ * self.zoom_) glMatrixMode(GL_MODELVIEW) if event.key() == Qt.Key_Minus: self.zoom_ += .1 glMatrixMode(GL_PROJECTION) #// You had GL_MODELVIEW glLoadIdentity() gluOrtho2D(-self.corner_ * self.zoom_, self.corner_ * self.zoom_, -self.corner_ * self.zoom_, self.corner_ * self.zoom_) glMatrixMode(GL_MODELVIEW) if event.key() == Qt.Key_Up: self.trans_y_ += 2 self.counter_trans_y += 2 if event.key() == Qt.Key_Down: self.trans_y_ -= 2 self.counter_trans_y -= 2 if event.key() == Qt.Key_Left: self.trans_x_ -= 2 self.counter_trans_x -= 2 if event.key() == Qt.Key_Right: self.trans_x_ += 2 self.counter_trans_x += 2 if event.key() == Qt.Key_Z: self.mode = "ZOOMING" if self.counter_trans_x < 0 and self.counter_trans_y < 0: while self.counter_trans_x < 0 and self.counter_trans_y < 0: self.trans_x_ = self.counter_trans_x self.trans_y_ = self.counter_trans_y self.updateGL() self.counter_trans_x += 1 self.counter_trans_y += 1 if self.counter_trans_x > 0 and self.counter_trans_y < 0: while self.counter_trans_x < 0 and self.counter_trans_y < 0: self.trans_x_ = self.counter_trans_x self.trans_y_ = self.counter_trans_y self.updateGL() self.counter_trans_x -= 1 self.counter_trans_y += 1 if self.counter_trans_x < 0 and self.counter_trans_y > 0: while self.counter_trans_x < 0 and self.counter_trans_y < 0: self.trans_x_ = self.counter_trans_x self.trans_y_ = self.counter_trans_y self.updateGL() self.counter_trans_x += 1 self.counter_trans_y -= 1 if self.counter_trans_x < 0 and self.counter_trans_y > 0: while self.counter_trans_x < 0 and self.counter_trans_y < 0: self.trans_x_ = self.counter_trans_x self.trans_y_ = self.counter_trans_y self.updateGL() self.counter_trans_x -= 1 self.counter_trans_y -= 1 if self.zoom_ != 1.5: self.zoom = 1.5 #self.updateGL() self.updateGL() self.resetTranslations() def mousePressEvent(self, event): if self.mode == "ZOOMING": self.mode = "None" self.computeSelection() else: self.lastpos = QPoint(event.pos()) self.setCursor(QCursor(Qt.ClosedHandCursor)) #if event.buttons() == Qt.RightButton: # self.mode = "PANNING" def computeSelection(self): print "selected" def mouseMoveEvent(self, event): dx = event.x() - self.lastpos.x() dy = event.y() - self.lastpos.y() if self.mode == "ZOOMING": font = QFont("Typewriter") self.renderText(-self.corner_ - 30.0, self.corner_, 0., "ZOOMING MODE ACTIVATED", font) self.updateGL() glColor(0., 0., 1., .5) XMAX = 900. XMIN = 180. pointer_x = (self.lastpos.x() * 200.) / XMAX norm_dx = (dx * 200.) / XMAX """ if pointer_x > 100. or pointer_x < 100. \ or norm_dx >100. or norm_dx<-100.: event.ignore() """ glBegin(GL_QUADS) glVertex2d(pointer_x, -100.) glVertex2d(pointer_x + norm_dx, -100.) glVertex2d(pointer_x + norm_dx, 100.) glVertex2d(pointer_x, 100.) glEnd() self.updateGL() #update for seeing the rectangle mapping = self.mapFromGlobal cursorPos = self.inGLCoordinate(mapping(self.cursor().pos())) QToolTip.showText(self.cursor().pos(), "x:"+str(cursorPos.x())+ \ ", y:"+str(cursorPos.y()) ) if self.mode == "None": if event.buttons() == Qt.LeftButton: self.trans_y_ -= dy / 5 self.counter_trans_y -= dy / 5 self.trans_x_ += dx / 5 self.counter_trans_x += dx / 5 self.lastpos = QPoint(event.pos()) self.glDraw() self.resetTranslations() def mouseReleaseEvent(self, event): self.setCursor(QCursor(Qt.ArrowCursor))
# set resizing callback function glfw.set_framebuffer_size_callback(theWindow, window_resize_callback) # create VBO to store vertices verticesVBO = VBO(triangleVertices, usage='GL_STATIC_DRAW') verticesVBO.create_buffers() # create VAO to describe array information triangleVAO = glGenVertexArrays(1) # bind VAO glBindVertexArray(triangleVAO) # bind VBO verticesVBO.bind() # buffer data into OpenGL verticesVBO.copy_data() # configure the fist 3-vector (pos) # arguments: index, size, type, normalized, stride, pointer # the stride is 6 * 4 because there are six floats per vertex, and the size of # each float is 4 bytes glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * 4, ctypes.c_void_p(0)) glEnableVertexAttribArray(0) # configure the second 3-vector (color) # the offset is 3 * 4 = 12 bytes glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * 4, ctypes.c_void_p(3 * 4)) glEnableVertexAttribArray(1)
class TextBatcher(object): __tag_type = namedtuple("render_tag", ["textinfo", "matrix", "color"]) def __init__(self, tr): self.text_render = tr self.__cached = {} self.__clist = [] self.restart() def restart(self): self.__render_tags = defaultdict(list) def initializeGL(self): # Working VBO that will contain glyph data self.vbo = VBO(numpy.ndarray(0, dtype=self.text_render.buffer_dtype), GL.GL_DYNAMIC_DRAW, GL.GL_ARRAY_BUFFER) self.vao = VAO() with self.vao, self.vbo: self.text_render.b1.assign() self.text_render.b2.assign() self.__vbo_needs_update = True def render(self, key=None): if self.__vbo_needs_update: arr = numpy.array(self.__clist, dtype=self.text_render.buffer_dtype) self.vbo.data = arr self.vbo.size = None self.vbo.copied = False self.vbo.bind() self.__vbo_needs_update = False self.text_render.updateTexture() with self.text_render.sdf_shader, self.text_render.tex.on(GL.GL_TEXTURE_2D), self.vao: GL.glUniform1i(self.text_render.sdf_shader.uniforms.tex1, 0) for tag in self.__render_tags[key]: mat_calc = tag.matrix GL.glUniformMatrix3fv(self.text_render.sdf_shader.uniforms.mat, 1, True, mat_calc.astype(numpy.float32)) GL.glUniform4f(self.text_render.sdf_shader.uniforms.color, *tag.color) GL.glDrawArrays(GL.GL_TRIANGLES, tag.textinfo.start, tag.textinfo.count) def submit(self, ts, mat, color, k=None): self.__render_tags[k].append(self.__tag_type(ts, mat, color)) def submit_text_box(self, premat, text, box, color, k=None): ts = self.get_string(text) mat = premat.dot(ts.get_render_to_mat(box)) self.submit(ts, mat, color, k) def get_string(self, text): if text in self.__cached: return self.__cached[text] self.__cached[text] = ti = self.text_render.getString(text) ti.start = len(self.__clist) self.__clist.extend(ti.arr) ti.count = len(ti.arr) self.__vbo_needs_update = True return ti
class instancingdemo(df.demoplate): def init(self): print "OpenGL Information:" for prop in ["GL_VENDOR", "GL_RENDERER", "GL_VERSION", "GL_SHADING_LANGUAGE_VERSION"]: print "\t%s = %s" % (prop, glGetString(globals()[prop])) self.campos = np.array([2.5, 1.5, 1.5, 1], dtype = np.float32) self.center = np.array([0.0,0.0,0.0,1.0], dtype = np.float32) self.perspective_mat = None self.mvp = None # OpenGL Stuff glEnable(GL_DEPTH_TEST) #glEnable(GL_CULL_FACE) glClearColor(1, 1, 1, 0) glPointSize(5) # Shader Stuff. with open('shader.fs') as fs, open('shader.vs') as vs: self.shader = su.Shader(list(vs), list(fs)) self.shader.bindfragdata(0, 'fragcolor') self.mvploc = self.shader.uniformlocation('mvp') self.objploc = self.shader.uniformlocation('objp') self.objoffsetloc = self.shader.uniformlocation('objoffset') self.positionloc = self.shader.attributelocation('vs_position') self.normalloc = self.shader.attributelocation('vs_normal') self.loadsquirrel() self.buildobjoffsets() def buildobjoffsets(self): objoffset = np.zeros((5,5,5,4), dtype = np.float32) start = np.array([-1, -1, -1, 0], dtype = np.float32) steps = np.arange(0, 2.1, 0.5, dtype = np.float32) for x in range(5): for y in range(5): for z in range(5): objoffset[x,y,z,] = start + np.array([steps[x], steps[y], steps[z], 0], dtype = np.float32) self.shader.use() glUniform4fv(self.objoffsetloc, 5 * 5 * 5, objoffset.flatten().tolist()) def loadsquirrel(self): # When loading the data, pad the normals to 4 floats (16bytes) since GPUs hate unaligned memory. obj = wf.ObjFileParser("squirrel.obj", padnormals = 4) self.objscale = 1 / np.max(obj.scale / 2) self.objcenter = obj.minpos + (obj.scale / 2) self.obj_mat = hm.scale(hm.identity(), [self.objscale * 0.2] * 3) self.obj_mat = hm.translation(self.obj_mat, -self.objcenter) # Generate a GL compatible indexed vertex buffer for the object self.vbdata, ibdata = obj.generateIndexedBuffer([0,1], np.uint16) vbdata = self.vbdata self.elementnum = np.shape(ibdata)[0] # VAO self.vertobj = glGenVertexArrays(1) glBindVertexArray(self.vertobj) # Setup the VBO for the vertex data. self.vertbuf = VBO(vbdata, GL_STATIC_DRAW, GL_ARRAY_BUFFER) self.vertbuf.bind() glVertexAttribPointer(self.positionloc, 4, GL_FLOAT, GL_TRUE, 8 * 4, ctypes.c_void_p(0)) glVertexAttribPointer(self.normalloc, 4, GL_FLOAT, GL_TRUE, 8 * 4, ctypes.c_void_p(16)) glEnableVertexAttribArray(self.positionloc) glEnableVertexAttribArray(self.normalloc) # Indexbuffer self.indexbuf = VBO(ibdata, GL_STATIC_DRAW, GL_ELEMENT_ARRAY_BUFFER) self.indexbuf.bind() glBindVertexArray(0) self.vertbuf.unbind() self.indexbuf.unbind() #Animation init... self.rotation = 0 def resize(self, width, height): glViewport(0, 0, width, height) self.perspective_mat = hm.perspective(hm.identity(), 60, float(width) / height, 0.1, 6.0) self.modelview_mat = hm.lookat(hm.identity(), self.campos, self.center) self.mvp = np.dot(self.perspective_mat, self.modelview_mat) def display(self, timediff): glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) self.shader.use() glUniformMatrix4fv(self.objploc, 1, GL_TRUE, np.dot(hm.rotation(hm.identity(), self.rotation, [0,1,0]), self.obj_mat).tolist()) glUniformMatrix4fv(self.mvploc, 1, GL_TRUE, hm.rotation(self.mvp, self.rotation / 4, [0.577350269189626] * 3).tolist()) glBindVertexArray(self.vertobj) glDrawElementsInstanced(GL_TRIANGLES, self.elementnum, GL_UNSIGNED_SHORT, ctypes.c_void_p(0), 125) glBindVertexArray(0) self.rotation += timediff / 5 * 360
class _THBatch: def __init__(self, parent): self.parent = parent self.restart() self.initialized = False def restart(self): self.__deferred_list_filled = [] self.__deferred_list_outline = [] def _initializeGL(self): self.initialized = True self.__filled_vao = VAO() self.__outline_vao = VAO() with self.__filled_vao, self.parent._sq_vbo: vbobind(self.parent._filled_shader, self.parent._sq_vbo.data.dtype, "vertex").assign() # Use a fake array to get a zero-length VBO for initial binding filled_instance_array = numpy.ndarray( 0, dtype=self.parent._filled_instance_dtype) self.filled_instance_vbo = VBO(filled_instance_array) with self.__filled_vao, self.filled_instance_vbo: vbobind(self.parent._filled_shader, self.parent._filled_instance_dtype, "pos", div=1).assign() vbobind(self.parent._filled_shader, self.parent._filled_instance_dtype, "r", div=1).assign() vbobind(self.parent._filled_shader, self.parent._filled_instance_dtype, "r_inside_frac_sq", div=1).assign() vbobind(self.parent._filled_shader, self.parent._filled_instance_dtype, "color", div=1).assign() with self.__outline_vao, self.parent._outline_vbo: vbobind(self.parent._outline_shader, self.parent._outline_vbo.data.dtype, "vertex").assign() # Build instance for outline rendering # We don't have an inner 'r' for this because we just do two instances per vertex # Use a fake array to get a zero-length VBO for initial binding outline_instance_array = numpy.ndarray( 0, dtype=self.parent._outline_instance_dtype) self.outline_instance_vbo = VBO(outline_instance_array) with self.__outline_vao, self.outline_instance_vbo: vbobind(self.parent._outline_shader, self.parent._outline_instance_dtype, "pos", div=1).assign() vbobind(self.parent._outline_shader, self.parent._outline_instance_dtype, "r", div=1).assign() vbobind(self.parent._outline_shader, self.parent._outline_instance_dtype, "color", div=1).assign() def deferred(self, center, r1, r2, rs, render_hint=RENDER_HINT_NORMAL): if rs & RENDER_OUTLINES: self.__deferred_list_outline.append((center, r1, r2, rs)) else: self.__deferred_list_filled.append((center, r1, r2, rs)) def prepare(self): self.__prepare_filled() self.__prepare_outline() def __prepare_filled(self): if not self.initialized: self._initializeGL() count = len(self.__deferred_list_filled) self.__filled_count = count if count == 0: return # Resize instance data array instance_array = numpy.ndarray( count, dtype=self.parent._filled_instance_dtype) color_a = [0.6, 0.6, 0.6, 1] for n, (center, r1, r2, rs) in enumerate(self.__deferred_list_filled): # HACK, color object color_mod = self.parent.parent.sel_colormod( rs & RENDER_SELECTED, color_a) # frag shader uses pythag to determine is frag is within # shaded area. Precalculate comparison term r_frac_sq = (r2 / r1)**2 instance_array[n] = (center, r1, r_frac_sq, color_mod) self.filled_instance_vbo.data = instance_array self.filled_instance_vbo.size = None self.filled_instance_vbo.copied = False self.filled_instance_vbo.bind() def __prepare_outline(self): count = 0 for center, r1, r2, rs in self.__deferred_list_outline: if r2 == 0: count += 1 else: count += 2 self.__outline_count = count if count == 0: return # Resize instance data array instance_array = numpy.ndarray( count, dtype=self.parent._outline_instance_dtype) n = 0 for center, r1, r2, rs in self.__deferred_list_outline: color_a = [0.6, 0.6, 0.6, 1] # HACK, color object color_a = self.parent.parent.sel_colormod(rs & RENDER_SELECTED, color_a) instance_array[n] = (center, r1, color_a) n += 1 if r2 > 0: instance_array[n] = (center, r2, color_a) n += 1 self.outline_instance_vbo.data = instance_array self.outline_instance_vbo.size = None self.outline_instance_vbo.copied = False self.outline_instance_vbo.bind() def render(self, mat): self.render_filled(mat) self.render_outline(mat) def render_filled(self, mat): if self.__filled_count == 0: return with self.parent._filled_shader, self.__filled_vao, self.filled_instance_vbo, self.parent._sq_vbo: GL.glUniformMatrix3fv(self.parent._filled_shader.uniforms.mat, 1, True, mat.ctypes.data_as(GLI.c_float_p)) GL.glDrawArraysInstanced(GL.GL_TRIANGLE_STRIP, 0, 4, len(self.__deferred_list_filled)) def render_outline(self, mat): if self.__outline_count == 0: return with self.parent._outline_shader, self.__outline_vao, self.outline_instance_vbo, self.parent._sq_vbo: GL.glUniformMatrix3fv(self.parent._outline_shader.uniforms.mat, 1, True, mat.ctypes.data_as(GLI.c_float_p)) GL.glDrawArraysInstanced(GL.GL_LINE_LOOP, 0, N_OUTLINE_SEGMENTS, self.__outline_count)
class GcodeModel(Model): """ Model for displaying Gcode data. """ # vertices for arrow to display the direction of movement arrow = numpy.require([ [0.0, 0.0, 0.0], [0.4, -0.1, 0.0], [0.4, 0.1, 0.0], ], 'f') def load_data(self, model_data, callback=None): t_start = time.time() vertex_list = [] color_list = [] self.layer_stops = [0] arrow_list = [] num_layers = len(model_data) for layer_idx, layer in enumerate(model_data): for movement in layer: vertex_list.append(movement.src) vertex_list.append(movement.dst) arrow = self.arrow # position the arrow with respect to movement arrow = vector.rotate(arrow, movement.angle(), 0.0, 0.0, 1.0) arrow_list.extend(arrow) vertex_color = self.movement_color(movement) color_list.append(vertex_color) self.layer_stops.append(len(vertex_list)) if callback: callback(layer_idx + 1, num_layers) self.vertices = numpy.array(vertex_list, 'f') self.colors = numpy.array(color_list, 'f') self.arrows = numpy.array(arrow_list, 'f') # by translating the arrow vertices outside of the loop, we achieve a # significant performance gain thanks to numpy. it would be really nice # if we could rotate in a similar fashion... self.arrows = self.arrows + self.vertices[1::2].repeat(3, 0) # for every pair of vertices of the model, there are 3 vertices for the arrow assert len(self.arrows) == ((len(self.vertices) // 2) * 3), \ 'The 2:3 ratio of model vertices to arrow vertices does not hold.' self.max_layers = len(self.layer_stops) - 1 self.num_layers_to_draw = self.max_layers self.arrows_enabled = True self.initialized = False t_end = time.time() logging.info('Initialized Gcode model in %.2f seconds' % (t_end - t_start)) logging.info('Vertex count: %d' % len(self.vertices)) def movement_color(self, move): """ Return the color to use for particular type of movement. """ # default movement color is gray color = [0.6, 0.6, 0.6, 0.6] extruder_on = (move.flags & Movement.FLAG_EXTRUDER_ON or move.delta_e > 0) outer_perimeter = (move.flags & Movement.FLAG_PERIMETER and move.flags & Movement.FLAG_PERIMETER_OUTER) if extruder_on and outer_perimeter: color = [0.0, 0.875, 0.875, 0.6] # cyan elif extruder_on and move.flags & Movement.FLAG_PERIMETER: color = [0.0, 1.0, 0.0, 0.6] # green elif extruder_on and move.flags & Movement.FLAG_LOOP: color = [1.0, 0.875, 0.0, 0.6] # yellow elif extruder_on: color = [1.0, 0.0, 0.0, 0.6] # red return color # ------------------------------------------------------------------------ # DRAWING # ------------------------------------------------------------------------ def init(self): self.vertex_buffer = VBO(self.vertices, 'GL_STATIC_DRAW') self.vertex_color_buffer = VBO(self.colors.repeat(2, 0), 'GL_STATIC_DRAW') # each pair of vertices shares the color if self.arrows_enabled: self.arrow_buffer = VBO(self.arrows, 'GL_STATIC_DRAW') self.arrow_color_buffer = VBO(self.colors.repeat(3, 0), 'GL_STATIC_DRAW') # each triplet of vertices shares the color self.initialized = True def display(self, mode_2d=False): glPushMatrix() glTranslate(self.offset_x, self.offset_y, 0) glEnableClientState(GL_VERTEX_ARRAY) glEnableClientState(GL_COLOR_ARRAY) self._display_movements(mode_2d) if self.arrows_enabled: self._display_arrows() glDisableClientState(GL_COLOR_ARRAY) glDisableClientState(GL_VERTEX_ARRAY) glPopMatrix() def _display_movements(self, mode_2d=False): self.vertex_buffer.bind() glVertexPointer(3, GL_FLOAT, 0, None) self.vertex_color_buffer.bind() glColorPointer(4, GL_FLOAT, 0, None) if mode_2d: glScale(1.0, 1.0, 0.0) # discard z coordinates start = self.layer_stops[self.num_layers_to_draw - 1] end = self.layer_stops[self.num_layers_to_draw] - start else: # 3d start = 0 end = self.layer_stops[self.num_layers_to_draw] glDrawArrays(GL_LINES, start, end) self.vertex_buffer.unbind() self.vertex_color_buffer.unbind() def _display_arrows(self): self.arrow_buffer.bind() glVertexPointer(3, GL_FLOAT, 0, None) self.arrow_color_buffer.bind() glColorPointer(4, GL_FLOAT, 0, None) start = (self.layer_stops[self.num_layers_to_draw - 1] // 2) * 3 end = (self.layer_stops[self.num_layers_to_draw] // 2) * 3 glDrawArrays(GL_TRIANGLES, start, end - start) self.arrow_buffer.unbind() self.arrow_color_buffer.unbind()
class _RenderData: def __init__(self, dtype, shader, glhint): self.__dtype = dtype self.vao = VAO() self.batch_vbo = VBO(numpy.array([], dtype=dtype), glhint) with self.vao, self.batch_vbo: vbobind(shader, dtype, "vertex").assign() self.clear() def clear(self): self.__vbo_update = True self.__groups = defaultdict(list) self.__group_lookup = {} self.group_offsets = {} def build_vbo(self): if not self.__vbo_update: return self.__vbo_update = False self.group_offsets = {} built_list = [] for group, points in self.__groups.items(): self.group_offsets[group] = len(built_list) built_list.extend([(i,) for i in points]) if not built_list: return ar = numpy.array(built_list, dtype=self.__dtype) self.batch_vbo.data = ar self.batch_vbo.size = None self.batch_vbo.copied = False self.batch_vbo.bind() def last_index(self, group): return len(self.__groups[group]) def add_point(self, p1, p2, group): group = self.__groups[group] idx = len(group) // 2 group.append(p1) group.append(p2) self.__vbo_update = True return idx def get_point_index(self, p1, p2, group): key = group, p1.intTuple(), p2.intTuple() try: return self.__group_lookup[key] except KeyError: pass idx = self.__group_lookup[key] = self.add_point(p1, p2, group) return idx
class GcodeModel(Model): """ Model for displaying Gcode data. """ # vertices for arrow to display the direction of movement arrow = numpy.require([ [0.0, 0.0, 0.0], [0.4, -0.1, 0.0], [0.4, 0.1, 0.0], ], 'f') layer_entry_marker = numpy.require([ [ 0.23, -0.14, 0.0], [ 0.0, 0.26, 0.0], [-0.23, -0.14, 0.0], ], 'f') layer_exit_marker = numpy.require([ [-0.23, -0.23, 0.0], [ 0.23, -0.23, 0.0], [ 0.23, 0.23, 0.0], [ 0.23, 0.23, 0.0], [-0.23, 0.23, 0.0], [-0.23, -0.23, 0.0], ], 'f') def load_data(self, model_data, callback=None): t_start = time.time() vertex_list = [] color_list = [] self.layer_stops = [0] self.layer_heights = [] arrow_list = [] layer_markers_list = [] self.layer_marker_stops = [0] num_layers = len(model_data) callback_every = max(1, int(math.floor(num_layers / 100))) # the first movement designates the starting point start = prev = model_data[0][0] del model_data[0][0] for layer_idx, layer in enumerate(model_data): first = layer[0] for movement in layer: vertex_list.append(prev.v) vertex_list.append(movement.v) arrow = self.arrow # position the arrow with respect to movement arrow = vector.rotate(arrow, movement.angle(prev.v), 0.0, 0.0, 1.0) arrow_list.extend(arrow) vertex_color = self.movement_color(movement) color_list.append(vertex_color) prev = movement self.layer_stops.append(len(vertex_list)) self.layer_heights.append(first.v[2]) # add the layer entry marker if layer_idx > 0 and len(model_data[layer_idx - 1]) > 0: layer_markers_list.extend(self.layer_entry_marker + model_data[layer_idx-1][-1].v) elif layer_idx == 0 and len(layer) > 0: layer_markers_list.extend(self.layer_entry_marker + layer[0].v) # add the layer exit marker if len(layer) > 1: layer_markers_list.extend(self.layer_exit_marker + layer[-1].v) self.layer_marker_stops.append(len(layer_markers_list)) if callback and layer_idx % callback_every == 0: callback(layer_idx + 1, num_layers) self.vertices = numpy.array(vertex_list, 'f') self.colors = numpy.array(color_list, 'f') self.arrows = numpy.array(arrow_list, 'f') self.layer_markers = numpy.array(layer_markers_list, 'f') # by translating the arrow vertices outside of the loop, we achieve a # significant performance gain thanks to numpy. it would be really nice # if we could rotate in a similar fashion... self.arrows = self.arrows + self.vertices[1::2].repeat(3, 0) # for every pair of vertices of the model, there are 3 vertices for the arrow assert len(self.arrows) == ((len(self.vertices) // 2) * 3), \ 'The 2:3 ratio of model vertices to arrow vertices does not hold.' self.max_layers = len(self.layer_stops) - 1 self.num_layers_to_draw = self.max_layers self.arrows_enabled = True self.initialized = False self.vertex_count = len(self.vertices) t_end = time.time() logging.info('Initialized Gcode model in %.2f seconds' % (t_end - t_start)) logging.info('Vertex count: %d' % self.vertex_count) def movement_color(self, move): """ Return the color to use for particular type of movement. """ # default movement color is gray color = (0.6, 0.6, 0.6, 0.6) extruder_on = (move.flags & Movement.FLAG_EXTRUDER_ON or move.delta_e > 0) outer_perimeter = (move.flags & Movement.FLAG_PERIMETER and move.flags & Movement.FLAG_PERIMETER_OUTER) if extruder_on and outer_perimeter: color = (0.0, 0.875, 0.875, 0.6) # cyan elif extruder_on and move.flags & Movement.FLAG_PERIMETER: color = (0.0, 1.0, 0.0, 0.6) # green elif extruder_on and move.flags & Movement.FLAG_LOOP: color = (1.0, 0.875, 0.0, 0.6) # yellow elif extruder_on: color = (1.0, 0.0, 0.0, 0.6) # red return color # ------------------------------------------------------------------------ # DRAWING # ------------------------------------------------------------------------ def init(self): self.vertex_buffer = VBO(self.vertices, 'GL_STATIC_DRAW') self.vertex_color_buffer = VBO(self.colors.repeat(2, 0), 'GL_STATIC_DRAW') # each pair of vertices shares the color if self.arrows_enabled: self.arrow_buffer = VBO(self.arrows, 'GL_STATIC_DRAW') self.arrow_color_buffer = VBO(self.colors.repeat(3, 0), 'GL_STATIC_DRAW') # each triplet of vertices shares the color self.layer_marker_buffer = VBO(self.layer_markers, 'GL_STATIC_DRAW') self.initialized = True def display(self, elevation=0, eye_height=0, mode_ortho=False, mode_2d=False): glPushMatrix() offset_z = self.offset_z if not mode_2d else 0 glTranslate(self.offset_x, self.offset_y, offset_z) glEnableClientState(GL_VERTEX_ARRAY) glEnableClientState(GL_COLOR_ARRAY) self._display_movements(elevation, eye_height, mode_ortho, mode_2d) if self.arrows_enabled: self._display_arrows() glDisableClientState(GL_COLOR_ARRAY) if self.arrows_enabled: self._display_layer_markers() glDisableClientState(GL_VERTEX_ARRAY) glPopMatrix() def _display_movements(self, elevation=0, eye_height=0, mode_ortho=False, mode_2d=False): self.vertex_buffer.bind() glVertexPointer(3, GL_FLOAT, 0, None) self.vertex_color_buffer.bind() glColorPointer(4, GL_FLOAT, 0, None) if mode_2d: glScale(1.0, 1.0, 0.0) # discard z coordinates start = self.layer_stops[self.num_layers_to_draw - 1] end = self.layer_stops[self.num_layers_to_draw] glDrawArrays(GL_LINES, start, end - start) elif mode_ortho: if elevation >= 0: # draw layers in normal order, bottom to top start = 0 end = self.layer_stops[self.num_layers_to_draw] glDrawArrays(GL_LINES, start, end - start) else: # draw layers in reverse order, top to bottom stop_idx = self.num_layers_to_draw - 1 while stop_idx >= 0: start = self.layer_stops[stop_idx] end = self.layer_stops[stop_idx + 1] glDrawArrays(GL_LINES, start, end - start) stop_idx -= 1 else: # 3d projection mode reverse_threshold_layer = self._layer_up_to_height(eye_height - self.offset_z) if reverse_threshold_layer >= 0: # draw layers up to (and including) the threshold in normal order, bottom to top normal_layers_to_draw = min(self.num_layers_to_draw, reverse_threshold_layer + 1) start = 0 end = self.layer_stops[normal_layers_to_draw] glDrawArrays(GL_LINES, start, end - start) if reverse_threshold_layer + 1 < self.num_layers_to_draw: # draw layers from the threshold in reverse order, top to bottom stop_idx = self.num_layers_to_draw - 1 while stop_idx > reverse_threshold_layer: start = self.layer_stops[stop_idx] end = self.layer_stops[stop_idx + 1] glDrawArrays(GL_LINES, start, end - start) stop_idx -= 1 self.vertex_buffer.unbind() self.vertex_color_buffer.unbind() def _layer_up_to_height(self, height): """Return the index of the last layer lower than height.""" for idx in range(len(self.layer_heights) - 1, -1, -1): if self.layer_heights[idx] < height: return idx return 0 def _display_arrows(self): self.arrow_buffer.bind() glVertexPointer(3, GL_FLOAT, 0, None) self.arrow_color_buffer.bind() glColorPointer(4, GL_FLOAT, 0, None) start = (self.layer_stops[self.num_layers_to_draw - 1] // 2) * 3 end = (self.layer_stops[self.num_layers_to_draw] // 2) * 3 glDrawArrays(GL_TRIANGLES, start, end - start) self.arrow_buffer.unbind() self.arrow_color_buffer.unbind() def _display_layer_markers(self): self.layer_marker_buffer.bind() glVertexPointer(3, GL_FLOAT, 0, None) start = self.layer_marker_stops[self.num_layers_to_draw - 1] end = self.layer_marker_stops[self.num_layers_to_draw] glColor4f(0.6, 0.6, 0.6, 0.6) glDrawArrays(GL_TRIANGLES, start, end - start) self.layer_marker_buffer.unbind()
glfw.set_scroll_callback(theWindow, window_scroll_callback) # create VBOs to store vertices, normals and elements cubeDataVBO = VBO(cubeData, usage='GL_STATIC_DRAW') cubeDataVBO.create_buffers() sphereDataVBO = VBO(sphereData, usage='GL_STATIC_DRAW') sphereDataVBO.create_buffers() # create VAO to describe array information triangleVAO, sphereVAO = glGenVertexArrays(2) # bind VAO glBindVertexArray(triangleVAO) # bind data VBO cubeDataVBO.bind() cubeDataVBO.copy_data() glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(0)) glEnableVertexAttribArray(0) glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(3 * ctypes.sizeof(ctypes.c_float))) glEnableVertexAttribArray(1) # unbind VBO cubeDataVBO.unbind() # unbind VAO glBindVertexArray(0) glBindVertexArray(sphereVAO)
glfw.set_key_callback(theWindow, window_keypress_callback) # disable cursor glfw.set_input_mode(theWindow, glfw.CURSOR, glfw.CURSOR_DISABLED) glfw.set_cursor_pos_callback(theWindow, window_cursor_callback) # initialize cursor position cursorPos = glfw.get_cursor_pos(theWindow) glfw.set_scroll_callback(theWindow, window_scroll_callback) vbo = VBO(vertices, 'GL_STATIC_DRAW') vbo.create_buffers() vao = glGenVertexArrays(1) glBindVertexArray(vao) vbo.bind() vbo.copy_data() glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(0)) glEnableVertexAttribArray(0) glBindVertexArray(0) # compile program renderProgram = GLProgram(rayTracingVertexShaderSource, rayTracingFragmentShaderSource) renderProgram.compile_and_link() uniformInfos = [ ('backColor', 'vec3f'), ('ambientColor', 'vec3f'), ('o_c', 'vec3f'), ('o_p', 'vec3f'), ('x_c', 'vec3f'), ('y_c', 'vec3f'),
class Mesh(Component): meshes = dict() # cache to reuse meshes @classmethod def fromXMLElement(cls, xmlElement, actor=None): return cls.getMesh(xmlElement.get('src', 'Empty'), actor) # NOTE If src attribute is not found in XML element, special 'Empty' mesh is used @classmethod def getMesh(cls, src, actor=None): if not src in cls.meshes: if src == 'Empty': cls.meshes[src] = EmptyMesh() # special empty mesh to be used as a placeholder, and for empty actors else: cls.meshes[src] = Mesh(src) # NOTE Meshes are currently shared, therefore not linked to individual actors return cls.meshes[src] def __init__(self, src, actor=None): Component.__init__(self, actor) # TODO Include a mesh name (e.g. 'Dragon') as ID as well as src (e.g. '../res/models/Dragon.obj') self.src = src self.filepath = Context.getInstance().getResourcePath('models', src) # OpenGL version-dependent code (NOTE assumes major version = 3) self.vao = None if Context.getInstance().GL_version_minor > 0: # 3.1 (or greater?) self.vao = glGenVertexArrays(1) else: # 3.0 (or less?) self.vao = GLuint(0) glGenVertexArrays(1, self.vao) glBindVertexArray(self.vao) self.loadModel(self.filepath) self.vbo = VBO(self.meshData, GL_STATIC_DRAW) self.vbo.bind() glEnableVertexAttribArray(0) glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6*4, self.vbo+0) glEnableVertexAttribArray(1) glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6*4, self.vbo+12) self.ebo = glGenBuffers(1) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.ebo) glBufferData(GL_ELEMENT_ARRAY_BUFFER, len(self.elements)*4, self.elements, GL_STATIC_DRAW) def loadModel(self, filename): f = open(filename, 'r') self.meshData = [] self.elements = [] vertices = [] normals = [] texCoords = [] for line in f: s = line.split() if len(s) > 0: if s[0] == 'v': v = np.array([s[1], s[2], s[3]], dtype = np.float32) vertices.append(v) elif s[0] == 'vn': v = np.array([s[1], s[2], s[3]], dtype = np.float32) normals.append(v) elif s[0] == 'vt': v = np.array([s[1], s[2]], dtype = np.float32) texCoords.append(v) elif s[0] == 'f': for i in xrange(1, 4): l = s[i].split('/') self.meshData.append(float(vertices[int(l[0]) - 1][0])) self.meshData.append(float(vertices[int(l[0]) - 1][1])) self.meshData.append(float(vertices[int(l[0]) - 1][2])) self.meshData.append(float(normals[int(l[2]) - 1][0])) self.meshData.append(float(normals[int(l[2]) - 1][1])) self.meshData.append(float(normals[int(l[2]) - 1][2])) self.elements.append(len(self.elements)) self.meshData = np.array(self.meshData, dtype = np.float32) self.elements = np.array(self.elements, dtype = np.uint32) def render(self): glBindVertexArray(self.vao) glDrawElements(GL_TRIANGLES, self.elements.size, GL_UNSIGNED_INT, None) def toXMLElement(self): xmlElement = Component.toXMLElement(self) xmlElement.set('src', self.src) return xmlElement def toString(self, indent=""): return indent + "Mesh: { src: \"" + self.src + "\" }" def __str__(self): return self.toString()
class GcodeModel(Model): """ Model for displaying Gcode data. """ # define color names for different types of extruder movements color_map = { 'red': [1.0, 0.0, 0.0, 0.6], 'yellow': [1.0, 0.875, 0.0, 0.6], 'orange': [1.0, 0.373, 0.0, 0.6], 'green': [0.0, 1.0, 0.0, 0.6], 'cyan': [0.0, 0.875, 0.875, 0.6], 'gray': [0.6, 0.6, 0.6, 0.6], } # vertices for arrow to display the direction of movement arrow = numpy.require([ [0.0, 0.0, 0.0], [0.4, -0.1, 0.0], [0.4, 0.1, 0.0], ], 'f') def __init__(self, model_data): Model.__init__(self) t_start = time.time() self.create_vertex_arrays(model_data) self.max_layers = len(self.layer_stops) - 1 self.num_layers_to_draw = self.max_layers self.arrows_enabled = True self.initialized = False t_end = time.time() logging.info('Initialized Gcode model in %.2f seconds' % (t_end - t_start)) logging.info('Vertex count: %d' % len(self.vertices)) def create_vertex_arrays(self, model_data): """ Construct vertex lists from gcode data. """ vertex_list = [] color_list = [] self.layer_stops = [0] arrow_list = [] for layer in model_data: for movement in layer: a, b = movement.point_a, movement.point_b vertex_list.append([a.x, a.y, a.z]) vertex_list.append([b.x, b.y, b.z]) arrow = self.arrow # position the arrow with respect to movement arrow = vector.rotate(arrow, movement.angle(), 0.0, 0.0, 1.0) arrow_list.extend(arrow) vertex_color = self.movement_color(movement) color_list.append(vertex_color) self.layer_stops.append(len(vertex_list)) self.vertices = numpy.array(vertex_list, 'f') self.colors = numpy.array(color_list, 'f') self.arrows = numpy.array(arrow_list, 'f') # by translating the arrow vertices outside of the loop, we achieve a # significant performance gain thanks to numpy. it would be really nice # if we could rotate in a similar fashion... self.arrows = self.arrows + self.vertices[1::2].repeat(3, 0) # for every pair of vertices of the model, there are 3 vertices for the arrow assert len(self.arrows) == ((len(self.vertices) // 2) * 3), \ 'The 2:3 ratio of model vertices to arrow vertices does not hold.' def movement_color(self, movement): """ Return the color to use for particular type of movement. """ if not movement.extruder_on: color = self.color_map['gray'] elif movement.is_loop: color = self.color_map['yellow'] elif movement.is_perimeter and movement.is_perimeter_outer: color = self.color_map['cyan'] elif movement.is_perimeter: color = self.color_map['green'] else: color = self.color_map['red'] return color # ------------------------------------------------------------------------ # DRAWING # ------------------------------------------------------------------------ def init(self): self.vertex_buffer = VBO(self.vertices, 'GL_STATIC_DRAW') self.vertex_color_buffer = VBO(self.colors.repeat(2, 0), 'GL_STATIC_DRAW') # each pair of vertices shares the color if self.arrows_enabled: self.arrow_buffer = VBO(self.arrows, 'GL_STATIC_DRAW') self.arrow_color_buffer = VBO(self.colors.repeat(3, 0), 'GL_STATIC_DRAW') # each triplet of vertices shares the color self.initialized = True def display(self, mode_2d=False): glEnableClientState(GL_VERTEX_ARRAY) glEnableClientState(GL_COLOR_ARRAY) self._display_movements(mode_2d) if self.arrows_enabled: self._display_arrows() glDisableClientState(GL_COLOR_ARRAY) glDisableClientState(GL_VERTEX_ARRAY) def _display_movements(self, mode_2d=False): self.vertex_buffer.bind() glVertexPointer(3, GL_FLOAT, 0, None) self.vertex_color_buffer.bind() glColorPointer(4, GL_FLOAT, 0, None) if mode_2d: glScale(1.0, 1.0, 0.0) # discard z coordinates start = self.layer_stops[self.num_layers_to_draw - 1] end = self.layer_stops[self.num_layers_to_draw] - start else: # 3d start = 0 end = self.layer_stops[self.num_layers_to_draw] glDrawArrays(GL_LINES, start, end) self.vertex_buffer.unbind() self.vertex_color_buffer.unbind() def _display_arrows(self): self.arrow_buffer.bind() glVertexPointer(3, GL_FLOAT, 0, None) self.arrow_color_buffer.bind() glColorPointer(4, GL_FLOAT, 0, None) start = (self.layer_stops[self.num_layers_to_draw - 1] // 2) * 3 end = (self.layer_stops[self.num_layers_to_draw] // 2) * 3 glDrawArrays(GL_TRIANGLES, start, end - start) self.arrow_buffer.unbind() self.arrow_color_buffer.unbind()
class model: ''' class for STL file / 3d model ''' def __init__(self, stl_data, batchh=None): ''' initialise model data''' vert, norm = stl_data self.vertices = numpy.array(vert, dtype=GLfloat) self.normals = numpy.array(norm, dtype=GLfloat) self.vertex_buffer = VBO(self.vertices, 'GL_STATIC_DRAW') self.normal_buffer = VBO(self.normals, 'GL_STATIC_DRAW') # calculate model scale self.corner = self.vertices.transpose().min(1) self.corner2 = self.vertices.transpose().max(1) self.scale = abs(self.corner) + abs(self.corner2) self.scale = max(self.scale[0], self.scale[1], self.scale[2]) print 'STL File loaded in: ', loadtime print 'Object information' print 'corner 1: ', self.corner print 'corner 2: ', self.corner2 print 'object scale: ', self.scale def draw(self): ''' draw model on screen ''' glMatrixMode(GL_MODELVIEW) glPushMatrix() glLoadIdentity() # center the model glTranslate(window.width/2, window.height/2, -150) # scale the model glScale(150/self.scale, 150/self.scale, 150/self.scale) # draw grid and coordinate lines draw_grid() draw_xyz(0, 0, 0, -20, -20) # demo rotation glRotate(rot, 1, 0, 0) glRotate(rot2, 0, 1, 0) glRotate(rot3, 0, 0, 1) self.vertex_buffer.bind() glVertexPointer(3, GL_FLOAT, 0, None) self.normal_buffer.bind() glNormalPointer(GL_FLOAT, 0, None) glEnableClientState(GL_VERTEX_ARRAY) glEnableClientState(GL_NORMAL_ARRAY) glDrawArrays(GL_TRIANGLES, 0, len(self.vertices)) glDisableClientState(GL_NORMAL_ARRAY) glDisableClientState(GL_VERTEX_ARRAY) self.normal_buffer.unbind() self.vertex_buffer.unbind() glPopMatrix()
class THRenderer: def __init__(self, parent_view): self.parent = parent_view self.restart() def restart(self): self.__deferred_list_filled = [] self.__deferred_list_outline = [] def initializeGL(self, glshared): self.__filled_shader = glshared.shader_cache.get( "via_filled_vertex_shader", "via_filled_fragment_shader") self.__outline_shader = glshared.shader_cache.get( "via_outline_vertex_shader", "frag1" ) self.__filled_vao = VAO() self.__outline_vao = VAO() # Build geometry for filled rendering using the frag shader for circle borders filled_points = [ ((-1, -1), ), ((1, -1), ), ((-1, 1), ), ((1, 1), ), ] ar = numpy.array( filled_points , dtype=[("vertex", numpy.float32, 2)]) self.__sq_vbo = VBO(ar, GL.GL_STATIC_DRAW) with self.__filled_vao, self.__sq_vbo: vbobind(self.__filled_shader, ar.dtype, "vertex").assign() # Build and bind an instance array for the "filled" geometry self.filled_instance_dtype = numpy.dtype([ ("pos", numpy.float32, 2), ("r", numpy.float32, 1), ("r_inside_frac_sq", numpy.float32, 1), ("color", numpy.float32, 4) ]) # Use a fake array to get a zero-length VBO for initial binding filled_instance_array = numpy.ndarray(0, dtype=self.filled_instance_dtype) self.filled_instance_vbo = VBO(filled_instance_array) with self.__filled_vao, self.filled_instance_vbo: vbobind(self.__filled_shader, self.filled_instance_dtype, "pos", div=1).assign() vbobind(self.__filled_shader, self.filled_instance_dtype, "r", div=1).assign() vbobind(self.__filled_shader, self.filled_instance_dtype, "r_inside_frac_sq", div=1).assign() vbobind(self.__filled_shader, self.filled_instance_dtype, "color", div=1).assign() # Build geometry for outline rendering outline_points = [] for i in numpy.linspace(0, math.pi * 2, N_OUTLINE_SEGMENTS, False): outline_points.append(((math.cos(i), math.sin(i)), )) ar = numpy.array( outline_points , dtype=[("vertex", numpy.float32, 2)]) self.__outline_vbo = VBO(ar, GL.GL_STATIC_DRAW) with self.__outline_vao, self.__outline_vbo: vbobind(self.__outline_shader, ar.dtype, "vertex").assign() # Build instance for outline rendering # We don't have an inner 'r' for this because we just do two instances per vertex self.outline_instance_dtype = numpy.dtype([ ("pos", numpy.float32, 2), ("r", numpy.float32, 1), ("color", numpy.float32, 4) ]) # Use a fake array to get a zero-length VBO for initial binding outline_instance_array = numpy.ndarray(0, dtype=self.outline_instance_dtype) self.outline_instance_vbo = VBO(outline_instance_array) with self.__outline_vao, self.outline_instance_vbo: vbobind(self.__outline_shader, self.outline_instance_dtype, "pos", div=1).assign() vbobind(self.__outline_shader, self.outline_instance_dtype, "r", div=1).assign() vbobind(self.__outline_shader, self.outline_instance_dtype, "color", div=1).assign() def deferred(self, center, r1, r2, rs, render_hint=RENDER_HINT_NORMAL): if rs & RENDER_OUTLINES: self.__deferred_list_outline.append((center, r1, r2, rs)) else: self.__deferred_list_filled.append((center, r1, r2, rs)) def render(self, mat): self.render_filled(mat) self.render_outline(mat) def render_filled(self, mat): count = len(self.__deferred_list_filled) if count == 0: return # Resize instance data array instance_array = numpy.ndarray(count, dtype = self.filled_instance_dtype) for n, (center, r1, r2, rs) in enumerate(self.__deferred_list_filled): color_a = [0.6, 0.6, 0.6, 1] # HACK, color object color_a = self.parent.sel_colormod(rs & RENDER_SELECTED, color_a) # frag shader uses pythag to determine is frag is within # shaded area. Precalculate comparison term r_frac_sq = (r2 / r1) ** 2 instance_array[n] = (center, r1, r_frac_sq, color_a) self.filled_instance_vbo.data = instance_array self.filled_instance_vbo.size = None self.filled_instance_vbo.copied = False self.filled_instance_vbo.bind() with self.__filled_shader, self.__filled_vao, self.filled_instance_vbo, self.__sq_vbo: GL.glUniformMatrix3fv(self.__filled_shader.uniforms.mat, 1, True, mat.ctypes.data_as(GLI.c_float_p)) GL.glDrawArraysInstanced(GL.GL_TRIANGLE_STRIP, 0, 4, count) def render_outline(self, mat): count = 0 for center, r1, r2, rs in self.__deferred_list_outline: if r2 == 0: count += 1 else: count += 2 if count == 0: return # Resize instance data array instance_array = numpy.ndarray(count, dtype = self.outline_instance_dtype) n = 0 for center, r1, r2, rs in self.__deferred_list_outline: color_a = [0.6, 0.6, 0.6, 1] # HACK, color object color_a = self.parent.sel_colormod(rs & RENDER_SELECTED, color_a) instance_array[n] = (center, r1, color_a) n += 1 if r2 > 0: instance_array[n] = (center, r2, color_a) n += 1 self.outline_instance_vbo.data = instance_array self.outline_instance_vbo.size = None self.outline_instance_vbo.copied = False self.outline_instance_vbo.bind() with self.__outline_shader, self.__outline_vao, self.outline_instance_vbo, self.__sq_vbo: GL.glUniformMatrix3fv(self.__outline_shader.uniforms.mat, 1, True, mat.ctypes.data_as(GLI.c_float_p)) GL.glDrawArraysInstanced(GL.GL_LINE_LOOP, 0, N_OUTLINE_SEGMENTS, count)
class PolygonVBOPair: __RESTART_INDEX = 2 ** 32 - 1 def __init__(self, view): """ :type gls: pcbre.ui.gl.glshared.GLShared :param gls: :return: """ self.__view = view self.__gls = view.gls self.__position_list = [] self.__position_lookup = {} # Known_polys is a list of (start,end) indicies in self.__index_list self.__tri_draw_ranges = {} self.__outline_draw_ranges = {} self.__tri_index_list = [] self.__outline_index_list = [] self.__vert_vbo_current = False self.__index_vbo_current = False self.restart() def restart(self): self.__deferred_tri_render_ranges = defaultdict(list) self.__deferred_line_render_ranges = defaultdict(list) def initializeGL(self): self.__vao = VAO() # Lookup for vertex positions self.__vert_vbo_dtype = numpy.dtype([("vertex", numpy.float32, 2)]) self.__vert_vbo = VBO(numpy.ndarray(0, dtype=self.__vert_vbo_dtype), GL.GL_DYNAMIC_DRAW) self.__vert_vbo_current = False self.__index_vbo_dtype = numpy.uint32 self.__index_vbo = VBO(numpy.ndarray(0, dtype=self.__index_vbo_dtype), GL.GL_DYNAMIC_DRAW, GL.GL_ELEMENT_ARRAY_BUFFER) self.__index_vbo_current = False self.__shader = self.__gls.shader_cache.get("vert2", "frag1") with self.__vao, self.__vert_vbo: vbobind(self.__shader, self.__vert_vbo_dtype, "vertex").assign() self.__index_vbo.bind() def __update_vert_vbo(self): if self.__vert_vbo_current or not len(self.__position_list): return ar = numpy.ndarray(len(self.__position_list), dtype=self.__vert_vbo_dtype) ar["vertex"] = self.__position_list self.__vert_vbo.data = ar self.__vert_vbo.size = None self.__vert_vbo.copied = False self.__vert_vbo.bind() self.__vert_vbo_current = True def __update_index_vbo(self): if self.__index_vbo_current or not len(self.__tri_index_list): return self.__outline_index_offset = len(self.__tri_index_list) self.__index_vbo.data = numpy.array(self.__tri_index_list + self.__outline_index_list, dtype=self.__index_vbo_dtype) self.__index_vbo.size = None self.__index_vbo.copied = False self.__index_vbo.bind() self.__index_vbo_current = True def __get_position_index(self, point): """ Get the index in the point VBO for a given Point2 coordinate :type point: pcbre.matrix.Point2 :param point: :return: """ norm_pos = point.intTuple() try: return self.__position_lookup[norm_pos] except KeyError: self.__position_lookup[norm_pos] = len(self.__position_list) self.__position_list.append(norm_pos) self.__vert_vbo_current = False return self.__position_lookup[norm_pos] def __add(self, polygon): tris = polygon.get_tris_repr() tri_index_first = len(self.__tri_index_list) for t in tris: for p in t.a, t.b, t.c: self.__tri_index_list.append(self.__get_position_index(Point2(p.x, p.y))) tr = (tri_index_first, len(self.__tri_index_list)) self.__tri_draw_ranges[polygon] = tr outline_index_first = len(self.__outline_index_list) poly_repr = polygon.get_poly_repr() for edge in [poly_repr.exterior] + list(poly_repr.interiors): for pt in edge.coords: self.__outline_index_list.append(self.__get_position_index(Point2(pt))) self.__outline_index_list.append(self.__RESTART_INDEX) lr = (outline_index_first, len(self.__outline_index_list)) self.__outline_draw_ranges[polygon] = lr self.__index_vbo_current = False return tr, lr def deferred(self, polygon, render_settings=RENDER_STANDARD): if polygon in self.__tri_draw_ranges: trange, lrange = self.__tri_draw_ranges[polygon], self.__outline_draw_ranges[polygon] else: trange, lrange = self.__add(polygon) if render_settings & RENDER_OUTLINES: self.__deferred_line_render_ranges[render_settings].append(lrange) else: self.__deferred_tri_render_ranges[render_settings].append(trange) def render(self, matrix, layer): self.__update_vert_vbo() self.__update_index_vbo() overall_color = self.__view.color_for_layer(layer) sel_color = self.__view.sel_colormod(True, overall_color) def _c_for_rs(rs): if rs & RENDER_SELECTED: return tuple(sel_color) + (1,) else: return tuple(overall_color) + (1,) with self.__shader, self.__vao: GL.glUniformMatrix3fv(self.__shader.uniforms.mat, 1, True, matrix.ctypes.data_as(GLI.c_float_p)) for rs, ranges in self.__deferred_tri_render_ranges.items(): tri_draw_list = get_consolidated_draws(ranges) GL.glUniform4f(self.__shader.uniforms.color, *_c_for_rs(rs)) for first, last in tri_draw_list: GL.glDrawElements(GL.GL_TRIANGLES, last - first, GL.GL_UNSIGNED_INT, ctypes.c_void_p(first * 4)) GL.glEnable(GL.GL_PRIMITIVE_RESTART) GL.glPrimitiveRestartIndex(self.__RESTART_INDEX) for rs, ranges in self.__deferred_line_render_ranges.items(): GL.glUniform4f(self.__shader.uniforms.color, *_c_for_rs(rs)) line_draw_list = get_consolidated_draws(ranges) for first, last in line_draw_list: GL.glDrawElements(GL.GL_LINE_STRIP, last - first, GL.GL_UNSIGNED_INT, ctypes.c_void_p((first + self.__outline_index_offset) * 4)) GL.glDisable(GL.GL_PRIMITIVE_RESTART)
class TextBatcher(object): __tag_type = namedtuple("render_tag", ["textinfo", "matrix", "color"]) def __init__(self, tr): self.text_render = tr self.__cached = {} self.__clist = [] self.restart() def restart(self): self.__render_tags = defaultdict(list) def initializeGL(self): # Working VBO that will contain glyph data self.vbo = VBO(numpy.ndarray(0, dtype=self.text_render.buffer_dtype), GL.GL_DYNAMIC_DRAW, GL.GL_ARRAY_BUFFER) self.vao = VAO() with self.vao, self.vbo: self.text_render.b1.assign() self.text_render.b2.assign() self.__vbo_needs_update = True def render(self, key=None): if self.__vbo_needs_update: arr = numpy.array(self.__clist, dtype=self.text_render.buffer_dtype) self.vbo.data = arr self.vbo.size = None self.vbo.copied = False self.vbo.bind() self.__vbo_needs_update = False self.text_render.updateTexture() with self.text_render.sdf_shader, self.text_render.tex.on( GL.GL_TEXTURE_2D), self.vao: GL.glUniform1i(self.text_render.sdf_shader.uniforms.tex1, 0) for tag in self.__render_tags[key]: mat_calc = tag.matrix GL.glUniformMatrix3fv(self.text_render.sdf_shader.uniforms.mat, 1, True, mat_calc.astype(numpy.float32)) GL.glUniform4f(self.text_render.sdf_shader.uniforms.color, *tag.color) GL.glDrawArrays(GL.GL_TRIANGLES, tag.textinfo.start, tag.textinfo.count) def submit(self, ts, mat, color, k=None): self.__render_tags[k].append(self.__tag_type(ts, mat, color)) def submit_text_box(self, premat, text, box, color, k=None): ts = self.get_string(text) mat = premat.dot(ts.get_render_to_mat(box)) self.submit(ts, mat, color, k) def get_string(self, text): if text in self.__cached: return self.__cached[text] self.__cached[text] = ti = self.text_render.getStringMetrics(text) ti.start = len(self.__clist) self.__clist.extend(ti.arr) ti.count = len(ti.arr) self.__vbo_needs_update = True return ti
class TraceRender: def __init__(self, parent_view): self.parent = parent_view self.restart() def initializeGL(self, gls): self.__uniform_shader_vao = VAO() self.__attribute_shader_vao = VAO() # Load two versions of the shader, one for rendering a single line through uniforms # (no additional bound instance info), and one for rendering instanced geometry self.__uniform_shader = gls.shader_cache.get( "line_vertex_shader", "frag1", defines={"INPUT_TYPE": "uniform"}) self.__attribute_shader = gls.shader_cache.get( "line_vertex_shader", "frag1", defines={"INPUT_TYPE": "in"}) # Generate geometry for trace and endcaps # ptid is a variable with value 0 or 1 that indicates which endpoint the geometry is associated with # Build trace vertex VBO and associated vertex data dtype = [("vertex", numpy.float32, 2), ("ptid", numpy.uint32)] self.working_array = numpy.zeros(NUM_ENDCAP_SEGMENTS * 2 + 2, dtype=dtype) self.trace_vbo = VBO(self.working_array, GL.GL_DYNAMIC_DRAW) self.__build_trace() # Now we build an index buffer that allows us to render filled geometry from the same # VBO. arr = [] for i in range(NUM_ENDCAP_SEGMENTS - 1): arr.append(0) arr.append(i + 2) arr.append(i + 3) for i in range(NUM_ENDCAP_SEGMENTS - 1): arr.append(1) arr.append(i + NUM_ENDCAP_SEGMENTS + 2) arr.append(i + NUM_ENDCAP_SEGMENTS + 3) arr.append(2) arr.append(2 + NUM_ENDCAP_SEGMENTS - 1) arr.append(2 + NUM_ENDCAP_SEGMENTS) arr.append(2 + NUM_ENDCAP_SEGMENTS) arr.append(2 + NUM_ENDCAP_SEGMENTS * 2 - 1) arr.append(2) arr = numpy.array(arr, dtype=numpy.uint32) self.index_vbo = VBO(arr, target=GL.GL_ELEMENT_ARRAY_BUFFER) # And bind the entire state together with self.__uniform_shader_vao, self.trace_vbo: vbobind(self.__uniform_shader, self.trace_vbo.dtype, "vertex").assign() vbobind(self.__uniform_shader, self.trace_vbo.dtype, "ptid").assign() self.index_vbo.bind() self.instance_dtype = numpy.dtype([ ("pos_a", numpy.float32, 2), ("pos_b", numpy.float32, 2), ("thickness", numpy.float32, 1), #("color", numpy.float32, 4) ]) # Use a fake array to get a zero-length VBO for initial binding instance_array = numpy.ndarray(0, dtype=self.instance_dtype) self.instance_vbo = VBO(instance_array) with self.__attribute_shader_vao, self.trace_vbo: vbobind(self.__attribute_shader, self.trace_vbo.dtype, "vertex").assign() vbobind(self.__attribute_shader, self.trace_vbo.dtype, "ptid").assign() with self.__attribute_shader_vao, self.instance_vbo: self.__bind_pos_a = vbobind(self.__attribute_shader, self.instance_dtype, "pos_a", div=1) self.__bind_pos_b = vbobind(self.__attribute_shader, self.instance_dtype, "pos_b", div=1) self.__bind_thickness = vbobind(self.__attribute_shader, self.instance_dtype, "thickness", div=1) #vbobind(self.__attribute_shader, self.instance_dtype, "color", div=1).assign() self.__base_rebind(0) self.index_vbo.bind() self.__last_prepared = weakref.WeakKeyDictionary() def __base_rebind(self, base): self.__bind_pos_a.assign(base) self.__bind_pos_b.assign(base) self.__bind_thickness.assign(base) def restart(self): self.__deferred_layer = defaultdict(list) self.__prepared = False self.__needs_rebuild = False def __build_trace(self): # Update trace VBO self.working_array["vertex"][0] = (0, 0) self.working_array["ptid"][0] = 0 self.working_array["vertex"][1] = (0, 0) self.working_array["ptid"][1] = 1 end = Vec2(1, 0) for i in range(0, NUM_ENDCAP_SEGMENTS): theta = math.pi * i / (NUM_ENDCAP_SEGMENTS - 1) + math.pi / 2 m = rotate(theta).dot(end.homol()) self.working_array["vertex"][2 + i] = m[:2] self.working_array["ptid"][2 + i] = 0 self.working_array["vertex"][2 + i + NUM_ENDCAP_SEGMENTS] = -m[:2] self.working_array["ptid"][2 + i + NUM_ENDCAP_SEGMENTS] = 1 # Force data copy self.trace_vbo.copied = False self.trace_vbo.bind() def deferred_multiple(self, trace_settings, render_settings=0): """ :param trace_settings: list of traces to draw and the settings for that particular trace :param render_settings: :return: """ for t, tr in trace_settings: tr |= render_settings self.deferred(t, tr) def deferred(self, trace, render_settings, render_hint): assert not self.__prepared self.__deferred_layer[trace.layer].append((trace, render_settings)) if trace not in self.__last_prepared: self.__needs_rebuild = True def prepare(self): """ :return: Build VBOs and information for rendering pass """ self.__prepared = True if not self.__needs_rebuild: return self.__last_prepared = weakref.WeakKeyDictionary() # Total trace count, across all layers count = sum(len(i) for i in self.__deferred_layer.values()) # Allocate an array of that size instance_array = numpy.ndarray(count, dtype=self.instance_dtype) pos = 0 for layer, traces in self.__deferred_layer.items(): # We reorder the traces to batch them by outline, and net to encourage # maximum draw call length. The rationale is that nets are commonly selected # or may be commonly drawn in different colors. traces = sorted(traces, key=lambda i: (i[1] & RENDER_OUTLINES, id(i[0].net))) for trace, _ in traces: # Now insert into the array instance_array[pos] = (trace.p0, trace.p1, trace.thickness / 2) # And memoize where the trace occurs self.__last_prepared[trace] = pos pos += 1 # Force full resend of VBO self.instance_vbo.data = instance_array self.instance_vbo.size = None self.instance_vbo.copied = False self.instance_vbo.bind() def render_deferred_layer(self, mat, layer): if not self.__prepared: self.prepare() trace_settings = self.__deferred_layer[layer] count = len(trace_settings) if count == 0: return # key format: is_selected, is_outline draw_bins = defaultdict(list) draw_range_bins = dict() # Bin the traces by draw call for t, tr in trace_settings: is_selected = bool(tr & RENDER_SELECTED) is_outline = bool(tr & RENDER_OUTLINES) draw_bins[is_selected, is_outline].append(self.__last_prepared[t]) # Build draw ranges for key, bin in draw_bins.items(): draw_range_bins[key] = get_consolidated_draws_1(draw_bins[key]) # HACK / Fixme: Precalculate selected / nonselected colors color_a = self.parent.color_for_layer(layer) + [1] color_sel = self.parent.sel_colormod(True, color_a) has_base_instance = False with self.__attribute_shader, self.__attribute_shader_vao: # Setup overall calls GL.glUniformMatrix3fv(self.__attribute_shader.uniforms.mat, 1, True, mat.ctypes.data_as(GLI.c_float_p)) for (is_selected, is_outline), ranges in draw_range_bins.items(): if is_selected: color = color_sel else: color = color_a GL.glUniform4f(self.__attribute_shader.uniforms.color, *color) if has_base_instance: # Many instances backport glDrawElementsInstancedBaseInstance # This is faster than continually rebinding, so support if possible if not is_outline: for first, last in ranges: # filled traces come first in the array GL.glDrawElementsInstancedBaseInstance( GL.GL_TRIANGLES, TRIANGLES_SIZE, GL.GL_UNSIGNED_INT, ctypes.c_void_p(0), last - first, first) else: for first, last in ranges: # Then outline traces. We reuse the vertex data for the outside GL.glDrawArraysInstancedBaseInstance( GL.GL_LINE_LOOP, 2, NUM_ENDCAP_SEGMENTS * 2, last - first, first) else: with self.instance_vbo: if not is_outline: for first, last in ranges: # filled traces come first in the array self.__base_rebind(first) GL.glDrawElementsInstanced( GL.GL_TRIANGLES, TRIANGLES_SIZE, GL.GL_UNSIGNED_INT, ctypes.c_void_p(0), last - first) else: for first, last in ranges: self.__base_rebind(first) # Then outline traces. We reuse the vertex data for the outside GL.glDrawArraysInstanced( GL.GL_LINE_LOOP, 2, NUM_ENDCAP_SEGMENTS * 2, last - first) # Immediate-mode render of a single trace # SLOW (at least for bulk-rendering) # Useful for rendering UI elements def render(self, mat, trace, render_settings=RENDER_STANDARD): color_a = self.parent.color_for_trace(trace) + [1] color_a = self.parent.sel_colormod(render_settings & RENDER_SELECTED, color_a) with self.__uniform_shader, self.__uniform_shader_vao: GL.glUniform1f(self.__uniform_shader.uniforms.thickness, trace.thickness / 2) GL.glUniform2f(self.__uniform_shader.uniforms.pos_a, trace.p0.x, trace.p0.y) GL.glUniform2f(self.__uniform_shader.uniforms.pos_b, trace.p1.x, trace.p1.y) GL.glUniformMatrix3fv(self.__uniform_shader.uniforms.mat, 1, True, mat.ctypes.data_as(GLI.c_float_p)) GL.glUniform4f(self.__uniform_shader.uniforms.color, *color_a) if render_settings & RENDER_OUTLINES: GL.glDrawArrays(GL.GL_LINE_LOOP, 2, NUM_ENDCAP_SEGMENTS * 2) else: GL.glDrawElements(GL.GL_TRIANGLES, TRIANGLES_SIZE, GL.GL_UNSIGNED_INT, ctypes.c_void_p(0))
class TextBatcher: _render_tag = namedtuple("_render_tag", ["textinfo", "matrix", "color"]) def __init__(self, tr: 'TextRender') -> None: self.text_render = tr self.__cached: Dict[str, '_StringMetrics'] = {} self._va = VA_tex(1024) self.restart() def restart(self) -> None: self.__render_tags: Dict[ Any, List['TextBatcher._render_tag']] = defaultdict(list) self._va.clear() def initializeGL(self) -> None: # Working VBO that will contain glyph data self.vbo = VBO(numpy.ndarray([], dtype=self.text_render.buffer_dtype), GL.GL_DYNAMIC_DRAW, GL.GL_ARRAY_BUFFER) self.vao = VAO() with self.vao, self.vbo: self.text_render.b1.assign() self.text_render.b2.assign() self.__vbo_needs_update = True def render(self, key: Any = None) -> None: return if self.__vbo_needs_update: self.vbo.data = self._va.buffer()[:] self.vbo.bind() self.__vbo_needs_update = False self.text_render.updateTexture() with self.text_render.sdf_shader.program, self.text_render.tex.on( GL.GL_TEXTURE_2D), self.vao: GL.glUniform1i(self.text_render.sdf_shader.program.uniforms.tex1, 0) GL.glUniform4ui(self.text_render.sdf_shader.uniforms.layer_info, 255, COL_TEXT, 0, 255) for tag in self.__render_tags[key]: mat_calc = tag.matrix GL.glUniformMatrix3fv(self.text_render.sdf_shader.uniforms.mat, 1, True, mat_calc.astype(numpy.float32)) GL.glDrawArrays(GL.GL_TRIANGLES, tag.textinfo.start, tag.textinfo.count) def submit(self, ts: '_StringMetrics', mat: 'npt.NDArray[numpy.float64]', color: Any, k: Any = None) -> None: self.__render_tags[k].append(self._render_tag(ts, mat, color)) def submit_text_box(self, premat: 'npt.NDArray[numpy.float64]', text: str, box: Rect, color: Any, k: Any = None) -> None: ts = self.get_string(text) mat = premat.dot(ts.get_render_to_mat(box)) self.submit(ts, mat, color, k) def get_string(self, text: str) -> '_StringMetrics': if text in self.__cached: return self.__cached[text] self.__cached[text] = ti = self.text_render.getStringMetrics(text) ti.start = self._va.tell() self._va.extend(ti.arr) ti.count = ti.arr.count() self.__vbo_needs_update = True return ti
glfw.set_input_mode(theWindow, glfw.CURSOR, glfw.CURSOR_DISABLED) glfw.set_cursor_pos_callback(theWindow, window_cursor_callback) # initialize cursor position cursorPos = glfw.get_cursor_pos(theWindow) glfw.set_scroll_callback(theWindow, window_scroll_callback) vertexVBO = VBO(vertices, usage='GL_STATIC_DRAW') vertexVBO.create_buffers() textureVBO = VBO(textureVertices, usage='GL_STATIC_DRAW') textureVBO.create_buffers() frameVAO = glGenVertexArrays(1) glBindVertexArray(frameVAO) vertexVBO.bind() vertexVBO.copy_data() glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(0)) glEnableVertexAttribArray(0) glBindVertexArray(0) textureVAO = glGenVertexArrays(1) glBindVertexArray(textureVAO) textureVBO.bind() textureVBO.copy_data() glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(0)) glEnableVertexAttribArray(0)
# disable cursor glfw.set_input_mode(theWindow, glfw.CURSOR, glfw.CURSOR_DISABLED) glfw.set_cursor_pos_callback(theWindow, window_cursor_callback) # initialize cursor position cursorPos = glfw.get_cursor_pos(theWindow) glfw.set_scroll_callback(theWindow, window_scroll_callback) sphereDataVBO = VBO(sphereData, usage='GL_STATIC_DRAW') sphereDataVBO.create_buffers() sphereVAO = glGenVertexArrays(1) glBindVertexArray(sphereVAO) sphereDataVBO.bind() sphereDataVBO.copy_data() glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(0)) glEnableVertexAttribArray(0) glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(3 * ctypes.sizeof(ctypes.c_float))) glEnableVertexAttribArray(1) sphereDataVBO.unbind() glBindVertexArray(0) cylinderDataVBO = VBO(cylinderData, usage='GL_STATIC_DRAW') cylinderDataVBO.create_buffers()
# create window theWindow = glfw.create_window(windowSize[0], windowSize[1], 'Audio Oscilloscope', None, None) # make window the current context glfw.make_context_current(theWindow) # enable z-buffer glEnable(GL_DEPTH_TEST) dataVBO = VBO(dataBuffer, usage='GL_STATIC_DRAW') dataVAO = glGenVertexArrays(1) glBindVertexArray(dataVAO) dataVBO.bind() dataVBO.copy_data() glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(0)) glEnableVertexAttribArray(0) dataVBO.unbind() glBindVertexArray(0) renderProgram = GLProgram(waveVertexShaderSource, waveFragmentShaderSource) renderProgram.compile_and_link() waveColorUniform = GLUniform(renderProgram.get_program_id(), 'waveColor', 'vec3f') # change drawing mode
class PolygonVBOPair: __RESTART_INDEX = 2**32 - 1 def __init__(self, view): """ :type gls: pcbre.ui.gl.glshared.GLShared :param gls: :return: """ self.__view = view self.__gls = view.gls self.__position_list = [] self.__position_lookup = {} # Known_polys is a list of (start,end) indicies in self.__index_list self.__tri_draw_ranges = {} self.__outline_draw_ranges = {} self.__tri_index_list = [] self.__outline_index_list = [] self.__vert_vbo_current = False self.__index_vbo_current = False self.restart() def restart(self): self.__deferred_tri_render_ranges = defaultdict(list) self.__deferred_line_render_ranges = defaultdict(list) def initializeGL(self): self.__vao = VAO() # Lookup for vertex positions self.__vert_vbo_dtype = numpy.dtype([("vertex", numpy.float32, 2)]) self.__vert_vbo = VBO(numpy.ndarray(0, dtype=self.__vert_vbo_dtype), GL.GL_DYNAMIC_DRAW) self.__vert_vbo_current = False self.__index_vbo_dtype = numpy.uint32 self.__index_vbo = VBO(numpy.ndarray(0, dtype=self.__index_vbo_dtype), GL.GL_DYNAMIC_DRAW, GL.GL_ELEMENT_ARRAY_BUFFER) self.__index_vbo_current = False self.__shader = self.__gls.shader_cache.get("vert2", "frag1") with self.__vao, self.__vert_vbo: vbobind(self.__shader, self.__vert_vbo_dtype, "vertex").assign() self.__index_vbo.bind() def __update_vert_vbo(self): if self.__vert_vbo_current or not len(self.__position_list): return ar = numpy.ndarray(len(self.__position_list), dtype=self.__vert_vbo_dtype) ar["vertex"] = self.__position_list self.__vert_vbo.data = ar self.__vert_vbo.size = None self.__vert_vbo.copied = False self.__vert_vbo.bind() self.__vert_vbo_current = True def __update_index_vbo(self): if self.__index_vbo_current or not len(self.__tri_index_list): return self.__outline_index_offset = len(self.__tri_index_list) self.__index_vbo.data = numpy.array(self.__tri_index_list + self.__outline_index_list, dtype=self.__index_vbo_dtype) self.__index_vbo.size = None self.__index_vbo.copied = False self.__index_vbo.bind() self.__index_vbo_current = True def __get_position_index(self, point): """ Get the index in the point VBO for a given Point2 coordinate :type point: pcbre.matrix.Point2 :param point: :return: """ norm_pos = point.intTuple() try: return self.__position_lookup[norm_pos] except KeyError: self.__position_lookup[norm_pos] = len(self.__position_list) self.__position_list.append(norm_pos) self.__vert_vbo_current = False return self.__position_lookup[norm_pos] def __add(self, polygon): tris = polygon.get_tris_repr() tri_index_first = len(self.__tri_index_list) for t in tris: for p in t.a, t.b, t.c: self.__tri_index_list.append( self.__get_position_index(Point2(p.x, p.y))) tr = (tri_index_first, len(self.__tri_index_list)) self.__tri_draw_ranges[polygon] = tr outline_index_first = len(self.__outline_index_list) poly_repr = polygon.get_poly_repr() for edge in [poly_repr.exterior] + list(poly_repr.interiors): for pt in edge.coords: self.__outline_index_list.append( self.__get_position_index(Point2(pt))) self.__outline_index_list.append(self.__RESTART_INDEX) lr = (outline_index_first, len(self.__outline_index_list)) self.__outline_draw_ranges[polygon] = lr self.__index_vbo_current = False return tr, lr def deferred(self, polygon, render_settings=RENDER_STANDARD): if polygon in self.__tri_draw_ranges: trange, lrange = self.__tri_draw_ranges[ polygon], self.__outline_draw_ranges[polygon] else: trange, lrange = self.__add(polygon) if render_settings & RENDER_OUTLINES: self.__deferred_line_render_ranges[render_settings].append(lrange) else: self.__deferred_tri_render_ranges[render_settings].append(trange) def render(self, matrix, layer): self.__update_vert_vbo() self.__update_index_vbo() overall_color = self.__view.color_for_layer(layer) sel_color = self.__view.sel_colormod(True, overall_color) def _c_for_rs(rs): if rs & RENDER_SELECTED: return tuple(sel_color) + (1, ) else: return tuple(overall_color) + (1, ) with self.__shader, self.__vao: GL.glUniformMatrix3fv(self.__shader.uniforms.mat, 1, True, matrix.ctypes.data_as(GLI.c_float_p)) for rs, ranges in self.__deferred_tri_render_ranges.items(): tri_draw_list = get_consolidated_draws(ranges) GL.glUniform4f(self.__shader.uniforms.color, *_c_for_rs(rs)) for first, last in tri_draw_list: GL.glDrawElements(GL.GL_TRIANGLES, last - first, GL.GL_UNSIGNED_INT, ctypes.c_void_p(first * 4)) GL.glEnable(GL.GL_PRIMITIVE_RESTART) GL.glPrimitiveRestartIndex(self.__RESTART_INDEX) for rs, ranges in self.__deferred_line_render_ranges.items(): GL.glUniform4f(self.__shader.uniforms.color, *_c_for_rs(rs)) line_draw_list = get_consolidated_draws(ranges) for first, last in line_draw_list: GL.glDrawElements( GL.GL_LINE_STRIP, last - first, GL.GL_UNSIGNED_INT, ctypes.c_void_p( (first + self.__outline_index_offset) * 4)) GL.glDisable(GL.GL_PRIMITIVE_RESTART)
class _THBatch: def __init__(self, parent): self.parent = parent self.restart() self.initialized = False def restart(self): self.__deferred_list_filled = [] self.__deferred_list_outline = [] def _initializeGL(self): self.initialized = True self.__filled_vao = VAO() self.__outline_vao = VAO() with self.__filled_vao, self.parent._sq_vbo: vbobind(self.parent._filled_shader, self.parent._sq_vbo.data.dtype, "vertex").assign() # Use a fake array to get a zero-length VBO for initial binding filled_instance_array = numpy.ndarray(0, dtype=self.parent._filled_instance_dtype) self.filled_instance_vbo = VBO(filled_instance_array) with self.__filled_vao, self.filled_instance_vbo: vbobind(self.parent._filled_shader, self.parent._filled_instance_dtype, "pos", div=1).assign() vbobind(self.parent._filled_shader, self.parent._filled_instance_dtype, "r", div=1).assign() vbobind(self.parent._filled_shader, self.parent._filled_instance_dtype, "r_inside_frac_sq", div=1).assign() vbobind(self.parent._filled_shader, self.parent._filled_instance_dtype, "color", div=1).assign() with self.__outline_vao, self.parent._outline_vbo: vbobind(self.parent._outline_shader, self.parent._outline_vbo.data.dtype, "vertex").assign() # Build instance for outline rendering # We don't have an inner 'r' for this because we just do two instances per vertex # Use a fake array to get a zero-length VBO for initial binding outline_instance_array = numpy.ndarray(0, dtype=self.parent._outline_instance_dtype) self.outline_instance_vbo = VBO(outline_instance_array) with self.__outline_vao, self.outline_instance_vbo: vbobind(self.parent._outline_shader, self.parent._outline_instance_dtype, "pos", div=1).assign() vbobind(self.parent._outline_shader, self.parent._outline_instance_dtype, "r", div=1).assign() vbobind(self.parent._outline_shader, self.parent._outline_instance_dtype, "color", div=1).assign() def deferred(self, center, r1, r2, rs, render_hint=RENDER_HINT_NORMAL): if rs & RENDER_OUTLINES: self.__deferred_list_outline.append((center, r1, r2, rs)) else: self.__deferred_list_filled.append((center, r1, r2, rs)) def prepare(self): self.__prepare_filled() self.__prepare_outline() def __prepare_filled(self): if not self.initialized: self._initializeGL() count = len(self.__deferred_list_filled) self.__filled_count = count if count == 0: return # Resize instance data array instance_array = numpy.ndarray(count, dtype = self.parent._filled_instance_dtype) color_a = [0.6, 0.6, 0.6, 1] for n, (center, r1, r2, rs) in enumerate(self.__deferred_list_filled): # HACK, color object color_mod = self.parent.parent.sel_colormod(rs & RENDER_SELECTED, color_a) # frag shader uses pythag to determine is frag is within # shaded area. Precalculate comparison term r_frac_sq = (r2 / r1) ** 2 instance_array[n] = (center, r1, r_frac_sq, color_mod) self.filled_instance_vbo.data = instance_array self.filled_instance_vbo.size = None self.filled_instance_vbo.copied = False self.filled_instance_vbo.bind() def __prepare_outline(self): count = 0 for center, r1, r2, rs in self.__deferred_list_outline: if r2 == 0: count += 1 else: count += 2 self.__outline_count = count if count == 0: return # Resize instance data array instance_array = numpy.ndarray(count, dtype = self.parent._outline_instance_dtype) n = 0 for center, r1, r2, rs in self.__deferred_list_outline: color_a = [0.6, 0.6, 0.6, 1] # HACK, color object color_a = self.parent.parent.sel_colormod(rs & RENDER_SELECTED, color_a) instance_array[n] = (center, r1, color_a) n += 1 if r2 > 0: instance_array[n] = (center, r2, color_a) n += 1 self.outline_instance_vbo.data = instance_array self.outline_instance_vbo.size = None self.outline_instance_vbo.copied = False self.outline_instance_vbo.bind() def render(self, mat): self.render_filled(mat) self.render_outline(mat) def render_filled(self, mat): if self.__filled_count == 0: return with self.parent._filled_shader, self.__filled_vao, self.filled_instance_vbo, self.parent._sq_vbo: GL.glUniformMatrix3fv(self.parent._filled_shader.uniforms.mat, 1, True, mat.ctypes.data_as(GLI.c_float_p)) GL.glDrawArraysInstanced(GL.GL_TRIANGLE_STRIP, 0, 4, len(self.__deferred_list_filled)) def render_outline(self, mat): if self.__outline_count == 0: return with self.parent._outline_shader, self.__outline_vao, self.outline_instance_vbo, self.parent._sq_vbo: GL.glUniformMatrix3fv(self.parent._outline_shader.uniforms.mat, 1, True, mat.ctypes.data_as(GLI.c_float_p)) GL.glDrawArraysInstanced(GL.GL_LINE_LOOP, 0, N_OUTLINE_SEGMENTS, self.__outline_count)
class StlModel(Model): """ Model for displaying and manipulating STL data. """ def load_data(self, model_data, callback=None): t_start = time.time() vertices, normals = model_data # convert python lists to numpy arrays for constructing vbos self.vertices = numpy.require(vertices, 'f') self.normals = numpy.require(normals, 'f') self.scaling_factor = 1.0 self.rotation_angle = { self.AXIS_X: 0.0, self.AXIS_Y: 0.0, self.AXIS_Z: 0.0, } self.mat_specular = (1.0, 1.0, 1.0, 1.0) self.mat_shininess = 50.0 self.light_position = (20.0, 20.0, 20.0) self.initialized = False t_end = time.time() logging.info('Initialized STL model in %.2f seconds' % (t_end - t_start)) logging.info('Vertex count: %d' % len(self.vertices)) def normal_data_empty(self): """ Return true if the model has no normal data. """ empty = (self.normals.max() == 0 and self.normals.min() == 0) return empty def calculate_normals(self): """ Calculate surface normals for model vertices. """ a = self.vertices[0::3] - self.vertices[1::3] b = self.vertices[1::3] - self.vertices[2::3] cross = numpy.cross(a, b) # normalize the cross product magnitudes = numpy.apply_along_axis(numpy.linalg.norm, 1, cross).reshape(-1, 1) normals = cross / magnitudes # each of 3 facet vertices shares the same normal normals = normals.repeat(3, 0) return normals # ------------------------------------------------------------------------ # DRAWING # ------------------------------------------------------------------------ def init(self): """ Create vertex buffer objects (VBOs). """ self.vertex_buffer = VBO(self.vertices, 'GL_STATIC_DRAW') if self.normal_data_empty(): logging.info('STL model has no normal data') self.normals = self.calculate_normals() self.normal_buffer = VBO(self.normals, 'GL_STATIC_DRAW') self.initialized = True def draw_facets(self): glPushMatrix() glEnable(GL_LIGHT0) glEnable(GL_LIGHT1) glShadeModel(GL_SMOOTH) # material properties (white plastic) glMaterial(GL_FRONT, GL_AMBIENT, (0.0, 0.0, 0.0, 1.0)) glMaterial(GL_FRONT, GL_DIFFUSE, (0.55, 0.55, 0.55, 1.0)) glMaterial(GL_FRONT, GL_SPECULAR, (0.7, 0.7, 0.7, 1.0)) glMaterial(GL_FRONT, GL_SHININESS, 32.0) # lights properties glLight(GL_LIGHT0, GL_AMBIENT, (0.3, 0.3, 0.3, 1.0)) glLight(GL_LIGHT0, GL_DIFFUSE, (0.3, 0.3, 0.3, 1.0)) glLight(GL_LIGHT1, GL_DIFFUSE, (0.3, 0.3, 0.3, 1.0)) # lights position glLightfv(GL_LIGHT0, GL_POSITION, self.light_position) glLightfv(GL_LIGHT1, GL_POSITION, (-20.0, -20.0, 20.0)) glColor(1.0, 1.0, 1.0) ### VBO stuff self.vertex_buffer.bind() glVertexPointer(3, GL_FLOAT, 0, None) self.normal_buffer.bind() glNormalPointer(GL_FLOAT, 0, None) glEnableClientState(GL_VERTEX_ARRAY) glEnableClientState(GL_NORMAL_ARRAY) glDrawArrays(GL_TRIANGLES, 0, len(self.vertices)) glDisableClientState(GL_NORMAL_ARRAY) glDisableClientState(GL_VERTEX_ARRAY) self.normal_buffer.unbind() self.vertex_buffer.unbind() ### end VBO stuff glDisable(GL_LIGHT1) glDisable(GL_LIGHT0) glPopMatrix() def display(self, mode_2d=False): glEnable(GL_LIGHTING) self.draw_facets() glDisable(GL_LIGHTING) # ------------------------------------------------------------------------ # TRANSFORMATIONS # ------------------------------------------------------------------------ def scale(self, factor): if factor != self.scaling_factor: logging.info('actually scaling vertices') self.vertices *= (factor / self.scaling_factor) self.scaling_factor = factor self.invalidate_bounding_box() self.modified = True def translate(self, x, y, z): self.vertices = vector.translate(self.vertices, x, y, z) self.invalidate_bounding_box() self.modified = True def rotate_rel(self, angle, axis): logging.info('rotating vertices by a relative angle of ' '%.2f degrees along the %s axis' % (angle, self.axis_letter_map[axis])) angle = angle % 360 self.vertices = vector.rotate(self.vertices, angle, *axis) self.rotation_angle[axis] += angle self.invalidate_bounding_box() self.modified = True def rotate_abs(self, angle, axis): angle = angle % 360 if self.rotation_angle[axis] == angle: return logging.info('rotating vertices by an absolute angle of ' '%.2f degrees along the %s axis' % (angle, self.axis_letter_map[axis])) final_matrix = vector.identity_matrix() # modify matrix to rotate to initial position for v in [self.AXIS_Z, self.AXIS_Y, self.AXIS_X]: matrix = vector.rotation_matrix(-self.rotation_angle[v], *v) final_matrix = final_matrix.dot(matrix) # change the angle self.rotation_angle[axis] = angle # modify matrix to rotate to new position for v in [self.AXIS_X, self.AXIS_Y, self.AXIS_Z]: matrix = vector.rotation_matrix(self.rotation_angle[v], *v) final_matrix = final_matrix.dot(matrix) self.vertices = self.vertices.dot(final_matrix) self.invalidate_bounding_box() self.modified = True
class Drawable(Attributes): """Base class for objects that can be rendered by the OpenGL engine. This is the basic drawable object in the pyFormex OpenGL rendering engine. It collects all the data that are needed to properly described any object to be rendered by the OpenGL shader programs. It has a multitude of optional attributes allowing it to describe many very different objects and rendering situations. This class is however not intended to be directly used to construct an object for rendering. The :class:`Actor` class and its multiple subclasses should be used for that purpose. The Actor classes provide an easier and more logical interface, and more powerful at the same time, since they can be compound: one Actor can hold multiple Drawables. The elementary objects that can be directly drawn by the shader programs are more simple, yet very diverse. The Drawable class collects all the data that are needed by the OpenGL engine to do a proper rendering of the object. It this represents a single, versatile interface of the Actor classes with the GPU shader programs. The versatility comes from the :class:`Attributes` base class, with an unlimited set of attributes. Any undefined attribute just returns None. Some of the most important attributes are described hereafter: - `rendertype`: int: the type of rendering process that will be applied by the rendering engine to the Drawable: 0: A full 3D Actor. The Drawable will be rendered in full 3D with all active capabilities, such as camera rotation, projection, rendermode, lighting. The full object undergoes the camera transformations, and thus will appear as a 3D object in space. The object's vertices are defined in 3D world coordinates. Used in: :class:`Actor`. 1: A 2D object (often a text or an image) inserted at a 3D position. The 2D object always keeps its orientation towards the camera. When the camera changes, the object can change its position on the viewport, but the oject itself looks the same. This can be used to add annotations to (parts of) a 3D object. The object is defined in viewport coordinates, the insertion points are in 3D world coordinates. Used in: :class:`textext.Text`. 2: A 2D object inserted at a 2D position. Both object and position are defined in viewport coordinates. The object will take a fixed position on the viewport. This can be use to add decorations to the viewport (like color legends and background images). Used in: :class:`decors.ColorLegend`. 3: Like 2, but with special purpose. These Drawables are not part of the user scene, but used for system purposes (like setting the background color, or adding an elastic rectangle during mouse picking). Used in: :meth:`Canvas.createBackground`. -1: Like 1, but with different insertion points for the multiple items in the object. Used to place a list of marks at a list of points. Used in: :class:`textext.Text`. -2: A 3D object inserted at a 2D position. The 3D object will rotate when the camera changes directions, but it will always be located on the same position of the viewport. This is normally used to display a helper object showing the global axis directions. Used in: :class:`decors.Triade`. The initialization of a Drawable takes a single parameter: `parent`, which is the Actor that created the Drawable. All other parameters should be keyword arguments, and are stored as attributes in the Drawable. Methods: - `prepare...`: creates sanitized and derived attributes/data. Its action pend on current canvas settings: mode, transparent, avgnormals - `render`: push values to shader and render the object: depends on canvas and renderer. - `pick`: fake render to be used during pick operations - `str`: format the full data set of the Drawable """ # A list of acceptable attributes in the drawable # These are the parent attributes that can be overridden attributes = [ 'cullface', 'subelems', 'color', 'name', 'highlight', 'opak', 'linewidth', 'pointsize', 'lighting', 'offset', 'vbo', 'nbo', 'ibo', 'alpha', 'drawface', 'objectColor', 'useObjectColor', 'rgbamode', 'texture', 'texcoords', ] def __init__(self, parent, **kargs): """Create a new drawable.""" # Should we really restrict this???? #kargs = utils.selectDict(kargs, Drawable.attributes) Attributes.__init__(self, kargs, default=parent) #print("ATTRIBUTES STORED IN DRAWABLE",self) #print("ATTRIBUTES STORED IN PARENT",parent) # Default lighting parameter: # rendertype 0 (3D) follows canvas lighting # other rendertypes set lighting=False by default if self.rendertype != 0 and self.lighting is None: self.lighting = False # The final plexitude of the drawn objects if self.subelems is not None: self.nplex = self.subelems.shape[-1] else: self.nplex = self._fcoords.shape[-2] self.glmode = glObjType(self.nplex) self.prepareColor() #self.prepareNormals() # The normals are currently always vertex self.prepareSubelems() if self.texture is not None: self.prepareTexture() def prepareColor(self): """Prepare the colors for the shader.""" # # This should probably be moved to Actor # if self.highlight: # we set single highlight color in shader # Currently do everything in Formex model # And we always need this one self.avbo = VBO(self.fcoords) self.useObjectColor = 1 self.objectColor = np.array(colors.red) elif self.color is not None: #print("COLOR",self.color) if self.color.ndim == 1: # here we only accept a single color for front and back # different colors should have been handled before self.useObjectColor = 1 self.objectColor = self.color elif self.color.ndim == 2: self.useObjectColor = 0 self.vertexColor = at.multiplex(self.color, self.object.nplex()) #pf.debug("Multiplexing colors: %s -> %s " % (self.color.shape, self.vertexColor.shape),pf.DEBUG.OPENGL2) elif self.color.ndim == 3: self.useObjectColor = 0 self.vertexColor = self.color if self.vertexColor is not None: #print("Shader suffix:[%s]" % pf.options.shader) if pf.options.shader == 'alpha': self.alpha = 0.5 if self.vertexColor.shape[-1] == 3: # Expand to 4 !!! self.vertexColor = at.growAxis(self.vertexColor, 1, fill=0.5) self.cbo = VBO(self.vertexColor.astype(float32)) if pf.options.shader == 'alpha': size_report("Created cbo VBO", self.cbo) self.rgbamode = self.useObjectColor == 0 and self.vertexColor.shape[ -1] == 4 #### TODO: should we make this configurable ?? # # !!!!!!!!!!!! Fix a bug with AMD cards !!!!!!!!!!!!!!! # # it turns out that some? AMD? cards do not like an unbound cbo # even if that attribute is not used in the shader. # Creating a dummy color buffer seems to solve that problem # if self.cbo is None: self.cbo = VBO(np.array(colors.red)) #if self.rendertype == 3: # print("CBO DATA %s\n" % self.name,self.cbo.data) def changeVertexColor(self, color): """Change the vertex color buffer of the object. This is experimental!!! Just make sure that the passed data have the correct shape! """ if self.useObjectColor: return if pf.options.shader == 'alpha': #if color.size != self.cbo.size: size_report('color', color) size_report('cbo', self.cbo) print(self.cbo.size / color.size) print("Can not change vertex color from shape %s to shape %s" % (str(self.cbo.shape), str(color.shape))) #return self.vertexColor = self.color self.cbo = VBO(color.astype(float32)) if pf.options.shader == 'alpha': print("Replace cbo with") size_report('cbo', self.cbo) def prepareTexture(self): """Prepare texture and texture coords""" if self.useTexture == 1: if self.texcoords.ndim == 2: #curshape = self.texcoords.shape self.texcoords = at.multiplex(self.texcoords, self.object.nelems(), axis=-2) #print("Multiplexing texture coords: %s -> %s " % (curshape, self.texcoords.shape)) self.tbo = VBO(self.texcoords.astype(float32)) self.texture.activate() def prepareSubelems(self): """Create an index buffer to draw subelements This is always used for nplex > 3, but also to draw the edges for nplex=3. """ if self.ibo is None and self.subelems is not None: self.ibo = VBO(self.subelems.astype(int32), target=GL.GL_ELEMENT_ARRAY_BUFFER) def render(self, renderer): """Render the geometry of this object""" def render_geom(): if self.ibo: GL.glDrawElementsui(self.glmode, self.ibo) else: GL.glDrawArrays(self.glmode, 0, np.asarray(self.vbo.shape[:-1]).prod()) if self.offset: pf.debug("POLYGON OFFSET", pf.DEBUG.DRAW) GL.glPolygonOffset(1.0, 1.0) if self.linewidth: GL.glLineWidth(self.linewidth) renderer.shader.loadUniforms(self) if self.offset3d is not None: offset = renderer.camera.toNDC(self.offset3d) offset[..., 2] = 0. offset += (1., 1., 0.) #print(self.rendertype) #print("OFFSET=",offset) #print("COORDS=",self.vbo.data) if offset.shape == (3, ): renderer.shader.uniformVec3('offset3', offset) elif offset.ndim > 1: self.obo = VBO(offset.astype(float32)) #self.obo = VBO(self._default_dict_.fcoords+offset) #print(self._default_dict_.fcoords) #print(offset) #print(self.obo.data.shape,self.vbo.data.shape) self.obo.bind() GL.glEnableVertexAttribArray( renderer.shader.attribute['vertexOffset']) GL.glVertexAttribPointer( renderer.shader.attribute['vertexOffset'], 3, GL.GL_FLOAT, False, 0, self.obo) if self.rendertype == -2: # This is currently a special code for the Triade # It needs an object with coords in pixel values, # centered around the origin # and must have attributes x,y, set to the viewport # position of the (0,0,0) point after rotation. # rot = renderer.camera.modelview.rot x = np.dot(self._fcoords.reshape(-1, 3), rot).reshape(self._fcoords.shape) x[:, :, 0] += self.x x[:, :, 1] += self.y x[:, :, 2] = 0 self.vbo = VBO(x) self.vbo.bind() GL.glEnableVertexAttribArray(renderer.shader.attribute['vertexCoords']) GL.glVertexAttribPointer(renderer.shader.attribute['vertexCoords'], 3, GL.GL_FLOAT, False, 0, self.vbo) if self.ibo: self.ibo.bind() if self.nbo: self.nbo.bind() GL.glEnableVertexAttribArray( renderer.shader.attribute['vertexNormal']) GL.glVertexAttribPointer(renderer.shader.attribute['vertexNormal'], 3, GL.GL_FLOAT, False, 0, self.nbo) if self.cbo: self.cbo.bind() GL.glEnableVertexAttribArray( renderer.shader.attribute['vertexColor']) GL.glVertexAttribPointer(renderer.shader.attribute['vertexColor'], self.cbo.shape[-1], GL.GL_FLOAT, False, 0, self.cbo) if self.tbo: self.tbo.bind() GL.glEnableVertexAttribArray( renderer.shader.attribute['vertexTexturePos']) GL.glVertexAttribPointer( renderer.shader.attribute['vertexTexturePos'], 2, GL.GL_FLOAT, False, 0, self.tbo) if self.cullface == 'front': # Draw back faces GL.glEnable(GL.GL_CULL_FACE) GL.glCullFace(GL.GL_FRONT) elif self.cullface == 'back': # Draw front faces GL.glEnable(GL.GL_CULL_FACE) GL.glCullFace(GL.GL_BACK) else: GL.glDisable(GL.GL_CULL_FACE) # Specifiy the depth comparison function if self.ontop: GL.glDepthFunc(GL.GL_ALWAYS) # Bind the texture if self.texture: self.texture.bind() render_geom() if self.ibo: self.ibo.unbind() if self.obo: self.obo.unbind() GL.glDisableVertexAttribArray( renderer.shader.attribute['vertexOffset']) if self.cbo: self.cbo.unbind() GL.glDisableVertexAttribArray( renderer.shader.attribute['vertexColor']) if self.tbo: self.tbo.unbind() GL.glDisableVertexAttribArray( renderer.shader.attribute['vertexTexturePos']) if self.nbo: self.nbo.unbind() GL.glDisableVertexAttribArray( renderer.shader.attribute['vertexNormal']) self.vbo.unbind() GL.glDisableVertexAttribArray( renderer.shader.attribute['vertexCoords']) if self.offset: pf.debug("POLYGON OFFSET RESET", pf.DEBUG.DRAW) GL.glPolygonOffset(0.0, 0.0) def pick(self, renderer): """Pick the geometry of this object""" def render_geom(): if self.ibo: GL.glDrawElementsui(self.glmode, self.ibo) else: GL.glDrawArrays(self.glmode, 0, np.asarray(self.vbo.shape[:-1]).prod()) renderer.shader.loadUniforms(self) self.vbo.bind() GL.glEnableVertexAttribArray(renderer.shader.attribute['vertexCoords']) GL.glVertexAttribPointer(renderer.shader.attribute['vertexCoords'], 3, GL.GL_FLOAT, False, 0, self.vbo) if self.ibo: self.ibo.bind() if self.cullface == 'front': # Draw back faces GL.glEnable(GL.GL_CULL_FACE) GL.glCullFace(GL.GL_FRONT) elif self.cullface == 'back': # Draw front faces GL.glEnable(GL.GL_CULL_FACE) GL.glCullFace(GL.GL_BACK) else: GL.glDisable(GL.GL_CULL_FACE) # Specifiy the depth comparison function if self.ontop: GL.glDepthFunc(GL.GL_ALWAYS) render_geom() if self.ibo: self.ibo.unbind() self.vbo.unbind() GL.glDisableVertexAttribArray( renderer.shader.attribute['vertexCoords']) def __str__(self): keys = sorted(set(self.keys()) - set(('_default_dict_', ))) print("Keys %s" % keys) out = utils.formatDict(utils.selectDict(self, keys)) print(out) return out
class TraceRender: def __init__(self, parent_view): self.parent = parent_view self.restart() def __initialize_uniform(self, gls): self.__uniform_shader_vao = VAO() self.__uniform_shader = gls.shader_cache.get("line_vertex_shader", "frag1", defines={"INPUT_TYPE": "uniform"}) with self.__uniform_shader_vao, self.trace_vbo: vbobind(self.__uniform_shader, self.trace_vbo.dtype, "vertex").assign() vbobind(self.__uniform_shader, self.trace_vbo.dtype, "ptid").assign() self.index_vbo.bind() def initializeGL(self, gls): # Build trace vertex VBO and associated vertex data dtype = [("vertex", numpy.float32, 2), ("ptid", numpy.uint32)] self.working_array = numpy.zeros(NUM_ENDCAP_SEGMENTS * 2 + 2, dtype=dtype) self.trace_vbo = VBO(self.working_array, GL.GL_DYNAMIC_DRAW) # Generate geometry for trace and endcaps # ptid is a variable with value 0 or 1 that indicates which endpoint the geometry is associated with self.__build_trace() self.__attribute_shader_vao = VAO() self.__attribute_shader = gls.shader_cache.get("line_vertex_shader", "frag1", defines={"INPUT_TYPE": "in"}) # Now we build an index buffer that allows us to render filled geometry from the same # VBO. arr = [] for i in range(NUM_ENDCAP_SEGMENTS - 1): arr.append(0) arr.append(i + 2) arr.append(i + 3) for i in range(NUM_ENDCAP_SEGMENTS - 1): arr.append(1) arr.append(i + NUM_ENDCAP_SEGMENTS + 2) arr.append(i + NUM_ENDCAP_SEGMENTS + 3) arr.append(2) arr.append(2 + NUM_ENDCAP_SEGMENTS - 1) arr.append(2 + NUM_ENDCAP_SEGMENTS) arr.append(2 + NUM_ENDCAP_SEGMENTS) arr.append(2 + NUM_ENDCAP_SEGMENTS * 2 - 1) arr.append(2) arr = numpy.array(arr, dtype=numpy.uint32) self.index_vbo = VBO(arr, target=GL.GL_ELEMENT_ARRAY_BUFFER) self.instance_dtype = numpy.dtype( [ ("pos_a", numpy.float32, 2), ("pos_b", numpy.float32, 2), ("thickness", numpy.float32, 1), # ("color", numpy.float32, 4) ] ) # Use a fake array to get a zero-length VBO for initial binding instance_array = numpy.ndarray(0, dtype=self.instance_dtype) self.instance_vbo = VBO(instance_array) with self.__attribute_shader_vao, self.trace_vbo: vbobind(self.__attribute_shader, self.trace_vbo.dtype, "vertex").assign() vbobind(self.__attribute_shader, self.trace_vbo.dtype, "ptid").assign() with self.__attribute_shader_vao, self.instance_vbo: self.__bind_pos_a = vbobind(self.__attribute_shader, self.instance_dtype, "pos_a", div=1) self.__bind_pos_b = vbobind(self.__attribute_shader, self.instance_dtype, "pos_b", div=1) self.__bind_thickness = vbobind(self.__attribute_shader, self.instance_dtype, "thickness", div=1) # vbobind(self.__attribute_shader, self.instance_dtype, "color", div=1).assign() self.__base_rebind(0) self.index_vbo.bind() self.__initialize_uniform(gls) self.__last_prepared = weakref.WeakKeyDictionary() def __base_rebind(self, base): self.__bind_pos_a.assign(base) self.__bind_pos_b.assign(base) self.__bind_thickness.assign(base) def restart(self): self.__deferred_layer = defaultdict(list) self.__prepared = False self.__needs_rebuild = False def __build_trace(self): # Update trace VBO self.working_array["vertex"][0] = (0, 0) self.working_array["ptid"][0] = 0 self.working_array["vertex"][1] = (0, 0) self.working_array["ptid"][1] = 1 end = Vec2(1, 0) for i in range(0, NUM_ENDCAP_SEGMENTS): theta = math.pi * i / (NUM_ENDCAP_SEGMENTS - 1) + math.pi / 2 m = rotate(theta).dot(end.homol()) self.working_array["vertex"][2 + i] = m[:2] self.working_array["ptid"][2 + i] = 0 self.working_array["vertex"][2 + i + NUM_ENDCAP_SEGMENTS] = -m[:2] self.working_array["ptid"][2 + i + NUM_ENDCAP_SEGMENTS] = 1 # Force data copy self.trace_vbo.copied = False self.trace_vbo.bind() def deferred_multiple(self, trace_settings, render_settings=0): """ :param trace_settings: list of traces to draw and the settings for that particular trace :param render_settings: :return: """ for t, tr in trace_settings: tr |= render_settings self.deferred(t, tr) def deferred(self, trace, render_settings, render_hint): assert not self.__prepared self.__deferred_layer[trace.layer].append((trace, render_settings)) if trace not in self.__last_prepared: self.__needs_rebuild = True def prepare(self): """ :return: Build VBOs and information for rendering pass """ self.__prepared = True if not self.__needs_rebuild: return self.__last_prepared = weakref.WeakKeyDictionary() # Total trace count, across all layers count = sum(len(i) for i in self.__deferred_layer.values()) # Allocate an array of that size instance_array = numpy.ndarray(count, dtype=self.instance_dtype) pos = 0 for layer, traces in self.__deferred_layer.items(): # We reorder the traces to batch them by outline, and net to encourage # maximum draw call length. The rationale is that nets are commonly selected # or may be commonly drawn in different colors. traces = sorted(traces, key=lambda i: (i[1] & RENDER_OUTLINES, id(i[0].net))) for trace, _ in traces: # Now insert into the array instance_array[pos] = (trace.p0, trace.p1, trace.thickness / 2) # And memoize where the trace occurs self.__last_prepared[trace] = pos pos += 1 # Force full resend of VBO self.instance_vbo.data = instance_array self.instance_vbo.size = None self.instance_vbo.copied = False self.instance_vbo.bind() def render_deferred_layer(self, mat, layer): if not self.__prepared: self.prepare() trace_settings = self.__deferred_layer[layer] count = len(trace_settings) if count == 0: return # key format: is_selected, is_outline draw_bins = defaultdict(list) draw_range_bins = dict() # Bin the traces by draw call for t, tr in trace_settings: is_selected = bool(tr & RENDER_SELECTED) is_outline = bool(tr & RENDER_OUTLINES) draw_bins[is_selected, is_outline].append(self.__last_prepared[t]) # Build draw ranges for key, bin in draw_bins.items(): draw_range_bins[key] = get_consolidated_draws_1(draw_bins[key]) # HACK / Fixme: Precalculate selected / nonselected colors color_a = self.parent.color_for_layer(layer) + [1] color_sel = self.parent.sel_colormod(True, color_a) has_base_instance = False with self.__attribute_shader, self.__attribute_shader_vao: # Setup overall calls GL.glUniformMatrix3fv(self.__attribute_shader.uniforms.mat, 1, True, mat.ctypes.data_as(GLI.c_float_p)) # We order the draw calls such that selected areas are drawn on top of nonselected. sorted_kvs = sorted(draw_range_bins.items(), key=lambda i: i[0][0]) for (is_selected, is_outline), ranges in sorted_kvs: if is_selected: color = color_sel else: color = color_a GL.glUniform4f(self.__attribute_shader.uniforms.color, *color) if has_base_instance: # Many instances backport glDrawElementsInstancedBaseInstance # This is faster than continually rebinding, so support if possible if not is_outline: for first, last in ranges: # filled traces come first in the array GL.glDrawElementsInstancedBaseInstance( GL.GL_TRIANGLES, TRIANGLES_SIZE, GL.GL_UNSIGNED_INT, ctypes.c_void_p(0), last - first, first, ) else: for first, last in ranges: # Then outline traces. We reuse the vertex data for the outside GL.glDrawArraysInstancedBaseInstance( GL.GL_LINE_LOOP, 2, NUM_ENDCAP_SEGMENTS * 2, last - first, first ) else: with self.instance_vbo: if not is_outline: for first, last in ranges: # filled traces come first in the array self.__base_rebind(first) GL.glDrawElementsInstanced( GL.GL_TRIANGLES, TRIANGLES_SIZE, GL.GL_UNSIGNED_INT, ctypes.c_void_p(0), last - first, ) else: for first, last in ranges: self.__base_rebind(first) # Then outline traces. We reuse the vertex data for the outside GL.glDrawArraysInstanced(GL.GL_LINE_LOOP, 2, NUM_ENDCAP_SEGMENTS * 2, last - first) # Immediate-mode render of a single trace # SLOW (at least for bulk-rendering) # Useful for rendering UI elements def render(self, mat, trace, render_settings=RENDER_STANDARD): color_a = self.parent.color_for_trace(trace) + [1] color_a = self.parent.sel_colormod(render_settings & RENDER_SELECTED, color_a) with self.__uniform_shader, self.__uniform_shader_vao: GL.glUniform1f(self.__uniform_shader.uniforms.thickness, trace.thickness / 2) GL.glUniform2f(self.__uniform_shader.uniforms.pos_a, trace.p0.x, trace.p0.y) GL.glUniform2f(self.__uniform_shader.uniforms.pos_b, trace.p1.x, trace.p1.y) GL.glUniformMatrix3fv(self.__uniform_shader.uniforms.mat, 1, True, mat.ctypes.data_as(GLI.c_float_p)) GL.glUniform4f(self.__uniform_shader.uniforms.color, *color_a) if render_settings & RENDER_OUTLINES: GL.glDrawArrays(GL.GL_LINE_LOOP, 2, NUM_ENDCAP_SEGMENTS * 2) else: GL.glDrawElements(GL.GL_TRIANGLES, TRIANGLES_SIZE, GL.GL_UNSIGNED_INT, ctypes.c_void_p(0))
class MSGLCanvas2D(QGLWidget): """ Canvas GL plotting in 2 dimensions """ MAX = 100. corner_=100.0 zoom_= 1.5 xrot_=220 yrot_ = 220 trans_x_ =0.0 trans_y_ = 0.0 def __init__(self, data, parent=None, **kw): """ Constructor, initialization """ QGLWidget.__init__(self, parent) self.setFormat(QGLFormat(QGL.SampleBuffers)) self.setMinimumSize(500,300)#300 self.setMouseTracking(True) self.setFocusPolicy(Qt.StrongFocus) self.data=data vertexes=[] colors=[] from utils.misc import IceAndFire maxY=max(map(max, [log10(el.y_data) for el in data])) maxX=max(map(max, [el.x_data for el in data])) rtmax=max([z.rtmin for z in data]) for el in data: for i, x in enumerate(el.x_data): c=IceAndFire.getQColor(log10(el.y_data[i])/maxY) colors.append(c) vertexes.append([(x*2*self.corner_)/maxX, (el.rt*2*self.corner_)/rtmax]) from OpenGL.arrays.vbo import VBO self.vertexes= VBO(array(vertexes,'f')) self.colors=VBO(array(colors,'f')) self.mode = "None" # "ZOOMING", "PANNING", "NONE" self.lastpos = QPoint() self.counter_trans_x = 0 self.counter_trans_y = 0 self.defaultColors = {'ticks':(0.,0.,0.,0.), 'axes':(0.,0.,0.,0.), 'curves':(0.,0.,1.,0.), 'backgroundColor':(1.,1.,1.,1.) } #self.axes=self.drawAxes() self.transformationMatrix = self.setupTransformationMatrix(self.width(), self.height()) def setupTransformationMatrix(self,w, h): """ use a matrix to translate in the gl landmark """ m = QMatrix() m.translate(-w/2, h/2) m.scale(300./w, 300./h) print w, h, w/300., 1-((h/300)-1) #m.scale((self.width()*100)/300, -(self.height()*100)/300) #self.currentSize.x = w #self.currentSize.y = h return m def inGLCoordinate(self, point): return self.transformationMatrix.map(point) def resizeGL(self, w, h): """ called when window is being resized """ glViewport(0,0, w, h) glMatrixMode(GL_PROJECTION) glLoadIdentity() gluOrtho2D(-self.corner_*self.zoom_, self.corner_*self.zoom_, -self.corner_*self.zoom_, self.corner_*self.zoom_) #self.transformationMatrix = self.setupTransformationMatrix(w, h) glMatrixMode(GL_MODELVIEW) def initializeGL(self): """needed, initialize GL parameters""" #glClearColor(1.,1.,1.,1.) glDisable(GL_DEPTH_TEST) glEnable(GL_LINE_SMOOTH) glEnable(GL_POINT_SMOOTH) glHint (GL_LINE_SMOOTH_HINT, GL_NICEST) glLoadIdentity() #model view by default # self.grid_lines = self.drawGridLines() # self.ticks =self.drawAxisTick() # self.axes = self.drawAxes() def paintGL(self): """Draw the scene, needed, called each time glDraw""" glClear(GL_COLOR_BUFFER_BIT) glTranslated(self.trans_x_, self.trans_y_, 0.) #addition glMatrixMode(GL_PROJECTION) glLoadIdentity() gluOrtho2D(-self.corner_*self.zoom_, self.corner_*self.zoom_, -self.corner_*self.zoom_, self.corner_*self.zoom_) glMatrixMode(GL_MODELVIEW) #end addition #glCallList(self.grid_lines) #glCallList(self.ticks) #glCallList(self.axes) glLineWidth(30.0) self.scatterPlot() # if self.flags == "chrom": # self.drawAxisLegend("retention time[s]", "intensity[%]") # glCallList(self.lines) # elif self.flags == "spectrum": # self.drawAxisLegend("m/z", "intensity") def drawAxes(self, width=2., colour=(0.,0.,0.)): """ Draw Axes """ #width must be a float axes = glGenLists(1) glNewList(axes, GL_COMPILE) glLineWidth(width) glColor(colour[0],colour[1],colour[2]) glBegin(GL_LINES) #x_achse glVertex2d(-self.corner_, -self.corner_) glVertex2d( self.corner_, -self.corner_) #y-achse glVertex2d(-self.corner_, -self.corner_) glVertex2d( -self.corner_, self.corner_) glEnd() glEndList() return axes def drawLegends(self, pos): """ draw legend at the specified position """ pass def drawAxisLegend(self, x_label, y_label): """ Draw Axis Legend """ font =QFont("Typewriter") #RT axis legend font.setPixelSize(12) self.renderText(self.corner_, -self.corner_-20.0, 0., x_label)# font self.renderText(-self.corner_-20.0, self.corner_, 0., y_label, font) def resetTranslations(self): """ reset the different translation to 0 """ self.trans_x_ =0. self.trans_y_ =0. self.counter_trans_x=0. self.counter_trans_y=0. def normalizeAngle(self,angle): while (angle < 0): angle += 360 * 16 while (angle > 360 * 16): angle -= 360 * 16 ########DRAWING METHODS################################################## def drawLine(self, point_, point): glBegin(GL_LINES) glVertex2d(point_.x(), point_.y()) glVertex2d(point.x(), point.y()) glEnd() def drawRect(self, p_1, p_2, p_3=None, p_4 = None): pass def drawOnePoint(self, point, colour= Qt.yellow): pass def scatterPlot(self): """ Draw Data (x, y)""" if self.vertexes is not None and self.colors is not None: self.vertexes.bind() glEnableClientState(GL_VERTEX_ARRAY) glVertexPointerf(self.vertexes) self.colors.bind() glEnableClientState(GL_COLOR_ARRAY) glColorPointerf(self.colors) glDrawArrays(GL_LINES, 0, len(self.vertexes)) self.vertexes.unbind() self.colors.unbind() #self.textures.unbind() glDisableClientState(GL_VERTEX_ARRAY) glDisableClientState(GL_COLOR_ARRAY) def spectrumPlot(self, points): pass def histogramPlot(self, points, bin = 5.): pass def barPlot(points, width =2.):pass ########MOUSE AND KEYBOARDS EVENTS########################################################################### def wheelEvent(self, event): if event.delta() >0: self.zoom_ -= .05 else: self.zoom_ += .05 glMatrixMode(GL_PROJECTION) glLoadIdentity() gluOrtho2D(-self.corner_*self.zoom_, self.corner_*self.zoom_, -self.corner_*self.zoom_, self.corner_*self.zoom_) self.updateGL() glMatrixMode(GL_MODELVIEW) event.accept() def keyPressEvent(self, event): if event.key() == Qt.Key_Plus: self.zoom_ -= .1 glMatrixMode(GL_PROJECTION) glLoadIdentity() gluOrtho2D(-self.corner_*self.zoom_, self.corner_*self.zoom_, -self.corner_*self.zoom_, self.corner_*self.zoom_) glMatrixMode(GL_MODELVIEW) if event.key() == Qt.Key_Minus: self.zoom_ += .1 glMatrixMode(GL_PROJECTION) #// You had GL_MODELVIEW glLoadIdentity() gluOrtho2D(-self.corner_*self.zoom_, self.corner_*self.zoom_, -self.corner_*self.zoom_, self.corner_*self.zoom_) glMatrixMode(GL_MODELVIEW) if event.key() == Qt.Key_Up: self.trans_y_ += 2 self.counter_trans_y +=2 if event.key() == Qt.Key_Down: self.trans_y_ -=2 self.counter_trans_y -=2 if event.key() == Qt.Key_Left: self.trans_x_ -=2 self.counter_trans_x -=2 if event.key() == Qt.Key_Right: self.trans_x_ +=2 self.counter_trans_x +=2 if event.key() == Qt.Key_Z: self.mode= "ZOOMING" if self.counter_trans_x < 0 and self.counter_trans_y < 0: while self.counter_trans_x < 0 and self.counter_trans_y < 0: self.trans_x_ = self.counter_trans_x self.trans_y_ = self.counter_trans_y self.updateGL() self.counter_trans_x += 1 self.counter_trans_y += 1 if self.counter_trans_x > 0 and self.counter_trans_y < 0: while self.counter_trans_x < 0 and self.counter_trans_y < 0: self.trans_x_ = self.counter_trans_x self.trans_y_ = self.counter_trans_y self.updateGL() self.counter_trans_x -= 1 self.counter_trans_y += 1 if self.counter_trans_x < 0 and self.counter_trans_y > 0: while self.counter_trans_x < 0 and self.counter_trans_y < 0: self.trans_x_ = self.counter_trans_x self.trans_y_ = self.counter_trans_y self.updateGL() self.counter_trans_x += 1 self.counter_trans_y -= 1 if self.counter_trans_x < 0 and self.counter_trans_y > 0: while self.counter_trans_x < 0 and self.counter_trans_y < 0: self.trans_x_ = self.counter_trans_x self.trans_y_ = self.counter_trans_y self.updateGL() self.counter_trans_x -= 1 self.counter_trans_y -= 1 if self.zoom_ != 1.5: self.zoom = 1.5 #self.updateGL() self.updateGL() self.resetTranslations() def mousePressEvent(self, event): if self.mode == "ZOOMING": self.mode = "None" self.computeSelection() else: self.lastpos = QPoint(event.pos()) self.setCursor(QCursor(Qt.ClosedHandCursor)) #if event.buttons() == Qt.RightButton: # self.mode = "PANNING" def computeSelection(self): print "selected" def mouseMoveEvent(self, event): dx = event.x() - self.lastpos.x() dy = event.y() - self.lastpos.y() if self.mode == "ZOOMING": font = QFont("Typewriter") self.renderText(-self.corner_ -30.0, self.corner_, 0., "ZOOMING MODE ACTIVATED", font) self.updateGL() glColor(0., 0., 1., .5) XMAX = 900.; XMIN = 180. pointer_x = (self.lastpos.x()*200.)/XMAX norm_dx = (dx*200.)/XMAX """ if pointer_x > 100. or pointer_x < 100. \ or norm_dx >100. or norm_dx<-100.: event.ignore() """ glBegin(GL_QUADS) glVertex2d(pointer_x, -100.) glVertex2d(pointer_x+ norm_dx, -100.) glVertex2d(pointer_x+ norm_dx, 100.) glVertex2d(pointer_x, 100.) glEnd() self.updateGL()#update for seeing the rectangle mapping = self.mapFromGlobal cursorPos = self.inGLCoordinate(mapping(self.cursor().pos())) QToolTip.showText(self.cursor().pos(), "x:"+str(cursorPos.x())+ \ ", y:"+str(cursorPos.y()) ) if self.mode == "None": if event.buttons()== Qt.LeftButton: self.trans_y_ -= dy/5 self.counter_trans_y -= dy/5 self.trans_x_ += dx/5 self.counter_trans_x += dx/5 self.lastpos = QPoint(event.pos()) self.glDraw() self.resetTranslations() def mouseReleaseEvent(self, event): self.setCursor(QCursor(Qt.ArrowCursor))