1// Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "shaderdata_p.h"
5#include "qshaderdata.h"
6#include "qshaderdata_p.h"
7#include <QMetaProperty>
8#include <QMetaObject>
9#include <private/qbackendnode_p.h>
10#include <private/managers_p.h>
11#include <private/nodemanagers_p.h>
12#include <Qt3DRender/private/stringtoint_p.h>
13
14QT_BEGIN_NAMESPACE
15
16using namespace Qt3DCore;
17
18namespace Qt3DRender {
19namespace Render {
20
21namespace {
22
23const int qNodeIdTypeId = qMetaTypeId<Qt3DCore::QNodeId>();
24
25}
26
27ShaderData::ShaderData()
28 : m_managers(nullptr)
29{
30}
31
32ShaderData::~ShaderData()
33{
34}
35
36void ShaderData::setManagers(NodeManagers *managers)
37{
38 m_managers = managers;
39}
40
41void ShaderData::syncFromFrontEnd(const QNode *frontEnd, bool firstTime)
42{
43 const QShaderData *node = qobject_cast<const QShaderData *>(object: frontEnd);
44 if (!node)
45 return;
46 BackendNode::syncFromFrontEnd(frontEnd, firstTime);
47
48 if (firstTime) {
49 m_propertyReader = node->propertyReader();
50 m_blockNameToPropertyValues.clear();
51
52 const QMetaObject *metaObj = node->metaObject();
53 const int propertyOffset = QShaderData::staticMetaObject.propertyOffset();
54 const int propertyCount = metaObj->propertyCount();
55 // Dynamic properties names
56 const auto dynamicPropertyNames = node->dynamicPropertyNames();
57
58 QList<QString> propertyNames;
59 propertyNames.reserve(asize: propertyCount - propertyOffset + dynamicPropertyNames.size());
60
61 // Statiically defined properties
62 for (int i = propertyOffset; i < propertyCount; ++i) {
63 const QMetaProperty pro = metaObj->property(index: i);
64 if (pro.isWritable())
65 propertyNames.push_back(t: QString::fromLatin1(ba: pro.name()));
66 }
67 // Dynamic properties
68 for (const QByteArray &propertyName : dynamicPropertyNames)
69 propertyNames.push_back(t: QString::fromLatin1(ba: propertyName));
70
71 for (const QString &propertyName : propertyNames) {
72 if (propertyName == QStringLiteral("data") ||
73 propertyName == QStringLiteral("objectName") ||
74 propertyName == QStringLiteral("childNodes")) // We don't handle default Node properties
75 continue;
76
77 const QVariant &propertyValue = m_propertyReader->readProperty(v: node->property(name: propertyName.toLatin1()));
78 bool isNode = false;
79 bool isTransformed = false;
80 bool isArray = false;
81
82 // We check if the property is a QNodeId
83 isNode = (propertyValue.userType() == qNodeIdTypeId);
84 // We check if QList<QNodeId>
85 if (propertyValue.userType() == QMetaType::QVariantList) {
86 isArray = true;
87 QVariantList list = propertyValue.value<QVariantList>();
88 if (list.size() > 0 && list.at(i: 0).userType() == qNodeIdTypeId)
89 isNode = true;
90 }
91
92 // We check if property is a Transformed property
93 QString transformedPropertyName;
94 if (propertyValue.userType() == QMetaType::QVector3D) {
95 // if there is a matching QShaderData::TransformType propertyTransformed
96 transformedPropertyName = propertyName + QLatin1String("Transformed");
97 isTransformed = propertyNames.contains(str: transformedPropertyName);
98 if (!isTransformed)
99 transformedPropertyName.clear();
100 }
101 m_originalProperties.insert(key: propertyName, value: { .value: propertyValue, .isNode: isNode, .isArray: isArray, .isTransformed: isTransformed, .transformedPropertyName: transformedPropertyName });
102 }
103 BackendNode::markDirty(changes: AbstractRenderer::ParameterDirty);
104 } else {
105 // Updates
106 if (!m_propertyReader.isNull()) {
107 auto it = m_originalProperties.begin();
108 const auto end = m_originalProperties.end();
109
110 while (it != end) {
111 const QVariant newValue = m_propertyReader->readProperty(v: node->property(name: it.key().toLatin1()));
112 PropertyValue &propValue = it.value();
113 if (propValue.value != newValue) {
114 // Note we aren't notified about nested QShaderData in this call
115 // only scalar / vec properties
116 propValue.value = newValue;
117 BackendNode::markDirty(changes: AbstractRenderer::ParameterDirty);
118 }
119 ++it;
120 }
121 }
122 }
123}
124
125bool ShaderData::hasPropertyValuesForBlock(int blockName) const
126{
127 std::shared_lock readLocker(m_lock);
128 return m_blockNameToPropertyValues.find(x: blockName) != m_blockNameToPropertyValues.cend();
129}
130
131const ShaderData::PropertyValuesForBlock &ShaderData::propertyValuesForBlock(int blockName) const
132{
133 std::shared_lock readLocker(m_lock);
134 return m_blockNameToPropertyValues.at(k: blockName);
135}
136
137void ShaderData::generatePropertyValuesForBlock(const QString &fullBlockName)
138{
139 const QHash<QString, ShaderData::PropertyValue> &props = properties();
140
141 ShaderData::PropertyValuesForBlock valueBlock;
142 valueBlock.reserve(n: props.size());
143
144 auto it = props.cbegin();
145 const auto end = props.cend();
146 while (it != end) {
147 QString propertyName = it.key();
148 // If we are dealing with a nested value, check if it is an an array
149 if (it->isArray && !it->isNode)
150 propertyName += QLatin1String("[0]");
151
152 QString fullPropertyName;
153 fullPropertyName.reserve(asize: fullBlockName.size() + 1 + it.key().size());
154 fullPropertyName.append(s: fullBlockName);
155 fullPropertyName.append(s: QLatin1String("."));
156 fullPropertyName.append(s: propertyName);
157
158 // We only do this for properties on root level
159 valueBlock.push_back(x: { StringToInt::lookupId(str: fullPropertyName),
160 StringToInt::lookupId(str: propertyName),
161 it.operator ->() });
162 ++it;
163 }
164
165 std::unique_lock writeLocker(m_lock);
166 m_blockNameToPropertyValues[StringToInt::lookupId(str: fullBlockName)] = std::move(valueBlock);
167}
168
169ShaderData *ShaderData::lookupResource(NodeManagers *managers, QNodeId id)
170{
171 return managers->shaderDataManager()->lookupResource(id);
172}
173
174ShaderData *ShaderData::lookupResource(QNodeId id)
175{
176 return ShaderData::lookupResource(managers: m_managers, id);
177}
178
179// RenderCommand updater jobs
180QVariant ShaderData::getTransformedProperty(const PropertyValue *v, const Matrix4x4 &viewMatrix) const noexcept
181{
182 // Note protecting m_worldMatrix at this point as we assume all world updates
183 // have been performed when reaching this point
184 if (v->isTransformed) {
185 const auto transformedIt = m_originalProperties.constFind(key: v->transformedPropertyName);
186 if (transformedIt != m_originalProperties.constEnd()) {
187 const PropertyValue &transformedValue = transformedIt.value();
188 const TransformType transformType = static_cast<TransformType>(transformedValue.value.toInt());
189 switch (transformType) {
190 case ModelToEye:
191 return QVariant::fromValue(value: viewMatrix.map(point: m_worldMatrix.map(point: Vector3D(v->value.value<QVector3D>()))));
192 case ModelToWorld:
193 return QVariant::fromValue(value: m_worldMatrix.map(point: Vector3D(v->value.value<QVector3D>())));
194 case ModelToWorldDirection:
195 return QVariant::fromValue(value: Vector3D(m_worldMatrix * Vector4D(v->value.value<QVector3D>(), 0.0f)));
196 case NoTransform:
197 break;
198 }
199 }
200 }
201 return v->value;
202}
203
204// Unit tests only
205ShaderData::TransformType ShaderData::propertyTransformType(const QString &name) const
206{
207 const auto it = m_originalProperties.constFind(key: name);
208 if (it != m_originalProperties.constEnd()) {
209 const PropertyValue &propertyValue = it.value();
210 if (propertyValue.isTransformed) {
211 auto transformedIt = m_originalProperties.constFind(key: name + QLatin1String("Transformed"));
212 if (transformedIt != m_originalProperties.end())
213 return static_cast<TransformType>(transformedIt.value().value.toInt());
214 }
215 }
216 return NoTransform;
217}
218
219// Called by FramePreparationJob or by RenderView when dealing with lights
220void ShaderData::updateWorldTransform(const Matrix4x4 &worldMatrix)
221{
222 if (m_worldMatrix != worldMatrix) {
223 m_worldMatrix = worldMatrix;
224 }
225}
226
227RenderShaderDataFunctor::RenderShaderDataFunctor(AbstractRenderer *renderer, NodeManagers *managers)
228 : m_managers(managers)
229 , m_renderer(renderer)
230{
231}
232
233Qt3DCore::QBackendNode *RenderShaderDataFunctor::create(Qt3DCore::QNodeId id) const
234{
235 ShaderData *backend = m_managers->shaderDataManager()->getOrCreateResource(id);
236 backend->setManagers(m_managers);
237 backend->setRenderer(m_renderer);
238 return backend;
239}
240
241Qt3DCore::QBackendNode *RenderShaderDataFunctor::get(Qt3DCore::QNodeId id) const
242{
243 return m_managers->shaderDataManager()->lookupResource(id);
244}
245
246void RenderShaderDataFunctor::destroy(Qt3DCore::QNodeId id) const
247{
248 m_managers->shaderDataManager()->releaseResource(id);
249}
250
251} // namespace Render
252} // namespace Qt3DRender
253
254QT_END_NAMESPACE
255

source code of qt3d/src/render/materialsystem/shaderdata.cpp