Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

What is the best way to load and display .blend files with QT



  • Of course I know about GitHub. If I were to use a game engine it would have to be something that I could easily customize. There are also reasons why I want to do this with QT



  • It is better to use .dae (COLLADA) that has XML format. Qt has a build-in XML parser. The .dae format can keep skeleton animations.

    This is my example how to load a textured object from the .dae format using PyQt5 and PySide2:

    13078d33-1275-4480-a880-411b3ff53dc0-image.png

    PyQt5: https://rextester.com/LPUC58350

    import sys
    import numpy as np
    from OpenGL import GL as gl
    from PyQt5.QtWidgets import QApplication, QOpenGLWidget
    from PyQt5.QtGui import QOpenGLShaderProgram, QOpenGLShader, QOpenGLBuffer
    from PyQt5.QtGui import QOpenGLTexture, QImage
    from PyQt5.QtGui import QMatrix4x4, QVector3D
    from PyQt5.QtXml import QDomDocument, QDomElement
    from PyQt5.QtCore import Qt, QFile, QIODevice
     
    class VertexBuffers:
        vertex_pos_buffer = None
        normal_buffer = None
        tex_coord_buffer = None
        amount_of_vertices = None
     
    class Locations:
        mvp_matrix_location = None
        model_matrix_location = None
        normal_matrix_location = None
     
    class Object3D:
        position = QVector3D(0, 0, 0)
        rotation = QVector3D(0, 0, 0)
        scale = QVector3D(1, 1, 1)
        mvp_matrix = QMatrix4x4()
        model_matrix = QMatrix4x4()
        normal_matrix = QMatrix4x4()
     
        def __init__(self, vert_buffers, locations, texture):
            self.vert_pos_buffer = vert_buffers.vert_pos_buffer
            self.normal_buffer = vert_buffers.normal_buffer
            self.tex_coord_buffer = vert_buffers.tex_coord_buffer
            self.amount_of_vertices = vert_buffers.amount_of_vertices
            
            self.mvp_matrix_location = locations.mvp_matrix_location
            self.model_matrix_location = locations.model_matrix_location
            self.normal_matrix_location = locations.normal_matrix_location
            
            self.texture = texture
     
        def draw(self, program, proj_view_matrix):
            program.bind()
     
            self.vert_pos_buffer.bind()
            program.setAttributeBuffer(0, gl.GL_FLOAT, 0, 3)
            program.enableAttributeArray(0)
     
            self.normal_buffer.bind()
            program.setAttributeBuffer(1, gl.GL_FLOAT, 0, 3)
            program.enableAttributeArray(1)
     
            self.tex_coord_buffer.bind()
            program.setAttributeBuffer(2, gl.GL_FLOAT, 0, 2)
            program.enableAttributeArray(2)
            
            self.model_matrix.setToIdentity()
            self.model_matrix.translate(self.position)
            self.model_matrix.rotate(self.rotation.x(), QVector3D(1, 0, 0))
            self.model_matrix.rotate(self.rotation.y(), QVector3D(0, 1, 0))
            self.model_matrix.rotate(self.rotation.z(), QVector3D(0, 0, 1))
            self.model_matrix.scale(self.scale)
            self.mvp_matrix = proj_view_matrix * self.model_matrix;
            
            self.normal_matrix = self.model_matrix.inverted()
            self.normal_matrix = self.normal_matrix[0].transposed()
            
            program.bind()
            program.setUniformValue(self.mvp_matrix_location, self.mvp_matrix)
            program.setUniformValue(self.model_matrix_location, self.model_matrix)
            program.setUniformValue(self.normal_matrix_location, self.normal_matrix)
            
            self.texture.bind()
     
            gl.glDrawArrays(gl.GL_TRIANGLES, 0, self.amount_of_vertices)
     
    class Window(QOpenGLWidget):
     
        def __init__(self):
            super().__init__()
            self.setWindowTitle("Dae, Collada")
            self.resize(268, 268)
     
        def initializeGL(self):
            gl.glClearColor(0.2, 0.2, 0.2, 1)
            gl.glEnable(gl.GL_DEPTH_TEST)
            vertShaderSrc = """
                #version 330 core
     
                in vec4 aPosition;
                in vec4 aNormal;
                in vec2 aTexCoord;
     
                uniform mat4 uMvpMatrix;
                uniform mat4 uModelMatrix;
                uniform mat4 uNormalMatrix;
     
                out vec3 vPosition;
                out vec3 vNormal;
                out vec2 vTexCoord;
     
                void main()
                {
                    gl_Position = uMvpMatrix * aPosition;
                    vPosition = vec3(uModelMatrix * aPosition);
                    vNormal = normalize(vec3(uNormalMatrix * aNormal));
                    vTexCoord = aTexCoord;
                }
            """
            fragShaderSrc = """
                #version 330 core
     
                const vec3 lightColor = vec3(0.8, 0.8, 0.8);
                const vec3 lightPosition = vec3(5.0, 7.0, 2.0);
                const vec3 ambientLight = vec3(0.3, 0.3, 0.3);
     
                uniform sampler2D uSampler;
     
                in vec3 vPosition;
                in vec3 vNormal;
                in vec2 vTexCoord;
     
                void main()
                {
                    vec4 color = texture2D(uSampler, vTexCoord);
                    vec3 normal = normalize(vNormal);
                    vec3 lightDirection = normalize(lightPosition - vPosition);
                    float nDotL = max(dot(lightDirection, normal), 0.0);
                    vec3 diffuse = lightColor * color.rgb * nDotL;
                    vec3 ambient = ambientLight * color.rgb;
                    gl_FragColor = vec4(diffuse + ambient, color.a);
                }
            """
            self.program = QOpenGLShaderProgram()
            self.program.addShaderFromSourceCode(QOpenGLShader.Vertex, vertShaderSrc)
            self.program.addShaderFromSourceCode(QOpenGLShader.Fragment, fragShaderSrc)
            self.program.link()
            self.program.bind()
            self.program.bindAttributeLocation("aPosition", 0)
            self.program.bindAttributeLocation("aNormal", 1)
            self.program.bindAttributeLocation("aTexCoord", 2)
            locations = Locations()
            self.program.bind()
            locations.mvp_matrix_location = self.program.uniformLocation("uMvpMatrix");
            locations.model_matrix_location = self.program.uniformLocation("uModelMatrix")
            locations.normal_matrix_location = self.program.uniformLocation("uNormalMatrix")
            self.vert_buffers = self.initVertexBuffers("assets/cube.dae")
            self.proj_view_matrix = QMatrix4x4()
            self.proj_matrix = QMatrix4x4()
            self.view_matrix = QMatrix4x4()
            self.view_matrix.lookAt(
                QVector3D(2, 3, 5),
                QVector3D(0, 0, 0),
                QVector3D(0, 1, 0))
            
            self.texture = QOpenGLTexture(QOpenGLTexture.Target2D)
            self.texture.create()
            self.texture.setData(QImage("assets/cube.png"))
            self.texture.setMinMagFilters(QOpenGLTexture.Linear, QOpenGLTexture.Linear)
            self.texture.setWrapMode(QOpenGLTexture.ClampToEdge)
            self.plane = Object3D(self.vert_buffers, locations, self.texture)
            
        def paintGL(self):
            gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
            self.proj_view_matrix = self.proj_matrix * self.view_matrix
            self.plane.draw(self.program, self.proj_view_matrix)
     
        def resizeGL(self, w, h):
            gl.glViewport(0, 0, w, h)
            self.proj_matrix.setToIdentity()
            self.proj_matrix.perspective(50, float(w) / float(h), 0.1, 100)
            
        def initVertexBuffers(self, path):
            xml_doc = QDomDocument()
            file = QFile(path)
            if not file.open(QIODevice.ReadOnly):
                print("Failed to open the file: " + path)
            xml_doc.setContent(file)
            file.close()
            
            vert_pos_array = []
            normal_array = []
            tex_coord_array = []
            index_array = []
            
            root = xml_doc.documentElement()
            dae_elem = root.firstChildElement()
            while not dae_elem.isNull():
                if dae_elem.tagName() == "library_geometries":
                    geom_elem = dae_elem.firstChildElement()
                    if geom_elem.tagName() == "geometry":
                        mesh_elem = geom_elem.firstChildElement()
                        if mesh_elem.tagName() == "mesh":
                            mesh_child_elem = mesh_elem.firstChildElement()
                            while not mesh_child_elem.isNull():
                                float_array_elem = mesh_child_elem.firstChildElement()
                                str_array = float_array_elem.firstChild().toText().data().split(" ")
                                if mesh_child_elem.attribute("id").endswith("-mesh-positions"):
                                    vert_pos_array = list(map(float, str_array))
                                if mesh_child_elem.attribute("id").endswith("-mesh-normals"):
                                    normal_array = list(map(float, str_array))
                                if mesh_child_elem.attribute("id").endswith("-mesh-map-0"):
                                    tex_coord_array = list(map(float, str_array))
                                if mesh_child_elem.tagName() == "triangles" or mesh_child_elem.tagName() == "polylist":
                                    p_child_elem = mesh_child_elem.firstChildElement()
                                    while not p_child_elem.isNull():
                                        if p_child_elem.tagName() == "p":
                                            str_indices = p_child_elem.firstChild().toText().data().split(" ")
                                            index_array = list(map(int, str_indices))
                                        p_child_elem = p_child_elem.nextSiblingElement()
                                mesh_child_elem = mesh_child_elem.nextSiblingElement()
                dae_elem = dae_elem.nextSiblingElement()
            # print(vert_pos_array)
            # print(normal_array)
            # print(tex_coord_array)
            # print(index_array)
            
            num_of_attributes = 3
            vert_positions = []
            normals = []
            tex_coords = []
            for i in range(0, len(index_array), num_of_attributes):
                vert_pos_index = index_array[i + 0]
                vert_positions.append(vert_pos_array[vert_pos_index * 3 + 0])
                vert_positions.append(vert_pos_array[vert_pos_index * 3 + 1])
                vert_positions.append(vert_pos_array[vert_pos_index * 3 + 2])
                
                normal_index = index_array[i + 1]
                normals.append(normal_array[normal_index * 3 + 0])
                normals.append(normal_array[normal_index * 3 + 1])
                normals.append(normal_array[normal_index * 3 + 2])
                
                tex_coord_index = index_array[i + 2]
                tex_coords.append(tex_coord_array[tex_coord_index * 2 + 0])
                tex_coords.append(tex_coord_array[tex_coord_index * 2 + 1])
            # print(vert_positions)
            # print(normals)
            # print(tex_coords)
            
            output = {}
     
            vert_positions = np.array(vert_positions, dtype=np.float32)
            vert_pos_buffer = QOpenGLBuffer()
            vert_pos_buffer.create()
            vert_pos_buffer.bind()
            vert_pos_buffer.allocate(vert_positions, len(vert_positions) * 4)
            
            normals = np.array(normals, dtype=np.float32)
            normal_buffer = QOpenGLBuffer()
            normal_buffer.create()
            normal_buffer.bind()
            normal_buffer.allocate(normals, len(normals) * 4)
            
            tex_coords = np.array(tex_coords, dtype=np.float32)
            tex_coord_buffer = QOpenGLBuffer()
            tex_coord_buffer.create()
            tex_coord_buffer.bind()
            tex_coord_buffer.allocate(tex_coords, len(tex_coords) * 4)
     
            vert_buffers = VertexBuffers()
            vert_buffers.vert_pos_buffer = vert_pos_buffer
            vert_buffers.normal_buffer = normal_buffer
            vert_buffers.tex_coord_buffer = tex_coord_buffer
            vert_buffers.amount_of_vertices = int(len(index_array) / 3);
            
            return vert_buffers
     
    def main():
        QApplication.setAttribute(Qt.AA_UseDesktopOpenGL)
        app = QApplication(sys.argv)
        w = Window()
        w.show()
        sys.exit(app.exec_())
     
    if __name__ == "__main__":
        main()
    

    PySide2: https://rextester.com/APZF83828

    import sys
    import numpy as np
    from OpenGL import GL as gl
    from PySide2.QtWidgets import QApplication, QOpenGLWidget
    from PySide2.QtGui import QOpenGLShaderProgram, QOpenGLShader, QOpenGLBuffer
    from PySide2.QtGui import QOpenGLTexture, QImage
    from PySide2.QtGui import QMatrix4x4, QVector3D
    from PySide2.QtXml import QDomDocument, QDomElement
    from PySide2.QtCore import Qt, QFile, QIODevice
     
    class VertexBuffers:
        vertex_pos_buffer = None
        normal_buffer = None
        tex_coord_buffer = None
        amount_of_vertices = None
     
    class Locations:
        mvp_matrix_location = None
        model_matrix_location = None
        normal_matrix_location = None
     
    class Object3D:
        position = QVector3D(0, 0, 0)
        rotation = QVector3D(0, 0, 0)
        scale = QVector3D(1, 1, 1)
        mvp_matrix = QMatrix4x4()
        model_matrix = QMatrix4x4()
        normal_matrix = QMatrix4x4()
     
        def __init__(self, vert_buffers, locations, texture):
            self.vert_pos_buffer = vert_buffers.vert_pos_buffer
            self.normal_buffer = vert_buffers.normal_buffer
            self.tex_coord_buffer = vert_buffers.tex_coord_buffer
            self.amount_of_vertices = vert_buffers.amount_of_vertices
            
            self.mvp_matrix_location = locations.mvp_matrix_location
            self.model_matrix_location = locations.model_matrix_location
            self.normal_matrix_location = locations.normal_matrix_location
            
            self.texture = texture
     
        def draw(self, program, proj_view_matrix):
            program.bind()
     
            self.vert_pos_buffer.bind()
            program.setAttributeBuffer(0, gl.GL_FLOAT, 0, 3)
            program.enableAttributeArray(0)
     
            self.normal_buffer.bind()
            program.setAttributeBuffer(1, gl.GL_FLOAT, 0, 3)
            program.enableAttributeArray(1)
     
            self.tex_coord_buffer.bind()
            program.setAttributeBuffer(2, gl.GL_FLOAT, 0, 2)
            program.enableAttributeArray(2)
            
            self.model_matrix.setToIdentity()
            self.model_matrix.translate(self.position)
            self.model_matrix.rotate(self.rotation.x(), QVector3D(1, 0, 0))
            self.model_matrix.rotate(self.rotation.y(), QVector3D(0, 1, 0))
            self.model_matrix.rotate(self.rotation.z(), QVector3D(0, 0, 1))
            self.model_matrix.scale(self.scale)
            self.mvp_matrix = proj_view_matrix * self.model_matrix;
            
            self.normal_matrix = self.model_matrix.inverted()
            self.normal_matrix = self.normal_matrix[0].transposed()
            
            program.bind()
            program.setUniformValue(self.mvp_matrix_location, self.mvp_matrix)
            program.setUniformValue(self.model_matrix_location, self.model_matrix)
            program.setUniformValue(self.normal_matrix_location, self.normal_matrix)
            
            self.texture.bind()
     
            gl.glDrawArrays(gl.GL_TRIANGLES, 0, self.amount_of_vertices)
     
    class Window(QOpenGLWidget):
     
        def __init__(self):
            super().__init__()
            self.setWindowTitle("Dae, Collada")
            self.resize(268, 268)
     
        def initializeGL(self):
            gl.glClearColor(0.2, 0.2, 0.2, 1)
            gl.glEnable(gl.GL_DEPTH_TEST)
            vertShaderSrc = """
                #version 330 core
     
                in vec4 aPosition;
                in vec4 aNormal;
                in vec2 aTexCoord;
     
                uniform mat4 uMvpMatrix;
                uniform mat4 uModelMatrix;
                uniform mat4 uNormalMatrix;
     
                out vec3 vPosition;
                out vec3 vNormal;
                out vec2 vTexCoord;
     
                void main()
                {
                    gl_Position = uMvpMatrix * aPosition;
                    vPosition = vec3(uModelMatrix * aPosition);
                    vNormal = normalize(vec3(uNormalMatrix * aNormal));
                    vTexCoord = aTexCoord;
                }
            """
            fragShaderSrc = """
                #version 330 core
     
                const vec3 lightColor = vec3(0.8, 0.8, 0.8);
                const vec3 lightPosition = vec3(5.0, 7.0, 2.0);
                const vec3 ambientLight = vec3(0.3, 0.3, 0.3);
     
                uniform sampler2D uSampler;
     
                in vec3 vPosition;
                in vec3 vNormal;
                in vec2 vTexCoord;
     
                void main()
                {
                    vec4 color = texture2D(uSampler, vTexCoord);
                    vec3 normal = normalize(vNormal);
                    vec3 lightDirection = normalize(lightPosition - vPosition);
                    float nDotL = max(dot(lightDirection, normal), 0.0);
                    vec3 diffuse = lightColor * color.rgb * nDotL;
                    vec3 ambient = ambientLight * color.rgb;
                    gl_FragColor = vec4(diffuse + ambient, color.a);
                }
            """
            self.program = QOpenGLShaderProgram()
            self.program.addShaderFromSourceCode(QOpenGLShader.Vertex, vertShaderSrc)
            self.program.addShaderFromSourceCode(QOpenGLShader.Fragment, fragShaderSrc)
            self.program.link()
            self.program.bind()
            self.program.bindAttributeLocation("aPosition", 0)
            self.program.bindAttributeLocation("aNormal", 1)
            self.program.bindAttributeLocation("aTexCoord", 2)
            locations = Locations()
            self.program.bind()
            locations.mvp_matrix_location = self.program.uniformLocation("uMvpMatrix");
            locations.model_matrix_location = self.program.uniformLocation("uModelMatrix")
            locations.normal_matrix_location = self.program.uniformLocation("uNormalMatrix")
            self.vert_buffers = self.initVertexBuffers("assets/cube.dae")
            self.proj_view_matrix = QMatrix4x4()
            self.proj_matrix = QMatrix4x4()
            self.view_matrix = QMatrix4x4()
            self.view_matrix.lookAt(
                QVector3D(2, 3, 5),
                QVector3D(0, 0, 0),
                QVector3D(0, 1, 0))
            
            self.texture = QOpenGLTexture(QOpenGLTexture.Target2D)
            self.texture.create()
            self.texture.setData(QImage("assets/cube.png"))
            self.texture.setMinMagFilters(QOpenGLTexture.Linear, QOpenGLTexture.Linear)
            self.texture.setWrapMode(QOpenGLTexture.ClampToEdge)
            self.plane = Object3D(self.vert_buffers, locations, self.texture)
            
        def paintGL(self):
            gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
            self.proj_view_matrix = self.proj_matrix * self.view_matrix
            self.plane.draw(self.program, self.proj_view_matrix)
     
        def resizeGL(self, w, h):
            gl.glViewport(0, 0, w, h)
            self.proj_matrix.setToIdentity()
            self.proj_matrix.perspective(50, float(w) / float(h), 0.1, 100)
            
        def initVertexBuffers(self, path):
            xml_doc = QDomDocument()
            file = QFile(path)
            if not file.open(QIODevice.ReadOnly):
                print("Failed to open the file: " + path)
            xml_doc.setContent(file)
            file.close()
            
            vert_pos_array = []
            normal_array = []
            tex_coord_array = []
            index_array = []
            
            root = xml_doc.documentElement()
            dae_elem = root.firstChildElement()
            while not dae_elem.isNull():
                if dae_elem.tagName() == "library_geometries":
                    geom_elem = dae_elem.firstChildElement()
                    if geom_elem.tagName() == "geometry":
                        mesh_elem = geom_elem.firstChildElement()
                        if mesh_elem.tagName() == "mesh":
                            mesh_child_elem = mesh_elem.firstChildElement()
                            while not mesh_child_elem.isNull():
                                float_array_elem = mesh_child_elem.firstChildElement()
                                str_array = float_array_elem.firstChild().toText().data().split(" ")
                                if mesh_child_elem.attribute("id").endswith("-mesh-positions"):
                                    vert_pos_array = list(map(float, str_array))
                                if mesh_child_elem.attribute("id").endswith("-mesh-normals"):
                                    normal_array = list(map(float, str_array))
                                if mesh_child_elem.attribute("id").endswith("-mesh-map-0"):
                                    tex_coord_array = list(map(float, str_array))
                                if mesh_child_elem.tagName() == "triangles" or mesh_child_elem.tagName() == "polylist":
                                    p_child_elem = mesh_child_elem.firstChildElement()
                                    while not p_child_elem.isNull():
                                        if p_child_elem.tagName() == "p":
                                            str_indices = p_child_elem.firstChild().toText().data().split(" ")
                                            index_array = list(map(int, str_indices))
                                        p_child_elem = p_child_elem.nextSiblingElement()
                                mesh_child_elem = mesh_child_elem.nextSiblingElement()
                dae_elem = dae_elem.nextSiblingElement()
            # print(vert_pos_array)
            # print(normal_array)
            # print(tex_coord_array)
            # print(index_array)
            
            num_of_attributes = 3
            vert_positions = []
            normals = []
            tex_coords = []
            for i in range(0, len(index_array), num_of_attributes):
                vert_pos_index = index_array[i + 0]
                vert_positions.append(vert_pos_array[vert_pos_index * 3 + 0])
                vert_positions.append(vert_pos_array[vert_pos_index * 3 + 1])
                vert_positions.append(vert_pos_array[vert_pos_index * 3 + 2])
                
                normal_index = index_array[i + 1]
                normals.append(normal_array[normal_index * 3 + 0])
                normals.append(normal_array[normal_index * 3 + 1])
                normals.append(normal_array[normal_index * 3 + 2])
                
                tex_coord_index = index_array[i + 2]
                tex_coords.append(tex_coord_array[tex_coord_index * 2 + 0])
                tex_coords.append(tex_coord_array[tex_coord_index * 2 + 1])
            # print(vert_positions)
            # print(normals)
            # print(tex_coords)
            
            output = {}
     
            vert_positions = np.array(vert_positions, dtype=np.float32)
            vert_pos_buffer = QOpenGLBuffer()
            vert_pos_buffer.create()
            vert_pos_buffer.bind()
            vert_pos_buffer.allocate(vert_positions, len(vert_positions) * 4)
            
            normals = np.array(normals, dtype=np.float32)
            normal_buffer = QOpenGLBuffer()
            normal_buffer.create()
            normal_buffer.bind()
            normal_buffer.allocate(normals, len(normals) * 4)
            
            tex_coords = np.array(tex_coords, dtype=np.float32)
            tex_coord_buffer = QOpenGLBuffer()
            tex_coord_buffer.create()
            tex_coord_buffer.bind()
            tex_coord_buffer.allocate(tex_coords, len(tex_coords) * 4)
     
            vert_buffers = VertexBuffers()
            vert_buffers.vert_pos_buffer = vert_pos_buffer
            vert_buffers.normal_buffer = normal_buffer
            vert_buffers.tex_coord_buffer = tex_coord_buffer
            vert_buffers.amount_of_vertices = int(len(index_array) / 3);
            
            return vert_buffers
     
    def main():
        QApplication.setAttribute(Qt.AA_UseDesktopOpenGL)
        app = QApplication(sys.argv)
        w = Window()
        w.show()
        sys.exit(app.exec_())
     
    if __name__ == "__main__":
        main()
    


  • That is nice but I want to use C++ and be able to load my procedural textures.



  • @AI_Messiah
    There isn't any simple/supported way to do that. The intended workflow from Blender is to bake your textures to images, and export as something other than .blend for import into other applications. If you want to use Blender procedural textures in your own renderer, you'll either need to use a ton of Blender code in your app, or study their behavior and try to reimplement them from scratch.


  • Banned

    This post is deleted!

Log in to reply