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:
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. -
This post is deleted!