From fb9c320633e433a9a037edd73fc93feb4f526453 Mon Sep 17 00:00:00 2001 From: Shaun Reed Date: Sat, 22 Mar 2025 12:55:31 -0400 Subject: [PATCH] Fix segfault binding unloaded texture. --- example-app/examplescene.cpp | 13 +------------ src/qtk/meshrenderer.cpp | 8 ++------ src/qtk/skybox.cpp | 29 ++++++++++++++++++++++++----- src/qtk/skybox.h | 7 ++++++- src/qtk/texture.cpp | 4 ++-- src/qtk/texture.h | 26 ++++++++++++++++++++------ 6 files changed, 55 insertions(+), 32 deletions(-) diff --git a/example-app/examplescene.cpp b/example-app/examplescene.cpp index ecd60d0..bb2c719 100644 --- a/example-app/examplescene.cpp +++ b/example-app/examplescene.cpp @@ -22,18 +22,7 @@ ExampleScene::~ExampleScene() = default; void ExampleScene::init() { - setSkybox(new Qtk::Skybox(":/textures/skybox/right.png", - ":/textures/skybox/top.png", - ":/textures/skybox/front.png", - ":/textures/skybox/left.png", - ":/textures/skybox/bottom.png", - ":/textures/skybox/back.png", - "Skybox")); - - std::string spartanPath = QTK_EXAMPLE_SOURCE_DIR; - spartanPath += "/../resources/models/spartan/spartan.obj"; - auto spartan = addObject(new Model("spartan", spartanPath.c_str())); - spartan->getTransform().setTranslation(-4.0f, 0.0f, 0.0f); + setSkybox(new Qtk::Skybox); auto mesh = addObject( new Qtk::MeshRenderer("rightTriangle", Triangle(QTK_DRAW_ELEMENTS))); diff --git a/src/qtk/meshrenderer.cpp b/src/qtk/meshrenderer.cpp index fd8cb12..3a021ed 100644 --- a/src/qtk/meshrenderer.cpp +++ b/src/qtk/meshrenderer.cpp @@ -121,9 +121,7 @@ void MeshRenderer::draw() bindShaders(); mVAO.bind(); - if (mTexture.hasTexture()) { - mTexture.getOpenGLTexture().bind(); - } + mTexture.bind(); // TODO: Automate uniforms some other way setUniformMVP(); @@ -138,9 +136,7 @@ void MeshRenderer::draw() mShape.mIndices.data()); } - if (mTexture.hasTexture()) { - mTexture.getOpenGLTexture().release(); - } + mTexture.bind(); mVAO.release(); releaseShaders(); diff --git a/src/qtk/skybox.cpp b/src/qtk/skybox.cpp index 78b4eee..079dea6 100644 --- a/src/qtk/skybox.cpp +++ b/src/qtk/skybox.cpp @@ -17,9 +17,28 @@ using namespace Qtk; * Constructors / Destructors ******************************************************************************/ -Skybox::Skybox(QOpenGLTexture * cubeMap, const std::string & name) +Skybox::Skybox(const std::string & name) : + mVBO(QOpenGLBuffer::VertexBuffer), + mVertices(Cube(QTK_DRAW_ELEMENTS).getVertices()), + mIndices(Cube(QTK_DRAW_ELEMENTS).getIndexData()) { - mTexture.setTexture(cubeMap); + QImage image({1024, 1024}, QImage::Format_RGBA8888); + image.fill(Qt::darkGray); + mTexture.setCubeMap(image, image, image, image, image, image); + init(); +} + +Skybox::Skybox(QOpenGLTexture * cubeMap, const std::string & name) : + mVBO(QOpenGLBuffer::VertexBuffer), + mVertices(Cube(QTK_DRAW_ELEMENTS).getVertices()), + mIndices(Cube(QTK_DRAW_ELEMENTS).getIndexData()) +{ + if (cubeMap == Q_NULLPTR) { + qDebug() + << "[Qtk] Failed to set cubemap for skybox with null QOpenGLTexture."; + } else { + mTexture.setTexture(cubeMap); + } init(); } @@ -34,13 +53,13 @@ Skybox::Skybox(const std::string & right, mVertices(Cube(QTK_DRAW_ELEMENTS).getVertices()), mIndices(Cube(QTK_DRAW_ELEMENTS).getIndexData()) { - init(); mTexture.setCubeMap(QImage(right.c_str()).mirrored(), QImage(top.c_str()), QImage(front.c_str()), QImage(left.c_str()), QImage(bottom.c_str()), QImage(back.c_str())); + init(); } /******************************************************************************* @@ -54,7 +73,7 @@ void Skybox::draw() mVAO.bind(); mProgram.bind(); - mTexture.getOpenGLTexture().bind(); + mTexture.bind(); mProgram.setUniformValue("uProjectionMatrix", Scene::getProjectionMatrix()); mProgram.setUniformValue("uViewMatrix", Scene::getCamera().toMatrix()); @@ -62,7 +81,7 @@ void Skybox::draw() glDrawElements( GL_TRIANGLES, mIndices.size(), GL_UNSIGNED_INT, mIndices.data()); - mTexture.getOpenGLTexture().bind(); + mTexture.bind(); mProgram.release(); mVAO.release(); diff --git a/src/qtk/skybox.h b/src/qtk/skybox.h index d694d72..3dbba23 100644 --- a/src/qtk/skybox.h +++ b/src/qtk/skybox.h @@ -34,7 +34,12 @@ namespace Qtk * Constructors / Destructors ************************************************************************/ - // Delegate this constructor to use default skybox images + /** + * Construct a skybox with a default texture. + * + * @param name The objectName to use for the Skybox. + */ + explicit Skybox(const std::string & name = "Skybox"); /** * Construct a skybox with an existing QOpenGLTexture. diff --git a/src/qtk/texture.cpp b/src/qtk/texture.cpp index 4eb3828..debe248 100644 --- a/src/qtk/texture.cpp +++ b/src/qtk/texture.cpp @@ -104,9 +104,9 @@ QOpenGLTexture * OpenGLTextureFactory::initCubeMap(const QImage & right, QOpenGLTexture::CubeMapNegativeZ}; int i = 0; for (const auto & face : faces) { - QImage faceImage(faceTextures[i]); + QImage & faceImage = faceTextures[i]; if (faceImage.isNull()) { - qDebug() << "Error loading cube map image\n"; + qDebug() << "[libqtk] Error loading cube map image\n"; faceImage = defaultTexture(); } faceImage = faceImage.convertToFormat(QImage::Format_RGBA8888); diff --git a/src/qtk/texture.h b/src/qtk/texture.h index bbc474d..0251fdf 100644 --- a/src/qtk/texture.h +++ b/src/qtk/texture.h @@ -217,6 +217,20 @@ namespace Qtk return mOpenGLTexture != Q_NULLPTR; } + /** + * Bind the OpenGL texture if it exists, avoiding segmentation faults. + */ + bool bind() const + { + if (hasTexture()) { + // TODO: It would be nice to warn here but some objects may not have + // a texture. Factor Texture out of those objects so we don't bind. + mOpenGLTexture->bind(); + return true; + } + return false; + } + /************************************************************************* * Accessors ************************************************************************/ @@ -300,12 +314,12 @@ namespace Qtk /** * Sets this Texture to be a cube map with provided sides. * - * @param right Path to texture to use for right cube map side. - * @param top Path to texture to use for top cube map side. - * @param front Path to texture to use for front cube map side. - * @param left Path to texture to use for left cube map side. - * @param bottom Path to texture to use for bottom cube map side. - * @param back Path to texture to use for back cube map side. + * @param right QImage texture to use for right cube map side. + * @param top QImage texture to use for top cube map side. + * @param front QImage texture to use for front cube map side. + * @param left QImage texture to use for left cube map side. + * @param bottom QImage texture to use for bottom cube map side. + * @param back QImage texture to use for back cube map side. */ virtual inline void setCubeMap(const QImage & right, const QImage & top,