1/****************************************************************************
2**
3** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt3D module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "shaderdata_p.h"
41#include "qshaderdata.h"
42#include "qshaderdata_p.h"
43#include <QMetaProperty>
44#include <QMetaObject>
45#include <Qt3DCore/qdynamicpropertyupdatedchange.h>
46#include <Qt3DCore/qpropertyupdatedchange.h>
47#include <private/qbackendnode_p.h>
48#include <private/managers_p.h>
49#include <private/nodemanagers_p.h>
50
51QT_BEGIN_NAMESPACE
52
53using namespace Qt3DCore;
54
55namespace Qt3DRender {
56namespace Render {
57
58namespace {
59
60const int qNodeIdTypeId = qMetaTypeId<Qt3DCore::QNodeId>();
61
62}
63
64ShaderData::ShaderData()
65 : m_managers(nullptr)
66{
67}
68
69ShaderData::~ShaderData()
70{
71}
72
73void ShaderData::setManagers(NodeManagers *managers)
74{
75 m_managers = managers;
76}
77
78void ShaderData::syncFromFrontEnd(const QNode *frontEnd, bool firstTime)
79{
80 const QShaderData *node = qobject_cast<const QShaderData *>(object: frontEnd);
81 if (!node)
82 return;
83 BackendNode::syncFromFrontEnd(frontEnd, firstTime);
84
85 if (firstTime) {
86 m_propertyReader = node->propertyReader();
87
88 const QMetaObject *metaObj = node->metaObject();
89 const int propertyOffset = QShaderData::staticMetaObject.propertyOffset();
90 const int propertyCount = metaObj->propertyCount();
91 // Dynamic properties names
92 const auto dynamicPropertyNames = node->dynamicPropertyNames();
93
94 QVector<QString> propertyNames;
95 propertyNames.reserve(asize: propertyCount - propertyOffset + dynamicPropertyNames.size());
96
97 // Statiically defined properties
98 for (int i = propertyOffset; i < propertyCount; ++i) {
99 const QMetaProperty pro = metaObj->property(index: i);
100 if (pro.isWritable())
101 propertyNames.push_back(t: QString::fromLatin1(str: pro.name()));
102 }
103 // Dynamic properties
104 for (const QByteArray &propertyName : dynamicPropertyNames)
105 propertyNames.push_back(t: QString::fromLatin1(str: propertyName));
106
107 for (const QString &propertyName : propertyNames) {
108 if (propertyName == QStringLiteral("data") ||
109 propertyName == QStringLiteral("childNodes")) // We don't handle default Node properties
110 continue;
111
112 const QVariant &propertyValue = m_propertyReader->readProperty(v: node->property(name: propertyName.toLatin1()));
113 bool isNested = false;
114 bool isTransformed = false;
115
116 // We check if the property is a QNodeId
117 isNested = (propertyValue.userType() == qNodeIdTypeId);
118 // We check if QVector<QNodeId>
119 if (propertyValue.userType() == QMetaType::QVariantList) {
120 QVariantList list = propertyValue.value<QVariantList>();
121 if (list.count() > 0 && list.at(i: 0).userType() == qNodeIdTypeId)
122 isNested = true;
123 }
124
125 // We check if property is a Transformed property
126 if (propertyValue.userType() == QVariant::Vector3D) {
127 // if there is a matching QShaderData::TransformType propertyTransformed
128 isTransformed = propertyNames.contains(t: propertyName + QLatin1String("Transformed"));
129 }
130 m_originalProperties.insert(akey: propertyName, avalue: { .value: propertyValue, .isNested: isNested, .isTransformed: isTransformed });
131 }
132 BackendNode::markDirty(changes: AbstractRenderer::ParameterDirty);
133 } else {
134 // Updates
135 if (!m_propertyReader.isNull()) {
136 auto it = m_originalProperties.begin();
137 const auto end = m_originalProperties.end();
138
139 while (it != end) {
140 const QVariant newValue = m_propertyReader->readProperty(v: node->property(name: it.key().toLatin1()));
141 PropertyValue &propValue = it.value();
142 if (propValue.value != newValue) {
143 // Note we aren't notified about nested QShaderData in this call
144 // only scalar / vec properties
145 propValue.value = newValue;
146 BackendNode::markDirty(changes: AbstractRenderer::ParameterDirty);
147 }
148 ++it;
149 }
150 }
151 }
152}
153
154ShaderData *ShaderData::lookupResource(NodeManagers *managers, QNodeId id)
155{
156 return managers->shaderDataManager()->lookupResource(id);
157}
158
159ShaderData *ShaderData::lookupResource(QNodeId id)
160{
161 return ShaderData::lookupResource(managers: m_managers, id);
162}
163
164// RenderCommand updater jobs
165QVariant ShaderData::getTransformedProperty(const QString &name, const Matrix4x4 &viewMatrix) const noexcept
166{
167 // Note protecting m_worldMatrix at this point as we assume all world updates
168 // have been performed when reaching this point
169 const auto it = m_originalProperties.constFind(akey: name);
170 if (it != m_originalProperties.constEnd()) {
171 const PropertyValue &propertyValue = it.value();
172 if (propertyValue.isTransformed) {
173 const auto transformedIt = m_originalProperties.constFind(akey: name + QLatin1String("Transformed"));
174 if (transformedIt != m_originalProperties.constEnd()) {
175 const PropertyValue &transformedValue = transformedIt.value();
176 const TransformType transformType = static_cast<TransformType>(transformedValue.value.toInt());
177 switch (transformType) {
178 case ModelToEye:
179 return QVariant::fromValue(value: viewMatrix * m_worldMatrix * Vector3D(propertyValue.value.value<QVector3D>()));
180 case ModelToWorld:
181 return QVariant::fromValue(value: m_worldMatrix * Vector3D(propertyValue.value.value<QVector3D>()));
182 case ModelToWorldDirection:
183 return QVariant::fromValue(value: Vector3D(m_worldMatrix * Vector4D(propertyValue.value.value<QVector3D>(), 0.0f)));
184 case NoTransform:
185 break;
186 }
187 }
188 }
189 return propertyValue.value;
190 }
191 return QVariant();
192}
193
194// Unit tests only
195ShaderData::TransformType ShaderData::propertyTransformType(const QString &name) const
196{
197 const auto it = m_originalProperties.constFind(akey: name);
198 if (it != m_originalProperties.constEnd()) {
199 const PropertyValue &propertyValue = it.value();
200 if (propertyValue.isTransformed) {
201 auto transformedIt = m_originalProperties.constFind(akey: name + QLatin1String("Transformed"));
202 if (transformedIt != m_originalProperties.end())
203 return static_cast<TransformType>(transformedIt.value().value.toInt());
204 }
205 }
206 return NoTransform;
207}
208
209// Called by FramePreparationJob or by RenderView when dealing with lights
210void ShaderData::updateWorldTransform(const Matrix4x4 &worldMatrix)
211{
212 if (m_worldMatrix != worldMatrix) {
213 m_worldMatrix = worldMatrix;
214 }
215}
216
217RenderShaderDataFunctor::RenderShaderDataFunctor(AbstractRenderer *renderer, NodeManagers *managers)
218 : m_managers(managers)
219 , m_renderer(renderer)
220{
221}
222
223Qt3DCore::QBackendNode *RenderShaderDataFunctor::create(const Qt3DCore::QNodeCreatedChangeBasePtr &change) const
224{
225 ShaderData *backend = m_managers->shaderDataManager()->getOrCreateResource(id: change->subjectId());
226 backend->setManagers(m_managers);
227 backend->setRenderer(m_renderer);
228 return backend;
229}
230
231Qt3DCore::QBackendNode *RenderShaderDataFunctor::get(Qt3DCore::QNodeId id) const
232{
233 return m_managers->shaderDataManager()->lookupResource(id);
234}
235
236void RenderShaderDataFunctor::destroy(Qt3DCore::QNodeId id) const
237{
238 m_managers->shaderDataManager()->releaseResource(id);
239}
240
241} // namespace Render
242} // namespace Qt3DRender
243
244QT_END_NAMESPACE
245

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