From 97d8d27cd0f6a20829a3f0cc94c3441a81642cdf Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Sat, 28 Sep 2024 12:54:47 -0700 Subject: [PATCH] ability to specify texture samplers --- .../gpu-gl-common/src/gpu/gl/GLBackend.h | 7 +- .../src/gpu/gl/GLBackendPipeline.cpp | 30 +- .../gpu-gl-common/src/gpu/gl/GLTexture.h | 4 +- libraries/gpu-gl/src/gpu/gl41/GL41Backend.h | 6 +- .../src/gpu/gl41/GL41BackendTexture.cpp | 23 +- libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 8 +- .../src/gpu/gl45/GL45BackendTexture.cpp | 16 +- .../gpu/gl45/GL45BackendVariableTexture.cpp | 16 +- .../src/gpu/gles/GLESBackendTexture.cpp | 23 +- libraries/gpu/src/gpu/Batch.cpp | 18 + libraries/gpu/src/gpu/Batch.h | 2 + libraries/gpu/src/gpu/FrameIOKeys.h | 1 + libraries/gpu/src/gpu/FrameReader.cpp | 17 + libraries/gpu/src/gpu/FrameWriter.cpp | 18 + libraries/gpu/src/gpu/Texture.cpp | 121 ++++++ libraries/gpu/src/gpu/Texture.h | 5 + libraries/graphics/src/graphics/Material.cpp | 21 + libraries/graphics/src/graphics/Material.h | 15 +- libraries/hfm/src/hfm/HFM.h | 5 +- .../model-serializers/src/GLTFSerializer.cpp | 63 +++ .../procedural/src/procedural/Procedural.cpp | 50 ++- .../procedural/src/procedural/Procedural.h | 3 +- .../procedural/ProceduralMaterialCache.cpp | 367 +++++++++--------- .../src/procedural/ProceduralMaterialCache.h | 3 - .../render-utils/src/RenderPipelines.cpp | 19 + 25 files changed, 587 insertions(+), 274 deletions(-) diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h index ec85d0fb90..e8c90722a8 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.h @@ -347,7 +347,7 @@ protected: void setupStereoSide(int side); #endif - virtual void setResourceTexture(unsigned int slot, const TexturePointer& resourceTexture); + virtual void setResourceTexture(unsigned int slot, const TexturePointer& resourceTexture, const Sampler& sampler); virtual void setFramebuffer(const FramebufferPointer& framebuffer); virtual void initInput() final; virtual void killInput() final; @@ -530,7 +530,7 @@ protected: // Helper function that provides common code used by do_setResourceTexture and // do_setResourceTextureTable (in non-bindless mode) - void bindResourceTexture(uint32_t slot, const TexturePointer& texture); + void bindResourceTexture(uint32_t slot, const TexturePointer& texture, const Sampler& sampler); // update resource cache and do the gl unbind call with the current gpu::Texture cached at slot s void releaseResourceTexture(uint32_t slot); @@ -539,7 +539,8 @@ protected: struct ResourceStageState { struct TextureState { - TextureReference _texture{}; + TextureReference _texture {}; + Sampler _sampler {}; GLenum _target; }; std::array _buffers{}; diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackendPipeline.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendPipeline.cpp index 5c2219e720..199d029355 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackendPipeline.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendPipeline.cpp @@ -245,15 +245,19 @@ void GLBackend::do_setResourceTexture(const Batch& batch, size_t paramOffset) { } const auto& resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); - bindResourceTexture(slot, resourceTexture); + Sampler sampler; + if (resourceTexture) { + sampler = batch._samplers.get(batch._params[paramOffset + 2]._uint); + } + bindResourceTexture(slot, resourceTexture, sampler); } -void GLBackend::bindResourceTexture(uint32_t slot, const TexturePointer& resourceTexture) { +void GLBackend::bindResourceTexture(uint32_t slot, const TexturePointer& resourceTexture, const Sampler& sampler) { if (!resourceTexture) { releaseResourceTexture(slot); return; } - setResourceTexture(slot, resourceTexture); + setResourceTexture(slot, resourceTexture, sampler); } void GLBackend::do_setResourceFramebufferSwapChainTexture(const Batch& batch, size_t paramOffset) { @@ -276,13 +280,14 @@ void GLBackend::do_setResourceFramebufferSwapChainTexture(const Batch& batch, si auto renderBufferSlot = batch._params[paramOffset + 3]._uint; const auto& resourceFramebuffer = swapChain->get(index); const auto& resourceTexture = resourceFramebuffer->getRenderBuffer(renderBufferSlot); - setResourceTexture(slot, resourceTexture); + Sampler sampler = batch._samplers.get(batch._params[paramOffset + 4]._uint); + setResourceTexture(slot, resourceTexture, sampler); } -void GLBackend::setResourceTexture(unsigned int slot, const TexturePointer& resourceTexture) { +void GLBackend::setResourceTexture(unsigned int slot, const TexturePointer& resourceTexture, const Sampler& sampler) { auto& textureState = _resource._textures[slot]; // check cache before thinking - if (compare(textureState._texture, resourceTexture)) { + if (compare(textureState._texture, resourceTexture) && textureState._sampler == sampler) { return; } @@ -295,8 +300,10 @@ void GLBackend::setResourceTexture(unsigned int slot, const TexturePointer& reso assign(textureState._texture, resourceTexture); GLuint to = object->_texture; textureState._target = object->_target; + textureState._sampler = sampler; glActiveTexture(GL_TEXTURE0 + slot); glBindTexture(textureState._target, to); + object->syncSampler(sampler); (void)CHECK_GL_ERROR(); _stats._RSAmountTextureMemoryBounded += (uint64_t)object->size(); @@ -312,10 +319,19 @@ void GLBackend::do_setResourceTextureTable(const Batch& batch, size_t paramOffse return; } + GLuint startSlot = batch._params[paramOffset + 1]._uint; + size_t samplerIndex = 0; const auto& textureTable = *textureTablePointer; const auto& textures = textureTable.getTextures(); for (GLuint slot = 0; slot < textures.size(); ++slot) { - bindResourceTexture(slot, textures[slot]); + const auto& texture = textures[slot]; + + Sampler sampler; + if (texture) { + sampler = batch._samplers.get(batch._params[paramOffset + 2 + (samplerIndex++)]._uint); + } + + bindResourceTexture(slot + startSlot, texture, sampler); } } diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLTexture.h b/libraries/gpu-gl-common/src/gpu/gl/GLTexture.h index d3e8f386de..c3f4b7815b 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gl-common/src/gpu/gl/GLTexture.h @@ -168,7 +168,7 @@ public: protected: virtual void generateMips() const = 0; - virtual void syncSampler() const = 0; + virtual void syncSampler(const Sampler& sampler) const = 0; virtual void copyTextureMipsInGPUMem(GLuint srcId, GLuint destId, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) {} // Only relevant for Variable Allocation textures @@ -183,7 +183,7 @@ public: protected: GLExternalTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id); void generateMips() const override {} - void syncSampler() const override {} + void syncSampler(const Sampler& sampler) const override {} Size copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override { return 0;} Size size() const override { return 0; } diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h index 967d94a687..fb1d1b584e 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h @@ -58,7 +58,7 @@ public: GL41Texture(const std::weak_ptr& backend, const Texture& texture); void generateMips() const override; Size copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override; - void syncSampler() const override; + void syncSampler(const Sampler& sampler) const override; void withPreservedTexture(std::function f) const; }; @@ -78,7 +78,7 @@ public: protected: Size size() const override { return _size; } void allocateStorage() const; - void syncSampler() const override; + void syncSampler(const Sampler& sampler) const override; const Size _size { 0 }; }; @@ -109,7 +109,7 @@ public: ~GL41VariableAllocationTexture(); void allocateStorage(uint16 allocatedMip); - void syncSampler() const override; + void syncSampler(const Sampler& sampler) const override; size_t promote() override; size_t demote() override; void populateTransferQueue(TransferQueue& pendingTransfers) override; diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index f47211555a..c39280fef9 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -174,9 +174,7 @@ Size GL41Texture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const return amountCopied; } -void GL41Texture::syncSampler() const { - const Sampler& sampler = _gpuObject.getSampler(); - +void GL41Texture::syncSampler(const Sampler& sampler) const { const auto& fm = FILTER_MODES[sampler.getFilter()]; glTexParameteri(_target, GL_TEXTURE_MIN_FILTER, fm.minFilter); glTexParameteri(_target, GL_TEXTURE_MAG_FILTER, fm.magFilter); @@ -205,7 +203,7 @@ using GL41FixedAllocationTexture = GL41Backend::GL41FixedAllocationTexture; GL41FixedAllocationTexture::GL41FixedAllocationTexture(const std::weak_ptr& backend, const Texture& texture) : GL41Texture(backend, texture), _size(texture.evalTotalSize()) { withPreservedTexture([&] { allocateStorage(); - syncSampler(); + syncSampler(texture.getSampler()); }); } @@ -245,9 +243,8 @@ void GL41FixedAllocationTexture::allocateStorage() const { glTexParameteri(_target, GL_TEXTURE_MAX_LEVEL, numMips - 1); } -void GL41FixedAllocationTexture::syncSampler() const { - Parent::syncSampler(); - const Sampler& sampler = _gpuObject.getSampler(); +void GL41FixedAllocationTexture::syncSampler(const Sampler& sampler) const { + Parent::syncSampler(sampler); glTexParameterf(_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); glTexParameterf(_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.0f : sampler.getMaxMip())); } @@ -317,7 +314,7 @@ GL41VariableAllocationTexture::GL41VariableAllocationTexture(const std::weak_ptr allocateStorage(allocatedMip); copyMipsFromTexture(); - syncSampler(); + syncSampler(texture.getSampler()); } GL41VariableAllocationTexture::~GL41VariableAllocationTexture() { @@ -365,9 +362,9 @@ Size GL41VariableAllocationTexture::copyMipFaceLinesFromTexture(uint16_t mip, ui return amountCopied; } -void GL41VariableAllocationTexture::syncSampler() const { +void GL41VariableAllocationTexture::syncSampler(const Sampler& sampler) const { withPreservedTexture([&] { - Parent::syncSampler(); + Parent::syncSampler(sampler); glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, _populatedMip - _allocatedMip); }); } @@ -530,7 +527,7 @@ size_t GL41VariableAllocationTexture::promote() { glDeleteTextures(1, &oldId); // Update sampler - syncSampler(); + syncSampler(_gpuObject.getSampler()); // update the memory usage Backend::textureResourceGPUMemSize.update(oldSize, 0); @@ -559,7 +556,7 @@ size_t GL41VariableAllocationTexture::demote() { glDeleteTextures(1, &oldId); // Update sampler - syncSampler(); + syncSampler(_gpuObject.getSampler()); // update the memory usage Backend::textureResourceGPUMemSize.update(oldSize, 0); @@ -624,7 +621,7 @@ void GL41VariableAllocationTexture::populateTransferQueue(TransferQueue& pending _populatedMip = sourceMip; incrementPopulatedSize(_gpuObject.evalMipSize(sourceMip)); sanityCheck(); - syncSampler(); + syncSampler(_gpuObject.getSampler()); })); } while (sourceMip != _allocatedMip); } diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index e0b921237e..ff9420d814 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -63,7 +63,7 @@ public: GL45Texture(const std::weak_ptr& backend, const Texture& texture); void generateMips() const override; Size copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override; - void syncSampler() const override; + void syncSampler(const Sampler& sampler) const override; #if GPU_BINDLESS_TEXTURES bool isBindless() const { @@ -99,7 +99,7 @@ public: static Sampler getInvalidSampler(); // This stores the texture handle (64 bits) in xy, the min mip available in z, and the sampler ID in w - mutable Sampler _cachedSampler{ getInvalidSampler() }; + mutable Sampler _cachedSampler { getInvalidSampler() }; }; #if GPU_BINDLESS_TEXTURES @@ -137,7 +137,7 @@ public: Size size() const override { return _size; } void allocateStorage() const; - void syncSampler() const override; + void syncSampler(const Sampler& sampler) const override; const Size _size{ 0 }; }; @@ -189,7 +189,7 @@ public: protected: GL45ResourceTexture(const std::weak_ptr& backend, const Texture& texture); - void syncSampler() const override; + void syncSampler(const Sampler& sampler) const override; size_t promote() override; size_t demote() override; void populateTransferQueue(TransferQueue& pendingTransfers) override; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 3d30ebf03e..aa8fe4dd37 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -114,7 +114,6 @@ GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) { Q_UNREACHABLE(); } } else { - if (texture.getUsageType() == TextureUsageType::RESOURCE) { auto varTex = static_cast (object); @@ -285,11 +284,10 @@ const GL45Texture::Bindless& GL45Texture::getBindless() const { #endif -void GL45Texture::syncSampler() const { - const Sampler& sampler = _gpuObject.getSampler(); +void GL45Texture::syncSampler(const Sampler& sampler) const { if (_cachedSampler == sampler) { return; - } + } _cachedSampler = sampler; @@ -328,7 +326,7 @@ using GL45FixedAllocationTexture = GL45Backend::GL45FixedAllocationTexture; GL45FixedAllocationTexture::GL45FixedAllocationTexture(const std::weak_ptr& backend, const Texture& texture) : GL45Texture(backend, texture), _size(texture.evalTotalSize()) { allocateStorage(); - syncSampler(); + syncSampler(texture.getSampler()); } GL45FixedAllocationTexture::~GL45FixedAllocationTexture() { @@ -361,9 +359,8 @@ void GL45FixedAllocationTexture::allocateStorage() const { glTextureParameteri(_id, GL_TEXTURE_MAX_LEVEL, mips - 1); } -void GL45FixedAllocationTexture::syncSampler() const { - Parent::syncSampler(); - const Sampler& sampler = _gpuObject.getSampler(); +void GL45FixedAllocationTexture::syncSampler(const Sampler& sampler) const { + Parent::syncSampler(sampler); glTextureParameterf(_id, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip())); } @@ -401,7 +398,7 @@ GL45StrictResourceTexture::GL45StrictResourceTexture(const std::weak_ptr_id); } + // FIXME: handle samplers } #endif diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp index f53eff637d..172cff186e 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendVariableTexture.cpp @@ -97,7 +97,7 @@ GL45ResourceTexture::GL45ResourceTexture(const std::weak_ptr& backend allocateStorage(allocatedMip); copyMipsFromTexture(); - syncSampler(); + syncSampler(texture.getSampler()); } void GL45ResourceTexture::allocateStorage(uint16 allocatedMip) { @@ -129,8 +129,8 @@ Size GL45ResourceTexture::copyMipsFromTexture() { return amount; } -void GL45ResourceTexture::syncSampler() const { - Parent::syncSampler(); +void GL45ResourceTexture::syncSampler(const Sampler& sampler) const { + Parent::syncSampler(sampler); #if GPU_BINDLESS_TEXTURES if (!isBindless()) { glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, _populatedMip - _allocatedMip); @@ -178,7 +178,7 @@ size_t GL45ResourceTexture::promote() { // Update sampler _cachedSampler = getInvalidSampler(); - syncSampler(); + syncSampler(_gpuObject.getSampler()); // update the memory usage Backend::textureResourceGPUMemSize.update(oldSize, 0); @@ -220,7 +220,7 @@ size_t GL45ResourceTexture::demote() { // Update sampler _cachedSampler = getInvalidSampler(); - syncSampler(); + syncSampler(_gpuObject.getSampler()); // update the memory usage Backend::textureResourceGPUMemSize.update(oldSize, 0); @@ -285,7 +285,7 @@ void GL45ResourceTexture::populateTransferQueue(TransferQueue& pendingTransfers) _populatedMip = sourceMip; incrementPopulatedSize(_gpuObject.evalMipSize(sourceMip)); sanityCheck(); - syncSampler(); + syncSampler(_gpuObject.getSampler()); })); } while (sourceMip != _allocatedMip); } @@ -384,7 +384,7 @@ GL45SparseResourceTexture::GL45SparseResourceTexture(const std::weak_ptr& backend, const Texture& texture) : GLESTexture(backend, texture), _size(texture.evalTotalSize()) { withPreservedTexture([&] { allocateStorage(); - syncSampler(); + syncSampler(texture.getSampler()); }); } @@ -313,9 +311,8 @@ void GLESFixedAllocationTexture::allocateStorage() const { (void)CHECK_GL_ERROR(); } -void GLESFixedAllocationTexture::syncSampler() const { - Parent::syncSampler(); - const Sampler& sampler = _gpuObject.getSampler(); +void GLESFixedAllocationTexture::syncSampler(const Sampler& sampler) const { + Parent::syncSampler(sampler); glTexParameterf(_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); glTexParameterf(_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.0f : sampler.getMaxMip())); } @@ -386,7 +383,7 @@ GLESVariableAllocationTexture::GLESVariableAllocationTexture(const std::weak_ptr allocateStorage(allocatedMip); copyMipsFromTexture(); - syncSampler(); + syncSampler(texture.getSampler()); } GLESVariableAllocationTexture::~GLESVariableAllocationTexture() { @@ -433,9 +430,9 @@ Size GLESVariableAllocationTexture::copyMipFaceLinesFromTexture(uint16_t mip, ui return amountCopied; } -void GLESVariableAllocationTexture::syncSampler() const { +void GLESVariableAllocationTexture::syncSampler(const Sampler& sampler) const { withPreservedTexture([&] { - Parent::syncSampler(); + Parent::syncSampler(sampler); glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, _populatedMip - _allocatedMip); }); } @@ -486,7 +483,7 @@ size_t GLESVariableAllocationTexture::promote() { glDeleteTextures(1, &oldId); // Update sampler - syncSampler(); + syncSampler(_gpuObject.getSampler()); // update the memory usage Backend::textureResourceGPUMemSize.update(oldSize, 0); @@ -515,7 +512,7 @@ size_t GLESVariableAllocationTexture::demote() { glDeleteTextures(1, &oldId); // Update sampler - syncSampler(); + syncSampler(_gpuObject.getSampler()); // update the memory usage Backend::textureResourceGPUMemSize.update(oldSize, 0); @@ -580,7 +577,7 @@ void GLESVariableAllocationTexture::populateTransferQueue(TransferJob::Queue& qu _populatedMip = sourceMip; incrementPopulatedSize(_gpuObject.evalMipSize(sourceMip)); sanityCheck(); - syncSampler(); + syncSampler(_gpuObject.getSampler()); })); } while (sourceMip != _allocatedMip); } diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index 0e1da3c4f0..c9904c8644 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -15,6 +15,7 @@ #include #include "ShaderConstants.h" +#include "TextureTable.h" #include "GPULogging.h" @@ -96,6 +97,7 @@ void Batch::clear() { _streamFormats.clear(); _textures.clear(); _textureTables.clear(); + _samplers.clear(); _transforms.clear(); _name.clear(); @@ -385,6 +387,9 @@ void Batch::setResourceTexture(uint32 slot, const TexturePointer& texture) { _params.emplace_back(_textures.cache(texture)); _params.emplace_back(slot); + if (texture) { + _params.emplace_back(_samplers.cache(texture->getSampler())); + } } void Batch::setResourceTexture(uint32 slot, const TextureView& view) { @@ -395,6 +400,14 @@ void Batch::setResourceTextureTable(const TextureTablePointer& textureTable, uin ADD_COMMAND(setResourceTextureTable); _params.emplace_back(_textureTables.cache(textureTable)); _params.emplace_back(slot); + if (textureTable) { + TextureTable::Array textures = textureTable->getTextures(); + for (auto& texture : textures) { + if (texture) { + _params.emplace_back(_samplers.cache(texture->getSampler())); + } + } + } } void Batch::setResourceFramebufferSwapChainTexture(uint32 slot, const FramebufferSwapChainPointer& framebuffer, unsigned int swapChainIndex, unsigned int renderBufferSlot) { @@ -404,6 +417,11 @@ void Batch::setResourceFramebufferSwapChainTexture(uint32 slot, const Framebuffe _params.emplace_back(slot); _params.emplace_back(swapChainIndex); _params.emplace_back(renderBufferSlot); + if (framebuffer) { + const auto& resourceFramebuffer = framebuffer->get(swapChainIndex); + const auto& resourceTexture = resourceFramebuffer->getRenderBuffer(renderBufferSlot); + _params.emplace_back(_samplers.cache(resourceTexture->getSampler())); + } } void Batch::setFramebuffer(const FramebufferPointer& framebuffer) { diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index 765fb23964..1088e86234 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -478,6 +478,7 @@ public: typedef Cache::Vector BufferCaches; typedef Cache::Vector TextureCaches; typedef Cache::Vector TextureTableCaches; + typedef Cache::Vector SamplerCaches; typedef Cache::Vector StreamFormatCaches; typedef Cache::Vector TransformCaches; typedef Cache::Vector PipelineCaches; @@ -531,6 +532,7 @@ public: BufferCaches _buffers; TextureCaches _textures; TextureTableCaches _textureTables; + SamplerCaches _samplers; StreamFormatCaches _streamFormats; TransformCaches _transforms; PipelineCaches _pipelines; diff --git a/libraries/gpu/src/gpu/FrameIOKeys.h b/libraries/gpu/src/gpu/FrameIOKeys.h index 3df1ef9d9a..5ada50d8bb 100644 --- a/libraries/gpu/src/gpu/FrameIOKeys.h +++ b/libraries/gpu/src/gpu/FrameIOKeys.h @@ -104,6 +104,7 @@ constexpr const char* texelFormat = "texelFormat"; constexpr const char* texture = "texture"; constexpr const char* textureTables = "textureTables"; constexpr const char* textures = "textures"; +constexpr const char* samplers = "samplers"; constexpr const char* transforms = "transforms"; constexpr const char* type = "type"; constexpr const char* usageType = "usageType"; diff --git a/libraries/gpu/src/gpu/FrameReader.cpp b/libraries/gpu/src/gpu/FrameReader.cpp index d156720a1f..cedc2af721 100644 --- a/libraries/gpu/src/gpu/FrameReader.cpp +++ b/libraries/gpu/src/gpu/FrameReader.cpp @@ -746,6 +746,23 @@ BatchPointer Deserializer::readBatch(const json& node) { readOptional(batch._drawcallUniformReset, node, keys::drawcallUniformReset); readPointerCache(batch._textures, node, keys::textures, textures); readPointerCache(batch._textureTables, node, keys::textureTables, textureTables); + { + auto transform = [](const json& node) -> Sampler { + Sampler::Desc desc; + desc._borderColor = readVec4(node["borderColor"]); + desc._maxAnisotropy = node["maxAnisotropy"]; + desc._filter = node["filter"]; + desc._comparisonFunc = node["comparisonFunc"]; + desc._wrapModeU = node["wrapModeU"]; + desc._wrapModeV = node["wrapModeV"]; + desc._wrapModeW = node["wrapModeW"]; + desc._mipOffset = node["mipOffset"]; + desc._minMip = node["minMip"]; + desc._maxMip = node["maxMip"]; + return Sampler(desc); + }; + readBatchCacheTransformed(batch._samplers, node, keys::samplers, transform); + } readPointerCache(batch._buffers, node, keys::buffers, buffers); readPointerCache(batch._pipelines, node, keys::pipelines, pipelines); readPointerCache(batch._streamFormats, node, keys::formats, formats); diff --git a/libraries/gpu/src/gpu/FrameWriter.cpp b/libraries/gpu/src/gpu/FrameWriter.cpp index f3e632bcad..ea1ec0702b 100644 --- a/libraries/gpu/src/gpu/FrameWriter.cpp +++ b/libraries/gpu/src/gpu/FrameWriter.cpp @@ -236,6 +236,24 @@ json Serializer::writeBatch(const Batch& batch) { if (0 != batch._textureTables.size()) { batchNode[keys::textureTables] = serializePointerCache(batch._textureTables, textureTableMap); } + if (0 != batch._samplers.size()) { + auto transform = [](const Sampler& object) -> json { + json result = json::object(); + const Sampler::Desc& desc = object.getDesc(); + result["borderColor"] = writeVec4(desc._borderColor); + result["maxAnisotropy"] = desc._maxAnisotropy; + result["filter"] = desc._filter; + result["comparisonFunc"] = desc._comparisonFunc; + result["wrapModeU"] = desc._wrapModeU; + result["wrapModeV"] = desc._wrapModeV; + result["wrapModeW"] = desc._wrapModeW; + result["mipOffset"] = desc._mipOffset; + result["minMip"] = desc._minMip; + result["maxMip"] = desc._maxMip; + return result; + }; + batchNode[keys::samplers] = serializeDataCache(batch._samplers, transform); + } if (0 != batch._buffers.size()) { batchNode[keys::buffers] = serializePointerCache(batch._buffers, bufferMap); } diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 48159c5da5..30645228e0 100644 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -16,6 +16,7 @@ #include #include +#include "QJsonObject" #include #include #include @@ -927,6 +928,19 @@ bool TextureSource::isDefined() const { return _gpuTexture && _gpuTexture->isDefined(); } +void TextureSource::setSampler(const gpu::Sampler& sampler) { + if (_gpuTextureOperator && !_locked) { + _locked = true; + auto gpuTexture = _gpuTextureOperator(); + _locked = false; + if (gpuTexture) { + gpuTexture->setSampler(sampler); + } + } else if (_gpuTexture) { + _gpuTexture->setSampler(sampler); + } +} + bool Texture::setMinMip(uint16 newMinMip) { uint16 oldMinMip = _minMip; _minMip = std::min(std::max(_minMip, newMinMip), getMaxMip()); @@ -973,3 +987,110 @@ Texture::ExternalUpdates Texture::getUpdates() const { void Texture::setStorage(std::unique_ptr& newStorage) { _storage.swap(newStorage); } + +Sampler Sampler::parseSampler(const QJsonObject& object) { + gpu::Sampler::Desc samplerDesc; + + auto filterItr = object.constFind("filter"); + if (filterItr != object.constEnd() && filterItr->isString()) { + auto filterStr = filterItr->toString(); + if (filterStr == "point") { + samplerDesc._filter = gpu::Sampler::Filter::FILTER_MIN_MAG_POINT; + } else if (filterStr == "linear") { + samplerDesc._filter = gpu::Sampler::Filter::FILTER_MIN_MAG_LINEAR; + } + } else { + auto minFilterItr = object.constFind("minFilter"); + auto magFilterItr = object.constFind("magFilter"); + if (minFilterItr != object.constEnd() && minFilterItr->isString() && magFilterItr != object.constEnd() && + magFilterItr->isString()) { + auto minFilterStr = minFilterItr->toString(); + auto magFilterStr = magFilterItr->toString(); + if (magFilterStr == "point") { + if (minFilterStr == "point") { + samplerDesc._filter = gpu::Sampler::Filter::FILTER_MIN_MAG_POINT; + } else if (minFilterStr == "linear") { + samplerDesc._filter = gpu::Sampler::Filter::FILTER_MIN_LINEAR_MAG_POINT; + } else if (minFilterStr == "mipmapPoint") { + samplerDesc._filter = gpu::Sampler::Filter::FILTER_MIN_MAG_MIP_POINT; + } else if (minFilterStr == "mipmapLinear") { + samplerDesc._filter = gpu::Sampler::Filter::FILTER_MIN_MAG_POINT_MIP_LINEAR; + } else if (minFilterStr == "linearMipmapPoint") { + samplerDesc._filter = gpu::Sampler::Filter::FILTER_MIN_LINEAR_MAG_MIP_POINT; + } else if (minFilterStr == "linearMipmapLinear") { + samplerDesc._filter = gpu::Sampler::Filter::FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR; + } + } else if (magFilterStr == "linear") { + if (minFilterStr == "point") { + samplerDesc._filter = gpu::Sampler::Filter::FILTER_MIN_POINT_MAG_LINEAR; + } else if (minFilterStr == "linear") { + samplerDesc._filter = gpu::Sampler::Filter::FILTER_MIN_MAG_LINEAR; + } else if (minFilterStr == "mipmapPoint") { + samplerDesc._filter = gpu::Sampler::Filter::FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT; + } else if (minFilterStr == "mipmapLinear") { + samplerDesc._filter = gpu::Sampler::Filter::FILTER_MIN_POINT_MAG_MIP_LINEAR; + } else if (minFilterStr == "linearMipmapPoint") { + samplerDesc._filter = gpu::Sampler::Filter::FILTER_MIN_MAG_LINEAR_MIP_POINT; + } else if (minFilterStr == "linearMipmapLinear") { + samplerDesc._filter = gpu::Sampler::Filter::FILTER_MIN_MAG_MIP_LINEAR; + } + } + } + } + + auto wrapItr = object.constFind("wrap"); + if (wrapItr != object.constEnd() && wrapItr->isString()) { + auto wrapStr = wrapItr->toString(); + if (wrapStr == "repeat") { + samplerDesc._wrapModeU = gpu::Sampler::WrapMode::WRAP_REPEAT; + } else if (wrapStr == "mirror") { + samplerDesc._wrapModeU = gpu::Sampler::WrapMode::WRAP_MIRROR; + } else if (wrapStr == "clamp") { + samplerDesc._wrapModeU = gpu::Sampler::WrapMode::WRAP_CLAMP; + } else if (wrapStr == "border") { + samplerDesc._wrapModeU = gpu::Sampler::WrapMode::WRAP_BORDER; + } else if (wrapStr == "mirrorOnce") { + samplerDesc._wrapModeU = gpu::Sampler::WrapMode::WRAP_MIRROR_ONCE; + } + samplerDesc._wrapModeV = samplerDesc._wrapModeU; + } else { + auto wrapSItr = object.constFind("wrapS"); + if (wrapSItr != object.constEnd() && wrapSItr->isString()) { + auto wrapSStr = wrapSItr->toString(); + if (wrapSStr == "repeat") { + samplerDesc._wrapModeU = gpu::Sampler::WrapMode::WRAP_REPEAT; + } else if (wrapSStr == "mirror") { + samplerDesc._wrapModeU = gpu::Sampler::WrapMode::WRAP_MIRROR; + } else if (wrapSStr == "clamp") { + samplerDesc._wrapModeU = gpu::Sampler::WrapMode::WRAP_CLAMP; + } else if (wrapSStr == "border") { + samplerDesc._wrapModeU = gpu::Sampler::WrapMode::WRAP_BORDER; + } else if (wrapSStr == "mirrorOnce") { + samplerDesc._wrapModeU = gpu::Sampler::WrapMode::WRAP_MIRROR_ONCE; + } + } + + auto wrapTItr = object.constFind("wrapT"); + if (wrapTItr != object.constEnd() && wrapTItr->isString()) { + auto wrapTStr = wrapTItr->toString(); + if (wrapTStr == "repeat") { + samplerDesc._wrapModeV = gpu::Sampler::WrapMode::WRAP_REPEAT; + } else if (wrapTStr == "mirror") { + samplerDesc._wrapModeV = gpu::Sampler::WrapMode::WRAP_MIRROR; + } else if (wrapTStr == "clamp") { + samplerDesc._wrapModeV = gpu::Sampler::WrapMode::WRAP_CLAMP; + } else if (wrapTStr == "border") { + samplerDesc._wrapModeV = gpu::Sampler::WrapMode::WRAP_BORDER; + } else if (wrapTStr == "mirrorOnce") { + samplerDesc._wrapModeV = gpu::Sampler::WrapMode::WRAP_MIRROR_ONCE; + } + } + } + + auto borderColorItr = object.constFind("borderColor"); + if (borderColorItr != object.constEnd()) { + samplerDesc._borderColor = vec4FromVariant(borderColorItr->toVariant()); + } + + return Sampler(samplerDesc); +} diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index a6f527e657..cc8242c992 100644 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -219,6 +219,9 @@ public: bool operator!=(const Sampler& other) const { return !(*this == other); } + + static Sampler parseSampler(const QJsonObject& object); + protected: Desc _desc; @@ -785,6 +788,8 @@ public: bool isDefined() const; std::function getTextureOperator() const { return _gpuTextureOperator; } + void setSampler(const gpu::Sampler& sampler); + protected: gpu::TexturePointer _gpuTexture; std::function _gpuTextureOperator { nullptr }; diff --git a/libraries/graphics/src/graphics/Material.cpp b/libraries/graphics/src/graphics/Material.cpp index 1061347e27..2624c06964 100644 --- a/libraries/graphics/src/graphics/Material.cpp +++ b/libraries/graphics/src/graphics/Material.cpp @@ -197,6 +197,21 @@ void Material::setTextureMap(MapChannel channel, const TextureMapPointer& textur } +void Material::setSampler(MapChannel channel, const gpu::Sampler& sampler) { + std::lock_guard locker(_textureMapsMutex); + _samplers[channel] = sampler; +} + +void Material::applySampler(MapChannel channel) { + std::lock_guard locker(_textureMapsMutex); + + auto samplerItr = _samplers.find(channel); + auto textureMapsItr = _textureMaps.find(channel); + if (samplerItr != _samplers.end() && textureMapsItr != _textureMaps.end() && textureMapsItr->second->getTextureSource()) { + textureMapsItr->second->getTextureSource()->setSampler(samplerItr->second); + } +} + bool Material::resetOpacityMap() const { // If OpacityMapMode explicit then nothing need to change here. if (_key.isOpacityMapMode()) { @@ -351,3 +366,9 @@ void MultiMaterial::setMToonTime() { // Minimize floating point error by doing an integer division to milliseconds, before the floating point division to seconds _schemaBuffer.edit()._time = (float)((usecTimestampNow() - mtoonStartTime) / USECS_PER_MSEC) / MSECS_PER_SECOND; } + +void MultiMaterial::applySamplers() const { + for (auto& func : _samplerFuncs) { + func(); + } +} diff --git a/libraries/graphics/src/graphics/Material.h b/libraries/graphics/src/graphics/Material.h index fd9c76dd97..2c19736da1 100644 --- a/libraries/graphics/src/graphics/Material.h +++ b/libraries/graphics/src/graphics/Material.h @@ -14,13 +14,13 @@ #include #include -#include #include #include #include #include +#include #include #include "MaterialMappingMode.h" @@ -342,7 +342,8 @@ public: class Material { public: typedef MaterialKey::MapChannel MapChannel; - typedef std::map TextureMaps; + typedef std::unordered_map TextureMaps; + typedef std::unordered_map SamplerMap; Material(); Material(const Material& material); @@ -396,6 +397,9 @@ public: virtual TextureMaps getTextureMaps() const { return _textureMaps; } // FIXME - not thread safe... const TextureMapPointer getTextureMap(MapChannel channel) const; + void setSampler(MapChannel channel, const gpu::Sampler& sampler); + void applySampler(MapChannel channel); + // Albedo maps cannot have opacity detected until they are loaded // This method allows const changing of the key/schemaBuffer without touching the map // return true if the opacity changed, flase otherwise @@ -494,6 +498,7 @@ private: glm::vec2 _materialParams { 0.0, 1.0 }; MaterialKey::CullFaceMode _cullFaceMode { DEFAULT_CULL_FACE_MODE }; TextureMaps _textureMaps; + SamplerMap _samplers; bool _defaultFallthrough { false }; std::unordered_map _propertyFallthroughs { NUM_TOTAL_FLAGS }; @@ -670,6 +675,10 @@ public: void setOutlineWidth(float width) { _outlineWidth = width; } void setOutline(const glm::vec3& outline) { _outline = outline; } + void addSamplerFunc(std::function samplerFunc) { _samplerFuncs.push_back(samplerFunc); } + void resetSamplers() { _samplerFuncs.clear(); } + void applySamplers() const; + private: gpu::BufferView _schemaBuffer; graphics::MaterialKey::CullFaceMode _cullFaceMode { graphics::Material::DEFAULT_CULL_FACE_MODE }; @@ -692,6 +701,8 @@ private: uint8_t _outlineWidthMode { 0 }; float _outlineWidth { 0.0f }; glm::vec3 _outline { graphics::Material::DEFAULT_OUTLINE }; + + std::vector> _samplerFuncs; }; }; diff --git a/libraries/hfm/src/hfm/HFM.h b/libraries/hfm/src/hfm/HFM.h index 6932572b1d..fe55f47145 100644 --- a/libraries/hfm/src/hfm/HFM.h +++ b/libraries/hfm/src/hfm/HFM.h @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -143,7 +144,9 @@ public: int texcoordSet; QString texcoordSetName; - bool isBumpmap{ false }; + bool isBumpmap { false }; + + gpu::Sampler sampler; bool isNull() const { return name.isEmpty() && filename.isEmpty() && content.isEmpty(); } }; diff --git a/libraries/model-serializers/src/GLTFSerializer.cpp b/libraries/model-serializers/src/GLTFSerializer.cpp index d4cee367d3..13b8f20c68 100644 --- a/libraries/model-serializers/src/GLTFSerializer.cpp +++ b/libraries/model-serializers/src/GLTFSerializer.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -1283,6 +1284,56 @@ QNetworkReply* GLTFSerializer::request(hifi::URL& url, bool isTest) { return netReply; // trying to sync later on. } +static gpu::Sampler::Filter filterFromGL(cgltf_int minFilter, cgltf_int magFilter) { + if (magFilter == GL_NEAREST) { + if (minFilter == GL_NEAREST) { + return gpu::Sampler::Filter::FILTER_MIN_MAG_POINT; + } else if (minFilter == GL_LINEAR) { + return gpu::Sampler::Filter::FILTER_MIN_LINEAR_MAG_POINT; + } else if (minFilter == GL_NEAREST_MIPMAP_NEAREST) { + return gpu::Sampler::Filter::FILTER_MIN_MAG_MIP_POINT; + } else if (minFilter == GL_NEAREST_MIPMAP_LINEAR) { + return gpu::Sampler::Filter::FILTER_MIN_MAG_POINT_MIP_LINEAR; + } else if (minFilter == GL_LINEAR_MIPMAP_NEAREST) { + return gpu::Sampler::Filter::FILTER_MIN_LINEAR_MAG_MIP_POINT; + } else if (minFilter == GL_LINEAR_MIPMAP_LINEAR) { + return gpu::Sampler::Filter::FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR; + } + } else if (magFilter == GL_LINEAR) { + if (minFilter == GL_NEAREST) { + return gpu::Sampler::Filter::FILTER_MIN_POINT_MAG_LINEAR; + } else if (minFilter == GL_LINEAR) { + return gpu::Sampler::Filter::FILTER_MIN_MAG_LINEAR; + } else if (minFilter == GL_NEAREST_MIPMAP_NEAREST) { + return gpu::Sampler::Filter::FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT; + } else if (minFilter == GL_NEAREST_MIPMAP_LINEAR) { + return gpu::Sampler::Filter::FILTER_MIN_POINT_MAG_MIP_LINEAR; + } else if (minFilter == GL_LINEAR_MIPMAP_NEAREST) { + return gpu::Sampler::Filter::FILTER_MIN_MAG_LINEAR_MIP_POINT; + } else if (minFilter == GL_LINEAR_MIPMAP_LINEAR) { + return gpu::Sampler::Filter::FILTER_MIN_MAG_MIP_LINEAR; + } + } + + return gpu::Sampler::Filter::FILTER_MIN_MAG_POINT; +} + +static gpu::Sampler::WrapMode wrapModeFromGL(cgltf_int wrapMode) { + if (wrapMode == GL_REPEAT) { + return gpu::Sampler::WrapMode::WRAP_REPEAT; + } else if (wrapMode == GL_MIRRORED_REPEAT) { + return gpu::Sampler::WrapMode::WRAP_MIRROR; + } else if (wrapMode == GL_CLAMP_TO_EDGE) { + return gpu::Sampler::WrapMode::WRAP_CLAMP; + } else if (wrapMode == GL_CLAMP_TO_BORDER) { + return gpu::Sampler::WrapMode::WRAP_BORDER; + } else if (wrapMode == GL_MIRROR_CLAMP_TO_EDGE) { + return gpu::Sampler::WrapMode::WRAP_MIRROR_ONCE; + } + + return gpu::Sampler::WrapMode::WRAP_REPEAT; +} + HFMTexture GLTFSerializer::getHFMTexture(const cgltf_texture *texture) { HFMTexture hfmTex = HFMTexture(); hfmTex.texcoordSet = 0; @@ -1339,6 +1390,18 @@ HFMTexture GLTFSerializer::getHFMTexture(const cgltf_texture *texture) { hfmTex.content = requestEmbeddedData(url); } } + + auto sampler = texture->sampler; + if (sampler) { + gpu::Sampler::Desc samplerDesc; + + samplerDesc._filter = filterFromGL(sampler->min_filter, sampler->mag_filter); + samplerDesc._wrapModeU = wrapModeFromGL(sampler->wrap_s); + samplerDesc._wrapModeV = wrapModeFromGL(sampler->wrap_t); + + hfmTex.sampler = gpu::Sampler(samplerDesc); + } + return hfmTex; } diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp index b6550516e4..084bd63834 100644 --- a/libraries/procedural/src/procedural/Procedural.cpp +++ b/libraries/procedural/src/procedural/Procedural.cpp @@ -160,18 +160,46 @@ void Procedural::setProceduralData(const ProceduralData& proceduralData) { if (proceduralData.channels != _data.channels) { _data.channels = proceduralData.channels; + + static gpu::Sampler defaultSampler; + static std::once_flag once; + std::call_once(once, [&] { + defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR); + }); + // Must happen on the main thread auto textureCache = DependencyManager::get(); size_t channelCount = std::min(MAX_PROCEDURAL_TEXTURE_CHANNELS, (size_t)proceduralData.channels.size()); - size_t channel; - for (channel = 0; channel < MAX_PROCEDURAL_TEXTURE_CHANNELS; ++channel) { + for (size_t channel = 0; channel < MAX_PROCEDURAL_TEXTURE_CHANNELS; ++channel) { if (channel < channelCount) { - QString url = proceduralData.channels.at((int)channel).toString(); - _channels[channel] = textureCache->getTexture(QUrl(url)); - } else { - // Release those textures no longer in use - _channels[channel] = textureCache->getTexture(QUrl()); + QJsonValue channelValue = proceduralData.channels.at((int)channel); + if (channelValue.isString()) { + QString url = channelValue.toString(); + _channels[channel] = textureCache->getTexture(QUrl(url)); + _samplers[channel] = defaultSampler; + continue; + } else if (channelValue.isObject()) { + QJsonObject channelMap = channelValue.toObject(); + + auto urlItr = channelMap.constFind("url"); + if (urlItr != channelMap.constEnd() && urlItr->isString()) { + _channels[channel] = textureCache->getTexture(QUrl(urlItr->toString())); + + auto samplerItr = channelMap.constFind("sampler"); + if (samplerItr != channelMap.constEnd() && samplerItr->isObject()) { + _samplers[channel] = gpu::Sampler::parseSampler(samplerItr->toObject()); + } else { + _samplers[channel] = defaultSampler; + } + continue; + } + } } + + // Fallthrough case: + // Release those textures no longer in use + _channels[channel] = textureCache->getTexture(QUrl()); + _samplers[channel] = defaultSampler; } } @@ -467,17 +495,11 @@ void Procedural::prepare(gpu::Batch& batch, lambda(batch); } - static gpu::Sampler sampler; - static std::once_flag once; - std::call_once(once, [&] { - sampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR); - }); - for (size_t i = 0; i < MAX_PROCEDURAL_TEXTURE_CHANNELS; ++i) { if (_channels[i] && _channels[i]->isLoaded()) { auto gpuTexture = _channels[i]->getGPUTexture(); if (gpuTexture) { - gpuTexture->setSampler(sampler); + gpuTexture->setSampler(_samplers[i]); gpuTexture->setAutoGenerateMips(true); } batch.setResourceTexture((gpu::uint32)(procedural::slot::texture::Channel0 + i), gpuTexture); diff --git a/libraries/procedural/src/procedural/Procedural.h b/libraries/procedural/src/procedural/Procedural.h index b4d6c55f3d..23854e2e65 100644 --- a/libraries/procedural/src/procedural/Procedural.h +++ b/libraries/procedural/src/procedural/Procedural.h @@ -40,7 +40,7 @@ const size_t MAX_PROCEDURAL_TEXTURE_CHANNELS{ 4 }; * If a procedural material contains a vertex shader, the bounding box of the material entity is used to cull the object to which the material is applied. * @property {string} fragmentShaderURL - A link to a fragment shader. Currently, only GLSL shaders are supported. The shader must implement a different method depending on the version. * shaderUrl is an alias. - * @property {string[]} channels=[] - An array of input texture URLs or entity IDs. Currently, up to 4 are supported. An entity ID may be that of an Image or Web entity. + * @property {string[]|Entities.Texture[]} channels=[] - An array of input textures or entity IDs. Currently, up to 4 are supported. An entity ID may be that of an Image or Web entity. * @property {ProceduralUniforms} uniforms={} - A {@link ProceduralUniforms} object containing all the custom uniforms to be passed to the shader. */ @@ -188,6 +188,7 @@ protected: // Rendering objects UniformLambdas _uniforms; NetworkTexturePointer _channels[MAX_PROCEDURAL_TEXTURE_CHANNELS]; + gpu::Sampler _samplers[MAX_PROCEDURAL_TEXTURE_CHANNELS]; std::unordered_map _vertexReplacements; std::unordered_map _fragmentReplacements; std::unordered_map _slotMap; diff --git a/libraries/procedural/src/procedural/ProceduralMaterialCache.cpp b/libraries/procedural/src/procedural/ProceduralMaterialCache.cpp index f76200ea57..eaa5e78d77 100644 --- a/libraries/procedural/src/procedural/ProceduralMaterialCache.cpp +++ b/libraries/procedural/src/procedural/ProceduralMaterialCache.cpp @@ -17,6 +17,8 @@ #include "Procedural.h" #include "ReferenceMaterial.h" +const QString FALLTHROUGH("fallthrough"); + NetworkMaterialResource::NetworkMaterialResource(const QUrl& url) : Resource(url) {} @@ -51,7 +53,7 @@ void NetworkMaterialResource::downloadFinished(const QByteArray& data) { * * @typedef {array} RGBS */ -bool NetworkMaterialResource::parseJSONColor(const QJsonValue& array, glm::vec3& color, bool& isSRGB) { +static bool parseJSONColor(const QJsonValue& array, glm::vec3& color, bool& isSRGB) { if (array.isArray()) { QJsonArray colorArray = array.toArray(); if (colorArray.size() >= 3 && colorArray[0].isDouble() && colorArray[1].isDouble() && colorArray[2].isDouble()) { @@ -124,6 +126,98 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseMaterialF return toReturn; } +static void setMaterialColor(const QJsonValue value, const std::function& setter) { + glm::vec3 color; + bool isSRGB; + bool valid = parseJSONColor(value, color, isSRGB); + if (valid) { + setter(color, isSRGB); + } +} + +static void setMaterialMapString(const QString& valueString, const std::shared_ptr& material, uint property, + const QUrl& baseUrl, const std::function& setter) { + if (valueString == FALLTHROUGH) { + material->setPropertyDoesFallthrough(property); + } else { + setter(baseUrl.resolved(valueString)); + } +} + +static void setMaterialMap(const QJsonValue& value, const std::shared_ptr& material, uint property, + graphics::MaterialKey::MapChannel channel, const QUrl& baseUrl, const std::function& setter) { + if (value.isObject()) { + QJsonObject valueMap = value.toObject(); + auto urlItr = valueMap.constFind("url"); + if (urlItr != valueMap.constEnd() && urlItr->isString()) { + setMaterialMapString(urlItr->toString(), material, property, baseUrl, setter); + + auto samplerItr = valueMap.constFind("sampler"); + if (samplerItr != valueMap.constEnd() && samplerItr->isObject()) { + auto samplerObject = samplerItr->toObject(); + material->setSampler(channel, gpu::Sampler::parseSampler(samplerObject)); + } + } + } else if (value.isString()) { + setMaterialMapString(value.toString(), material, property, baseUrl, setter); + } +} + +/*@jsdoc + *

Specifies the filter applied to a texture when it is sampled.

+ * + * + * + * + * + * + * + * + * + * + * + * + *
ValueDescription
"point"Select the texel nearest the texture coordinate.
"linear"Perform a weighted linear blend between the nearest adjacent samples.
"mipmapPoint"Minification filter only. Same as "point", but with mipmaps.
"mipmapLinear"Minification filter only. Same as "linear", but with mipmaps.
"linearMipmapPoint"Minification filter only. Same as "point", but with mipmaps and linear blending between them.
"linearMipmapLinear"Minification filter only. Same as "linear", but with mipmaps and linear blending between them.
+ * @typedef {string} FilterMode + */ + +/*@jsdoc + *

Specifies the wrap mode applied to a texture when it is sampled.

+ * + * + * + * + * + * + * + * + * + * + * + *
ValueDescription
"repeat"The texture coordinate wraps around the texture.
"mirror"The texture coordinate wraps around like a mirror.
"clamp"The texture coordinate is clamped to the [0, 1] range.
"border"The texture coordinate is clamped to the [0, 1] range, but the edge texels are blended with a constant border color.
"mirrorOnce"The texture coordinates are clamped to the [-1, 1] range, but the negative coordinates are mirrors of the positive. + * This effectively makes the texture twice as big through mirroring, but clamps to the edge beyond that.
+ * @typedef {string} WrapMode + */ + +/*@jsdoc + * A sampler describes the parameters used to access a texture in a shader. + * @typedef {object} Entities.Sampler + * @property {FilterMode} filter="point" - Set the min and mag filters to the same thing. + * @property {FilterMode} minFilter="point" - Set the minification filter. + * @property {FilterMode} magFilter="point" - Set the magnification filter. + * @property {WrapMode} wrap="repeat" - Set both the horizontal and vertical wrapping behavior. + * @property {WrapMode} wrapS="repeat" - Set the horizontal wrapping behavior. + * @property {WrapMode} wrapT="repeat" - Set the vertical wrapping behavior. + * @property {Vec4} borderColor=1,1,1,1 - The border color used if the wrap mode is "border". + */ + +/*@jsdoc + * A description of a texture, combining a URL and a sampler. + * @typedef {object} Entities.Texture + * @property {string} url - The URL of the texture. + * @property {Entities.Sampler} sampler - Set sampler used for this texture. + */ + /*@jsdoc * A material used in a {@link Entities.MaterialResource|MaterialResource}. * @typedef {object} Entities.Material @@ -149,10 +243,10 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseMaterialF * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". * @property {number|string} scattering - The scattering, range 0.01.0. * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". - * @property {string} emissiveMap - The URL of the emissive texture image, or an entity ID. An entity ID may be that of an + * @property {string|Entities.Texture} emissiveMap - The URL of the emissive texture image, or an entity ID. An entity ID may be that of an * Image or Web entity. Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr", * "vrm_mtoon". - * @property {string} albedoMap - The URL of the albedo texture image, or an entity ID. An entity ID may be that of an Image + * @property {string|Entities.Texture} albedoMap - The URL of the albedo texture image, or an entity ID. An entity ID may be that of an Image * or Web entity. Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr", * "vrm_mtoon". * @property {string} opacityMap - The URL of the opacity texture image, or an entity ID. An entity ID may be that of an Image @@ -178,31 +272,31 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseMaterialF *
  • "CULL_BACK" (the default) to cull the back faces of the geometry.
  • * * Set to "fallthrough" to fall through to the material below. Supported models: all. - * @property {string} roughnessMap - The URL of the roughness texture image. You can use this or glossMap, but not + * @property {string|Entities.Texture} roughnessMap - The URL of the roughness texture image. You can use this or glossMap, but not * both. * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". - * @property {string} glossMap - The URL of the gloss texture image. You can use this or roughnessMap, but not + * @property {string|Entities.Texture} glossMap - The URL of the gloss texture image. You can use this or roughnessMap, but not * both. * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". - * @property {string} metallicMap - The URL of the metallic texture image, or an entity ID. An entity ID may be that of an + * @property {string|Entities.Texture} metallicMap - The URL of the metallic texture image, or an entity ID. An entity ID may be that of an * Image or Web entity. You can use this or specularMap, but not both. * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". - * @property {string} specularMap - The URL of the specular texture image, or an entity ID. An entity ID may be that of an + * @property {string|Entities.Texture} specularMap - The URL of the specular texture image, or an entity ID. An entity ID may be that of an * Image or Web entity. You can use this or metallicMap, but not both. * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". - * @property {string} normalMap - The URL of the normal texture image, or an entity ID. An entity ID may be that of an Image + * @property {string|Entities.Texture} normalMap - The URL of the normal texture image, or an entity ID. An entity ID may be that of an Image * or Web entity. You can use this or bumpMap, but not both. Set to "fallthrough" to fall * through to the material below. Supported models: "hifi_pbr", "vrm_mtoon". - * @property {string} bumpMap - The URL of the bump texture image, or an entity ID. An entity ID may be that of an Image + * @property {string|Entities.Texture} bumpMap - The URL of the bump texture image, or an entity ID. An entity ID may be that of an Image * or Web entity. You can use this or normalMap, but not both. Set to "fallthrough" to * fall through to the material below. Supported models: "hifi_pbr", "vrm_mtoon". - * @property {string} occlusionMap - The URL of the occlusion texture image, or an entity ID. An entity ID may be that of + * @property {string|Entities.Texture} occlusionMap - The URL of the occlusion texture image, or an entity ID. An entity ID may be that of * an Image or Web entity. Set to "fallthrough" to fall through to the material below. * Supported models: "hifi_pbr". - * @property {string} scatteringMap - The URL of the scattering texture image, or an entity ID. An entity ID may be that of an + * @property {string|Entities.Texture} scatteringMap - The URL of the scattering texture image, or an entity ID. An entity ID may be that of an * Image or Web entity. Only used if normalMap or bumpMap is specified. * Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". - * @property {string} lightMap - The URL of the light map texture image, or an entity ID. An entity ID may be that of an Image + * @property {string|Entities.Texture} lightMap - The URL of the light map texture image, or an entity ID. An entity ID may be that of an Image * or Web entity. Set to "fallthrough" to fall through to the material below. Supported models: "hifi_pbr". * @property {Mat4|string} texCoordTransform0 - The transform to use for all of the maps apart from occlusionMap * and lightMap. @@ -222,12 +316,12 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseMaterialF * @property {ColorFloat|RGBS|string} shade - The shade color. A {@link ColorFloat} value is treated as sRGB and must have * component values in the range 0.01.0. A {@link RGBS} value can be either RGB or sRGB. * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". - * @property {string} shadeMap - The URL of the shade texture image, or an entity ID. An entity ID may be that of an + * @property {string|Entities.Texture} shadeMap - The URL of the shade texture image, or an entity ID. An entity ID may be that of an * Image or Web entity. * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". * @property {number|string} shadingShift - The shading shift. * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". - * @property {string} shadingShiftMap - The URL of the shading shift texture image, or an entity ID. An entity ID may be that of an + * @property {string|Entities.Texture} shadingShiftMap - The URL of the shading shift texture image, or an entity ID. An entity ID may be that of an * Image or Web entity. * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". * @property {number|string} shadingToony - The shading toony factor. Range 0.01.0. @@ -235,7 +329,7 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseMaterialF * @property {ColorFloat|RGBS|string} matcap - The matcap color. A {@link ColorFloat} value is treated as sRGB and must have * component values in the range 0.01.0. A {@link RGBS} value can be either RGB or sRGB. * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". - * @property {string} matcapMap - The URL of the matcap texture image, or an entity ID. An entity ID may be that of an + * @property {string|Entities.Texture} matcapMap - The URL of the matcap texture image, or an entity ID. An entity ID may be that of an * Image or Web entity. * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". * @property {ColorFloat|RGBS|string} parametricRim - The rim color. A {@link ColorFloat} value is treated as sRGB and must have @@ -245,7 +339,7 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseMaterialF * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". * @property {number|string} parametricRimLift - The parametric rim lift factor. * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". - * @property {string} rimMap - The URL of the rim texture image, or an entity ID. An entity ID may be that of an + * @property {string|Entities.Texture} rimMap - The URL of the rim texture image, or an entity ID. An entity ID may be that of an * Image or Web entity. * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". * @property {number|string} rimLightingMix - How much to mix between the rim color and normal lighting. Range 0.0 @@ -264,7 +358,7 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseMaterialF * @property {ColorFloat|RGBS|string} outline - The outline color. A {@link ColorFloat} value is treated as sRGB and must have * component values in the range 0.01.0. A {@link RGBS} value can be either RGB or sRGB. * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". - * @property {string} uvAnimationMaskMap - The URL of the UV animation mask texture image, or an entity ID. An entity ID may be that of an + * @property {string|Entities.Texture} uvAnimationMaskMap - The URL of the UV animation mask texture image, or an entity ID. An entity ID may be that of an * Image or Web entity. * Set to "fallthrough" to fall through to the material below. Supported models: "vrm_mtoon". * @property {number|string} uvAnimationScrollXSpeed - The speed of the UV scrolling animation in the X dimension, in UV units per second. @@ -299,7 +393,6 @@ std::pair> NetworkMaterialResource std::array texcoordTransforms; - const QString FALLTHROUGH("fallthrough"); if (modelString == graphics::Material::HIFI_PBR || modelString == graphics::Material::VRM_MTOON) { std::shared_ptr material; if (modelString == graphics::Material::HIFI_PBR) { @@ -319,12 +412,7 @@ std::pair> NetworkMaterialResource if (value.isString() && value.toString() == FALLTHROUGH) { material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::EMISSIVE_VAL_BIT); } else { - glm::vec3 color; - bool isSRGB; - bool valid = parseJSONColor(value, color, isSRGB); - if (valid) { - material->setEmissive(color, isSRGB); - } + setMaterialColor(value, [&](glm::vec3 color, bool isSRGB) { material->setEmissive(color, isSRGB); }); } } else if (key == "opacity") { auto value = materialJSON.value(key); @@ -338,12 +426,7 @@ std::pair> NetworkMaterialResource if (value.isString() && value.toString() == FALLTHROUGH) { material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::ALBEDO_VAL_BIT); } else { - glm::vec3 color; - bool isSRGB; - bool valid = parseJSONColor(value, color, isSRGB); - if (valid) { - material->setAlbedo(color, isSRGB); - } + setMaterialColor(value, [&](glm::vec3 color, bool isSRGB) { material->setAlbedo(color, isSRGB); }); } } else if (key == "opacityMapMode") { auto value = materialJSON.value(key); @@ -358,7 +441,7 @@ std::pair> NetworkMaterialResource } } } - } else if (key == "opacityCutoff") { + } else if (key == "opacityCutoff") { auto value = materialJSON.value(key); if (value.isString() && value.toString() == FALLTHROUGH) { material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::OPACITY_CUTOFF_VAL_BIT); @@ -378,51 +461,29 @@ std::pair> NetworkMaterialResource } } } - } else if (key == "emissiveMap") { + } else if (key == "emissiveMap") { auto value = materialJSON.value(key); - if (value.isString()) { - auto valueString = value.toString(); - if (valueString == FALLTHROUGH) { - material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::EMISSIVE_MAP_BIT); - } else { - material->setEmissiveMap(baseUrl.resolved(valueString)); - } - } + setMaterialMap(value, material, graphics::MaterialKey::FlagBit::EMISSIVE_MAP_BIT, graphics::Material::MapChannel::EMISSIVE_MAP, + baseUrl, [&](const QUrl& url) { material->setEmissiveMap(url); }); } else if (key == "albedoMap") { auto value = materialJSON.value(key); - if (value.isString()) { - QString valueString = value.toString(); - if (valueString == FALLTHROUGH) { - material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::ALBEDO_MAP_BIT); - } else { + setMaterialMap(value, material, graphics::MaterialKey::FlagBit::ALBEDO_MAP_BIT, graphics::Material::MapChannel::ALBEDO_MAP, + baseUrl, [&](const QUrl& url) { bool useAlphaChannel = false; auto opacityMap = materialJSON.find("opacityMap"); - if (opacityMap != materialJSON.end() && opacityMap->isString() && opacityMap->toString() == valueString) { + if (opacityMap != materialJSON.end() && opacityMap->isString() && baseUrl.resolved(opacityMap->toString()) == url) { useAlphaChannel = true; } - material->setAlbedoMap(baseUrl.resolved(valueString), useAlphaChannel); - } - } + material->setAlbedoMap(url, useAlphaChannel); + }); } else if (key == "normalMap") { auto value = materialJSON.value(key); - if (value.isString()) { - auto valueString = value.toString(); - if (valueString == FALLTHROUGH) { - material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::NORMAL_MAP_BIT); - } else { - material->setNormalMap(baseUrl.resolved(valueString), false); - } - } + setMaterialMap(value, material, graphics::MaterialKey::FlagBit::NORMAL_MAP_BIT, graphics::Material::MapChannel::NORMAL_MAP, + baseUrl, [&](const QUrl& url) { material->setNormalMap(url, false); }); } else if (key == "bumpMap") { auto value = materialJSON.value(key); - if (value.isString()) { - auto valueString = value.toString(); - if (valueString == FALLTHROUGH) { - material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::NORMAL_MAP_BIT); - } else { - material->setNormalMap(baseUrl.resolved(valueString), true); - } - } + setMaterialMap(value, material, graphics::MaterialKey::FlagBit::NORMAL_MAP_BIT, graphics::Material::MapChannel::NORMAL_MAP, + baseUrl, [&](const QUrl& url) { material->setNormalMap(url, true); }); } else if (key == "texCoordTransform0") { auto value = materialJSON.value(key); if (value.isString()) { @@ -494,74 +555,32 @@ std::pair> NetworkMaterialResource } } else if (key == "roughnessMap") { auto value = materialJSON.value(key); - if (value.isString()) { - auto valueString = value.toString(); - if (valueString == FALLTHROUGH) { - material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::ROUGHNESS_MAP_BIT); - } else { - material->setRoughnessMap(baseUrl.resolved(valueString), false); - } - } + setMaterialMap(value, material, graphics::MaterialKey::FlagBit::ROUGHNESS_MAP_BIT, graphics::Material::MapChannel::ROUGHNESS_MAP, + baseUrl, [&](const QUrl& url) { material->setRoughnessMap(url, false); }); } else if (key == "glossMap") { auto value = materialJSON.value(key); - if (value.isString()) { - auto valueString = value.toString(); - if (valueString == FALLTHROUGH) { - material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::ROUGHNESS_MAP_BIT); - } else { - material->setRoughnessMap(baseUrl.resolved(valueString), true); - } - } + setMaterialMap(value, material, graphics::MaterialKey::FlagBit::ROUGHNESS_MAP_BIT, graphics::Material::MapChannel::ROUGHNESS_MAP, + baseUrl, [&](const QUrl& url) { material->setRoughnessMap(url, true); }); } else if (key == "metallicMap") { auto value = materialJSON.value(key); - if (value.isString()) { - auto valueString = value.toString(); - if (valueString == FALLTHROUGH) { - material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::METALLIC_MAP_BIT); - } else { - material->setMetallicMap(baseUrl.resolved(valueString), false); - } - } + setMaterialMap(value, material, graphics::MaterialKey::FlagBit::METALLIC_MAP_BIT, graphics::Material::MapChannel::METALLIC_MAP, + baseUrl, [&](const QUrl& url) { material->setMetallicMap(url, false); }); } else if (key == "specularMap") { auto value = materialJSON.value(key); - if (value.isString()) { - auto valueString = value.toString(); - if (valueString == FALLTHROUGH) { - material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::METALLIC_MAP_BIT); - } else { - material->setMetallicMap(baseUrl.resolved(valueString), true); - } - } + setMaterialMap(value, material, graphics::MaterialKey::FlagBit::METALLIC_MAP_BIT, graphics::Material::MapChannel::METALLIC_MAP, + baseUrl, [&](const QUrl& url) { material->setMetallicMap(url, true); }); } else if (key == "occlusionMap") { auto value = materialJSON.value(key); - if (value.isString()) { - auto valueString = value.toString(); - if (valueString == FALLTHROUGH) { - material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::OCCLUSION_MAP_BIT); - } else { - material->setOcclusionMap(baseUrl.resolved(valueString)); - } - } + setMaterialMap(value, material, graphics::MaterialKey::FlagBit::OCCLUSION_MAP_BIT, graphics::Material::MapChannel::OCCLUSION_MAP, + baseUrl, [&](const QUrl& url) { material->setOcclusionMap(url); }); } else if (key == "scatteringMap") { auto value = materialJSON.value(key); - if (value.isString()) { - auto valueString = value.toString(); - if (valueString == FALLTHROUGH) { - material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::SCATTERING_MAP_BIT); - } else { - material->setScatteringMap(baseUrl.resolved(valueString)); - } - } + setMaterialMap(value, material, graphics::MaterialKey::FlagBit::SCATTERING_MAP_BIT, graphics::Material::MapChannel::SCATTERING_MAP, + baseUrl, [&](const QUrl& url) { material->setScatteringMap(url); }); } else if (key == "lightMap") { auto value = materialJSON.value(key); - if (value.isString()) { - auto valueString = value.toString(); - if (valueString == FALLTHROUGH) { - material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::LIGHT_MAP_BIT); - } else { - material->setLightMap(baseUrl.resolved(valueString)); - } - } + setMaterialMap(value, material, graphics::MaterialKey::FlagBit::LIGHT_MAP_BIT, graphics::Material::MapChannel::LIGHT_MAP, + baseUrl, [&](const QUrl& url) { material->setLightMap(url); }); } else if (key == "lightmapParams") { auto value = materialJSON.value(key); if (value.isString()) { @@ -579,23 +598,13 @@ std::pair> NetworkMaterialResource if (value.isString() && value.toString() == FALLTHROUGH) { material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::SHADE_VAL_BIT); } else { - glm::vec3 color; - bool isSRGB; - bool valid = parseJSONColor(value, color, isSRGB); - if (valid) { - toonMaterial->setShade(color, isSRGB); - } + setMaterialColor(value, [&](glm::vec3 color, bool isSRGB) { toonMaterial->setShade(color, isSRGB); }); } } else if (key == "shadeMap") { auto value = materialJSON.value(key); - if (value.isString()) { - auto valueString = value.toString(); - if (valueString == FALLTHROUGH) { - material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::SHADE_MAP_BIT); - } else { - toonMaterial->setShadeMap(baseUrl.resolved(valueString)); - } - } + setMaterialMap(value, material, NetworkMToonMaterial::MToonFlagBit::SHADE_MAP_BIT, + (graphics::Material::MapChannel) NetworkMToonMaterial::MToonMapChannel::SHADE_MAP, + baseUrl, [&](const QUrl& url) { toonMaterial->setShadeMap(url); }); } else if (key == "shadingShift") { auto value = materialJSON.value(key); if (value.isString() && value.toString() == FALLTHROUGH) { @@ -605,14 +614,9 @@ std::pair> NetworkMaterialResource } } else if (key == "shadingShiftMap") { auto value = materialJSON.value(key); - if (value.isString()) { - auto valueString = value.toString(); - if (valueString == FALLTHROUGH) { - material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::SHADING_SHIFT_MAP_BIT); - } else { - toonMaterial->setShadingShiftMap(baseUrl.resolved(valueString)); - } - } + setMaterialMap(value, material, NetworkMToonMaterial::MToonFlagBit::SHADING_SHIFT_MAP_BIT, + (graphics::Material::MapChannel) NetworkMToonMaterial::MToonMapChannel::SHADING_SHIFT_MAP, + baseUrl, [&](const QUrl& url) { toonMaterial->setShadingShiftMap(url); }); } else if (key == "shadingToony") { auto value = materialJSON.value(key); if (value.isString() && value.toString() == FALLTHROUGH) { @@ -625,34 +629,19 @@ std::pair> NetworkMaterialResource if (value.isString() && value.toString() == FALLTHROUGH) { material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::MATCAP_VAL_BIT); } else { - glm::vec3 color; - bool isSRGB; - bool valid = parseJSONColor(value, color, isSRGB); - if (valid) { - toonMaterial->setMatcap(color, isSRGB); - } + setMaterialColor(value, [&](glm::vec3 color, bool isSRGB) { toonMaterial->setMatcap(color, isSRGB); }); } } else if (key == "matcapMap") { auto value = materialJSON.value(key); - if (value.isString()) { - auto valueString = value.toString(); - if (valueString == FALLTHROUGH) { - material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::MATCAP_MAP_BIT); - } else { - toonMaterial->setMatcapMap(baseUrl.resolved(valueString)); - } - } + setMaterialMap(value, material, NetworkMToonMaterial::MToonFlagBit::MATCAP_MAP_BIT, + (graphics::Material::MapChannel) NetworkMToonMaterial::MToonMapChannel::MATCAP_MAP, + baseUrl, [&](const QUrl& url) { toonMaterial->setMatcapMap(url); }); } else if (key == "parametricRim") { auto value = materialJSON.value(key); if (value.isString() && value.toString() == FALLTHROUGH) { material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::PARAMETRIC_RIM_VAL_BIT); } else { - glm::vec3 color; - bool isSRGB; - bool valid = parseJSONColor(value, color, isSRGB); - if (valid) { - toonMaterial->setParametricRim(color, isSRGB); - } + setMaterialColor(value, [&](glm::vec3 color, bool isSRGB) { toonMaterial->setParametricRim(color, isSRGB); }); } } else if (key == "parametricRimFresnelPower") { auto value = materialJSON.value(key); @@ -670,14 +659,9 @@ std::pair> NetworkMaterialResource } } else if (key == "rimMap") { auto value = materialJSON.value(key); - if (value.isString()) { - auto valueString = value.toString(); - if (valueString == FALLTHROUGH) { - material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::RIM_MAP_BIT); - } else { - toonMaterial->setRimMap(baseUrl.resolved(valueString)); - } - } + setMaterialMap(value, material, NetworkMToonMaterial::MToonFlagBit::RIM_MAP_BIT, + (graphics::Material::MapChannel) NetworkMToonMaterial::MToonMapChannel::RIM_MAP, + baseUrl, [&](const QUrl& url) { toonMaterial->setRimMap(url); }); } else if (key == "rimLightingMix") { auto value = materialJSON.value(key); if (value.isString() && value.toString() == FALLTHROUGH) { @@ -687,14 +671,9 @@ std::pair> NetworkMaterialResource } } else if (key == "uvAnimationMaskMap") { auto value = materialJSON.value(key); - if (value.isString()) { - auto valueString = value.toString(); - if (valueString == FALLTHROUGH) { - material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_MASK_MAP_BIT); - } else { - toonMaterial->setUVAnimationMaskMap(baseUrl.resolved(valueString)); - } - } + setMaterialMap(value, material, NetworkMToonMaterial::MToonFlagBit::UV_ANIMATION_MASK_MAP_BIT, + (graphics::Material::MapChannel) NetworkMToonMaterial::MToonMapChannel::UV_ANIMATION_MASK_MAP, + baseUrl, [&](const QUrl& url) { toonMaterial->setUVAnimationMaskMap(url); }); } else if (key == "uvAnimationScrollXSpeed") { auto value = materialJSON.value(key); if (value.isString() && value.toString() == FALLTHROUGH) { @@ -742,12 +721,7 @@ std::pair> NetworkMaterialResource if (value.isString() && value.toString() == FALLTHROUGH) { material->setPropertyDoesFallthrough(NetworkMToonMaterial::MToonFlagBit::OUTLINE_VAL_BIT); } else { - glm::vec3 color; - bool isSRGB; - bool valid = parseJSONColor(value, color, isSRGB); - if (valid) { - toonMaterial->setOutline(color, isSRGB); - } + setMaterialColor(value, [&](glm::vec3 color, bool isSRGB) { toonMaterial->setOutline(color, isSRGB); }); } } // TODO: support outlineWidthTexture and outlineLightingMix @@ -783,12 +757,7 @@ std::pair> NetworkMaterialResource if (value.isString() && value.toString() == FALLTHROUGH) { material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::ALBEDO_VAL_BIT); } else { - glm::vec3 color; - bool isSRGB; - bool valid = parseJSONColor(value, color, isSRGB); - if (valid) { - material->setAlbedo(color, isSRGB); - } + setMaterialColor(value, [&](glm::vec3 color, bool isSRGB) { material->setAlbedo(color, isSRGB); }); } } else if (key == "defaultFallthrough") { auto value = materialJSON.value(key); @@ -873,6 +842,7 @@ graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl qDebug() << "GeometryResource::setTextures: TextureCache dependency not available, skipping textures"; } _textures[channel] = Texture { hfmTexture.name, texture }; + setSampler(channel, hfmTexture.sampler); auto map = std::make_shared(); if (texture) { @@ -978,6 +948,7 @@ NetworkMaterial::NetworkMaterial(const HFMMaterial& material, const QUrl& textur } setTextureMap(MapChannel::ALBEDO_MAP, map); + setSampler(MapChannel::ALBEDO_MAP, material.albedoTexture.sampler); } @@ -985,22 +956,27 @@ NetworkMaterial::NetworkMaterial(const HFMMaterial& material, const QUrl& textur auto type = (material.normalTexture.isBumpmap ? image::TextureUsage::BUMP_TEXTURE : image::TextureUsage::NORMAL_TEXTURE); auto map = fetchTextureMap(textureBaseUrl, material.normalTexture, type, MapChannel::NORMAL_MAP); setTextureMap(MapChannel::NORMAL_MAP, map); + setSampler(MapChannel::NORMAL_MAP, material.normalTexture.sampler); } if (!material.roughnessTexture.filename.isEmpty()) { auto map = fetchTextureMap(textureBaseUrl, material.roughnessTexture, image::TextureUsage::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP); setTextureMap(MapChannel::ROUGHNESS_MAP, map); + setSampler(MapChannel::ROUGHNESS_MAP, material.roughnessTexture.sampler); } else if (!material.glossTexture.filename.isEmpty()) { auto map = fetchTextureMap(textureBaseUrl, material.glossTexture, image::TextureUsage::GLOSS_TEXTURE, MapChannel::ROUGHNESS_MAP); setTextureMap(MapChannel::ROUGHNESS_MAP, map); + setSampler(MapChannel::ROUGHNESS_MAP, material.glossTexture.sampler); } if (!material.metallicTexture.filename.isEmpty()) { auto map = fetchTextureMap(textureBaseUrl, material.metallicTexture, image::TextureUsage::METALLIC_TEXTURE, MapChannel::METALLIC_MAP); setTextureMap(MapChannel::METALLIC_MAP, map); + setSampler(MapChannel::METALLIC_MAP, material.metallicTexture.sampler); } else if (!material.specularTexture.filename.isEmpty()) { auto map = fetchTextureMap(textureBaseUrl, material.specularTexture, image::TextureUsage::SPECULAR_TEXTURE, MapChannel::METALLIC_MAP); setTextureMap(MapChannel::METALLIC_MAP, map); + setSampler(MapChannel::METALLIC_MAP, material.specularTexture.sampler); } if (!material.occlusionTexture.filename.isEmpty()) { @@ -1009,16 +985,19 @@ NetworkMaterial::NetworkMaterial(const HFMMaterial& material, const QUrl& textur map->setTextureTransform(material.occlusionTexture.transform); } setTextureMap(MapChannel::OCCLUSION_MAP, map); + setSampler(MapChannel::OCCLUSION_MAP, material.occlusionTexture.sampler); } if (!material.emissiveTexture.filename.isEmpty()) { auto map = fetchTextureMap(textureBaseUrl, material.emissiveTexture, image::TextureUsage::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP); setTextureMap(MapChannel::EMISSIVE_MAP, map); + setSampler(MapChannel::EMISSIVE_MAP, material.emissiveTexture.sampler); } if (!material.scatteringTexture.filename.isEmpty()) { auto map = fetchTextureMap(textureBaseUrl, material.scatteringTexture, image::TextureUsage::SCATTERING_TEXTURE, MapChannel::SCATTERING_MAP); setTextureMap(MapChannel::SCATTERING_MAP, map); + setSampler(MapChannel::SCATTERING_MAP, material.scatteringTexture.sampler); } if (!material.lightmapTexture.filename.isEmpty()) { @@ -1030,6 +1009,7 @@ NetworkMaterial::NetworkMaterial(const HFMMaterial& material, const QUrl& textur map->setLightmapOffsetScale(_lightmapParams.x, _lightmapParams.y); } setTextureMap(MapChannel::LIGHT_MAP, map); + setSampler(MapChannel::LIGHT_MAP, material.lightmapTexture.sampler); } } @@ -1142,26 +1122,31 @@ NetworkMToonMaterial::NetworkMToonMaterial(const HFMMaterial& material, const QU if (!material.shadeTexture.filename.isEmpty()) { auto map = fetchTextureMap(textureBaseUrl, material.shadeTexture, image::TextureUsage::ALBEDO_TEXTURE, (MapChannel)MToonMapChannel::SHADE_MAP); setTextureMap((MapChannel)MToonMapChannel::SHADE_MAP, map); + setSampler((MapChannel)MToonMapChannel::SHADE_MAP, material.shadeTexture.sampler); } if (!material.shadingShiftTexture.filename.isEmpty()) { auto map = fetchTextureMap(textureBaseUrl, material.shadingShiftTexture, image::TextureUsage::ROUGHNESS_TEXTURE, (MapChannel)MToonMapChannel::SHADING_SHIFT_MAP); setTextureMap((MapChannel)MToonMapChannel::SHADING_SHIFT_MAP, map); + setSampler((MapChannel)MToonMapChannel::SHADING_SHIFT_MAP, material.shadingShiftTexture.sampler); } if (!material.matcapTexture.filename.isEmpty()) { auto map = fetchTextureMap(textureBaseUrl, material.matcapTexture, image::TextureUsage::EMISSIVE_TEXTURE, (MapChannel)MToonMapChannel::MATCAP_MAP); setTextureMap((MapChannel)MToonMapChannel::MATCAP_MAP, map); + setSampler((MapChannel)MToonMapChannel::MATCAP_MAP, material.matcapTexture.sampler); } if (!material.rimTexture.filename.isEmpty()) { auto map = fetchTextureMap(textureBaseUrl, material.rimTexture, image::TextureUsage::ALBEDO_TEXTURE, (MapChannel)MToonMapChannel::RIM_MAP); setTextureMap((MapChannel)MToonMapChannel::RIM_MAP, map); + setSampler((MapChannel)MToonMapChannel::RIM_MAP, material.rimTexture.sampler); } if (!material.uvAnimationTexture.filename.isEmpty()) { auto map = fetchTextureMap(textureBaseUrl, material.uvAnimationTexture, image::TextureUsage::ROUGHNESS_TEXTURE, (MapChannel)MToonMapChannel::UV_ANIMATION_MASK_MAP); setTextureMap((MapChannel)MToonMapChannel::UV_ANIMATION_MASK_MAP, map); + setSampler((MapChannel)MToonMapChannel::UV_ANIMATION_MASK_MAP, material.uvAnimationTexture.sampler); } } diff --git a/libraries/procedural/src/procedural/ProceduralMaterialCache.h b/libraries/procedural/src/procedural/ProceduralMaterialCache.h index a7c5baa011..c359270eaf 100644 --- a/libraries/procedural/src/procedural/ProceduralMaterialCache.h +++ b/libraries/procedural/src/procedural/ProceduralMaterialCache.h @@ -224,9 +224,6 @@ public: static ParsedMaterials parseJSONMaterials(const QJsonDocument& materialJSON, const QUrl& baseUrl); static ParsedMaterials parseMaterialForUUID(const QJsonValue& entityIDJSON); static std::pair> parseJSONMaterial(const QJsonValue& materialJSONValue, const QUrl& baseUrl = QUrl()); - -private: - static bool parseJSONColor(const QJsonValue& array, glm::vec3& color, bool& isSRGB); }; using NetworkMaterialResourcePointer = QSharedPointer; diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index f491d127c9..fa6746ce06 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -533,6 +533,7 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial multiMaterial.resetReferenceTexturesAndMaterials(); multiMaterial.setisMToon(!multiMaterial.empty() && multiMaterial.top().material && multiMaterial.top().material->isMToon()); multiMaterial.resetOutline(); + multiMaterial.resetSamplers(); // The total list of things we need to look for static std::set allFlags; @@ -643,6 +644,7 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial if (itr->second->isDefined()) { material->resetOpacityMap(); drawMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, itr->second->getTextureView()); + multiMaterial.addSamplerFunc([=] () { material->applySampler(graphics::MaterialKey::ALBEDO_MAP); }); if (itr->second->getTextureView().isReference()) { multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); } @@ -665,6 +667,7 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial if (itr != textureMaps.end()) { if (itr->second->isDefined()) { drawMaterialTextures->setTexture(gr::Texture::MaterialMetallic, itr->second->getTextureView()); + multiMaterial.addSamplerFunc([=] () { material->applySampler(graphics::MaterialKey::METALLIC_MAP); }); if (itr->second->getTextureView().isReference()) { multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); } @@ -685,6 +688,7 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial if (itr != textureMaps.end()) { if (itr->second->isDefined()) { drawMaterialTextures->setTexture(gr::Texture::MaterialRoughness, itr->second->getTextureView()); + multiMaterial.addSamplerFunc([=] () { material->applySampler(graphics::MaterialKey::ROUGHNESS_MAP); }); if (itr->second->getTextureView().isReference()) { multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); } @@ -705,6 +709,7 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial if (itr != textureMaps.end()) { if (itr->second->isDefined()) { drawMaterialTextures->setTexture(gr::Texture::MaterialNormal, itr->second->getTextureView()); + multiMaterial.addSamplerFunc([=] () { material->applySampler(graphics::MaterialKey::NORMAL_MAP); }); if (itr->second->getTextureView().isReference()) { multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); } @@ -725,6 +730,7 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial if (itr != textureMaps.end()) { if (itr->second->isDefined()) { drawMaterialTextures->setTexture(gr::Texture::MaterialOcclusion, itr->second->getTextureView()); + multiMaterial.addSamplerFunc([=] () { material->applySampler(graphics::MaterialKey::OCCLUSION_MAP); }); if (itr->second->getTextureView().isReference()) { multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); } @@ -745,6 +751,7 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial if (itr != textureMaps.end()) { if (itr->second->isDefined()) { drawMaterialTextures->setTexture(gr::Texture::MaterialScattering, itr->second->getTextureView()); + multiMaterial.addSamplerFunc([=] () { material->applySampler(graphics::MaterialKey::SCATTERING_MAP); }); if (itr->second->getTextureView().isReference()) { multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); } @@ -766,6 +773,7 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial if (itr != textureMaps.end()) { if (itr->second->isDefined()) { drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView()); + multiMaterial.addSamplerFunc([=] () { material->applySampler(graphics::MaterialKey::EMISSIVE_MAP); }); if (itr->second->getTextureView().isReference()) { multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); } @@ -789,6 +797,7 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial if (itr != textureMaps.end()) { if (itr->second->isDefined()) { drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView()); + multiMaterial.addSamplerFunc([=] () { material->applySampler(graphics::MaterialKey::LIGHT_MAP); }); if (itr->second->getTextureView().isReference()) { multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); } @@ -873,6 +882,7 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial if (itr->second->isDefined()) { material->resetOpacityMap(); drawMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, itr->second->getTextureView()); + multiMaterial.addSamplerFunc([=] () { material->applySampler(graphics::MaterialKey::ALBEDO_MAP); }); if (itr->second->getTextureView().isReference()) { multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); } @@ -895,6 +905,7 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial if (itr != textureMaps.end()) { if (itr->second->isDefined()) { drawMaterialTextures->setTexture(gr::Texture::MaterialNormal, itr->second->getTextureView()); + multiMaterial.addSamplerFunc([=] () { material->applySampler(graphics::MaterialKey::NORMAL_MAP); }); if (itr->second->getTextureView().isReference()) { multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); } @@ -916,6 +927,7 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial if (itr != textureMaps.end()) { if (itr->second->isDefined()) { drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView()); + multiMaterial.addSamplerFunc([=] () { material->applySampler(graphics::MaterialKey::EMISSIVE_MAP); }); if (itr->second->getTextureView().isReference()) { multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); } @@ -993,6 +1005,7 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial if (itr != textureMaps.end()) { if (itr->second->isDefined()) { drawMaterialTextures->setTexture(gr::Texture::MaterialShade, itr->second->getTextureView()); + multiMaterial.addSamplerFunc([=] () { material->applySampler((graphics::Material::MapChannel) NetworkMToonMaterial::SHADE_MAP); }); if (itr->second->getTextureView().isReference()) { multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); } @@ -1013,6 +1026,7 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial if (itr != textureMaps.end()) { if (itr->second->isDefined()) { drawMaterialTextures->setTexture(gr::Texture::MaterialShadingShift, itr->second->getTextureView()); + multiMaterial.addSamplerFunc([=] () { material->applySampler((graphics::Material::MapChannel) NetworkMToonMaterial::SHADING_SHIFT_MAP); }); if (itr->second->getTextureView().isReference()) { multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); } @@ -1033,6 +1047,7 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial if (itr != textureMaps.end()) { if (itr->second->isDefined()) { drawMaterialTextures->setTexture(gr::Texture::MaterialMatcap, itr->second->getTextureView()); + multiMaterial.addSamplerFunc([=] () { material->applySampler((graphics::Material::MapChannel) NetworkMToonMaterial::MATCAP_MAP); }); if (itr->second->getTextureView().isReference()) { multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); } @@ -1053,6 +1068,7 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial if (itr != textureMaps.end()) { if (itr->second->isDefined()) { drawMaterialTextures->setTexture(gr::Texture::MaterialRim, itr->second->getTextureView()); + multiMaterial.addSamplerFunc([=] () { material->applySampler((graphics::Material::MapChannel) NetworkMToonMaterial::RIM_MAP); }); if (itr->second->getTextureView().isReference()) { multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); } @@ -1073,6 +1089,7 @@ void RenderPipelines::updateMultiMaterial(graphics::MultiMaterial& multiMaterial if (itr != textureMaps.end()) { if (itr->second->isDefined()) { drawMaterialTextures->setTexture(gr::Texture::MaterialUVAnimationMask, itr->second->getTextureView()); + multiMaterial.addSamplerFunc([=] () { material->applySampler((graphics::Material::MapChannel) NetworkMToonMaterial::UV_ANIMATION_MASK_MAP); }); if (itr->second->getTextureView().isReference()) { multiMaterial.addReferenceTexture(itr->second->getTextureView().getTextureOperator()); } @@ -1289,6 +1306,8 @@ bool RenderPipelines::bindMaterials(graphics::MultiMaterial& multiMaterial, gpu: multiMaterial.setMToonTime(); } + multiMaterial.applySamplers(); + auto textureCache = DependencyManager::get(); static gpu::TextureTablePointer defaultMaterialTextures = std::make_shared();