/**************************************************************************** ** ** Copyright (C) 2020 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Quick 3D. ** ** $QT_BEGIN_LICENSE:GPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 or (at your option) any later version ** approved by the KDE Free Qt Foundation. The licenses are as published by ** the Free Software Foundation and appearing in the file LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qquick3dshaderutils_p.h" #include #include #include #include #include QT_BEGIN_NAMESPACE /*! \qmltype Shader \inherits Object \inqmlmodule QtQuick3D \brief Container component for defining shader code used by CustomMaterials and Effects. */ /*! \qmlproperty string Shader::shader Specifies the name of the shader source file. */ /*! \qmlproperty enumeration Shader::stage Specifies the shader stage. The default is \c Shader.Shared \value Shader.Shared The shader can be shared among different stages \value Shader.Vertex The shader is a vertex shader \value Shader.Fragment The shader is a fragment shader \value Shader.Geometry The shader is a geometry shader \value Shader.Compute The shader is a compute shader */ /*! \qmltype ShaderInfo \inherits Object \inqmlmodule QtQuick3D \brief Defines basic information about custom shader code for CustomMaterials. */ /*! \qmlproperty string ShaderInfo::version Specifies the shader code version. */ /*! \qmlproperty string ShaderInfo::type Specifies the shader code type. */ /*! \qmlproperty string ShaderInfo::shaderKey Specifies the options used by the shader using the combination of shader key values. \value ShaderInfo.Diffuse The shader uses diffuse lighting. \value ShaderInfo.Specular The shader uses specular lighting. \value ShaderInfo.Cutout The shader uses alpha cutout. \value ShaderInfo.Refraction The shader uses refraction. \value ShaderInfo.Transparent The shader uses transparency. \value ShaderInfo.Displace The shader uses displacement mapping. \value ShaderInfo.Transmissive The shader uses transmissiveness. \value ShaderInfo.Glossy The shader is default glossy. This is a combination of \c ShaderInfo.Diffuse and \c ShaderInfo.Specular. */ /*! \qmltype TextureInput \inherits Object \inqmlmodule QtQuick3D \brief Defines a texture channel for a Custom Material or an Effect. */ /*! \qmlproperty Texture TextureInput::texture Specifies the Texture to input. */ /*! \qmlproperty bool TextureInput::enabled The property determines if this TextureInput is enabled. */ /*! \qmltype Pass \inherits Object \inqmlmodule QtQuick3D \brief Defines a render pass in the CustomMaterial or the Effect. */ /*! \qmlproperty Buffer Pass::output Specifies the output \l {Buffer}{buffer} of the pass. */ /*! \qmlproperty list Pass::commands Specifies the list of render \l {Command}{commands} of the pass. */ /*! \qmlproperty list Pass::shaders Specifies the list of \l {Shader}{shaders} of the pass. */ /*! \qmltype Command \inherits Object \inqmlmodule QtQuick3D \brief Defines a command to be performed in a pass of a CustomMaterial or an Effect. */ /*! \qmltype BufferInput \inherits Command \inqmlmodule QtQuick3D \brief Defines an input buffer to be used for a pass of a CustomMaterial or an Effect. */ /*! \qmlproperty Buffer BufferInput::buffer Specifies the \l {Buffer}{buffer} used for the parameter. */ /*! \qmlproperty string BufferInput::param Specifies the name of the input parameter in the shader. */ /*! \qmltype BufferBlit \inherits Command \inqmlmodule QtQuick3D \brief Defines a copy operation between two buffers in a pass of a CustomMaterial or an Effect. */ /*! \qmlproperty Buffer BufferBlit::source Specifies the source \l {Buffer}{buffer} of the copy operation. */ /*! \qmlproperty Buffer BufferBlit::destination Specifies the destination \l {Buffer}{buffer} of the copy operation. */ /*! \qmltype Blending \inherits Command \inqmlmodule QtQuick3D \brief Defines the blending state in a pass of a CustomMaterial or an Effect. */ /*! \qmlproperty enumeration Blending::srcBlending Specifies the source blending function. \value Blending.Unknown \value Blending.Zero \value Blending.One \value Blending.SrcColor \value Blending.OneMinusSrcColor \value Blending.DstColor \value Blending.OneMinusDstColor \value Blending.SrcAlpha \value Blending.OneMinusSrcAlpha \value Blending.DstAlpha \value Blending.OneMinusDstAlpha \value Blending.ConstantColor \value Blending.OneMinusConstantColor \value Blending.ConstantAlpha \value Blending.OneMinusConstantAlpha \value Blending.SrcAlphaSaturate */ /*! \qmlproperty enumeration Blending::destBlending Specifies the destination blending function. \value Blending.Unknown \value Blending.Zero \value Blending.One \value Blending.SrcColor \value Blending.OneMinusSrcColor \value Blending.DstColor \value Blending.OneMinusDstColor \value Blending.SrcAlpha \value Blending.OneMinusSrcAlpha \value Blending.DstAlpha \value Blending.OneMinusDstAlpha \value Blending.ConstantColor \value Blending.OneMinusConstantColor \value Blending.ConstantAlpha \value Blending.OneMinusConstantAlpha */ /*! \qmltype Buffer \inherits Object \inqmlmodule QtQuick3D \brief Defines a buffer to be used for a pass of a CustomMaterial or an Effect. */ /*! \qmlproperty enumeration Buffer::format Specifies the buffer format. \value Buffer.Unknown \value Buffer.R8 \value Buffer.R16 \value Buffer.R16F \value Buffer.R32I \value Buffer.R32UI \value Buffer.R32F \value Buffer.RG8 \value Buffer.RGBA8 \value Buffer.RGB8 \value Buffer.SRGB8 \value Buffer.SRGB8A8 \value Buffer.RGB565 \value Buffer.RGBA16F \value Buffer.RG16F \value Buffer.RG32F \value Buffer.RGB32F \value Buffer.RGBA32F \value Buffer.R11G11B10 \value Buffer.RGB9E5 \value Buffer.Depth16 \value Buffer.Depth24 \value Buffer.Depth32 \value Buffer.Depth24Stencil8 */ /*! \qmlproperty enumeration Buffer::textureFilterOperation Specifies the filter operation when a render \l {Pass}{pass} is reading the buffer that is different size as the current output buffer. \value Buffer.Unknown Value not set. \value Buffer.Nearest Use nearest-neighbor. \value Buffer.Linear Use linear filtering. */ /*! \qmlproperty enumeration Buffer::textureCoordOperation Specifies the texture coordinate operation for coordinates outside [0, 1] range. \value Buffer.Unknown Value not set. \value Buffer.ClampToEdge Clamp coordinate to edge. \value Buffer.MirroredRepeat Repeat the coordinate, but flip direction at the beginning and end. \value Buffer.Repeat Repeat the coordinate always from the beginning. */ /*! \qmlproperty real Buffer::sizeMultiplier Specifies the size multiplier of the buffer. \c 1.0 creates buffer with the same size while \c 0.5 creates buffer with width and height halved. */ /*! \qmlproperty enumeration Buffer::bufferFlags Specifies the buffer allocation flags. \value Buffer.None Value not set. \value Buffer.SceneLifetime The buffer is allocated for the whole lifetime of the scene. */ /*! \qmlproperty string Buffer::name Specifies the name of the buffer */ /*! \qmltype RenderState \inherits Command \inqmlmodule QtQuick3D \brief Defines the render state to be disabled in a pass of a CustomMaterial or an Effect. */ /*! \qmlproperty enumeration RenderState::renderState Specifies the render state to enable/disable in a \l {Pass}{pass}. \value RenderState.Unknown \value RenderState.Blend \value RenderState.CullFace \value RenderState.DepthTest \value RenderState.StencilTest \value RenderState.ScissorTest \value RenderState.DepthWrite \value RenderState.Multisample */ /*! \qmlproperty bool RenderState::enable Specifies if the state is enabled or disabled. */ /*! \qmltype CullMode \inherits Command \inqmlmodule QtQuick3D \brief Defines the cull mode for render pass. \since 5.15 \note This command can only be used with the CustomMaterial. */ /*! \qmlproperty enumeration CullMode::cullMode Specifies the culling mode in a \l {Pass}{pass} when \c RenderState.CullFace is enabled. The material culling mode is overridden. \value Material.BackFaceCulling \value Material.FrontFaceCulling \value Material.NoCulling */ /*! \qmltype DepthInput \inherits Command \inqmlmodule QtQuick3D \brief Defines the output texture for the depth buffer. \since 5.15 */ /*! \qmlproperty string DepthInput::param Specifies the name of the texture the depth buffer will bind to. */ /*! \qmltype SetUniformValue \inherits Command \inqmlmodule QtQuick3D \brief Defines a value to be set during a single \l {Pass}{pass}. \since 5.15 \note The value set by this command is will only be set during the \l {Pass}{pass} it occurs in. For consecutive passes the value will be revert to the initial value of the uniform as it was defined in the effect or custom material item. */ /*! \qmlproperty string SetUniformValue::target Specifies the name of the uniform that will have its value changed during the \l {Pass}{pass}. */ /*! \qmlproperty Variant SetUniformValue::value Specifies the value that will be set on the \c target uniform. */ void QSSGShaderUtils::addSnapperSampler(const QByteArray &texName, QByteArray &shaderPrefix) { const char *filter = "linear"; const char *clamp = "clamp"; // Output macro so we can change the set of variables used for this // independent of the // meta data system. shaderPrefix.append("SNAPPER_SAMPLER2D("); shaderPrefix.append(texName); shaderPrefix.append(", "); shaderPrefix.append(texName); shaderPrefix.append(", "); shaderPrefix.append(filter); shaderPrefix.append(", "); shaderPrefix.append(clamp); shaderPrefix.append(", "); shaderPrefix.append("false )\n"); } QByteArray QSSGShaderUtils::resolveShader(const QByteArray &shader, QByteArray &shaderPath, const QObject *qmlObj) { if (!shaderPath.isEmpty()) shaderPath.append('>'); int offset = -1; if (shader.startsWith("qrc:/")) offset = 3; else if (shader.startsWith("file:/")) offset = 6; else if (shader.startsWith(":/")) offset = 0; QString path; if (offset == -1) { QUrl u(QString::fromUtf8(shader)); if (u.isLocalFile()) path = u.toLocalFile(); } if (offset == -1 && path.isEmpty()) path = QString::fromLatin1(":/") + QString::fromLocal8Bit(shader); else path = QString::fromLocal8Bit(shader.constData() + offset); QFile f(path); if (f.open(QIODevice::ReadOnly | QIODevice::Text)) { shaderPath += path.toLatin1(); return f.readAll(); } else if (offset == -1) { // Plain schemeless string can also be a local file instead of resource, so let's try to // load a local file relative to qml context QQmlContext *context = qmlContext(qmlObj); if (context) { QUrl resolvedUrl = context->resolvedUrl(QUrl(QString::fromUtf8(shader))); path = resolvedUrl.toLocalFile(); QFile file(path); if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { shaderPath += path.toLatin1(); return file.readAll(); } } } shaderPath += QByteArrayLiteral("Inline_") + QByteArray::number(qHash(shader, uint(qGlobalQHashSeed()))); return shader; } QByteArray QSSGShaderUtils::mergeShaderCode(const QByteArray &shared, const QByteArray &uniforms, const QByteArray &textures, const QByteArray &vertex, const QByteArray &geometry, const QByteArray &fragment) { QByteArray shaderCode; // Shared if (!shared.isEmpty()) shaderCode.append(shared); if (!textures.isEmpty()) shaderCode.append(textures); if (!uniforms.isEmpty()) shaderCode.append(uniforms); // Vetex shaderCode.append(QByteArrayLiteral("\n#ifdef VERTEX_SHADER\n")); if (!vertex.isEmpty()) shaderCode.append(vertex); else shaderCode.append(QByteArrayLiteral("void vert(){}")); shaderCode.append(QByteArrayLiteral("\n#endif\n")); // Geometry if (!geometry.isEmpty()) { shaderCode.append(QByteArrayLiteral("\n#ifdef USER_GEOMETRY_SHADER\n")); shaderCode.append(geometry); shaderCode.append(QByteArrayLiteral("\n#endif\n")); } // Fragment shaderCode.append(QByteArrayLiteral("\n#ifdef FRAGMENT_SHADER\n")); if (!fragment.isEmpty()) shaderCode.append(fragment); else shaderCode.append(QByteArrayLiteral("void frag(){}")); shaderCode.append(QByteArrayLiteral("\n#endif\n")); return shaderCode; } QQuick3DShaderUtilsBuffer::TextureFormat QQuick3DShaderUtilsBuffer::mapRenderTextureFormat(QSSGRenderTextureFormat::Format fmt) { using TextureFormat = QQuick3DShaderUtilsBuffer::TextureFormat; switch (fmt) { case QSSGRenderTextureFormat::R8: return TextureFormat::R8; case QSSGRenderTextureFormat::R16: return TextureFormat::R16; case QSSGRenderTextureFormat::R16F: return TextureFormat::R16F; case QSSGRenderTextureFormat::R32I: return TextureFormat::R32I; case QSSGRenderTextureFormat::R32UI: return TextureFormat::R32UI; case QSSGRenderTextureFormat::R32F: return TextureFormat::R32F; case QSSGRenderTextureFormat::RG8: return TextureFormat::RG8; case QSSGRenderTextureFormat::RGBA8: return TextureFormat::RGBA8; case QSSGRenderTextureFormat::RGB8: return TextureFormat::RGB8; case QSSGRenderTextureFormat::SRGB8: return TextureFormat::SRGB8; case QSSGRenderTextureFormat::SRGB8A8: return TextureFormat::SRGB8A8; case QSSGRenderTextureFormat::RGB565: return TextureFormat::RGB565; case QSSGRenderTextureFormat::RGBA16F: return TextureFormat::RGBA16F; case QSSGRenderTextureFormat::RG16F: return TextureFormat::RG16F; case QSSGRenderTextureFormat::RG32F: return TextureFormat::RG32F; case QSSGRenderTextureFormat::RGB32F: return TextureFormat::RGB32F; case QSSGRenderTextureFormat::RGBA32F: return TextureFormat::RGBA32F; case QSSGRenderTextureFormat::R11G11B10: return TextureFormat::R11G11B10; case QSSGRenderTextureFormat::RGB9E5: return TextureFormat::RGB9E5; case QSSGRenderTextureFormat::Depth16: return TextureFormat::Depth16; case QSSGRenderTextureFormat::Depth24: return TextureFormat::Depth24; case QSSGRenderTextureFormat::Depth32: return TextureFormat::Depth32; case QSSGRenderTextureFormat::Depth24Stencil8: return TextureFormat::Depth24Stencil8; default: break; } return TextureFormat::Unknown; } QSSGRenderTextureFormat::Format QQuick3DShaderUtilsBuffer::mapTextureFormat(QQuick3DShaderUtilsBuffer::TextureFormat fmt) { using TextureFormat = QQuick3DShaderUtilsBuffer::TextureFormat; switch (fmt) { case TextureFormat::R8: return QSSGRenderTextureFormat::R8; case TextureFormat::R16: return QSSGRenderTextureFormat::R16; case TextureFormat::R16F: return QSSGRenderTextureFormat::R16F; case TextureFormat::R32I: return QSSGRenderTextureFormat::R32I; case TextureFormat::R32UI: return QSSGRenderTextureFormat::R32UI; case TextureFormat::R32F: return QSSGRenderTextureFormat::R32F; case TextureFormat::RG8: return QSSGRenderTextureFormat::RG8; case TextureFormat::RGBA8: return QSSGRenderTextureFormat::RGBA8; case TextureFormat::RGB8: return QSSGRenderTextureFormat::RGB8; case TextureFormat::SRGB8: return QSSGRenderTextureFormat::SRGB8; case TextureFormat::SRGB8A8: return QSSGRenderTextureFormat::SRGB8A8; case TextureFormat::RGB565: return QSSGRenderTextureFormat::RGB565; case TextureFormat::RGBA16F: return QSSGRenderTextureFormat::RGBA16F; case TextureFormat::RG16F: return QSSGRenderTextureFormat::RG16F; case TextureFormat::RG32F: return QSSGRenderTextureFormat::RG32F; case TextureFormat::RGB32F: return QSSGRenderTextureFormat::RGB32F; case TextureFormat::RGBA32F: return QSSGRenderTextureFormat::RGBA32F; case TextureFormat::R11G11B10: return QSSGRenderTextureFormat::R11G11B10; case TextureFormat::RGB9E5: return QSSGRenderTextureFormat::RGB9E5; case TextureFormat::Depth16: return QSSGRenderTextureFormat::Depth16; case TextureFormat::Depth24: return QSSGRenderTextureFormat::Depth24; case TextureFormat::Depth32: return QSSGRenderTextureFormat::Depth32; case TextureFormat::Depth24Stencil8: return QSSGRenderTextureFormat::Depth24Stencil8; default: break; } return QSSGRenderTextureFormat::Unknown; } QQuick3DShaderUtilsBuffer::TextureFormat QQuick3DShaderUtilsBuffer::format() const { return mapRenderTextureFormat(command.m_format.format); } void QQuick3DShaderUtilsBuffer::setFormat(TextureFormat format) { command.m_format = mapTextureFormat(format); } void QQuick3DShaderUtilsRenderPass::qmlAppendCommand(QQmlListProperty *list, QQuick3DShaderUtilsRenderCommand *command) { if (!command) return; QQuick3DShaderUtilsRenderPass *that = qobject_cast(list->object); that->m_commands.push_back(command); } QQuick3DShaderUtilsRenderCommand *QQuick3DShaderUtilsRenderPass::qmlCommandAt(QQmlListProperty *list, int index) { QQuick3DShaderUtilsRenderPass *that = qobject_cast(list->object); return that->m_commands.at(index); } int QQuick3DShaderUtilsRenderPass::qmlCommandCount(QQmlListProperty *list) { QQuick3DShaderUtilsRenderPass *that = qobject_cast(list->object); return that->m_commands.count(); } void QQuick3DShaderUtilsRenderPass::qmlCommandClear(QQmlListProperty *list) { QQuick3DShaderUtilsRenderPass *that = qobject_cast(list->object); that->m_commands.clear(); } QQmlListProperty QQuick3DShaderUtilsRenderPass::commands() { return QQmlListProperty(this, nullptr, QQuick3DShaderUtilsRenderPass::qmlAppendCommand, QQuick3DShaderUtilsRenderPass::qmlCommandCount, QQuick3DShaderUtilsRenderPass::qmlCommandAt, QQuick3DShaderUtilsRenderPass::qmlCommandClear); } void QQuick3DShaderUtilsRenderPass::qmlAppendShader(QQmlListProperty *list, QQuick3DShaderUtilsShader *shader) { if (!shader) return; QQuick3DShaderUtilsRenderPass *that = qobject_cast(list->object); that->m_shaders[int(shader->stage)] = shader; } QQuick3DShaderUtilsShader *QQuick3DShaderUtilsRenderPass::qmlShaderAt(QQmlListProperty *list, int index) { QQuick3DShaderUtilsRenderPass *that = qobject_cast(list->object); return that->m_shaders.at(index); } int QQuick3DShaderUtilsRenderPass::qmlShaderCount(QQmlListProperty *list) { QQuick3DShaderUtilsRenderPass *that = qobject_cast(list->object); return that->m_shaders.count(); } void QQuick3DShaderUtilsRenderPass::qmlShaderClear(QQmlListProperty *list) { QQuick3DShaderUtilsRenderPass *that = qobject_cast(list->object); auto it = that->m_shaders.begin(); const auto end = that->m_shaders.end(); for (;it != end; ++it) *it = nullptr; } QQmlListProperty QQuick3DShaderUtilsRenderPass::shaders() { return QQmlListProperty(this, nullptr, QQuick3DShaderUtilsRenderPass::qmlAppendShader, QQuick3DShaderUtilsRenderPass::qmlShaderCount, QQuick3DShaderUtilsRenderPass::qmlShaderAt, QQuick3DShaderUtilsRenderPass::qmlShaderClear); } void QQuick3DShaderUtilsTextureInput::setTexture(QQuick3DTexture *texture) { if (m_texture == texture) return; QObject *p = parent(); while (p != nullptr) { if (QQuick3DMaterial *mat = qobject_cast(p)) { mat->setDynamicTextureMap(texture, name); break; } else if (QQuick3DEffect *efx = qobject_cast(p)) { efx->setDynamicTextureMap(texture, name); break; } p = p->parent(); } if (p == nullptr) { qWarning("A texture was defined out of Material or Effect"); } m_texture = texture; Q_EMIT textureDirty(this); } QT_END_NAMESPACE