diff --git a/libraries/animation/CMakeLists.txt b/libraries/animation/CMakeLists.txt index 30addadcaa..1ab54ed342 100644 --- a/libraries/animation/CMakeLists.txt +++ b/libraries/animation/CMakeLists.txt @@ -4,5 +4,6 @@ link_hifi_libraries(shared graphics fbx) include_hifi_library_headers(networking) include_hifi_library_headers(gpu) include_hifi_library_headers(hfm) +include_hifi_library_headers(image) target_nsight() diff --git a/libraries/baking/src/TextureBaker.cpp b/libraries/baking/src/TextureBaker.cpp index 2516323c37..6407ce1846 100644 --- a/libraries/baking/src/TextureBaker.cpp +++ b/libraries/baking/src/TextureBaker.cpp @@ -154,7 +154,7 @@ void TextureBaker::processTexture() { gpu::BackendTarget::GLES32 }}; for (auto target : BACKEND_TARGETS) { - auto processedTexture = image::processImage(buffer, _textureURL.toString().toStdString(), + auto processedTexture = image::processImage(buffer, _textureURL.toString().toStdString(), image::ColorChannel::NONE, ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, true, target, _abortProcessing); if (!processedTexture) { @@ -197,7 +197,7 @@ void TextureBaker::processTexture() { // Uncompressed KTX if (_textureType == image::TextureUsage::Type::CUBE_TEXTURE) { buffer->reset(); - auto processedTexture = image::processImage(std::move(buffer), _textureURL.toString().toStdString(), + auto processedTexture = image::processImage(std::move(buffer), _textureURL.toString().toStdString(), image::ColorChannel::NONE, ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, false, gpu::BackendTarget::GL45, _abortProcessing); if (!processedTexture) { handleError("Could not process texture " + _textureURL.toString()); diff --git a/libraries/fbx/src/GLTFSerializer.cpp b/libraries/fbx/src/GLTFSerializer.cpp index d6de3d4b25..82a4361723 100755 --- a/libraries/fbx/src/GLTFSerializer.cpp +++ b/libraries/fbx/src/GLTFSerializer.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include "FBXSerializer.h" @@ -1146,8 +1147,10 @@ void GLTFSerializer::setHFMMaterial(HFMMaterial& fbxmat, const GLTFMaterial& mat } if (material.pbrMetallicRoughness.defined["metallicRoughnessTexture"]) { fbxmat.roughnessTexture = getHFMTexture(_file.textures[material.pbrMetallicRoughness.metallicRoughnessTexture]); + fbxmat.roughnessTexture.sourceChannel = image::ColorChannel::GREEN; fbxmat.useRoughnessMap = true; fbxmat.metallicTexture = getHFMTexture(_file.textures[material.pbrMetallicRoughness.metallicRoughnessTexture]); + fbxmat.metallicTexture.sourceChannel = image::ColorChannel::BLUE; fbxmat.useMetallicMap = true; } if (material.pbrMetallicRoughness.defined["roughnessFactor"]) { diff --git a/libraries/hfm/CMakeLists.txt b/libraries/hfm/CMakeLists.txt index 553fd935d9..be3d866b70 100644 --- a/libraries/hfm/CMakeLists.txt +++ b/libraries/hfm/CMakeLists.txt @@ -5,3 +5,4 @@ link_hifi_libraries(shared) include_hifi_library_headers(gpu) include_hifi_library_headers(graphics) +include_hifi_library_headers(image) diff --git a/libraries/hfm/src/hfm/HFM.h b/libraries/hfm/src/hfm/HFM.h index cccfaa3f7d..577ca6f413 100644 --- a/libraries/hfm/src/hfm/HFM.h +++ b/libraries/hfm/src/hfm/HFM.h @@ -25,6 +25,8 @@ #include #include +#include + #if defined(Q_OS_ANDROID) #define HFM_PACK_NORMALS 0 #else @@ -123,6 +125,7 @@ public: QString name; QByteArray filename; QByteArray content; + image::ColorChannel sourceChannel { image::ColorChannel::NONE }; Transform transform; int maxNumPixels { MAX_NUM_PIXELS_FOR_FBX_TEXTURE }; diff --git a/libraries/image/src/image/ColorChannel.h b/libraries/image/src/image/ColorChannel.h new file mode 100644 index 0000000000..e1d107018b --- /dev/null +++ b/libraries/image/src/image/ColorChannel.h @@ -0,0 +1,26 @@ +// +// ColorChannel.h +// libraries/image/src/image +// +// Created by Sabrina Shanman on 2019/02/12. +// Copyright 2019 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 +// + +#ifndef hifi_image_ColorChannel_h +#define hifi_image_ColorChannel_h + +namespace image { + enum class ColorChannel { + NONE, + RED, + GREEN, + BLUE, + ALPHA, + COUNT + }; +}; + +#endif // hifi_image_ColorChannel_h diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index ac2813667f..a2161caec9 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -221,7 +222,45 @@ QImage processRawImageData(QIODevice& content, const std::string& filename) { return QImage(); } -gpu::TexturePointer processImage(std::shared_ptr content, const std::string& filename, +void mapToRedChannel(QImage& image, ColorChannel sourceChannel) { + // Change format of image so we know exactly how to process it + if (image.format() != QImage::Format_ARGB32) { + image = image.convertToFormat(QImage::Format_ARGB32); + } + + for (int i = 0; i < image.height(); i++) { + QRgb* pixel = reinterpret_cast(image.scanLine(i)); + // Past end pointer + QRgb* lineEnd = pixel + image.width(); + + // Transfer channel data from source to target + for (; pixel < lineEnd; pixel++) { + int colorValue; + switch (sourceChannel) { + case ColorChannel::RED: + colorValue = qRed(*pixel); + break; + case ColorChannel::GREEN: + colorValue = qGreen(*pixel); + break; + case ColorChannel::BLUE: + colorValue = qBlue(*pixel); + break; + case ColorChannel::ALPHA: + colorValue = qAlpha(*pixel); + break; + default: + colorValue = qRed(*pixel); + break; + } + + // Dump the color in the red channel, ignore the rest + *pixel = qRgba(colorValue, 0, 0, 255); + } + } +} + +gpu::TexturePointer processImage(std::shared_ptr content, const std::string& filename, ColorChannel sourceChannel, int maxNumPixels, TextureUsage::Type textureType, bool compress, BackendTarget target, const std::atomic& abortProcessing) { @@ -252,6 +291,11 @@ gpu::TexturePointer processImage(std::shared_ptr content, const std:: QSize(originalWidth, originalHeight) << " to " << QSize(imageWidth, imageHeight) << ")"; } + + // Re-map to image with single red channel texture if requested + if (sourceChannel != ColorChannel::NONE) { + mapToRedChannel(image, sourceChannel); + } auto loader = TextureUsage::getTextureLoaderForType(textureType); auto texture = loader(std::move(image), filename, compress, target, abortProcessing); diff --git a/libraries/image/src/image/Image.h b/libraries/image/src/image/Image.h index ae72a183b3..40c31eeeff 100644 --- a/libraries/image/src/image/Image.h +++ b/libraries/image/src/image/Image.h @@ -16,6 +16,8 @@ #include +#include "ColorChannel.h" + class QByteArray; class QImage; @@ -81,7 +83,7 @@ gpu::TexturePointer processCubeTextureColorFromImage(QImage&& srcImage, const st const QStringList getSupportedFormats(); -gpu::TexturePointer processImage(std::shared_ptr content, const std::string& url, +gpu::TexturePointer processImage(std::shared_ptr content, const std::string& url, ColorChannel sourceChannel, int maxNumPixels, TextureUsage::Type textureType, bool compress, gpu::BackendTarget target, const std::atomic& abortProcessing = false); diff --git a/libraries/model-baker/CMakeLists.txt b/libraries/model-baker/CMakeLists.txt index 6fa7c1815a..aabd6eba3a 100644 --- a/libraries/model-baker/CMakeLists.txt +++ b/libraries/model-baker/CMakeLists.txt @@ -2,3 +2,5 @@ set(TARGET_NAME model-baker) setup_hifi_library() link_hifi_libraries(shared task gpu graphics hfm) + +include_hifi_library_headers(image) diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index a4eba0c7a9..a4fae67958 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -599,7 +599,7 @@ graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl } const auto url = getTextureUrl(baseUrl, hfmTexture); - const auto texture = DependencyManager::get()->getTexture(url, type, hfmTexture.content, hfmTexture.maxNumPixels); + const auto texture = DependencyManager::get()->getTexture(url, type, hfmTexture.content, hfmTexture.maxNumPixels, hfmTexture.sourceChannel); _textures[channel] = Texture { hfmTexture.name, texture }; auto map = std::make_shared(); diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index d4cf7e6ce9..a78812b2f9 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -192,6 +192,7 @@ public: image::TextureUsage::Type type; const QByteArray& content; int maxNumPixels; + image::ColorChannel sourceChannel; }; namespace std { @@ -206,19 +207,19 @@ namespace std { struct hash { size_t operator()(const TextureExtra& a) const { size_t result = 0; - hash_combine(result, (int)a.type, a.content, a.maxNumPixels); + hash_combine(result, (int)a.type, a.content, a.maxNumPixels, (int)a.sourceChannel); return result; } }; } -ScriptableResource* TextureCache::prefetch(const QUrl& url, int type, int maxNumPixels) { +ScriptableResource* TextureCache::prefetch(const QUrl& url, int type, int maxNumPixels, image::ColorChannel sourceChannel) { auto byteArray = QByteArray(); - TextureExtra extra = { (image::TextureUsage::Type)type, byteArray, maxNumPixels }; + TextureExtra extra = { (image::TextureUsage::Type)type, byteArray, maxNumPixels, sourceChannel }; return ResourceCache::prefetch(url, &extra, std::hash()(extra)); } -NetworkTexturePointer TextureCache::getTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels) { +NetworkTexturePointer TextureCache::getTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels, image::ColorChannel sourceChannel) { if (url.scheme() == RESOURCE_SCHEME) { return getResourceTexture(url); } @@ -228,7 +229,7 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, image::TextureUs query.addQueryItem("skybox", ""); modifiedUrl.setQuery(query.toString()); } - TextureExtra extra = { type, content, maxNumPixels }; + TextureExtra extra = { type, content, maxNumPixels, sourceChannel }; return ResourceCache::getResource(modifiedUrl, QUrl(), &extra, std::hash()(extra)).staticCast(); } @@ -346,6 +347,7 @@ NetworkTexture::NetworkTexture(const QUrl& url, bool resourceTexture) : NetworkTexture::NetworkTexture(const NetworkTexture& other) : Resource(other), _type(other._type), + _sourceChannel(other._sourceChannel), _currentlyLoadingResourceType(other._currentlyLoadingResourceType), _originalWidth(other._originalWidth), _originalHeight(other._originalHeight), @@ -353,6 +355,11 @@ NetworkTexture::NetworkTexture(const NetworkTexture& other) : _height(other._height), _maxNumPixels(other._maxNumPixels) { + if (_width == 0 || _height == 0 || + other._currentlyLoadingResourceType == ResourceType::META || + (other._currentlyLoadingResourceType == ResourceType::KTX && other._ktxResourceState != KTXResourceState::WAITING_FOR_MIP_REQUEST)) { + _startedLoading = false; + } } static bool isLocalUrl(const QUrl& url) { @@ -364,6 +371,7 @@ void NetworkTexture::setExtra(void* extra) { const TextureExtra* textureExtra = static_cast(extra); _type = textureExtra ? textureExtra->type : image::TextureUsage::DEFAULT_TEXTURE; _maxNumPixels = textureExtra ? textureExtra->maxNumPixels : ABSOLUTE_MAX_TEXTURE_NUM_PIXELS; + _sourceChannel = textureExtra ? textureExtra->sourceChannel : image::ColorChannel::NONE; _textureSource = std::make_shared(_url, (int)_type); _lowestRequestedMipLevel = 0; @@ -425,7 +433,8 @@ gpu::TexturePointer NetworkTexture::getFallbackTexture() const { class ImageReader : public QRunnable { public: ImageReader(const QWeakPointer& resource, const QUrl& url, - const QByteArray& data, size_t extraHash, int maxNumPixels); + const QByteArray& data, size_t extraHash, int maxNumPixels, + image::ColorChannel sourceChannel); void run() override final; void read(); @@ -437,6 +446,7 @@ private: QByteArray _content; size_t _extraHash; int _maxNumPixels; + image::ColorChannel _sourceChannel; }; NetworkTexture::~NetworkTexture() { @@ -1069,7 +1079,7 @@ void NetworkTexture::loadTextureContent(const QByteArray& content) { return; } - QThreadPool::globalInstance()->start(new ImageReader(_self, _url, content, _extraHash, _maxNumPixels)); + QThreadPool::globalInstance()->start(new ImageReader(_self, _url, content, _extraHash, _maxNumPixels, _sourceChannel)); } void NetworkTexture::refresh() { @@ -1094,12 +1104,13 @@ void NetworkTexture::refresh() { Resource::refresh(); } -ImageReader::ImageReader(const QWeakPointer& resource, const QUrl& url, const QByteArray& data, size_t extraHash, int maxNumPixels) : +ImageReader::ImageReader(const QWeakPointer& resource, const QUrl& url, const QByteArray& data, size_t extraHash, int maxNumPixels, image::ColorChannel sourceChannel) : _resource(resource), _url(url), _content(data), _extraHash(extraHash), - _maxNumPixels(maxNumPixels) + _maxNumPixels(maxNumPixels), + _sourceChannel(sourceChannel) { DependencyManager::get()->incrementStat("PendingProcessing"); listSupportedImageFormats(); @@ -1207,7 +1218,7 @@ void ImageReader::read() { constexpr bool shouldCompress = false; #endif auto target = getBackendTarget(); - texture = image::processImage(std::move(buffer), _url.toString().toStdString(), _maxNumPixels, networkTexture->getTextureType(), shouldCompress, target); + texture = image::processImage(std::move(buffer), _url.toString().toStdString(), _sourceChannel, _maxNumPixels, networkTexture->getTextureType(), shouldCompress, target); if (!texture) { QMetaObject::invokeMethod(resource.data(), "setImage", diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index cdedc64ea5..acca916acc 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -96,6 +97,7 @@ private: friend class ImageReader; image::TextureUsage::Type _type; + image::ColorChannel _sourceChannel; enum class ResourceType { META, @@ -178,7 +180,8 @@ public: /// Loads a texture from the specified URL. NetworkTexturePointer getTexture(const QUrl& url, image::TextureUsage::Type type = image::TextureUsage::DEFAULT_TEXTURE, - const QByteArray& content = QByteArray(), int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS); + const QByteArray& content = QByteArray(), int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, + image::ColorChannel sourceChannel = image::ColorChannel::NONE); gpu::TexturePointer getTextureByHash(const std::string& hash); gpu::TexturePointer cacheTextureByHash(const std::string& hash, const gpu::TexturePointer& texture); @@ -201,7 +204,7 @@ signals: protected: // Overload ResourceCache::prefetch to allow specifying texture type for loads - Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, int type, int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS); + Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, int type, int maxNumPixels = ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, image::ColorChannel sourceChannel = image::ColorChannel::NONE); virtual QSharedPointer createResource(const QUrl& url) override; QSharedPointer createResourceCopy(const QSharedPointer& resource) override; diff --git a/libraries/procedural/CMakeLists.txt b/libraries/procedural/CMakeLists.txt index 6d6610a323..f3c3be687a 100644 --- a/libraries/procedural/CMakeLists.txt +++ b/libraries/procedural/CMakeLists.txt @@ -1,4 +1,3 @@ set(TARGET_NAME procedural) setup_hifi_library() link_hifi_libraries(shared gpu shaders networking graphics model-networking ktx image) - diff --git a/tools/skeleton-dump/CMakeLists.txt b/tools/skeleton-dump/CMakeLists.txt index baec1d163b..7d4248d171 100644 --- a/tools/skeleton-dump/CMakeLists.txt +++ b/tools/skeleton-dump/CMakeLists.txt @@ -2,3 +2,5 @@ set(TARGET_NAME skeleton-dump) setup_hifi_project(Core) setup_memory_debugger() link_hifi_libraries(shared fbx hfm graphics gpu gl animation) + +include_hifi_library_headers(image) diff --git a/tools/vhacd-util/CMakeLists.txt b/tools/vhacd-util/CMakeLists.txt index aa6642c610..90cfdf878a 100644 --- a/tools/vhacd-util/CMakeLists.txt +++ b/tools/vhacd-util/CMakeLists.txt @@ -2,6 +2,8 @@ set(TARGET_NAME vhacd-util) setup_hifi_project(Core) link_hifi_libraries(shared fbx hfm graphics gpu gl) +include_hifi_library_headers(image) + add_dependency_external_projects(vhacd) find_package(VHACD REQUIRED)