//// TextureCache.cpp// interface/src/renderer//// Created by Andrzej Kapolka on 8/6/13.// Copyright 2013 High Fidelity, Inc.//// Distributed under the Apache License, Version 2.0.// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// // include this before QGLWidget, which includes an earlier version of OpenGL#include <gpu/GPUConfig.h> #include <QEvent>#include <QGLWidget>#include <QNetworkReply>#include <QOpenGLFramebufferObject>#include <QResizeEvent>#include <QRunnable>#include <QThreadPool> #include <glm/glm.hpp>#include <glm/gtc/random.hpp> #include "TextureCache.h" #include "gpu/GLBackend.h" TextureCache::TextureCache() : _permutationNormalTexture(0), _whiteTexture(0), _blueTexture(0), _primaryDepthTextureID(0), _primaryNormalTextureID(0), _primarySpecularTextureID(0), _primaryFramebufferObject(NULL), _secondaryFramebufferObject(NULL), _tertiaryFramebufferObject(NULL), _shadowFramebufferObject(NULL), _frameBufferSize(100, 100), _associatedWidget(NULL){ const qint64 TEXTURE_DEFAULT_UNUSED_MAX_SIZE = DEFAULT_UNUSED_MAX_SIZE; setUnusedResourceCacheSize(TEXTURE_DEFAULT_UNUSED_MAX_SIZE);} TextureCache::~TextureCache() { if (_primaryFramebufferObject) { glDeleteTextures(1, &_primaryDepthTextureID); glDeleteTextures(1, &_primaryNormalTextureID); glDeleteTextures(1, &_primarySpecularTextureID); } if (_primaryFramebufferObject) { delete _primaryFramebufferObject; } if (_secondaryFramebufferObject) { delete _secondaryFramebufferObject; } if (_tertiaryFramebufferObject) { delete _tertiaryFramebufferObject; }} void TextureCache::setFrameBufferSize(QSize frameBufferSize) { //If the size changed, we need to delete our FBOs if (_frameBufferSize != frameBufferSize) { _frameBufferSize = frameBufferSize; if (_primaryFramebufferObject) { delete _primaryFramebufferObject; _primaryFramebufferObject = NULL; glDeleteTextures(1, &_primaryDepthTextureID); _primaryDepthTextureID = 0; glDeleteTextures(1, &_primaryNormalTextureID); _primaryNormalTextureID = 0; glDeleteTextures(1, &_primarySpecularTextureID); _primarySpecularTextureID = 0; } if (_secondaryFramebufferObject) { delete _secondaryFramebufferObject; _secondaryFramebufferObject = NULL; } if (_tertiaryFramebufferObject) { delete _tertiaryFramebufferObject; _tertiaryFramebufferObject = NULL; } }} // use fixed table of permutations. Could also make ordered list programmatically// and then shuffle algorithm. For testing, this ensures consistent behavior in each run.// this list taken from Ken Perlin's Improved Noise reference implementation (orig. in Java) at// http://mrl.nyu.edu/~perlin/noise/ const int permutation[256] = { 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180}; #define USE_CHRIS_NOISE 1 const gpu::TexturePointer& TextureCache::getPermutationNormalTexture() { if (_permutationNormalTexture.isNull()) { // the first line consists of random permutation offsets unsigned char data[256 * 2 * 3];#if (USE_CHRIS_NOISE==1) for (int i = 0; i < 256; i++) { data[3*i+0] = permutation[i]; data[3*i+1] = permutation[i]; data[3*i+2] = permutation[i];#else for (int i = 0; i < 256 * 3; i++) { data[i] = rand() % 256;#endif } for (int i = 256 * 3; i < 256 * 3 * 2; i += 3) { glm::vec3 randvec = glm::sphericalRand(1.0f); data[i] = ((randvec.x + 1.0f) / 2.0f) * 255.0f; data[i + 1] = ((randvec.y + 1.0f) / 2.0f) * 255.0f; data[i + 2] = ((randvec.z + 1.0f) / 2.0f) * 255.0f; } _permutationNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB), 256, 2)); _permutationNormalTexture->assignStoredMip(0, _blueTexture->getTexelFormat(), sizeof(data), data); // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } return _permutationNormalTexture;} const unsigned char OPAQUE_WHITE[] = { 0xFF, 0xFF, 0xFF, 0xFF };//const unsigned char TRANSPARENT_WHITE[] = { 0xFF, 0xFF, 0xFF, 0x0 };//const unsigned char OPAQUE_BLACK[] = { 0x0, 0x0, 0x0, 0xFF };const unsigned char OPAQUE_BLUE[] = { 0x80, 0x80, 0xFF, 0xFF }; /*static void loadSingleColorTexture(const unsigned char* color) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, color); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);}*/ const gpu::TexturePointer& TextureCache::getWhiteTexture() { if (_whiteTexture.isNull()) { _whiteTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA), 1, 1)); _whiteTexture->assignStoredMip(0, _whiteTexture->getTexelFormat(), sizeof(OPAQUE_WHITE), OPAQUE_WHITE); } return _whiteTexture;} const gpu::TexturePointer& TextureCache::getBlueTexture() { if (_blueTexture.isNull()) { _blueTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA), 1, 1)); _blueTexture->assignStoredMip(0, _blueTexture->getTexelFormat(), sizeof(OPAQUE_BLUE), OPAQUE_BLUE); } return _blueTexture;} /// Extra data for creating textures.class TextureExtra {public: TextureType type; const QByteArray& content;}; NetworkTexturePointer TextureCache::getTexture(const QUrl& url, TextureType type, bool dilatable, const QByteArray& content) { if (!dilatable) { TextureExtra extra = { type, content }; return ResourceCache::getResource(url, QUrl(), false, &extra).staticCast<NetworkTexture>(); } NetworkTexturePointer texture = _dilatableNetworkTextures.value(url); if (texture.isNull()) { texture = NetworkTexturePointer(new DilatableNetworkTexture(url, content), &Resource::allReferencesCleared); texture->setSelf(texture); texture->setCache(this); _dilatableNetworkTextures.insert(url, texture); } else { removeUnusedResource(texture); } return texture;} QOpenGLFramebufferObject* TextureCache::getPrimaryFramebufferObject() { if (!_primaryFramebufferObject) { _primaryFramebufferObject = createFramebufferObject(); glGenTextures(1, &_primaryDepthTextureID); glBindTexture(GL_TEXTURE_2D, _primaryDepthTextureID); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, _frameBufferSize.width(), _frameBufferSize.height(), 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glGenTextures(1, &_primaryNormalTextureID); glBindTexture(GL_TEXTURE_2D, _primaryNormalTextureID); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _frameBufferSize.width(), _frameBufferSize.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glBindTexture(GL_TEXTURE_2D, 0); glGenTextures(1, &_primarySpecularTextureID); glBindTexture(GL_TEXTURE_2D, _primarySpecularTextureID); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _frameBufferSize.width(), _frameBufferSize.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glBindTexture(GL_TEXTURE_2D, 0); _primaryFramebufferObject->bind(); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, _primaryDepthTextureID, 0); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, _primaryNormalTextureID, 0); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, _primarySpecularTextureID, 0); _primaryFramebufferObject->release(); } return _primaryFramebufferObject;} GLuint TextureCache::getPrimaryDepthTextureID() { // ensure that the primary framebuffer object is initialized before returning the depth texture id getPrimaryFramebufferObject(); return _primaryDepthTextureID;} GLuint TextureCache::getPrimaryNormalTextureID() { // ensure that the primary framebuffer object is initialized before returning the normal texture id getPrimaryFramebufferObject(); return _primaryNormalTextureID;} GLuint TextureCache::getPrimarySpecularTextureID() { getPrimaryFramebufferObject(); return _primarySpecularTextureID;} void TextureCache::setPrimaryDrawBuffers(bool color, bool normal, bool specular) { GLenum buffers[3]; int bufferCount = 0; if (color) { buffers[bufferCount++] = GL_COLOR_ATTACHMENT0; } if (normal) { buffers[bufferCount++] = GL_COLOR_ATTACHMENT1; } if (specular) { buffers[bufferCount++] = GL_COLOR_ATTACHMENT2; } glDrawBuffers(bufferCount, buffers);} QOpenGLFramebufferObject* TextureCache::getSecondaryFramebufferObject() { if (!_secondaryFramebufferObject) { _secondaryFramebufferObject = createFramebufferObject(); } return _secondaryFramebufferObject;} QOpenGLFramebufferObject* TextureCache::getTertiaryFramebufferObject() { if (!_tertiaryFramebufferObject) { _tertiaryFramebufferObject = createFramebufferObject(); } return _tertiaryFramebufferObject;} QOpenGLFramebufferObject* TextureCache::getShadowFramebufferObject() { if (!_shadowFramebufferObject) { const int SHADOW_MAP_SIZE = 2048; _shadowFramebufferObject = new QOpenGLFramebufferObject(SHADOW_MAP_SIZE, SHADOW_MAP_SIZE, QOpenGLFramebufferObject::NoAttachment, GL_TEXTURE_2D, GL_RGB); glGenTextures(1, &_shadowDepthTextureID); glBindTexture(GL_TEXTURE_2D, _shadowDepthTextureID); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, SHADOW_MAP_SIZE, SHADOW_MAP_SIZE, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); const float DISTANT_BORDER[] = { 1.0f, 1.0f, 1.0f, 1.0f }; glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, DISTANT_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); glBindTexture(GL_TEXTURE_2D, 0); _shadowFramebufferObject->bind(); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, _shadowDepthTextureID, 0); _shadowFramebufferObject->release(); } return _shadowFramebufferObject;} GLuint TextureCache::getShadowDepthTextureID() { // ensure that the shadow framebuffer object is initialized before returning the depth texture id getShadowFramebufferObject(); return _shadowDepthTextureID;} bool TextureCache::eventFilter(QObject* watched, QEvent* event) { if (event->type() == QEvent::Resize) { QSize size = static_cast<QResizeEvent*>(event)->size(); if (_primaryFramebufferObject && _primaryFramebufferObject->size() != size) { delete _primaryFramebufferObject; _primaryFramebufferObject = NULL; glDeleteTextures(1, &_primaryDepthTextureID); glDeleteTextures(1, &_primaryNormalTextureID); glDeleteTextures(1, &_primarySpecularTextureID); } if (_secondaryFramebufferObject && _secondaryFramebufferObject->size() != size) { delete _secondaryFramebufferObject; _secondaryFramebufferObject = NULL; } if (_tertiaryFramebufferObject && _tertiaryFramebufferObject->size() != size) { delete _tertiaryFramebufferObject; _tertiaryFramebufferObject = NULL; } } return false;} QSharedPointer<Resource> TextureCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) { const TextureExtra* textureExtra = static_cast<const TextureExtra*>(extra); return QSharedPointer<Resource>(new NetworkTexture(url, textureExtra->type, textureExtra->content), &Resource::allReferencesCleared);} void TextureCache::associateWithWidget(QGLWidget* widget) { if (_associatedWidget) { _associatedWidget->removeEventFilter(this); } _associatedWidget = widget; _associatedWidget->installEventFilter(this);} QOpenGLFramebufferObject* TextureCache::createFramebufferObject() { QOpenGLFramebufferObject* fbo = new QOpenGLFramebufferObject(_frameBufferSize); glBindTexture(GL_TEXTURE_2D, fbo->texture()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, 0); return fbo;} Texture::Texture() {} Texture::~Texture() {} GLuint Texture::getID() const { return gpu::GLBackend::getTextureID(_gpuTexture);} NetworkTexture::NetworkTexture(const QUrl& url, TextureType type, const QByteArray& content) : Resource(url, !content.isEmpty()), _type(type), _translucent(false), _width(0), _height(0) { if (!url.isValid()) { _loaded = true; } // default to white/blue/black /* glBindTexture(GL_TEXTURE_2D, getID()); switch (type) { case NORMAL_TEXTURE: loadSingleColorTexture(OPAQUE_BLUE); break; case SPECULAR_TEXTURE: loadSingleColorTexture(OPAQUE_BLACK); break; case SPLAT_TEXTURE: loadSingleColorTexture(TRANSPARENT_WHITE); break; default: loadSingleColorTexture(OPAQUE_WHITE); break; } glBindTexture(GL_TEXTURE_2D, 0); */ // if we have content, load it after we have our self pointer if (!content.isEmpty()) { _startedLoading = true; QMetaObject::invokeMethod(this, "loadContent", Qt::QueuedConnection, Q_ARG(const QByteArray&, content)); }} class ImageReader : public QRunnable {public: ImageReader(const QWeakPointer<Resource>& texture, QNetworkReply* reply, const QUrl& url = QUrl(), const QByteArray& content = QByteArray()); virtual void run(); private: QWeakPointer<Resource> _texture; QNetworkReply* _reply; QUrl _url; QByteArray _content;}; ImageReader::ImageReader(const QWeakPointer<Resource>& texture, QNetworkReply* reply, const QUrl& url, const QByteArray& content) : _texture(texture), _reply(reply), _url(url), _content(content) {} void ImageReader::run() { QSharedPointer<Resource> texture = _texture.toStrongRef(); if (texture.isNull()) { if (_reply) { _reply->deleteLater(); } return; } if (_reply) { _url = _reply->url(); _content = _reply->readAll(); _reply->deleteLater(); } QImage image = QImage::fromData(_content); int originalWidth = image.width(); int originalHeight = image.height(); // enforce a fixed maximum area (1024 * 2048) const int MAXIMUM_AREA_SIZE = 2097152; int imageArea = image.width() * image.height(); if (imageArea > MAXIMUM_AREA_SIZE) { float scaleRatio = sqrtf((float)MAXIMUM_AREA_SIZE) / sqrtf((float)imageArea); int resizeWidth = static_cast<int>(std::floor(scaleRatio * static_cast<float>(image.width()))); int resizeHeight = static_cast<int>(std::floor(scaleRatio * static_cast<float>(image.height()))); qDebug() << "Image greater than maximum size:" << _url << image.width() << image.height() << " scaled to:" << resizeWidth << resizeHeight; image = image.scaled(resizeWidth, resizeHeight, Qt::IgnoreAspectRatio); imageArea = image.width() * image.height(); } const int EIGHT_BIT_MAXIMUM = 255; if (!image.hasAlphaChannel()) { if (image.format() != QImage::Format_RGB888) { image = image.convertToFormat(QImage::Format_RGB888); } int redTotal = 0, greenTotal = 0, blueTotal = 0; for (int y = 0; y < image.height(); y++) { for (int x = 0; x < image.width(); x++) { QRgb rgb = image.pixel(x, y); redTotal += qRed(rgb); greenTotal += qGreen(rgb); blueTotal += qBlue(rgb); } } QColor averageColor(EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM); if (imageArea > 0) { averageColor.setRgb(redTotal / imageArea, greenTotal / imageArea, blueTotal / imageArea); } QMetaObject::invokeMethod(texture.data(), "setImage", Q_ARG(const QImage&, image), Q_ARG(bool, false), Q_ARG(const QColor&, averageColor), Q_ARG(int, originalWidth), Q_ARG(int, originalHeight)); return; } if (image.format() != QImage::Format_ARGB32) { image = image.convertToFormat(QImage::Format_ARGB32); }Lines 1–500 of 635. Load more
// // TextureCache.cpp // interface/src/renderer // // Created by Andrzej Kapolka on 8/6/13. // Copyright 2013 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // // include this before QGLWidget, which includes an earlier version of OpenGL #include <gpu/GPUConfig.h> #include <QEvent> #include <QGLWidget> #include <QNetworkReply> #include <QOpenGLFramebufferObject> #include <QResizeEvent> #include <QRunnable> #include <QThreadPool> #include <glm/glm.hpp> #include <glm/gtc/random.hpp> #include "TextureCache.h" #include "gpu/GLBackend.h" TextureCache::TextureCache() : _permutationNormalTexture(0), _whiteTexture(0), _blueTexture(0), _primaryDepthTextureID(0), _primaryNormalTextureID(0), _primarySpecularTextureID(0), _primaryFramebufferObject(NULL), _secondaryFramebufferObject(NULL), _tertiaryFramebufferObject(NULL), _shadowFramebufferObject(NULL), _frameBufferSize(100, 100), _associatedWidget(NULL) { const qint64 TEXTURE_DEFAULT_UNUSED_MAX_SIZE = DEFAULT_UNUSED_MAX_SIZE; setUnusedResourceCacheSize(TEXTURE_DEFAULT_UNUSED_MAX_SIZE); } TextureCache::~TextureCache() { if (_primaryFramebufferObject) { glDeleteTextures(1, &_primaryDepthTextureID); glDeleteTextures(1, &_primaryNormalTextureID); glDeleteTextures(1, &_primarySpecularTextureID); } if (_primaryFramebufferObject) { delete _primaryFramebufferObject; } if (_secondaryFramebufferObject) { delete _secondaryFramebufferObject; } if (_tertiaryFramebufferObject) { delete _tertiaryFramebufferObject; } } void TextureCache::setFrameBufferSize(QSize frameBufferSize) { //If the size changed, we need to delete our FBOs if (_frameBufferSize != frameBufferSize) { _frameBufferSize = frameBufferSize; if (_primaryFramebufferObject) { delete _primaryFramebufferObject; _primaryFramebufferObject = NULL; glDeleteTextures(1, &_primaryDepthTextureID); _primaryDepthTextureID = 0; glDeleteTextures(1, &_primaryNormalTextureID); _primaryNormalTextureID = 0; glDeleteTextures(1, &_primarySpecularTextureID); _primarySpecularTextureID = 0; } if (_secondaryFramebufferObject) { delete _secondaryFramebufferObject; _secondaryFramebufferObject = NULL; } if (_tertiaryFramebufferObject) { delete _tertiaryFramebufferObject; _tertiaryFramebufferObject = NULL; } } } // use fixed table of permutations. Could also make ordered list programmatically // and then shuffle algorithm. For testing, this ensures consistent behavior in each run. // this list taken from Ken Perlin's Improved Noise reference implementation (orig. in Java) at // http://mrl.nyu.edu/~perlin/noise/ const int permutation[256] = { 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180 }; #define USE_CHRIS_NOISE 1 const gpu::TexturePointer& TextureCache::getPermutationNormalTexture() { if (_permutationNormalTexture.isNull()) { // the first line consists of random permutation offsets unsigned char data[256 * 2 * 3]; #if (USE_CHRIS_NOISE==1) for (int i = 0; i < 256; i++) { data[3*i+0] = permutation[i]; data[3*i+1] = permutation[i]; data[3*i+2] = permutation[i]; #else for (int i = 0; i < 256 * 3; i++) { data[i] = rand() % 256; #endif } for (int i = 256 * 3; i < 256 * 3 * 2; i += 3) { glm::vec3 randvec = glm::sphericalRand(1.0f); data[i] = ((randvec.x + 1.0f) / 2.0f) * 255.0f; data[i + 1] = ((randvec.y + 1.0f) / 2.0f) * 255.0f; data[i + 2] = ((randvec.z + 1.0f) / 2.0f) * 255.0f; } _permutationNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB), 256, 2)); _permutationNormalTexture->assignStoredMip(0, _blueTexture->getTexelFormat(), sizeof(data), data); // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } return _permutationNormalTexture; } const unsigned char OPAQUE_WHITE[] = { 0xFF, 0xFF, 0xFF, 0xFF }; //const unsigned char TRANSPARENT_WHITE[] = { 0xFF, 0xFF, 0xFF, 0x0 }; //const unsigned char OPAQUE_BLACK[] = { 0x0, 0x0, 0x0, 0xFF }; const unsigned char OPAQUE_BLUE[] = { 0x80, 0x80, 0xFF, 0xFF }; /* static void loadSingleColorTexture(const unsigned char* color) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, color); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } */ const gpu::TexturePointer& TextureCache::getWhiteTexture() { if (_whiteTexture.isNull()) { _whiteTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA), 1, 1)); _whiteTexture->assignStoredMip(0, _whiteTexture->getTexelFormat(), sizeof(OPAQUE_WHITE), OPAQUE_WHITE); } return _whiteTexture; } const gpu::TexturePointer& TextureCache::getBlueTexture() { if (_blueTexture.isNull()) { _blueTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA), 1, 1)); _blueTexture->assignStoredMip(0, _blueTexture->getTexelFormat(), sizeof(OPAQUE_BLUE), OPAQUE_BLUE); } return _blueTexture; } /// Extra data for creating textures. class TextureExtra { public: TextureType type; const QByteArray& content; }; NetworkTexturePointer TextureCache::getTexture(const QUrl& url, TextureType type, bool dilatable, const QByteArray& content) { if (!dilatable) { TextureExtra extra = { type, content }; return ResourceCache::getResource(url, QUrl(), false, &extra).staticCast<NetworkTexture>(); } NetworkTexturePointer texture = _dilatableNetworkTextures.value(url); if (texture.isNull()) { texture = NetworkTexturePointer(new DilatableNetworkTexture(url, content), &Resource::allReferencesCleared); texture->setSelf(texture); texture->setCache(this); _dilatableNetworkTextures.insert(url, texture); } else { removeUnusedResource(texture); } return texture; } QOpenGLFramebufferObject* TextureCache::getPrimaryFramebufferObject() { if (!_primaryFramebufferObject) { _primaryFramebufferObject = createFramebufferObject(); glGenTextures(1, &_primaryDepthTextureID); glBindTexture(GL_TEXTURE_2D, _primaryDepthTextureID); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, _frameBufferSize.width(), _frameBufferSize.height(), 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glGenTextures(1, &_primaryNormalTextureID); glBindTexture(GL_TEXTURE_2D, _primaryNormalTextureID); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _frameBufferSize.width(), _frameBufferSize.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glBindTexture(GL_TEXTURE_2D, 0); glGenTextures(1, &_primarySpecularTextureID); glBindTexture(GL_TEXTURE_2D, _primarySpecularTextureID); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _frameBufferSize.width(), _frameBufferSize.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glBindTexture(GL_TEXTURE_2D, 0); _primaryFramebufferObject->bind(); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, _primaryDepthTextureID, 0); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, _primaryNormalTextureID, 0); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, _primarySpecularTextureID, 0); _primaryFramebufferObject->release(); } return _primaryFramebufferObject; } GLuint TextureCache::getPrimaryDepthTextureID() { // ensure that the primary framebuffer object is initialized before returning the depth texture id getPrimaryFramebufferObject(); return _primaryDepthTextureID; } GLuint TextureCache::getPrimaryNormalTextureID() { // ensure that the primary framebuffer object is initialized before returning the normal texture id getPrimaryFramebufferObject(); return _primaryNormalTextureID; } GLuint TextureCache::getPrimarySpecularTextureID() { getPrimaryFramebufferObject(); return _primarySpecularTextureID; } void TextureCache::setPrimaryDrawBuffers(bool color, bool normal, bool specular) { GLenum buffers[3]; int bufferCount = 0; if (color) { buffers[bufferCount++] = GL_COLOR_ATTACHMENT0; } if (normal) { buffers[bufferCount++] = GL_COLOR_ATTACHMENT1; } if (specular) { buffers[bufferCount++] = GL_COLOR_ATTACHMENT2; } glDrawBuffers(bufferCount, buffers); } QOpenGLFramebufferObject* TextureCache::getSecondaryFramebufferObject() { if (!_secondaryFramebufferObject) { _secondaryFramebufferObject = createFramebufferObject(); } return _secondaryFramebufferObject; } QOpenGLFramebufferObject* TextureCache::getTertiaryFramebufferObject() { if (!_tertiaryFramebufferObject) { _tertiaryFramebufferObject = createFramebufferObject(); } return _tertiaryFramebufferObject; } QOpenGLFramebufferObject* TextureCache::getShadowFramebufferObject() { if (!_shadowFramebufferObject) { const int SHADOW_MAP_SIZE = 2048; _shadowFramebufferObject = new QOpenGLFramebufferObject(SHADOW_MAP_SIZE, SHADOW_MAP_SIZE, QOpenGLFramebufferObject::NoAttachment, GL_TEXTURE_2D, GL_RGB); glGenTextures(1, &_shadowDepthTextureID); glBindTexture(GL_TEXTURE_2D, _shadowDepthTextureID); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, SHADOW_MAP_SIZE, SHADOW_MAP_SIZE, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); const float DISTANT_BORDER[] = { 1.0f, 1.0f, 1.0f, 1.0f }; glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, DISTANT_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); glBindTexture(GL_TEXTURE_2D, 0); _shadowFramebufferObject->bind(); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, _shadowDepthTextureID, 0); _shadowFramebufferObject->release(); } return _shadowFramebufferObject; } GLuint TextureCache::getShadowDepthTextureID() { // ensure that the shadow framebuffer object is initialized before returning the depth texture id getShadowFramebufferObject(); return _shadowDepthTextureID; } bool TextureCache::eventFilter(QObject* watched, QEvent* event) { if (event->type() == QEvent::Resize) { QSize size = static_cast<QResizeEvent*>(event)->size(); if (_primaryFramebufferObject && _primaryFramebufferObject->size() != size) { delete _primaryFramebufferObject; _primaryFramebufferObject = NULL; glDeleteTextures(1, &_primaryDepthTextureID); glDeleteTextures(1, &_primaryNormalTextureID); glDeleteTextures(1, &_primarySpecularTextureID); } if (_secondaryFramebufferObject && _secondaryFramebufferObject->size() != size) { delete _secondaryFramebufferObject; _secondaryFramebufferObject = NULL; } if (_tertiaryFramebufferObject && _tertiaryFramebufferObject->size() != size) { delete _tertiaryFramebufferObject; _tertiaryFramebufferObject = NULL; } } return false; } QSharedPointer<Resource> TextureCache::createResource(const QUrl& url, const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) { const TextureExtra* textureExtra = static_cast<const TextureExtra*>(extra); return QSharedPointer<Resource>(new NetworkTexture(url, textureExtra->type, textureExtra->content), &Resource::allReferencesCleared); } void TextureCache::associateWithWidget(QGLWidget* widget) { if (_associatedWidget) { _associatedWidget->removeEventFilter(this); } _associatedWidget = widget; _associatedWidget->installEventFilter(this); } QOpenGLFramebufferObject* TextureCache::createFramebufferObject() { QOpenGLFramebufferObject* fbo = new QOpenGLFramebufferObject(_frameBufferSize); glBindTexture(GL_TEXTURE_2D, fbo->texture()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, 0); return fbo; } Texture::Texture() { } Texture::~Texture() { } GLuint Texture::getID() const { return gpu::GLBackend::getTextureID(_gpuTexture); } NetworkTexture::NetworkTexture(const QUrl& url, TextureType type, const QByteArray& content) : Resource(url, !content.isEmpty()), _type(type), _translucent(false), _width(0), _height(0) { if (!url.isValid()) { _loaded = true; } // default to white/blue/black /* glBindTexture(GL_TEXTURE_2D, getID()); switch (type) { case NORMAL_TEXTURE: loadSingleColorTexture(OPAQUE_BLUE); break; case SPECULAR_TEXTURE: loadSingleColorTexture(OPAQUE_BLACK); break; case SPLAT_TEXTURE: loadSingleColorTexture(TRANSPARENT_WHITE); break; default: loadSingleColorTexture(OPAQUE_WHITE); break; } glBindTexture(GL_TEXTURE_2D, 0); */ // if we have content, load it after we have our self pointer if (!content.isEmpty()) { _startedLoading = true; QMetaObject::invokeMethod(this, "loadContent", Qt::QueuedConnection, Q_ARG(const QByteArray&, content)); } } class ImageReader : public QRunnable { public: ImageReader(const QWeakPointer<Resource>& texture, QNetworkReply* reply, const QUrl& url = QUrl(), const QByteArray& content = QByteArray()); virtual void run(); private: QWeakPointer<Resource> _texture; QNetworkReply* _reply; QUrl _url; QByteArray _content; }; ImageReader::ImageReader(const QWeakPointer<Resource>& texture, QNetworkReply* reply, const QUrl& url, const QByteArray& content) : _texture(texture), _reply(reply), _url(url), _content(content) { } void ImageReader::run() { QSharedPointer<Resource> texture = _texture.toStrongRef(); if (texture.isNull()) { if (_reply) { _reply->deleteLater(); } return; } if (_reply) { _url = _reply->url(); _content = _reply->readAll(); _reply->deleteLater(); } QImage image = QImage::fromData(_content); int originalWidth = image.width(); int originalHeight = image.height(); // enforce a fixed maximum area (1024 * 2048) const int MAXIMUM_AREA_SIZE = 2097152; int imageArea = image.width() * image.height(); if (imageArea > MAXIMUM_AREA_SIZE) { float scaleRatio = sqrtf((float)MAXIMUM_AREA_SIZE) / sqrtf((float)imageArea); int resizeWidth = static_cast<int>(std::floor(scaleRatio * static_cast<float>(image.width()))); int resizeHeight = static_cast<int>(std::floor(scaleRatio * static_cast<float>(image.height()))); qDebug() << "Image greater than maximum size:" << _url << image.width() << image.height() << " scaled to:" << resizeWidth << resizeHeight; image = image.scaled(resizeWidth, resizeHeight, Qt::IgnoreAspectRatio); imageArea = image.width() * image.height(); } const int EIGHT_BIT_MAXIMUM = 255; if (!image.hasAlphaChannel()) { if (image.format() != QImage::Format_RGB888) { image = image.convertToFormat(QImage::Format_RGB888); } int redTotal = 0, greenTotal = 0, blueTotal = 0; for (int y = 0; y < image.height(); y++) { for (int x = 0; x < image.width(); x++) { QRgb rgb = image.pixel(x, y); redTotal += qRed(rgb); greenTotal += qGreen(rgb); blueTotal += qBlue(rgb); } } QColor averageColor(EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM); if (imageArea > 0) { averageColor.setRgb(redTotal / imageArea, greenTotal / imageArea, blueTotal / imageArea); } QMetaObject::invokeMethod(texture.data(), "setImage", Q_ARG(const QImage&, image), Q_ARG(bool, false), Q_ARG(const QColor&, averageColor), Q_ARG(int, originalWidth), Q_ARG(int, originalHeight)); return; } if (image.format() != QImage::Format_ARGB32) { image = image.convertToFormat(QImage::Format_ARGB32); }