1732 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesScreenTexture))
1733 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::ScreenTexture,
true);
1734 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesScreenMipTexture))
1735 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::ScreenMipTexture,
true);
1736 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesDepthTexture))
1737 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::DepthTexture,
true);
1738 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesNormalTexture))
1739 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::NormalTexture,
true);
1740 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesAoTexture))
1741 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::AoTexture,
true);
1742 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesProjectionMatrix))
1743 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::ProjectionMatrix,
true);
1744 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesInverseProjectionMatrix))
1745 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::InverseProjectionMatrix,
true);
1746 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesVarColor))
1747 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::VarColor,
true);
1748 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesIblOrientation))
1749 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::IblOrientation,
true);
1750 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesLightmap))
1751 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::Lightmap,
true);
1752 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesSkinning))
1753 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::Skinning,
true);
1754 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesMorphing))
1755 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::Morphing,
true);
1756 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesViewIndex))
1757 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::ViewIndex,
true);
1758 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesClearcoat))
1759 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::Clearcoat,
true);
1760 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesSheen))
1761 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::Sheen,
true);
1762 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesAnisotropy))
1763 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::Anisotropy,
true);
1764 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesIridescence))
1765 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::Iridescence,
true);
1766 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesDispersion))
1767 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::Dispersion,
true);
1768 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesClearcoatFresnelScaleBias))
1769 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::ClearcoatFresnelScaleBias,
true);
1770 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesFresnelScaleBias))
1771 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::FresnelScaleBias,
true);
1772 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesMotionVectorTexture))
1773 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::MotionVectorTexture,
true);
1774 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesTransmission)) {
1775 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::Transmission,
true);
1776 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::ScreenTexture,
true);
1777 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::ScreenMipTexture,
true);
1781 if (meta.flags.testFlag(QSSGCustomShaderMetaData::OverridesPosition))
1782 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::OverridesPosition,
true);
1785 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesSharedVars))
1786 material->m_usesSharedVariables =
true;
1814QSSGRenderGraphObject *QQuick3DCustomMaterial::updateSpatialNode(QSSGRenderGraphObject *node)
1816 using namespace QSSGShaderUtils;
1818 const auto &renderContext = QQuick3DObjectPrivate::get(
this)->sceneManager->wattached->rci();
1819 if (!renderContext) {
1820 qWarning(
"QQuick3DCustomMaterial: No render context interface?");
1824 QSSGShaderCustomMaterialAdapter::StringPairList uniforms;
1825 QSSGRenderCustomMaterial *customMaterial =
static_cast<QSSGRenderCustomMaterial *>(node);
1826 bool newBackendNode =
false;
1827 bool shadersMayChange =
false;
1828 if (!customMaterial) {
1829 customMaterial =
new QSSGRenderCustomMaterial;
1830 newBackendNode =
true;
1831 }
else if (m_dirtyAttributes & ShaderSettingsDirty) {
1832 shadersMayChange =
true;
1835 if (newBackendNode || shadersMayChange) {
1838 customMaterial->m_properties.clear();
1839 customMaterial->m_textureProperties.clear();
1841 customMaterial->m_shadingMode = QSSGRenderCustomMaterial::ShadingMode(
int(m_shadingMode));
1843 QMetaMethod propertyDirtyMethod;
1844 const int idx = metaObject()->indexOfSlot(
"onPropertyDirty()");
1846 propertyDirtyMethod = metaObject()->method(idx);
1848 const int propCount = metaObject()->propertyCount();
1849 int propOffset = metaObject()->propertyOffset();
1852 const QMetaObject *superClass = metaObject()->superClass();
1853 while (superClass && qstrcmp(superClass->className(),
"QQuick3DCustomMaterial") != 0) {
1854 propOffset = superClass->propertyOffset();
1855 superClass = superClass->superClass();
1858 using TextureInputProperty = QPair<QQuick3DShaderUtilsTextureInput *,
const char *>;
1859 QVector<TextureInputProperty> textureProperties;
1861 for (
int i = propOffset; i != propCount; ++i) {
1862 const auto property = metaObject()->property(i);
1863 if (Q_UNLIKELY(!property.isValid()))
1866 const auto name = property.name();
1867 QMetaType propType = property.metaType();
1868 QVariant propValue = property.read(
this);
1869 if (propType == QMetaType(QMetaType::QVariant))
1870 propType = propValue.metaType();
1872 if (propType.id() >= QMetaType::User) {
1873 if (propType.id() == qMetaTypeId<QQuick3DShaderUtilsTextureInput *>()) {
1874 if (QQuick3DShaderUtilsTextureInput *texture = property.read(
this).value<QQuick3DShaderUtilsTextureInput *>())
1875 textureProperties.push_back({texture, name});
1877 }
else if (propType == QMetaType(QMetaType::QObjectStar)) {
1878 if (QQuick3DShaderUtilsTextureInput *texture = qobject_cast<QQuick3DShaderUtilsTextureInput *>(propValue.value<QObject *>()))
1879 textureProperties.push_back({texture, name});
1881 const auto type = uniformType(propType);
1882 if (type != QSSGRenderShaderValue::Unknown) {
1883 uniforms.append({ uniformTypeName(propType), name });
1884 customMaterial->m_properties.push_back({ name, propValue, uniformType(propType), i});
1885 if (newBackendNode) {
1887 if (property.hasNotifySignal() && propertyDirtyMethod.isValid())
1888 connect(
this, property.notifySignal(),
this, propertyDirtyMethod);
1898 const auto processTextureProperty = [&](QQuick3DShaderUtilsTextureInput &texture,
const QByteArray &name) {
1899 texture.name = name;
1901 QSSGRenderCustomMaterial::TextureProperty textureData;
1902 textureData.texInput = &texture;
1903 textureData.name = name;
1904 textureData.shaderDataType = QSSGRenderShaderValue::Texture;
1906 if (newBackendNode) {
1907 connect(&texture, &QQuick3DShaderUtilsTextureInput::enabledChanged,
this, &QQuick3DCustomMaterial::onTextureDirty);
1908 connect(&texture, &QQuick3DShaderUtilsTextureInput::textureChanged,
this, &QQuick3DCustomMaterial::onTextureDirty);
1911 QQuick3DTexture *tex = texture.texture();
1912 if (tex && QQuick3DObjectPrivate::get(tex)->type == QQuick3DObjectPrivate::Type::ImageCube) {
1913 uniforms.append({ QByteArrayLiteral(
"samplerCube"), textureData.name });
1914 }
else if (tex && tex->textureData() && tex->textureData()->depth() > 0) {
1915 uniforms.append({ QByteArrayLiteral(
"sampler3D"), textureData.name });
1916 }
else if (tex && tex->textureProvider() && QQuick3DObjectPrivate::get(tex->textureProvider())->type == QQuick3DObjectPrivate::Type::TextureProvider) {
1917 auto textureProvider =
static_cast<QQuick3DTextureProviderExtension *>(tex->textureProvider());
1918 switch (textureProvider->samplerHint()) {
1919 case QQuick3DTextureProviderExtension::SamplerHint::Sampler2D:
1920 uniforms.append({ QByteArrayLiteral(
"sampler2D"), textureData.name });
1922 case QQuick3DTextureProviderExtension::SamplerHint::Sampler2DArray:
1923 uniforms.append({ QByteArrayLiteral(
"sampler2DArray"), textureData.name });
1925 case QQuick3DTextureProviderExtension::SamplerHint::Sampler3D:
1926 uniforms.append({ QByteArrayLiteral(
"sampler3D"), textureData.name });
1928 case QQuick3DTextureProviderExtension::SamplerHint::SamplerCube:
1929 uniforms.append({ QByteArrayLiteral(
"samplerCube"), textureData.name });
1931 case QQuick3DTextureProviderExtension::SamplerHint::SamplerCubeArray:
1932 uniforms.append({ QByteArrayLiteral(
"samplerCubeArray"), textureData.name });
1934 case QQuick3DTextureProviderExtension::SamplerHint::SamplerBuffer:
1935 uniforms.append({ QByteArrayLiteral(
"samplerBuffer"), textureData.name });
1939 uniforms.append({ QByteArrayLiteral(
"sampler2D"), textureData.name });
1942 customMaterial->m_textureProperties.push_back(textureData);
1945 for (
const auto &textureProperty : std::as_const(textureProperties))
1946 processTextureProperty(*textureProperty.first, textureProperty.second);
1948 if (customMaterial->incompleteBuildTimeObject || (m_dirtyAttributes & DynamicPropertiesDirty)) {
1949 const auto names = dynamicPropertyNames();
1950 for (
const auto &name : names) {
1951 QVariant propValue = property(name.constData());
1952 QMetaType propType = propValue.metaType();
1953 if (propType == QMetaType(QMetaType::QVariant))
1954 propType = propValue.metaType();
1956 if (propType.id() >= QMetaType::User) {
1957 if (propType.id() == qMetaTypeId<QQuick3DShaderUtilsTextureInput *>()) {
1958 if (QQuick3DShaderUtilsTextureInput *texture = propValue.value<QQuick3DShaderUtilsTextureInput *>())
1959 textureProperties.push_back({texture, name});
1961 }
else if (propType.id() == QMetaType::QObjectStar) {
1962 if (QQuick3DShaderUtilsTextureInput *texture = qobject_cast<QQuick3DShaderUtilsTextureInput *>(propValue.value<QObject *>()))
1963 textureProperties.push_back({texture, name});
1965 const auto type = uniformType(propType);
1966 if (type != QSSGRenderShaderValue::Unknown) {
1967 uniforms.append({ uniformTypeName(propType), name });
1968 customMaterial->m_properties.push_back({ name, propValue,
1969 uniformType(propType), -1 });
1974 qWarning(
"No known uniform conversion found for custom material property %s. Skipping", name.constData());
1979 for (
const auto &property : std::as_const(textureProperties))
1980 processTextureProperty(*property.first, property.second);
1983 const QQmlContext *context = qmlContext(
this);
1985 QByteArray fragment;
1986 QByteArray vertexProcessed[2];
1987 QSSGCustomShaderMetaData vertexMeta;
1988 QByteArray fragmentProcessed[2];
1989 QSSGCustomShaderMetaData fragmentMeta;
1990 QByteArray shaderPathKey(
"custom material --");
1992 customMaterial->m_renderFlags = {};
1994 if (!m_vertexShader.isEmpty())
1995 vertex = QSSGShaderUtils::resolveShader(m_vertexShader, context, shaderPathKey);
1996 else if (!m_vertexShaderCode.isEmpty())
1997 vertex = m_vertexShaderCode.toLatin1();
1999 if (!m_fragmentShader.isEmpty())
2000 fragment = QSSGShaderUtils::resolveShader(m_fragmentShader, context, shaderPathKey);
2001 else if (!m_fragmentShaderCode.isEmpty())
2002 fragment = m_fragmentShaderCode.toLatin1();
2009 vertexProcessed[QSSGRenderCustomMaterial::RegularShaderPathKeyIndex] =
2010 prepareCustomShader(customMaterial, uniforms, vertex, QSSGShaderCache::ShaderType::Vertex, vertexMeta,
false);
2011 fragmentProcessed[QSSGRenderCustomMaterial::RegularShaderPathKeyIndex] =
2012 prepareCustomShader(customMaterial, uniforms, fragment, QSSGShaderCache::ShaderType::Fragment, fragmentMeta,
false);
2014 vertexProcessed[QSSGRenderCustomMaterial::MultiViewShaderPathKeyIndex] =
2015 prepareCustomShader(customMaterial, uniforms, vertex, QSSGShaderCache::ShaderType::Vertex, vertexMeta,
true);
2016 fragmentProcessed[QSSGRenderCustomMaterial::MultiViewShaderPathKeyIndex] =
2017 prepareCustomShader(customMaterial, uniforms, fragment, QSSGShaderCache::ShaderType::Fragment, fragmentMeta,
true);
2024 customMaterial->m_customShaderPresence = {};
2025 for (
int i : { QSSGRenderCustomMaterial::RegularShaderPathKeyIndex, QSSGRenderCustomMaterial::MultiViewShaderPathKeyIndex }) {
2026 if (vertexProcessed[i].isEmpty() && fragmentProcessed[i].isEmpty())
2029 const QByteArray key = shaderPathKey +
':' + QCryptographicHash::hash(QByteArray(vertexProcessed[i] + fragmentProcessed[i]), QCryptographicHash::Algorithm::Sha1).toHex();
2031 customMaterial->m_shaderPathKey[i] = key;
2032 if (!vertexProcessed[i].isEmpty()) {
2033 customMaterial->m_customShaderPresence.setFlag(QSSGRenderCustomMaterial::CustomShaderPresenceFlag::Vertex);
2034 renderContext->shaderLibraryManager()->setShaderSource(key, QSSGShaderCache::ShaderType::Vertex, vertexProcessed[i], vertexMeta);
2036 if (!fragmentProcessed[i].isEmpty()) {
2037 customMaterial->m_customShaderPresence.setFlag(QSSGRenderCustomMaterial::CustomShaderPresenceFlag::Fragment);
2038 renderContext->shaderLibraryManager()->setShaderSource(key, QSSGShaderCache::ShaderType::Fragment, fragmentProcessed[i], fragmentMeta);
2043 customMaterial->setAlwaysDirty(m_alwaysDirty);
2044 if (m_srcBlend != BlendMode::NoBlend && m_dstBlend != BlendMode::NoBlend) {
2045 customMaterial->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::Blending,
true);
2046 customMaterial->m_srcBlend = toRhiBlendFactor(m_srcBlend);
2047 customMaterial->m_dstBlend = toRhiBlendFactor(m_dstBlend);
2049 if (m_srcAlphaBlend != BlendMode::NoBlend && m_dstAlphaBlend != BlendMode::NoBlend) {
2050 customMaterial->m_srcAlphaBlend = toRhiBlendFactor(m_srcAlphaBlend);
2051 customMaterial->m_dstAlphaBlend = toRhiBlendFactor(m_dstAlphaBlend);
2053 customMaterial->m_srcAlphaBlend = customMaterial->m_srcBlend;
2054 customMaterial->m_dstAlphaBlend = customMaterial->m_dstBlend;
2057 customMaterial->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::Blending,
false);
2059 customMaterial->m_lineWidth = m_lineWidth;
2061 QQuick3DMaterial::updateSpatialNode(customMaterial);
2063 if (m_dirtyAttributes & Dirty::PropertyDirty) {
2064 for (
auto &prop : customMaterial->m_properties) {
2065 auto p = metaObject()->property(prop.pid);
2066 if (Q_LIKELY(p.isValid()))
2067 prop.value = p.read(
this);
2071 if (m_dirtyAttributes & Dirty::TextureDirty) {
2072 for (QSSGRenderCustomMaterial::TextureProperty &prop : customMaterial->m_textureProperties) {
2073 QQuick3DTexture *tex = prop.texInput->texture();
2075 if (prop.texInput->enabled)
2076 prop.texImage = tex->getRenderImage();
2078 prop.texImage =
nullptr;
2079 prop.minFilterType = tex->minFilter() == QQuick3DTexture::Nearest ? QSSGRenderTextureFilterOp::Nearest
2080 : QSSGRenderTextureFilterOp::Linear;
2081 prop.magFilterType = tex->magFilter() == QQuick3DTexture::Nearest ? QSSGRenderTextureFilterOp::Nearest
2082 : QSSGRenderTextureFilterOp::Linear;
2083 prop.mipFilterType = tex->generateMipmaps() ? (tex->mipFilter() == QQuick3DTexture::Nearest ? QSSGRenderTextureFilterOp::Nearest
2084 : QSSGRenderTextureFilterOp::Linear)
2085 : QSSGRenderTextureFilterOp::None;
2086 prop.horizontalClampType = tex->horizontalTiling() == QQuick3DTexture::Repeat ? QSSGRenderTextureCoordOp::Repeat
2087 : (tex->horizontalTiling() == QQuick3DTexture::ClampToEdge) ? QSSGRenderTextureCoordOp::ClampToEdge
2088 : QSSGRenderTextureCoordOp::MirroredRepeat;
2089 prop.verticalClampType = tex->verticalTiling() == QQuick3DTexture::Repeat ? QSSGRenderTextureCoordOp::Repeat
2090 : (tex->verticalTiling() == QQuick3DTexture::ClampToEdge) ? QSSGRenderTextureCoordOp::ClampToEdge
2091 : QSSGRenderTextureCoordOp::MirroredRepeat;
2092 prop.zClampType = tex->depthTiling() == QQuick3DTexture::Repeat ? QSSGRenderTextureCoordOp::Repeat
2093 : (tex->depthTiling() == QQuick3DTexture::ClampToEdge) ? QSSGRenderTextureCoordOp::ClampToEdge
2094 : QSSGRenderTextureCoordOp::MirroredRepeat;
2096 prop.texImage =
nullptr;
2099 if (tex != prop.lastConnectedTexture) {
2100 prop.lastConnectedTexture = tex;
2101 disconnect(prop.minFilterChangedConn);
2102 disconnect(prop.magFilterChangedConn);
2103 disconnect(prop.mipFilterChangedConn);
2104 disconnect(prop.horizontalTilingChangedConn);
2105 disconnect(prop.verticalTilingChangedConn);
2106 disconnect(prop.depthTilingChangedConn);
2108 prop.minFilterChangedConn = connect(tex, &QQuick3DTexture::minFilterChanged,
this, &QQuick3DCustomMaterial::onTextureDirty);
2109 prop.magFilterChangedConn = connect(tex, &QQuick3DTexture::magFilterChanged,
this, &QQuick3DCustomMaterial::onTextureDirty);
2110 prop.mipFilterChangedConn = connect(tex, &QQuick3DTexture::mipFilterChanged,
this, &QQuick3DCustomMaterial::onTextureDirty);
2111 prop.horizontalTilingChangedConn = connect(tex, &QQuick3DTexture::horizontalTilingChanged,
this, &QQuick3DCustomMaterial::onTextureDirty);
2112 prop.verticalTilingChangedConn = connect(tex, &QQuick3DTexture::verticalTilingChanged,
this, &QQuick3DCustomMaterial::onTextureDirty);
2113 prop.depthTilingChangedConn = connect(tex, &QQuick3DTexture::depthTilingChanged,
this, &QQuick3DCustomMaterial::onTextureDirty);
2119 m_dirtyAttributes = 0;
2121 return customMaterial;