Atlante45typoa52c79c11 years agoHistory
810 lines · 31.2 KB · C++
RawDownload
////  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 <gpu/Batch.h>#include <gpu/GLBackend.h>#include <gpu/GPUConfig.h> #include <QNetworkReply>#include <QPainter>#include <QRunnable>#include <QThreadPool>#include <qimagereader.h> #include <glm/glm.hpp>#include <glm/gtc/random.hpp> #include "RenderUtilsLogging.h"#include "TextureCache.h"  #include <mutex> TextureCache::TextureCache() :    _permutationNormalTexture(0),    _whiteTexture(0),    _blueTexture(0),    _frameBufferSize(100, 100){    const qint64 TEXTURE_DEFAULT_UNUSED_MAX_SIZE = DEFAULT_UNUSED_MAX_SIZE;    setUnusedResourceCacheSize(TEXTURE_DEFAULT_UNUSED_MAX_SIZE);} TextureCache::~TextureCache() {} void TextureCache::setFrameBufferSize(QSize frameBufferSize) {    //If the size changed, we need to delete our FBOs    if (_frameBufferSize != frameBufferSize) {        _frameBufferSize = frameBufferSize;         _primaryFramebuffer.reset();        _primaryDepthTexture.reset();        _primaryColorTexture.reset();        _primaryNormalTexture.reset();        _primarySpecularTexture.reset();         _secondaryFramebuffer.reset();         _tertiaryFramebuffer.reset();    }} // 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) {         // 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) {        _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) {        _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;} void TextureCache::createPrimaryFramebuffer() {    _primaryFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create());     auto colorFormat = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA);    auto width = _frameBufferSize.width();    auto height = _frameBufferSize.height();     auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT);    _primaryColorTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler));    _primaryNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler));    _primarySpecularTexture = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width, height, defaultSampler));     _primaryFramebuffer->setRenderBuffer(0, _primaryColorTexture);    _primaryFramebuffer->setRenderBuffer(1, _primaryNormalTexture);    _primaryFramebuffer->setRenderBuffer(2, _primarySpecularTexture);      auto depthFormat = gpu::Element(gpu::SCALAR, gpu::FLOAT, gpu::DEPTH);    _primaryDepthTexture = gpu::TexturePointer(gpu::Texture::create2D(depthFormat, width, height, defaultSampler));     _primaryFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat);} gpu::FramebufferPointer TextureCache::getPrimaryFramebuffer() {    if (!_primaryFramebuffer) {        createPrimaryFramebuffer();    }    return _primaryFramebuffer;} gpu::TexturePointer TextureCache::getPrimaryDepthTexture() {    if (!_primaryDepthTexture) {        createPrimaryFramebuffer();    }    return _primaryDepthTexture;} gpu::TexturePointer TextureCache::getPrimaryColorTexture() {    if (!_primaryColorTexture) {        createPrimaryFramebuffer();    }    return _primaryColorTexture;} gpu::TexturePointer TextureCache::getPrimaryNormalTexture() {    if (!_primaryNormalTexture) {        createPrimaryFramebuffer();    }    return _primaryNormalTexture;} gpu::TexturePointer TextureCache::getPrimarySpecularTexture() {    if (!_primarySpecularTexture) {        createPrimaryFramebuffer();    }    return _primarySpecularTexture;} GLuint TextureCache::getPrimaryDepthTextureID() {    return gpu::GLBackend::getTextureID(getPrimaryDepthTexture());} GLuint TextureCache::getPrimaryColorTextureID() {    return gpu::GLBackend::getTextureID(getPrimaryColorTexture());} GLuint TextureCache::getPrimaryNormalTextureID() {    return gpu::GLBackend::getTextureID(getPrimaryNormalTexture());} GLuint TextureCache::getPrimarySpecularTextureID() {    return gpu::GLBackend::getTextureID(getPrimarySpecularTexture());} void TextureCache::setPrimaryDrawBuffers(bool color, bool normal, bool specular) {    gpu::Batch batch;    setPrimaryDrawBuffers(batch, color, normal, specular);    gpu::GLBackend::renderBatch(batch);}    void TextureCache::setPrimaryDrawBuffers(gpu::Batch& batch, 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;    }    batch._glDrawBuffers(bufferCount, buffers);} gpu::FramebufferPointer TextureCache::getSecondaryFramebuffer() {    if (!_secondaryFramebuffer) {        _secondaryFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(gpu::Element::COLOR_RGBA_32, _frameBufferSize.width(), _frameBufferSize.height()));    }    return _secondaryFramebuffer;} gpu::FramebufferPointer TextureCache::getTertiaryFramebuffer() {    if (!_tertiaryFramebuffer) {        _tertiaryFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(gpu::Element::COLOR_RGBA_32, _frameBufferSize.width(), _frameBufferSize.height()));    }    return _tertiaryFramebuffer;}  gpu::FramebufferPointer TextureCache::getShadowFramebuffer() {    if (!_shadowFramebuffer) {        const int SHADOW_MAP_SIZE = 2048;        _shadowFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::createShadowmap(SHADOW_MAP_SIZE));         _shadowTexture = _shadowFramebuffer->getDepthStencilBuffer();    }    return _shadowFramebuffer;} GLuint TextureCache::getShadowDepthTextureID() {    // ensure that the shadow framebuffer object is initialized before returning the depth texture id    getShadowFramebuffer();    return gpu::GLBackend::getTextureID(_shadowTexture);} /// Returns a texture version of an image filegpu::TexturePointer TextureCache::getImageTexture(const QString& path) {    QImage image = QImage(path).mirrored(false, true);    gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB);    gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB);    if (image.hasAlphaChannel()) {        formatGPU = gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA);        formatMip = gpu::Element(gpu::VEC4, gpu::UINT8, gpu::BGRA);    }    gpu::TexturePointer texture = gpu::TexturePointer(        gpu::Texture::create2D(formatGPU, image.width(), image.height(),             gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));    texture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits());    texture->autoGenerateMips(-1);    return texture;} 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);} 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;    }     std::string theName = url.toString().toStdString();    // 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, TextureType type, QNetworkReply* reply, const QUrl& url = QUrl(),        const QByteArray& content = QByteArray());        virtual void run(); private:        QWeakPointer<Resource> _texture;    TextureType _type;    QNetworkReply* _reply;    QUrl _url;    QByteArray _content;}; void NetworkTexture::downloadFinished(QNetworkReply* reply) {    // send the reader off to the thread pool    QThreadPool::globalInstance()->start(new ImageReader(_self, _type, reply));} void NetworkTexture::loadContent(const QByteArray& content) {    QThreadPool::globalInstance()->start(new ImageReader(_self, _type, NULL, _url, content));} ImageReader::ImageReader(const QWeakPointer<Resource>& texture, TextureType type, QNetworkReply* reply,        const QUrl& url, const QByteArray& content) :    _texture(texture),    _type(type),    _reply(reply),    _url(url),    _content(content) {} std::once_flag onceListSupportedFormatsflag;void listSupportedImageFormats() {    std::call_once(onceListSupportedFormatsflag, [](){        auto supportedFormats = QImageReader::supportedImageFormats();        QString formats;        foreach(const QByteArray& f, supportedFormats) {            formats += QString(f) + ",";        }        qCDebug(renderutils) << "List of supported Image formats:" << formats;    });}  class CubeLayout {public:    int _widthRatio = 1;    int _heightRatio = 1;     class Face {    public:        int _x = 0;        int _y = 0;        bool _horizontalMirror = false;        bool _verticalMirror = false;         Face() {}        Face(int x, int y, bool horizontalMirror, bool verticalMirror) : _x(x), _y(y), _horizontalMirror(horizontalMirror), _verticalMirror(verticalMirror) {}    };                    Face _faceXPos;    Face _faceXNeg;    Face _faceYPos;    Face _faceYNeg;    Face _faceZPos;    Face _faceZNeg;     CubeLayout(int wr, int hr, Face fXP, Face fXN, Face fYP, Face fYN, Face fZP, Face fZN) :            _widthRatio(wr),            _heightRatio(hr),            _faceXPos(fXP),            _faceXNeg(fXN),            _faceYPos(fYP),            _faceYNeg(fYN),            _faceZPos(fZP),            _faceZNeg(fZN) {}}; 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();    }     listSupportedImageFormats();     // try to help the QImage loader by extracting the image file format from the url filename ext    // Some tga are not created properly for example without it    auto filename = _url.fileName().toStdString();    auto filenameExtension = filename.substr(filename.find_last_of('.') + 1);    QImage image = QImage::fromData(_content, filenameExtension.c_str());     // Note that QImage.format is the pixel format which is different from the "format" of the image file...    auto imageFormat = image.format();     int originalWidth = image.width();    int originalHeight = image.height();        if (originalWidth == 0 || originalHeight == 0 || imageFormat == QImage::Format_Invalid) {        if (filenameExtension.empty()) {            qCDebug(renderutils) << "QImage failed to create from content, no file extension:" << _url;        } else {            qCDebug(renderutils) << "QImage failed to create from content" << _url;        }        return;    }     int imageArea = image.width() * image.height();    auto ntex = dynamic_cast<NetworkTexture*>(&*texture);    if (ntex && (ntex->getType() == CUBE_TEXTURE)) {        qCDebug(renderutils) << "Cube map size:" << _url << image.width() << image.height();    } else {         // enforce a fixed maximum area (1024 * 2048)        const int MAXIMUM_AREA_SIZE = 2097152;        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())));            qCDebug(renderutils) << "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();        }    }        int opaquePixels = 0;    int translucentPixels = 0;    bool isTransparent = false;    int redTotal = 0, greenTotal = 0, blueTotal = 0, alphaTotal = 0;    const int EIGHT_BIT_MAXIMUM = 255;        QColor averageColor(EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM);     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++) {

Lines 1–500 of 810.