Beispiel #1
0
    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()
Beispiel #2
0
    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
Beispiel #3
0
    def createIndexBuffer(self, mesh: "MeshData", **kwargs: Any):
        if not mesh.hasIndices():
            return None

        if not kwargs.get("force_recreate", False) and hasattr(
                mesh, OpenGL.IndexBufferProperty):
            if hasattr(mesh, OpenGL.IndexBufferRangeProperty):
                if getattr(mesh, OpenGL.IndexBufferRangeProperty) == (
                        kwargs['index_start'], kwargs['index_stop']):
                    return getattr(mesh, OpenGL.IndexBufferProperty)
            else:
                return getattr(mesh, OpenGL.IndexBufferProperty)

        buffer = QOpenGLBuffer(QOpenGLBuffer.IndexBuffer)
        buffer.create()
        buffer.bind()

        data = cast(bytes, mesh.getIndicesAsByteArray()
                    )  # We check for None at the beginning of the method
        if 'index_start' in kwargs and 'index_stop' in kwargs:
            setattr(mesh, OpenGL.IndexBufferRangeProperty,
                    (kwargs['index_start'], kwargs['index_stop']))
            buffer.allocate(
                data[4 * kwargs['index_start']:4 * kwargs['index_stop']],
                4 * (kwargs['index_stop'] - kwargs['index_start']))
        else:
            buffer.allocate(data, len(data))
        buffer.release()

        setattr(mesh, OpenGL.IndexBufferProperty, buffer)

        return buffer
Beispiel #4
0
    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()
    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
Beispiel #6
0
    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
Beispiel #7
0
    def createVertexBuffer(self, mesh, **kwargs):
        if not kwargs.get("force_recreate", False) and hasattr(
                mesh, OpenGL.VertexBufferProperty):
            return getattr(mesh, OpenGL.VertexBufferProperty)

        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, OpenGL.VertexBufferProperty, buffer)
        return buffer
Beispiel #8
0
    def createIndexBuffer(self, mesh, **kwargs):
        if not mesh.hasIndices():
            return None

        if not kwargs.get("force_recreate", False) and hasattr(mesh, OpenGL.IndexBufferProperty):
            return getattr(mesh, OpenGL.IndexBufferProperty)

        buffer = QOpenGLBuffer(QOpenGLBuffer.IndexBuffer)
        buffer.create()
        buffer.bind()

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

        setattr(mesh, OpenGL.IndexBufferProperty, buffer)
        return buffer
Beispiel #9
0
    def init_buffers(self, vertex_positions, vertex_normals, vertex_tex_coords,
                     faces):
        vertices = array.array('f')
        vertices_dict = {}

        indices = array.array('I')

        # We only use one index buffer, but the obj format indexes vertex positions,
        # normals and texture coordinates separately. So we create a vertex for each used
        # combination of position, normal and texture coordinate.
        for tri in faces:
            for vert_idx, tex_coord_idx, normal_idx in tri:
                key = (vert_idx, tex_coord_idx, normal_idx)

                if key in vertices_dict:
                    indices.append(vertices_dict[key])
                    continue

                vertices.extend(vertex_positions[vert_idx])
                vertices.extend(vertex_normals[normal_idx])
                vertices.extend(vertex_tex_coords[tex_coord_idx])
                assert len(
                    vertices
                ) % self.vertex_buf_stride == 0, 'The vertices contained data which was not aligned to the vertex_buf_stride'
                index = (len(vertices) // self.vertex_buf_stride) - 1
                vertices_dict[key] = index
                indices.append(index)

        self.bounding_sphere_radius = add_axis(self.bounding_box, vertices,
                                               indices)
        self.index_count = len(indices)

        vertex_buf = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer)
        vertex_buf.create()
        vertex_buf.bind()
        vertex_buf.allocate(vertices, len(vertices) * vertices.itemsize)
        vertex_buf.release()

        index_buf = QOpenGLBuffer(QOpenGLBuffer.IndexBuffer)
        index_buf.create()
        index_buf.bind()
        index_buf.allocate(indices, len(indices) * indices.itemsize)
        index_buf.release()

        return vertex_buf, index_buf
Beispiel #10
0
    def createIndexBuffer(self, mesh, **kwargs):
        if not mesh.hasIndices():
            return None

        if not kwargs.get("force_recreate", False) and hasattr(
                mesh, OpenGL.IndexBufferProperty):
            return getattr(mesh, OpenGL.IndexBufferProperty)

        buffer = QOpenGLBuffer(QOpenGLBuffer.IndexBuffer)
        buffer.create()
        buffer.bind()

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

        setattr(mesh, OpenGL.IndexBufferProperty, buffer)
        return buffer
Beispiel #11
0
    def createVertexBuffer(self, mesh, **kwargs):
        if not kwargs.get("force_recreate", False) and hasattr(mesh, OpenGL.VertexBufferProperty):
            return getattr(mesh, OpenGL.VertexBufferProperty)

        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, OpenGL.VertexBufferProperty, buffer)
        return buffer
Beispiel #12
0
    def createIndexBuffer(self, mesh, **kwargs):
        if not mesh.hasIndices():
            return None

        if not kwargs.get("force_recreate", False) and hasattr(mesh, OpenGL.IndexBufferProperty):
            return getattr(mesh, OpenGL.IndexBufferProperty)

        buffer = QOpenGLBuffer(QOpenGLBuffer.IndexBuffer)
        buffer.create()
        buffer.bind()

        data = mesh.getIndicesAsByteArray()
        if 'index_start' in kwargs and 'index_stop' in kwargs:
            buffer.allocate(data[4 * kwargs['index_start']:4 * kwargs['index_stop']], 4*(kwargs['index_stop'] - kwargs['index_start']))
        else:
            buffer.allocate(data, len(data))
        buffer.release()

        setattr(mesh, OpenGL.IndexBufferProperty, buffer)
        return buffer
Beispiel #13
0
    def createIndexBuffer(self, mesh: "MeshData", **kwargs: Any):
        if not mesh.hasIndices():
            return None

        if not kwargs.get("force_recreate", False) and hasattr(mesh, OpenGL.IndexBufferProperty):
            return getattr(mesh, OpenGL.IndexBufferProperty)

        buffer = QOpenGLBuffer(QOpenGLBuffer.IndexBuffer)
        buffer.create()
        buffer.bind()

        data = cast(bytes, mesh.getIndicesAsByteArray()) # We check for None at the beginning of the method
        if 'index_start' in kwargs and 'index_stop' in kwargs:
            buffer.allocate(data[4 * kwargs['index_start']:4 * kwargs['index_stop']], 4*(kwargs['index_stop'] - kwargs['index_start']))
        else:
            buffer.allocate(data, len(data))
        buffer.release()

        setattr(mesh, OpenGL.IndexBufferProperty, buffer)
        return buffer
Beispiel #14
0
    def createIndexBuffer(self, mesh: "MeshData", **kwargs: Any):
        """Create an index buffer for a mesh.

        This will create an index buffer object that is filled with the
        index data of the mesh.

        By default, the associated index buffer should be cached using a
        custom property on the mesh. This should use the IndexBufferProperty
        property name.

        :param mesh: The mesh to create an index buffer for.
        :param kwargs: Keyword arguments.
            Possible values:
            - force_recreate: Ignore the cached value if set and always create a new buffer.
        """
        if not mesh.hasIndices():
            return None

        if not kwargs.get("force_recreate", False) and hasattr(
                mesh, OpenGL.IndexBufferProperty):
            return getattr(mesh, OpenGL.IndexBufferProperty)

        buffer = QOpenGLBuffer(QOpenGLBuffer.IndexBuffer)
        buffer.create()
        buffer.bind()

        data = cast(bytes, mesh.getIndicesAsByteArray()
                    )  # We check for None at the beginning of the method
        if 'index_start' in kwargs and 'index_stop' in kwargs:
            buffer.allocate(
                data[4 * kwargs['index_start']:4 * kwargs['index_stop']],
                4 * (kwargs['index_stop'] - kwargs['index_start']))
        else:
            buffer.allocate(data, len(data))
        buffer.release()

        setattr(mesh, OpenGL.IndexBufferProperty, buffer)

        return buffer
Beispiel #15
0
    def init_buffers(self, vertex_positions, vertex_normals, vertex_tex_coords, faces):
        vertices = array.array('f')
        arr_dict = {}

        indices = array.array('H')

        for tri in faces:
            for vert_idx, tex_coord_idx, normal_idx in tri:
                key = vert_idx << 32 | tex_coord_idx << 16 | normal_idx
                if key in arr_dict:
                    indices.append(arr_dict[key])
                    continue

                vertices.extend(vertex_positions[vert_idx])
                vertices.extend(vertex_normals[normal_idx])
                vertices.extend(vertex_tex_coords[tex_coord_idx])
                assert len(vertices) % self.vertex_buf_stride == 0, 'The vertices contained data which was not aligned to the vertex_buf_stride'
                index = (len(vertices) // self.vertex_buf_stride) - 1
                arr_dict[key] = index
                indices.append(index)

        self.bounding_sphere_radius = add_axis(self.bounding_box, vertex_positions, vertices, indices)
        self.index_count = len(indices)

        vertex_buf = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer)
        vertex_buf.create()
        vertex_buf.bind()
        vertex_buf.allocate(vertices, len(vertices) * vertices.itemsize)
        vertex_buf.release()

        index_buf = QOpenGLBuffer(QOpenGLBuffer.IndexBuffer)
        index_buf.create()
        index_buf.bind()
        index_buf.allocate(indices, len(indices) * indices.itemsize)
        index_buf.release()

        return vertex_buf, index_buf
Beispiel #16
0
    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()
Beispiel #17
0
class Actor(QObject):
    class RenderType:
        NoType = 0  ## No special state changes are done.
        Solid = 1  ## Depth testing and depth writing are enabled.
        Transparent = 2  ## Depth testing is enabled, depth writing is disabled.
        Overlay = 3  ## Depth testing is disabled.
        Types = [NoType, Solid, Transparent, Overlay]

    ##  The mode to render objects in. These correspond to OpenGL render modes.
    class RenderMode:
        Points = GL.GL_POINTS
        Lines = GL.GL_LINES
        LineLoop = GL.GL_LINE_LOOP
        LineStrip = GL.GL_LINE_STRIP
        Triangles = GL.GL_TRIANGLES
        TriangleStrip = GL.GL_TRIANGLE_STRIP
        TriangleFan = GL.GL_TRIANGLE_FAN
        Modes = [
            Points, Lines, LineLoop, LineStrip, Triangles, TriangleStrip,
            TriangleFan
        ]

    ## initialization
    def __init__(self, scene, **kwargs):
        """Initialize actor."""
        super(Actor, self).__init__()

        self._state = 0  #1=scale, 2=rotation, 3=scale
        self._lines = None
        self._axis = 0  # 1=x, 2=y, 3=z

        self._scene = scene
        self._transform = kwargs.get("transform", QMatrix4x4())
        self._render_mode = kwargs.get("mode", Actor.RenderMode.Triangles)
        self._render_type = kwargs.get("type", Actor.RenderType.Solid)
        self._material = kwargs.get("material", Material())
        self._wireframe = kwargs.get(
            "wireframe", Material(diffuse=QVector3D(0.25, 0.25, 0.25)))
        self._viewport = kwargs.get("viewport", (0.0, 0.0, 1.0, 1.0))

        self._name = kwargs.get("name", "Actor" + str(id(self)))
        self._shader_collection = Shaders()
        #self._texture_collection = Textures()
        self._solid_shader = self._shader_collection.uniformMaterialPhongShader(
        )
        self._solid_flat_shader = self._shader_collection.uniformMaterialPhongFlatShader(
        )
        self._nolight_solid_shader = self._shader_collection.uniformMaterialShader(
        )
        self._wireframe_shader = self._shader_collection.uniformMaterialShader(
        )
        self._nolight_wireframe_shader = self._shader_collection.uniformMaterialShader(
        )
        self._normal_visualizing_shader = self._shader_collection.normalVisShader(
        )
        self._active_shader = self._solid_shader
        self._active_material = self._material

        self._vao = QOpenGLVertexArrayObject()
        self._vbo = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer)
        self._ibo = QOpenGLBuffer(QOpenGLBuffer.IndexBuffer)
        self._num_vertices = 0
        self._num_indices = 0

        self._hasNormals = False
        self._hasColors = False
        self._hasTextureCoords = False
        self._hasIndices = False
        self._hasFaces = False

        self._texture = None

        #self._bbox = None
        self._visible = True
        self._enabled = False
        self._pickable = True
        self._selectable = False
        self._selected = False
        self._highlighted = False
        self._errorMaterial = Material.ruby()
        self._errorHighlight = False
        self._warningMaterial = Material.gold()
        self._warningHighlight = False

        self._pickFactor = 1.0

    def update(self, **kwargs):
        """Update this node"""
        self._transform = kwargs.get("transform", QMatrix4x4())
        self._render_mode = kwargs.get("mode", Actor.RenderMode.Triangles)
        self._render_type = kwargs.get("type", Actor.RenderType.Solid)
        self._material = kwargs.get("material", Material())
        self._wireframe = kwargs.get(
            "wireframe", Material(diffuse=QVector3D(0.25, 0.25, 0.25)))

    def scene(self):
        return self._scene

    @property
    def name(self):
        """Returns the name of this actor"""
        return self._name

    def setName(self, name):
        """Sets this actor's name"""
        self._name = name

    @property
    def material(self):
        """Returns the material of this node"""
        return self._material

    def setTransform(self, xform):
        self._transform = xform

    def scale(self, amt):
        if (self._axis == 1):
            self._transform.scale(amt, 1.0, 1.0)
        elif (self._axis == 2):
            self._transform.scale(1.0, amt, 1.0)
        elif (self._axis == 3):
            self._transform.scale(1.0, 1.0, amt)

    def translate(self, pos):
        self._transform[0, 3] += pos.x()
        self._transform[1, 3] += pos.y()
        self._transform[2, 3] += pos.z()

    def transform(self):
        return self._transform

    def position(self):
        xform = self.transform()
        return QVector3D(xform[0, 3], xform[1, 3], xform[2, 3])

    def setPosition(self, pos):
        #print("pos==",pos)
        self._transform = QMatrix4x4()
        self._transform.translate(pos.x(), pos.y(), pos.z())

    def texture(self):
        """Returns the texture image"""
        return self._texture

    def setTexture(self, texture):
        """Sets the current texture"""
        self._texture = texture

    def isPickable(self):
        """Sets whether or not this actor is pickable"""
        return self._pickable

    def setPickable(self, value):
        """Sets whether this actor is pickable"""
        self._pickable = value

    def isVisible(self):
        """Sets the visibility of this actor"""
        return self._visible

    def setVisible(self, value):
        """Sets the visibility of this actor"""
        self._visible = value

    def isEnabled(self):
        """Returns whether this actor is enabled or not"""
        return self._enabled

    def setEnabled(self, value):
        """Sets whether this actor is enabled or not"""
        self._enabled = value

    def setSelectable(self, value):
        """Sets whther or not this actor is selectable"""
        self._selectable = value

    def isSelectable(self):
        """Returns true if actor is selectable"""
        return self._selectable

    def setSelected(self, value):
        """Sets selection to value"""
        self._selected = value

    def isSelected(self):
        """Returns true if it is selected"""
        return self._selected

    def setHighlighted(self, value):
        """Sets the highlight value"""
        self._highlighted = value

    def isHighlighted(self):
        """Returns true if it is highlighted"""
        return self._highlighted

    def setErrorMaterial(self, material):
        """Sets the error material"""
        self._errorMaterial = material

    def setErrorHighlight(self, value):
        """Sets the error highlight"""
        self._errorHighlight = value

    def setWarningMaterial(self, material):
        """Sets the error material"""
        self._warningMaterial = material

    def setWarningHighlight(self, value):
        """Sets the warning highlight"""
        self._warningHighlight = value

    @property
    def shaderCollection(self):
        """Returns the shader collection"""
        return self._shader_collection

    @property
    def renderType(self):
        """Returns the rendering type of this actor"""
        return self._render_type

    @property
    def renderMode(self):
        """Returns the rendering mode of this actor"""
        return self._render_mode

    @property
    def solidShader(self):
        """Returns the default solid shader of this actor"""
        return self._solid_shader

    def setSolidShader(self, shader):
        """Sets the solid shader of this actor"""
        self._solid_shader = shader

    @property
    def solidFlatShader(self):
        """Returns the default solid flat shader of this actor"""
        return self._solid_flat_shader

    def setSolidFlatShader(self, shader):
        """Sets the solid flat shader of this actor"""
        self._solid_flat_shader = shader

    @property
    def noLightSolidShader(self):
        """Returns the default no light solid shader of this actor"""
        return self._nolight_solid_shader

    def setNoLightSolidShader(self, shader):
        """Sets the solid shader of this actor"""
        self._nolight_solid_shader = shader

    @property
    def wireframeShader(self):
        """Returns the default wireframe shader of this actor"""
        return self._wireframe_shader

    def setWireframeShader(self, shader):
        """Sets the default wireframe shader of this actor"""
        self._wireframe_shader = shader

    @property
    def noLightWireframeShader(self):
        """Returns the default no light wireframe shader of this actor"""
        return self._nolight_wireframe_shader

    def setNoLightWireframeShader(self, shader):
        """Sets the no light wireframe shader of this actor"""
        self._nolight_wireframe_shader = shader

    @property
    def numberOfVertices(self):
        """Returns the number of vertices of this actor"""
        return self._num_vertices

    @property
    def numberOfIndices(self):
        """Returns the number of indices of this actor"""
        return self._num_indices

    def mapBuffer(self, offset, count, access):
        """Map the given buffer into a numpy array"""
        vbo_ptr = self._vbo.mapRange(offset, count, access)
        vp_array = ctypes.cast(
            ctypes.c_void_p(int(vbo_ptr)),
            ctypes.POINTER(ctypes.c_byte * self._vbo.size())).contents
        # Note: we could have returned the raw ctypes.c_byte array instead... see pyglet github for map/unmap classes
        array = np.frombuffer(vp_array, 'B')
        return array

    def unmapBuffer(self):
        """Update the GPU with new buffer contents"""
        self._vbo.unmap()

    def updateBuffer(self,
                     vertices=None,
                     normals=None,
                     colors=None,
                     texcoords=None):
        """Update buffer with new data"""
        self._vbo.bind()
        if vertices is not None:
            vertices = vertices.tostring()
            #vertices = np.fromstring(vertices, dtype="uint8")
            self._vbo.write(0, vertices, len(vertices))
        #buffer = self.mapBuffer(0, len(vertices), QOpenGLBuffer.RangeWrite | QOpenGLBuffer.RangeInvalidate)
        #buffer[:len(vertices)] = vertices
        #self.unmapBuffer()
        if normals is not None:
            normals = normals.tostring()
            self._vbo.write(self._offsetNormals, normals, len(normals))
        if colors is not None:
            colors = colors.tostring()
            self._vbo.write(self._offsetColors, colors, len(colors))
        if texcoords is not None:
            texcoords = texcoords.tostring()
            self._vbo.write(self._offsetTexCoords, texcoords, len(texcoords))
        self._vbo.release()

    def create(self,
               vertices,
               normals=None,
               colors=None,
               texcoords=None,
               indices=None,
               faces=None,
               usage=QOpenGLBuffer.StaticDraw):
        """Create object vertex arrays and buffers"""

        ## list of shaders
        shaders = [
            self._solid_shader, self._wireframe_shader,
            self._nolight_solid_shader, self._nolight_wireframe_shader,
            self._normal_visualizing_shader
        ]

        ## bind vao
        self._vao.create()
        self._vao.bind()

        ## define total sizes
        vertices = vertices.tostring()
        total_vertices = len(vertices)
        total_normals = 0
        total_colors = 0
        total_texcoords = 0
        total_faces = 0
        self._num_vertices = total_vertices // (np.dtype(np.float32).itemsize *
                                                3)
        #print('total vertices=', self._num_vertices)

        if normals is not None:
            self._hasNormals = True
            normals = normals.tostring()
            total_normals = len(normals)

        if colors is not None:
            self._hasColors = True
            colors = colors.tostring()
            total_colors = len(colors)

        if texcoords is not None:
            self._hasTextureCoords = True
            texcoords = texcoords.tostring()
            total_texcoords = len(texcoords)

        if indices is not None:
            self._hasIndices = True
            indices = indices.tostring()
            total_indices = len(indices)
            self._num_indices = total_indices // np.dtype(np.uint32).itemsize
            #print('total indices=', self._num_indices)

        if faces is not None:
            self._hasFaces = True
            faces = faces.tostring()
            total_faces = len(faces)

        ## create vertex buffer object
        self._vbo.setUsagePattern(usage)
        self._vbo.create()
        self._vbo.bind()

        ## populate vertex buffer object with data
        offset = 0
        self._vbo.allocate(total_vertices + total_normals + total_colors +
                           total_texcoords + total_faces)
        self._vbo.write(offset, vertices, total_vertices)
        for each in shaders:
            each.setAttributeBuffer('position', GL.GL_FLOAT, offset, 3,
                                    3 * np.dtype(np.float32).itemsize)
        offset += total_vertices
        self._offsetNormals = offset

        if self._hasNormals:
            self._vbo.write(offset, normals, total_normals)
            for each in shaders:
                each.setAttributeBuffer('normal', GL.GL_FLOAT, offset, 3,
                                        3 * np.dtype(np.float32).itemsize)
            offset += total_normals
        if self._hasColors:
            self._offsetColors = offset
            self._vbo.write(offset, colors, total_colors)
            for each in shaders:
                each.setAttributeBuffer('color', GL.GL_FLOAT, offset, 3,
                                        3 * np.dtype(np.float32).itemsize)
            offset += total_colors
        if self._hasTextureCoords:
            self._offsetTexCoords = offset
            self._vbo.write(offset, texcoords, total_texcoords)
            for each in shaders:
                each.setAttributeBuffer('texcoord', GL.GL_FLOAT, offset, 2,
                                        2 * np.dtype(np.float32).itemsize)
            offset += total_texcoords
        if self._hasFaces:
            self._offsetFaces = offset
            self._vbo.write(offset, faces, total_faces)
            for each in shaders:
                each.setAttributeBuffer('faces', GL.GL_INT, offset, 9,
                                        9 * np.dtype(np.int).itemsize)
            offset += total_faces
        ## release buffer
        self._vbo.release(QOpenGLBuffer.VertexBuffer)

        ## enable arrays as part of the vao state
        for each in shaders:
            each.enableAttributeArray('position')
        if self._hasNormals:
            for each in shaders:
                each.enableAttributeArray('normal')
        if self._hasColors:
            for each in shaders:
                each.enableAttributeArray('color')
        if self._hasTextureCoords:
            for each in shaders:
                each.enableAttributeArray('texcoord')
        if self._hasFaces:
            for each in shaders:
                each.enableAttributeArray('faces')
        ## create index buffer object if required by the actor
        if self._hasIndices:
            self._ibo.setUsagePattern(usage)
            self._ibo.create()
            self._ibo.bind()

            self._ibo.allocate(total_indices)
            self._ibo.write(0, indices, total_indices)

        ## release vao
        self._vao.release()

        ## release ibo
        if self._hasIndices:
            self._ibo.release(QOpenGLBuffer.IndexBuffer)

    def setUniformBindings(self, wireframe=False):
        """Sets up uniform shader bindings"""
        normalMatrix = self._transform.normalMatrix()
        self._active_shader.setUniformValue("modelMatrix", self._transform)
        self._active_shader.setUniformValue("viewMatrix",
                                            self._scene.camera.viewMatrix)
        self._active_shader.setUniformValue(
            "projectionMatrix", self._scene.camera.projectionMatrix)
        self._active_shader.setUniformValue("normalMatrix", normalMatrix)

        if self.texture() is not None:
            self._active_shader.setUniformValue("texObject", 1.0)

        ## bind active material
        if self.isSelectable() and self.isSelected():
            self._active_shader.setUniformValue("selected", 0.5)
        else:
            self._active_shader.setUniformValue("selected", 0.0)

        ## set highlight color
        if self.isHighlighted():
            self._active_shader.setUniformValue("material.emission",
                                                QVector3D(0.25, 0.25, 0.25))
        else:
            self._active_shader.setUniformValue(
                "material.emission", self._active_material.emissionColor)
        self._active_shader.setUniformValue("material.ambient",
                                            self._active_material.ambientColor)

        ## set the enabled color
        if self.isEnabled():
            self._active_shader.setUniformValue("material.emission",
                                                QVector3D(0.25, 0.25, 0.25))
            self._active_shader.setUniformValue(
                "material.diffuse", self._active_material.diffuseColor)
        else:
            self._active_shader.setUniformValue(
                "material.diffuse", self._active_material.diffuseColor)
        self._active_shader.setUniformValue(
            "material.specular", self._active_material.specularColor)
        self._active_shader.setUniformValue("material.shininess",
                                            self._active_material.shininess)

        ## set the error and warning colors
        if self._errorHighlight:
            self._active_shader.setUniformValue(
                "material.ambient", self._errorMaterial.ambientColor)
            self._active_shader.setUniformValue(
                "material.diffuse", self._errorMaterial.diffuseColor)
            self._active_shader.setUniformValue(
                "material.specular", self._errorMaterial.specularColor)
            self._active_shader.setUniformValue("material.shininess",
                                                self._errorMaterial.shininess)
        if self._warningHighlight:
            self._active_shader.setUniformValue(
                "material.ambient", self._warningMaterial.ambientColor)
            self._active_shader.setUniformValue(
                "material.diffuse", self._warningMaterial.diffuseColor)
            self._active_shader.setUniformValue(
                "material.specular", self._warningMaterial.specularColor)
            self._active_shader.setUniformValue(
                "material.shininess", self._warningMaterial.shininess)

        ## bind lights
        camera_position = QVector4D(self._scene.camera.position[0],
                                    self._scene.camera.position[1],
                                    self._scene.camera.position[2], 1.0)
        if self._scene.light.headlight:
            if self._scene.light.directional:
                self._active_shader.setUniformValue(
                    "lightPosition", QVector4D(0.0, 0.0, 1.0, 0.0))
            else:
                self._active_shader.setUniformValue(
                    "lightPosition", QVector4D(0.0, 0.0, 0.0, 1.0))
        else:
            self._active_shader.setUniformValue(
                "lightPosition",
                self._scene.camera.viewMatrix * self._scene.light.position)

        self._active_shader.setUniformValue("light.ambient",
                                            self._scene.light.ambientColor)
        self._active_shader.setUniformValue("light.diffuse",
                                            self._scene.light.diffuseColor)
        self._active_shader.setUniformValue("light.specular",
                                            self._scene.light.specularColor)
        self._active_shader.setUniformValue("lightAttenuation",
                                            self._scene.light.attenuation)

    ## This should set up any required state before any actual rendering happens.
    def beginRendering(self, draw_style, lighting, shading, passNumber):
        ## determine right shader to bind
        if lighting:
            if draw_style == GL.GL_LINE:
                self._active_shader = self._wireframe_shader
                self._active_material = self._material if passNumber == 0 else self._wireframe
            else:
                if shading == GL.GL_SMOOTH:
                    self._active_shader = self._solid_shader
                else:
                    self._active_shader = self._solid_flat_shader
                self._active_material = self._material
        else:
            if draw_style == GL.GL_LINE:
                self._active_shader = self._nolight_wireframe_shader
                self._active_material = self._material if passNumber == 0 else self._wireframe
            else:
                self._active_shader = self._nolight_solid_shader
                self._active_material = self._material

        GL.glPolygonMode(GL.GL_FRONT_AND_BACK, draw_style)

        ## determine rendering type to use
        if self._render_type == self.RenderType.Solid:
            GL.glEnable(GL.GL_DEPTH_TEST)
            GL.glDepthMask(GL.GL_TRUE)
        elif self._render_type == self.RenderType.Transparent:
            GL.glEnable(GL.GL_DEPTH_TEST)
            GL.glDepthMask(GL.GL_FALSE)
        elif self._render_type == self.RenderType.Overlay:
            GL.glDisable(GL.GL_DEPTH_TEST)

        ## bind shader
        self._active_shader.bind()

        ## set up uniform variables
        self.setUniformBindings()

        if self._texture is not None:
            #self.glEnable(GL.GL_BLEND)
            self._texture.bind()

        ## bind shader
        self._vao.bind()

    def render(self):
        """Render this actor"""
        raise NotImplementedError(
            "render() must be implemented in child class")

    def endRendering(self):
        """Finished rendering, clean yourself up"""

        ## unbind vao
        self._vao.release()

        ## unbind texture
        if self._texture is not None:
            self._texture.release()

        ## unbind shader
        self._active_shader.release()

    def pickFactor(self):
        """Returns the pick factor for intersection calculations"""
        return self._pickFactor

    def setPickFactor(self, value):
        """Sets the pick factor for intersection calculations"""
        self._pickFactor = value

    def destroy(self):
        self._vao.destroy()
        self._vbo.destroy()
        self._ibo.destroy()

    def intersect(self, ray):
        """Returns intersection if any"""
        tMin = -math.inf
        tMax = math.inf
        obb_xform = self.transform()
        obb_center = QVector3D(obb_xform[0, 3], obb_xform[1, 3], obb_xform[2,
                                                                           3])
        point = obb_center - ray.origin()
        for i in range(3):
            axis = QVector3D(obb_xform[0, i], obb_xform[1, i],
                             obb_xform[2, i]).normalized()
            half_length = QVector3D(obb_xform[i, 0], obb_xform[i, 1],
                                    obb_xform[i, 2]).length() / 2.0
            e = QVector3D.dotProduct(axis, point)
            f = QVector3D.dotProduct(axis, ray.direction())
            if abs(f) > 10E-6:
                t1 = (e + half_length * self._pickFactor) / f
                t2 = (e - half_length * self._pickFactor) / f
                if t1 > t2:
                    w = t1
                    t1 = t2
                    t2 = w
                if t1 > tMin:
                    tMin = t1
                if t2 < tMax:
                    tMax = t2
                if tMin > tMax:
                    return (False, math.inf)
                if tMax < 0:
                    return (False, math.inf)
            elif -e - half_length > 0.0 or -e + half_length < 0.0:
                return (False, math.inf)
        if tMin > 0:
            return (True, tMin)
        return (True, tMax)
Beispiel #18
0
    def createVertexBuffer(self, mesh: "MeshData",
                           **kwargs: Any) -> QOpenGLBuffer:
        if not kwargs.get("force_recreate", False) and hasattr(
                mesh, OpenGL.VertexBufferProperty):
            return getattr(mesh, OpenGL.VertexBufferProperty)

        buffer = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer)
        buffer.create()
        buffer.bind()

        float_size = ctypes.sizeof(ctypes.c_float)
        int_size = ctypes.sizeof(ctypes.c_int)

        buffer_size = mesh.getVertexCount(
        ) * 3 * float_size  # Vertex count * number of components * sizeof(float32)
        if mesh.hasNormals():
            buffer_size += mesh.getVertexCount(
            ) * 3 * float_size  # Vertex count * number of components * sizeof(float32)
        if mesh.hasColors():
            buffer_size += mesh.getVertexCount(
            ) * 4 * float_size  # Vertex count * number of components * sizeof(float32)
        if mesh.hasUVCoordinates():
            buffer_size += mesh.getVertexCount(
            ) * 2 * float_size  # Vertex count * number of components * sizeof(float32)
        for attribute_name in mesh.attributeNames():
            attribute = mesh.getAttribute(attribute_name)
            if attribute["opengl_type"] == "vector2f":
                buffer_size += mesh.getVertexCount() * 2 * float_size
            elif attribute["opengl_type"] == "vector4f":
                buffer_size += mesh.getVertexCount() * 4 * float_size
            elif attribute["opengl_type"] == "int":
                buffer_size += mesh.getVertexCount() * int_size
            elif attribute["opengl_type"] == "float":
                buffer_size += mesh.getVertexCount() * float_size
            else:
                Logger.log(
                    "e",
                    "Could not determine buffer size for attribute [%s] with type [%s]"
                    % (attribute_name, attribute["opengl_type"]))
        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 = cast(bytes, mesh.getNormalsAsByteArray())
            buffer.write(offset, normals, len(normals))
            offset += len(normals)

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

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

        for attribute_name in mesh.attributeNames():
            attribute = mesh.getAttribute(attribute_name)
            attribute_byte_array = attribute["value"].tostring()
            buffer.write(offset, attribute_byte_array,
                         len(attribute_byte_array))
            offset += len(attribute_byte_array)

        buffer.release()

        setattr(mesh, OpenGL.VertexBufferProperty, buffer)
        return buffer
Beispiel #19
0
class DDSWidget(QOpenGLWidget):
    def __init__(self, ddsFile, debugContext = False, parent = None, f = Qt.WindowFlags()):
        super(DDSWidget, self).__init__(parent, f)
        
        self.ddsFile = ddsFile
        
        self.clean = True
        
        self.logger = None
        
        self.program = None
        self.transparecyProgram = None
        self.texture = None
        self.vbo = None
        self.vao = None
        
        self.backgroundColour = None
        
        if debugContext:
            format = QSurfaceFormat()
            format.setOption(QSurfaceFormat.DebugContext)
            self.setFormat(format)
            self.logger = QOpenGLDebugLogger(self)
        
        qDebug("__init__()")
    
    def __del__(self):
        qDebug("__del__()")
        self.cleanup()
    
    def __dtor__(self):
        qDebug("__dtor__()")
        self.cleanup()
    
    def initializeGL(self):
        qDebug("initializeGL()")
        if self.logger:
            self.logger.initialize()
            self.logger.messageLogged.connect(lambda message: qDebug(self.__tr("OpenGL debug message: {0}").fomat(message.message())))
            self.logger.startLogging()
        
        gl = QOpenGLContext.currentContext().versionFunctions(glVersionProfile)
        QOpenGLContext.currentContext().aboutToBeDestroyed.connect(self.cleanup)
        
        self.clean = False
        
        fragmentShader = None
        vertexShader = vertexShader2D
        if self.ddsFile.isCubemap:
            fragmentShader = fragmentShaderCube
            vertexShader = vertexShaderCube
            if QOpenGLContext.currentContext().hasExtension(b"GL_ARB_seamless_cube_map"):
                GL_TEXTURE_CUBE_MAP_SEAMLESS = 0x884F
                gl.glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS)
        elif self.ddsFile.glFormat.samplerType == "F":
            fragmentShader = fragmentShaderFloat
        elif self.ddsFile.glFormat.samplerType == "UI":
            fragmentShader = fragmentShaderUInt
        else:
            fragmentShader = fragmentShaderSInt
        
        self.program = QOpenGLShaderProgram(self)
        self.program.addShaderFromSourceCode(QOpenGLShader.Vertex, vertexShader)
        self.program.addShaderFromSourceCode(QOpenGLShader.Fragment, fragmentShader)
        self.program.bindAttributeLocation("position", 0)
        self.program.bindAttributeLocation("texCoordIn", 1)
        self.program.link()
        
        self.transparecyProgram = QOpenGLShaderProgram(self)
        self.transparecyProgram.addShaderFromSourceCode(QOpenGLShader.Vertex, transparencyVS)
        self.transparecyProgram.addShaderFromSourceCode(QOpenGLShader.Fragment, transparencyFS)
        self.transparecyProgram.bindAttributeLocation("position", 0)
        self.transparecyProgram.link()
        
        self.vao = QOpenGLVertexArrayObject(self)
        vaoBinder = QOpenGLVertexArrayObject.Binder(self.vao)
        
        self.vbo = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer)
        self.vbo.create()
        self.vbo.bind()
        
        theBytes = struct.pack("%sf" % len(vertices), *vertices)
        self.vbo.allocate(theBytes, len(theBytes))
        
        gl.glEnableVertexAttribArray(0)
        gl.glEnableVertexAttribArray(1)
        gl.glVertexAttribPointer(0, 4, gl.GL_FLOAT, False, 6 * 4, 0)
        gl.glVertexAttribPointer(1, 2, gl.GL_FLOAT, False, 6 * 4, 4 * 4)
        
        self.texture = self.ddsFile.asQOpenGLTexture(gl, QOpenGLContext.currentContext())
    
    def resizeGL(self, w, h):
        qDebug("resizeGL(" + str(w) + ", " + str(h) + ")")
        aspectRatioTex = self.texture.width() / self.texture.height() if self.texture else 1.0
        aspectRatioWidget = w / h
        ratioRatio = aspectRatioTex / aspectRatioWidget
        
        self.program.bind()
        self.program.setUniformValue("aspectRatioRatio", ratioRatio)
        self.program.release()
    
    def paintGL(self):
        qDebug("paintGL()")
        gl = QOpenGLContext.currentContext().versionFunctions(glVersionProfile)
        
        vaoBinder = QOpenGLVertexArrayObject.Binder(self.vao)
        
        # Draw checkerboard so transparency is obvious
        self.transparecyProgram.bind()
        
        if self.backgroundColour and self.backgroundColour.isValid():
            self.transparecyProgram.setUniformValue("backgroundColour", self.backgroundColour)
        
        gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)
        
        self.transparecyProgram.release()
        
        self.program.bind()
            
        if self.texture:
            self.texture.bind()
        
        gl.glEnable(gl.GL_BLEND)
        gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA)
        
        gl.glDrawArrays(gl.GL_TRIANGLES, 0, 6)
        
        if self.texture:
            self.texture.release()
        self.program.release()
    
    def cleanup(self):
        qDebug("cleanup()")
        if not self.clean:
            self.makeCurrent()
            
            self.program = None
            self.transparecyProgram = None
            if self.texture:
                self.texture.destroy()
            self.texture = None
            self.vbo.destroy()
            self.vbo = None
            self.vao.destroy()
            self.vao = None
            
            self.doneCurrent()
            self.clean = True
    
    def setBackgroundColour(self, colour):
        self.backgroundColour = colour
    
    def getBackgroundColour(self):
        return self.backgroundColour
    
    def __tr(self, str):
        return QCoreApplication.translate("DDSWidget", str)
class Resources(object):

    directory = os.path.dirname(__file__)

    ''' GLSL program to draw water '''
    waterProgram = None
    ''' Vertex Buffer Object with water mesh '''
    waterVBO = None
    ''' Number of verticies in water mesh '''
    numberOfWaterVertices = None

    ''' GLSL program to draw simplified scene for use 
        as refraction
    '''
    waterRefractionProgram = None
    ''' Framebuffer to draw simplified scene onto '''
    refractionFramebuffer = None
    ''' Water normal map, primary used as bump for
        refraction.
    '''
    refractionNormalMap = None
    
    ''' GLSL program to draw depth info of scene '''
    depthProgram = None
    ''' Framebuffer to draw depth info onto '''
    depthFramebuffer = None
    ''' Texture of depth component '''
    depthTexture = None

    ''' GLSL program to draw landscape '''
    landscapeProgram = None
    ''' Vertex Buffer Object with landscape mesh '''
    landscapeVBO = None
    ''' Number of verticies in landscape mesh '''
    numberOfLandscapeVertices = None

    ''' Texture with information about landscape and 
        water heights. 
        Red component stands for landscape height,
        green for water.
    '''
    heightsTexture = None

    ''' logical_resources.Resources '''
    logicalResources = None

    def __init__(self, logicalResources):
        self.logicalResources = logicalResources


    def initialize(self, gl):
        """
        Creating resources, which require OpenGL context
        """

        self.waterProgram = self.linkProgram(gl, 'water')
        self.waterVBO = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer)
        assert self.waterVBO.create(), "Can't create water vertex buffer =\\"
        self.waterVBO.setUsagePattern(QOpenGLBuffer.DynamicDraw)

        self.waterRefractionProgram = self.linkProgram(gl, 'water-refraction')
        self.refractionFramebuffer = self.createFramebuffer(gl, 512, depth=True)
        self.refractionNormalMap = self.createTexture(gl, wrapMode=QOpenGLTexture.Repeat, filename='normalmap.bmp')

        self.depthProgram = self.linkProgram(gl, 'depth')
        self.depthFramebuffer = self.createFramebuffer(gl, 512)
        self.depthTexture = self.createTexture(gl, self.depthFramebuffer.width(), format=QOpenGLTexture.D32F, allocate=False,
                GL_TEXTURE_COMPARE_MODE=gl.GL_COMPARE_REF_TO_TEXTURE,
                GL_TEXTURE_COMPARE_FUNC=gl.GL_LESS)
        self.depthTexture.bind()
        gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_DEPTH_COMPONENT32, 
                self.depthFramebuffer.width(), self.depthFramebuffer.height(), 
                0, gl.GL_DEPTH_COMPONENT, gl.GL_FLOAT, None)
        self.depthTexture.release()
        assert self.depthFramebuffer.bind()
        gl.glFramebufferTexture2D(gl.GL_FRAMEBUFFER, gl.GL_DEPTH_ATTACHMENT, gl.GL_TEXTURE_2D, self.depthTexture.textureId(), 0)
        assert self.depthFramebuffer.release()

        self.landscapeProgram = self.linkProgram(gl, 'landscape')
        self.landscapeVBO = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer)
        assert self.landscapeVBO.create(), "Can't create water vertex buffer =\\"
        self.landscapeVBO.setUsagePattern(QOpenGLBuffer.DynamicDraw)

        self.heightsTexture = self.createTexture(gl, self.logicalResources.m, self.logicalResources.n, 
                format=QOpenGLTexture.RG32F, filter=QOpenGLTexture.Nearest)
       
        self.updateMeshesAndHeightsTexture(gl)

    def updateMeshesAndHeightsTexture(self, gl, water=True, landscape=True):
        ''' Updates water and/or landscape mesh when they have changed '''
        if landscape:
            self.numberOfLandscapeVertices = self.generateLandscapeMesh(gl, self.landscapeVBO)
        if water:
            self.numberOfWaterVertices = self.generateWaterMesh(gl, self.waterVBO)
        if water or landscape:
            self.updateHeightsTexture(gl)
 
    def updateHeightsTexture(self, gl):
        """
        Updates texture with landscape and water heights info
        """

        if (self.heightsTexture.width() != self.logicalResources.n or 
                self.heightsTexture.height() != self.logicalResources.m):
            self.heightsTexture.destroy()

            self.heightsTexture = self.createTexture(gl, self.logicalResources.m, self.logicalResources.n, 
                    format=QOpenGLTexture.RG32F, filter=QOpenGLTexture.Nearest)
    
        data = []
        for i in range(self.logicalResources.n):
            for j in range(self.logicalResources.m):
                data.extend([ self.logicalResources.landscapeHeightsMatrix[i][j] * ZScale
                            , self.logicalResources.waterHeightsMatrix[i][j] * ZScale])
        data = struct.pack('{}f'.format(len(data)), *data)

        self.heightsTexture.setData(QOpenGLTexture.RG, QOpenGLTexture.Float32, data)


    def loadFile(self, name):
        ''' Loads whole file content as single string '''
        with open(name, 'r') as f: return ''.join(f)
    
    def loadShaders(self, name):
        ''' Loads vertex and fragment shader '''
        vertexShaderSource = self.loadFile('{}/shaders/{}.vert'.format(Resources.directory, name))
        fragmentShaderSource = self.loadFile('{}/shaders/{}.frag'.format(Resources.directory, name))

        return vertexShaderSource, fragmentShaderSource

    def linkProgram(self, gl, name, **kwargs):
        ''' Links GLSL program from *name*.vert and *name*.frag shaders '''
        program = QOpenGLShaderProgram()

        vertexShader, fragmentShader = self.loadShaders(name)
        program.addShaderFromSourceCode(QOpenGLShader.Vertex,
                vertexShader)
        program.addShaderFromSourceCode(QOpenGLShader.Fragment,
                fragmentShader)

        assert program.link(), "Can't link ShaderProgram"
        assert program.bind(), "Can't bind ShaderProgram for initialization"

        for k, v in kwargs.items():
            if type(v) in [list, tuple]:
                program.setUniformValue(k, *v)
            else:
                program.setUniformValue(k, v)

        program.release()

        return program



    def createFramebuffer(self, gl, dim, depth=False, filter=None, internalFormat=None, format=None, type=None):
        ''' Creates framebuffer object with required parameters '''
        if filter is None: filter = gl.GL_LINEAR
        framebuffer = QOpenGLFramebufferObject(dim, dim)
        if depth: framebuffer.setAttachment(QOpenGLFramebufferObject.Depth)
        textureId = framebuffer.texture()
        assert textureId >= 0

        gl.glBindTexture(gl.GL_TEXTURE_2D, textureId)
        gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, filter)
        gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, filter)
        if internalFormat and format and type:
            gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, internalFormat, dim, dim, 0, format, type, None)
        gl.glBindTexture(gl.GL_TEXTURE_2D, 0)

        return framebuffer

    def createTexture(self, gl, width=None, height=None, 
            wrapMode=QOpenGLTexture.ClampToEdge, format=QOpenGLTexture.RGBA8U, filter=QOpenGLTexture.Linear, 
            filename=None, allocate=True, **kwparams):
        ''' Creates texture object with required parameters '''
        assert width is not None or filename is not None
        if height is None: height = width

        if filename:
            texture = QOpenGLTexture(QImage('{}/{}'.format(Resources.directory, filename)).mirrored(), QOpenGLTexture.DontGenerateMipMaps)
        else:
            texture = QOpenGLTexture(QOpenGLTexture.Target2D)
            texture.setFormat(format)
            texture.setSize(width, height)
            if allocate: texture.allocateStorage()

        texture.setMinificationFilter(filter)
        texture.setMagnificationFilter(filter)
        texture.setWrapMode(wrapMode)

        assert texture.create()

        texture.bind()
        for k, v in kwparams.items():
            gl.glTexParameteri(gl.GL_TEXTURE_2D, getattr(gl, k), v)
        texture.release()

        return texture


      
    def safeLandscapeHeightsMatrixIndexer(self, i, j):
        ''' Accesses landscapeHeightsMatrix preventing out of bound errors.
            Used as z0 in *generateLandscapeMesh*.
        '''
        return self.logicalResources.landscapeHeightsMatrix[max(0, min(i, self.logicalResources.n-1))][max(0, min(j, self.logicalResources.m-1))]

    def generateWaterMesh(self, gl, vbo):
        ''' Generates water mesh '''
#       reusing existing code        
        return self.generateLandscapeMesh(gl, vbo, self.logicalResources.waterHeightsMatrix, self.safeLandscapeHeightsMatrixIndexer)

   
    def generateLandscapeMesh(self, gl, vbo, matrix=None, z0=0):
        ''' Generates landscape mesh, and stores in *vbo*.
            Due similiarity also used for generating water mesh. 
        '''
        if matrix is None: matrix = self.logicalResources.landscapeHeightsMatrix

        vbo.bind()

        vertices, normals, indexiesInMatrix = self.buildMeshTriangles(matrix, z0)
        numberOfLandscapeVertices = len(vertices) // 3
        vertices = struct.pack('{}f'.format(len(vertices)), *vertices)
        normals = struct.pack('{}f'.format(len(normals)), *normals)
        indexiesInMatrix = struct.pack('{}f'.format(len(indexiesInMatrix)), *indexiesInMatrix)
        assert numberOfLandscapeVertices*3*4 == len(vertices)
        size = len(vertices) + len(normals) + len(indexiesInMatrix)

        gl.glBufferData(gl.GL_ARRAY_BUFFER, size, None, gl.GL_DYNAMIC_DRAW)
        gl.glBufferSubData(gl.GL_ARRAY_BUFFER, 0, len(vertices), vertices)
        gl.glBufferSubData(gl.GL_ARRAY_BUFFER, len(vertices), len(normals), normals)
        gl.glBufferSubData(gl.GL_ARRAY_BUFFER, len(vertices) + len(normals), len(indexiesInMatrix), indexiesInMatrix)

        vbo.release()

        return numberOfLandscapeVertices

    def buildMeshTriangles(self, matrix, z0):
        ''' Generates lists of vertices, normals and vertices' indexes
            of mesh (landscape or water).
        '''

#       Used for landscape mesh, when z0 is always constant 0
        def constant(value):
            return lambda *args: value


        triangles = []
        n = self.logicalResources.n
        m = self.logicalResources.m

        if not callable(z0):
            triangles.extend([
                     0,      0, z0, (0, 0, -1), (-1, -1),
                     0,      n, z0, (0, 0, -1), (-1, -1),
                     m,      0, z0, (0, 0, -1), (-1, -1),

                     m,      0, z0, (0, 0, -1), (-1, -1),
                     0,      n, z0, (0, 0, -1), (-1, -1),
                     m,      n, z0, (0, 0, -1), (-1, -1),
                ])
            z0 = constant(z0)

        for i in range(     n):
            for j in range(     m):
                p = (j, i)
#       up        
                z1 = z0(i-1, j) + (0 if i == 0 else matrix[i-1][j])
#       right        
                z2 = z0(i, j+1) + (0 if j+1 == m else matrix[i][j+1])
#       down
                z3 = z0(i+1, j) + (0 if i+1 == n else matrix[i+1][j])
#       left
                z4 = z0(i, j-1) + (0 if j == 0 else matrix[i][j-1])
                z05 = z0(i, j)
                z5 = z05 + matrix[i][j]

                triangles.extend([
                        j,     i, z5, (0, 0, 1), p,
                    j + 1,     i, z5, (0, 0, 1), p,
                    j + 1, i + 1, z5, (0, 0, 1), p,
                   
                        j,     i, z5, (0, 0, 1), p,
                    j + 1, i + 1, z5, (0, 0, 1), p,
                        j, i + 1, z5, (0, 0, 1), p,
                    ])
                

                if z4 < z5:
                    z4 = max(z05, z4)
                    triangles.extend([
                            j,     i, z5, (-1, 0, 0), p,
                            j, i + 1, z5, (-1, 0, 0), p,
                            j, i + 1, z4, (-1, 0, 0), p,
                           
                            j,     i, z5, (-1, 0, 0), p,
                            j, i + 1, z4, (-1, 0, 0), p,
                            j,     i, z4, (-1, 0, 0), p,
                        ])

                if z2 < z5:
                    z2 = max(z05, z2)
                    triangles.extend([
                        j + 1, i + 1, z5, ( 1, 0, 0), p,
                        j + 1,     i, z5, ( 1, 0, 0), p,
                        j + 1,     i, z2, ( 1, 0, 0), p,
                       
                        j + 1, i + 1, z5, ( 1, 0, 0), p,
                        j + 1,     i, z2, ( 1, 0, 0), p,
                        j + 1, i + 1, z2, ( 1, 0, 0), p,
                        ])

                if z1 < z5:
                    z1 = max(z05, z1)
                    triangles.extend([
                        j + 1,     i, z5, (0, -1, 0), p,
                            j,     i, z5, (0, -1, 0), p,
                            j,     i, z1, (0, -1, 0), p,
                   
                        j + 1,     i, z5, (0, -1, 0), p,
                            j,     i, z1, (0, -1, 0), p,
                        j + 1,     i, z1, (0, -1, 0), p,
                        ])

                if z3 < z5:
                    z3 = max(z05, z3)
                    triangles.extend([
                            j, i + 1, z5, (0,  1, 0), p,
                        j + 1, i + 1, z5, (0,  1, 0), p,
                        j + 1, i + 1, z3, (0,  1, 0), p,
                       
                            j, i + 1, z5, (0,  1, 0), p,
                        j + 1, i + 1, z3, (0,  1, 0), p,
                            j, i + 1, z3, (0,  1, 0), p,
                        ])

        vertices = []
        normals = []
        indexiesInMatrix = []
        i = 0
        while i < len(triangles):
            vertices.extend([
                triangles[i]/m, 
                triangles[i+1]/n, 
                triangles[i+2] * ZScale])
            normals.extend(triangles[i+3])
            indexiesInMatrix.extend([
                triangles[i+4][0],
                triangles[i+4][1],
                ])
            i += 5

        return vertices, normals, indexiesInMatrix
Beispiel #21
0
    def createVertexBuffer(self, mesh: "MeshData", **kwargs: Any) -> QOpenGLBuffer:
        if not kwargs.get("force_recreate", False) and hasattr(mesh, OpenGL.VertexBufferProperty):
            return getattr(mesh, OpenGL.VertexBufferProperty)

        buffer = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer)
        buffer.create()
        buffer.bind()

        float_size = ctypes.sizeof(ctypes.c_float)
        int_size = ctypes.sizeof(ctypes.c_int)

        buffer_size = mesh.getVertexCount() * 3 * float_size # Vertex count * number of components * sizeof(float32)
        if mesh.hasNormals():
            buffer_size += mesh.getVertexCount() * 3 * float_size # Vertex count * number of components * sizeof(float32)
        if mesh.hasColors():
            buffer_size += mesh.getVertexCount() * 4 * float_size # Vertex count * number of components * sizeof(float32)
        if mesh.hasUVCoordinates():
            buffer_size += mesh.getVertexCount() * 2 * float_size # Vertex count * number of components * sizeof(float32)
        for attribute_name in mesh.attributeNames():
            attribute = mesh.getAttribute(attribute_name)
            if attribute["opengl_type"] == "vector2f":
                buffer_size += mesh.getVertexCount() * 2 * float_size
            elif attribute["opengl_type"] == "vector4f":
                buffer_size += mesh.getVertexCount() * 4 * float_size
            elif attribute["opengl_type"] == "int":
                buffer_size += mesh.getVertexCount() * int_size
            elif attribute["opengl_type"] == "float":
                buffer_size += mesh.getVertexCount() * float_size
            else:
                Logger.log(
                    "e", "Could not determine buffer size for attribute [%s] with type [%s]" % (attribute_name, attribute["opengl_type"]))
        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 = cast(bytes, mesh.getNormalsAsByteArray())
            buffer.write(offset, normals, len(normals))
            offset += len(normals)

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

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

        for attribute_name in mesh.attributeNames():
            attribute = mesh.getAttribute(attribute_name)
            attribute_byte_array = attribute["value"].tostring()
            buffer.write(offset, attribute_byte_array, len(attribute_byte_array))
            offset += len(attribute_byte_array)

        buffer.release()

        setattr(mesh, OpenGL.VertexBufferProperty, buffer)
        return buffer
Beispiel #22
0
    def createVertexBuffer(self, mesh: "MeshData",
                           **kwargs: Any) -> QOpenGLBuffer:
        """Create a Vertex buffer for a mesh.

        This will create a vertex buffer object that is filled with the
        vertex data of the mesh.

        By default, the associated vertex buffer should be cached using a
        custom property on the mesh. This should use the VertexBufferProperty
        property name.

        :param mesh: The mesh to create a vertex buffer for.
        :param kwargs: Keyword arguments.
        Possible values:
        - force_recreate: Ignore the cached value if set and always create a new buffer.
        """
        if not kwargs.get("force_recreate", False) and hasattr(
                mesh, OpenGL.VertexBufferProperty):
            return getattr(mesh, OpenGL.VertexBufferProperty)

        buffer = QOpenGLBuffer(QOpenGLBuffer.VertexBuffer)
        buffer.create()
        buffer.bind()

        float_size = ctypes.sizeof(ctypes.c_float)
        int_size = ctypes.sizeof(ctypes.c_int)

        buffer_size = mesh.getVertexCount(
        ) * 3 * float_size  # Vertex count * number of components * sizeof(float32)
        if mesh.hasNormals():
            buffer_size += mesh.getVertexCount(
            ) * 3 * float_size  # Vertex count * number of components * sizeof(float32)
        if mesh.hasColors():
            buffer_size += mesh.getVertexCount(
            ) * 4 * float_size  # Vertex count * number of components * sizeof(float32)
        if mesh.hasUVCoordinates():
            buffer_size += mesh.getVertexCount(
            ) * 2 * float_size  # Vertex count * number of components * sizeof(float32)
        for attribute_name in mesh.attributeNames():
            attribute = mesh.getAttribute(attribute_name)
            if attribute["opengl_type"] == "vector2f":
                buffer_size += mesh.getVertexCount() * 2 * float_size
            elif attribute["opengl_type"] == "vector4f":
                buffer_size += mesh.getVertexCount() * 4 * float_size
            elif attribute["opengl_type"] == "int":
                buffer_size += mesh.getVertexCount() * int_size
            elif attribute["opengl_type"] == "float":
                buffer_size += mesh.getVertexCount() * float_size
            else:
                Logger.log(
                    "e",
                    "Could not determine buffer size for attribute [%s] with type [%s]"
                    % (attribute_name, attribute["opengl_type"]))
        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 = cast(bytes, mesh.getNormalsAsByteArray())
            buffer.write(offset, normals, len(normals))
            offset += len(normals)

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

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

        for attribute_name in mesh.attributeNames():
            attribute = mesh.getAttribute(attribute_name)
            attribute_byte_array = attribute["value"].tostring()
            buffer.write(offset, attribute_byte_array,
                         len(attribute_byte_array))
            offset += len(attribute_byte_array)

        buffer.release()

        setattr(mesh, OpenGL.VertexBufferProperty, buffer)
        return buffer