summaryrefslogtreecommitdiffstats
path: root/src/render/texture
diff options
context:
space:
mode:
Diffstat (limited to 'src/render/texture')
-rw-r--r--src/render/texture/apitexturemanager_p.h17
-rw-r--r--src/render/texture/gltexture.cpp59
-rw-r--r--src/render/texture/gltexture_p.h15
-rw-r--r--src/render/texture/qabstracttexture.cpp5
-rw-r--r--src/render/texture/qabstracttexture_p.h4
-rw-r--r--src/render/texture/qpaintedtextureimage.cpp44
-rw-r--r--src/render/texture/qtexture.cpp202
-rw-r--r--src/render/texture/qtexture_p.h28
-rw-r--r--src/render/texture/texture.cpp28
-rw-r--r--src/render/texture/texture_p.h7
-rw-r--r--src/render/texture/textureimage.cpp2
11 files changed, 307 insertions, 104 deletions
diff --git a/src/render/texture/apitexturemanager_p.h b/src/render/texture/apitexturemanager_p.h
index 2fd340e7e..91747b3bc 100644
--- a/src/render/texture/apitexturemanager_p.h
+++ b/src/render/texture/apitexturemanager_p.h
@@ -237,6 +237,21 @@ public:
return true;
}
+ // Change the texture data generator for given texture, if it is a non-shared texture
+ // Return true, if it was changed successfully, false otherwise
+ bool setGenerator(APITexture *tex, const QTextureGeneratorPtr &generator)
+ {
+ Q_ASSERT(tex);
+
+ if (isShared(tex))
+ return false;
+
+ tex->setGenerator(generator);
+ m_updatedTextures.push_back(tex);
+
+ return true;
+ }
+
// Retrieves abandoned textures. This should be regularly called from the OpenGL thread
// to make sure needed GL resources are de-allocated.
QVector<APITexture*> takeAbandonedTextures()
@@ -258,7 +273,7 @@ public:
if (impl->isUnique())
return false;
- auto it = m_sharedTextures.find(impl);
+ auto it = m_sharedTextures.constFind(impl);
if (it == m_sharedTextures.cend())
return false;
diff --git a/src/render/texture/gltexture.cpp b/src/render/texture/gltexture.cpp
index 0fc37891d..854789e94 100644
--- a/src/render/texture/gltexture.cpp
+++ b/src/render/texture/gltexture.cpp
@@ -120,7 +120,7 @@ QOpenGLTexture* GLTexture::getOrCreateGLTexture()
if (m_properties.target != QAbstractTexture::TargetAutomatic)
qWarning() << "[Qt3DRender::GLTexture] When a texture provides a generator, it's target is expected to be TargetAutomatic";
- m_properties.target = m_textureData->target();
+ m_actualTarget = m_textureData->target();
m_properties.width = m_textureData->width();
m_properties.height = m_textureData->height();
m_properties.depth = m_textureData->depth();
@@ -188,6 +188,8 @@ QOpenGLTexture* GLTexture::getOrCreateGLTexture()
if (!m_gl) {
m_gl = buildGLTexture();
+ if (!m_gl)
+ return nullptr;
m_gl->allocateStorage();
if (!m_gl->isStorageAllocated()) {
qWarning() << Q_FUNC_INFO << "texture storage allocation failed";
@@ -224,6 +226,7 @@ void GLTexture::setProperties(const TextureProperties &props)
if (m_properties != props) {
m_properties = props;
QMutexLocker locker(&m_dirtyFlagMutex);
+ m_actualTarget = props.target;
m_dirty |= Properties;
}
}
@@ -278,6 +281,26 @@ void GLTexture::setImages(const QVector<Image> &images)
}
}
+void GLTexture::setGenerator(const QTextureGeneratorPtr &generator)
+{
+ // Note: we do not compare if the generator is different
+ // as in some cases we may want to reset the same generator to force a reload
+ // e.g when using remote urls for textures
+ if (m_dataFunctor)
+ m_textureDataManager->releaseData(m_dataFunctor, this);
+
+ m_textureData.reset();
+ m_dataFunctor = generator;
+
+ if (m_dataFunctor) {
+ m_textureDataManager->requestData(m_dataFunctor, this);
+ requestUpload();
+ }
+}
+
+// Return nullptr if
+// - context cannot be obtained
+// - texture hasn't yet been loaded
QOpenGLTexture *GLTexture::buildGLTexture()
{
QOpenGLContext *ctx = QOpenGLContext::currentContext();
@@ -286,12 +309,14 @@ QOpenGLTexture *GLTexture::buildGLTexture()
return nullptr;
}
- if (m_properties.target == QAbstractTexture::TargetAutomatic) {
- qWarning() << Q_FUNC_INFO << "something went wrong, target shouldn't be automatic at this point";
+ if (m_actualTarget == QAbstractTexture::TargetAutomatic) {
+ // If the target is automatic at this point, it means that the texture
+ // hasn't been loaded yet (case of remote urls) and that loading failed
+ // and that target format couldn't be deduced
return nullptr;
}
- QOpenGLTexture* glTex = new QOpenGLTexture(static_cast<QOpenGLTexture::Target>(m_properties.target));
+ QOpenGLTexture* glTex = new QOpenGLTexture(static_cast<QOpenGLTexture::Target>(m_actualTarget));
// m_format may not be ES2 compatible. Now it's time to convert it, if necessary.
QAbstractTexture::TextureFormat format = m_properties.format;
@@ -329,16 +354,16 @@ QOpenGLTexture *GLTexture::buildGLTexture()
static_cast<QOpenGLTexture::TextureFormat>(format));
glTex->setSize(m_properties.width, m_properties.height, m_properties.depth);
// Set layers count if texture array
- if (m_properties.target == QAbstractTexture::Target1DArray ||
- m_properties.target == QAbstractTexture::Target2DArray ||
- m_properties.target == QAbstractTexture::Target3D ||
- m_properties.target == QAbstractTexture::Target2DMultisampleArray ||
- m_properties.target == QAbstractTexture::TargetCubeMapArray) {
+ if (m_actualTarget == QAbstractTexture::Target1DArray ||
+ m_actualTarget == QAbstractTexture::Target2DArray ||
+ m_actualTarget == QAbstractTexture::Target3D ||
+ m_actualTarget == QAbstractTexture::Target2DMultisampleArray ||
+ m_actualTarget == QAbstractTexture::TargetCubeMapArray) {
glTex->setLayers(m_properties.layers);
}
- if (m_properties.target == QAbstractTexture::Target2DMultisample ||
- m_properties.target == QAbstractTexture::Target2DMultisampleArray) {
+ if (m_actualTarget == QAbstractTexture::Target2DMultisample ||
+ m_actualTarget == QAbstractTexture::Target2DMultisampleArray) {
// Set samples count if multisampled texture
// (multisampled textures don't have mipmaps)
glTex->setSamples(m_properties.samples);
@@ -384,7 +409,7 @@ void GLTexture::uploadGLTextureData()
for (int layer = 0; layer < data->layers(); layer++) {
for (int face = 0; face < data->faces(); face++) {
for (int level = 0; level < mipLevels; level++) {
- // ensure we don't accidently cause a detach / copy of the raw bytes
+ // ensure we don't accidentally cause a detach / copy of the raw bytes
const QByteArray bytes(data->data(layer, face, level));
uploadGLData(m_gl, level, layer,
static_cast<QOpenGLTexture::CubeMapFace>(QOpenGLTexture::CubeMapPositiveX + face),
@@ -399,7 +424,7 @@ void GLTexture::uploadGLTextureData()
for (int i = 0; i < m_images.size(); i++) {
const QTextureImageDataPtr &imgData = m_imageData.at(i);
- // ensure we don't accidently cause a detach / copy of the raw bytes
+ // ensure we don't accidentally cause a detach / copy of the raw bytes
const QByteArray bytes(imgData->data());
uploadGLData(m_gl, m_images[i].mipLevel, m_images[i].layer,
static_cast<QOpenGLTexture::CubeMapFace>(m_images[i].face),
@@ -410,11 +435,11 @@ void GLTexture::uploadGLTextureData()
void GLTexture::updateGLTextureParameters()
{
m_gl->setWrapMode(QOpenGLTexture::DirectionS, static_cast<QOpenGLTexture::WrapMode>(m_parameters.wrapModeX));
- if (m_properties.target != QAbstractTexture::Target1D &&
- m_properties.target != QAbstractTexture::Target1DArray &&
- m_properties.target != QAbstractTexture::TargetBuffer)
+ if (m_actualTarget != QAbstractTexture::Target1D &&
+ m_actualTarget != QAbstractTexture::Target1DArray &&
+ m_actualTarget != QAbstractTexture::TargetBuffer)
m_gl->setWrapMode(QOpenGLTexture::DirectionT, static_cast<QOpenGLTexture::WrapMode>(m_parameters.wrapModeY));
- if (m_properties.target == QAbstractTexture::Target3D)
+ if (m_actualTarget == QAbstractTexture::Target3D)
m_gl->setWrapMode(QOpenGLTexture::DirectionR, static_cast<QOpenGLTexture::WrapMode>(m_parameters.wrapModeZ));
m_gl->setMinMagFilters(static_cast<QOpenGLTexture::Filter>(m_parameters.minificationFilter),
static_cast<QOpenGLTexture::Filter>(m_parameters.magnificationFilter));
diff --git a/src/render/texture/gltexture_p.h b/src/render/texture/gltexture_p.h
index 424e77854..4c0957f76 100644
--- a/src/render/texture/gltexture_p.h
+++ b/src/render/texture/gltexture_p.h
@@ -148,6 +148,16 @@ public:
m_dirty |= TextureData;
}
+ bool isDirty()
+ {
+ QMutexLocker locker(&m_dirtyFlagMutex);
+ return m_dirty == 0 ? false : true;
+ }
+
+ QMutex *textureLock()
+ {
+ return &m_dirtyFlagMutex;
+ }
protected:
template<class APITexture, class APITextureImage>
@@ -164,11 +174,12 @@ protected:
void setParameters(const TextureParameters &params);
void setProperties(const TextureProperties &props);
void setImages(const QVector<Image> &images);
+ void setGenerator(const QTextureGeneratorPtr &generator);
private:
enum DirtyFlag {
- TextureData = 0x01, // one or more generators have been executed, data needs uploading to GPU
+ TextureData = 0x01, // one or more image generators have been executed, data needs uploading to GPU
Properties = 0x02, // texture needs to be (re-)created
Parameters = 0x04 // texture parameters need to be (re-)set
@@ -188,6 +199,8 @@ private:
TextureDataManager *m_textureDataManager;
TextureImageDataManager *m_textureImageDataManager;
+ // target which is actually used for GL texture
+ QAbstractTexture::Target m_actualTarget;
TextureProperties m_properties;
TextureParameters m_parameters;
diff --git a/src/render/texture/qabstracttexture.cpp b/src/render/texture/qabstracttexture.cpp
index 7703933a4..1bd002104 100644
--- a/src/render/texture/qabstracttexture.cpp
+++ b/src/render/texture/qabstracttexture.cpp
@@ -69,6 +69,11 @@ QAbstractTexturePrivate::QAbstractTexturePrivate()
{
}
+QTextureGeneratorPtr QAbstractTexturePrivate::dataFunctor() const
+{
+ return m_dataFunctor;
+}
+
void QAbstractTexturePrivate::setDataFunctor(const QTextureGeneratorPtr &generator)
{
if (generator != m_dataFunctor) {
diff --git a/src/render/texture/qabstracttexture_p.h b/src/render/texture/qabstracttexture_p.h
index 7f5a32c94..a27ae3729 100644
--- a/src/render/texture/qabstracttexture_p.h
+++ b/src/render/texture/qabstracttexture_p.h
@@ -56,12 +56,13 @@
#include <Qt3DRender/qabstracttexture.h>
#include <Qt3DRender/qtexturewrapmode.h>
#include <Qt3DRender/qtexturegenerator.h>
+#include <Qt3DRender/private/qt3drender_global_p.h>
QT_BEGIN_NAMESPACE
namespace Qt3DRender {
-class Q_AUTOTEST_EXPORT QAbstractTexturePrivate : public Qt3DCore::QNodePrivate
+class QT3DRENDERSHARED_PRIVATE_EXPORT QAbstractTexturePrivate : public Qt3DCore::QNodePrivate
{
public :
QAbstractTexturePrivate();
@@ -86,6 +87,7 @@ public :
int m_layers;
int m_samples;
+ QTextureGeneratorPtr dataFunctor() const;
void setDataFunctor(const QTextureGeneratorPtr &generator);
private:
diff --git a/src/render/texture/qpaintedtextureimage.cpp b/src/render/texture/qpaintedtextureimage.cpp
index 9ae43378a..4d03eb809 100644
--- a/src/render/texture/qpaintedtextureimage.cpp
+++ b/src/render/texture/qpaintedtextureimage.cpp
@@ -104,14 +104,9 @@ QPaintedTextureImage::~QPaintedTextureImage()
/*!
\property QPaintedTextureImage::width
- Holds the width of the texture image
- */
-
-/*!
- \property QPaintedTextureImage::width
-
- \return the width of the texture image.
- */
+ This property holds the width of the texture image.
+ The width must be greater than or equal to 1.
+*/
int QPaintedTextureImage::width() const
{
Q_D(const QPaintedTextureImage);
@@ -121,14 +116,9 @@ int QPaintedTextureImage::width() const
/*!
\property QPaintedTextureImage::height
- Holds the height of the texture image
- */
-
-/*!
- \property QPaintedTextureImage::height
-
- \return the height of the texture image.
- */
+ This property holds the height of the texture image.
+ The height must be greater than or equal to 1.
+*/
int QPaintedTextureImage::height() const
{
Q_D(const QPaintedTextureImage);
@@ -138,14 +128,11 @@ int QPaintedTextureImage::height() const
/*!
\property QPaintedTextureImage::size
- Holds the width and height of the texture image
- */
+ This property holds the size of the texture image.
-/*!
- \property QPaintedTextureImage::size
+ \sa height, width
- \return the size of the texture image.
- */
+*/
QSize QPaintedTextureImage::size() const
{
Q_D(const QPaintedTextureImage);
@@ -153,7 +140,7 @@ QSize QPaintedTextureImage::size() const
}
/*!
- Sets the width of the texture image. Triggers an update, if the size changes.
+ Sets the width (\a w) of the texture image. Triggers an update, if the size changes.
*/
void QPaintedTextureImage::setWidth(int w)
{
@@ -165,7 +152,7 @@ void QPaintedTextureImage::setWidth(int w)
}
/*!
- Sets the height of the texture image. Triggers an update, if the size changes.
+ Sets the height (\a h) of the texture image. Triggers an update, if the size changes.
*/
void QPaintedTextureImage::setHeight(int h)
{
@@ -177,7 +164,7 @@ void QPaintedTextureImage::setHeight(int h)
}
/*!
- Sets the width and height of the texture image. Triggers an update, if the size changes.
+ Sets the width and height of the texture image. Triggers an update, if the \a size changes.
*/
void QPaintedTextureImage::setSize(QSize size)
{
@@ -206,9 +193,10 @@ void QPaintedTextureImage::setSize(QSize size)
}
/*!
- Will trigger an update of the texture image, meaning the paint() method will
- be invoked.
- */
+ Schedules the painted texture's paint() function to be called,
+ which in turn uploads the new image to the GPU.
+ Parameter \a rect is currently unused.
+*/
void QPaintedTextureImage::update(const QRect &rect)
{
Q_UNUSED(rect)
diff --git a/src/render/texture/qtexture.cpp b/src/render/texture/qtexture.cpp
index 7dc071833..931d66c64 100644
--- a/src/render/texture/qtexture.cpp
+++ b/src/render/texture/qtexture.cpp
@@ -44,7 +44,17 @@
#include "qtexture.h"
#include "qtexture_p.h"
#include <QFileInfo>
+#include <QMimeDatabase>
+#include <QMimeType>
#include <qendian.h>
+#include <Qt3DCore/private/qscene_p.h>
+#include <Qt3DCore/qaspectengine.h>
+#include <Qt3DCore/private/qdownloadhelperservice_p.h>
+#include <Qt3DRender/private/qrenderaspect_p.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/managers_p.h>
+#include <Qt3DRender/private/texture_p.h>
+#include <Qt3DRender/private/qurlhelper_p.h>
QT_BEGIN_NAMESPACE
@@ -390,7 +400,7 @@ const struct DX10Format
{ DXGI_FORMAT_R8_UNORM, { QOpenGLTexture::Red, QOpenGLTexture::R8_UNorm, QOpenGLTexture::UInt8, 1, false } },
{ DXGI_FORMAT_R8G8_UNORM, { QOpenGLTexture::RG, QOpenGLTexture::RG8_UNorm, QOpenGLTexture::UInt8, 2, false } },
{ DXGI_FORMAT_R16_UNORM, { QOpenGLTexture::Red, QOpenGLTexture::R16_UNorm, QOpenGLTexture::UInt16, 2, false } },
-{ DXGI_FORMAT_R16_UNORM, { QOpenGLTexture::RG, QOpenGLTexture::RG16_UNorm, QOpenGLTexture::UInt16, 4, false } },
+{ DXGI_FORMAT_R16G16_UNORM, { QOpenGLTexture::RG, QOpenGLTexture::RG16_UNorm, QOpenGLTexture::UInt16, 4, false } },
// depth formats
{ DXGI_FORMAT_D16_UNORM, { QOpenGLTexture::Depth, QOpenGLTexture::D16, QOpenGLTexture::NoPixelType, 2, false } },
@@ -424,9 +434,8 @@ enum CompressedFormatExtension {
PKM
};
-CompressedFormatExtension texturedCompressedFormat(const QString &source)
+CompressedFormatExtension texturedCompressedFormat(const QString &suffix)
{
- const QString suffix = QFileInfo(source).suffix();
if (suffix == QStringLiteral("pkm"))
return PKM;
if (suffix == QStringLiteral("dds"))
@@ -434,19 +443,14 @@ CompressedFormatExtension texturedCompressedFormat(const QString &source)
return None;
}
-QTextureImageDataPtr setPkmFile(const QString &source)
+QTextureImageDataPtr setPkmFile(QIODevice *source)
{
QTextureImageDataPtr imageData;
- QFile f(source);
- if (!f.open(QIODevice::ReadOnly)) {
- qWarning() << "Failed to open" << source;
- return imageData;
- }
- // ETC1 in PKM, as generated by f.ex. Android's etc1tool
+ // ETC1 in PKM, as generated by source->ex. Android's etc1tool
static const char pkmMagic[] = { 'P', 'K', 'M', ' ', '1', '0' };
const int pkmHeaderSize = 6 + 2 + 4 * 2;
- const QByteArray header = f.read(pkmHeaderSize);
+ const QByteArray header = source->read(pkmHeaderSize);
if (header.size() >= pkmHeaderSize && !qstrncmp(header.constData(), pkmMagic, 6)) {
imageData = QTextureImageDataPtr::create();
imageData->setTarget(QOpenGLTexture::Target2D);
@@ -455,7 +459,7 @@ QTextureImageDataPtr setPkmFile(const QString &source)
imageData->setWidth(qFromBigEndian(*(reinterpret_cast<const quint16 *>(header.constData() + 6 + 2))));
imageData->setHeight(qFromBigEndian(*(reinterpret_cast<const quint16 *>(header.constData() + 6 + 2 + 2))));
imageData->setDepth(1);
- const QByteArray data = f.readAll();
+ const QByteArray data = source->readAll();
if (data.size() < (imageData->width() / 4) * (imageData->height() / 4) * 8)
qWarning() << "Unexpected end of ETC1 data in" << source;
const bool isCompressed = true;
@@ -467,17 +471,12 @@ QTextureImageDataPtr setPkmFile(const QString &source)
return imageData;
}
-QTextureImageDataPtr setDdsFile(const QString &source)
+QTextureImageDataPtr setDdsFile(QIODevice *source)
{
QTextureImageDataPtr imageData;
- QFile f(source);
- if (!f.open(QIODevice::ReadOnly)) {
- qWarning() << "Failed to open" << source;
- return imageData;
- }
DdsHeader header;
- if ((f.read(reinterpret_cast<char *>(&header), sizeof header) != sizeof header)
+ if ((source->read(reinterpret_cast<char *>(&header), sizeof header) != sizeof header)
|| (qstrncmp(header.magic, "DDS ", 4) != 0))
return imageData;
@@ -490,7 +489,7 @@ QTextureImageDataPtr setDdsFile(const QString &source)
if (fourCC == DdsFourCC<'D', 'X', '1', '0'>::value) {
// DX10 texture
DdsDX10Header dx10Header;
- if (f.read(reinterpret_cast<char *>(&dx10Header), sizeof dx10Header) != sizeof dx10Header)
+ if (source->read(reinterpret_cast<char *>(&dx10Header), sizeof dx10Header) != sizeof dx10Header)
return imageData;
layers = qFromLittleEndian(dx10Header.arraySize);
@@ -582,13 +581,13 @@ QTextureImageDataPtr setDdsFile(const QString &source)
// data
const int dataSize = layers * layerSize;
- const QByteArray data = f.read(dataSize);
+ const QByteArray data = source->read(dataSize);
if (data.size() < dataSize) {
qWarning() << "Unexpected end of data in" << source;
return imageData;
}
- if (!f.atEnd())
+ if (!source->atEnd())
qWarning() << "Unrecognized data in" << source;
imageData = QTextureImageDataPtr::create();
@@ -626,26 +625,39 @@ QTextureImageDataPtr TextureLoadingHelper::loadTextureData(const QUrl &url, bool
#endif
) {
const QString source = Qt3DRender::QUrlHelper::urlToLocalFileOrQrc(url);
- const CompressedFormatExtension formatExtension = texturedCompressedFormat(source);
- switch (formatExtension) {
- case DDS:
- textureData = setDdsFile(source);
- break;
- case PKM:
- textureData = setPkmFile(source);
- break;
- default:
- QImage img;
- if (img.load(source)) {
- textureData = QTextureImageDataPtr::create();
- textureData->setImage(mirrored ? img.mirrored() : img);
- }
- break;
- }
+ QFile f(source);
+ if (!f.open(QIODevice::ReadOnly))
+ qWarning() << "Failed to open" << source;
+ else
+ textureData = loadTextureData(&f, QFileInfo(source).suffix(), allow3D, mirrored);
+ }
+ return textureData;
+}
- if (!allow3D && textureData && (textureData->layers() > 1 || textureData->depth() > 1))
- qWarning() << "Texture data has a 3rd dimension which wasn't expected";
+QTextureImageDataPtr TextureLoadingHelper::loadTextureData(QIODevice *data, const QString& suffix,
+ bool allow3D, bool mirrored)
+{
+ QTextureImageDataPtr textureData;
+ const CompressedFormatExtension formatExtension = texturedCompressedFormat(suffix);
+ switch (formatExtension) {
+ case DDS:
+ textureData = setDdsFile(data);
+ break;
+ case PKM:
+ textureData = setPkmFile(data);
+ break;
+ default: {
+ QImage img;
+ if (img.load(data, suffix.toLatin1())) {
+ textureData = QTextureImageDataPtr::create();
+ textureData->setImage(mirrored ? img.mirrored() : img);
+ }
+ break;
+ }
}
+
+ if (!allow3D && textureData && (textureData->layers() > 1 || textureData->depth() > 1))
+ qWarning() << "Texture data has a 3rd dimension which wasn't expected";
return textureData;
}
@@ -653,8 +665,43 @@ QTextureDataPtr QTextureFromSourceGenerator::operator ()()
{
QTextureDataPtr generatedData = QTextureDataPtr::create();
m_status = QAbstractTexture::Loading;
+ QTextureImageDataPtr textureData;
+
+ if (!Qt3DCore::QDownloadHelperService::isLocal(m_url)) {
+ if (m_sourceData.isEmpty()) {
+ // first time around, trigger a download
+ if (m_texture) {
+ auto downloadService = Qt3DCore::QDownloadHelperService::getService(m_engine);
+ Qt3DCore::QDownloadRequestPtr request(new TextureDownloadRequest(m_texture, m_url,
+ m_engine));
+ downloadService->submitRequest(request);
+ }
+ return generatedData;
+ }
+
+ // second time around, we have the data
+ QT_PREPEND_NAMESPACE(QBuffer) buffer(&m_sourceData);
+ if (buffer.open(QIODevice::ReadOnly)) {
+ QString suffix = m_url.toString();
+ suffix = suffix.right(suffix.length() - suffix.lastIndexOf('.'));
+
+ QStringList ext(suffix);
+
+ QMimeDatabase db;
+ QMimeType mtype = db.mimeTypeForData(m_sourceData);
+ if (mtype.isValid()) {
+ ext << mtype.suffixes();
+ }
- const QTextureImageDataPtr textureData = TextureLoadingHelper::loadTextureData(m_url, true, m_mirrored);
+ for (QString s: qAsConst(ext)) {
+ textureData = TextureLoadingHelper::loadTextureData(&buffer, suffix, true, m_mirrored);
+ if (textureData && textureData->data().length() > 0)
+ break;
+ }
+ }
+ } else {
+ textureData = TextureLoadingHelper::loadTextureData(m_url, true, m_mirrored);
+ }
if (textureData && textureData->data().length() > 0) {
generatedData->setTarget(static_cast<QAbstractTexture::Target>(textureData->target()));
@@ -672,12 +719,55 @@ QTextureDataPtr QTextureFromSourceGenerator::operator ()()
return generatedData;
}
+TextureDownloadRequest::TextureDownloadRequest(Qt3DCore::QNodeId texture, const QUrl& source,
+ Qt3DCore::QAspectEngine *engine)
+ : Qt3DCore::QDownloadRequest(source)
+ , m_texture(texture)
+ , m_engine(engine)
+{
+
+}
+
+// Executed in download thread
+void TextureDownloadRequest::onCompleted()
+{
+ if (cancelled() || !succeeded())
+ return;
+
+ QRenderAspectPrivate* d_aspect = QRenderAspectPrivate::findPrivate(m_engine);
+ if (!d_aspect)
+ return;
+
+ Render::Texture *texture = d_aspect->m_nodeManagers->textureManager()->lookupResource(m_texture);
+ if (!texture)
+ return;
+
+ QSharedPointer<QTextureFromSourceGenerator> functor =
+ qSharedPointerCast<QTextureFromSourceGenerator>(texture->dataGenerator());
+ functor->m_sourceData = m_data;
+
+ // mark the component as dirty so that the functor runs again in the correct job
+ texture->addDirtyFlag(Render::Texture::DirtyDataGenerator);
+}
+
QTextureLoaderPrivate::QTextureLoaderPrivate()
: QAbstractTexturePrivate()
, m_mirrored(true)
{
}
+void QTextureLoaderPrivate::setScene(Qt3DCore::QScene *scene)
+{
+ QAbstractTexturePrivate::setScene(scene);
+ updateFunctor();
+}
+
+void QTextureLoaderPrivate::updateFunctor()
+{
+ Qt3DCore::QAspectEngine *engine = m_scene ? m_scene->engine() : nullptr;
+ setDataFunctor(QTextureFromSourceGeneratorPtr::create(m_id, m_source, m_mirrored, engine));
+}
+
/*!
\class Qt3DRender::QTexture1D
\inmodule Qt3DRender
@@ -900,10 +990,25 @@ QTextureBuffer::~QTextureBuffer()
/*!
* Constructs a new Qt3DRender::QTextureLoader instance with \a parent as parent.
+ *
+ * Note that by default, if not contradicted by the file metadata, the loaded texture
+ * will have the following properties set:
+ * - wrapMode set to Repeat
+ * - minificationFilter set to LinearMipMapLinear
+ * - magnificationFilter set to Linear
+ * - generateMipMaps set to true
+ * - maximumAnisotropy set to 16.0f
+ * - target set to TargetAutomatic
*/
QTextureLoader::QTextureLoader(QNode *parent)
: QAbstractTexture(*new QTextureLoaderPrivate, parent)
{
+ d_func()->m_wrapMode.setX(QTextureWrapMode::Repeat);
+ d_func()->m_wrapMode.setY(QTextureWrapMode::Repeat);
+ d_func()->m_minFilter = LinearMipMapLinear;
+ d_func()->m_magFilter = Linear;
+ d_func()->m_autoMipMap = true;
+ d_func()->m_maximumAnisotropy = 16.0f;
d_func()->m_target = TargetAutomatic;
}
@@ -936,7 +1041,7 @@ void QTextureLoader::setSource(const QUrl& source)
Q_D(QTextureLoader);
if (source != d->m_source) {
d->m_source = source;
- d->setDataFunctor(QTextureFromSourceGeneratorPtr::create(d->m_source, d->m_mirrored));
+ d->updateFunctor();
const bool blocked = blockNotifications(true);
emit sourceChanged(source);
blockNotifications(blocked);
@@ -984,7 +1089,7 @@ void QTextureLoader::setMirrored(bool mirrored)
Q_D(QTextureLoader);
if (mirrored != d->m_mirrored) {
d->m_mirrored = mirrored;
- d->setDataFunctor(QTextureFromSourceGeneratorPtr::create(d->m_source, d->m_mirrored));
+ d->updateFunctor();
const bool blocked = blockNotifications(true);
emit mirroredChanged(mirrored);
blockNotifications(blocked);
@@ -1000,7 +1105,8 @@ bool QTextureFromSourceGenerator::operator ==(const QTextureGenerator &other) co
const QTextureFromSourceGenerator *otherFunctor = functor_cast<QTextureFromSourceGenerator>(&other);
return (otherFunctor != nullptr &&
otherFunctor->m_url == m_url &&
- otherFunctor->m_mirrored == m_mirrored);
+ otherFunctor->m_mirrored == m_mirrored &&
+ otherFunctor->m_engine == m_engine);
}
QUrl QTextureFromSourceGenerator::url() const
@@ -1018,16 +1124,18 @@ bool QTextureFromSourceGenerator::isMirrored() const
* instance with \a url.
* \param url
*/
-QTextureFromSourceGenerator::QTextureFromSourceGenerator(const QUrl &url, bool mirrored)
+QTextureFromSourceGenerator::QTextureFromSourceGenerator(Qt3DCore::QNodeId texture,
+ const QUrl &url, bool mirrored,
+ Qt3DCore::QAspectEngine *engine)
: QTextureGenerator()
, m_url(url)
, m_status(QAbstractTexture::None)
, m_mirrored(mirrored)
+ , m_texture(texture)
+ , m_engine(engine)
{
}
} // namespace Qt3DRender
QT_END_NAMESPACE
-
-
diff --git a/src/render/texture/qtexture_p.h b/src/render/texture/qtexture_p.h
index a0ea71a58..4afb14d62 100644
--- a/src/render/texture/qtexture_p.h
+++ b/src/render/texture/qtexture_p.h
@@ -51,8 +51,11 @@
// We mean it.
//
+#include <Qt3DCore/QNodeId>
+#include <Qt3DCore/private/qdownloadhelperservice_p.h>
#include <Qt3DRender/private/qabstracttexture_p.h>
#include <Qt3DRender/qtexturegenerator.h>
+#include <Qt3DRender/qtexture.h>
QT_BEGIN_NAMESPACE
@@ -63,14 +66,30 @@ class QTextureLoaderPrivate : public QAbstractTexturePrivate
public:
QTextureLoaderPrivate();
+ void setScene(Qt3DCore::QScene *scene) override;
+ void updateFunctor();
+
QUrl m_source;
bool m_mirrored;
};
+class Q_AUTOTEST_EXPORT TextureDownloadRequest : public Qt3DCore::QDownloadRequest
+{
+public:
+ TextureDownloadRequest(Qt3DCore::QNodeId texture, const QUrl &url, Qt3DCore::QAspectEngine *engine);
+
+ void onCompleted() Q_DECL_OVERRIDE;
+
+private:
+ Qt3DCore::QNodeId m_texture;
+ Qt3DCore::QAspectEngine *m_engine;
+};
+
class Q_AUTOTEST_EXPORT QTextureFromSourceGenerator : public QTextureGenerator
{
public:
- explicit QTextureFromSourceGenerator(const QUrl &url, bool mirrored);
+ explicit QTextureFromSourceGenerator(Qt3DCore::QNodeId texture, const QUrl &url,
+ bool mirrored, Qt3DCore::QAspectEngine *engine);
QTextureDataPtr operator ()() Q_DECL_OVERRIDE;
bool operator ==(const QTextureGenerator &other) const Q_DECL_OVERRIDE;
inline QAbstractTexture::Status status() const { return m_status; }
@@ -81,9 +100,14 @@ public:
bool isMirrored() const;
private:
+ friend class TextureDownloadRequest;
+
QUrl m_url;
QAbstractTexture::Status m_status;
bool m_mirrored;
+ QByteArray m_sourceData;
+ Qt3DCore::QNodeId m_texture;
+ Qt3DCore::QAspectEngine *m_engine;
};
typedef QSharedPointer<QTextureFromSourceGenerator> QTextureFromSourceGeneratorPtr;
@@ -91,6 +115,8 @@ class Q_AUTOTEST_EXPORT TextureLoadingHelper
{
public:
static QTextureImageDataPtr loadTextureData(const QUrl &source, bool allow3D, bool mirrored);
+ static QTextureImageDataPtr loadTextureData(QIODevice *data, const QString& suffix,
+ bool allow3D, bool mirrored);
};
} // namespace Qt3DRender
diff --git a/src/render/texture/texture.cpp b/src/render/texture/texture.cpp
index 863733523..21f29d0b6 100644
--- a/src/render/texture/texture.cpp
+++ b/src/render/texture/texture.cpp
@@ -61,7 +61,7 @@ namespace Render {
Texture::Texture()
// We need backend -> frontend notifications to update the status of the texture
: BackendNode(ReadWrite)
- , m_dirty(DirtyGenerators|DirtyProperties|DirtyParameters)
+ , m_dirty(DirtyImageGenerators|DirtyProperties|DirtyParameters|DirtyDataGenerator)
, m_textureImageManager(nullptr)
{
}
@@ -81,11 +81,19 @@ void Texture::setTextureImageManager(TextureImageManager *manager)
void Texture::addDirtyFlag(DirtyFlags flags)
{
+ QMutexLocker lock(&m_flagsMutex);
m_dirty |= flags;
}
+Texture::DirtyFlags Texture::dirtyFlags()
+{
+ QMutexLocker lock(&m_flagsMutex);
+ return m_dirty;
+}
+
void Texture::unsetDirty()
{
+ QMutexLocker lock(&m_flagsMutex);
m_dirty = Texture::NotDirty;
}
@@ -101,7 +109,7 @@ void Texture::addTextureImage(Qt3DCore::QNodeId id)
qWarning() << "[Qt3DRender::TextureNode] addTextureImage: image handle is NULL";
} else if (!m_textureImages.contains(handle)) {
m_textureImages << handle;
- addDirtyFlag(DirtyGenerators);
+ addDirtyFlag(DirtyImageGenerators);
}
}
@@ -117,7 +125,7 @@ void Texture::removeTextureImage(Qt3DCore::QNodeId id)
qWarning() << "[Qt3DRender::TextureNode] removeTextureImage: image handle is NULL";
} else {
m_textureImages.removeAll(handle);
- addDirtyFlag(DirtyGenerators);
+ addDirtyFlag(DirtyImageGenerators);
}
}
@@ -214,7 +222,7 @@ void Texture::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
dirty = DirtyProperties;
} else if (propertyChange->propertyName() == QByteArrayLiteral("generator")) {
m_dataFunctor = propertyChange->value().value<QTextureGeneratorPtr>();
- dirty = DirtyGenerators;
+ dirty = DirtyDataGenerator;
}
}
break;
@@ -246,6 +254,16 @@ void Texture::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
BackendNode::sceneChangeEvent(e);
}
+bool Texture::isValid() const
+{
+ for (const auto handle : m_textureImages) {
+ TextureImage *img = m_textureImageManager->data(handle);
+ if (img == nullptr)
+ return false;
+ }
+ return true;
+}
+
void Texture::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change)
{
const auto typedChange = qSharedPointerCast<Qt3DCore::QNodeCreatedChange<QAbstractTextureData>>(change);
@@ -269,7 +287,7 @@ void Texture::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &chan
m_parameters.comparisonMode = data.comparisonMode;
m_dataFunctor = data.dataFunctor;
- addDirtyFlag(DirtyFlags(DirtyGenerators|DirtyProperties|DirtyParameters));
+ addDirtyFlag(DirtyFlags(DirtyImageGenerators|DirtyProperties|DirtyParameters));
}
diff --git a/src/render/texture/texture_p.h b/src/render/texture/texture_p.h
index 8f5300552..1f3ba729c 100644
--- a/src/render/texture/texture_p.h
+++ b/src/render/texture/texture_p.h
@@ -135,14 +135,15 @@ public:
NotDirty = 0,
DirtyProperties = 0x1,
DirtyParameters = 0x2,
- DirtyGenerators = 0x4
+ DirtyImageGenerators = 0x4,
+ DirtyDataGenerator = 0x8
};
Q_DECLARE_FLAGS(DirtyFlags, DirtyFlag)
void setTextureImageManager(TextureImageManager *manager);
void addDirtyFlag(DirtyFlags flags);
- inline DirtyFlags dirtyFlags() const { return m_dirty; }
+ DirtyFlags dirtyFlags();
void unsetDirty();
void addTextureImage(Qt3DCore::QNodeId id);
@@ -156,6 +157,7 @@ public:
inline const QVector<HTextureImage>& textureImages() const { return m_textureImages; }
inline const QTextureGeneratorPtr& dataGenerator() const { return m_dataFunctor; }
+ bool isValid() const;
private:
void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) Q_DECL_FINAL;
@@ -167,6 +169,7 @@ private:
QVector<HTextureImage> m_textureImages;
TextureImageManager *m_textureImageManager;
+ QMutex m_flagsMutex;
};
class TextureFunctor : public Qt3DCore::QBackendNodeMapper
diff --git a/src/render/texture/textureimage.cpp b/src/render/texture/textureimage.cpp
index f44a82649..b732be2d9 100644
--- a/src/render/texture/textureimage.cpp
+++ b/src/render/texture/textureimage.cpp
@@ -106,7 +106,7 @@ void TextureImage::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
// Notify the Texture that we were updated and request it to schedule an update job
Texture *txt = m_textureManager->data(m_textureProvider);
if (txt != nullptr)
- txt->addDirtyFlag(Texture::DirtyGenerators);
+ txt->addDirtyFlag(Texture::DirtyImageGenerators);
}
markDirty(AbstractRenderer::AllDirty);