Esempio n. 1
0
    def mix(c1: QtGui.QColor, c2: QtGui.QColor, bias: float=0.5):
        def mixQreal(a, b, bias):
            return a + (b-a) * bias

        if bias <= 0:
            return c1
        elif bias >= 1:
            return c2
        
        r = mixQreal(c1.redF(), c2.redF(), bias)
        g = mixQreal(c1.greenF(), c2.greenF(), bias)
        b = mixQreal(c1.blueF(), c2.blueF(), bias)
        a = mixQreal(c1.alphaF(), c2.alphaF(), bias)
        
        return QtGui.QColor.fromRgbF(r, g, b, a)
 def currentLineColor(self, countDistance, totalNrOfLines, trailFadePerc,
                      minOpacity, colorinput):
     color = QColor(colorinput)
     if countDistance == 0:
         return color
     minAlphaF = minOpacity / 100.0
     distanceThreshold = int(
         math.ceil((totalNrOfLines - 1) * trailFadePerc / 100.0))
     if countDistance > distanceThreshold:
         color.setAlphaF(minAlphaF)
     else:
         alphaDiff = color.alphaF() - minAlphaF
         gradient = alphaDiff / float(distanceThreshold + 1)
         resultAlpha = color.alphaF() - gradient * countDistance
         # If alpha is out of bounds, clip it.
         resultAlpha = min(1.0, max(0.0, resultAlpha))
         color.setAlphaF(resultAlpha)
     return color
Esempio n. 3
0
 def currentLineColor(self, countDistance, totalNrOfLines,
                      trailFadePerc, minOpacity, colorinput):
     color = QColor(colorinput)
     if countDistance == 0:
         return color
     minAlphaF = minOpacity / 100.0
     distanceThreshold = int(
             math.ceil((totalNrOfLines-1)*trailFadePerc/100))
     if countDistance > distanceThreshold:
         color.setAlphaF(minAlphaF)
     else:
         alphaDiff = color.alphaF() - minAlphaF
         gradient = alphaDiff / float(distanceThreshold + 1)
         resultAlpha = color.alphaF() - gradient * countDistance
         # If alpha is out of bounds, clip it.
         resultAlpha = min(1.0, max(0.0, resultAlpha))
         color.setAlphaF(resultAlpha)
     return color
Esempio n. 4
0
    def set_viewer_color(self):
        if self.set_viewer_bgr:
            color = QColor(self.viewer_bgr_color)
            c = (color.redF(), color.greenF(), color.blueF(), color.alphaF())

            nc = Ncat(TCP_IP, TCP_PORT)
            nc.connect()
            try:
                # BACKGROUND VIEWER f f f f
                nc.send('BACKGROUND VIEWER {:.4f} {:.4f} {:.4f} {:.4f};'.format(*c))
            except Exception as e:
                LOGGER.error('Sending viewer size command failed.\n%s', e)

            nc.close()
Esempio n. 5
0
class QtRenderer(Renderer):
    def __init__(self):
        super().__init__()

        self._controller = Application.getInstance().getController()
        self._scene = self._controller.getScene()

        self._vertex_buffer_cache = {}
        self._index_buffer_cache = {}

        self._initialized = False

        self._light_position = Vector(0, 0, 0)
        self._background_color = QColor(128, 128, 128)
        self._viewport_width = 0
        self._viewport_height = 0
        self._window_width = 0
        self._window_height = 0

        self._batches = []

        self._quad_buffer = None

        self._camera = None

    initialized = Signal()

    ##  Get an integer multiplier that can be used to correct for screen DPI.
    def getPixelMultiplier(self):
        # Standard assumption for screen pixel density is 96 DPI. We use that as baseline to get
        # a multiplication factor we can use for screens > 96 DPI.
        return round(
            Application.getInstance().primaryScreen().physicalDotsPerInch() /
            96.0)

    ##  Get the list of render batches.
    def getBatches(self):
        return self._batches

    ##  Overridden from Renderer.
    #
    #   This makes sure the added render pass has the right size.
    def addRenderPass(self, render_pass):
        super().addRenderPass(render_pass)
        render_pass.setSize(self._viewport_width, self._viewport_height)

    ##  Set background color used when rendering.
    def setBackgroundColor(self, color):
        self._background_color = color

    def getViewportWidth(self):
        return self._viewport_width

    def getViewportHeight(self):
        return self._viewport_height

    ##  Set the viewport size.
    #
    #   \param width The new width of the viewport.
    #   \param height The new height of the viewport.
    def setViewportSize(self, width, height):
        self._viewport_width = width
        self._viewport_height = height

        for render_pass in self._render_passes:
            render_pass.setSize(width, height)

    ##  Set the window size.
    def setWindowSize(self, width, height):
        self._window_width = width
        self._window_height = height

    ##  Get the window size.
    #
    #   \return A tuple of (window_width, window_height)
    def getWindowSize(self):
        return (self._window_width, self._window_height)

    ##  Overrides Renderer::beginRendering()
    def beginRendering(self):
        if not self._initialized:
            self._initialize()

        self._gl.glViewport(0, 0, self._viewport_width, self._viewport_height)
        self._gl.glClearColor(self._background_color.redF(),
                              self._background_color.greenF(),
                              self._background_color.blueF(),
                              self._background_color.alphaF())
        self._gl.glClear(self._gl.GL_COLOR_BUFFER_BIT
                         | self._gl.GL_DEPTH_BUFFER_BIT)
        self._gl.glClearColor(0.0, 0.0, 0.0, 0.0)

    ##  Overrides Renderer::queueNode()
    def queueNode(self, node, **kwargs):
        type = kwargs.pop("type", RenderBatch.RenderType.Solid)
        if kwargs.pop("transparent", False):
            type = RenderBatch.RenderType.Transparent
        elif kwargs.pop("overlay", False):
            type = RenderBatch.RenderType.Overlay

        shader = kwargs.pop("shader", self._default_material)
        batch = RenderBatch(shader, type=type, **kwargs)

        batch.addItem(node.getWorldTransformation(),
                      kwargs.get("mesh", node.getMeshData()),
                      kwargs.pop("uniforms", None))

        self._batches.append(batch)

    ##  Overrides Renderer::render()
    def render(self):
        self._batches.sort()

        for render_pass in self.getRenderPasses():
            render_pass.render()

    ##  Overrides Renderer::endRendering()
    def endRendering(self):
        self._batches.clear()

    ##  Render a full screen quad.
    #
    #   \param shader The shader to use when rendering.
    def renderFullScreenQuad(self, shader):
        self._gl.glDisable(self._gl.GL_DEPTH_TEST)
        self._gl.glDisable(self._gl.GL_BLEND)

        shader.setUniformValue("u_modelViewProjectionMatrix", Matrix())

        self._quad_buffer.bind()

        shader.enableAttribute("a_vertex", "vector3f", 0)
        shader.enableAttribute("a_uvs", "vector2f", 72)

        self._gl.glDrawArrays(self._gl.GL_TRIANGLES, 0, 6)

        shader.disableAttribute("a_vertex")
        shader.disableAttribute("a_uvs")
        self._quad_buffer.release()

    def _initialize(self):
        OpenGL.setInstance(QtOpenGL())
        self._gl = OpenGL.getInstance().getBindingsObject()

        self._default_material = OpenGL.getInstance().createShaderProgram(
            Resources.getPath(Resources.Shaders, "default.shader"))

        self._render_passes.add(
            DefaultPass(self._viewport_width, self._viewport_height))
        self._render_passes.add(
            SelectionPass(self._viewport_width, self._viewport_height))
        self._render_passes.add(
            CompositePass(self._viewport_width, self._viewport_height))

        buffer = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer)
        buffer.create()
        buffer.bind()
        buffer.allocate(120)
        data = numpy.array([
            -1.0, -1.0, 0.0, 1.0, 1.0, 0.0, -1.0, 1.0, 0.0, -1.0, -1.0, 0.0,
            1.0, -1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0,
            0.0, 1.0, 0.0, 1.0, 1.0
        ],
                           dtype=numpy.float32).tostring()
        buffer.write(0, data, len(data))
        buffer.release()
        self._quad_buffer = buffer

        self._initialized = True
        self.initialized.emit()
Esempio n. 6
0
def drawGlyphPoints(painter,
                    glyph,
                    scale,
                    rect,
                    drawStartPoints=True,
                    drawOnCurves=True,
                    drawOffCurves=True,
                    drawCoordinates=True,
                    onCurveColor=None,
                    otherColor=None,
                    backgroundColor=None):
    layer = glyph.layer
    onCurveColor = None
    if layer is not None:
        if layer.color is not None:
            onCurveColor = colorToQColor(layer.color)
    if onCurveColor is None:
        onCurveColor = defaultColor("glyphOnCurvePoints")
    if otherColor is None:
        otherColor = defaultColor("glyphOtherPoints")
    if backgroundColor is None:
        backgroundColor = defaultColor("background")
    # get the outline data
    outlineData = glyph.getRepresentation("defconQt.OutlineInformation")
    points = []
    # start points
    if drawStartPoints and outlineData["startPoints"]:
        startWidth = startHeight = 15 * scale
        startHalf = startWidth / 2.0
        path = QPainterPath()
        for point, angle in outlineData["startPoints"]:
            x, y = point
            if angle is not None:
                path.moveTo(x, y)
                path.arcTo(x - startHalf, y - startHalf, startWidth,
                           startHeight, 180 - angle, 180)
                path.closeSubpath()
            else:
                path.addEllipse(x - startHalf, y - startHalf, startWidth,
                                startHeight)
        startPointColor = QColor(otherColor)
        aF = startPointColor.alphaF()
        startPointColor.setAlphaF(aF * .3)
        painter.fillPath(path, startPointColor)
    # off curve
    if drawOffCurves and outlineData["offCurvePoints"]:
        # lines
        painter.save()
        painter.setPen(otherColor)
        for pt1, pt2 in outlineData["bezierHandles"]:
            x1, y1 = pt1
            x2, y2 = pt2
            # TODO: should lineWidth account scale by default
            drawLine(painter, x1, y1, x2, y2, 1.0 * scale)
        # points
        offWidth = 5 * scale
        offHalf = offWidth / 2.0
        path = QPainterPath()
        selectedPath = QPainterPath()
        for point in outlineData["offCurvePoints"]:
            x, y = point["point"]
            points.append((x, y))
            pointPath = QPainterPath()
            x -= offHalf
            y -= offHalf
            pointPath.addEllipse(x, y, offWidth, offWidth)
            if point["selected"]:
                selectedPath.addPath(pointPath)
            else:
                path.addPath(pointPath)
        pen = QPen(otherColor)
        pen.setWidthF(3.0 * scale)
        painter.setPen(pen)
        painter.drawPath(path)
        painter.fillPath(path, QBrush(backgroundColor))
        painter.drawPath(selectedPath)
        painter.fillPath(selectedPath, QBrush(otherColor))
        painter.restore()
    # on curve
    if drawOnCurves and outlineData["onCurvePoints"]:
        width = 7 * scale
        half = width / 2.0
        smoothWidth = 8 * scale
        smoothHalf = smoothWidth / 2.0
        painter.save()
        path = QPainterPath()
        selectedPath = QPainterPath()
        for point in outlineData["onCurvePoints"]:
            x, y = point["point"]
            points.append((x, y))
            pointPath = QPainterPath()
            if point["smooth"]:
                x -= smoothHalf
                y -= smoothHalf
                pointPath.addEllipse(x, y, smoothWidth, smoothWidth)
            else:
                x -= half
                y -= half
                pointPath.addRect(x, y, width, width)
            if point["selected"]:
                selectedPath.addPath(pointPath)
            path.addPath(pointPath)
        pen = QPen(onCurveColor)
        pen.setWidthF(1.5 * scale)
        painter.setPen(pen)
        painter.fillPath(selectedPath, onCurveColor)
        painter.drawPath(path)
        painter.restore()
    # coordinates
    if drawCoordinates:
        otherColor = QColor(otherColor)
        otherColor.setAlphaF(otherColor.alphaF() * .6)
        painter.save()
        painter.setPen(otherColor)
        for x, y in points:
            posX = x
            # TODO: We use + here because we align on top. Consider abstracting
            # yOffset.
            posY = y + 3
            x = round(x, 1)
            if int(x) == x:
                x = int(x)
            y = round(y, 1)
            if int(y) == y:
                y = int(y)
            text = "%d  %d" % (x, y)
            drawTextAtPoint(painter,
                            text,
                            posX,
                            posY,
                            scale,
                            xAlign="center",
                            yAlign="top")
        painter.restore()
Esempio n. 7
0
def drawGlyphPoints(
        painter, glyph, scale, rect,
        drawStartPoints=True, drawOnCurves=True, drawOffCurves=True,
        drawCoordinates=False, drawSelection=True, onCurveColor=None,
        otherColor=None, backgroundColor=None):
    if onCurveColor is None:
        layer = glyph.layer
        if layer is not None and layer.color is not None:
            onCurveColor = colorToQColor(layer.color)
        else:
            onCurveColor = defaultColor("glyphOnCurvePoints")
    if otherColor is None:
        otherColor = defaultColor("glyphOtherPoints")
    if backgroundColor is None:
        backgroundColor = defaultColor("background")
    # get the outline data
    outlineData = glyph.getRepresentation("defconQt.OutlineInformation")
    points = []
    # start points
    if drawStartPoints and outlineData["startPoints"]:
        startWidth = startHeight = 15 * scale
        startHalf = startWidth / 2.0
        path = QPainterPath()
        for point, angle in outlineData["startPoints"]:
            x, y = point
            if angle is not None:
                path.moveTo(x, y)
                path.arcTo(x - startHalf, y - startHalf, startWidth,
                           startHeight, 180 - angle, 180)
                path.closeSubpath()
            else:
                path.addEllipse(
                    x - startHalf, y - startHalf, startWidth, startHeight)
        startPointColor = QColor(otherColor)
        aF = startPointColor.alphaF()
        startPointColor.setAlphaF(aF * .3)
        painter.fillPath(path, startPointColor)
    # handles
    if drawOffCurves and outlineData["offCurvePoints"]:
        painter.save()
        painter.setPen(otherColor)
        for pt1, pt2 in outlineData["bezierHandles"]:
            x1, y1 = pt1
            x2, y2 = pt2
            # TODO: should lineWidth account scale by default
            drawLine(painter, x1, y1, x2, y2, 1.0 * scale)
        painter.restore()
    # on curve
    if drawOnCurves and outlineData["onCurvePoints"]:
        width = 7 * scale
        half = width / 2.0
        smoothWidth = 8 * scale
        smoothHalf = smoothWidth / 2.0
        painter.save()
        path = QPainterPath()
        selectedPath = QPainterPath()
        for point in outlineData["onCurvePoints"]:
            x, y = point["point"]
            points.append((x, y))
            pointPath = QPainterPath()
            if point["smooth"]:
                x -= smoothHalf
                y -= smoothHalf
                pointPath.addEllipse(x, y, smoothWidth, smoothWidth)
            else:
                x -= half
                y -= half
                pointPath.addRect(x, y, width, width)
            if drawSelection and point["selected"]:
                selectedPath.addPath(pointPath)
            path.addPath(pointPath)
        pen = QPen(onCurveColor)
        pen.setWidthF(1.5 * scale)
        painter.setPen(pen)
        painter.fillPath(selectedPath, onCurveColor)
        painter.drawPath(path)
        painter.restore()
    # off curve
    if drawOffCurves and outlineData["offCurvePoints"]:
        # lines
        # points
        offWidth = 5 * scale
        offHalf = offWidth / 2.0
        path = QPainterPath()
        selectedPath = QPainterPath()
        for point in outlineData["offCurvePoints"]:
            x, y = point["point"]
            points.append((x, y))
            pointPath = QPainterPath()
            x -= offHalf
            y -= offHalf
            pointPath.addEllipse(x, y, offWidth, offWidth)
            if drawSelection and point["selected"]:
                selectedPath.addPath(pointPath)
            else:
                path.addPath(pointPath)
        pen = QPen(otherColor)
        pen.setWidthF(3.0 * scale)
        painter.save()
        painter.setPen(pen)
        painter.drawPath(path)
        painter.fillPath(path, QBrush(backgroundColor))
        painter.drawPath(selectedPath)
        painter.fillPath(selectedPath, QBrush(otherColor))
        painter.restore()
    # coordinates
    if drawCoordinates:
        otherColor = QColor(otherColor)
        otherColor.setAlphaF(otherColor.alphaF() * .6)
        painter.save()
        painter.setPen(otherColor)
        # TODO: decision + color
        font = painter.font()
        font.setPointSize(7)
        painter.setFont(font)
        for x, y in points:
            posX = x
            # TODO: We use + here because we align on top. Consider abstracting
            # yOffset.
            posY = y + 3
            x = round(x, 1)
            if int(x) == x:
                x = int(x)
            y = round(y, 1)
            if int(y) == y:
                y = int(y)
            text = "%d  %d" % (x, y)
            drawTextAtPoint(painter, text, posX, posY, scale,
                            xAlign="center", yAlign="top")
        painter.restore()
Esempio n. 8
0
class GLWidget(QOpenGLWidget):

    clicked = pyqtSignal()

    PROGRAM_VERTEX_ATTRIBUTE, PROGRAM_TEXCOORD_ATTRIBUTE = range(2)

    vsrc = """
attribute highp vec4 vertex;
attribute mediump vec4 texCoord;
varying mediump vec4 texc;
uniform mediump mat4 matrix;
void main(void)
{
    gl_Position = matrix * vertex;
    texc = texCoord;
}
"""

    fsrc = """
uniform sampler2D texture;
varying mediump vec4 texc;
void main(void)
{
    gl_FragColor = texture2D(texture, texc.st);
}
"""

    coords = (
        (( +1, -1, -1 ), ( -1, -1, -1 ), ( -1, +1, -1 ), ( +1, +1, -1 )),
        (( +1, +1, -1 ), ( -1, +1, -1 ), ( -1, +1, +1 ), ( +1, +1, +1 )),
        (( +1, -1, +1 ), ( +1, -1, -1 ), ( +1, +1, -1 ), ( +1, +1, +1 )),
        (( -1, -1, -1 ), ( -1, -1, +1 ), ( -1, +1, +1 ), ( -1, +1, -1 )),
        (( +1, -1, +1 ), ( -1, -1, +1 ), ( -1, -1, -1 ), ( +1, -1, -1 )),
        (( -1, -1, +1 ), ( +1, -1, +1 ), ( +1, +1, +1 ), ( -1, +1, +1 ))
    )

    def __init__(self, parent=None):
        super(GLWidget, self).__init__(parent)

        self.clearColor = QColor(Qt.black)
        self.xRot = 0
        self.yRot = 0
        self.zRot = 0
        self.program = None

        self.lastPos = QPoint()

    def minimumSizeHint(self):
        return QSize(50, 50)

    def sizeHint(self):
        return QSize(200, 200)

    def rotateBy(self, xAngle, yAngle, zAngle):
        self.xRot += xAngle
        self.yRot += yAngle
        self.zRot += zAngle
        self.update()

    def setClearColor(self, color):
        self.clearColor = color
        self.update()

    def initializeGL(self):
        self.gl = self.context().versionFunctions()
        self.gl.initializeOpenGLFunctions()

        self.makeObject()

        self.gl.glEnable(self.gl.GL_DEPTH_TEST)
        self.gl.glEnable(self.gl.GL_CULL_FACE)

        vshader = QOpenGLShader(QOpenGLShader.Vertex, self)
        vshader.compileSourceCode(self.vsrc)

        fshader = QOpenGLShader(QOpenGLShader.Fragment, self)
        fshader.compileSourceCode(self.fsrc)

        self.program = QOpenGLShaderProgram()
        self.program.addShader(vshader)
        self.program.addShader(fshader)
        self.program.bindAttributeLocation('vertex',
                self.PROGRAM_VERTEX_ATTRIBUTE)
        self.program.bindAttributeLocation('texCoord',
                self.PROGRAM_TEXCOORD_ATTRIBUTE)
        self.program.link()

        self.program.bind()
        self.program.setUniformValue('texture', 0)

        self.program.enableAttributeArray(self.PROGRAM_VERTEX_ATTRIBUTE)
        self.program.enableAttributeArray(self.PROGRAM_TEXCOORD_ATTRIBUTE)
        self.program.setAttributeArray(self.PROGRAM_VERTEX_ATTRIBUTE,
                self.vertices)
        self.program.setAttributeArray(self.PROGRAM_TEXCOORD_ATTRIBUTE,
                self.texCoords)

    def paintGL(self):
        self.gl.glClearColor(self.clearColor.redF(), self.clearColor.greenF(),
                self.clearColor.blueF(), self.clearColor.alphaF())
        self.gl.glClear(
                self.gl.GL_COLOR_BUFFER_BIT | self.gl.GL_DEPTH_BUFFER_BIT)

        m = QMatrix4x4()
        m.ortho(-0.5, 0.5, 0.5, -0.5, 4.0, 15.0)
        m.translate(0.0, 0.0, -10.0)
        m.rotate(self.xRot / 16.0, 1.0, 0.0, 0.0)
        m.rotate(self.yRot / 16.0, 0.0, 1.0, 0.0)
        m.rotate(self.zRot / 16.0, 0.0, 0.0, 1.0)

        self.program.setUniformValue('matrix', m)

        for i, texture in enumerate(self.textures):
            texture.bind()
            self.gl.glDrawArrays(self.gl.GL_TRIANGLE_FAN, i * 4, 4)

    def resizeGL(self, width, height):
        side = min(width, height)
        self.gl.glViewport((width - side) // 2, (height - side) // 2, side,
                side)

    def mousePressEvent(self, event):
        self.lastPos = event.pos()

    def mouseMoveEvent(self, event):
        dx = event.x() - self.lastPos.x()
        dy = event.y() - self.lastPos.y()

        if event.buttons() & Qt.LeftButton:
            self.rotateBy(8 * dy, 8 * dx, 0)
        elif event.buttons() & Qt.RightButton:
            self.rotateBy(8 * dy, 0, 8 * dx)

        self.lastPos = event.pos()

    def mouseReleaseEvent(self, event):
        self.clicked.emit()

    def makeObject(self):
        self.textures = []
        self.texCoords = []
        self.vertices = []

        root = QFileInfo(__file__).absolutePath()

        for i in range(6):
            self.textures.append(
                    QOpenGLTexture(
                            QImage(root + ('/images/side%d.png' % (i + 1))).mirrored()))

            for j in range(4):
                self.texCoords.append(((j == 0 or j == 3), (j == 0 or j == 1)))

                x, y, z = self.coords[i][j]
                self.vertices.append((0.2 * x, 0.2 * y, 0.2 * z))
Esempio n. 9
0
class WaitingSpinner(QWidget):

    SPINNER_SOLID = 'solid'
    SPINNER_STRIPED = 'striped'

    def __init__(self,
                 center_on_parent=True,
                 disable_parent_when_spinning=True,
                 fill=SPINNER_SOLID,
                 *args,
                 **kwargs):
        QWidget.__init__(self, *args, **kwargs)
        self._center_on_parent = center_on_parent
        self._disable_parent_when_spinning = disable_parent_when_spinning
        self._timer = QTimer(self)
        self._color = QColor(Qt.gray)
        self._roundness = 100.0
        self._minimum_trail_opacity = 31.4159265358979323846
        self._trail_fade_percentage = 50.0
        self._revolutions_per_second = 1.57079632679489661923
        if fill == WaitingSpinner.SPINNER_SOLID:
            self._number_of_lines = 70
        elif fill == WaitingSpinner.SPINNER_STRIPED:
            self._number_of_lines = 20
        else:
            self._number_of_lines = 10
        self._line_length = 10
        self._line_width = 2
        self._inner_radius = 20
        self._current_counter = 0
        self._is_spinning = False

        self._init()

    # noinspection PyUnresolvedReferences
    def _init(self):
        self.hide()
        self._timer.timeout.connect(self._rotate)
        self._update_size()
        self._update_timer()

    # noinspection PyArgumentList
    @pyqtSlot()
    def _rotate(self):
        self._current_counter += 1
        if self._current_counter > self._number_of_lines:
            self._current_counter = 0
        self.update()

    def _update_size(self):
        size = (self._inner_radius + self._line_length) * 2
        self.setFixedSize(size, size)

    def _update_timer(self):
        self._timer.setInterval(
            1000 / (self._number_of_lines * self._revolutions_per_second))

    def _update_position(self):
        if self.parentWidget() and self._center_on_parent:
            self.move(self.parentWidget().width() / 2 - self.width() / 2,
                      self.parentWidget().height() / 2 - self.height() / 2)

    @staticmethod
    def _line_count_distance_from_primary(current, primary,
                                          total_num_of_lines):
        distance = primary - current
        if distance < 0:
            distance += total_num_of_lines
        return distance

    def _current_line_color(self, count_distance, total_num_of_lines,
                            trail_fade_percentage, min_opacity, color):
        if count_distance == 0:
            return color
        min_alpha_f = min_opacity / 100.0

        distance_threshold = ceil(
            (total_num_of_lines - 1) * trail_fade_percentage / 100.0)
        if count_distance > distance_threshold:
            color.setAlphaF(min_alpha_f)
        else:
            alpha_diff = self._color.alphaF() - min_alpha_f
            gradient = alpha_diff / distance_threshold + 1.0
            result_alpha = min(
                1.0, max(0.0,
                         color.alphaF() - gradient * count_distance))
            color.setAlphaF(result_alpha)
        return color

    def paintEvent(self, event):
        self._update_position()
        painter = QPainter(self)
        painter.fillRect(self.rect(), Qt.transparent)
        painter.setRenderHint(QPainter.Antialiasing, True)
        if self._current_counter > self._number_of_lines:
            self._current_counter = 0
        painter.setPen(Qt.NoPen)

        for i in range(self._number_of_lines):
            painter.save()
            painter.translate(self._inner_radius + self._line_length,
                              self._inner_radius + self._line_length)
            rotate_angle = 360.0 * i / self._number_of_lines
            painter.rotate(rotate_angle)
            painter.translate(self._inner_radius, 0)
            distance = self._line_count_distance_from_primary(
                i, self._current_counter, self._number_of_lines)
            color = self._current_line_color(distance, self._number_of_lines,
                                             self._trail_fade_percentage,
                                             self._minimum_trail_opacity,
                                             self._color)
            painter.setBrush(color)
            painter.drawRoundedRect(
                QRect(0, -self._line_width // 2, self.line_length,
                      self._line_length), self._roundness, Qt.RelativeSize)
            painter.restore()

    def start(self):
        self._update_position()
        self._is_spinning = True

        if self.parentWidget() and self._disable_parent_when_spinning:
            self.parentWidget().setEnabled(False)

        if not self._timer.isActive():
            self._timer.start()
            self._current_counter = 0

        self.show()

    def stop(self):
        self._is_spinning = False
        self.hide()

        if self.parentWidget() and self._disable_parent_when_spinning:
            self.parentWidget().setEnabled(True)
        if self._timer.isActive():
            self._timer.stop()
            self._current_counter = 0

    def set_number_of_lines(self, value):
        self._number_of_lines = value
        self._update_timer()

    def set_line_length(self, value):
        self._line_length = value
        self._update_size()

    def set_line_width(self, value):
        self._line_width = value
        self._update_size()

    def set_inner_radius(self, value):
        self._inner_radius = value
        self._update_size()

    def set_roundness(self, value):
        self._roundness = min(0.0, max(100, value))

    def set_color(self, value):
        self._color = value

    def set_revolutions_per_second(self, value):
        self._revolutions_per_second = value
        self._update_timer()

    def set_trail_fade_percentage(self, value):
        self._trail_fade_percentage = value

    def set_minimum_trail_opacity(self, value):
        self._minimum_trail_opacity = value

    @property
    def color(self):
        return self._color

    @property
    def roundness(self):
        return self._roundness

    @property
    def minimum_trail_opacity(self):
        return self._minimum_trail_opacity

    @property
    def trail_fade_percentage(self):
        return self._trail_fade_percentage

    @property
    def revolutions_pers_second(self):
        return self._revolutions_per_second

    @property
    def number_of_lines(self):
        return self._number_of_lines

    @property
    def line_length(self):
        return self._line_length

    @property
    def line_width(self):
        return self._line_width

    @property
    def inner_radius(self):
        return self._inner_radius

    @property
    def is_spinning(self):
        return self._is_spinning
Esempio n. 10
0
class QtGL2Renderer(Renderer):
    def __init__(self):
        super().__init__()

        self._controller = Application.getInstance().getController()
        self._scene = self._controller.getScene()

        self._vertex_buffer_cache = {}
        self._index_buffer_cache = {}

        self._initialized = False

        self._light_position = Vector(0, 0, 0)
        self._background_color = QColor(128, 128, 128)
        self._viewport_width = 0
        self._viewport_height = 0
        self._window_width = 0
        self._window_height = 0

        self._solids_queue = []
        self._transparent_queue = []
        self._overlay_queue = []

        self._render_selection = True
        self._selection_buffer = None
        self._selection_map = {}
        self._selection_image = None

        self._camera = None

    def getPixelMultiplier(self):
        # Standard assumption for screen pixel density is 96 DPI. We use that as baseline to get
        # a multiplication factor we can use for screens > 96 DPI.
        return round(
            Application.getInstance().primaryScreen().physicalDotsPerInch() /
            96.0)

    ##  Create a new material
    #   \param vert
    #   \param frag
    #   \return material
    def createMaterial(self, vert, frag):
        mat = QtGL2Material.QtGL2Material(self)
        mat.loadVertexShader(vert)
        mat.loadFragmentShader(frag)
        mat.build()
        return mat

    ##  Create frame buffer with given width/height
    #   \param width Width of buffer
    #   \param height Height of buffer
    #   \return Created frame buffer
    def createFrameBuffer(self, width, height):
        buffer_format = QOpenGLFramebufferObjectFormat()
        buffer_format.setAttachment(QOpenGLFramebufferObject.Depth)
        return QOpenGLFramebufferObject(width, height, buffer_format)

    ##  Set light position of global light
    def setLightPosition(self, position):
        self._light_position = position

    ##  Set background color of the rendering.
    def setBackgroundColor(self, color):
        self._background_color = color

    def setViewportSize(self, width, height):
        self._viewport_width = width
        self._viewport_height = height

    def setWindowSize(self, width, height):
        self._window_width = width
        self._window_height = height

    ##  Reset the selection image, so a redraw is forced.
    #   This is used when the scene is changed by delete actions, so the image needs to be redrawn.
    #   It can happen that mouse events fire much faster than rendering, which can cause problems
    #   if the old selection image is still used.
    def resetSelectionImage(self):
        self._selection_image = None

    ##  Get the selection colors within a radius.
    #   All objects (either full objects or single points in a cloud are drawn with an unique color.
    #   \param x from -1 to 1
    #   \param y from -1 to 1
    #   \param radius Radius in pixels to select.
    #   \return list of colors ARGB (values from 0 to 1.)
    def getSelectionColorAtCoorindateRadius(self, x, y, radius):
        if not self._selection_image:
            return None
        px = (0.5 + x / 2.0) * self._viewport_width
        py = (0.5 + y / 2.0) * self._viewport_height
        squared_radius = radius * radius
        samples = []
        for sx in range(-radius, radius):
            squared_sx = sx * sx
            if px + sx < 0 or px + sx > (self._selection_image.width() - 1):
                continue
            for sy in range(-radius, radius):
                squared_sy = sy * sy
                if py + sy < 0 or py + sy > (self._selection_image.height() -
                                             1):
                    continue
                if squared_sx + squared_sy < squared_radius:
                    pixel = self._selection_image.pixel(px + sx, py + sy)
                    samples.append(Color.fromARGB(pixel))
        return samples

    ##  Get the selection colors on coordinate
    #   All objects (either full objects or single points in a cloud are drawn with an unique color.
    #   \param x from -1 to 1
    #   \param y from -1 to 1
    #   \return color ARGB (values from 0 to 1.)
    def getSelectionColorAtCoordinate(self, x, y):
        if not self._selection_image:
            return None
        px = (0.5 + x / 2.0) * self._viewport_width
        py = (0.5 + y / 2.0) * self._viewport_height
        return Color.fromARGB(self._selection_image.pixel(px, py))

    ##  Get object ID at coordinate.
    #   \param x from -1 to 1
    #   \param y from -1 to 1
    def getIdAtCoordinate(self, x, y):
        if not self._selection_image:
            return None

        px = (0.5 + x / 2.0) * self._window_width
        py = (0.5 + y / 2.0) * self._window_height

        if px < 0 or px > (self._selection_image.width() -
                           1) or py < 0 or py > (
                               self._selection_image.height() - 1):
            return None

        pixel = self._selection_image.pixel(px, py)
        return self._selection_map.get(Color.fromARGB(pixel), None)

    ##  Render selection is used to 'highlight' the selected objects
    def setRenderSelection(self, render):
        self._render_selection = render

    def beginRendering(self):
        if not self._initialized:
            self._initialize()

        self._gl.glViewport(0, 0, self._viewport_width, self._viewport_height)
        self._gl.glClearColor(self._background_color.redF(),
                              self._background_color.greenF(),
                              self._background_color.blueF(),
                              self._background_color.alphaF())
        self._gl.glClear(self._gl.GL_COLOR_BUFFER_BIT
                         | self._gl.GL_DEPTH_BUFFER_BIT)

        if not QOpenGLContext.currentContext().format().renderableType(
        ) == QSurfaceFormat.OpenGLES:
            self._gl.glPointSize(2)

        self._solids_queue.clear()
        self._transparent_queue.clear()
        self._overlay_queue.clear()

        self._render_selection = True

    ##  Put a node in the render queue
    def queueNode(self, node, **kwargs):
        queue_item = {"node": node}

        if "mesh" in kwargs:
            queue_item["mesh"] = kwargs["mesh"]

        queue_item["material"] = kwargs.get("material", self._default_material)

        mode = kwargs.get("mode", Renderer.RenderTriangles)
        if mode is Renderer.RenderLines:
            queue_item["mode"] = self._gl.GL_LINES
        elif mode is Renderer.RenderLineLoop:
            queue_item["mode"] = self._gl.GL_LINE_LOOP
        elif mode is Renderer.RenderPoints:
            queue_item["mode"] = self._gl.GL_POINTS
        else:
            queue_item["mode"] = self._gl.GL_TRIANGLES

        queue_item["wireframe"] = (mode == Renderer.RenderWireframe)

        queue_item["force_single_sided"] = kwargs.get("force_single_sided",
                                                      False)

        if kwargs.get("end", None):
            queue_item["range"] = [kwargs.get("start", 0), kwargs.get("end")]

        if kwargs.get("transparent", False):
            self._transparent_queue.append(queue_item)
        elif kwargs.get("overlay", False):
            self._overlay_queue.append(queue_item)
        else:
            self._solids_queue.append(queue_item)

    ##  Render all nodes in the queue
    def renderQueuedNodes(self):
        self._gl.glEnable(self._gl.GL_DEPTH_TEST)
        self._gl.glDepthFunc(self._gl.GL_LESS)
        self._gl.glDepthMask(self._gl.GL_TRUE)
        self._gl.glDisable(self._gl.GL_CULL_FACE)
        self._gl.glLineWidth(self.getPixelMultiplier())

        self._scene.acquireLock()

        self._camera = self._scene.getActiveCamera()
        if not self._camera:
            Logger.log("e", "No active camera set, can not render")
            self._scene.releaseLock()
            return

        # Render the selection image
        selectable_nodes = []
        for node in DepthFirstIterator(self._scene.getRoot()):
            if node.isSelectable() and node.getMeshData():
                selectable_nodes.append(node)

        if not selectable_nodes:
            self._selection_image = None

        if selectable_nodes:
            #TODO: Use a limited area around the mouse rather than a full viewport for rendering
            if self._selection_buffer.width(
            ) != self._viewport_width or self._selection_buffer.height(
            ) != self._viewport_height:
                self._selection_buffer = self.createFrameBuffer(
                    self._viewport_width, self._viewport_height)

            self._selection_buffer.bind()
            self._gl.glClearColor(0.0, 0.0, 0.0, 0.0)
            self._gl.glClear(self._gl.GL_COLOR_BUFFER_BIT
                             | self._gl.GL_DEPTH_BUFFER_BIT)
            self._gl.glDisable(self._gl.GL_BLEND)
            self._selection_map.clear()
            for node in selectable_nodes:
                if type(
                        node
                ) is PointCloudNode:  #Pointcloud node sets vertex color (to be used for point precise selection)
                    self._renderItem({
                        "node": node,
                        "material": self._handle_material,
                        "mode": self._gl.GL_POINTS
                    })
                else:
                    color = self._getObjectColor(node)
                    self._selection_map[color] = id(node)
                    self._selection_material.setUniformValue("u_color", color)
                    self._renderItem({
                        "node": node,
                        "material": self._selection_material,
                        "mode": self._gl.GL_TRIANGLES
                    })
            tool = self._controller.getActiveTool()
            if tool:
                tool_handle = tool.getHandle()
                if tool_handle and tool_handle.getSelectionMesh(
                ) and tool_handle.getParent():
                    self._selection_map.update(tool_handle.getSelectionMap())
                    self._gl.glDisable(self._gl.GL_DEPTH_TEST)
                    self._renderItem({
                        "node": tool_handle,
                        "mesh": tool_handle.getSelectionMesh(),
                        "material": self._handle_material,
                        "mode": self._gl.GL_TRIANGLES
                    })
                    self._gl.glEnable(self._gl.GL_DEPTH_TEST)

            self._selection_image = self._selection_buffer.toImage()
            self._selection_buffer.release()

        # Workaround for a MacOSX Intel HD Graphics 6000 Bug
        # Apparently, performing a glReadPixels call on a bound framebuffer breaks releasing the
        # depth buffer, which means the rest of the depth tested geometry never renders. To work-
        # around this we perform a clear here. Note that for some reason we also need to set
        # glClearColor since it is apparently not stored properly.
        self._gl.glClearColor(self._background_color.redF(),
                              self._background_color.greenF(),
                              self._background_color.blueF(),
                              self._background_color.alphaF())
        self._gl.glClear(self._gl.GL_COLOR_BUFFER_BIT
                         | self._gl.GL_DEPTH_BUFFER_BIT)

        self._gl.glEnable(self._gl.GL_STENCIL_TEST)
        self._gl.glStencilMask(0xff)
        self._gl.glClearStencil(0)
        self._gl.glClear(self._gl.GL_STENCIL_BUFFER_BIT)
        self._gl.glStencilFunc(self._gl.GL_ALWAYS, 0xff, 0xff)
        self._gl.glStencilOp(self._gl.GL_REPLACE, self._gl.GL_REPLACE,
                             self._gl.GL_REPLACE)
        self._gl.glStencilMask(0)

        for item in self._solids_queue:
            if Selection.isSelected(item["node"]):
                self._gl.glStencilMask(0xff)
                self._renderItem(item)
                self._gl.glStencilMask(0)
            else:
                self._renderItem(item)

        if self._render_selection:
            self._gl.glStencilMask(0)
            self._gl.glStencilFunc(self._gl.GL_EQUAL, 0, 0xff)
            self._gl.glLineWidth(2 * self.getPixelMultiplier())
            for node in Selection.getAllSelectedObjects():
                if node.getMeshData() and type(node) is not PointCloudNode:
                    self._renderItem({
                        "node": node,
                        "material": self._outline_material,
                        "mode": self._gl.GL_TRIANGLES,
                        "wireframe": True
                    })

            self._gl.glLineWidth(self.getPixelMultiplier())

        self._gl.glDisable(self._gl.GL_STENCIL_TEST)
        self._gl.glDepthMask(self._gl.GL_FALSE)
        self._gl.glEnable(self._gl.GL_BLEND)
        self._gl.glEnable(self._gl.GL_CULL_FACE)
        self._gl.glBlendFunc(self._gl.GL_SRC_ALPHA,
                             self._gl.GL_ONE_MINUS_SRC_ALPHA)

        for item in self._transparent_queue:
            self._renderItem(item)

        self._gl.glDisable(self._gl.GL_DEPTH_TEST)
        self._gl.glDisable(self._gl.GL_CULL_FACE)

        for item in self._overlay_queue:
            self._renderItem(item)

        self._scene.releaseLock()

    def endRendering(self):
        pass

    def _initialize(self):
        profile = QOpenGLVersionProfile()
        profile.setVersion(2, 0)
        self._gl = QOpenGLContext.currentContext().versionFunctions(profile)
        self._gl.initializeOpenGLFunctions()

        self._default_material = self.createMaterial(
            Resources.getPath(Resources.Shaders, "default.vert"),
            Resources.getPath(Resources.Shaders, "default.frag"))

        self._default_material.setUniformValue("u_ambientColor",
                                               Color(0.3, 0.3, 0.3, 1.0))
        self._default_material.setUniformValue("u_diffuseColor",
                                               Color(0.5, 0.5, 0.5, 1.0))
        self._default_material.setUniformValue("u_specularColor",
                                               Color(0.7, 0.7, 0.7, 1.0))
        self._default_material.setUniformValue("u_shininess", 20.)

        self._selection_buffer = self.createFrameBuffer(128, 128)
        self._selection_material = self.createMaterial(
            Resources.getPath(Resources.Shaders, "basic.vert"),
            Resources.getPath(Resources.Shaders, "color.frag"))

        self._handle_material = self.createMaterial(
            Resources.getPath(Resources.Shaders, "basic.vert"),
            Resources.getPath(Resources.Shaders, "vertexcolor.frag"))

        self._outline_material = self.createMaterial(
            Resources.getPath(Resources.Shaders, "outline.vert"),
            Resources.getPath(Resources.Shaders, "outline.frag"))

        self._initialized = True

    def _renderItem(self, item):
        node = item["node"]
        mesh = item.get("mesh", node.getMeshData())
        if not mesh:
            return  #Something went wrong, node has no mesh.
        transform = node.getWorldTransformation()
        material = item["material"]
        mode = item["mode"]
        wireframe = item.get("wireframe", False)
        range = item.get("range", None)

        culling_enabled = self._gl.glIsEnabled(self._gl.GL_CULL_FACE)
        if item.get("force_single_sided") and not culling_enabled:
            self._gl.glEnable(self._gl.GL_CULL_FACE)

        material.bind()
        material.setUniformValue("u_projectionMatrix",
                                 self._camera.getProjectionMatrix(),
                                 cache=False)
        material.setUniformValue(
            "u_viewMatrix",
            self._camera.getWorldTransformation().getInverse(),
            cache=False)
        material.setUniformValue("u_viewPosition",
                                 self._camera.getWorldPosition(),
                                 cache=False)
        material.setUniformValue("u_modelMatrix", transform, cache=False)
        material.setUniformValue("u_lightPosition",
                                 self._camera.getWorldPosition() +
                                 Vector(0, 50, 0),
                                 cache=False)

        if mesh.hasNormals():
            normal_matrix = copy.deepcopy(transform)
            normal_matrix.setRow(3, [0, 0, 0, 1])
            normal_matrix.setColumn(3, [0, 0, 0, 1])
            normal_matrix = normal_matrix.getInverse().getTransposed()
            material.setUniformValue("u_normalMatrix",
                                     normal_matrix,
                                     cache=False)

        vertex_buffer = None
        try:
            vertex_buffer = getattr(mesh, vertexBufferProperty)
        except AttributeError:
            pass

        if vertex_buffer is None:
            vertex_buffer = self._createVertexBuffer(mesh)

        vertex_buffer.bind()

        if mesh.hasIndices():
            index_buffer = None
            try:
                index_buffer = getattr(mesh, indexBufferProperty)
            except AttributeError:
                pass

            if index_buffer is None:
                index_buffer = self._createIndexBuffer(mesh)

            index_buffer.bind()

        material.enableAttribute("a_vertex", "vector3f", 0)
        offset = mesh.getVertexCount() * 3 * 4

        if mesh.hasNormals():
            material.enableAttribute("a_normal", "vector3f", offset)
            offset += mesh.getVertexCount() * 3 * 4

        if mesh.hasColors():
            material.enableAttribute("a_color", "vector4f", offset)
            offset += mesh.getVertexCount() * 4 * 4

        if mesh.hasUVCoordinates():
            material.enableAttribute("a_uvs", "vector2f", offset)
            offset += mesh.getVertexCount() * 2 * 4

        if wireframe and hasattr(self._gl, "glPolygonMode"):
            self._gl.glPolygonMode(self._gl.GL_FRONT_AND_BACK,
                                   self._gl.GL_LINE)

        if mesh.hasIndices():
            if range is None:
                if mode == self._gl.GL_TRIANGLES:
                    self._gl.glDrawElements(mode,
                                            mesh.getFaceCount() * 3,
                                            self._gl.GL_UNSIGNED_INT, None)
                else:
                    self._gl.glDrawElements(mode, mesh.getFaceCount(),
                                            self._gl.GL_UNSIGNED_INT, None)
            else:
                if mode == self._gl.GL_TRIANGLES:
                    self._gl.glDrawRangeElements(mode, range[0], range[1],
                                                 range[1] - range[0],
                                                 self._gl.GL_UNSIGNED_INT,
                                                 None)
                else:
                    self._gl.glDrawRangeElements(mode, range[0], range[1],
                                                 range[1] - range[0],
                                                 self._gl.GL_UNSIGNED_INT,
                                                 None)
        else:
            self._gl.glDrawArrays(mode, 0, mesh.getVertexCount())

        if wireframe and hasattr(self._gl, "glPolygonMode"):
            self._gl.glPolygonMode(self._gl.GL_FRONT_AND_BACK,
                                   self._gl.GL_FILL)

        material.disableAttribute("a_vertex")
        material.disableAttribute("a_normal")
        material.disableAttribute("a_color")
        material.disableAttribute("a_uvs")
        vertex_buffer.release()

        if mesh.hasIndices():
            index_buffer.release()

        material.release()

        if item.get("force_single_sided") and not culling_enabled:
            self._gl.glDisable(self._gl.GL_CULL_FACE)

    def _createVertexBuffer(self, mesh):
        buffer = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer)
        buffer.create()
        buffer.bind()

        buffer_size = mesh.getVertexCount(
        ) * 3 * 4  # Vertex count * number of components * sizeof(float32)
        if mesh.hasNormals():
            buffer_size += mesh.getVertexCount(
            ) * 3 * 4  # Vertex count * number of components * sizeof(float32)
        if mesh.hasColors():
            buffer_size += mesh.getVertexCount(
            ) * 4 * 4  # Vertex count * number of components * sizeof(float32)
        if mesh.hasUVCoordinates():
            buffer_size += mesh.getVertexCount(
            ) * 2 * 4  # Vertex count * number of components * sizeof(float32)

        buffer.allocate(buffer_size)

        offset = 0
        vertices = mesh.getVerticesAsByteArray()
        if vertices is not None:
            buffer.write(0, vertices, len(vertices))
            offset += len(vertices)

        if mesh.hasNormals():
            normals = mesh.getNormalsAsByteArray()
            buffer.write(offset, normals, len(normals))
            offset += len(normals)

        if mesh.hasColors():
            colors = mesh.getColorsAsByteArray()
            buffer.write(offset, colors, len(colors))
            offset += len(colors)

        if mesh.hasUVCoordinates():
            uvs = mesh.getUVCoordinatesAsByteArray()
            buffer.write(offset, uvs, len(uvs))
            offset += len(uvs)

        buffer.release()

        setattr(mesh, vertexBufferProperty, buffer)
        return buffer

    def _createIndexBuffer(self, mesh):
        buffer = QOpenGLBuffer(QOpenGLBuffer.IndexBuffer)
        buffer.create()
        buffer.bind()

        data = mesh.getIndicesAsByteArray()
        buffer.allocate(data, len(data))
        buffer.release()

        setattr(mesh, indexBufferProperty, buffer)
        return buffer

    ##  Create object color based on ID of node.
    #   \param node Node to get color for
    #   \return Color
    def _getObjectColor(self, node):
        obj_id = id(node)
        r = (obj_id & 0xff000000) >> 24
        g = (obj_id & 0x00ff0000) >> 16
        b = (obj_id & 0x0000ff00) >> 8
        a = (obj_id & 0x000000ff) >> 0
        return Color(r, g, b, a)
Esempio n. 11
0
class QtRenderer(Renderer):
    """A Renderer implementation using PyQt's OpenGL implementation to render."""

    def __init__(self) -> None:
        super().__init__()

        self._controller = Application.getInstance().getController()  # type: Controller
        self._scene = self._controller.getScene()  # type: Scene

        self._initialized = False  # type: bool

        self._light_position = Vector(0, 0, 0)  # type: Vector
        self._background_color = QColor(128, 128, 128)  # type: QColor
        self._viewport_width = 0  # type: int
        self._viewport_height = 0  # type: int
        self._window_width = 0  # type: int
        self._window_height = 0  # type: int

        self._batches = []  # type: List[RenderBatch]

        self._quad_buffer = None  # type: QOpenGLBuffer

        self._camera = None  # type: Optional[Camera]

    initialized = Signal()

    def getPixelMultiplier(self) -> int:
        """Get an integer multiplier that can be used to correct for screen DPI."""

        # Standard assumption for screen pixel density is 96 DPI. We use that as baseline to get
        # a multiplication factor we can use for screens > 96 DPI.
        return round(UM.Qt.QtApplication.QtApplication.getInstance().primaryScreen().physicalDotsPerInch() / 96.0)

    def getBatches(self) -> List[RenderBatch]:
        """Get the list of render batches."""

        return self._batches

    def addRenderPass(self, render_pass: "******") -> None:
        """Overridden from Renderer.

        This makes sure the added render pass has the right size.
        """

        super().addRenderPass(render_pass)
        render_pass.setSize(self._viewport_width, self._viewport_height)

    def setBackgroundColor(self, color: QColor) -> None:
        """Set background color used when rendering."""

        self._background_color = color

    def getViewportWidth(self) -> int:
        return self._viewport_width

    def getViewportHeight(self) -> int:
        return self._viewport_height

    def setViewportSize(self, width: int, height: int) -> None:
        """Set the viewport size.

        :param width: The new width of the viewport.
        :param height: The new height of the viewport.
        """

        self._viewport_width = width
        self._viewport_height = height

        for render_pass in self._render_passes:
            render_pass.setSize(width, height)

    def setWindowSize(self, width: int, height: int) -> None:
        """Set the window size."""

        self._window_width = width
        self._window_height = height

    def getWindowSize(self) -> Tuple[int, int]:
        """Get the window size.

        :return: A tuple of (window_width, window_height)
        """

        return self._window_width, self._window_height

    def beginRendering(self) -> None:
        """Overrides Renderer::beginRendering()"""

        if not self._initialized:
            self._initialize()

        self._gl.glViewport(0, 0, self._viewport_width, self._viewport_height)
        self._gl.glClearColor(self._background_color.redF(), self._background_color.greenF(), self._background_color.blueF(), self._background_color.alphaF())
        self._gl.glClear(self._gl.GL_COLOR_BUFFER_BIT | self._gl.GL_DEPTH_BUFFER_BIT)
        self._gl.glClearColor(0.0, 0.0, 0.0, 0.0)

    def queueNode(self, node: "SceneNode", **kwargs) -> None:
        """Overrides Renderer::queueNode()"""

        type = kwargs.pop("type", RenderBatch.RenderType.Solid)
        if kwargs.pop("transparent", False):
            type = RenderBatch.RenderType.Transparent
        elif kwargs.pop("overlay", False):
            type = RenderBatch.RenderType.Overlay

        shader = kwargs.pop("shader", self._default_material)
        batch = RenderBatch(shader, type = type, **kwargs)

        batch.addItem(node.getWorldTransformation(), kwargs.get("mesh", node.getMeshData()), kwargs.pop("uniforms", None))

        self._batches.append(batch)

    def render(self) -> None:
        """Overrides Renderer::render()"""

        self._batches.sort()

        for render_pass in self.getRenderPasses():
            width, height = render_pass.getSize()
            self._gl.glViewport(0, 0, width, height)
            render_pass.render()

    def reRenderLast(self):
        """Sometimes not an *entire* new render is required (QML is updated, but the scene did not).
        In that case we ask the composite pass (which must be the last render pass) to render (instead of re-rendering
        all the passes & the views.
        """

        self.beginRendering() # First ensure that the viewport is set correctly.
        self.getRenderPasses()[-1].render()

    def endRendering(self) -> None:
        """Overrides Renderer::endRendering()"""

        self._batches.clear()

    def renderFullScreenQuad(self, shader: "ShaderProgram") -> None:
        """Render a full screen quad (rectangle).

        The function is used to draw render results on.
        :param shader: The shader to use when rendering.
        """

        self._gl.glDisable(self._gl.GL_DEPTH_TEST)
        self._gl.glDisable(self._gl.GL_BLEND)

        shader.setUniformValue("u_modelViewProjectionMatrix", Matrix())

        if OpenGLContext.properties["supportsVertexArrayObjects"]:
            vao = QOpenGLVertexArrayObject()
            vao.create()
            vao.bind()

        self._quad_buffer.bind()

        shader.enableAttribute("a_vertex", "vector3f", 0)
        shader.enableAttribute("a_uvs", "vector2f", 72)

        self._gl.glDrawArrays(self._gl.GL_TRIANGLES, 0, 6)

        shader.disableAttribute("a_vertex")
        shader.disableAttribute("a_uvs")
        self._quad_buffer.release()

    def _initialize(self) -> None:
        supports_vao = OpenGLContext.supportsVertexArrayObjects()  # fill the OpenGLContext.properties
        Logger.log("d", "Support for Vertex Array Objects: %s", supports_vao)

        OpenGL()
        self._gl = OpenGL.getInstance().getBindingsObject()

        self._default_material = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "default.shader")) #type: ShaderProgram

        self._render_passes.add(DefaultPass(self._viewport_width, self._viewport_height))
        self._render_passes.add(SelectionPass(self._viewport_width, self._viewport_height))
        self._render_passes.add(CompositePass(self._viewport_width, self._viewport_height))

        buffer = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer)
        buffer.create()
        buffer.bind()
        buffer.allocate(120)
        data = numpy.array([
            -1.0, -1.0, 0.0,
             1.0,  1.0, 0.0,
            -1.0,  1.0, 0.0,
            -1.0, -1.0, 0.0,
             1.0, -1.0, 0.0,
             1.0,  1.0, 0.0,
             0.0,  0.0,
             1.0,  1.0,
             0.0,  1.0,
             0.0,  0.0,
             1.0,  0.0,
             1.0,  1.0
        ], dtype = numpy.float32).tostring()
        buffer.write(0, data, len(data))
        buffer.release()
        self._quad_buffer = buffer

        self._initialized = True
        self.initialized.emit()
Esempio n. 12
0
class QtGL2Renderer(Renderer):
    def __init__(self):
        super().__init__()

        self._controller = Application.getInstance().getController()
        self._scene = self._controller.getScene()

        self._vertex_buffer_cache = {}
        self._index_buffer_cache = {}

        self._initialized = False

        self._light_position = Vector(0, 0, 0)
        self._background_color = QColor(128, 128, 128)
        self._viewport_width = 0
        self._viewport_height = 0

        self._solids_queue = []
        self._transparent_queue = []
        self._overlay_queue = []

        self._render_selection = True
        self._selection_buffer = None
        self._selection_map = {}
        self._selection_image = None

        self._camera = None

    def getPixelMultiplier(self):
        # Standard assumption for screen pixel density is 96 DPI. We use that as baseline to get
        # a multiplication factor we can use for screens > 96 DPI.
        return round(Application.getInstance().primaryScreen().physicalDotsPerInch() / 96.0)

    ##  Create a new material
    #   \param vert
    #   \param frag
    #   \return material
    def createMaterial(self, vert, frag):
        mat = QtGL2Material.QtGL2Material(self)
        mat.loadVertexShader(vert)
        mat.loadFragmentShader(frag)
        mat.build()
        return mat
    
    ##  Create frame buffer with given width/height
    #   \param width Width of buffer
    #   \param height Height of buffer
    #   \return Created frame buffer
    def createFrameBuffer(self, width, height):
        buffer_format = QOpenGLFramebufferObjectFormat()
        buffer_format.setAttachment(QOpenGLFramebufferObject.Depth)
        return QOpenGLFramebufferObject(width, height, buffer_format)

    ##  Set light position of global light
    def setLightPosition(self, position):
        self._light_position = position
    
    ##  Set background color of the rendering.
    def setBackgroundColor(self, color):
        self._background_color = color

    def setViewportSize(self, width, height):
        self._viewport_width = width
        self._viewport_height = height
        
    ##  Reset the selection image, so a redraw is forced.
    #   This is used when the scene is changed by delete actions, so the image needs to be redrawn.
    #   It can happen that mouse events fire much faster than rendering, which can cause problems 
    #   if the old selection image is still used.
    def resetSelectionImage(self):
        self._selection_image = None
    
    ##  Get the selection colors within a radius.
    #   All objects (either full objects or single points in a cloud are drawn with an unique color.
    #   \param x from -1 to 1
    #   \param y from -1 to 1
    #   \param radius Radius in pixels to select.
    #   \return list of colors ARGB (values from 0 to 1.)
    def getSelectionColorAtCoorindateRadius(self,x,y,radius):
        if not self._selection_image:
            return None
        px = (0.5 + x / 2.0) * self._viewport_width
        py = (0.5 + y / 2.0) * self._viewport_height
        squared_radius = radius * radius
        samples = []
        for sx in range(-radius, radius):
            squared_sx = sx*sx
            if px + sx < 0 or px + sx > (self._selection_image.width() - 1):
                continue
            for sy in range(-radius, radius):
                squared_sy = sy * sy
                if py + sy < 0 or py + sy > (self._selection_image.height() - 1):
                    continue
                if squared_sx + squared_sy < squared_radius:
                    pixel = self._selection_image.pixel(px + sx, py + sy)
                    samples.append(Color.fromARGB(pixel))
        return samples
    
    ##  Get the selection colors on coordinate
    #   All objects (either full objects or single points in a cloud are drawn with an unique color.
    #   \param x from -1 to 1
    #   \param y from -1 to 1
    #   \return color ARGB (values from 0 to 1.)
    def getSelectionColorAtCoordinate(self,x,y):
        if not self._selection_image:
            return None
        px = (0.5 + x / 2.0) * self._viewport_width
        py = (0.5 + y / 2.0) * self._viewport_height
        return Color.fromARGB(self._selection_image.pixel(px,py))
    
    ##  Get object ID at coordinate. 
    #   \param x from -1 to 1
    #   \param y from -1 to 1
    def getIdAtCoordinate(self, x, y):
        if not self._selection_image:
            return None

        px = (0.5 + x / 2.0) * self._viewport_width
        py = (0.5 + y / 2.0) * self._viewport_height

        if px < 0 or px > (self._selection_image.width() - 1) or py < 0 or py > (self._selection_image.height() - 1):
            return None

        pixel = self._selection_image.pixel(px, py)
        return self._selection_map.get(Color.fromARGB(pixel), None)

    ##  Render selection is used to 'highlight' the selected objects
    def setRenderSelection(self, render):
        self._render_selection = render

    def beginRendering(self):
        if not self._initialized:
            self._initialize()

        self._gl.glViewport(0, 0, self._viewport_width, self._viewport_height)
        self._gl.glClearColor(self._background_color.redF(), self._background_color.greenF(), self._background_color.blueF(), self._background_color.alphaF())
        self._gl.glClear(self._gl.GL_COLOR_BUFFER_BIT | self._gl.GL_DEPTH_BUFFER_BIT)

        if not QOpenGLContext.currentContext().format().renderableType() == QSurfaceFormat.OpenGLES:
            self._gl.glPointSize(2)

        self._solids_queue.clear()
        self._transparent_queue.clear()
        self._overlay_queue.clear()

        self._render_selection = True
   
    ##  Put a node in the render queue
    def queueNode(self, node, **kwargs):
        queue_item = { "node": node }

        if "mesh" in kwargs:
            queue_item["mesh"] = kwargs["mesh"]

        queue_item["material"] = kwargs.get("material", self._default_material)

        mode = kwargs.get("mode", Renderer.RenderTriangles)
        if mode is Renderer.RenderLines:
            queue_item["mode"] = self._gl.GL_LINES
        elif mode is Renderer.RenderLineLoop:
            queue_item["mode"] = self._gl.GL_LINE_LOOP
        elif mode is Renderer.RenderPoints:
            queue_item["mode"] = self._gl.GL_POINTS
        else:
            queue_item["mode"] = self._gl.GL_TRIANGLES

        queue_item["wireframe"] = (mode == Renderer.RenderWireframe)

        queue_item["force_single_sided"] = kwargs.get("force_single_sided", False)

        if kwargs.get("end", None):
            queue_item["range"] = [kwargs.get("start", 0), kwargs.get("end")]

        if kwargs.get("transparent", False):
            self._transparent_queue.append(queue_item)
        elif kwargs.get("overlay", False):
            self._overlay_queue.append(queue_item)
        else:
            self._solids_queue.append(queue_item)
    
    ##  Render all nodes in the queue
    def renderQueuedNodes(self):
        self._gl.glEnable(self._gl.GL_DEPTH_TEST)
        self._gl.glDepthFunc(self._gl.GL_LESS)
        self._gl.glDepthMask(self._gl.GL_TRUE)
        self._gl.glDisable(self._gl.GL_CULL_FACE)
        self._gl.glLineWidth(self.getPixelMultiplier())

        self._scene.acquireLock()

        self._camera = self._scene.getActiveCamera()
        if not self._camera:
            Logger.log("e", "No active camera set, can not render")
            self._scene.releaseLock()
            return

        # Render the selection image
        selectable_nodes = []
        for node in DepthFirstIterator(self._scene.getRoot()):
            if node.isSelectable() and node.getMeshData():
                selectable_nodes.append(node)
        if selectable_nodes:
            #TODO: Use a limited area around the mouse rather than a full viewport for rendering
            if self._selection_buffer.width() < self._viewport_width or self._selection_buffer.height() < self._viewport_height:
                self._selection_buffer = self.createFrameBuffer(self._viewport_width, self._viewport_height)

            self._selection_buffer.bind()
            self._gl.glClearColor(0.0, 0.0, 0.0, 0.0)
            self._gl.glClear(self._gl.GL_COLOR_BUFFER_BIT | self._gl.GL_DEPTH_BUFFER_BIT)
            self._gl.glDisable(self._gl.GL_BLEND)
            self._selection_map.clear()
            for node in selectable_nodes:
                if type(node) is PointCloudNode: #Pointcloud node sets vertex color (to be used for point precise selection)
                    self._renderItem({
                        "node": node,
                        "material": self._handle_material,
                        "mode": self._gl.GL_POINTS
                    })
                else :
                    color = self._getObjectColor(node)
                    self._selection_map[color] = id(node)
                    self._selection_material.setUniformValue("u_color", color)
                    self._renderItem({
                        "node": node,
                        "material": self._selection_material,
                        "mode": self._gl.GL_TRIANGLES
                    })
            tool = self._controller.getActiveTool()
            if tool:
                tool_handle = tool.getHandle()
                if tool_handle and tool_handle.getSelectionMesh() and tool_handle.getParent():
                    self._selection_map.update(tool_handle.getSelectionMap())
                    self._gl.glDisable(self._gl.GL_DEPTH_TEST)
                    self._renderItem({
                        "node": tool_handle,
                        "mesh": tool_handle.getSelectionMesh(),
                        "material": self._handle_material,
                        "mode": self._gl.GL_TRIANGLES
                    })
                    self._gl.glEnable(self._gl.GL_DEPTH_TEST)

            self._selection_buffer.release()
            self._selection_image = self._selection_buffer.toImage()

        # Workaround for a MacOSX Intel HD Graphics 6000 Bug
        # Apparently, performing a glReadPixels call on a bound framebuffer breaks releasing the
        # depth buffer, which means the rest of the depth tested geometry never renders. To work-
        # around this we perform a clear here. Note that for some reason we also need to set
        # glClearColor since it is apparently not stored properly.
        self._gl.glClearColor(self._background_color.redF(), self._background_color.greenF(), self._background_color.blueF(), self._background_color.alphaF())
        self._gl.glClear(self._gl.GL_COLOR_BUFFER_BIT | self._gl.GL_DEPTH_BUFFER_BIT)

        self._gl.glEnable(self._gl.GL_STENCIL_TEST)
        self._gl.glStencilMask(0xff)
        self._gl.glClearStencil(0)
        self._gl.glClear(self._gl.GL_STENCIL_BUFFER_BIT)
        self._gl.glStencilFunc(self._gl.GL_ALWAYS, 0xff, 0xff)
        self._gl.glStencilOp(self._gl.GL_REPLACE, self._gl.GL_REPLACE, self._gl.GL_REPLACE)
        self._gl.glStencilMask(0)

        for item in self._solids_queue:
            if Selection.isSelected(item["node"]):
                self._gl.glStencilMask(0xff)
                self._renderItem(item)
                self._gl.glStencilMask(0)
            else:
                self._renderItem(item)

        if self._render_selection:
            self._gl.glStencilMask(0)
            self._gl.glStencilFunc(self._gl.GL_EQUAL, 0, 0xff)
            self._gl.glLineWidth(2 * self.getPixelMultiplier())
            for node in Selection.getAllSelectedObjects():
                if node.getMeshData() and type(node) is not PointCloudNode:
                    self._renderItem({
                        "node": node,
                        "material": self._outline_material,
                        "mode": self._gl.GL_TRIANGLES,
                        "wireframe": True
                    })

            self._gl.glLineWidth(self.getPixelMultiplier())

        self._gl.glDisable(self._gl.GL_STENCIL_TEST)
        self._gl.glDepthMask(self._gl.GL_FALSE)
        self._gl.glEnable(self._gl.GL_BLEND)
        self._gl.glEnable(self._gl.GL_CULL_FACE)
        self._gl.glBlendFunc(self._gl.GL_SRC_ALPHA, self._gl.GL_ONE_MINUS_SRC_ALPHA)

        for item in self._transparent_queue:
            self._renderItem(item)

        self._gl.glDisable(self._gl.GL_DEPTH_TEST)
        self._gl.glDisable(self._gl.GL_CULL_FACE)

        for item in self._overlay_queue:
            self._renderItem(item)

        self._scene.releaseLock()

    def endRendering(self):
        pass

    def _initialize(self):
        profile = QOpenGLVersionProfile()
        profile.setVersion(2, 0)
        self._gl = QOpenGLContext.currentContext().versionFunctions(profile)
        self._gl.initializeOpenGLFunctions()

        self._default_material = self.createMaterial(
                                     Resources.getPath(Resources.Shaders, "default.vert"),
                                     Resources.getPath(Resources.Shaders, "default.frag")
                                )

        self._default_material.setUniformValue("u_ambientColor", Color(0.3, 0.3, 0.3, 1.0))
        self._default_material.setUniformValue("u_diffuseColor", Color(0.5, 0.5, 0.5, 1.0))
        self._default_material.setUniformValue("u_specularColor", Color(0.7, 0.7, 0.7, 1.0))
        self._default_material.setUniformValue("u_shininess", 20.)

        self._selection_buffer = self.createFrameBuffer(128, 128)
        self._selection_material = self.createMaterial(
                                        Resources.getPath(Resources.Shaders, "basic.vert"),
                                        Resources.getPath(Resources.Shaders, "color.frag")
                                   )

        self._handle_material = self.createMaterial(
                                     Resources.getPath(Resources.Shaders, "basic.vert"),
                                     Resources.getPath(Resources.Shaders, "vertexcolor.frag")
                                )

        self._outline_material = self.createMaterial(
                                      Resources.getPath(Resources.Shaders, "outline.vert"),
                                       Resources.getPath(Resources.Shaders, "outline.frag")
                                 )

        self._initialized = True

    def _renderItem(self, item):
        node = item["node"]
        mesh = item.get("mesh", node.getMeshData())
        if not mesh:
            return #Something went wrong, node has no mesh.
        transform = node.getWorldTransformation()
        material = item["material"]
        mode = item["mode"]
        wireframe = item.get("wireframe", False)
        range = item.get("range", None)

        culling_enabled = self._gl.glIsEnabled(self._gl.GL_CULL_FACE)
        if item.get("force_single_sided") and not culling_enabled:
            self._gl.glEnable(self._gl.GL_CULL_FACE)

        material.bind()
        material.setUniformValue("u_projectionMatrix", self._camera.getProjectionMatrix(), cache = False)
        material.setUniformValue("u_viewMatrix", self._camera.getWorldTransformation().getInverse(), cache = False)
        material.setUniformValue("u_viewPosition", self._camera.getWorldPosition(), cache = False)
        material.setUniformValue("u_modelMatrix", transform, cache = False)
        material.setUniformValue("u_lightPosition", self._camera.getWorldPosition() + Vector(0, 50, 0), cache = False)

        if mesh.hasNormals():
            normal_matrix = copy.deepcopy(transform)
            normal_matrix.setRow(3, [0, 0, 0, 1])
            normal_matrix.setColumn(3, [0, 0, 0, 1])
            normal_matrix = normal_matrix.getInverse().getTransposed()
            material.setUniformValue("u_normalMatrix", normal_matrix, cache = False)

        try:
            vertex_buffer = getattr(mesh, vertexBufferProperty)
        except AttributeError:
            vertex_buffer =  self._createVertexBuffer(mesh)
        vertex_buffer.bind()

        if mesh.hasIndices():
            try:
                index_buffer = getattr(mesh, indexBufferProperty)
            except AttributeError:
                index_buffer = self._createIndexBuffer(mesh)
            index_buffer.bind()

        material.enableAttribute("a_vertex", "vector3f", 0)
        offset = mesh.getVertexCount() * 3 * 4

        if mesh.hasNormals():
            material.enableAttribute("a_normal", "vector3f", offset)
            offset += mesh.getVertexCount() * 3 * 4

        if mesh.hasColors():
            material.enableAttribute("a_color", "vector4f", offset)
            offset += mesh.getVertexCount() * 4 * 4

        if mesh.hasUVCoordinates():
            material.enableAttribute("a_uvs", "vector2f", offset)
            offset += mesh.getVertexCount() * 2 * 4

        if wireframe and hasattr(self._gl, "glPolygonMode"):
            self._gl.glPolygonMode(self._gl.GL_FRONT_AND_BACK, self._gl.GL_LINE)

        if mesh.hasIndices():
            if range is None:
                if mode == self._gl.GL_TRIANGLES:
                    self._gl.glDrawElements(mode, mesh.getFaceCount() * 3 , self._gl.GL_UNSIGNED_INT, None)
                else:
                    self._gl.glDrawElements(mode, mesh.getFaceCount(), self._gl.GL_UNSIGNED_INT, None)
            else:
                if mode == self._gl.GL_TRIANGLES:
                    self._gl.glDrawRangeElements(mode, range[0], range[1], range[1] - range[0], self._gl.GL_UNSIGNED_INT, None)
                else:
                    self._gl.glDrawRangeElements(mode, range[0], range[1], range[1] - range[0], self._gl.GL_UNSIGNED_INT, None)
        else:
            self._gl.glDrawArrays(mode, 0, mesh.getVertexCount())

        if wireframe and hasattr(self._gl, "glPolygonMode"):
            self._gl.glPolygonMode(self._gl.GL_FRONT_AND_BACK, self._gl.GL_FILL)

        material.disableAttribute("a_vertex")
        material.disableAttribute("a_normal")
        material.disableAttribute("a_color")
        material.disableAttribute("a_uvs")
        vertex_buffer.release()

        if mesh.hasIndices():
            index_buffer.release()

        material.release()

        if item.get("force_single_sided") and not culling_enabled:
            self._gl.glDisable(self._gl.GL_CULL_FACE)

    def _createVertexBuffer(self, mesh):
        buffer = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer)
        buffer.create()
        buffer.bind()

        buffer_size = mesh.getVertexCount() * 3 * 4 # Vertex count * number of components * sizeof(float32)
        if mesh.hasNormals():
            buffer_size += mesh.getVertexCount() * 3 * 4 # Vertex count * number of components * sizeof(float32)
        if mesh.hasColors():
            buffer_size += mesh.getVertexCount() * 4 * 4 # Vertex count * number of components * sizeof(float32)
        if mesh.hasUVCoordinates():
            buffer_size += mesh.getVertexCount() * 2 * 4 # Vertex count * number of components * sizeof(float32)

        buffer.allocate(buffer_size)

        offset = 0
        vertices = mesh.getVerticesAsByteArray()
        if vertices is not None:
            buffer.write(0, vertices, len(vertices))
            offset += len(vertices)

        if mesh.hasNormals():
            normals = mesh.getNormalsAsByteArray()
            buffer.write(offset, normals, len(normals))
            offset += len(normals)

        if mesh.hasColors():
            colors = mesh.getColorsAsByteArray()
            buffer.write(offset, colors, len(colors))
            offset += len(colors)

        if mesh.hasUVCoordinates():
            uvs = mesh.getUVCoordinatesAsByteArray()
            buffer.write(offset, uvs, len(uvs))
            offset += len(uvs)

        buffer.release()

        setattr(mesh, vertexBufferProperty, buffer)
        return buffer

    def _createIndexBuffer(self, mesh):
        buffer = QOpenGLBuffer(QOpenGLBuffer.IndexBuffer)
        buffer.create()
        buffer.bind()

        data = mesh.getIndicesAsByteArray()
        buffer.allocate(data, len(data))
        buffer.release()

        setattr(mesh, indexBufferProperty, buffer)
        return buffer
    
    ##  Create object color based on ID of node.
    #   \param node Node to get color for
    #   \return Color
    def _getObjectColor(self, node):
        obj_id = id(node)
        r = (obj_id & 0xff000000) >> 24
        g = (obj_id & 0x00ff0000) >> 16
        b = (obj_id & 0x0000ff00) >> 8
        a = (obj_id & 0x000000ff) >> 0
        return Color(r, g, b, a)
class GLWidget(QOpenGLWidget):

    clicked = pyqtSignal()

    PROGRAM_VERTEX_ATTRIBUTE, PROGRAM_TEXCOORD_ATTRIBUTE = range(2)

    vsrc = """
attribute highp vec4 vertex;
attribute mediump vec4 texCoord;
varying mediump vec4 texc;
uniform mediump mat4 matrix;
void main(void)
{
    gl_Position = matrix * vertex;
    texc = texCoord;
}
"""

    fsrc = """
uniform sampler2D texture;
varying mediump vec4 texc;
void main(void)
{
    gl_FragColor = texture2D(texture, texc.st);
}
"""

    coords = (((+1, -1, 0), (-1, -1, 0), (-1, +1, 0), (+1, +1, 0)),
              ((+1, 0, -1), (-1, 0, -1), (-1, 0, +1), (+1, 0, +1)),
              ((0, -1, +1), (0, -1, -1), (0, +1, -1), (0, +1, +1)),
              ((0, -1, -1), (0, -1, +1), (0, +1, +1),
               (0, +1, -1)), ((+1, 0, +1), (-1, 0, +1), (-1, 0, -1),
                              (+1, 0, -1)), ((-1, -1, 0), (+1, -1, 0),
                                             (+1, +1, 0), (-1, +1, 0)))

    def __init__(self, parent=None):
        super(GLWidget, self).__init__(parent)

        print("Constructure")
        self.clearColor = QColor(Qt.black)
        self.xRot = 0
        self.yRot = 0
        self.zRot = 0
        self.program = 0

        self.lastPos = QPoint()

        self.textures = []
        self.texCoords = []
        self.vertices = []
        self.Isvol = False

        self.rate_x = 0.32  #self.s1/sum
        self.rate_y = 0.32  #self.s3/sum
        self.rate_z = 0.35  #self.s2/sum

        self.move_x = 0.0
        self.move_y = 0.0
        self.move_z = 0.0

        default_pix = 100
        _black = np.zeros((default_pix, default_pix, 3), dtype=np.uint8)

        # images

        ax_image = _black
        self.ax_image = ax_image.astype(np.uint8).copy()
        self.ax_image_flip = np.flipud(self.ax_image).copy()

        co_image = _black
        self.co_image = co_image.astype(np.uint8).copy()
        self.co_image_flip = np.fliplr(self.co_image).copy()

        sa_image = _black
        self.sa_image = sa_image.astype(np.uint8).copy()
        self.sa_image_flip = np.fliplr(self.sa_image).copy()

        # QImage

        self.axial_01 = QImage(self.ax_image, default_pix, default_pix,
                               3 * default_pix, QImage.Format_RGB888)
        self.axial_02 = QImage(self.ax_image_flip, default_pix, default_pix,
                               3 * default_pix, QImage.Format_RGB888)

        self.coronal_01 = QImage(self.co_image, default_pix, default_pix,
                                 3 * default_pix, QImage.Format_RGB888)
        self.coronal_02 = QImage(self.co_image_flip, default_pix, default_pix,
                                 3 * default_pix, QImage.Format_RGB888)

        self.sagittal_01 = QImage(self.sa_image, default_pix, default_pix,
                                  3 * default_pix, QImage.Format_RGB888)
        self.sagittal_02 = QImage(self.sa_image_flip, default_pix, default_pix,
                                  3 * default_pix, QImage.Format_RGB888)

    def axis_change(self):
        self.rate_x = 0.0
        self.rate_y = 0.0
        self.rate_z = 0.0
        self.makeObject()
        self.update()

    def Volume_change(self, volume_info=[]):
        print("Volume_change")
        ax_image, co_image, sa_image, ax_sz, co_sz, sa_sz = volume_info
        ax_image = ax_image.astype(np.uint8)
        co_image = co_image.astype(np.uint8)
        sa_image = sa_image.astype(np.uint8)

        # Size
        self.ax_sz = ax_sz
        self.co_sz = co_sz
        self.sa_sz = sa_sz

        # images

        #ax_image = self.brainmask_rgb[int(self.s3/2),:,:,:]
        ax_image = Draw_rect(ax_image, color=[0, 255, 0])
        self.ax_image = np.flipud(ax_image.astype(np.uint8)).copy()
        self.ax_image_flip = np.flipud(self.ax_image).copy()

        #co_image = self.brainmask_rgb[:, int(self.s2 / 2), :, :]
        co_image = Draw_rect(co_image, color=[255, 0, 0])
        self.co_image = co_image.astype(np.uint8).copy()
        self.co_image_flip = np.fliplr(self.co_image).copy()

        #sa_image = self.brainmask_rgb[:, :, int(self.s1/2), :]
        sa_image = Draw_rect(sa_image, color=[0, 0, 255])
        self.sa_image = sa_image.astype(np.uint8).copy()
        self.sa_image_flip = np.fliplr(self.sa_image).copy()

        # QImage
        # ax_sz, co_sz, sa_sz
        self.axial_01 = QImage(self.ax_image, sa_sz, co_sz, 3 * sa_sz,
                               QImage.Format_RGB888)
        self.axial_02 = QImage(self.ax_image_flip, sa_sz, co_sz, 3 * sa_sz,
                               QImage.Format_RGB888)

        self.coronal_01 = QImage(self.co_image, sa_sz, ax_sz, 3 * sa_sz,
                                 QImage.Format_RGB888)
        self.coronal_02 = QImage(self.co_image_flip, sa_sz, ax_sz, 3 * sa_sz,
                                 QImage.Format_RGB888)

        self.sagittal_01 = QImage(self.sa_image, co_sz, ax_sz, 3 * co_sz,
                                  QImage.Format_RGB888)
        self.sagittal_02 = QImage(self.sa_image_flip, co_sz, ax_sz, 3 * co_sz,
                                  QImage.Format_RGB888)

        # ax_sz, co_sz, sa_sz
        sum = ax_sz + co_sz + sa_sz
        # rate
        self.rate_x = co_sz / sum
        self.rate_y = ax_sz / sum
        self.rate_z = sa_sz / sum

        self.makeObject()

        self.update()

    def rotateBy(self, xAngle, yAngle, zAngle):
        self.xRot += xAngle
        self.yRot += yAngle
        self.zRot += zAngle
        self.update()

    def setClearColor(self, color):
        self.clearColor = color
        self.update()

    def load(self):
        # image change

        self.axial_01 = QImage(self.ax_image, self.s3, self.s2, 3 * self.s3,
                               QImage.Format_RGB888)
        self.axial_02 = QImage(self.ax_image_flip, self.s3, self.s2,
                               3 * self.s3, QImage.Format_RGB888)

        self.coronal_01 = QImage(
            self.ax_image, self.s3, self.s2, 3 * self.s3, QImage.Format_RGB888
        )  #QImage(self.co_image, self.s3, self.s1, 3 * self.s3, QImage.Format_RGB888)
        self.coronal_02 = QImage(
            self.ax_image_flip, self.s3, self.s2, 3 * self.s3,
            QImage.Format_RGB888
        )  #QImage(self.co_image_flip, self.s3, self.s1, 3 * self.s3, QImage.Format_RGB888)

        self.sagittal_01 = QImage(
            self.ax_image_flip, self.s3, self.s2, 3 * self.s3,
            QImage.Format_RGB888
        )  #QImage(self.sa_image, self.s2, self.s1, 3 * self.s2, QImage.Format_RGB888)
        self.sagittal_02 = QImage(
            self.ax_image_flip, self.s3, self.s2, 3 * self.s3,
            QImage.Format_RGB888
        )  #QImage(self.sa_image_flip, self.s2, self.s1, 3 * self.s2, QImage.Format_RGB888)

    def initializeGL(self):
        print("initializeGL")
        #self.gl = self.context().versionFunctions()
        #gl.initializeOpenGLFunctions()

        self.makeObject()

        gl.glEnable(gl.GL_DEPTH_TEST)
        gl.glEnable(gl.GL_CULL_FACE)
        gl.glEnable(gl.GL_TEXTURE_2D)
        vshader = QOpenGLShader(QOpenGLShader.Vertex, self)
        vshader.compileSourceCode(self.vsrc)

        fshader = QOpenGLShader(QOpenGLShader.Fragment, self)
        fshader.compileSourceCode(self.fsrc)
        #self.program.
        self.program = QOpenGLShaderProgram()
        self.program.addShader(vshader)
        self.program.addShader(fshader)
        self.program.bindAttributeLocation('vertex',
                                           self.PROGRAM_VERTEX_ATTRIBUTE)
        self.program.bindAttributeLocation('texCoord',
                                           self.PROGRAM_TEXCOORD_ATTRIBUTE)
        self.program.link()

        self.program.bind()
        self.program.setUniformValue('texture', 0)

        self.program.enableAttributeArray(self.PROGRAM_VERTEX_ATTRIBUTE)
        self.program.enableAttributeArray(self.PROGRAM_TEXCOORD_ATTRIBUTE)
        self.program.setAttributeArray(self.PROGRAM_VERTEX_ATTRIBUTE,
                                       self.vertices)
        self.program.setAttributeArray(self.PROGRAM_TEXCOORD_ATTRIBUTE,
                                       self.texCoords)

    def paintGL(self):
        gl.glClearColor(self.clearColor.redF(), self.clearColor.greenF(),
                        self.clearColor.blueF(), self.clearColor.alphaF())
        gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)

        m = QMatrix4x4()
        m.ortho(-0.5, 0.5, 0.5, -0.5, 4.0, 15.0)
        m.translate(0.0, 0.0, -10.0)
        m.rotate(self.xRot / 16.0, 1.0, 0.0, 0.0)
        m.rotate(self.yRot / 16.0, 0.0, 1.0, 0.0)
        m.rotate(self.zRot / 16.0, 0.0, 0.0, 1.0)

        camMatrix = QMatrix4x4(m)

        for i, texture in enumerate(self.textures):
            self.program.setUniformValue('matrix', camMatrix)

            if i == 0:
                localMatrix = QMatrix4x4(camMatrix)
                localMatrix.translate(0.0, 0.0, self.move_z)
                self.program.setUniformValue('matrix', localMatrix)

            elif i == 1:
                localMatrix = QMatrix4x4(camMatrix)
                localMatrix.translate(0.0, 0.0, self.move_z)
                self.program.setUniformValue('matrix', localMatrix)

            elif i == 4:
                localMatrix = QMatrix4x4(camMatrix)
                localMatrix.translate(0.0, self.move_y, 0.0)
                self.program.setUniformValue('matrix', localMatrix)

            elif i == 5:
                localMatrix = QMatrix4x4(camMatrix)
                localMatrix.translate(0.0, self.move_y, 0.0)
                self.program.setUniformValue('matrix', localMatrix)

            elif i == 3:
                localMatrix = QMatrix4x4(camMatrix)
                localMatrix.translate(self.move_x, 0.0, 0.0)
                self.program.setUniformValue('matrix', localMatrix)

            elif i == 2:
                localMatrix = QMatrix4x4(camMatrix)
                localMatrix.translate(self.move_x, 0.0, 0.0)
                self.program.setUniformValue('matrix', localMatrix)

            texture.bind()
            gl.glDrawArrays(gl.GL_TRIANGLE_FAN, i * 4, 4)

    def move_axial(self, volume_info, index):
        ax_image = volume_info
        ax_image = ax_image.astype(np.uint8)
        # images

        # co_image = self.brainmask_rgb[:, int(self.s2 / 2), :, :]
        ax_image = Draw_rect(ax_image, color=[0, 255, 0])
        self.ax_image = np.flipud(ax_image).astype(np.uint8).copy()
        self.ax_image_flip = np.flipud(self.ax_image).copy()

        # QImage
        # ax_sz, co_sz, sa_sz
        w, h, c = np.shape(self.ax_image)
        self.axial_01 = QImage(self.ax_image, h, w, 3 * h,
                               QImage.Format_RGB888)
        self.axial_02 = QImage(self.ax_image_flip, h, w, 3 * h,
                               QImage.Format_RGB888)

        self.move_y = (float(index) / float(self.ax_sz) -
                       0.5) * 2 * self.rate_y
        #print(self.move_y)

        self.makeObject()
        self.update()

    def move_sagittal(self, volume_info, index):
        sa_image = volume_info
        sa_image = sa_image.astype(np.uint8)
        # images

        # co_image = self.brainmask_rgb[:, int(self.s2 / 2), :, :]
        sa_image = Draw_rect(sa_image, color=[0, 0, 255])
        self.sa_image = sa_image.astype(np.uint8).copy()
        self.sa_image_flip = np.fliplr(self.sa_image).copy()
        #self.sa_image_flip = sa_image.astype(np.uint8).copy()
        # QImage
        # ax_sz, co_sz, sa_sz
        w, h, c = np.shape(self.sa_image)
        self.sagittal_01 = QImage(self.sa_image, h, w, 3 * h,
                                  QImage.Format_RGB888)
        self.sagittal_02 = QImage(self.sa_image_flip, h, w, 3 * h,
                                  QImage.Format_RGB888)

        self.move_x = (float(index) / float(self.sa_sz) -
                       0.5) * 2 * self.rate_x
        #print(self.move_x)

        self.makeObject()
        self.update()

    def move_coronal(self, volume_info, index):
        co_image = volume_info
        co_image = co_image.astype(np.uint8)
        # images

        # co_image = self.brainmask_rgb[:, int(self.s2 / 2), :, :]
        co_image = Draw_rect(co_image, color=[255, 0, 0])
        self.co_image = co_image.astype(np.uint8).copy()
        self.co_image_flip = np.fliplr(self.co_image).copy()

        # QImage
        # ax_sz, co_sz, sa_sz
        w, h, c = np.shape(self.co_image)
        self.coronal_01 = QImage(self.co_image, h, w, 3 * h,
                                 QImage.Format_RGB888)
        self.coronal_02 = QImage(self.co_image_flip, h, w, 3 * h,
                                 QImage.Format_RGB888)

        self.move_z = -(float(index) / float(self.co_sz) -
                        0.5) * 2 * self.rate_z
        #print(self.move_z)

        self.makeObject()
        self.update()

    def minimumSizeHint(self):
        return QSize(50, 50)

    def sizeHint(self):
        return QSize(200, 200)

    def makeObject(self):
        '''
            coords = (
            (( +1, -1, 0 ), ( -1, -1, 0 ), ( -1, +1, 0 ), ( +1, +1, 0 )),
            (( +1, 0, -1 ), ( -1, 0, -1 ), ( -1, 0, +1 ), ( +1, 0, +1 )),
            (( 0, -1, +1 ), ( 0, -1, -1 ), ( 0, +1, -1 ), ( 0, +1, +1 )),
            (( 0, -1, -1 ), ( 0, -1, +1 ), ( 0, +1, +1 ), ( 0, +1, -1 )),
            (( +1, 0, +1 ), ( -1, 0, +1 ), ( -1, 0, -1 ), ( +1, 0, -1 )),
            (( -1, -1, 0 ), ( +1, -1, 0 ), ( +1, +1, 0 ), ( -1, +1, 0 ))
        )
       '''

        self.textures = []
        self.texCoords = []
        self.vertices = []

        self.textures.append(QOpenGLTexture(self.coronal_01.mirrored()))
        for j in range(4):
            self.texCoords.append(((j == 0 or j == 3), (j == 0 or j == 1)))
            x, y, z = self.coords[0][j]
            self.vertices.append(
                (self.rate_x * x, self.rate_y * y, self.rate_z * z))

        self.textures.append(QOpenGLTexture(self.coronal_02.mirrored()))
        for j in range(4):
            self.texCoords.append(((j == 0 or j == 3), (j == 0 or j == 1)))
            x, y, z = self.coords[5][j]
            self.vertices.append(
                (self.rate_x * x, self.rate_y * y, self.rate_z * z))

        self.textures.append(QOpenGLTexture(self.sagittal_01.mirrored()))
        for j in range(4):
            self.texCoords.append(((j == 0 or j == 3), (j == 0 or j == 1)))
            x, y, z = self.coords[3][j]
            self.vertices.append(
                (self.rate_x * x, self.rate_y * y, self.rate_z * z))

        self.textures.append(QOpenGLTexture(self.sagittal_02.mirrored()))
        for j in range(4):
            self.texCoords.append(((j == 0 or j == 3), (j == 0 or j == 1)))
            x, y, z = self.coords[2][j]
            self.vertices.append(
                (self.rate_x * x, self.rate_y * y, self.rate_z * z))

        self.textures.append(QOpenGLTexture(self.axial_01.mirrored()))
        for j in range(4):
            self.texCoords.append(((j == 0 or j == 3), (j == 0 or j == 1)))
            x, y, z = self.coords[1][j]
            self.vertices.append(
                (self.rate_x * x, self.rate_y * y, self.rate_z * z))

        self.textures.append(QOpenGLTexture(self.axial_02.mirrored()))
        for j in range(4):
            self.texCoords.append(((j == 0 or j == 3), (j == 0 or j == 1)))
            x, y, z = self.coords[4][j]
            self.vertices.append(
                (self.rate_x * x, self.rate_y * y, self.rate_z * z))

    def resizeGL(self, width, height):
        side = min(width, height)
        gl.glViewport((width - side) // 2, (height - side) // 2, side, side)

    def mousePressEvent(self, event):
        self.lastPos = event.pos()

    def mouseMoveEvent(self, event):
        dx = event.x() - self.lastPos.x()
        dy = event.y() - self.lastPos.y()

        if event.buttons() & Qt.LeftButton:
            self.rotateBy(8 * dy, 8 * dx, 0)
        elif event.buttons() & Qt.RightButton:
            self.rotateBy(8 * dy, 0, 8 * dx)

        self.lastPos = event.pos()

    def mouseReleaseEvent(self, event):
        self.clicked.emit()
Esempio n. 14
0
class QtRenderer(Renderer):
    def __init__(self):
        super().__init__()

        self._controller = Application.getInstance().getController()
        self._scene = self._controller.getScene()

        self._vertex_buffer_cache = {}
        self._index_buffer_cache = {}

        self._initialized = False

        self._light_position = Vector(0, 0, 0)
        self._background_color = QColor(128, 128, 128)
        self._viewport_width = 0
        self._viewport_height = 0
        self._window_width = 0
        self._window_height = 0

        self._batches = []

        self._quad_buffer = None

        self._camera = None

    initialized = Signal()

    ##  Get an integer multiplier that can be used to correct for screen DPI.
    def getPixelMultiplier(self):
        # Standard assumption for screen pixel density is 96 DPI. We use that as baseline to get
        # a multiplication factor we can use for screens > 96 DPI.
        return round(Application.getInstance().primaryScreen().physicalDotsPerInch() / 96.0)

    ##  Get the list of render batches.
    def getBatches(self):
        return self._batches

    ##  Overridden from Renderer.
    #
    #   This makes sure the added render pass has the right size.
    def addRenderPass(self, render_pass):
        super().addRenderPass(render_pass)
        render_pass.setSize(self._viewport_width, self._viewport_height)

    ##  Set background color used when rendering.
    def setBackgroundColor(self, color):
        self._background_color = color

    def getViewportWidth(self):
        return self._viewport_width

    def getViewportHeight(self):
        return self._viewport_height

    ##  Set the viewport size.
    #
    #   \param width The new width of the viewport.
    #   \param height The new height of the viewport.
    def setViewportSize(self, width, height):
        self._viewport_width = width
        self._viewport_height = height

        for render_pass in self._render_passes:
            render_pass.setSize(width, height)

    ##  Set the window size.
    def setWindowSize(self, width, height):
        self._window_width = width
        self._window_height = height

    ##  Get the window size.
    #
    #   \return A tuple of (window_width, window_height)
    def getWindowSize(self):
        return (self._window_width, self._window_height)

    ##  Overrides Renderer::beginRendering()
    def beginRendering(self):
        if not self._initialized:
            self._initialize()

        self._gl.glViewport(0, 0, self._viewport_width, self._viewport_height)
        self._gl.glClearColor(self._background_color.redF(), self._background_color.greenF(), self._background_color.blueF(), self._background_color.alphaF())
        self._gl.glClear(self._gl.GL_COLOR_BUFFER_BIT | self._gl.GL_DEPTH_BUFFER_BIT)
        self._gl.glClearColor(0.0, 0.0, 0.0, 0.0)

    ##  Overrides Renderer::queueNode()
    def queueNode(self, node, **kwargs):
        type = kwargs.pop("type", RenderBatch.RenderType.Solid)
        if kwargs.pop("transparent", False):
            type = RenderBatch.RenderType.Transparent
        elif kwargs.pop("overlay", False):
            type = RenderBatch.RenderType.Overlay

        shader = kwargs.pop("shader", self._default_material)
        batch = RenderBatch(shader, type = type, **kwargs)

        batch.addItem(node.getWorldTransformation(), kwargs.get("mesh", node.getMeshData()), kwargs.pop("uniforms", None))

        self._batches.append(batch)

    ##  Overrides Renderer::render()
    def render(self):
        self._batches.sort()

        for render_pass in self.getRenderPasses():
            render_pass.render()

    ##  Overrides Renderer::endRendering()
    def endRendering(self):
        self._batches.clear()

    ##  Render a full screen quad (rectangle).
    #
    #   The function is used to draw render results on.
    #   \param shader The shader to use when rendering.
    def renderFullScreenQuad(self, shader):
        self._gl.glDisable(self._gl.GL_DEPTH_TEST)
        self._gl.glDisable(self._gl.GL_BLEND)

        shader.setUniformValue("u_modelViewProjectionMatrix", Matrix())

        if OpenGLContext.properties["supportsVertexArrayObjects"]:
            vao = QOpenGLVertexArrayObject()
            vao.create()
            vao.bind()

        self._quad_buffer.bind()

        shader.enableAttribute("a_vertex", "vector3f", 0)
        shader.enableAttribute("a_uvs", "vector2f", 72)

        self._gl.glDrawArrays(self._gl.GL_TRIANGLES, 0, 6)

        shader.disableAttribute("a_vertex")
        shader.disableAttribute("a_uvs")
        self._quad_buffer.release()

    def _initialize(self):
        supports_vao = OpenGLContext.supportsVertexArrayObjects()  # fill the OpenGLContext.properties
        Logger.log("d", "Support for Vertex Array Objects: %s", supports_vao)

        OpenGL.setInstance(OpenGL())
        self._gl = OpenGL.getInstance().getBindingsObject()

        self._default_material = OpenGL.getInstance().createShaderProgram(Resources.getPath(Resources.Shaders, "default.shader"))

        self._render_passes.add(DefaultPass(self._viewport_width, self._viewport_height))
        self._render_passes.add(SelectionPass(self._viewport_width, self._viewport_height))
        self._render_passes.add(CompositePass(self._viewport_width, self._viewport_height))

        buffer = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer)
        buffer.create()
        buffer.bind()
        buffer.allocate(120)
        data = numpy.array([
            -1.0, -1.0, 0.0,
             1.0,  1.0, 0.0,
            -1.0,  1.0, 0.0,
            -1.0, -1.0, 0.0,
             1.0, -1.0, 0.0,
             1.0,  1.0, 0.0,
             0.0,  0.0,
             1.0,  1.0,
             0.0,  1.0,
             0.0,  0.0,
             1.0,  0.0,
             1.0,  1.0
        ], dtype = numpy.float32).tostring()
        buffer.write(0, data, len(data))
        buffer.release()
        self._quad_buffer = buffer

        self._initialized = True
        self.initialized.emit()
    def currentChanged(self, selected, deselected):
        # Get selected item
        self.selected = selected
        self.deselected = deselected

        # Get translation object
        _ = self.app._tr

        # Clear existing settings
        self.win.clear_effect_controls()

        # Get animation details
        animation = self.get_animation_details()
        self.selected_template = animation.get("service")

        # In newer versions of Qt, setting the model invokes the currentChanged signal,
        # but the selection is -1. So, just do nothing here.
        if not self.selected_template:
            return

        # Assign a new unique id for each template selected
        self.generateUniqueFolder()

        # Loop through params
        for param in animation.get("params", []):
            log.info('Using parameter %s: %s' % (param["name"], param["title"]))

            # Is Hidden Param?
            if param["name"] == "start_frame" or param["name"] == "end_frame":
                # add value to dictionary
                self.params[param["name"]] = int(param["default"])

                # skip to next param without rendering the controls
                continue

            # Create Label
            widget = None
            label = QLabel()
            label.setText(_(param["title"]))
            label.setToolTip(_(param["title"]))

            if param["type"] == "spinner":
                # add value to dictionary
                self.params[param["name"]] = float(param["default"])

                # create spinner
                widget = QDoubleSpinBox()
                widget.setMinimum(float(param["min"]))
                widget.setMaximum(float(param["max"]))
                widget.setValue(float(param["default"]))
                widget.setSingleStep(0.01)
                widget.setToolTip(param["title"])
                widget.valueChanged.connect(functools.partial(self.spinner_value_changed, param))

            elif param["type"] == "text":
                # add value to dictionary
                self.params[param["name"]] = _(param["default"])

                # create spinner
                widget = QLineEdit()
                widget.setText(_(param["default"]))
                widget.textChanged.connect(functools.partial(self.text_value_changed, widget, param))

            elif param["type"] == "multiline":
                # add value to dictionary
                self.params[param["name"]] = _(param["default"])

                # create spinner
                widget = QPlainTextEdit()
                widget.setPlainText(_(param["default"]).replace("\\n", "\n"))
                widget.textChanged.connect(functools.partial(self.text_value_changed, widget, param))

            elif param["type"] == "dropdown":
                # add value to dictionary
                self.params[param["name"]] = param["default"]

                # create spinner
                widget = QComboBox()
                widget.currentIndexChanged.connect(functools.partial(self.dropdown_index_changed, widget, param))

                # Add values to dropdown
                if "project_files" in param["name"]:
                    # override files dropdown
                    param["values"] = {}
                    for file in File.filter():
                        if file.data["media_type"] not in ("image", "video"):
                            continue

                        fileName = os.path.basename(file.data["path"])
                        fileExtension = os.path.splitext(fileName)[1]

                        if fileExtension.lower() in (".svg"):
                            continue

                        param["values"][fileName] = "|".join(
                            (file.data["path"],
                             str(file.data["height"]),
                             str(file.data["width"]),
                             file.data["media_type"],
                             str(file.data["fps"]["num"] / file.data["fps"]["den"])
                             )
                        )

                # Add normal values
                box_index = 0
                for k, v in sorted(param["values"].items()):
                    # add dropdown item
                    widget.addItem(_(k), v)

                    # select dropdown (if default)
                    if v == param["default"]:
                        widget.setCurrentIndex(box_index)
                    box_index = box_index + 1

                if not param["values"]:
                    widget.addItem(_("No Files Found"), "")
                    widget.setEnabled(False)

            elif param["type"] == "color":
                # add value to dictionary
                color = QColor(param["default"])
                if "diffuse_color" in param.get("name"):
                    self.params[param["name"]] = [color.redF(), color.greenF(), color.blueF(), color.alphaF()]
                else:
                    self.params[param["name"]] = [color.redF(), color.greenF(), color.blueF()]

                widget = QPushButton()
                widget.setText("")
                widget.setStyleSheet("background-color: {}".format(param["default"]))
                widget.clicked.connect(functools.partial(self.color_button_clicked, widget, param))

            # Add Label and Widget to the form
            if (widget and label):
                self.win.settingsContainer.layout().addRow(label, widget)
            elif (label):
                self.win.settingsContainer.layout().addRow(label)

        # Enable interface
        self.enable_interface()

        # Init slider values
        self.init_slider_values()
Esempio n. 16
0
class GLWidget(QOpenGLWidget):

    clicked = pyqtSignal()

    PROGRAM_VERTEX_ATTRIBUTE, PROGRAM_TEXCOORD_ATTRIBUTE = range(2)

    vsrc = """
attribute highp vec4 vertex;
attribute mediump vec4 texCoord;
varying mediump vec4 texc;
uniform mediump mat4 matrix;
void main(void)
{
    gl_Position = matrix * vertex;
    texc = texCoord;
}
"""

    fsrc = """
uniform sampler2D texture;
varying mediump vec4 texc;
void main(void)
{
    gl_FragColor = texture2D(texture, texc.st);
}
"""

    coords = (
        ((+1, -1, -1), (-1, -1, -1), (-1, +1, -1), (+1, +1, -1)),
        ((+1, +1, -1), (-1, +1, -1), (-1, +1, +1), (+1, +1, +1)),
        ((+1, -1, +1), (+1, -1, -1), (+1, +1, -1), (+1, +1, +1)),
        ((-1, -1, -1), (-1, -1, +1), (-1, +1, +1), (-1, +1, -1)),
        ((+1, -1, +1), (-1, -1, +1), (-1, -1, -1), (+1, -1, -1)),
        ((-1, -1, +1), (+1, -1, +1), (+1, +1, +1), (-1, +1, +1)),
    )

    def __init__(self, parent=None):
        super(GLWidget, self).__init__(parent)

        self.clearColor = QColor(Qt.black)
        self.xRot = 0
        self.yRot = 0
        self.zRot = 0
        self.program = None

        self.lastPos = QPoint()

    def minimumSizeHint(self):
        return QSize(50, 50)

    def sizeHint(self):
        return QSize(200, 200)

    def rotateBy(self, xAngle, yAngle, zAngle):
        self.xRot += xAngle
        self.yRot += yAngle
        self.zRot += zAngle
        self.update()

    def setClearColor(self, color):
        self.clearColor = color
        self.update()

    def initializeGL(self):
        version_profile = QOpenGLVersionProfile()
        version_profile.setVersion(2, 0)
        self.gl = self.context().versionFunctions(version_profile)
        self.gl.initializeOpenGLFunctions()

        self.makeObject()

        self.gl.glEnable(self.gl.GL_DEPTH_TEST)
        self.gl.glEnable(self.gl.GL_CULL_FACE)

        vshader = QOpenGLShader(QOpenGLShader.Vertex, self)
        vshader.compileSourceCode(self.vsrc)

        fshader = QOpenGLShader(QOpenGLShader.Fragment, self)
        fshader.compileSourceCode(self.fsrc)

        self.program = QOpenGLShaderProgram()
        self.program.addShader(vshader)
        self.program.addShader(fshader)
        self.program.bindAttributeLocation("vertex",
                                           self.PROGRAM_VERTEX_ATTRIBUTE)
        self.program.bindAttributeLocation("texCoord",
                                           self.PROGRAM_TEXCOORD_ATTRIBUTE)
        self.program.link()

        self.program.bind()
        self.program.setUniformValue("texture", 0)

        self.program.enableAttributeArray(self.PROGRAM_VERTEX_ATTRIBUTE)
        self.program.enableAttributeArray(self.PROGRAM_TEXCOORD_ATTRIBUTE)
        self.program.setAttributeArray(self.PROGRAM_VERTEX_ATTRIBUTE,
                                       self.vertices)
        self.program.setAttributeArray(self.PROGRAM_TEXCOORD_ATTRIBUTE,
                                       self.texCoords)

    def paintGL(self):
        self.gl.glClearColor(
            self.clearColor.redF(),
            self.clearColor.greenF(),
            self.clearColor.blueF(),
            self.clearColor.alphaF(),
        )
        self.gl.glClear(self.gl.GL_COLOR_BUFFER_BIT
                        | self.gl.GL_DEPTH_BUFFER_BIT)

        m = QMatrix4x4()
        m.ortho(-0.5, 0.5, 0.5, -0.5, 4.0, 15.0)
        m.translate(0.0, 0.0, -10.0)
        m.rotate(self.xRot / 16.0, 1.0, 0.0, 0.0)
        m.rotate(self.yRot / 16.0, 0.0, 1.0, 0.0)
        m.rotate(self.zRot / 16.0, 0.0, 0.0, 1.0)

        self.program.setUniformValue("matrix", m)

        for i, texture in enumerate(self.textures):
            texture.bind()
            self.gl.glDrawArrays(self.gl.GL_TRIANGLE_FAN, i * 4, 4)

    def resizeGL(self, width, height):
        side = min(width, height)
        self.gl.glViewport((width - side) // 2, (height - side) // 2, side,
                           side)

    def mousePressEvent(self, event):
        self.lastPos = event.pos()

    def mouseMoveEvent(self, event):
        dx = event.x() - self.lastPos.x()
        dy = event.y() - self.lastPos.y()

        if event.buttons() & Qt.LeftButton:
            self.rotateBy(8 * dy, 8 * dx, 0)
        elif event.buttons() & Qt.RightButton:
            self.rotateBy(8 * dy, 0, 8 * dx)

        self.lastPos = event.pos()

    def mouseReleaseEvent(self, event):
        self.clicked.emit()

    def makeObject(self):
        self.textures = []
        self.texCoords = []
        self.vertices = []

        root = QFileInfo(__file__).absolutePath()

        for i in range(6):
            self.textures.append(
                QOpenGLTexture(
                    QImage(root + ("/images/side%d.png" %
                                   (i + 1))).mirrored()))

            for j in range(4):
                self.texCoords.append(((j == 0 or j == 3), (j == 0 or j == 1)))

                x, y, z = self.coords[i][j]
                self.vertices.append((0.2 * x, 0.2 * y, 0.2 * z))
Esempio n. 17
0
class GLWidget(QOpenGLWidget):

    clicked = pyqtSignal()

    PROGRAM_VERTEX_ATTRIBUTE, PROGRAM_TEXCOORD_ATTRIBUTE = range(2)

    vsrc = """
attribute highp vec4 vertex;
attribute mediump vec4 texCoord;
varying mediump vec4 texc;
uniform mediump mat4 matrix;
void main(void)
{
    gl_Position = matrix * vertex;
    texc = texCoord;
}
"""

    fsrc = """
uniform sampler2D texture;
varying mediump vec4 texc;
void main(void)
{
    gl_FragColor = texture2D(texture, texc.st);
}
"""

    coords = (((+1, -1, -1), (-1, -1, -1), (-1, +1, -1), (+1, +1, -1)),
              ((+1, +1, -1), (-1, +1, -1), (-1, +1, +1), (+1, +1, +1)),
              ((+1, -1, +1), (+1, -1, -1), (+1, +1, -1), (+1, +1, +1)),
              ((-1, -1, -1), (-1, -1, +1), (-1, +1, +1),
               (-1, +1, -1)), ((+1, -1, +1), (-1, -1, +1), (-1, -1, -1),
                               (+1, -1, -1)), ((-1, -1, +1), (+1, -1, +1),
                                               (+1, +1, +1), (-1, +1, +1)))

    def __init__(self, parent=None):
        super(GLWidget, self).__init__(parent)

        self.clearColor = QColor(Qt.black)
        self.xRot = 0
        self.yRot = 0
        self.zRot = 0
        self.program = None

        self.lastPos = QPoint()

    def minimumSizeHint(self):
        """
        Define the minimum size of the widget
        """
        return QSize(50, 50)

    def sizeHint(self):
        """
        Define a default size for the widget
        """
        return QSize(200, 200)

    def rotateBy(self, xAngle, yAngle, zAngle):
        self.xRot += xAngle
        self.yRot += yAngle
        self.zRot += zAngle
        self.update()

    def setClearColor(self, color):
        self.clearColor = color
        self.update()

    def initializeGL(self):
        self.gl = self.context().versionFunctions()
        self.gl.initializeOpenGLFunctions()

        self.makeObject()

        self.gl.glEnable(self.gl.GL_DEPTH_TEST)
        self.gl.glEnable(self.gl.GL_CULL_FACE)

        vshader = QOpenGLShader(QOpenGLShader.Vertex, self)
        vshader.compileSourceCode(self.vsrc)

        fshader = QOpenGLShader(QOpenGLShader.Fragment, self)
        fshader.compileSourceCode(self.fsrc)

        self.program = QOpenGLShaderProgram()
        self.program.addShader(vshader)
        self.program.addShader(fshader)
        self.program.bindAttributeLocation('vertex',
                                           self.PROGRAM_VERTEX_ATTRIBUTE)
        self.program.bindAttributeLocation('texCoord',
                                           self.PROGRAM_TEXCOORD_ATTRIBUTE)
        self.program.link()

        self.program.bind()
        self.program.setUniformValue('texture', 0)

        self.program.enableAttributeArray(self.PROGRAM_VERTEX_ATTRIBUTE)
        self.program.enableAttributeArray(self.PROGRAM_TEXCOORD_ATTRIBUTE)
        self.program.setAttributeArray(self.PROGRAM_VERTEX_ATTRIBUTE,
                                       self.vertices)
        self.program.setAttributeArray(self.PROGRAM_TEXCOORD_ATTRIBUTE,
                                       self.texCoords)

    def paintGL(self):
        self.gl.glClearColor(self.clearColor.redF(), self.clearColor.greenF(),
                             self.clearColor.blueF(), self.clearColor.alphaF())
        self.gl.glClear(self.gl.GL_COLOR_BUFFER_BIT
                        | self.gl.GL_DEPTH_BUFFER_BIT)

        m = QMatrix4x4()
        m.ortho(-0.5, 0.5, 0.5, -0.5, 4.0, 15.0)
        m.translate(0.0, 0.0, -10.0)

        self.program.setUniformValue('matrix', m)

        self.texture.bind()
        self.gl.glDrawArrays(self.gl.GL_TRIANGLE_FAN, 0, 4)

    def resizeGL(self, width, height):
        side = min(width, height)
        self.gl.glViewport((width - side) // 2, (height - side) // 2, side,
                           side)

    def makeObject(self):
        self.texCoords = [(True, True), (False, True), (False, False),
                          (True, False)]
        self.vertices = [(0.5, -0.5, -0.5), (-0.5, -0.5, -0.5),
                         (-0.5, 0.5, -0.5), (0.5, 0.5, -0.5)]

        my_movie = QImage('/Users/reno/Dropbox/media/cloudy.png')
        self.texture = QOpenGLTexture(my_movie.mirrored())