diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 9c79bb5ff5..b4c15ac2a8 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1080,9 +1080,10 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } else if (type.contains("transparentcolor")) { // it should be TransparentColor... // THis is how Maya assign a texture that affect diffuse color AND transparency ? diffuseTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1)); - } else if (type.contains("bump") || type.contains("normal")) { + } else if (type.contains("bump")) { bumpTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1)); - + } else if (type.contains("normal")) { + normalTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1)); } else if (type.contains("specular") || type.contains("reflection")) { specularTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1)); diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 0dc8d7ece3..ddcff8224b 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -112,7 +112,9 @@ public: Transform transform; int texcoordSet; QString texcoordSetName; - + + bool isBumpmap{ false }; + bool isNull() const { return name.isEmpty() && filename.isEmpty() && content.isEmpty(); } }; @@ -394,6 +396,7 @@ public: QHash diffuseTextures; QHash bumpTextures; + QHash normalTextures; QHash specularTextures; QHash emissiveTextures; QHash ambientTextures; diff --git a/libraries/fbx/src/FBXReader_Material.cpp b/libraries/fbx/src/FBXReader_Material.cpp index 6d0195bac5..c29c64030e 100644 --- a/libraries/fbx/src/FBXReader_Material.cpp +++ b/libraries/fbx/src/FBXReader_Material.cpp @@ -86,13 +86,21 @@ void FBXReader::consolidateFBXMaterials() { FBXTexture normalTexture; QString bumpTextureID = bumpTextures.value(material.materialID); - if (!bumpTextureID.isNull()) { - normalTexture = getTexture(bumpTextureID); - + QString normalTextureID = normalTextures.value(material.materialID); + if (!normalTextureID.isNull()) { + normalTexture = getTexture(normalTextureID); + normalTexture.isBumpmap = false; + + material.normalTexture = normalTexture; + detectDifferentUVs |= (normalTexture.texcoordSet != 0) || (!normalTexture.transform.isIdentity()); + } else if (!bumpTextureID.isNull()) { + normalTexture = getTexture(bumpTextureID); + normalTexture.isBumpmap = true; + material.normalTexture = normalTexture; - detectDifferentUVs |= (normalTexture.texcoordSet != 0) || (!normalTexture.transform.isIdentity()); } + FBXTexture specularTexture; QString specularTextureID = specularTextures.value(material.materialID); diff --git a/libraries/gpu-networking/src/gpu-networking/TextureCache.cpp b/libraries/gpu-networking/src/gpu-networking/TextureCache.cpp index e77c7c1145..fd69a2ab04 100644 --- a/libraries/gpu-networking/src/gpu-networking/TextureCache.cpp +++ b/libraries/gpu-networking/src/gpu-networking/TextureCache.cpp @@ -182,11 +182,10 @@ Texture::~Texture() { NetworkTexture::NetworkTexture(const QUrl& url, TextureType type, const QByteArray& content) : Resource(url, !content.isEmpty()), _type(type), - _translucent(false), _width(0), _height(0) { - _textureStorage.reset(new model::TextureStorage()); + _textureSource.reset(new model::TextureSource()); if (!url.isValid()) { _loaded = true; @@ -202,13 +201,12 @@ NetworkTexture::NetworkTexture(const QUrl& url, TextureType type, const QByteArr NetworkTexture::NetworkTexture(const QUrl& url, const TextureLoaderFunc& textureLoader, const QByteArray& content) : Resource(url, !content.isEmpty()), - // _type(type), + _type(CUSTOM_TEXTURE), _textureLoader(textureLoader), - _translucent(false), _width(0), _height(0) { - _textureStorage.reset(new model::TextureStorage()); + _textureSource.reset(new model::TextureSource()); if (!url.isValid()) { _loaded = true; @@ -223,11 +221,27 @@ NetworkTexture::NetworkTexture(const QUrl& url, const TextureLoaderFunc& texture } NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const { - if (_type != CUBE_TEXTURE) { - - return TextureLoaderFunc(model::TextureStorage::create2DTextureFromImage); - } else { - return TextureLoaderFunc(model::TextureStorage::createCubeTextureFromImage); + switch (_type) { + case CUBE_TEXTURE: { + return TextureLoaderFunc(model::TextureSource::createCubeTextureFromImage); + break; + } + case BUMP_TEXTURE: { + return TextureLoaderFunc(model::TextureSource::createNormalTextureFromBumpImage); + break; + } + case CUSTOM_TEXTURE: { + return _textureLoader; + break; + } + case DEFAULT_TEXTURE: + case NORMAL_TEXTURE: + case SPECULAR_TEXTURE: + case EMISSIVE_TEXTURE: + default: { + return TextureLoaderFunc(model::TextureSource::create2DTextureFromImage); + break; + } } } @@ -278,42 +292,6 @@ void listSupportedImageFormats() { }); } -/* -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 texture = _texture.toStrongRef(); if (texture.isNull()) { @@ -347,259 +325,25 @@ void ImageReader::run() { if (ntex) { theTexture = ntex->getTextureLoader()(image, _url.toString().toStdString()); } - -/* - int imageArea = image.width() * image.height(); - - gpu::Texture* theTexture = nullptr; - - if (ntex && (ntex->getType() == CUBE_TEXTURE)) { - qCDebug(gpunetwork) << "Cube map size:" << _url << 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++) { - for (int x = 0; x < image.width(); x++) { - QRgb rgb = image.pixel(x, y); - redTotal += qRed(rgb); - greenTotal += qGreen(rgb); - blueTotal += qBlue(rgb); - } - } - if (imageArea > 0) { - averageColor.setRgb(redTotal / imageArea, greenTotal / imageArea, blueTotal / imageArea); - } - } else { - if (image.format() != QImage::Format_ARGB32) { - image = image.convertToFormat(QImage::Format_ARGB32); - } - - // check for translucency/false transparency - // int opaquePixels = 0; - // int translucentPixels = 0; - // int redTotal = 0, greenTotal = 0, blueTotal = 0, alphaTotal = 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); - int alpha = qAlpha(rgb); - alphaTotal += alpha; - if (alpha == EIGHT_BIT_MAXIMUM) { - opaquePixels++; - } else if (alpha != 0) { - translucentPixels++; - } - } - } - if (opaquePixels == imageArea) { - qCDebug(gpunetwork) << "Image with alpha channel is completely opaque:" << _url; - image = image.convertToFormat(QImage::Format_RGB888); - } - - averageColor = QColor(redTotal / imageArea, - greenTotal / imageArea, blueTotal / imageArea, alphaTotal / imageArea); - - isTransparent = (translucentPixels >= imageArea / 2); - } - - gpu::Texture* theTexture = nullptr; - if ((image.width() > 0) && (image.height() > 0)) { - - // bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); - bool isLinearRGB = !(_type == CUBE_TEXTURE); //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); - - gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); - gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); - if (image.hasAlphaChannel()) { - formatGPU = gpu::Element(gpu::VEC4, gpu::UINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA)); - formatMip = gpu::Element(gpu::VEC4, gpu::UINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA)); - } - - if (_type == CUBE_TEXTURE) { - - const CubeLayout CUBEMAP_LAYOUTS[] = { - // Here is the expected layout for the faces in an image with the 1/6 aspect ratio: - // - // WIDTH - // <------> - // ^ +------+ - // | | | - // | | +X | - // | | | - // H +------+ - // E | | - // I | -X | - // G | | - // H +------+ - // T | | - // | | +Y | - // | | | - // | +------+ - // | | | - // | | -Y | - // | | | - // H +------+ - // E | | - // I | +Z | - // G | | - // H +------+ - // T | | - // | | -Z | - // | | | - // V +------+ - // - // FaceWidth = width = height / 6 - { 1, 6, - {0, 0, true, false}, - {0, 1, true, false}, - {0, 2, false, true}, - {0, 3, false, true}, - {0, 4, true, false}, - {0, 5, true, false} - }, - - // Here is the expected layout for the faces in an image with the 3/4 aspect ratio: - // - // <-----------WIDTH-----------> - // ^ +------+------+------+------+ - // | | | | | | - // | | | +Y | | | - // | | | | | | - // H +------+------+------+------+ - // E | | | | | - // I | -X | -Z | +X | +Z | - // G | | | | | - // H +------+------+------+------+ - // T | | | | | - // | | | -Y | | | - // | | | | | | - // V +------+------+------+------+ - // - // FaceWidth = width / 4 = height / 3 - { 4, 3, - {2, 1, true, false}, - {0, 1, true, false}, - {1, 0, false, true}, - {1, 2, false, true}, - {3, 1, true, false}, - {1, 1, true, false} - }, - - // Here is the expected layout for the faces in an image with the 4/3 aspect ratio: - // - // <-------WIDTH--------> - // ^ +------+------+------+ - // | | | | | - // | | | +Y | | - // | | | | | - // H +------+------+------+ - // E | | | | - // I | -X | -Z | +X | - // G | | | | - // H +------+------+------+ - // T | | | | - // | | | -Y | | - // | | | | | - // | +------+------+------+ - // | | | | | - // | | | +Z! | | <+Z is upside down! - // | | | | | - // V +------+------+------+ - // - // FaceWidth = width / 3 = height / 4 - { 3, 4, - {2, 1, true, false}, - {0, 1, true, false}, - {1, 0, false, true}, - {1, 2, false, true}, - {1, 3, false, true}, - {1, 1, true, false} - } - }; - const int NUM_CUBEMAP_LAYOUTS = sizeof(CUBEMAP_LAYOUTS) / sizeof(CubeLayout); - - // Find the layout of the cubemap in the 2D image - int foundLayout = -1; - for (int i = 0; i < NUM_CUBEMAP_LAYOUTS; i++) { - if ((image.height() * CUBEMAP_LAYOUTS[i]._widthRatio) == (image.width() * CUBEMAP_LAYOUTS[i]._heightRatio)) { - foundLayout = i; - break; - } - } - - std::vector faces; - // If found, go extract the faces as separate images - if (foundLayout >= 0) { - auto& layout = CUBEMAP_LAYOUTS[foundLayout]; - int faceWidth = image.width() / layout._widthRatio; - - faces.push_back(image.copy(QRect(layout._faceXPos._x * faceWidth, layout._faceXPos._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceXPos._horizontalMirror, layout._faceXPos._verticalMirror)); - faces.push_back(image.copy(QRect(layout._faceXNeg._x * faceWidth, layout._faceXNeg._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceXNeg._horizontalMirror, layout._faceXNeg._verticalMirror)); - faces.push_back(image.copy(QRect(layout._faceYPos._x * faceWidth, layout._faceYPos._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceYPos._horizontalMirror, layout._faceYPos._verticalMirror)); - faces.push_back(image.copy(QRect(layout._faceYNeg._x * faceWidth, layout._faceYNeg._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceYNeg._horizontalMirror, layout._faceYNeg._verticalMirror)); - faces.push_back(image.copy(QRect(layout._faceZPos._x * faceWidth, layout._faceZPos._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceZPos._horizontalMirror, layout._faceZPos._verticalMirror)); - faces.push_back(image.copy(QRect(layout._faceZNeg._x * faceWidth, layout._faceZNeg._y * faceWidth, faceWidth, faceWidth)).mirrored(layout._faceZNeg._horizontalMirror, layout._faceZNeg._verticalMirror)); - } else { - qCDebug(gpunetwork) << "Failed to find a known cube map layout from this image:" << _url; - return; - } - - // If the 6 faces have been created go on and define the true Texture - if (faces.size() == gpu::Texture::NUM_FACES_PER_TYPE[gpu::Texture::TEX_CUBE]) { - theTexture = gpu::Texture::createCube(formatGPU, faces[0].width(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR, gpu::Sampler::WRAP_CLAMP)); - theTexture->autoGenerateMips(-1); - int f = 0; - for (auto& face : faces) { - theTexture->assignStoredMipFace(0, formatMip, face.byteCount(), face.constBits(), f); - f++; - } - - // GEnerate irradiance while we are at it - theTexture->generateIrradiance(); - } - - } else { - theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); - theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); - theTexture->autoGenerateMips(-1); - } - } -*/ QMetaObject::invokeMethod(texture.data(), "setImage", Q_ARG(const QImage&, image), Q_ARG(void*, theTexture), - // Q_ARG(bool, isTransparent), - // Q_ARG(const QColor&, averageColor), Q_ARG(int, originalWidth), Q_ARG(int, originalHeight)); } -void NetworkTexture::setImage(const QImage& image, void* voidTexture,/* bool translucent, const QColor& averageColor, */ int originalWidth, +void NetworkTexture::setImage(const QImage& image, void* voidTexture, int originalWidth, int originalHeight) { - // _translucent = translucent; - // _averageColor = averageColor; _originalWidth = originalWidth; _originalHeight = originalHeight; gpu::Texture* texture = static_cast(voidTexture); + // Passing ownership - // _gpuTexture.reset(texture); - _textureStorage->resetTexture(texture); - auto gpuTexture = _textureStorage->getGPUTexture(); + _textureSource->resetTexture(texture); + auto gpuTexture = _textureSource->getGPUTexture(); if (gpuTexture) { _width = gpuTexture->getWidth(); diff --git a/libraries/gpu-networking/src/gpu-networking/TextureCache.h b/libraries/gpu-networking/src/gpu-networking/TextureCache.h index b33ede975b..335ea2d89c 100644 --- a/libraries/gpu-networking/src/gpu-networking/TextureCache.h +++ b/libraries/gpu-networking/src/gpu-networking/TextureCache.h @@ -29,7 +29,7 @@ class NetworkTexture; typedef QSharedPointer NetworkTexturePointer; -enum TextureType { DEFAULT_TEXTURE, NORMAL_TEXTURE, SPECULAR_TEXTURE, EMISSIVE_TEXTURE, SPLAT_TEXTURE, CUBE_TEXTURE }; +enum TextureType { DEFAULT_TEXTURE, NORMAL_TEXTURE, BUMP_TEXTURE, SPECULAR_TEXTURE, EMISSIVE_TEXTURE, CUBE_TEXTURE, CUSTOM_TEXTURE }; /// Stores cached textures, including render-to-texture targets. class TextureCache : public ResourceCache, public Dependency { @@ -97,9 +97,8 @@ public: Texture(); ~Texture(); - //const gpu::TexturePointer& getGPUTexture() const { return _gpuTexture; } - const gpu::TexturePointer getGPUTexture() const { return _textureStorage->getGPUTexture(); } - model::TextureStoragePointer _textureStorage; + const gpu::TexturePointer getGPUTexture() const { return _textureSource->getGPUTexture(); } + model::TextureSourcePointer _textureSource; protected: @@ -118,18 +117,10 @@ public: NetworkTexture(const QUrl& url, TextureType type, const QByteArray& content); NetworkTexture(const QUrl& url, const TextureLoaderFunc& textureLoader, const QByteArray& content); - /// Checks whether it "looks like" this texture is translucent - /// (majority of pixels neither fully opaque or fully transparent). - // bool isTranslucent() const { return _translucent; } - - /// Returns the lazily-computed average texture color. - // const QColor& getAverageColor() const { return _averageColor; } - int getOriginalWidth() const { return _originalWidth; } int getOriginalHeight() const { return _originalHeight; } int getWidth() const { return _width; } int getHeight() const { return _height; } - // TextureType getType() const { return _type; } TextureLoaderFunc getTextureLoader() const; @@ -139,8 +130,7 @@ protected: Q_INVOKABLE void loadContent(const QByteArray& content); // FIXME: This void* should be a gpu::Texture* but i cannot get it to work for now, moving on... - Q_INVOKABLE void setImage(const QImage& image, void* texture, /*bool translucent, const QColor& averageColor, */int originalWidth, - int originalHeight); + Q_INVOKABLE void setImage(const QImage& image, void* texture, int originalWidth, int originalHeight); virtual void imageLoaded(const QImage& image); @@ -148,8 +138,6 @@ protected: private: TextureLoaderFunc _textureLoader; - bool _translucent; - QColor _averageColor; int _originalWidth; int _originalHeight; int _width; diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 70e855bb15..f055f5e6fd 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -19,25 +19,25 @@ using namespace model; using namespace gpu; -// TextureStorage -TextureStorage::TextureStorage() +// TextureSource +TextureSource::TextureSource() {/* : Texture::Storage()//, // _gpuTexture(Texture::createFromStorage(this))*/ } -TextureStorage::~TextureStorage() { +TextureSource::~TextureSource() { } -void TextureStorage::reset(const QUrl& url, const TextureUsage& usage) { +void TextureSource::reset(const QUrl& url, const TextureUsage& usage) { _imageUrl = url; _usage = usage; } -void TextureStorage::resetTexture(gpu::Texture* texture) { +void TextureSource::resetTexture(gpu::Texture* texture) { _gpuTexture.reset(texture); } -bool TextureStorage::isDefined() const { +bool TextureSource::isDefined() const { if (_gpuTexture) { return _gpuTexture->isDefined(); } else { @@ -46,21 +46,21 @@ bool TextureStorage::isDefined() const { } -void TextureMap::setTextureStorage(TextureStoragePointer& texStorage) { - _textureStorage = texStorage; +void TextureMap::setTextureSource(TextureSourcePointer& texStorage) { + _textureSource = texStorage; } bool TextureMap::isDefined() const { - if (_textureStorage) { - return _textureStorage->isDefined(); + if (_textureSource) { + return _textureSource->isDefined(); } else { return false; } } gpu::TextureView TextureMap::getTextureView() const { - if (_textureStorage) { - return gpu::TextureView(_textureStorage->getGPUTexture(), 0); + if (_textureSource) { + return gpu::TextureView(_textureSource->getGPUTexture(), 0); } else { return gpu::TextureView(); } @@ -78,7 +78,7 @@ void TextureMap::setLightmapOffsetScale(float offset, float scale) { -gpu::Texture* TextureStorage::create2DTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { +gpu::Texture* TextureSource::create2DTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { QImage image = srcImage; int imageArea = image.width() * image.height(); @@ -145,7 +145,7 @@ gpu::Texture* TextureStorage::create2DTextureFromImage(const QImage& srcImage, c if ((image.width() > 0) && (image.height() > 0)) { // bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); - bool isLinearRGB = false; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); + bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); @@ -163,6 +163,94 @@ gpu::Texture* TextureStorage::create2DTextureFromImage(const QImage& srcImage, c return theTexture; } +int clampPixelCoordinate(int coordinate, int maxCoordinate) { + return coordinate - ((int)(coordinate < 0) * coordinate) + ((int)(coordinate > maxCoordinate) * (maxCoordinate - coordinate)); +} + +const int RGBA_MAX = 255; + +// transform -1 - 1 to 0 - 255 (from sobel value to rgb) +double mapComponent(double sobelValue) { + const double factor = RGBA_MAX / 2.0; + return (sobelValue + 1.0) * factor; +} + +gpu::Texture* TextureSource::createNormalTextureFromBumpImage(const QImage& srcImage, const std::string& srcImageName) { + QImage image = srcImage; + + // PR 5540 by AlessandroSigna + // integrated here as a specialized TextureLoader for bumpmaps + // The conversion is done using the Sobel Filter to calculate the derivatives from the grayscale image + const double pStrength = 2.0; + int width = image.width(); + int height = image.height(); + QImage result(width, height, image.format()); + + for (int i = 0; i < width; i++) { + const int iNextClamped = clampPixelCoordinate(i + 1, width - 1); + const int iPrevClamped = clampPixelCoordinate(i - 1, width - 1); + + for (int j = 0; j < height; j++) { + const int jNextClamped = clampPixelCoordinate(j + 1, height - 1); + const int jPrevClamped = clampPixelCoordinate(j - 1, height - 1); + + // surrounding pixels + const QRgb topLeft = image.pixel(iPrevClamped, jPrevClamped); + const QRgb top = image.pixel(iPrevClamped, j); + const QRgb topRight = image.pixel(iPrevClamped, jNextClamped); + const QRgb right = image.pixel(i, jNextClamped); + const QRgb bottomRight = image.pixel(iNextClamped, jNextClamped); + const QRgb bottom = image.pixel(iNextClamped, j); + const QRgb bottomLeft = image.pixel(iNextClamped, jPrevClamped); + const QRgb left = image.pixel(i, jPrevClamped); + + // take their gray intensities + // since it's a grayscale image, the value of each component RGB is the same + const double tl = qRed(topLeft); + const double t = qRed(top); + const double tr = qRed(topRight); + const double r = qRed(right); + const double br = qRed(bottomRight); + const double b = qRed(bottom); + const double bl = qRed(bottomLeft); + const double l = qRed(left); + + // apply the sobel filter + const double dX = (tr + pStrength * r + br) - (tl + pStrength * l + bl); + const double dY = (bl + pStrength * b + br) - (tl + pStrength * t + tr); + const double dZ = RGBA_MAX / pStrength; + + glm::vec3 v(dX, dY, dZ); + glm::normalize(v); + + // convert to rgb from the value obtained computing the filter + QRgb qRgbValue = qRgb(mapComponent(v.x), mapComponent(v.y), mapComponent(v.z)); + result.setPixel(i, j, qRgbValue); + } + } + + gpu::Texture* theTexture = nullptr; + if ((image.width() > 0) && (image.height() > 0)) { + + // bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); + bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE); + + gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); + gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::UINT8, (isLinearRGB ? gpu::RGB : gpu::SRGB)); + if (image.hasAlphaChannel()) { + formatGPU = gpu::Element(gpu::VEC4, gpu::UINT8, (isLinearRGB ? gpu::RGBA : gpu::SRGBA)); + formatMip = gpu::Element(gpu::VEC4, gpu::UINT8, (isLinearRGB ? gpu::BGRA : gpu::SBGRA)); + } + + + theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR))); + theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits()); + theTexture->autoGenerateMips(-1); + } + + return theTexture; +} + class CubeLayout { public: int _widthRatio = 1; @@ -197,7 +285,7 @@ public: _faceZNeg(fZN) {} }; -gpu::Texture* TextureStorage::createCubeTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { +gpu::Texture* TextureSource::createCubeTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { QImage image = srcImage; int imageArea = image.width() * image.height(); diff --git a/libraries/model/src/model/TextureMap.h b/libraries/model/src/model/TextureMap.h index cb5f510043..197172358b 100755 --- a/libraries/model/src/model/TextureMap.h +++ b/libraries/model/src/model/TextureMap.h @@ -32,13 +32,13 @@ public: int _environmentUsage = 0; }; -// TextureStorage is a specialized version of the gpu::Texture::Storage +// TextureSource is a specialized version of the gpu::Texture::Storage // It provides the mechanism to create a texture from a Url and the intended usage // that guides the internal format used -class TextureStorage { +class TextureSource { public: - TextureStorage(); - ~TextureStorage(); + TextureSource(); + ~TextureSource(); const QUrl& getUrl() const { return _imageUrl; } gpu::Texture::Type getType() const { return _usage._type; } @@ -51,6 +51,7 @@ public: bool isDefined() const; static gpu::Texture* create2DTextureFromImage(const QImage& image, const std::string& srcImageName); + static gpu::Texture* createNormalTextureFromBumpImage(const QImage& image, const std::string& srcImageName); static gpu::Texture* createCubeTextureFromImage(const QImage& image, const std::string& srcImageName); protected: @@ -58,7 +59,7 @@ protected: TextureUsage _usage; QUrl _imageUrl; }; -typedef std::shared_ptr< TextureStorage > TextureStoragePointer; +typedef std::shared_ptr< TextureSource > TextureSourcePointer; @@ -66,7 +67,7 @@ class TextureMap { public: TextureMap() {} - void setTextureStorage(TextureStoragePointer& texStorage); + void setTextureSource(TextureSourcePointer& texStorage); bool isDefined() const; gpu::TextureView getTextureView() const; @@ -78,7 +79,7 @@ public: const glm::vec2& getLightmapOffsetScale() const { return _lightmapOffsetScale; } protected: - TextureStoragePointer _textureStorage; + TextureSourcePointer _textureSource; Transform _texcoordTransform; glm::vec2 _lightmapOffsetScale{ 0.0f, 1.0f }; @@ -87,5 +88,5 @@ typedef std::shared_ptr< TextureMap > TextureMapPointer; }; -#endif // hifi_model_TextureStorage_h +#endif // hifi_model_TextureMap_h diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 40857dedf2..77f9b2cece 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -2072,17 +2072,17 @@ static NetworkMaterial* buildNetworkMaterial(const FBXMaterial& material, const networkMaterial->diffuseTextureName = material.diffuseTexture.name; auto diffuseMap = model::TextureMapPointer(new model::TextureMap()); - diffuseMap->setTextureStorage(networkMaterial->diffuseTexture->_textureStorage); + diffuseMap->setTextureSource(networkMaterial->diffuseTexture->_textureSource); diffuseMap->setTextureTransform(material.diffuseTexture.transform); material._material->setTextureMap(model::MaterialKey::DIFFUSE_MAP, diffuseMap); } if (!material.normalTexture.filename.isEmpty()) { - networkMaterial->normalTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.normalTexture.filename)), NORMAL_TEXTURE, material.normalTexture.content); + networkMaterial->normalTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.normalTexture.filename)), (material.normalTexture.isBumpmap ? BUMP_TEXTURE : NORMAL_TEXTURE), material.normalTexture.content); networkMaterial->normalTextureName = material.normalTexture.name; auto normalMap = model::TextureMapPointer(new model::TextureMap()); - normalMap->setTextureStorage(networkMaterial->normalTexture->_textureStorage); + normalMap->setTextureSource(networkMaterial->normalTexture->_textureSource); material._material->setTextureMap(model::MaterialKey::NORMAL_MAP, normalMap); } @@ -2091,7 +2091,7 @@ static NetworkMaterial* buildNetworkMaterial(const FBXMaterial& material, const networkMaterial->specularTextureName = material.specularTexture.name; auto glossMap = model::TextureMapPointer(new model::TextureMap()); - glossMap->setTextureStorage(networkMaterial->specularTexture->_textureStorage); + glossMap->setTextureSource(networkMaterial->specularTexture->_textureSource); material._material->setTextureMap(model::MaterialKey::GLOSS_MAP, glossMap); } @@ -2102,7 +2102,7 @@ static NetworkMaterial* buildNetworkMaterial(const FBXMaterial& material, const checkForTexcoordLightmap = true; auto lightmapMap = model::TextureMapPointer(new model::TextureMap()); - lightmapMap->setTextureStorage(networkMaterial->emissiveTexture->_textureStorage); + lightmapMap->setTextureSource(networkMaterial->emissiveTexture->_textureSource); lightmapMap->setTextureTransform(material.emissiveTexture.transform); lightmapMap->setLightmapOffsetScale(material.emissiveParams.x, material.emissiveParams.y);