diff --git a/lib/mayaUsd/render/vp2RenderDelegate/material.cpp b/lib/mayaUsd/render/vp2RenderDelegate/material.cpp index ccaeea1a4d..77c225e255 100644 --- a/lib/mayaUsd/render/vp2RenderDelegate/material.cpp +++ b/lib/mayaUsd/render/vp2RenderDelegate/material.cpp @@ -116,6 +116,7 @@ TF_DEFINE_PRIVATE_TOKENS( (sRGB) (raw) (glslfx) + (fallback) (input) (output) @@ -681,6 +682,16 @@ MHWRender::MSamplerStateDesc _GetSamplerStateDesc(const HdMaterialNode& node) #endif } + it = node.parameters.find(_tokens->fallback); + if (it != node.parameters.end()) { + const VtValue& value = it->second; + if (value.IsHolding()) { + const GfVec4f& fallbackValue = value.UncheckedGet(); + float const* value = fallbackValue.data(); + std::copy(value, value + 4, desc.borderColor); + } + } + return desc; } @@ -815,8 +826,11 @@ _LoadUdimTexture(const std::string& path, bool& isColorSpaceSRGB, MFloatArray& u } //! Load texture from the specified path -MHWRender::MTexture* -_LoadTexture(const std::string& path, bool& isColorSpaceSRGB, MFloatArray& uvScaleOffset) +MHWRender::MTexture* _LoadTexture( + const std::string& path, + bool& isColorSpaceSRGB, + MFloatArray& uvScaleOffset, + const HdMaterialNode& node) { MProfilingScope profilingScope( HdVP2RenderDelegate::sProfilerCategory, MProfiler::kColorD_L2, "LoadTexture", path.c_str()); @@ -844,7 +858,32 @@ _LoadTexture(const std::string& path, bool& isColorSpaceSRGB, MFloatArray& uvSca #endif if (!TF_VERIFY(image, "Unable to create an image from %s", path.c_str())) { - return nullptr; + // Create a 1x1 texture of the fallback color, if it was specified: + auto it = node.parameters.find(_tokens->fallback); + if (it == node.parameters.end()) { + return nullptr; + } + const VtValue& value = it->second; + if (!value.IsHolding()) { + return nullptr; + } + + MHWRender::MTextureDescription desc; + desc.setToDefault2DTexture(); + desc.fWidth = 1; + desc.fHeight = 1; + desc.fFormat = MHWRender::kR8G8B8A8_UNORM; + desc.fBytesPerRow = 4; + desc.fBytesPerSlice = desc.fBytesPerRow; + + const GfVec4f& fallbackValue = value.UncheckedGet(); + std::vector texels(4); + for (size_t i = 0; i < 4; ++i) { + float texelValue = std::max(std::min(fallbackValue[i], 1.0f), 0.0f); + texels[i] = static_cast(texelValue * 255.0); + } + isColorSpaceSRGB = false; + return textureMgr->acquireTexture(path.c_str(), desc, texels.data()); } // This image is used for loading pixel data from usdz only and should @@ -2168,7 +2207,7 @@ void HdVP2Material::_UpdateShaderInstance(const HdMaterialNetwork& mat) const std::string& assetPath = val.GetAssetPath(); if (_IsUsdUVTexture(node) && token == _tokens->file) { const HdVP2TextureInfo& info - = _AcquireTexture(!resolvedPath.empty() ? resolvedPath : assetPath); + = _AcquireTexture(!resolvedPath.empty() ? resolvedPath : assetPath, node); MHWRender::MTextureAssignment assignment; assignment.texture = info._texture.get(); @@ -2262,7 +2301,8 @@ void HdVP2Material::_UpdateShaderInstance(const HdMaterialNetwork& mat) /*! \brief Acquires a texture for the given image path. */ -const HdVP2TextureInfo& HdVP2Material::_AcquireTexture(const std::string& path) +const HdVP2TextureInfo& +HdVP2Material::_AcquireTexture(const std::string& path, const HdMaterialNode& node) { const auto it = _textureMap.find(path); if (it != _textureMap.end()) { @@ -2271,7 +2311,7 @@ const HdVP2TextureInfo& HdVP2Material::_AcquireTexture(const std::string& path) bool isSRGB = false; MFloatArray uvScaleOffset; - MHWRender::MTexture* texture = _LoadTexture(path, isSRGB, uvScaleOffset); + MHWRender::MTexture* texture = _LoadTexture(path, isSRGB, uvScaleOffset, node); HdVP2TextureInfo& info = _textureMap[path]; info._texture.reset(texture); diff --git a/lib/mayaUsd/render/vp2RenderDelegate/material.h b/lib/mayaUsd/render/vp2RenderDelegate/material.h index 103234c28b..dd98f4a1a8 100644 --- a/lib/mayaUsd/render/vp2RenderDelegate/material.h +++ b/lib/mayaUsd/render/vp2RenderDelegate/material.h @@ -110,7 +110,7 @@ class HdVP2Material final : public HdMaterial #endif MHWRender::MShaderInstance* _CreateShaderInstance(const HdMaterialNetwork& mat); void _UpdateShaderInstance(const HdMaterialNetwork& mat); - const HdVP2TextureInfo& _AcquireTexture(const std::string& path); + const HdVP2TextureInfo& _AcquireTexture(const std::string& path, const HdMaterialNode& node); #ifdef HDVP2_MATERIAL_CONSOLIDATION_UPDATE_WORKAROUND //! Trigger sync on all Rprims which are listening to changes on this material. diff --git a/test/lib/mayaUsd/render/vp2RenderDelegate/VP2RenderDelegateUSDPreviewSurface/baseline/TestFallbackColor.png b/test/lib/mayaUsd/render/vp2RenderDelegate/VP2RenderDelegateUSDPreviewSurface/baseline/TestFallbackColor.png new file mode 100644 index 0000000000..936048987b Binary files /dev/null and b/test/lib/mayaUsd/render/vp2RenderDelegate/VP2RenderDelegateUSDPreviewSurface/baseline/TestFallbackColor.png differ diff --git a/test/lib/mayaUsd/render/vp2RenderDelegate/testVP2RenderDelegateUSDPreviewSurface.py b/test/lib/mayaUsd/render/vp2RenderDelegate/testVP2RenderDelegateUSDPreviewSurface.py index 0643662210..c9ef8395ad 100644 --- a/test/lib/mayaUsd/render/vp2RenderDelegate/testVP2RenderDelegateUSDPreviewSurface.py +++ b/test/lib/mayaUsd/render/vp2RenderDelegate/testVP2RenderDelegateUSDPreviewSurface.py @@ -194,6 +194,18 @@ def testMetallicF0(self): self.assertSnapshotClose('F0_is_base_{}.png'.format(light_api)) + def testFallbackColor(self): + cmds.file(force=True, new=True) + mayaUtils.loadPlugin("mayaUsdPlugin") + + cmds.xform("persp", t=(0, 0, 5)) + cmds.xform("persp", ro=[0, 0, 0], ws=True) + + testFile = testUtils.getTestScene("UsdPreviewSurface", "TestFallbackColor.usda") + mayaUtils.createProxyFromFile(testFile) + + self.assertSnapshotClose('TestFallbackColor.png') + if __name__ == '__main__': fixturesUtils.runTests(globals()) diff --git a/test/testSamples/UsdPreviewSurface/TestFallbackColor.usda b/test/testSamples/UsdPreviewSurface/TestFallbackColor.usda new file mode 100644 index 0000000000..0bfab407c6 --- /dev/null +++ b/test/testSamples/UsdPreviewSurface/TestFallbackColor.usda @@ -0,0 +1,222 @@ +#usda 1.0 +( + defaultPrim = "pPlane1" + endTimeCode = 1 + framesPerSecond = 24 + metersPerUnit = 0.01 + startTimeCode = 1 + timeCodesPerSecond = 24 + upAxis = "Y" +) + +def Mesh "pPlane1" ( + prepend apiSchemas = ["MaterialBindingAPI"] + kind = "component" +) +{ + uniform bool doubleSided = 1 + float3[] extent = [(-0.5, 0, -0.5), (0.5, 0, 0.5)] + int[] faceVertexCounts = [4] + int[] faceVertexIndices = [0, 1, 3, 2] + rel material:binding = + point3f[] points = [(-0.5, 0, 0.5), (0.5, 0, 0.5), (-0.5, 0, -0.5), (0.5, 0, -0.5)] + texCoord2f[] primvars:st = [(0, 0), (1, 0), (0, 1), (1, 1)] ( + customData = { + dictionary Maya = { + token name = "map1" + } + } + interpolation = "faceVarying" + ) + int[] primvars:st:indices = [0, 1, 3, 2] + float3 xformOp:rotateXYZ = (90, 0, 0) + double3 xformOp:translate = (-1.5, 0, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:rotateXYZ"] + + def Scope "Looks" + { + def Material "usdPreviewSurface1SG" + { + token inputs:file1:varname = "st" + token outputs:surface.connect = + + def Shader "usdPreviewSurface1" + { + uniform token info:id = "UsdPreviewSurface" + color3f inputs:diffuseColor.connect = + token outputs:displacement + token outputs:surface + } + + def Shader "file1" + { + uniform token info:id = "UsdUVTexture" + float4 inputs:fallback = (0, 1, 0, 1) + asset inputs:file = @grid.png@ + token inputs:sourceColorSpace = "sRGB" + float2 inputs:st.connect = + token inputs:wrapS = "black" + token inputs:wrapT = "black" + float3 outputs:rgb + + def Shader "TexCoordReader" + { + uniform token info:id = "UsdPrimvarReader_float2" + token inputs:varname.connect = + float2 outputs:result + } + + def Shader "UsdTransform2d" + { + uniform token info:id = "UsdTransform2d" + float2 inputs:in.connect = + float inputs:rotation = 45 + float2 inputs:scale = (1, 1) + float2 inputs:translation = (0.49999997, -0.20710683) + float2 outputs:result + } + } + } + } +} + +def Mesh "pPlane2" ( + prepend apiSchemas = ["MaterialBindingAPI"] + kind = "component" +) +{ + uniform bool doubleSided = 1 + float3[] extent = [(-0.5, 0, -0.5), (0.5, 0, 0.5)] + int[] faceVertexCounts = [4] + int[] faceVertexIndices = [0, 1, 3, 2] + rel material:binding = + point3f[] points = [(-0.5, 0, 0.5), (0.5, 0, 0.5), (-0.5, 0, -0.5), (0.5, 0, -0.5)] + texCoord2f[] primvars:st = [(0, 0), (1, 0), (0, 1), (1, 1)] ( + customData = { + dictionary Maya = { + token name = "map1" + } + } + interpolation = "faceVarying" + ) + int[] primvars:st:indices = [0, 1, 3, 2] + float3 xformOp:rotateXYZ = (90, 0, 0) + uniform token[] xformOpOrder = ["xformOp:rotateXYZ"] + + def Scope "Looks" + { + def Material "usdPreviewSurface3SG" + { + token inputs:file3:varname = "st" + token outputs:surface.connect = + + def Shader "usdPreviewSurface3" + { + uniform token info:id = "UsdPreviewSurface" + color3f inputs:diffuseColor.connect = + token outputs:displacement + token outputs:surface + } + + def Shader "file3" + { + uniform token info:id = "UsdUVTexture" + float4 inputs:fallback = (1, 1, 0, 1) + asset inputs:file = @does_not_exist.png@ + token inputs:sourceColorSpace = "sRGB" + float2 inputs:st.connect = + token inputs:wrapS = "black" + token inputs:wrapT = "black" + float3 outputs:rgb + + def Shader "TexCoordReader" + { + uniform token info:id = "UsdPrimvarReader_float2" + token inputs:varname.connect = + float2 outputs:result + } + + def Shader "UsdTransform2d" + { + uniform token info:id = "UsdTransform2d" + float2 inputs:in.connect = + float inputs:rotation = 45 + float2 inputs:scale = (1, 1) + float2 inputs:translation = (0.49999997, -0.20710683) + float2 outputs:result + } + } + } + } +} + +def Mesh "pPlane3" ( + prepend apiSchemas = ["MaterialBindingAPI"] + kind = "component" +) +{ + uniform bool doubleSided = 1 + float3[] extent = [(-0.5, 0, -0.5), (0.5, 0, 0.5)] + int[] faceVertexCounts = [4] + int[] faceVertexIndices = [0, 1, 3, 2] + rel material:binding = + point3f[] points = [(-0.5, 0, 0.5), (0.5, 0, 0.5), (-0.5, 0, -0.5), (0.5, 0, -0.5)] + texCoord2f[] primvars:st = [(0, 0), (1, 0), (0, 1), (1, 1)] ( + customData = { + dictionary Maya = { + token name = "map1" + } + } + interpolation = "faceVarying" + ) + int[] primvars:st:indices = [0, 1, 3, 2] + float3 xformOp:rotateXYZ = (90, 0, 0) + double3 xformOp:translate = (1.5, 0, 0) + uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:rotateXYZ"] + + def Scope "Looks" + { + def Material "usdPreviewSurface4SG" + { + token inputs:file4:varname = "st" + token outputs:surface.connect = + + def Shader "usdPreviewSurface4" + { + uniform token info:id = "UsdPreviewSurface" + color3f inputs:diffuseColor.connect = + token outputs:displacement + token outputs:surface + } + + def Shader "file4" + { + uniform token info:id = "UsdUVTexture" + asset inputs:file = @neither_does_this_one.png@ + token inputs:sourceColorSpace = "sRGB" + float2 inputs:st.connect = + token inputs:wrapS = "black" + token inputs:wrapT = "black" + float3 outputs:rgb + + def Shader "TexCoordReader" + { + uniform token info:id = "UsdPrimvarReader_float2" + token inputs:varname.connect = + float2 outputs:result + } + + def Shader "UsdTransform2d" + { + uniform token info:id = "UsdTransform2d" + float2 inputs:in.connect = + float inputs:rotation = 45 + float2 inputs:scale = (1, 1) + float2 inputs:translation = (0.49999997, -0.20710683) + float2 outputs:result + } + } + } + } +} +