Example #1
0
    def draw(self, uniforms, entities, models, textures=(), lights=(), *args, **kwargs):
        glUseProgram(self)

        # PREPARE SHADER
        glUniformMatrix4fv(  # ctypes.data_as must be here and not at initialization.
            self.uniforms[b'perspective'], 1, GL_TRUE, uniforms.get(b'perspective').ctypes.data_as(POINTER(GLfloat))
        )
        glUniformMatrix4fv(
            self.uniforms[b'view'], 1, GL_TRUE, uniforms.get(b'view').ctypes.data_as(POINTER(GLfloat))
        )

        for i, entity in enumerate(lights):
            glUniform3f(self.uniforms[b'light[' + bytes(str(i), 'utf-8') + b'].position'],  *entity.location)
            glUniform3f(self.uniforms[b'light[' + bytes(str(i), 'utf-8') + b'].color'],     *entity.color)
            glUniform1f(self.uniforms[b'light[' + bytes(str(i), 'utf-8') + b'].constant'],  entity.attenuation[0])
            glUniform1f(self.uniforms[b'light[' + bytes(str(i), 'utf-8') + b'].linear'],    entity.attenuation[1])
            glUniform1f(self.uniforms[b'light[' + bytes(str(i), 'utf-8') + b'].quadratic'], entity.attenuation[1])


        # PREPARE MODELS
        for model_index, texture_mapping in self.entities.items():

            model = models[model_index]
            model.enable()

            # PREPARE TEXTURES
            glActiveTexture(GL_TEXTURE0)
            glActiveTexture(GL_TEXTURE0 + 1)
            for texture_list, entity_list in texture_mapping.items():

                if hasattr(texture_list, '__iter__'):
                    glBindTexture(GL_TEXTURE_2D, textures[0])
                    glUniform1i(self.uniforms[b'diffuse_texture'], 0)
                    glBindTexture(GL_TEXTURE_2D, textures[1])
                    glUniform1i(self.uniforms[b'specular_texture'], 1)

                    # textures[0].enable(slot=0)
                    # textures[1].enable(slot=1)

                else:
                    textures[texture_list].enable(slot=0)
                    glUniform1i(self.uniforms[b'diffuse_texture'], 0)


                # PREPARE ENTITIES
                for entity in entity_list:
                    glUniformMatrix4fv(
                        self.uniforms[b'transformation'], 1, GL_TRUE,
                        entity.get_transformation_matrix().ctypes.data_as(POINTER(GLfloat))
                    )
                    model.draw()

        glBindBuffer(GL_ARRAY_BUFFER, 0)
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)

        glBindTexture(GL_TEXTURE_2D, 0)

        glDisableVertexAttribArray(0)
        glDisableVertexAttribArray(1)
        glDisableVertexAttribArray(2)
Example #2
0
    def draw(self, uniforms, text, quad, textures=(), *args, **kwargs):
        glUseProgram(self)
        glDisable(GL_DEPTH_TEST)


        quad.enable()

        glUniform3f(self.uniforms[b'color'], 0.3, 0.3, 0.5)

        glUniformMatrix3fv(
            self.uniforms[b'transformation'], 1, GL_TRUE,
            text.get_transformation_matrix_2D().ctypes.data_as(POINTER(GLfloat))
        )

        textures.enable(slot=0)
        glUniform1i(self.uniforms[b'font_atlas'], 0)

        quad.draw()

        glBindBuffer(GL_ARRAY_BUFFER, 0)
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)

        glBindTexture(GL_TEXTURE_2D, 0)

        glDisableVertexAttribArray(0)

        glEnable(GL_DEPTH_TEST)
Example #3
0
    def draw(self, uniforms, entities, models, *args, **kwargs):
        glUseProgram(self)

        glUniformMatrix4fv(
            self.uniforms[b'perspective'], 1, GL_TRUE, uniforms.get(b'perspective').ctypes.data_as(POINTER(GLfloat))
        )
        glUniformMatrix4fv(
            self.uniforms[b'view'], 1, GL_TRUE, uniforms.get(b'view').ctypes.data_as(POINTER(GLfloat))
        )

        for model_index, entity_list in entities.items():
            model = models[model_index]

            model.enable()

            for entity in entity_list:
                glUniformMatrix4fv(
                    self.uniforms[b'transformation'], 1, GL_TRUE,
                    entity.get_transformation_matrix().ctypes.data_as(POINTER(GLfloat))
                )
                glUniform3f(self.uniforms[b'color'], *entity.color)

                model.draw()

        glBindBuffer(GL_ARRAY_BUFFER, 0)
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)

        glBindTexture(GL_TEXTURE_2D, 0)


        glDisableVertexAttribArray(0)
Example #4
0
    def draw(self, uniforms, entity, models, textures=(), color=(1.0, 1.0, 1.0), *args, **kwargs):
        glUseProgram(self)
        glDisable(GL_DEPTH_TEST)

        # Draw objects once as invisible to set stencil values.
        glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)  # Don't write any color values to color buffer.
        glStencilMask(0xFF)  # Enable writing.


        model = models[entity.model]

        model.positions.enable()
        model.indices.enable()

        glUniformMatrix4fv(
            self.uniforms[b'perspective'], 1, GL_TRUE, uniforms.get(b'perspective').ctypes.data_as(POINTER(GLfloat))
        )
        glUniformMatrix4fv(
            self.uniforms[b'view'], 1, GL_TRUE, uniforms.get(b'view').ctypes.data_as(POINTER(GLfloat))
        )
        glUniform3f(self.uniforms[b'color'], *color)

        glUniformMatrix4fv(
            self.uniforms[b'transformation'], 1, GL_TRUE,
            entity.get_transformation_matrix().ctypes.data_as(POINTER(GLfloat))
        )

        model.draw()

        # Draw again with larger model.
        glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)

        glStencilFunc(GL_NOTEQUAL, 1, 0xFF)  # Update with 1's where the objects are rendered.
        glStencilMask(0x00)  # Value that AND's the value written to buffer. 0x00 basically disables writing to stencil.

        glUniformMatrix4fv(
            self.uniforms[b'transformation'], 1, GL_TRUE,
            create_transformation_matrix(
                *entity.location, *entity.rotation, *(entity.scale + 0.05)
            ).ctypes.data_as(POINTER(GLfloat))
        )

        model.draw()

        glBindBuffer(GL_ARRAY_BUFFER, 0)
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)

        glDisableVertexAttribArray(0)

        glEnable(GL_DEPTH_TEST)
        glStencilMask(0xFF)
Example #5
0
    def _render_view(self):

        gl.glClearBufferfv(gl.GL_COLOR, 0, (gl.GLfloat * 4)(0.25, 0.25, 0.25,
                                                            1))

        if not self.view:
            return

        w, h, d = self.view.shape
        size = w, h
        window_size = self.get_size()

        ob = render_view(self)

        vm = make_view_matrix(window_size, size, self.zoom, self.offset)
        vm = (gl.GLfloat * 16)(*vm)
        gl.glViewport(0, 0, *window_size)

        self._update_border(self.view.shape)
        with self.border_vao, self.line_program:
            gl.glUniformMatrix4fv(0, 1, gl.GL_FALSE, vm)
            r, g, b, a = self.drawing.palette.colors[
                0]  # Color 0 is currently hardcoded background
            gl.glUniform3f(1, r / 256, g / 256, b / 256)
            gl.glDrawArrays(gl.GL_TRIANGLE_FAN, 0, 4)

        with self.vao, self.copy_program:
            # Draw the actual drawing
            with ob["color"]:
                gl.glEnable(gl.GL_BLEND)
                gl.glUniformMatrix4fv(0, 1, gl.GL_FALSE,
                                      (gl.GLfloat * 16)(*vm))
                gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)
            self._draw_mouse_cursor()

        with self.border_vao, self.line_program:
            gl.glUniform3f(1, 0., 0., 0.)
            gl.glLineWidth(1)
            gl.glDrawArrays(gl.GL_LINE_LOOP, 0, 4)
Example #6
0
def render_to_texture(in_size, out_size, view_z=None):
    z0, z1 = (0, in_size[2]) if view_z == None else view_z
    vertices = (VERTEX * 6)(((-1, -1), (0, 0)), ((1, -1), (1, 0)),
                            ((1, 1), (1, 1)), ((1, 1), (1, 1)),
                            ((-1, 1), (0, 1)), ((-1, -1), (0, 0)))
    gl.glBindTexture(gl.GL_TEXTURE_3D, rendered_texture)
    gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, framebuffer)
    draw_buffers = (gl.GLenum * 1)(gl.GL_COLOR_ATTACHMENT0)
    gl.glDrawBuffers(1, draw_buffers)
    gl.glViewport(0, 0, out_size[0], out_size[1])
    gl.glUseProgram(render_program)
    loc_depth = gl.glGetUniformLocation(render_program,
                                        ctypes.create_string_buffer(b'depth'))
    loc_texelSize = gl.glGetUniformLocation(
        render_program, ctypes.create_string_buffer(b'texelSize'))
    gl.glUniform3f(loc_texelSize, 1 / in_size[0], 1 / in_size[1],
                   1 / in_size[2])
    gl.glBindBuffer(gl.GL_ARRAY_BUFFER, render_vertexbuffer)
    gl.glBufferData(gl.GL_ARRAY_BUFFER, ctypes.sizeof(vertices), vertices,
                    gl.GL_DYNAMIC_DRAW)
    gl.glBindVertexArray(render_vao)
    gl.glClearColor(0.0, 0.0, 0.0, 0.0)
    for z in range(out_size[2]):
        gl.glFramebufferTexture3D(gl.GL_FRAMEBUFFER, gl.GL_COLOR_ATTACHMENT0,
                                  gl.GL_TEXTURE_3D, rendered_texture, 0, z)
        fbs = gl.glCheckFramebufferStatus(gl.GL_FRAMEBUFFER)
        assert fbs == gl.GL_FRAMEBUFFER_COMPLETE, 'FramebufferStatus is {}'.format(
            fbs)
        gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
        gl.glUniform1f(loc_depth,
                       (z0 + z * (z1 - z0)) / in_size[2] / out_size[2])
        gl.glBindTexture(gl.GL_TEXTURE_3D, input_texture)
        gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)
        if z % 10 == 0:
            gl.glFinish()
            print('\033[K{}/{}'.format(z, out_size[2] - 1), end='\r')
    gl.glFinish()

    gl.glBindVertexArray(0)
Example #7
0
  def __set_uniforms(self):
    # projection matrix (proj)
    proj_loc = gl.glGetUniformLocation(self.__prog.value, "proj")
    proj_matrix = self.__proj_matrix()
    proj_matrix_ptr = ct.cast( \
        ct.pointer(np.ctypeslib.as_ctypes(proj_matrix)),
        ct.POINTER(ct.c_float) )
    gl.glUniformMatrix4fv(proj_loc, 1, gl.GL_TRUE, 
        proj_matrix_ptr)

    # voxel spacing
    voxel_spacing_loc = gl.glGetUniformLocation(self.__prog.value,
        "voxel_spacing")
    gl.glUniform3f(voxel_spacing_loc,
        self.__voxel_spacing[0]*self.__downsample,
        self.__voxel_spacing[1]*self.__downsample,
        self.__voxel_spacing[2]*self.__downsample)

    # voxel size
    voxel_size_loc = gl.glGetUniformLocation(self.__prog.value,
        "voxel_size")
    gl.glUniform3f(voxel_size_loc,
        self.__voxel_size[0]*self.__downsample,
        self.__voxel_size[1]*self.__downsample,
        self.__voxel_size[2]*self.__downsample)

    # data; not technically a "uniform" but a texture
    data_loc = gl.glGetUniformLocation(self.__prog.value,
        "data")
    gl.glUniform1i(data_loc, 0)

    gl.glActiveTexture(gl.GL_TEXTURE0)
    gl.glBindTexture(gl.GL_TEXTURE_BUFFER, self.__data_texture.value)

    # dims
    dims_loc = gl.glGetUniformLocation(self.__prog.value,
        "dims")
    gl.glUniform3i(dims_loc,
        self.__data.shape[0]/self.__downsample,
        self.__data.shape[1]/self.__downsample,
        self.__data.shape[2]/self.__downsample)

    # global_opacity
    global_opacity_loc = gl.glGetUniformLocation(self.__prog.value,
        "global_opacity")
    gl.glUniform1f(global_opacity_loc, self.__opacity)

    # min value
    min_value_loc = gl.glGetUniformLocation(self.__prog.value,
        "min_value")
    gl.glUniform1f(min_value_loc, self.__min_value)

    # saturation value
    saturation_value_loc = gl.glGetUniformLocation(self.__prog.value,
        "saturation_value")
    gl.glUniform1f(saturation_value_loc, self.__saturation_value)

    # downsample
    downsample_loc = gl.glGetUniformLocation(self.__prog.value,
        "downsample")
    gl.glUniform1i(downsample_loc, self.__downsample)
Example #8
0
def render_view(window):
    """ Render the current view to a texture. """

    drawing = window.drawing
    view = window.view

    changed = False

    # Update the overlay with the current stroke
    overlay = view.overlay
    w, h, d = view.shape
    size = w, h
    overlay_texture = _get_overlay_texture(size)
    if overlay.dirty and overlay.lock.acquire(timeout=0.01):
        rect = overlay.dirty
        x0, y0, x1, y1 = rect.box()
        overlay_data = overlay.data[x0:x1, y0:y1].tobytes("F")
        gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT,
                         1)  # Needed for writing 8bit data
        gl.glTextureSubImage2D(overlay_texture.name, 0, *rect.position,
                               *rect.size, gl.GL_RGBA_INTEGER,
                               gl.GL_UNSIGNED_BYTE, overlay_data)
        gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT, 4)
        overlay.dirty = None
        overlay.lock.release()
        changed = True

    # Update the image texture
    data = drawing.data
    drawing_texture = _get_3d_texture(data.shape)
    if drawing.dirty:
        with drawing.lock:
            update_data = data[drawing.dirty].tobytes(order="F")
            sx, sy, sz = drawing.dirty
            drawing.dirty = None
        sw = sx.stop - sx.start
        sh = sy.stop - sy.start
        sd = sz.stop - sz.start
        gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT,
                         1)  # Needed for writing 8bit data
        gl.glTextureSubImage3D(drawing_texture.name, 0, sx.start, sy.start,
                               sz.start, sw, sh, sd, gl.GL_RED_INTEGER,
                               gl.GL_UNSIGNED_BYTE, update_data)
        gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT, 4)
        changed = True

    # Render everything to the offscreen buffer

    # TODO we actually should not have to redraw the offscreen_buffer unless something has changed
    # (e.g. drawing, overlay, palette or cursor)

    offscreen_buffer = _get_offscreen_buffer(size)
    colors = _get_colors(drawing.palette.colors)

    vao = _get_vao()
    draw_program = _get_program()
    empty_texture = _get_empty_texture(size)

    cursor_pos = d - view.index - 1  # TODO why?

    other_layer_alpha = 0.3 if view.show_only_current_layer or view.layer_being_switched else 1.0

    T = _get_transform(view.rotation)

    with vao, offscreen_buffer:

        gl.glEnable(gl.GL_BLEND)
        gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA)
        gl.glViewport(0, 0, w, h)
        gl.glClearBufferfv(gl.GL_COLOR, 0, EMPTY_COLOR)

        with draw_program, drawing_texture:

            gl.glUniformMatrix4fv(0, 1, gl.GL_FALSE, (gl.GLfloat * 16)(*T))
            gl.glUniform3f(1, *view.direction)
            gl.glUniform4fv(5, 256, colors)

            # Draw the layers below the current one
            if cursor_pos < d - 1:
                with empty_texture:
                    gl.glUniform1f(2, other_layer_alpha)
                    gl.glUniform2i(3, cursor_pos + 1, d)
                    gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)

            # Draw the current layer + overlay
            with overlay_texture:
                gl.glUniform1f(2, 1)
                gl.glUniform2i(3, cursor_pos, cursor_pos + 1)
                gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)

            # Draw the layers on top
            if cursor_pos > 0:
                with empty_texture:
                    gl.glUniform1f(2, other_layer_alpha)
                    gl.glUniform2i(3, 0, cursor_pos)
                    gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)

    return offscreen_buffer
Example #9
0
    def on_draw(self):

        # Prevent trying to draw before things have been set up
        if not hasattr(self, "offscreen_buffer"):
            return

        # Model matrix we'll use to position the main model
        suzanne_model_matrix = (Matrix4.new_identity().rotatex(
            -math.pi / 2).rotatez(time()))  # Rotate over time
        plane_model_matrix = Matrix4.new_rotatey(math.pi).translate(0, 0, 2)

        # Render to an offscreen buffer
        with self.offscreen_buffer, self.view_program, \
                enabled(gl.GL_DEPTH_TEST), disabled(gl.GL_CULL_FACE):

            gl.glDepthMask(gl.GL_TRUE)

            w, h = self.size
            aspect = h / w

            # Calculate a view frustum; this is basically our camera.
            near = 5
            far = 15
            width = 2
            height = 2 * aspect
            frustum = (Matrix4.new(near / width, 0, 0, 0, 0, near / height, 0,
                                   0, 0, 0, -(far + near) / (far - near), -1,
                                   0, 0, -2 * far * near / (far - near), 0))

            # The view matrix positions the camera in the scene
            view_matrix = (Matrix4.new_identity().translate(0, 0, -8))

            # Send the matrices to GL
            gl.glUniformMatrix4fv(0, 1, gl.GL_FALSE,
                                  gl_matrix(frustum * view_matrix))
            gl.glUniformMatrix4fv(1, 1, gl.GL_FALSE,
                                  gl_matrix(suzanne_model_matrix))

            gl.glUniform4f(2, 0.3, 0.3, 1,
                           1)  # Set the "color" uniform to blue
            self.suzanne.draw()

            # We'll also draw a simple plane behind the main model
            gl.glUniformMatrix4fv(1, 1, gl.GL_FALSE,
                                  gl_matrix(plane_model_matrix))
            gl.glUniform4f(2, 0.3, 1, 0.3,
                           1)  # Set the "color" uniform to green
            self.plane.draw(mode=gl.GL_TRIANGLE_STRIP)

        # Render shadow buffer
        # Basically the same scene as above, but to a different buffer and from a different view
        with self.shadow_buffer, self.view_program, enabled(
                gl.GL_DEPTH_TEST), disabled(gl.GL_CULL_FACE):
            gl.glDepthMask(gl.GL_TRUE)

            frustum = Matrix4.new_perspective(1, 1, 1, 12)
            view_matrix = (Matrix4.new_identity().translate(
                0, 0, -4).rotatey(0.5).rotatex(0.3))
            light_pos = (view_matrix.inverse() * Point3(0, 0, 0))
            light_view_matrix = frustum * view_matrix
            gl.glUniformMatrix4fv(0, 1, gl.GL_FALSE,
                                  gl_matrix(light_view_matrix))
            gl.glUniformMatrix4fv(1, 1, gl.GL_FALSE,
                                  gl_matrix(suzanne_model_matrix))
            gl.glUniform4f(2, 0.9, 0.3, 0.4, 1)
            self.suzanne.draw()

            gl.glUniformMatrix4fv(1, 1, gl.GL_FALSE,
                                  gl_matrix(plane_model_matrix))
            self.plane.draw(mode=gl.GL_TRIANGLE_STRIP)

        # Now draw the offscreen buffer to another buffer, combining it with the
        # lighting information to get a nice image.
        # Note: This step is pretty pointless here, as we might just draw directly to screen.
        # Just demonstrates how to do it.
        with self.vao, self.offscreen_buffer2, self.lighting_program, disabled(
                gl.GL_CULL_FACE, gl.GL_DEPTH_TEST):
            gl.glUniform3f(0, *light_pos)
            gl.glUniformMatrix4fv(1, 1, gl.GL_FALSE,
                                  gl_matrix(light_view_matrix))
            # Bind some of the offscreen buffer's textures so the shader can read them.
            with self.offscreen_buffer["color"], self.offscreen_buffer["normal"], \
                    self.offscreen_buffer["position"], self.shadow_buffer["depth"]:
                gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)

        # Now render the finished image to the screen
        with self.vao, self.copy_program, disabled(gl.GL_CULL_FACE,
                                                   gl.GL_DEPTH_TEST):
            with self.offscreen_buffer2["color"]:
                gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)
Example #10
0
    def on_draw(self):

        gl.glClearBufferfv(gl.GL_COLOR, 0, BG_COLOR)

        if self.drawing:

            window_size = self.get_pixel_aligned_size()
            w, h = self.drawing.size

            vm = (gl.GLfloat * 16)(*make_view_matrix(
                window_size, self.drawing.size, self.zoom, self.offset))
            offscreen_buffer = render_drawing(self.drawing,
                                              self.highlighted_layer)

            ww, wh = window_size
            gl.glViewport(0, 0, int(ww), int(wh))

            # Draw a background rectangle
            with self.vao, self.copy_program:
                gl.glUniformMatrix4fv(0, 1, gl.GL_FALSE,
                                      (gl.GLfloat * 16)(*vm))
                if self.drawing and self.drawing.grid:
                    with self.get_background_texture(
                            self.drawing.palette.colors[0], 0.9):
                        gw, gh = self.drawing.grid_size
                        gl.glUniform2f(1, w / (gw * 2), h / (gh * 2))
                        gl.glBlendFunc(gl.GL_ONE, gl.GL_ONE_MINUS_SRC_ALPHA)
                        gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)
                else:
                    #gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)
                    # r, g, b, _ = self.drawing.palette.get_color_as_float(self.drawing.palette.colors[0])
                    # gl.glClearColor(r, g, b, 1)
                    # TODO should fill with color 0 here!
                    # gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)
                    with self.get_background_texture(
                            self.drawing.palette.colors[0], 1):
                        gw, gh = self.drawing.grid_size
                        gl.glUniform2f(1, w / (gw * 2), h / (gh * 2))
                        gl.glBlendFunc(gl.GL_ONE, gl.GL_ONE_MINUS_SRC_ALPHA)
                        gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)

                with offscreen_buffer["color"]:
                    gl.glUniform2f(1, 1, 1)
                    gl.glEnable(gl.GL_BLEND)
                    gl.glBlendFunc(gl.GL_ONE, gl.GL_ONE_MINUS_SRC_ALPHA)
                    gl.glUniformMatrix4fv(0, 1, gl.GL_FALSE, vm)
                    gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)

            with self.line_program:
                with self.border_vao:
                    self.update_border(self.drawing.current.rect)
                    gl.glUniformMatrix4fv(0, 1, gl.GL_FALSE, vm)
                    # r, g, b, _ = self.drawing.palette.get_color_as_float(self.drawing.palette.colors[0])
                    # gl.glUniform3f(1, r, g, b)
                    # gl.glDrawArrays(gl.GL_TRIANGLE_FAN, 0, 4)
                    gl.glUniform3f(1, 0., 0., 0.)
                    gl.glLineWidth(1)
                    gl.glDrawArrays(gl.GL_LINE_LOOP, 0, 4)

                # Selection rectangle
                if self.stroke:
                    tool = self.stroke.tool
                    selection = ((tool and tool.show_rect and tool.rect)
                                 or self.selection)
                    if selection:
                        self.set_selection(selection)
                        with self.selection_vao:
                            gl.glUniformMatrix4fv(0, 1, gl.GL_FALSE, vm)
                            gl.glUniform3f(1, 1., 1., 0.)
                            gl.glLineWidth(1)
                            gl.glDrawArrays(gl.GL_LINE_LOOP, 0, 4)

        if not self.tablet.active:
            self._draw_mouse_cursor()

        ui.draw_ui(self)

        gl.glFinish(
        )  # No double buffering, to minimize latency (does this work?)
        def __init__(self):
            #consider bumping opengl version if apple supports it
            #amdgpu-mesa currently supports up to 4.5
            super(ControledRender,
                  self).__init__(512,
                                 512,
                                 fullscreen=False,
                                 config=gl.Config(major_version=4,
                                                  minor_version=1),
                                 visible=False)
            print(self.context.get_info().get_version())

            self.vertex_buffer = gl.GLuint(0)
            self.vao = gl.GLuint(0)
            #self.prev_program = (gl.GLint * 1)()

            self.setupFBOandTextures()

            self.setupShaders()

            data = [
                -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0,
                1.0
            ]

            dataGl = (gl.GLfloat * len(data))(*data)

            gl.glGenBuffers(1, ctypes.byref(self.vertex_buffer))
            gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.vertex_buffer)
            gl.glBufferData(gl.GL_ARRAY_BUFFER,
                            len(dataGl) * 4, dataGl, gl.GL_STATIC_DRAW)

            gl.glGenVertexArrays(1, ctypes.byref(self.vao))
            gl.glBindVertexArray(self.vao)
            gl.glUseProgram(self.programA)
            self.pos_posA = gl.glGetAttribLocation(
                self.programA, ctypes.create_string_buffer(b"a_position"))
            assert (self.pos_posA >= 0)
            gl.glEnableVertexAttribArray(self.pos_posA)
            gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self.vertex_buffer)
            gl.glVertexAttribPointer(self.pos_posA, 2, gl.GL_FLOAT, False, 0,
                                     0)

            if args["shape"] == "sphere":
                self.radius_pos = gl.glGetUniformLocation(
                    self.programA, b"radius")
                self.center_pos = gl.glGetUniformLocation(
                    self.programA, b"center")
                self.checkUniformLocation(self.radius_pos)
                self.checkUniformLocation(self.center_pos)
                gl.glUniform1f(self.radius_pos, args["radius"])
                gl.glUniform3f(self.center_pos, args["center"][0],
                               args["center"][1], args["center"][2])
            elif args["shape"] == "cylinder":
                self.radius_pos = gl.glGetUniformLocation(
                    self.programA, b"radius")
                self.height_pos = gl.glGetUniformLocation(
                    self.programA, b"height")
                self.checkUniformLocation(self.radius_pos)
                self.checkUniformLocation(self.height_pos)
                gl.glUniform1f(self.radius_pos, args["radius"])
                gl.glUniform1f(self.height_pos, args["height"])

            self.slice_pos = gl.glGetUniformLocation(self.programA, b"slice")
            self.step_pos = gl.glGetUniformLocation(self.programA, b"step")

            self.checkUniformLocation(self.slice_pos)

            gl.glUniform1f(self.step_pos, 1 / args["resolution"])
            #may need changed for nonsquare textures

            gl.glViewport(0, 0, args["resolution"], args["resolution"])