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()
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)
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 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 )
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 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 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 initializeGL(self, gls): self.gls = gls # Basic solid-color program self.prog = self.gls.shader_cache.get("vert2", "frag1") self.mat_loc = GL.glGetUniformLocation(self.prog, "mat") self.col_loc = GL.glGetUniformLocation(self.prog, "color") # Build a VBO for rendering square "drag-handles" self.vbo_handles_ar = numpy.ndarray(4, dtype=[("vertex", numpy.float32, 2)]) self.vbo_handles_ar["vertex"] = numpy.array(corners) * HANDLE_HALF_SIZE self.vbo_handles = VBO(self.vbo_handles_ar, GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER) self.vao_handles = VAO() with self.vbo_handles, self.vao_handles: vbobind(self.prog, self.vbo_handles_ar.dtype, "vertex").assign() # Build a VBO/VAO for the perimeter # We don't initialize it here because it is updated every render # 4 verticies for outside perimeter # 6 verticies for each dim self.vbo_per_dim_ar = numpy.zeros(16, dtype=[("vertex", numpy.float32, 2)]) self.vbo_per_dim = VBO(self.vbo_per_dim_ar, GL.GL_DYNAMIC_DRAW, GL.GL_ARRAY_BUFFER) self.vao_per_dim = VAO() with self.vao_per_dim, self.vbo_per_dim: vbobind(self.prog, self.vbo_per_dim_ar.dtype, "vertex").assign()
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 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 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 __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 __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 __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 __init__(self): self.face = None self.textures = dict() # compile rendering program self.renderProgram = GLProgram(_textVertexShaderSource, _textFragmentShaderSource) self.renderProgram.compile_and_link() # make projection uniform self.projectionUniform = GLUniform(self.renderProgram.get_program_id(), 'projection', 'mat4f') self.textColorUniform = GLUniform(self.renderProgram.get_program_id(), 'textColor', 'vec3f') self.textureSizeUniform = GLUniform( self.renderProgram.get_program_id(), 'textureSize', 'vec2f') # create rendering buffer self.vbo = VBO(_get_rendering_buffer(0, 0, 0, 0)) self.vbo.create_buffers() self.vboId = glGenBuffers(1) # initialize VAO self.vao = glGenVertexArrays(1) glBindVertexArray(self.vao) glBindBuffer(GL_ARRAY_BUFFER, self.vboId) self.vbo.bind() self.vbo.copy_data() glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(0)) glEnableVertexAttribArray(0) glVertexAttribPointer( 1, 2, GL_FLOAT, GL_FALSE, 5 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(3 * ctypes.sizeof(ctypes.c_float))) glEnableVertexAttribArray(1) # self.vbo.unbind() glBindVertexArray(0) self.zNear = -1.0 self.zFar = 1.0
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))
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)
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)
def __init__(self, data, attributes, segment, bufferSize, target=gl.GL_ARRAY_BUFFER, usage=gl.GL_STATIC_DRAW): self.target = target self.usage = usage if data==None or data.size==0: self.bind = doNothing self.bindAttributes = doNothing self.unbind = doNothing return GLVBO.__init__(self, data=data, usage=usage, target=target, size=bufferSize) self.segment = segment # GLVertexAttribute instances self.attributes = [] # queued slices self.slices = [] self.DUMMY = False # remember attribute locations in the shader for att in attributes: att.location = gl.glGetAttribLocation(segment.shaderProgram, att.name) if att.location!=-1: self.attributes.append(att) else: print "WARNING: cannot find attribute %s in shader" % att.name
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 initializeGL(self, gls: 'GLShared') -> None: self.gls = gls # zap the cached text on GL reinitialize (VBO handles / etc are likely invalid) self.textCached = {} # basic solid-color shader self.prog = gls.shader_cache.get("vert2", "frag1") # Construct a VBO containing all the points we need for rendering dtype = numpy.dtype([("vertex", numpy.float32, 2)]) points = numpy.ndarray((16, ), dtype=dtype) # keypoint display: edge half-dimension in pixels self.d1 = d1 = 20 # keypoint display: text-area "flag" height in pixels th = 16 # keypoint display: right-edge offset of text flag in pixels tw = 6 points["vertex"] = [ # Lines making up keypoint cross (rendered with GL_LINES) (-d1, -d1), (-d1, d1), (-d1, d1), (d1, d1), (d1, d1), (d1, -d1), (d1, -d1), (-d1, -d1), (0, -d1), (0, d1), (-d1, 0), (d1, 0), # flag (rendered with GL_TRIANGLE_STRIP) (-d1, -d1), (-d1, -d1 - th), (-tw, -d1), (-tw, -d1 - th) ] # Pack it all into a VBO self.handle_vbo = VBO(points, GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER) # and bind the program for rendering self.handle_vao = VAO() with self.handle_vao, self.handle_vbo: VBOBind(self.prog.program, dtype, "vertex").assign()
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)
def initGL(self, gls: 'GLShared') -> None: self._tex = Texture() # Setup the basic texture parameters with self._tex.on(GL.GL_TEXTURE_2D): GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST) GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR) GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE) GL.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE) # numpy packs data tightly, whereas the openGL default is 4-byte-aligned # fix line alignment to 1 byte so odd-sized textures load right GL.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1) # Download the data to the buffer. cv2 stores data in BGR format GL.glTexImage2D( GL.GL_TEXTURE_2D, 0, GL.GL_RGB, self.im.shape[1], self.im.shape[0], 0, GL.GL_BGR, GL.GL_UNSIGNED_BYTE, self.im.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8))) self.prog = gls.shader_cache.get("image_vert", "image_frag") ar = numpy.ndarray( (4, ), dtype=[("vertex", numpy.float32, 2), ("texpos", numpy.float32, 2)]) # type: ignore sca = max(self.im.shape[0], self.im.shape[1]) x = self.im.shape[1] / float(sca) y = self.im.shape[0] / float(sca) ar["vertex"] = [(-x, -y), (-x, y), (x, -y), (x, y)] ar["texpos"] = [(0, 0), (0, 1), (1, 0), (1, 1)] self.b1 = VBOBind(self.prog.program, ar.dtype, "vertex") self.b2 = VBOBind(self.prog.program, ar.dtype, "texpos") self.vbo = VBO(ar, GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER) self.mat_loc = GL.glGetUniformLocation(self.prog.program, "mat") self.tex1_loc = GL.glGetUniformLocation(self.prog.program, "tex1") self.vao = VAO() with self.vbo, self.vao: self.b1.assign() self.b2.assign()
def _prepareNormals(self, canvas): """Prepare the normals buffer object for the actor. The normals buffer object depends on the renderer settings: lighting, avgnormals """ #if renderer.canvas.settings.lighting: if True: if canvas.settings.avgnormals: normals = self.b_avgnormals else: normals = self.b_normals # Normals are always full fcoords size #print("SIZE OF NORMALS: %s; COORDS: %s" % (normals.size,self.fcoords.size)) self.nbo = VBO(normals)
class BackgroundGridRender: def __init__(self): self.__width = 0 self.__height = 0 self.__vbo_dtype = numpy.dtype([("vertex", numpy.float32, 2)]) def initializeGL(self, gls, width, height): # Get shader self.__shader = gls.shader_cache.get("basic_fill_vert", "basic_fill_frag") self.__vbo = VBO(numpy.ndarray(0, dtype=self.__vbo_dtype), GL.GL_STATIC_DRAW) self.__vao = VAO() # Create array of lines sufficient to fill screen self.resize(width, height) def __get_vbo_data(self): va = VA_xy(1024) # Unit steps from -n/2..n/2 along X axis. Y lines from -1 to +1 for i in range(0, self.n_steps * 2, 2): i_ = i - int(self.n_steps / 2) va.add_line(i_, -1, i_, 1) return va def resize(self, width, height): self.n_steps = max(self.__width, self.__height) // MIN_PIXEL_SPACE self.__vbo.set_array(self.__get_vbo_data()) def set_grid(self, step): pass def draw(self, transform_matrix): pass
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)))
def addHighlightPoints(self, sel=None): """Add a highlight for the selected points. Default is all.""" self.removeHighlight() vbo = VBO(self.object.points()) self._highlight = Drawable(self, vbo=vbo, subelems=sel.reshape(-1, 1), name=self.name + "_highlight", linewidth=10, lighting=False, color=np.array(colors.yellow), opak=True, pointsize=10, offset=1.0) # Put at the front to make visible self.drawable.insert(0, self._highlight)
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 compile_VBO(self, force=False): "Compiles the verticies of all faces into a VBO and saves the ref." if self._VBO_is_compiled and not force: return vbos = [] try: self._VBO_format = self.shapes[0].compile_VBO(force=True) except IndexError: raise ValueError("Shape3D tried to compile to VBO, but it didn't\ have any shapes.") for s in self.shapes: fmt = s.compile_VBO() if fmt != self._VBO_format and fmt is not None: # TODO figure out a good way of filling in the blanks? raise ValueError("While compiling a Shape3D to VBO, a Shape2D\ format mismatched with the format for the other shapes.") vbos.append(s._VBO) self._VBO = VBO(np.concatenate(vbos)) self._VBO_is_compiled = True
def displayfunc_gles2(display, surface, ctx): from OpenGL import GLES2 as GL from OpenGL.GLES2 import shaders eglMakeCurrent( display, surface, surface, ctx ) GL.glClearColor( 1,0,0, 0 ) GL.glClear( GL.GL_COLOR_BUFFER_BIT|GL.GL_DEPTH_BUFFER_BIT ) global shader,vbo,position_location,stride if shader is None: shader = shaders.compileProgram( shaders.compileShader( '''#version 130 attribute vec3 position; void main() { gl_Position = vec4( position, 0 ); }''', type=GL_VERTEX_SHADER), shaders.compileShader( '''#version 130 void main() { gl_FragColor = vec4( 1,0,0,0 ); }''', type=GL_FRAGMENT_SHADER), ) vbo = VBO( array([ (0,1,0), (1,-1,0), (-1,-1,0), ],dtype='f')) position_location = GL.glGetAttribLocation( self.shader, 'position' ) stride = 3*4 with vbo: with shader: GL.glEnableVertexAttribArray( position_location ) stride = 3*4 GL.glVertexAttribPointer( position_location, 3, GL_FLOAT,False, stride, self.vbo ) GL.glDrawArrays( GL.GL_TRIANGLES, 0, 3 ) eglSwapBuffers( display, surface )
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)
def sphere(radius=0.5, sectors=32, rings=16): """ Generate a sphere :param radius: Radius or the sphere :param rings: number or horizontal rings :param sectors: number of vertical segments :return: VAO containing the sphere """ R = 1.0 / (rings - 1) S = 1.0 / (sectors - 1) vertices = [0] * (rings * sectors * 3) normals = [0] * (rings * sectors * 3) uvs = [0] * (rings * sectors * 2) v, n, t = 0, 0, 0 for r in range(rings): for s in range(sectors): y = math.sin(-math.pi / 2 + math.pi * r * R) x = math.cos(2 * math.pi * s * S) * math.sin(math.pi * r * R) z = math.sin(2 * math.pi * s * S) * math.sin(math.pi * r * R) uvs[t] = s * S uvs[t + 1] = r * R vertices[v] = x * radius vertices[v + 1] = y * radius vertices[v + 2] = z * radius normals[n] = x normals[n + 1] = y normals[n + 2] = z t += 2 v += 3 n += 3 indices = [0] * rings * sectors * 6 i = 0 for r in range(rings - 1): for s in range(sectors - 1): indices[i] = r * sectors + s indices[i + 1] = (r + 1) * sectors + (s + 1) indices[i + 2] = r * sectors + (s + 1) indices[i + 3] = r * sectors + s indices[i + 4] = (r + 1) * sectors + s indices[i + 5] = (r + 1) * sectors + (s + 1) i += 6 vbo_vertices = VBO(numpy.array(vertices, dtype=numpy.float32)) vbo_normals = VBO(numpy.array(normals, dtype=numpy.float32)) vbo_uvs = VBO(numpy.array(uvs, dtype=numpy.float32)) vbo_element = VBO(numpy.array(indices, dtype=numpy.uint32), target="GL_ELEMENT_ARRAY_BUFFER") vao = VAO("sphere") # VBOs vao.add_array_buffer(GL.GL_FLOAT, vbo_vertices) vao.add_array_buffer(GL.GL_FLOAT, vbo_normals) vao.add_array_buffer(GL.GL_FLOAT, vbo_uvs) # Mapping vao.map_buffer(vbo_vertices, "in_position", 3) vao.map_buffer(vbo_normals, "in_normal", 3) vao.map_buffer(vbo_uvs, "in_uv", 2) # Index vao.set_element_buffer(GL.GL_UNSIGNED_INT, vbo_element) vao.build() return vao
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 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 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 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 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 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
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))
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 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 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)
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()
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 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 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)
glEnable(GL_DEBUG_OUTPUT) glDebugMessageCallback(GLDEBUGPROC(debug_message_callback), None) # 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) 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)
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
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()
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 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 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())
class RectAlignmentControllerView(BaseToolController, GenModel): changed = QtCore.Signal() def __init__(self, parent, model): super(RectAlignmentControllerView, self).__init__() self._parent = parent self.model_overall = model self.model = model.ra self.__init_interaction() self.gls = None self.active = False idx_handle_sel = mdlacc(None) idx_handle_hover = mdlacc(None) #sel_mode = mdlacc(SEL_MODE_NONE) behave_mode = mdlacc(MODE_NONE) ghost_handle = mdlacc(None) def change(self): self.changed.emit() def __init_interaction(self): # Selection / drag handling self.idx_handle_sel = None #self.sel_mode = SEL_MODE_NONE self.behave_mode = MODE_NONE # ghost handle for showing placement self.ghost_handle = None def initialize(self): self.__init_interaction() self.active = True def finalize(self): self.active = False def initializeGL(self, gls): self.gls = gls # Basic solid-color program self.prog = self.gls.shader_cache.get("vert2", "frag1") self.mat_loc = GL.glGetUniformLocation(self.prog, "mat") self.col_loc = GL.glGetUniformLocation(self.prog, "color") # Build a VBO for rendering square "drag-handles" self.vbo_handles_ar = numpy.ndarray(4, dtype=[("vertex", numpy.float32, 2)]) self.vbo_handles_ar["vertex"] = numpy.array(corners) * HANDLE_HALF_SIZE self.vbo_handles = VBO(self.vbo_handles_ar, GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER) self.vao_handles = VAO() with self.vbo_handles, self.vao_handles: vbobind(self.prog, self.vbo_handles_ar.dtype, "vertex").assign() # Build a VBO/VAO for the perimeter # We don't initialize it here because it is updated every render # 4 verticies for outside perimeter # 6 verticies for each dim self.vbo_per_dim_ar = numpy.zeros(16, dtype=[("vertex", numpy.float32, 2)]) self.vbo_per_dim = VBO(self.vbo_per_dim_ar, GL.GL_DYNAMIC_DRAW, GL.GL_ARRAY_BUFFER) self.vao_per_dim = VAO() with self.vao_per_dim, self.vbo_per_dim: vbobind(self.prog, self.vbo_per_dim_ar.dtype, "vertex").assign() def im2V(self, pt): """Translate Image coordinates to viewport coordinates""" if self.model_overall.view_mode: ph = projectPoint(self.model.image_matrix, pt) return self.viewState.tfW2V(ph) else: return self.viewState.tfW2V(pt) def V2im(self, pt): """ Translate viewport coordinates to image coordinates :param pt: :return: """ world = self.viewState.tfV2W(pt) if self.model_overall.view_mode: inv = numpy.linalg.inv(self.model.image_matrix) return projectPoint(inv, world) else: return Vec2(world) #inv = numpy.linalg.inv(self.model.image_matrix) #return Vec2(inv.dot(pt)[:2]) def gen_dim(self, idx, always_above = True): """ Generate rendering data for the dimension-lines :param idx: :return: """ a = self.im2V(self.model.dim_handles[0 + idx]) b = self.im2V(self.model.dim_handles[1 + idx]) d = b-a delta = (b-a).norm() normal = Point2(rotate(math.pi / 2)[:2,:2].dot(delta)) if always_above: if numpy.cross(Vec2(1,0), normal) > 0: normal = -normal res = numpy.array([ a + normal * 8, a + normal * 20, a + normal * 15, b + normal * 15, b + normal * 8, b + normal * 20, ]) return res def render(self, vs): self.viewState = vs disabled = not self.active or self.model_overall.view_mode # Perimeter is defined by the first 4 handles self.vbo_per_dim_ar["vertex"][:4] = [self.im2V(pt) for pt in self.model.align_handles[:4]] # Generate the dimension lines. For ease of use, we always draw the dim-lines above when dims are manual # or below when dims are unlocked self.vbo_per_dim_ar["vertex"][4:10] = self.gen_dim(0, not self.model.dims_locked) self.vbo_per_dim_ar["vertex"][10:16] = self.gen_dim(2, not self.model.dims_locked) self.vbo_per_dim.set_array(self.vbo_per_dim_ar) # Ugh..... PyOpenGL isn't smart enough to bind the data when it needs to be copied with self.vbo_per_dim: self.vbo_per_dim.copy_data() GL.glDisable(GL.GL_BLEND) # ... and draw the perimeter with self.vao_per_dim, self.prog: GL.glUniformMatrix3fv(self.mat_loc, 1, True, self.viewState.glWMatrix.astype(numpy.float32)) # Draw the outer perimeter if disabled: GL.glUniform4f(self.col_loc, 0.8, 0.8, 0.8, 1) else: GL.glUniform4f(self.col_loc, 0.8, 0.8, 0, 1) GL.glDrawArrays(GL.GL_LINE_LOOP, 0, 4) # Draw the dimensions GL.glUniform4f(self.col_loc, 0.8, 0.0, 0.0, 1) GL.glDrawArrays(GL.GL_LINES, 4, 6) GL.glUniform4f(self.col_loc, 0.0, 0.0, 0.8, 1) GL.glDrawArrays(GL.GL_LINES, 10, 6) if disabled: return # Now draw a handle at each corner with self.vao_handles, self.prog: for n, i in enumerate(self.model.align_handles): # skip nonexistent handles if i is None: continue is_anchor = IDX_IS_ANCHOR(n) corner_pos = self.im2V(i) if disabled: color = [0.8, 0.8, 0.8, 1] elif self.idx_handle_sel == n: color = [1, 1, 1, 1] elif self.idx_handle_hover == n: color = [1, 1, 0, 1] else: color = [0.8, 0.8, 0, 0.5] self.render_handle(corner_pos, color, is_anchor, True) if self.idx_handle_sel == n: self.render_handle(corner_pos, [0,0,0,1], is_anchor, False) if self.ghost_handle is not None: self.render_handle(self.ghost_handle,[0.8, 0.8, 0, 0.5], True) if not self.model.dims_locked: for n, i in enumerate(self.model.dim_handles): handle_pos = self.im2V(i) if n == self.idx_handle_sel: color = [1, 1, 1, 1] if n < 2: color = [0.8, 0.0, 0.0, 1] else: color = [0.0, 0.0, 0.8, 1] self.render_handle(handle_pos, color, False, True) if self.idx_handle_sel == n: self.render_handle(corner_pos, [0,0,0,1], is_anchor, False) def render_handle(self, position, color, diagonal=False, filled=False): if diagonal: r = rotate(math.pi/4) else: r = numpy.identity(3) m = self.viewState.glWMatrix.dot(translate(*position).dot(r)) GL.glUniformMatrix3fv(self.mat_loc, 1, True, m.astype(numpy.float32)) GL.glUniform4f(self.col_loc, *color) GL.glDrawArrays(GL.GL_TRIANGLE_FAN if filled else GL.GL_LINE_LOOP, 0, 4) def get_handle_for_mouse(self, pos): for n, handle in enumerate(self.model.all_handles()): if handle is None: continue # get the pix-wise BBOX of the handle p = self.im2V(handle) r = QtCore.QRect(p[0], p[1], 0, 0) r.adjust(-HANDLE_HALF_SIZE, -HANDLE_HALF_SIZE, HANDLE_HALF_SIZE, HANDLE_HALF_SIZE) # If event inside the bbox if r.contains(pos): return n return None def get_line_query_for_mouse(self, pos): for n, (p1, p2) in enumerate(self.model.line_iter()): p1_v = self.im2V(p1) p2_v = self.im2V(p2) p, d = project_point_line(pos, p1_v, p2_v) if p is not None and d < HANDLE_HALF_SIZE: return n, p return None, None def mousePressEvent(self, event): disabled = not self.active or self.model_overall.view_mode if disabled: return False handle = self.get_handle_for_mouse(event.pos()) if event.button() == QtCore.Qt.LeftButton and event.modifiers() & ADD_MODIFIER: idx, p = self.get_line_query_for_mouse(event.pos()) if idx is not None: anchors = self.model.get_anchors(idx) if len(anchors) < 2: p = self.V2im(p) idx = new_anchor_index(self.model, idx) cmd = cmd_set_handle_position(self.model, idx, p) self._parent.undoStack.push(cmd) self.idx_handle_sel = idx self.idx_handle_hover = None elif event.button() == QtCore.Qt.LeftButton and event.modifiers() & DEL_MODIFIER and ( handle is not None and handle >= 4): cmd = cmd_set_handle_position(self.model, handle, None) self._parent.undoStack.push(cmd) #self.model.set_handle(handle, None) self.idx_handle_sel = None self.idx_handle_hover = None elif event.button() == QtCore.Qt.LeftButton: self.idx_handle_sel = handle self.idx_handle_hover = None if handle is not None: self.behave_mode = MODE_DRAGGING else: return False return True def mouseReleaseEvent(self, event): disabled = not self.active or self.model_overall.view_mode if disabled: return False if event.button() == QtCore.Qt.LeftButton and self.behave_mode == MODE_DRAGGING: self.behave_mode = MODE_NONE else: return False return True def mouseMoveEvent(self, event): disabled = not self.active or self.model_overall.view_mode if disabled: return False needs_update = False idx = self.get_handle_for_mouse(event.pos()) if self.ghost_handle is not None: self.ghost_handle = None if self.behave_mode == MODE_NONE: if idx is not None: self.idx_handle_hover = idx else: self.idx_handle_hover = None if event.modifiers() & ADD_MODIFIER: line_idx, pos = self.get_line_query_for_mouse(event.pos()) if line_idx is not None: self.ghost_handle = pos elif self.behave_mode == MODE_DRAGGING: w_pos = self.V2im(Vec2(event.pos())) cmd = cmd_set_handle_position(self.model, self.idx_handle_sel, w_pos, merge=True) self._parent.undoStack.push(cmd) #self.model.set_handle(self.idx_handle_sel, w_pos) return False def focusOutEvent(self, evt): self.idx_handle_sel = None def keyPressEvent(self, evt): disabled = not self.active or self.model_overall.view_mode if disabled: return False if evt.key() == QtCore.Qt.Key_Escape: self.idx_handle_sel = None elif self.idx_handle_sel is not None: if evt.key() in (QtCore.Qt.Key_Delete, QtCore.Qt.Key_Backspace) and IDX_IS_ANCHOR(self.idx_handle_sel): cmd = cmd_set_handle_position(self.model, self.idx_handle_sel, None) self._parent.undoStack.push(cmd) #self.model.set_handle(self.idx_handle_sel, None) self.idx_handle_sel = None # Basic 1-px nudging elif evt.key() in (QtCore.Qt.Key_Left, QtCore.Qt.Key_Right, QtCore.Qt.Key_Up, QtCore.Qt.Key_Down): nudge = { QtCore.Qt.Key_Left: (-1, 0), QtCore.Qt.Key_Right: ( 1, 0), QtCore.Qt.Key_Up: ( 0, -1), QtCore.Qt.Key_Down: ( 0, 1), }[evt.key()] current = Vec2(self.im2V(self.model.align_handles[self.idx_handle_sel])) viewspace = self.V2im(current + nudge) cmd = cmd_set_handle_position(self.model, self.idx_handle_sel, viewspace) self._parent.undoStack.push(cmd) #self.model.set_handle(self.idx_handle_sel, viewspace) self.ghost_handle = None def next_prev_child(self, next): all_handles = self.model.all_handles() step = -1 if next: step = 1 if self.behave_mode == MODE_DRAGGING: return True else: idx = self.idx_handle_sel if idx == None: return False while 1: idx = (idx + step) % len(all_handles) if all_handles[idx] is not None: self.idx_handle_sel = idx return True
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()
class RectAlignmentControllerView(BaseToolController): changed = QtCore.Signal() def __init__(self, parent, model: 'pcbre.ui.dialogs.layeralignmentdialog.dialog.AlignmentViewModel'): super(RectAlignmentControllerView, self).__init__() self._parent = parent self.model_overall = model self.model = model.ra self.__idx_handle_sel: Optional[int] = None self.__idx_handle_hover: Optional[int] = None self.__behave_mode = DM.NONE self.__ghost_handle = None self.__init_interaction() self.gls: Optional[Any] = None self.active = False def change(self) -> None: self.changed.emit() @property def tool_actions(self) -> 'Sequence[ToolActionDescription]': return g_ACTIONS def __init_interaction(self) -> None: # Selection / drag handling self.__idx_handle_sel = None self.__behave_mode = DM.NONE # ghost handle for showing placement self.__ghost_handle = None def initialize(self) -> None: self.__init_interaction() self.active = True def finalize(self) -> None: self.active = False def initializeGL(self, gls: 'GLShared') -> None: self.gls = gls assert self.gls is not None # Basic solid-color program self.prog = self.gls.shader_cache.get("vert2", "frag1") self.mat_loc = GL.glGetUniformLocation(self.prog.program, "mat") self.col_loc = GL.glGetUniformLocation(self.prog.program, "color") # Build a VBO for rendering square "drag-handles" self.vbo_handles_ar = numpy.ndarray((4, ), dtype=[("vertex", numpy.float32, 2)]) self.vbo_handles_ar["vertex"] = numpy.array(corners) * HANDLE_HALF_SIZE self.vbo_handles = VBO(self.vbo_handles_ar, GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER) self.vao_handles = VAO() with self.vbo_handles, self.vao_handles: VBOBind(self.prog.program, self.vbo_handles_ar.dtype, "vertex").assign() # Build a VBO/VAO for the perimeter # We don't initialize it here because it is updated every render # 4 verticies for outside perimeter # 6 verticies for each dim self.vbo_per_dim_ar = numpy.zeros(16, dtype=[("vertex", numpy.float32, 2)]) self.vbo_per_dim = VBO(self.vbo_per_dim_ar, GL.GL_DYNAMIC_DRAW, GL.GL_ARRAY_BUFFER) self.vao_per_dim = VAO() with self.vao_per_dim, self.vbo_per_dim: VBOBind(self.prog.program, self.vbo_per_dim_ar.dtype, "vertex").assign() def im2V(self, pt: Vec2) -> Vec2: """Translate Image coordinates to viewport coordinates""" if self.model_overall.view_mode.is_aligned(): ph = project_point(self.model.image_matrix, pt) return self.viewPort.tfW2V(ph) else: return self.viewPort.tfW2V(pt) def V2im(self, pt: Vec2) -> Vec2: """ Translate viewport coordinates to image coordinates :param pt: :return: """ world = self.viewPort.tfV2W(pt) if self.model_overall.view_mode.is_aligned(): inv = numpy.linalg.inv(self.model.image_matrix) return project_point(inv, world) else: return world # inv = numpy.linalg.inv(self.model.image_matrix) # return Vec2(inv.dot(pt)[:2]) def gen_dim(self, idx: int, always_above: bool = True) -> 'npt.NDArray[numpy.float64]': """ Generate rendering data for the dimension-lines :param idx: :return: """ a = self.im2V(self.model.dim_handles[0 + idx]) b = self.im2V(self.model.dim_handles[1 + idx]) d = b - a delta = (b - a).norm() normal = Vec2.from_mat(rotate(math.pi / 2)[:2, :2].dot(delta)) if always_above: if numpy.cross(Vec2(1, 0), normal) > 0: normal = -normal res = numpy.array([ a + normal * 8, a + normal * 20, a + normal * 15, b + normal * 15, b + normal * 8, b + normal * 20, ], dtype=numpy.float64) return res @property def disabled(self) -> bool: # When the we're showing the aligned view; or if we are using keypoint align # don't render any handles return not self.active or self.model_overall.view_mode.is_aligned() def render(self, viewPort: 'ViewPort') -> None: self.viewPort = viewPort # Perimeter is defined by the first 4 handles self.vbo_per_dim_ar["vertex"][:4] = [self.im2V(pt) for pt in self.model.align_handles[:4]] # Generate the dimension lines. For ease of use, we always draw the dim-lines above when dims are manual # or below when dims are unlocked self.vbo_per_dim_ar["vertex"][4:10] = self.gen_dim(0, not self.model.dims_locked) self.vbo_per_dim_ar["vertex"][10:16] = self.gen_dim(2, not self.model.dims_locked) self.vbo_per_dim.set_array(self.vbo_per_dim_ar) # Ugh..... PyOpenGL isn't smart enough to bind the data when it needs to be copied with self.vbo_per_dim: self.vbo_per_dim.copy_data() GL.glDisable(GL.GL_BLEND) # ... and draw the perimeter with self.vao_per_dim, self.prog.program: GL.glUniformMatrix3fv(self.mat_loc, 1, True, self.viewPort.glWMatrix.astype(numpy.float32)) # Draw the outer perimeter if self.disabled: GL.glUniform4f(self.col_loc, 0.8, 0.8, 0.8, 1) else: GL.glUniform4f(self.col_loc, 0.8, 0.8, 0, 1) GL.glDrawArrays(GL.GL_LINE_LOOP, 0, 4) # Draw the dimensions GL.glUniform4f(self.col_loc, 0.8, 0.0, 0.0, 1) GL.glDrawArrays(GL.GL_LINES, 4, 6) GL.glUniform4f(self.col_loc, 0.0, 0.0, 0.8, 1) GL.glDrawArrays(GL.GL_LINES, 10, 6) if self.disabled: return # Now draw a handle at each corner with self.vao_handles, self.prog.program: for n, i in enumerate(self.model.align_handles): # skip nonexistent handles if i is None: continue is_anchor = IDX_IS_LINE(n) corner_pos = self.im2V(i) if self.disabled: color = [0.8, 0.8, 0.8, 1] elif self.__idx_handle_sel == n: color = [1, 1, 1, 1] elif self.__idx_handle_hover == n: color = [1, 1, 0, 1] else: color = [0.8, 0.8, 0, 0.5] self.render_handle(corner_pos, color, is_anchor, True) if self.__idx_handle_sel == n: self.render_handle(corner_pos, [0, 0, 0, 1], is_anchor, False) if self.__ghost_handle is not None: self.render_handle(self.__ghost_handle, [0.8, 0.8, 0, 0.5], True) if not self.model.dims_locked: for n, i in enumerate(self.model.dim_handles): handle_pos = self.im2V(i) if n == self.__idx_handle_sel: color = [1, 1, 1, 1] if n < 2: color = [0.8, 0.0, 0.0, 1] else: color = [0.0, 0.0, 0.8, 1] self.render_handle(handle_pos, color, False, True) if self.__idx_handle_sel == n: self.render_handle(corner_pos, [0, 0, 0, 1], is_anchor, False) def render_handle(self, position: Vec2, color: Sequence[float], diagonal: bool = False, filled: bool = False) -> None: if diagonal: r = rotate(math.pi / 4) else: r = numpy.identity(3) m = self.viewPort.glWMatrix.dot(translate(*position).dot(r)) GL.glUniformMatrix3fv(self.mat_loc, 1, True, m.astype(numpy.float32)) GL.glUniform4f(self.col_loc, *color) GL.glDrawArrays(GL.GL_TRIANGLE_FAN if filled else GL.GL_LINE_LOOP, 0, 4) def get_handle_index_for_mouse(self, pos: Vec2) -> Optional[int]: """ Returns the index of a handle (or None if one isn't present) given a MoveEvent""" for n, handle in enumerate(self.model.all_handles()): if handle is None: continue # get the pix-wise BBOX of the handle p = self.im2V(handle) # Rect encompassing the handle r = Rect.from_center_size(p, HANDLE_HALF_SIZE * 2, HANDLE_HALF_SIZE * 2) # If event inside the bbox if r.point_test(pos) != 0: return n return None def get_line_query_for_mouse(self, pos: Vec2) -> Tuple[Optional[int], Optional[Vec2]]: for n, (p1, p2) in enumerate(self.model.lines()): p1_v = self.im2V(p1) p2_v = self.im2V(p2) p, d = project_point_line(pos, p1_v, p2_v) if d is not None and d < HANDLE_HALF_SIZE: return n, p return None, None def event_add_constraint(self, event: 'ToolActionEvent') -> None: idx, p = self.get_line_query_for_mouse(event.cursor_pos) if idx is not None: anchors = self.model.get_anchors(idx) if len(anchors) < 2: p = self.V2im(p) idx = new_anchor_index(self.model, idx) cmd = cmd_set_handle_position(self.model, idx, p) self._parent.undoStack.push(cmd) self.__idx_handle_sel = idx self.__idx_handle_hover = None def event_remove_constraint(self, event: 'ToolActionEvent') -> None: handle = self.get_handle_index_for_mouse(event.cursor_pos) if handle is not None and handle >= 4: cmd = cmd_set_handle_position(self.model, handle, None) self._parent.undoStack.push(cmd) self.__idx_handle_sel = None self.__idx_handle_hover = None def event_select(self, event: 'ToolActionEvent') -> None: handle = self.get_handle_index_for_mouse(event.cursor_pos) self.__idx_handle_sel = handle self.__idx_handle_hover = None if handle is not None: self.__behave_mode = DM.DRAGGING def event_release(self, event: 'ToolActionEvent') -> None: if self.__behave_mode == DM.DRAGGING: self.__behave_mode = DM.NONE def tool_event(self, event: 'ToolActionEvent') -> None: if self.disabled: return if event.code == EventCode.Select: self.event_select(event) elif event.code == EventCode.Release: self.event_release(event) elif event.code == EventCode.AddToLine: self.event_add_constraint(event) elif event.code == EventCode.RemoveFromLine: self.event_remove_constraint(event) def mouseMoveEvent(self, event: 'MoveEvent') -> None: if self.disabled: return needs_update = False idx = self.get_handle_index_for_mouse(event.cursor_pos) if self.__ghost_handle is not None: self.__ghost_handle = None if self.__behave_mode == DM.NONE: if idx is not None: self.__idx_handle_hover = idx else: self.__idx_handle_hover = None # if event.modifiers() & ADD_MODIFIER: # line_idx, pos = self.get_line_query_for_mouse(event.cursor_pos) # if line_idx is not None: # self.__ghost_handle = pos elif self.__behave_mode == DM.DRAGGING: w_pos = self.V2im(event.cursor_pos) # print(w_pos, event.world_pos) cmd = cmd_set_handle_position(self.model, self.__idx_handle_sel, w_pos, merge=True) self._parent.undoStack.push(cmd) def focusOutEvent(self, evt: QtCore.QEvent) -> None: self.__idx_handle_sel = None def keyPressEvent(self, evt: QtGui.QKeyEvent) -> bool: if self.disabled: return False if evt.key() == QtCore.Qt.Key_Escape: self.__idx_handle_sel = None elif self.__idx_handle_sel is not None: if evt.key() in (QtCore.Qt.Key_Delete, QtCore.Qt.Key_Backspace) and IDX_IS_LINE(self.__idx_handle_sel): cmd = cmd_set_handle_position(self.model, self.__idx_handle_sel, None) self._parent.undoStack.push(cmd) # self.model.set_handle(self.__idx_handle_sel, None) self.__idx_handle_sel = None # Basic 1-px nudging elif evt.key() in (QtCore.Qt.Key_Left, QtCore.Qt.Key_Right, QtCore.Qt.Key_Up, QtCore.Qt.Key_Down): _nudgetab: Dict[int,Tuple[int, int]] = { QtCore.Qt.Key_Left: (-1, 0), QtCore.Qt.Key_Right: (1, 0), QtCore.Qt.Key_Up: (0, -1), QtCore.Qt.Key_Down: (0, 1), } nudge = _nudgetab[evt.key()] current = self.im2V(self.model.align_handles[self.__idx_handle_sel]) viewspace = self.V2im(current + Vec2.from_mat(nudge)) cmd = cmd_set_handle_position(self.model, self.__idx_handle_sel, viewspace) self._parent.undoStack.push(cmd) # self.model.set_handle(self.__idx_handle_sel, viewspace) self.__ghost_handle = None def next_prev_child(self, next: bool) -> bool: all_handles = self.model.all_handles() step = -1 if next: step = 1 if self.__behave_mode == DM.DRAGGING: return True else: idx = self.__idx_handle_sel if idx is None: return False while 1: idx = (idx + step) % len(all_handles) if all_handles[idx] is not None: self.__idx_handle_sel = idx return True
glEnable(GL_DEBUG_OUTPUT) glDebugMessageCallback(GLDEBUGPROC(debug_message_callback), None) # 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() 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'),