summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPasi Keränen <pasi.keranen@qt.io>2019-05-21 22:41:45 +0300
committerPasi Keränen <pasi.keranen@qt.io>2019-05-23 13:48:09 +0300
commit839ad797b34e6cf6d2cd486d4c2ca8159d5f34c5 (patch)
tree2e34bfbc5abfa435af6ee8926c8369dd82642e06
parent87a20071d506485a337340824a91edc6d24f782e (diff)
Add runtime support for DataOutput
Observes attribute changes in the CPresentation attributes and timeline changes for DataInput definitions. Slide changes are ignored as there is already API for slide change notifications. Task-number: QT3DS-3516 Change-Id: I9c384bccae391ddfb0b840ece67d2b412b4f86fe Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io> Reviewed-by: Janne Kangas <janne.kangas@qt.io> Reviewed-by: Antti Määttä <antti.maatta@qt.io> Reviewed-by: Pasi Keränen <pasi.keranen@qt.io>
-rw-r--r--src/Authoring/Studio/Application/PresentationFile.cpp75
-rw-r--r--src/Authoring/Studio/Application/PresentationFile.h4
-rw-r--r--src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.cpp17
-rw-r--r--src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.h2
-rw-r--r--src/Runtime/Source/engine/Qt3DSRuntimeView.cpp13
-rw-r--r--src/Runtime/Source/engine/Qt3DSRuntimeView.h2
-rw-r--r--src/Runtime/Source/foundation/StringTable.h5
-rw-r--r--src/Runtime/Source/runtime/Qt3DSApplication.cpp120
-rw-r--r--src/Runtime/Source/runtime/Qt3DSApplication.h43
-rw-r--r--src/Runtime/Source/runtime/Qt3DSAttributeHashes.cpp3
-rw-r--r--src/Runtime/Source/runtime/Qt3DSAttributeHashes.h3
-rw-r--r--src/Runtime/Source/runtime/Qt3DSAttributeHashes.txt1
-rw-r--r--src/Runtime/Source/runtime/Qt3DSPresentation.cpp77
-rw-r--r--src/Runtime/Source/runtime/Qt3DSPresentation.h9
-rw-r--r--src/Runtime/Source/runtime/Qt3DSQmlEngine.cpp203
-rw-r--r--src/Runtime/Source/uipparser/Qt3DSUIPParserImpl.cpp5
-rw-r--r--src/Runtime/Source/viewer/Qt3DSViewerApp.cpp10
-rw-r--r--src/Runtime/Source/viewer/Qt3DSViewerApp.h2
-rw-r--r--src/Runtime/api/studio3d/q3dscommandqueue.cpp1
-rw-r--r--src/Runtime/api/studio3d/q3dscommandqueue_p.h3
-rw-r--r--src/Runtime/api/studio3d/q3dsdataoutput.cpp118
-rw-r--r--src/Runtime/api/studio3d/q3dsdataoutput.h83
-rw-r--r--src/Runtime/api/studio3d/q3dsdataoutput_p.h75
-rw-r--r--src/Runtime/api/studio3d/q3dspresentation.cpp131
-rw-r--r--src/Runtime/api/studio3d/q3dspresentation.h11
-rw-r--r--src/Runtime/api/studio3d/q3dspresentation_p.h7
-rw-r--r--src/Runtime/api/studio3d/studio3d.pro3
-rw-r--r--src/Runtime/api/studio3dqml/q3dsplugin.cpp1
-rw-r--r--src/Runtime/api/studio3dqml/q3dspresentationitem.cpp21
-rw-r--r--src/Runtime/api/studio3dqml/q3dsrenderer.cpp14
-rw-r--r--src/Runtime/api/studio3dqml/q3dsrenderer.h1
-rw-r--r--src/Runtime/api/studio3dqml/q3dsstudio3d.cpp12
32 files changed, 1001 insertions, 74 deletions
diff --git a/src/Authoring/Studio/Application/PresentationFile.cpp b/src/Authoring/Studio/Application/PresentationFile.cpp
index 21260213..cbc7f417 100644
--- a/src/Authoring/Studio/Application/PresentationFile.cpp
+++ b/src/Authoring/Studio/Application/PresentationFile.cpp
@@ -274,7 +274,8 @@ void PresentationFile::getSourcePaths(const QFileInfo &uipSrc, const QFileInfo &
QHash<QString, QString> &outPathMap,
QString &outProjPathSrc,
QHash<QString, QString> &outPresentationNodes,
- QSet<QString> &outDataInputs)
+ QSet<QString> &outDataInputs,
+ QSet<QString> &outDataOutputs)
{
QDomDocument domDoc;
if (!StudioUtils::readFileToDomDocument(uipTarget.filePath(), domDoc))
@@ -355,11 +356,28 @@ void PresentationFile::getSourcePaths(const QFileInfo &uipSrc, const QFileInfo &
}
};
+ std::function<void(const QDomElement &, bool)> parseDataOutput;
+ parseDataOutput = [&](const QDomElement &elem, bool parseChildren) {
+ const QString obsAtt = elem.attribute(QStringLiteral("observedproperty"));
+ if (!obsAtt.isEmpty()) {
+ const QStringList dataOutputs = obsAtt.split(QLatin1Char('$'));
+ for (auto &dout : dataOutputs) {
+ if (!dout.isEmpty())
+ outDataOutputs.insert(dout.left(dout.indexOf(QLatin1Char(' '))));
+ }
+ }
+ if (parseChildren) {
+ const QDomNodeList children = elem.childNodes();
+ for (int i = 0; i < children.count(); ++i)
+ parseDataOutput(children.at(i).toElement(), true);
+ }
+ };
// mesh files for group imports, materials, and effects are found under <Graph>
QDomElement graphElement = domDoc.documentElement().firstChild()
.firstChildElement(QStringLiteral("Graph"));
parseDataInput(graphElement, true);
+ parseDataOutput(graphElement, true);
QDomNodeList modelElems = graphElement.elementsByTagName(QStringLiteral("Model"));
for (int i = 0; i < modelElems.count(); ++i) {
@@ -571,3 +589,58 @@ bool PresentationFile::getDataInputBindings(const SubPresentationRecord &subpres
}
return true;
}
+
+bool PresentationFile::getDataOutputBindings(const SubPresentationRecord &subpresentation,
+ QMultiMap<QString, QPair<QString, QString>> &outmap)
+{
+ QList<QString> ctrldPropList;
+
+ QString spPath(g_StudioApp.getRenderableAbsolutePath(subpresentation.m_id));
+
+ QDomDocument domDoc;
+ if (!StudioUtils::readFileToDomDocument(spPath, domDoc))
+ return false;
+
+ // search <Graph>
+ QDomElement graphElem = domDoc.documentElement().firstChild()
+ .firstChildElement(QStringLiteral("Graph"));
+ for (QDomElement p = graphElem.firstChild().toElement(); !p.isNull();
+ p = p.nextSibling().toElement()) {
+ QString ctrldPropStr = p.attribute(QStringLiteral("observedproperty"));
+ if (!ctrldPropStr.isEmpty())
+ ctrldPropList.append(ctrldPropStr);
+ }
+ // Search Logic - State - Add
+ QDomNodeList addElems = domDoc.documentElement().firstChild()
+ .firstChildElement(QStringLiteral("Logic"))
+ .elementsByTagName(QStringLiteral("Add"));
+
+ for (int i = 0; i < addElems.count(); ++i) {
+ QDomElement elem = addElems.at(i).toElement();
+ QString ctrldPropStr = elem.attribute(QStringLiteral("observedproperty"));
+ if (!ctrldPropStr.isEmpty())
+ ctrldPropList.append(ctrldPropStr);
+ }
+
+ for (auto dout : qAsConst(ctrldPropList)) {
+ QStringList split = dout.split(QLatin1Char(' '));
+ for (int i = 0; i < split.size(); i += 2) {
+ QString doName = split[i];
+ // Dataoutput names indicated with prefix "$", remove if found.
+ if (doName.startsWith(QLatin1Char('$')))
+ doName = doName.mid(1, doName.size() - 1);
+ QString propName = split[i + 1];
+ // We should find the dataoutputs from the global dataoutput list
+ // parsed out from UIA file, but check just in case and do not insert
+ // if not found.
+ if (g_StudioApp.m_dataInputDialogItems.contains(doName)) {
+ outmap.insert(subpresentation.m_id, QPair<QString, QString>(doName, propName));
+ } else {
+ qWarning() << "Subpresentation" << subpresentation.m_id
+ << "is using dataoutput" << doName << "that is "
+ "not found from the current UIA file";
+ }
+ }
+ }
+ return true;
+}
diff --git a/src/Authoring/Studio/Application/PresentationFile.h b/src/Authoring/Studio/Application/PresentationFile.h
index 5ff13221..1d8aba85 100644
--- a/src/Authoring/Studio/Application/PresentationFile.h
+++ b/src/Authoring/Studio/Application/PresentationFile.h
@@ -45,7 +45,7 @@ public:
static void getSourcePaths(const QFileInfo &uipSrc, const QFileInfo &uipTarget,
QHash<QString, QString> &outPathMap, QString &outRootPath,
QHash<QString, QString> &outPresentationNodes,
- QSet<QString> &outDataInputs);
+ QSet<QString> &outDataInputs, QSet<QString> &outDataOutputs);
static void updatePresentationId(const QString &url, const QString &oldId,
const QString &newId);
static void renameMaterial(const QString &uipPath, const QString &oldName,
@@ -54,6 +54,8 @@ public:
static QString findProjectFile(const QString &uipPath);
static bool getDataInputBindings(const SubPresentationRecord &subpresentation,
QMultiMap<QString, QPair<QString, QString>> &outmap);
+ static bool getDataOutputBindings(const SubPresentationRecord &subpresentation,
+ QMultiMap<QString, QPair<QString, QString>> &outmap);
private:
PresentationFile();
diff --git a/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.cpp b/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.cpp
index 6e859bf3..35048336 100644
--- a/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.cpp
+++ b/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.cpp
@@ -319,9 +319,11 @@ void ProjectFileSystemModel::updateProjectReferences()
// asset files
QString dummyStr;
QHash<QString, QString> dummyMap;
- QSet<QString> dummySet;
+ QSet<QString> dummyDataInputSet;
+ QSet<QString> dummyDataOutputSet;
PresentationFile::getSourcePaths(fi, fi, importPathMap,
- dummyStr, dummyMap, dummySet);
+ dummyStr, dummyMap, dummyDataInputSet,
+ dummyDataOutputSet);
addReferencesFromImportMap();
} else { // qml-stream
QQmlApplicationEngine qmlEngine;
@@ -845,9 +847,12 @@ void ProjectFileSystemModel::importUrl(QDir &targetDir, const QUrl &url,
if (CDialogs::isPresentationFileExtension(extension.toLatin1().data())) {
presentationPath = doc->GetCore()->getProjectFile().getRelativeFilePathTo(destPath);
QSet<QString> dataInputs;
+ QSet<QString> dataOutputs;
importPresentationAssets(fileInfo, QFileInfo(destPath), outPresentationNodes,
- outImportedFiles, dataInputs, outOverrideChoice);
+ outImportedFiles, dataInputs, dataOutputs, outOverrideChoice);
const QString projFile = PresentationFile::findProjectFile(fileInfo.absoluteFilePath());
+
+ // #TODO: Handle DataOutputs QT3DS-3510
QMap<QString, CDataInputDialogItem *> allDataInputs;
ProjectFile::loadDataInputs(projFile, allDataInputs);
for (auto &di : dataInputs) {
@@ -933,12 +938,12 @@ void ProjectFileSystemModel::importUrl(QDir &targetDir, const QUrl &url,
void ProjectFileSystemModel::importPresentationAssets(
const QFileInfo &uipSrc, const QFileInfo &uipTarget,
QHash<QString, QString> &outPresentationNodes, QStringList &outImportedFiles,
- QSet<QString> &outDataInputs, int &outOverrideChoice) const
+ QSet<QString> &outDataInputs, QSet<QString> &outDataOutputs, int &outOverrideChoice) const
{
QHash<QString, QString> importPathMap;
QString projPathSrc; // project absolute path for the source uip
PresentationFile::getSourcePaths(uipSrc, uipTarget, importPathMap, projPathSrc,
- outPresentationNodes, outDataInputs);
+ outPresentationNodes, outDataInputs, outDataOutputs);
const QDir projDir(g_StudioApp.GetCore()->getProjectFile().getProjectPath());
const QDir uipSrcDir = uipSrc.dir();
const QDir uipTargetDir = uipTarget.dir();
@@ -959,7 +964,7 @@ void ProjectFileSystemModel::importPresentationAssets(
// recursively load any uip asset's assets
importPresentationAssets(QFileInfo(srcAssetPath), QFileInfo(targetAssetPath),
outPresentationNodes, outImportedFiles, outDataInputs,
- outOverrideChoice);
+ outDataOutputs, outOverrideChoice);
// update the path in outPresentationNodes to be correctly relative in target project
const QString subId = outPresentationNodes.take(path);
diff --git a/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.h b/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.h
index f8786345..8e9cefb8 100644
--- a/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.h
+++ b/src/Authoring/Studio/Palettes/Project/ProjectFileSystemModel.h
@@ -112,7 +112,7 @@ private:
void importPresentationAssets(const QFileInfo &uipSrc, const QFileInfo &uipTarget,
QHash<QString, QString> &outPresentationNodes,
QStringList &outImportedFiles, QSet<QString> &outDataInputs,
- int &outOverrideChoice) const;
+ QSet<QString> &outDataOutputs,int &outOverrideChoice) const;
void modelRowsInserted(const QModelIndex &parent, int start, int end);
void modelRowsRemoved(const QModelIndex &parent, int start, int end);
diff --git a/src/Runtime/Source/engine/Qt3DSRuntimeView.cpp b/src/Runtime/Source/engine/Qt3DSRuntimeView.cpp
index eb17a9b2..9485a67b 100644
--- a/src/Runtime/Source/engine/Qt3DSRuntimeView.cpp
+++ b/src/Runtime/Source/engine/Qt3DSRuntimeView.cpp
@@ -208,6 +208,7 @@ public:
}
QList<QString> dataInputs() const override;
+ QList<QString> dataOutputs() const override;
float dataInputMax(const QString &name) const override;
float dataInputMin(const QString &name) const override;
@@ -314,6 +315,10 @@ bool CRuntimeView::InitializeGraphics(const QSurfaceFormat &format, bool delayed
signalProxy(), &QRuntimeViewSignalProxy::SigElementsCreated);
QObject::connect(m_Presentation->signalProxy(), &QPresentationSignalProxy::SigMaterialsCreated,
signalProxy(), &QRuntimeViewSignalProxy::SigMaterialsCreated);
+ QObject::connect(m_Presentation->signalProxy(),
+ &QPresentationSignalProxy::SigDataOutputValueUpdated,
+ signalProxy(),
+ &QRuntimeViewSignalProxy::SigDataOutputValueUpdated);
m_TimeProvider.Reset();
return true;
@@ -604,6 +609,14 @@ QList<QString> CRuntimeView::dataInputs() const
return {};
}
+QList<QString> CRuntimeView::dataOutputs() const
+{
+ if (m_Application)
+ return m_Application->dataOutputs();
+
+ return {};
+}
+
float CRuntimeView::dataInputMax(const QString &name) const
{
if (m_Application)
diff --git a/src/Runtime/Source/engine/Qt3DSRuntimeView.h b/src/Runtime/Source/engine/Qt3DSRuntimeView.h
index 38d30fa7..ecf7b252 100644
--- a/src/Runtime/Source/engine/Qt3DSRuntimeView.h
+++ b/src/Runtime/Source/engine/Qt3DSRuntimeView.h
@@ -60,6 +60,7 @@ Q_SIGNALS:
void SigPresentationReady();
void SigElementsCreated(const QStringList &elementPaths, const QString &error);
void SigMaterialsCreated(const QStringList &materialNames, const QString &error);
+ void SigDataOutputValueUpdated(const QString &name, const QVariant &value);
};
namespace qt3ds {
@@ -192,6 +193,7 @@ public:
virtual void SetDataInputValue(const QString &name, const QVariant &value,
int property) = 0;
virtual QList<QString> dataInputs() const = 0;
+ virtual QList<QString> dataOutputs() const = 0;
virtual float dataInputMax(const QString &name) const = 0;
virtual float dataInputMin(const QString &name) const = 0;
virtual void createElements(const QString &parentElementPath, const QString &slideName,
diff --git a/src/Runtime/Source/foundation/StringTable.h b/src/Runtime/Source/foundation/StringTable.h
index 123992cc..aa57857a 100644
--- a/src/Runtime/Source/foundation/StringTable.h
+++ b/src/Runtime/Source/foundation/StringTable.h
@@ -178,6 +178,11 @@ namespace foundation {
}
};
+ inline uint qHash(const CRegisteredString &rString)
+ {
+ return eastl::hash<uint>()(reinterpret_cast<size_t>(rString.c_str()));
+ }
+
class IStringTable;
class CStringHandle
diff --git a/src/Runtime/Source/runtime/Qt3DSApplication.cpp b/src/Runtime/Source/runtime/Qt3DSApplication.cpp
index 5beaba36..ed74b33f 100644
--- a/src/Runtime/Source/runtime/Qt3DSApplication.cpp
+++ b/src/Runtime/Source/runtime/Qt3DSApplication.cpp
@@ -644,6 +644,7 @@ struct SApp : public IApplication
bool m_createSuccessful;
DataInputMap m_dataInputDefs;
+ DataOutputMap m_dataOutputDefs;
bool m_initialFrame = true;
@@ -1001,6 +1002,55 @@ struct SApp : public IApplication
m_CoreFactory->GetScriptEngineQml().StepGC();
}
+ void NotifyDataOutputs()
+ {
+ {
+ SStackPerfTimer __updateTimer(m_RuntimeFactory->GetPerfTimer(),
+ "NotifyDataOutputs");
+
+ // Allow presentations to notify of registered data output changes
+ for (QT3DSU32 idx = 0, end = m_OrderedAssets.size(); idx < end; ++idx) {
+ if (m_OrderedAssets[idx].second->getType() == AssetValueTypes::Presentation) {
+ SPresentationAsset &asset(
+ *m_OrderedAssets[idx].second->getDataPtr<SPresentationAsset>());
+ CPresentation *presentation = asset.m_Presentation;
+ // allow PostUpdate also for inactive presentations so that we can
+ // activate it
+ if (presentation)
+ presentation->NotifyDataOutputs();
+ }
+ }
+
+ // Notify @timeline attribute changes and store latest value to notified DataOutputDef
+ QMutableMapIterator<QString, DataOutputDef> iter(m_dataOutputDefs);
+ while (iter.hasNext()) {
+ DataOutputDef &outDef = iter.value();
+ if (outDef.observedAttribute.propertyType == ATTRIBUTETYPE_DATAINPUT_TIMELINE
+ && outDef.timelineComponent) {
+ qreal newValue = outDef.timelineComponent->GetTimePolicy().GetTime();
+ qreal timelineEndTime
+ = outDef.timelineComponent->GetTimePolicy().GetLoopingDuration();
+
+ // Normalize the value to dataOutput range (if defined)
+ if (outDef.min < outDef.max && timelineEndTime != 0.0) {
+ newValue = (newValue/timelineEndTime) * qreal(outDef.max - outDef.min);
+ newValue += qreal(outDef.min);
+ } else {
+ // Normalize to milliseconds
+ newValue *= 1000.0;
+ }
+
+ if (!outDef.value.isValid() || newValue != outDef.value.toReal()) {
+ outDef.value.setValue(newValue);;
+ GetPrimaryPresentation()->signalProxy()->SigDataOutputValueUpdated(
+ outDef.name, outDef.value);
+ }
+ }
+ iter.next();
+ }
+ } // End SStackPerfTimer scope
+ }
+
void UpdateScenes() { m_RuntimeFactory->GetSceneManager().Update(); }
void Render()
@@ -1057,6 +1107,9 @@ struct SApp : public IApplication
Render();
m_InputEnginePtr->ClearInputFrame();
+
+ NotifyDataOutputs();
+
ClearPresentationDirtyLists();
if (!m_CoreFactory->GetEventSystem().GetAndClearEventFetchedFlag())
@@ -1260,28 +1313,67 @@ struct SApp : public IApplication
inReader.Att("max", diDef.max);
inReader.UnregisteredAtt("evaluator", evaluator);
if (AreEqual(type, "Ranged Number"))
- diDef.type = DataInputTypeRangedNumber;
+ diDef.type = DataInOutTypeRangedNumber;
else if (AreEqual(type, "String"))
- diDef.type = DataInputTypeString;
+ diDef.type = DataInOutTypeString;
else if (AreEqual(type, "Float"))
- diDef.type = DataInputTypeFloat;
+ diDef.type = DataInOutTypeFloat;
else if (AreEqual(type, "Vector4"))
- diDef.type = DataInputTypeVector4;
+ diDef.type = DataInOutTypeVector4;
else if (AreEqual(type, "Vector3"))
- diDef.type = DataInputTypeVector3;
+ diDef.type = DataInOutTypeVector3;
else if (AreEqual(type, "Vector2"))
- diDef.type = DataInputTypeVector2;
+ diDef.type = DataInOutTypeVector2;
else if (AreEqual(type, "Boolean"))
- diDef.type = DataInputTypeBoolean;
+ diDef.type = DataInOutTypeBoolean;
else if (AreEqual(type, "Variant"))
- diDef.type = DataInputTypeVariant;
+ diDef.type = DataInOutTypeVariant;
if (AreEqual(type, "Evaluator")) {
- diDef.type = DataInputTypeEvaluator;
+ diDef.type = DataInOutTypeEvaluator;
diDef.evaluator = QString::fromUtf8(evaluator);
}
m_dataInputDefs.insert(QString::fromUtf8(name), diDef);
+// #TODO Remove below once QT3DS-3510 task has been completed.
+ // By default data inputs should not have data outputs, but this is needed
+ // until editor can support configuring data nodes as in/out/in-out types
+ DataOutputDef outDef;
+ outDef.type = diDef.type;
+ outDef.value = diDef.value;
+ outDef.name = QString::fromUtf8(name);
+ m_dataOutputDefs.insert(QString::fromUtf8(name), outDef);
+// #TODO Remove above once QT3DS-3510 UI change has been done
+ } else if (AreEqual(assetName, "dataOutput")) {
+ DataOutputDef outDef;
+ const char8_t *name = "";
+ const char8_t *type = "";
+ outDef.value = QVariant::Invalid;
+ inReader.UnregisteredAtt("name", name);
+ inReader.UnregisteredAtt("type", type);
+ inReader.Att("min", outDef.min);
+ inReader.Att("max", outDef.max);
+ if (type) {
+ if (AreEqual(type, "Ranged Number"))
+ outDef.type = DataInOutTypeRangedNumber;
+ else if (AreEqual(type, "String"))
+ outDef.type = DataInOutTypeString;
+ else if (AreEqual(type, "Float"))
+ outDef.type = DataInOutTypeFloat;
+ else if (AreEqual(type, "Vector4"))
+ outDef.type = DataInOutTypeVector4;
+ else if (AreEqual(type, "Vector3"))
+ outDef.type = DataInOutTypeVector3;
+ else if (AreEqual(type, "Vector2"))
+ outDef.type = DataInOutTypeVector2;
+ else if (AreEqual(type, "Boolean"))
+ outDef.type = DataInOutTypeBoolean;
+ else if (AreEqual(type, "Variant"))
+ outDef.type = DataInOutTypeVariant;
+ }
+
+ outDef.name = QString::fromUtf8(name);
+ m_dataOutputDefs.insert(QString::fromUtf8(name), outDef);
} else if (AreEqual(assetName, "renderplugin")) {
const char8_t *pluginArgs = "";
inReader.UnregisteredAtt("args", pluginArgs);
@@ -1314,11 +1406,21 @@ struct SApp : public IApplication
return m_dataInputDefs;
}
+ DataOutputMap &dataOutputMap() override
+ {
+ return m_dataOutputDefs;
+ }
+
QList<QString> dataInputs() const override
{
return m_dataInputDefs.keys();
}
+ QList<QString> dataOutputs() const override
+ {
+ return m_dataOutputDefs.keys();
+ }
+
float dataInputMax(const QString &name) const override
{
return m_dataInputDefs[name].max;
diff --git a/src/Runtime/Source/runtime/Qt3DSApplication.h b/src/Runtime/Source/runtime/Qt3DSApplication.h
index cf1b268d..39cd3c4b 100644
--- a/src/Runtime/Source/runtime/Qt3DSApplication.h
+++ b/src/Runtime/Source/runtime/Qt3DSApplication.h
@@ -84,24 +84,24 @@ public:
virtual void Run() = 0;
};
-struct DataInputControlledAttribute
+struct DataInOutAttribute
{
QByteArray elementPath;
QVector<QByteArray> attributeName;
Q3DStudio::EAttributeType propertyType = Q3DStudio::ATTRIBUTETYPE_NONE;
};
-enum DataInputType {
- DataInputTypeInvalid = 0,
- DataInputTypeRangedNumber,
- DataInputTypeString,
- DataInputTypeFloat,
- DataInputTypeEvaluator,
- DataInputTypeBoolean,
- DataInputTypeVector4,
- DataInputTypeVector3,
- DataInputTypeVector2,
- DataInputTypeVariant
+enum DataInOutType {
+ DataInOutTypeInvalid = 0,
+ DataInOutTypeRangedNumber,
+ DataInOutTypeString,
+ DataInOutTypeFloat,
+ DataInOutTypeEvaluator,
+ DataInOutTypeBoolean,
+ DataInOutTypeVector4,
+ DataInOutTypeVector3,
+ DataInOutTypeVector2,
+ DataInOutTypeVariant
};
// Duplicated from Q3DSDataInput class on viewer side
@@ -113,8 +113,8 @@ enum class DataInputValueRole {
struct DataInputDef
{
- QVector<DataInputControlledAttribute> controlledAttributes;
- DataInputType type = DataInputTypeInvalid;
+ QVector<DataInOutAttribute> controlledAttributes;
+ DataInOutType type = DataInOutTypeInvalid;
float min = 0.0f;
float max = 0.0f;
QString evaluator;
@@ -126,7 +126,20 @@ struct DataInputDef
};
+struct DataOutputDef
+{
+ DataInOutAttribute observedAttribute;
+ DataInOutType type = DataInOutTypeInvalid;
+ QVariant value; // most recently notified value
+ QString name;
+ QT3DSU32 observedHandle;
+ float min = 0.0f;
+ float max = 0.0f;
+ Q3DStudio::TComponent *timelineComponent; // Valid only for @timeline
+};
+
typedef QMap<QString, DataInputDef> DataInputMap;
+typedef QMap<QString, DataOutputDef> DataOutputMap;
class QT3DS_AUTOTEST_EXPORT IApplication : public NVRefCounted
{
@@ -167,8 +180,10 @@ public:
virtual bool createSuccessful() = 0;
virtual DataInputMap &dataInputMap() = 0;
+ virtual DataOutputMap &dataOutputMap() = 0;
virtual QList<QString> dataInputs() const = 0;
+ virtual QList<QString> dataOutputs() const = 0;
virtual float dataInputMax(const QString &name) const = 0;
diff --git a/src/Runtime/Source/runtime/Qt3DSAttributeHashes.cpp b/src/Runtime/Source/runtime/Qt3DSAttributeHashes.cpp
index 766ce9ed..1daddc10 100644
--- a/src/Runtime/Source/runtime/Qt3DSAttributeHashes.cpp
+++ b/src/Runtime/Source/runtime/Qt3DSAttributeHashes.cpp
@@ -1,7 +1,7 @@
/****************************************************************************
**
** Copyright (C) 1993-2009 NVIDIA Corporation.
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt 3D Studio.
@@ -282,6 +282,7 @@ const char *GetAttributeString(const EAttribute inAttribute)
case ATTRIBUTE_PARTICLESIZE: return "particlesize";
case ATTRIBUTE_LIFETIME: return "lifetime";
case ATTRIBUTE_CONTROLLEDPROPERTY: return "controlledproperty";
+ case ATTRIBUTE_OBSERVEDPROPERTY: return "observedproperty";
case ATTRIBUTE_QT_IO: return "qt.io";
default: {
static char s_UnknownHash[16];
diff --git a/src/Runtime/Source/runtime/Qt3DSAttributeHashes.h b/src/Runtime/Source/runtime/Qt3DSAttributeHashes.h
index b2aa2280..1f712a8c 100644
--- a/src/Runtime/Source/runtime/Qt3DSAttributeHashes.h
+++ b/src/Runtime/Source/runtime/Qt3DSAttributeHashes.h
@@ -1,7 +1,7 @@
/****************************************************************************
**
** Copyright (C) 1993-2009 NVIDIA Corporation.
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt 3D Studio.
@@ -273,6 +273,7 @@ enum EAttribute {
ATTRIBUTE_PARTICLESIZE = 0x02534279, // particlesize
ATTRIBUTE_LIFETIME = 0x0033D297, // lifetime
ATTRIBUTE_CONTROLLEDPROPERTY = 0x022C0A1D, // controlledproperty
+ ATTRIBUTE_OBSERVEDPROPERTY = 0x02D1CE03, // observedproperty
ATTRIBUTE_QT_IO = 0x010EF2CF, // qt.io
}; // enum EAttribute
diff --git a/src/Runtime/Source/runtime/Qt3DSAttributeHashes.txt b/src/Runtime/Source/runtime/Qt3DSAttributeHashes.txt
index 6a9508ef..4168bbdc 100644
--- a/src/Runtime/Source/runtime/Qt3DSAttributeHashes.txt
+++ b/src/Runtime/Source/runtime/Qt3DSAttributeHashes.txt
@@ -246,3 +246,4 @@ particlesize
lifetime
controlledproperty
+observedproperty
diff --git a/src/Runtime/Source/runtime/Qt3DSPresentation.cpp b/src/Runtime/Source/runtime/Qt3DSPresentation.cpp
index dd63393d..0792119b 100644
--- a/src/Runtime/Source/runtime/Qt3DSPresentation.cpp
+++ b/src/Runtime/Source/runtime/Qt3DSPresentation.cpp
@@ -43,8 +43,12 @@
#include "Qt3DSSlideSystem.h"
#include "Qt3DSLogicSystem.h"
#include "Qt3DSParametersSystem.h"
+#include "Qt3DSApplication.h"
#include <QtCore/qfileinfo.h>
+#include <QtGui/qvector4d.h>
+#include <QtGui/qvector3d.h>
+#include <QtGui/qvector2d.h>
namespace Q3DStudio {
@@ -221,6 +225,79 @@ void CPresentation::PostUpdate(const TTimeUnit inGlobalTime)
m_PreviousGlobalTime = inGlobalTime;
}
+void CPresentation::NotifyDataOutputs()
+{
+ if (m_pathToDataOutMap.size() == 0)
+ return;
+
+ // Based on the dirty list, check if we need to fire DataOutput notifications
+ Q3DStudio::TElementList &dirtyList = GetFrameData().GetDirtyList();
+ for (int idx = 0, end = dirtyList.GetCount(); idx < end; ++idx) {
+ Q3DStudio::TElement &element = *dirtyList[idx];
+ if (m_pathToDataOutMap.contains(element.m_Path)) {
+ auto outDefIter = m_pathToDataOutMap.find(element.m_Path);
+
+ while (outDefIter != m_pathToDataOutMap.end() && outDefIter.key() == element.m_Path) {
+ qt3ds::runtime::DataOutputDef &outDef = outDefIter.value();
+
+ // Get current value
+ Q3DStudio::UVariant value;
+ qt3ds::QT3DSU32 attribHash
+ = CHash::HashAttribute(outDef.observedAttribute.attributeName[0]);
+ element.GetAttribute(attribHash, value);
+ QVariant qvar;
+ switch (outDef.observedAttribute.propertyType) {
+ case ATTRIBUTETYPE_INT32:
+ qvar.setValue(value.m_INT32);
+ break;
+ case ATTRIBUTETYPE_FLOAT:
+ qvar.setValue(value.m_FLOAT);
+ break;
+ case ATTRIBUTETYPE_BOOL:
+ qvar.setValue(value.m_INT32);
+ break;
+ case ATTRIBUTETYPE_STRING:
+ qvar.setValue(QString::fromUtf8(
+ GetStringTable().HandleToStr(value.m_StringHandle).c_str()));
+ break;
+ case ATTRIBUTETYPE_FLOAT4: {
+ QVector4D qvalue(value.m_FLOAT4[0], value.m_FLOAT4[1],
+ value.m_FLOAT4[2], value.m_FLOAT4[3]);
+ qvar.setValue(qvalue);
+ }
+ break;
+ case ATTRIBUTETYPE_FLOAT3: {
+ QVector3D qvalue(value.m_FLOAT3[0], value.m_FLOAT3[1], value.m_FLOAT3[2]);
+ qvar.setValue(qvalue);
+ }
+ break;
+ case ATTRIBUTETYPE_FLOAT2: {
+ QVector2D qvalue(value.m_FLOAT3[0], value.m_FLOAT3[1]);
+ qvar.setValue(qvalue);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (qvar.isValid() && (outDef.value != qvar)) {
+ outDef.value.setValue(qvar);
+ m_SignalProxy.SigDataOutputValueUpdated(outDef.name, outDef.value);
+ }
+
+ ++outDefIter;
+ }
+ }
+ }
+}
+
+void CPresentation::AddToDataOutputMap(const QHash<qt3ds::foundation::CRegisteredString,
+ qt3ds::runtime::DataOutputDef> &doMap)
+{
+ if (doMap.size() > 0)
+ m_pathToDataOutMap.unite(doMap);
+}
+
/**
* Process the Event/Command queue completely
* PostEventCommand is the method that will add new Event/Command to the queue.
diff --git a/src/Runtime/Source/runtime/Qt3DSPresentation.h b/src/Runtime/Source/runtime/Qt3DSPresentation.h
index b2edd416..76b0a6e7 100644
--- a/src/Runtime/Source/runtime/Qt3DSPresentation.h
+++ b/src/Runtime/Source/runtime/Qt3DSPresentation.h
@@ -53,12 +53,14 @@ Q_SIGNALS:
void SigPresentationReady();
void SigElementsCreated(const QStringList &elementPaths, const QString &error);
void SigMaterialsCreated(const QStringList &materialNames, const QString &error);
+ void SigDataOutputValueUpdated(const QString &name, const QVariant &value);
};
namespace qt3ds {
namespace runtime {
class IApplication;
class IActivityZone;
+ struct DataOutputDef;
}
}
@@ -131,6 +133,8 @@ public: // Execution
void EndUpdate() override;
// Run behaviors.
void PostUpdate(const TTimeUnit inGlobalTime) override;
+ // Notify DataUpdates if any are registered to this presentation
+ void NotifyDataOutputs();
public: // Bridge Control
IScene *GetScene() const override;
@@ -159,6 +163,10 @@ public: // Commands and Events
QPresentationSignalProxy *signalProxy();
+public: // Data Output
+ void AddToDataOutputMap(const QHash<qt3ds::foundation::CRegisteredString,
+ qt3ds::runtime::DataOutputDef> &doMap);
+
public: // Event Callbacks
void RegisterEventCallback(TElement *inElement, const TEventCommandHash inEventHash,
const TEventCallback inCallback, void *inContextData) override;
@@ -217,6 +225,7 @@ private: // Disabled Copy Construction
CPresentation &operator=(const CPresentation &);
private:
QPresentationSignalProxy m_SignalProxy;
+ QHash<qt3ds::foundation::CRegisteredString, qt3ds::runtime::DataOutputDef> m_pathToDataOutMap;
};
} // namespace Q3DStudio
diff --git a/src/Runtime/Source/runtime/Qt3DSQmlEngine.cpp b/src/Runtime/Source/runtime/Qt3DSQmlEngine.cpp
index 6fd3090c..ac1c994a 100644
--- a/src/Runtime/Source/runtime/Qt3DSQmlEngine.cpp
+++ b/src/Runtime/Source/runtime/Qt3DSQmlEngine.cpp
@@ -467,6 +467,7 @@ private:
TElement *getTarget(const char *component);
void listAllElements(TElement *root, QList<TElement *> &elements);
void initializeDataInputsInPresentation(CPresentation &presentation, bool isPrimary);
+ void initializeDataOutputsInPresentation(CPresentation &presentation, bool isPrimary);
// Splits down vector attributes to components as Runtime does not really
// handle vectors at this level anymore
bool getAttributeVector3(QVector<QByteArray> &outAttVec, const QByteArray &attName,
@@ -578,8 +579,10 @@ void CQmlEngineImpl::Initialize()
// Gather data input controlled properties
QList<CPresentation *> presentations = m_Application->GetPresentationList();
- for (int i = 0; i < presentations.size(); ++i)
+ for (int i = 0; i < presentations.size(); ++i) {
initializeDataInputsInPresentation(*presentations[i], i == 0);
+ initializeDataOutputsInPresentation(*presentations[i], i == 0);
+ }
}
void CQmlEngineImpl::SetAttribute(TElement *target, const char *attName, const char *value)
@@ -711,13 +714,13 @@ void CQmlEngineImpl::SetDataInputValue(
switch (valueRole) {
case qt3ds::runtime::DataInputValueRole::Value: { // switch (valueRole)
diDef.value = value;
- const QVector<qt3ds::runtime::DataInputControlledAttribute> &ctrlAtt
+ const QVector<qt3ds::runtime::DataInOutAttribute> &ctrlAtt
= diDef.controlledAttributes;
- for (const qt3ds::runtime::DataInputControlledAttribute &ctrlElem : ctrlAtt) {
+ for (const qt3ds::runtime::DataInOutAttribute &ctrlElem : ctrlAtt) {
switch (ctrlElem.propertyType) {
case ATTRIBUTETYPE_DATAINPUT_TIMELINE: {
// Quietly ignore other than number type data inputs when adjusting timeline
- if (diDef.type == qt3ds::runtime::DataInputTypeRangedNumber) {
+ if (diDef.type == qt3ds::runtime::DataInOutTypeRangedNumber) {
TTimeUnit endTime = 0;
TElement *element = getTarget(ctrlElem.elementPath.constData());
TComponent *component = static_cast<TComponent *>(element);
@@ -732,7 +735,7 @@ void CQmlEngineImpl::SetDataInputValue(
}
case ATTRIBUTETYPE_DATAINPUT_SLIDE: {
// Quietly ignore other than string type when adjusting slide
- if (diDef.type == qt3ds::runtime::DataInputTypeString) {
+ if (diDef.type == qt3ds::runtime::DataInOutTypeString) {
const QByteArray valueStr = value.toString().toUtf8();
GotoSlide(ctrlElem.elementPath.constData(), valueStr.constData(),
SScriptEngineGotoSlideArgs());
@@ -752,11 +755,11 @@ void CQmlEngineImpl::SetDataInputValue(
// than timeline animation i.e. disregard range min and max
case ATTRIBUTETYPE_FLOAT: {
float valueFloat;
- if (diDef.type == qt3ds::runtime::DataInputTypeFloat
- || diDef.type == qt3ds::runtime::DataInputTypeRangedNumber
- || diDef.type == qt3ds::runtime::DataInputTypeVariant) {
+ if (diDef.type == qt3ds::runtime::DataInOutTypeFloat
+ || diDef.type == qt3ds::runtime::DataInOutTypeRangedNumber
+ || diDef.type == qt3ds::runtime::DataInOutTypeVariant) {
valueFloat = value.toFloat();
- } else if (diDef.type == qt3ds::runtime::DataInputTypeEvaluator) {
+ } else if (diDef.type == qt3ds::runtime::DataInOutTypeEvaluator) {
valueFloat = callJSFunc(name, diDef, QVariant::Type::Double).toFloat();
} else {
qWarning() << __FUNCTION__ << "Property type "
@@ -774,7 +777,7 @@ void CQmlEngineImpl::SetDataInputValue(
}
case ATTRIBUTETYPE_FLOAT4: {
QVector4D valueVec;
- if (diDef.type == qt3ds::runtime::DataInputTypeVector4) {
+ if (diDef.type == qt3ds::runtime::DataInOutTypeVector4) {
valueVec = value.value<QVector4D>();
} else {
qWarning() << __FUNCTION__ << "Property type "
@@ -795,10 +798,10 @@ void CQmlEngineImpl::SetDataInputValue(
}
case ATTRIBUTETYPE_FLOAT3: {
QVector3D valueVec;
- if (diDef.type == qt3ds::runtime::DataInputTypeVector3
- || diDef.type == qt3ds::runtime::DataInputTypeVariant) {
+ if (diDef.type == qt3ds::runtime::DataInOutTypeVector3
+ || diDef.type == qt3ds::runtime::DataInOutTypeVariant) {
valueVec = value.value<QVector3D>();
- } else if (diDef.type == qt3ds::runtime::DataInputTypeEvaluator) {
+ } else if (diDef.type == qt3ds::runtime::DataInOutTypeEvaluator) {
const QVariant res = callJSFunc(name, diDef, QVariant::Type::Vector3D);
valueVec = res.value<QVector3D>();
} else {
@@ -821,10 +824,10 @@ void CQmlEngineImpl::SetDataInputValue(
case ATTRIBUTETYPE_FLOAT2:
{
QVector2D valueVec;
- if (diDef.type == qt3ds::runtime::DataInputTypeVector2
- || diDef.type == qt3ds::runtime::DataInputTypeVariant) {
+ if (diDef.type == qt3ds::runtime::DataInOutTypeVector2
+ || diDef.type == qt3ds::runtime::DataInOutTypeVariant) {
valueVec = value.value<QVector2D>();
- } else if (diDef.type == qt3ds::runtime::DataInputTypeEvaluator) {
+ } else if (diDef.type == qt3ds::runtime::DataInOutTypeEvaluator) {
const QVariant res = callJSFunc(name, diDef, QVariant::Type::Vector2D);
valueVec = res.value<QVector2D>();
} else {
@@ -846,10 +849,10 @@ void CQmlEngineImpl::SetDataInputValue(
}
case ATTRIBUTETYPE_BOOL: {
uint valueBool; // SetAttribute requires at least 32-bit variable
- if (diDef.type == qt3ds::runtime::DataInputTypeBoolean
- || diDef.type == qt3ds::runtime::DataInputTypeVariant) {
+ if (diDef.type == qt3ds::runtime::DataInOutTypeBoolean
+ || diDef.type == qt3ds::runtime::DataInOutTypeVariant) {
valueBool = value.toBool();
- } else if (diDef.type == qt3ds::runtime::DataInputTypeEvaluator) {
+ } else if (diDef.type == qt3ds::runtime::DataInOutTypeEvaluator) {
valueBool = callJSFunc(name, diDef, QVariant::Type::Bool).toBool();
} else {
qWarning() << __FUNCTION__ << "Property type "
@@ -868,12 +871,12 @@ void CQmlEngineImpl::SetDataInputValue(
case ATTRIBUTETYPE_STRING: {
QByteArray valueStr;
// Allow scalar number types also as inputs to string attribute
- if (diDef.type == qt3ds::runtime::DataInputTypeString
- || diDef.type == qt3ds::runtime::DataInputTypeRangedNumber
- || diDef.type == qt3ds::runtime::DataInputTypeFloat
- || diDef.type == qt3ds::runtime::DataInputTypeVariant) {
+ if (diDef.type == qt3ds::runtime::DataInOutTypeString
+ || diDef.type == qt3ds::runtime::DataInOutTypeRangedNumber
+ || diDef.type == qt3ds::runtime::DataInOutTypeFloat
+ || diDef.type == qt3ds::runtime::DataInOutTypeVariant) {
valueStr = value.toString().toUtf8();
- } else if (diDef.type == qt3ds::runtime::DataInputTypeEvaluator) {
+ } else if (diDef.type == qt3ds::runtime::DataInOutTypeEvaluator) {
valueStr = callJSFunc(name, diDef, QVariant::Type::String)
.toString().toUtf8();
} else {
@@ -1918,8 +1921,13 @@ void CQmlEngineImpl::initializeDataInputsInPresentation(CPresentation &presentat
QList<TElement *> elements;
listAllElements(parent, elements);
qt3ds::runtime::DataInputMap &diMap = m_Application->dataInputMap();
- qt3ds::foundation::IStringTable &strTable(presentation.GetStringTable());
+// #TODO: Remove below once QT3DS-3510 has been implemented in the editor
+ qt3ds::runtime::DataOutputMap &doMap = m_Application->dataOutputMap();
+// #TODO: Remove above once QT3DS-3510 has been implemented in the editor
+
+ qt3ds::foundation::IStringTable &strTable(presentation.GetStringTable());
+ QHash<CRegisteredString, qt3ds::runtime::DataOutputDef> elementPathToDataOutputDefMap;
for (TElement *element : qAsConst(elements)) {
Option<QT3DSU32> ctrlIndex = element->FindPropertyIndex(ATTRIBUTE_CONTROLLEDPROPERTY);
if (ctrlIndex.hasValue()) {
@@ -1935,7 +1943,7 @@ void CQmlEngineImpl::initializeDataInputsInPresentation(CPresentation &presentat
// remove datainput name prefix "$"
controllerName.remove(0, 1);
if (diMap.contains(controllerName)) {
- qt3ds::runtime::DataInputControlledAttribute ctrlElem;
+ qt3ds::runtime::DataInOutAttribute ctrlElem;
if (!isPrimary) {
// Prepend presentation id to element path
ctrlElem.elementPath = presentation.GetName();
@@ -1951,7 +1959,7 @@ void CQmlEngineImpl::initializeDataInputsInPresentation(CPresentation &presentat
TElement *component = &element->GetComponentParent();
ctrlElem.elementPath.append(component->m_Path);
} else if (diMap[controllerName].type
- == qt3ds::runtime::DataInputTypeVector3) {
+ == qt3ds::runtime::DataInOutTypeVector3) {
// special handling for vector datatype to handle
// expansion from <propertyname> to <propertyname>.x .y .z
QVector<QByteArray> attVec;
@@ -1969,7 +1977,7 @@ void CQmlEngineImpl::initializeDataInputsInPresentation(CPresentation &presentat
ctrlElem.propertyType = ATTRIBUTETYPE_NONE;
}
} else if (diMap[controllerName].type
- == qt3ds::runtime::DataInputTypeVector2) {
+ == qt3ds::runtime::DataInOutTypeVector2) {
// special handling for vector datatype to handle
// expansion from <propertyname> to <propertyname>.x .y
QVector<QByteArray> attVec;
@@ -1987,7 +1995,7 @@ void CQmlEngineImpl::initializeDataInputsInPresentation(CPresentation &presentat
ctrlElem.propertyType = ATTRIBUTETYPE_NONE;
}
} else if (diMap[controllerName].type
- == qt3ds::runtime::DataInputTypeEvaluator) {
+ == qt3ds::runtime::DataInOutTypeEvaluator) {
diMap[controllerName].evalFunc
= buildJSFunc(diMap[controllerName].evaluator);
auto referencedDIs = resolveDependentDatainputs(
@@ -2022,11 +2030,146 @@ void CQmlEngineImpl::initializeDataInputsInPresentation(CPresentation &presentat
}
qt3ds::runtime::DataInputDef &diDef = diMap[controllerName];
diDef.controlledAttributes.append(ctrlElem);
+
+// #TODO: Remove below once QT3DS-3510 has been implemented in the editor
+ // Note, in this temp implementation only the LAST of multiple attributes
+ // will be notified from the object under the DataInput name..
+ qt3ds::runtime::DataInputDef inDef = diMap[controllerName];
+ qt3ds::runtime::DataOutputDef &doDef = doMap[controllerName];
+ doDef.observedAttribute = ctrlElem;
+ doDef.type = inDef.type;
+ doDef.min = inDef.min;
+ doDef.max = inDef.max;
+
+ if (ctrlElem.propertyType == ATTRIBUTETYPE_DATAINPUT_TIMELINE) {
+ // Find the TElement for the @timeline attrib
+ TElement *target = nullptr;
+ QStringList split
+ = QString(ctrlElem.elementPath).split(QLatin1Char(':'));
+ if (split.size() > 1) {
+ target = CQmlElementHelper::GetElement(
+ *m_Application,
+ m_Application->GetPresentationById(
+ split.at(0).toStdString().c_str()),
+ split.at(1).toStdString().c_str(), nullptr);
+ } else {
+ target = CQmlElementHelper::GetElement(
+ *m_Application,
+ m_Application->GetPrimaryPresentation(),
+ split.at(0).toStdString().c_str(), nullptr);
+ }
+
+ doDef.timelineComponent = static_cast<TComponent *>(target);
+ } else if (ctrlElem.propertyType == ATTRIBUTETYPE_DATAINPUT_SLIDE) {
+ // Slide notifications are already done with separate signal
+ // No need to process
+ } else {
+ // Other than slide or timeline attributes are handled by CPresentation
+ CRegisteredString rString = strTable.RegisterStr(ctrlElem.elementPath);
+ elementPathToDataOutputDefMap.insertMulti(rString, doDef);
+ }
+// #TODO: Remove above once QT3DS-3510 has been implemented in the editor
}
}
}
}
}
+
+// #TODO: Remove below once QT3DS-3510 has been implemented in the editor
+ presentation.AddToDataOutputMap(elementPathToDataOutputDefMap);
+// #TODO: Remove above once QT3DS-3510 has been implemented in the editor
+}
+
+void CQmlEngineImpl::initializeDataOutputsInPresentation(CPresentation &presentation,
+ bool isPrimary)
+{
+ TElement *parent = presentation.GetRoot();
+ QList<TElement *> elements;
+ listAllElements(parent, elements);
+ qt3ds::runtime::DataOutputMap &doMap = m_Application->dataOutputMap();
+
+ qt3ds::foundation::IStringTable &strTable(presentation.GetStringTable());
+ QHash<CRegisteredString, qt3ds::runtime::DataOutputDef> elementPathToDataOutputDefMap;
+ for (TElement *element : qAsConst(elements)) {
+ Option<QT3DSU32> ctrlIndex = element->FindPropertyIndex(ATTRIBUTE_OBSERVEDPROPERTY);
+ if (ctrlIndex.hasValue()) {
+ Option<qt3ds::runtime::element::TPropertyDescAndValuePtr> propertyInfo =
+ element->GetPropertyByIndex(*ctrlIndex);
+ UVariant *valuePtr = propertyInfo->second;
+ QString valueStr =
+ QString::fromUtf8(strTable.HandleToStr(valuePtr->m_StringHandle));
+
+ if (!valueStr.isEmpty()) {
+ QStringList splitValues = valueStr.split(QLatin1Char(' '));
+
+ // Single value pair expected for DataOutputs
+ QString observerName = splitValues[0];
+ QString observedAttribute = splitValues[1];
+ // Remove DataOutput name prefix "$"
+ observerName.remove(0, 1);
+ qt3ds::runtime::DataOutputDef &doDef = doMap[observerName];
+
+ if (doMap.contains(observerName)) {
+ qt3ds::runtime::DataInOutAttribute obsElem;
+ if (!isPrimary) {
+ // Prepend presentation id to element path
+ obsElem.elementPath = presentation.GetName();
+ obsElem.elementPath.append(QByteArrayLiteral(":"));
+ }
+
+ obsElem.attributeName.append(observedAttribute.toUtf8());
+
+ if (obsElem.attributeName.first() == QByteArrayLiteral("@timeline")) {
+ // Timeline requires special additional handling
+ obsElem.propertyType = ATTRIBUTETYPE_DATAINPUT_TIMELINE;
+ TElement *component = &element->GetComponentParent();
+ obsElem.elementPath.append(component->m_Path);
+
+ // Find the TElement for the @timeline attrib
+ TElement *target = nullptr;
+ QStringList split = QString(obsElem.elementPath).split(QLatin1Char(':'));
+ if (split.size() > 1) {
+ target = CQmlElementHelper::GetElement(
+ *m_Application,
+ m_Application->GetPresentationById(
+ split.at(0).toStdString().c_str()),
+ split.at(1).toStdString().c_str(), nullptr);
+ } else {
+ target = CQmlElementHelper::GetElement(
+ *m_Application,
+ m_Application->GetPrimaryPresentation(),
+ split.at(0).toStdString().c_str(), nullptr);
+ }
+ doDef.timelineComponent = static_cast<TComponent *>(target);
+ } else if (obsElem.attributeName.first() == QByteArrayLiteral("@slide")) {
+ // Slides are ignored if set as we have separate signal in the API for
+ // slide transitions
+ } else {
+ // Every other type is handled by CPresentation
+ obsElem.elementPath.append(element->m_Path);
+ TStringHash attHash = CHash::HashAttribute(
+ obsElem.attributeName.first().constData());
+ Option<qt3ds::runtime::element::TPropertyDescAndValuePtr> attInfo
+ = element->FindProperty(attHash);
+ if (attInfo.hasValue()) {
+ obsElem.propertyType = attInfo->first.m_Type;
+ } else {
+ obsElem.propertyType = ATTRIBUTETYPE_NONE;
+ qWarning() << __FUNCTION__ << "Property"
+ << obsElem.attributeName.first() << "not existing!";
+ }
+
+ doDef.observedAttribute = obsElem;
+ CRegisteredString rString = strTable.RegisterStr(obsElem.elementPath);
+ elementPathToDataOutputDefMap.insertMulti(rString, doDef);
+ }
+ }
+ }
+ }
+ }
+
+ // Inform the presentation of the ready data output defs
+ presentation.AddToDataOutputMap(elementPathToDataOutputDefMap);
}
// Bit clumsy way of getting from "position" to "position .x .y .z" and enabling datainput
@@ -2165,7 +2308,7 @@ QVector<QString> CQmlEngineImpl::resolveDependentDatainputs(const QString &expre
for (auto di : args) {
auto diTrim = di.trimmed();
if (diMap.contains(diTrim)) {
- if (diMap[diTrim].type == qt3ds::runtime::DataInputTypeEvaluator
+ if (diMap[diTrim].type == qt3ds::runtime::DataInOutTypeEvaluator
&& diTrim != controllerName) {
qWarning() << __FUNCTION__ << "Invalid evaluator function in" << controllerName
<< ". Another evaluator is used as source data.";
diff --git a/src/Runtime/Source/uipparser/Qt3DSUIPParserImpl.cpp b/src/Runtime/Source/uipparser/Qt3DSUIPParserImpl.cpp
index e758f0af..9f7641b0 100644
--- a/src/Runtime/Source/uipparser/Qt3DSUIPParserImpl.cpp
+++ b/src/Runtime/Source/uipparser/Qt3DSUIPParserImpl.cpp
@@ -1180,6 +1180,7 @@ void CUIPParserImpl::CacheGraphRequiredAttributes(qt3dsdm::IDOMReader &inReader)
// Probably not the most optimal thing to expose this attribute for all elements that
// support it, since it is not really needed except during initialization
m_ParseElementManager.MarkAttributeAsReferenced(theData, "controlledproperty");
+ m_ParseElementManager.MarkAttributeAsReferenced(theData, "observedproperty");
// Behaviors need all attributes possible on the object on them all the time.
if (AreEqual(theType, "Behavior") || AreEqual(theType, "RenderPlugin")) {
@@ -1232,8 +1233,10 @@ BOOL CUIPParserImpl::CacheLogicRequiredAttributes(qt3dsdm::IDOMReader &inReader,
// set only those as referenced. In this case we might have to expand vec3
// type attributes to component parts
const char *ctrldProp;
+ const char *observedProp;
inReader.Att("controlledproperty", ctrldProp);
- if (ctrldProp)
+ inReader.Att("observedproperty", observedProp);
+ if (ctrldProp || observedProp)
m_ParseElementManager.MarkAllAttributesAsReferenced(*theData, true);
theCommandInstances.insert(theData->m_Id);
diff --git a/src/Runtime/Source/viewer/Qt3DSViewerApp.cpp b/src/Runtime/Source/viewer/Qt3DSViewerApp.cpp
index c5ac0d6b..ac4aa7a3 100644
--- a/src/Runtime/Source/viewer/Qt3DSViewerApp.cpp
+++ b/src/Runtime/Source/viewer/Qt3DSViewerApp.cpp
@@ -357,6 +357,8 @@ bool Q3DSViewerApp::InitializeApp(int winWidth, int winHeight, const QSurfaceFor
&QRuntimeViewSignalProxy::SigSlideExited, this, &Q3DSViewerApp::SigSlideExited);
connect(m_Impl.m_view->signalProxy(),
&QRuntimeViewSignalProxy::SigCustomSignal, this, &Q3DSViewerApp::SigCustomSignal);
+ connect(m_Impl.m_view->signalProxy(), &QRuntimeViewSignalProxy::SigDataOutputValueUpdated,
+ this, &Q3DSViewerApp::SigDataOutputValueUpdated);
QMetaObject::Connection *presReadyconn = new QMetaObject::Connection();
*presReadyconn = connect(m_Impl.m_view->signalProxy(),
&QRuntimeViewSignalProxy::SigPresentationReady, [&, presReadyconn]{
@@ -791,6 +793,14 @@ QList<QString> Q3DSViewerApp::dataInputs() const
return m_Impl.m_view->dataInputs();
}
+QList<QString> Q3DSViewerApp::dataOutputs() const
+{
+ if (!m_Impl.m_view)
+ return {};
+
+ return m_Impl.m_view->dataOutputs();
+}
+
float Q3DSViewerApp::dataInputMax(const QString &name) const
{
if (!m_Impl.m_view)
diff --git a/src/Runtime/Source/viewer/Qt3DSViewerApp.h b/src/Runtime/Source/viewer/Qt3DSViewerApp.h
index 8c873098..8022246c 100644
--- a/src/Runtime/Source/viewer/Qt3DSViewerApp.h
+++ b/src/Runtime/Source/viewer/Qt3DSViewerApp.h
@@ -357,6 +357,7 @@ public:
= qt3ds::runtime::DataInputValueRole::Value);
QList<QString> dataInputs() const;
+ QList<QString> dataOutputs() const;
float dataInputMax(const QString &name) const;
float dataInputMin(const QString &name) const;
@@ -409,6 +410,7 @@ Q_SIGNALS:
void SigCustomSignal(const QString &elementPath, const QString &name);
void SigElementsCreated(const QStringList &elementPaths, const QString &error);
void SigMaterialsCreated(const QStringList &materialNames, const QString &error);
+ void SigDataOutputValueUpdated(const QString &name, const QVariant &newValue);
void SigPresentationReady();
void SigPresentationLoaded();
};
diff --git a/src/Runtime/api/studio3d/q3dscommandqueue.cpp b/src/Runtime/api/studio3d/q3dscommandqueue.cpp
index 02ae9395..01f15125 100644
--- a/src/Runtime/api/studio3d/q3dscommandqueue.cpp
+++ b/src/Runtime/api/studio3d/q3dscommandqueue.cpp
@@ -278,6 +278,7 @@ void CommandQueue::copyCommands(CommandQueue &fromQueue)
case CommandType_UnloadSlide:
case CommandType_PreloadSlide:
case CommandType_RequestDataInputs:
+ case CommandType_RequestDataOutputs:
queueCommand(source.m_elementPath, source.m_commandType);
break;
default:
diff --git a/src/Runtime/api/studio3d/q3dscommandqueue_p.h b/src/Runtime/api/studio3d/q3dscommandqueue_p.h
index c396ec8e..72a88750 100644
--- a/src/Runtime/api/studio3d/q3dscommandqueue_p.h
+++ b/src/Runtime/api/studio3d/q3dscommandqueue_p.h
@@ -76,7 +76,8 @@ enum CommandType {
CommandType_RequestSlideInfo,
CommandType_RequestDataInputs,
CommandType_PreloadSlide,
- CommandType_UnloadSlide
+ CommandType_UnloadSlide,
+ CommandType_RequestDataOutputs
};
class Q_STUDIO3D_EXPORT ElementCommand
diff --git a/src/Runtime/api/studio3d/q3dsdataoutput.cpp b/src/Runtime/api/studio3d/q3dsdataoutput.cpp
new file mode 100644
index 00000000..e0db2dd5
--- /dev/null
+++ b/src/Runtime/api/studio3d/q3dsdataoutput.cpp
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $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 "q3dsdataoutput_p.h"
+#include "q3dspresentation_p.h"
+#include "q3dscommandqueue_p.h"
+
+Q3DSDataOutput::Q3DSDataOutput(QObject *parent)
+ : QObject(parent)
+ , d_ptr(new Q3DSDataOutputPrivate(this))
+{
+
+}
+
+Q3DSDataOutput::Q3DSDataOutput(const QString &name, QObject *parent)
+ : QObject(parent)
+ , d_ptr(new Q3DSDataOutputPrivate(this))
+{
+ d_ptr->m_name = name;
+}
+
+Q3DSDataOutput::~Q3DSDataOutput()
+{
+ delete d_ptr;
+}
+
+QString Q3DSDataOutput::name() const
+{
+ return d_ptr->m_name;
+}
+
+void Q3DSDataOutput::setName(const QString &name)
+{
+ if (d_ptr->m_name != name) {
+ d_ptr->m_name = name;
+ if (d_ptr->m_presentation)
+ d_ptr->m_presentation->registerDataOutput(this);
+ Q_EMIT nameChanged(name);
+ }
+}
+
+QVariant Q3DSDataOutput::value() const
+{
+ return d_ptr->m_value;
+}
+
+void Q3DSDataOutput::setValue(const QVariant &value)
+{
+ if (d_ptr->m_value == value)
+ return;
+
+ d_ptr->m_value = value;
+ Q_EMIT valueChanged(value);
+}
+
+void Q3DSDataOutputPrivate::setPresentation(Q3DSPresentation *presentation)
+{
+ m_presentation = presentation;
+}
+
+Q3DSDataOutputPrivate::Q3DSDataOutputPrivate(Q3DSDataOutput *parent)
+ : q_ptr(parent)
+{
+}
+
+Q3DSDataOutputPrivate::~Q3DSDataOutputPrivate()
+{
+ if (m_presentation)
+ m_presentation->unregisterDataOutput(q_ptr);
+}
+
+void Q3DSDataOutputPrivate::setValue(const QVariant &value)
+{
+ m_value = value;
+ Q_EMIT q_ptr->valueChanged(value);
+}
+
+void Q3DSDataOutputPrivate::setViewerApp(Q3DSViewer::Q3DSViewerApp *app)
+{
+ m_viewerApp = app;
+
+ if (m_viewerApp && m_value.isValid())
+ setValue(m_value);
+}
+
+void Q3DSDataOutputPrivate::setCommandQueue(CommandQueue *queue)
+{
+ m_commandQueue = queue;
+
+ if (m_commandQueue && m_value.isValid())
+ setValue(m_value);
+}
diff --git a/src/Runtime/api/studio3d/q3dsdataoutput.h b/src/Runtime/api/studio3d/q3dsdataoutput.h
new file mode 100644
index 00000000..93f74267
--- /dev/null
+++ b/src/Runtime/api/studio3d/q3dsdataoutput.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $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$
+**
+****************************************************************************/
+
+#ifndef Q3DSDATAOUTPUT_H
+#define Q3DSDATAOUTPUT_H
+
+#include <QtStudio3D/qstudio3dglobal.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qvariant.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q3DSDataOutputPrivate;
+class Q3DSPresentation;
+
+class Q_STUDIO3D_EXPORT Q3DSDataOutput : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(Q3DSDataOutput)
+
+ Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
+ Q_PROPERTY(QVariant value READ value NOTIFY valueChanged)
+
+public:
+
+ explicit Q3DSDataOutput(QObject *parent = nullptr);
+ explicit Q3DSDataOutput(const QString &name, QObject *parent = nullptr);
+ explicit Q3DSDataOutput(Q3DSPresentation *presentation, const QString &name,
+ QObject *parent = nullptr);
+ virtual ~Q3DSDataOutput();
+
+ QString name() const;
+ QVariant value() const;
+
+public Q_SLOTS:
+ void setName(const QString &name);
+
+Q_SIGNALS:
+ void nameChanged(const QString &newName);
+ void valueChanged(const QVariant &newValue);
+
+protected:
+ explicit Q3DSDataOutput(Q3DSDataOutputPrivate *d, Q3DSPresentation *presentation,
+ const QString &name, QObject *parent = nullptr);
+ Q3DSDataOutputPrivate *d_ptr;
+
+private:
+ Q_DISABLE_COPY(Q3DSDataOutput)
+ void setValue(const QVariant &newValue);
+ friend class Q3DSPresentationPrivate;
+};
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(Q3DSDataOutput*)
+
+#endif // Q3DSDATAOUTPUT_H
diff --git a/src/Runtime/api/studio3d/q3dsdataoutput_p.h b/src/Runtime/api/studio3d/q3dsdataoutput_p.h
new file mode 100644
index 00000000..c4745bfb
--- /dev/null
+++ b/src/Runtime/api/studio3d/q3dsdataoutput_p.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $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$
+**
+****************************************************************************/
+
+#ifndef Q3DSDATAOUTPUT_P_H
+#define Q3DSDATAOUTPUT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the QtStudio3D API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "q3dsdataoutput.h"
+#include "q3dscommandqueue_p.h"
+#include "Qt3DSViewerApp.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q3DSPresentationPrivate;
+
+class Q_STUDIO3D_EXPORT Q3DSDataOutputPrivate
+{
+ Q_DECLARE_PUBLIC(Q3DSDataOutput)
+public:
+ explicit Q3DSDataOutputPrivate(Q3DSDataOutput *parent);
+ virtual ~Q3DSDataOutputPrivate();
+
+ void setValue(const QVariant &value);
+ void setViewerApp(Q3DSViewer::Q3DSViewerApp *app);
+ void setCommandQueue(CommandQueue *queue);
+ void setPresentation(Q3DSPresentation *presentation);
+
+protected:
+ Q3DSDataOutput *q_ptr;
+ Q3DSViewer::Q3DSViewerApp *m_viewerApp = nullptr; // Not owned
+ CommandQueue *m_commandQueue = nullptr; // Not owned
+ Q3DSPresentation *m_presentation = nullptr; // Not owned
+ QString m_name;
+ QVariant m_value;
+};
+
+QT_END_NAMESPACE
+
+#endif // Q3DSDATAOUTPUT_P_H
diff --git a/src/Runtime/api/studio3d/q3dspresentation.cpp b/src/Runtime/api/studio3d/q3dspresentation.cpp
index 880780ff..bc7c7e2b 100644
--- a/src/Runtime/api/studio3d/q3dspresentation.cpp
+++ b/src/Runtime/api/studio3d/q3dspresentation.cpp
@@ -32,6 +32,7 @@
#include "q3dscommandqueue_p.h"
#include "viewerqmlstreamproxy_p.h"
#include "q3dsdatainput_p.h"
+#include "q3dsdataoutput_p.h"
#include <QtCore/qdebug.h>
#include <QtCore/qsettings.h>
@@ -90,6 +91,21 @@ Q3DSDataInput *Q3DSPresentation::registeredDataInput(const QString &name) const
return d_ptr->m_dataInputs.value(name, nullptr);
}
+void Q3DSPresentation::registerDataOutput(Q3DSDataOutput *dataOutput)
+{
+ d_ptr->registerDataOutput(dataOutput);
+}
+
+void Q3DSPresentation::unregisterDataOutput(Q3DSDataOutput *dataOutput)
+{
+ d_ptr->unregisterDataOutput(dataOutput);
+}
+
+Q3DSDataOutput *Q3DSPresentation::registeredDataOutput(const QString &name) const
+{
+ return d_ptr->m_dataOutputs.value(name, nullptr);
+}
+
QVector<Q3DSDataInput *> Q3DSPresentation::dataInputs() const
{
QVector<Q3DSDataInput *> ret;
@@ -100,6 +116,27 @@ QVector<Q3DSDataInput *> Q3DSPresentation::dataInputs() const
return ret;
}
+QVariantList Q3DSPresentation::getDataOutputs() const
+{
+ QVariantList ret;
+ const auto dataoutputs = dataOutputs();
+
+ for (const auto &it : dataoutputs)
+ ret.append(QVariant::fromValue(it));
+
+ return ret;
+}
+
+QVector<Q3DSDataOutput *> Q3DSPresentation::dataOutputs() const
+{
+ QVector<Q3DSDataOutput *> ret;
+ const auto datainputs = d_ptr->m_dataOutputs;
+ for (const auto &it : datainputs)
+ ret.append(it);
+
+ return ret;
+}
+
QVariantList Q3DSPresentation::getDataInputs() const
{
QVariantList ret;
@@ -444,6 +481,7 @@ Q3DSPresentationPrivate::~Q3DSPresentationPrivate()
{
unregisterAllElements();
unregisterAllDataInputs();
+ unregisterAllDataOutputs();
delete m_streamProxy;
}
@@ -493,6 +531,8 @@ void Q3DSPresentationPrivate::setViewerApp(Q3DSViewer::Q3DSViewerApp *app, bool
q_ptr, &Q3DSPresentation::slideExited);
connect(app, &Q3DSViewer::Q3DSViewerApp::SigCustomSignal,
q_ptr, &Q3DSPresentation::customSignalEmitted);
+ connect(app, &Q3DSViewer::Q3DSViewerApp::SigDataOutputValueUpdated,
+ this, &Q3DSPresentationPrivate::handleDataOutputValueUpdate);
connect(app, &Q3DSViewer::Q3DSViewerApp::SigElementsCreated,
q_ptr, &Q3DSPresentation::elementsCreated);
connect(app, &Q3DSViewer::Q3DSViewerApp::SigMaterialsCreated,
@@ -505,6 +545,8 @@ void Q3DSPresentationPrivate::setViewerApp(Q3DSViewer::Q3DSViewerApp *app, bool
q_ptr, &Q3DSPresentation::slideExited);
disconnect(oldApp, &Q3DSViewer::Q3DSViewerApp::SigCustomSignal,
q_ptr, &Q3DSPresentation::customSignalEmitted);
+ disconnect(oldApp, &Q3DSViewer::Q3DSViewerApp::SigDataOutputValueUpdated,
+ this, &Q3DSPresentationPrivate::handleDataOutputValueUpdate);
disconnect(oldApp, &Q3DSViewer::Q3DSViewerApp::SigElementsCreated,
q_ptr, &Q3DSPresentation::elementsCreated);
disconnect(oldApp, &Q3DSViewer::Q3DSViewerApp::SigMaterialsCreated,
@@ -527,9 +569,11 @@ void Q3DSPresentationPrivate::setCommandQueue(CommandQueue *queue)
if (m_commandQueue) {
setDelayedLoading(m_delayedLoading);
setVariantList(m_variantList);
- // Queue a request ASAP for datainputs defined in UIA file so that
- // getDataInputs has up-to-date info at the earliest.
+ // Queue a request ASAP for datainputs and outputs defined in UIA file so that
+ // getDataInputs has up-to-date info at the earliest and that data outputs
+ // connect from source to destination
m_commandQueue->queueCommand({}, CommandType_RequestDataInputs);
+ m_commandQueue->queueCommand({}, CommandType_RequestDataOutputs);
setSource(m_source);
}
}
@@ -559,6 +603,19 @@ void Q3DSPresentationPrivate::requestResponseHandler(CommandType commandType, vo
Q_EMIT q_ptr->dataInputsReady();
break;
}
+ case CommandType_RequestDataOutputs: {
+ QVariantList *response = reinterpret_cast<QVariantList *>(requestData);
+
+ for (int i = 0; i < response->size(); ++i) {
+ // Check and append to QML-side list if the (UIA) presentation has additional
+ // dataoutputs that are not explicitly defined in QML code.
+ if (!m_dataOutputs.contains(response->at(i).value<QString>()))
+ registerDataOutput(new Q3DSDataOutput(response->at(i).value<QString>(), nullptr));
+ }
+ delete response;
+ Q_EMIT q_ptr->dataOutputsReady();
+ break;
+ }
default:
Q_ASSERT(false);
break;
@@ -689,6 +746,66 @@ float Q3DSPresentationPrivate::dataInputMax(const QString &name) const
return m_viewerApp->dataInputMax(name);
}
+void Q3DSPresentationPrivate::registerDataOutput(Q3DSDataOutput *dataOutput)
+{
+ Q_ASSERT(!dataOutput->name().isEmpty());
+
+ // Allow only single registration for each DataOutput
+ QMutableHashIterator<QString, Q3DSDataOutput *> i(m_dataOutputs);
+ while (i.hasNext()) {
+ i.next();
+ if (i.value() == dataOutput) {
+ // If the same DataOutput object is already registered with different name,
+ // remove it from the map to avoid duplication.
+ if (i.key() != dataOutput->name())
+ i.remove();
+ } else if (i.key() == dataOutput->name()) {
+ // If the same name is registered by another DataOutput object, the old
+ // DataOutput object is unregistered.
+ i.value()->d_ptr->setViewerApp(nullptr);
+ i.value()->d_ptr->setPresentation(nullptr);
+ i.value()->d_ptr->setCommandQueue(nullptr);
+ i.remove();
+ }
+ }
+
+ dataOutput->d_ptr->setPresentation(q_ptr);
+ dataOutput->d_ptr->setViewerApp(m_viewerApp);
+ dataOutput->d_ptr->setCommandQueue(m_commandQueue);
+
+ m_dataOutputs.insert(dataOutput->name(), dataOutput);
+}
+
+void Q3DSPresentationPrivate::unregisterDataOutput(Q3DSDataOutput *dataOutput)
+{
+ Q3DSDataOutput *oldDout = m_dataOutputs.value(dataOutput->name());
+ if (oldDout == dataOutput) {
+ dataOutput->d_ptr->setCommandQueue(nullptr);
+ dataOutput->d_ptr->setViewerApp(nullptr);
+ dataOutput->d_ptr->setPresentation(nullptr);
+ m_dataOutputs.remove(dataOutput->name());
+ }
+}
+
+void Q3DSPresentationPrivate::unregisterAllDataOutputs()
+{
+ const auto values = m_dataOutputs.values();
+ for (Q3DSDataOutput *dout : values) {
+ dout->d_ptr->setViewerApp(nullptr);
+ dout->d_ptr->setCommandQueue(nullptr);
+ dout->d_ptr->setPresentation(nullptr);
+ }
+ m_dataOutputs.clear();
+}
+
+bool Q3DSPresentationPrivate::isValidDataOutput(const Q3DSDataOutput *dataOutput) const
+{
+ if (!m_viewerApp)
+ return false;
+
+ return m_viewerApp->dataOutputs().contains(dataOutput->name());
+}
+
Q3DStudio::EKeyCode Q3DSPresentationPrivate::getScanCode(QKeyEvent *e)
{
enum {
@@ -1000,4 +1117,14 @@ void Q3DSPresentationPrivate::handleSlideEntered(const QString &elementPath, uns
Q_EMIT q_ptr->slideEntered(elementPath, index, name);
}
+void Q3DSPresentationPrivate::handleDataOutputValueUpdate(const QString &name,
+ const QVariant &newValue)
+{
+ if (!m_dataOutputs.contains(name))
+ return;
+
+ Q3DSDataOutput *node = m_dataOutputs[name];
+ node->setValue(newValue);
+}
+
QT_END_NAMESPACE
diff --git a/src/Runtime/api/studio3d/q3dspresentation.h b/src/Runtime/api/studio3d/q3dspresentation.h
index 89daa262..6aa3b71f 100644
--- a/src/Runtime/api/studio3d/q3dspresentation.h
+++ b/src/Runtime/api/studio3d/q3dspresentation.h
@@ -36,6 +36,8 @@
#include <QtCore/qurl.h>
#include <QtCore/qvector.h>
#include <QtCore/qstringlist.h>
+#include <QtStudio3D/q3dsdatainput.h>
+#include <QtStudio3D/q3dsdataoutput.h>
QT_BEGIN_NAMESPACE
@@ -67,9 +69,14 @@ public:
void registerDataInput(Q3DSDataInput *dataInput);
void unregisterDataInput(Q3DSDataInput *dataInput);
Q3DSDataInput *registeredDataInput(const QString &name) const;
+ void registerDataOutput(Q3DSDataOutput *dataOutput);
+ void unregisterDataOutput(Q3DSDataOutput *dataOutput);
+ Q3DSDataOutput *registeredDataOutput(const QString &name) const;
Q_INVOKABLE QVariantList getDataInputs() const;
QVector<Q3DSDataInput *> dataInputs() const;
+ Q_INVOKABLE QVariantList getDataOutputs() const;
+ QVector<Q3DSDataOutput *> dataOutputs() const;
bool delayedLoading() const;
void setDelayedLoading(bool enable);
@@ -114,9 +121,11 @@ Q_SIGNALS:
void sourceChanged(const QUrl &source);
void slideEntered(const QString &elementPath, unsigned int index, const QString &name);
void slideExited(const QString &elementPath, unsigned int index, const QString &name);
- // Indicates that data input definitions in the Studio project have been parsed
+ // Indicates that data input and output definitions in the Studio project have been parsed
+ // and datainputs/dataoutputs are available through dataInputs() / getDataInputs().
// and datainputs are available through dataInputs() / getDataInputs().
void dataInputsReady();
+ void dataOutputsReady();
void customSignalEmitted(const QString &elementPath, const QString &name);
void delayedLoadingChanged(bool enable);
void elementsCreated(const QStringList &elementPaths, const QString &error);
diff --git a/src/Runtime/api/studio3d/q3dspresentation_p.h b/src/Runtime/api/studio3d/q3dspresentation_p.h
index 6172aa33..7b4877d9 100644
--- a/src/Runtime/api/studio3d/q3dspresentation_p.h
+++ b/src/Runtime/api/studio3d/q3dspresentation_p.h
@@ -61,6 +61,7 @@ class Q_STUDIO3D_EXPORT Q3DSPresentationPrivate : public QObject
public:
typedef QHash<QString, Q3DSElement *> ElementMap;
typedef QHash<QString, Q3DSDataInput *> DataInputMap;
+ typedef QHash<QString, Q3DSDataOutput *> DataOutputMap;
explicit Q3DSPresentationPrivate(Q3DSPresentation *parent);
~Q3DSPresentationPrivate();
@@ -78,10 +79,14 @@ public:
void registerDataInput(Q3DSDataInput *dataInput);
void unregisterDataInput(Q3DSDataInput *dataInput);
void unregisterAllDataInputs();
+ void registerDataOutput(Q3DSDataOutput *dataOutput);
+ void unregisterDataOutput(Q3DSDataOutput *dataOutput);
+ void unregisterAllDataOutputs();
bool isValidDataInput(const Q3DSDataInput *dataInput) const;
float dataInputMin(const QString &name) const;
float dataInputMax(const QString &name) const;
+ bool isValidDataOutput(const Q3DSDataOutput *dataOutput) const;
ViewerQmlStreamProxy *streamProxy();
Q3DStudio::EKeyCode getScanCode(QKeyEvent *e);
@@ -90,6 +95,7 @@ public:
public Q_SLOTS:
void handleSlideEntered(const QString &elementPath, unsigned int index, const QString &name);
+ void handleDataOutputValueUpdate(const QString &name, const QVariant &newValue);
public:
Q3DSPresentation *q_ptr;
@@ -99,6 +105,7 @@ private:
CommandQueue *m_commandQueue; // Not owned
ElementMap m_elements;
DataInputMap m_dataInputs;
+ DataOutputMap m_dataOutputs;
QUrl m_source;
QStringList m_variantList;
ViewerQmlStreamProxy *m_streamProxy;
diff --git a/src/Runtime/api/studio3d/studio3d.pro b/src/Runtime/api/studio3d/studio3d.pro
index 6c5e0585..0cc066b5 100644
--- a/src/Runtime/api/studio3d/studio3d.pro
+++ b/src/Runtime/api/studio3d/studio3d.pro
@@ -14,6 +14,8 @@ LIBS += \
-lqt3dsqmlstreamer$$qtPlatformTargetSuffix()
HEADERS += \
+ q3dsdataoutput.h \
+ q3dsdataoutput_p.h \
q3dswidget.h \
q3dswidget_p.h \
q3dssurfaceviewer.h \
@@ -36,6 +38,7 @@ HEADERS += \
q3dsdatainput_p.h
SOURCES += q3dswidget.cpp \
+ q3dsdataoutput.cpp \
q3dssurfaceviewer.cpp \
viewerqmlstreamproxy.cpp \
q3dsviewersettings.cpp \
diff --git a/src/Runtime/api/studio3dqml/q3dsplugin.cpp b/src/Runtime/api/studio3dqml/q3dsplugin.cpp
index aa558775..5ae5aab2 100644
--- a/src/Runtime/api/studio3dqml/q3dsplugin.cpp
+++ b/src/Runtime/api/studio3dqml/q3dsplugin.cpp
@@ -56,6 +56,7 @@ void Q3DSPlugin::registerTypes(const char *uri)
qmlRegisterType<Q3DSQmlStream>(uri, 2, 4, "QmlStream");
qmlRegisterType<Q3DSSubPresentationSettings>(uri, 2, 4, "SubPresentationSettings");
qmlRegisterType<Q3DSDataInput>(uri, 2, 4, "DataInput");
+ qmlRegisterType<Q3DSDataOutput>(uri, 2, 4, "DataOutput");
}
QT_END_NAMESPACE
diff --git a/src/Runtime/api/studio3dqml/q3dspresentationitem.cpp b/src/Runtime/api/studio3dqml/q3dspresentationitem.cpp
index 6c4c8e36..d588c419 100644
--- a/src/Runtime/api/studio3dqml/q3dspresentationitem.cpp
+++ b/src/Runtime/api/studio3dqml/q3dspresentationitem.cpp
@@ -86,11 +86,23 @@ void Q3DSPresentationItem::appendQmlChildren(QQmlListProperty<QObject> *list, QO
}
} else {
auto dataInput = qobject_cast<Q3DSDataInput *>(obj);
- if (item->registeredDataInput(dataInput->name())) {
- qWarning() << __FUNCTION__
- << "Duplicate DataInput defined for Presentation.";
+ if (dataInput) {
+ if (item->registeredDataInput(dataInput->name())) {
+ qWarning() << __FUNCTION__
+ << "Duplicate DataInput defined for Presentation.";
+ } else {
+ item->registerDataInput(dataInput);
+ }
} else {
- item->registerDataInput(dataInput);
+ auto dataOutput = qobject_cast<Q3DSDataOutput *>(obj);
+ if (dataOutput) {
+ if (item->registeredDataOutput(dataOutput->name())) {
+ qWarning() << __FUNCTION__
+ << "Duplicate DataOutput defined for Presentation.";
+ } else {
+ item->registerDataOutput(dataOutput);
+ }
+ }
}
}
}
@@ -98,5 +110,4 @@ void Q3DSPresentationItem::appendQmlChildren(QQmlListProperty<QObject> *list, QO
}
}
-
QT_END_NAMESPACE
diff --git a/src/Runtime/api/studio3dqml/q3dsrenderer.cpp b/src/Runtime/api/studio3dqml/q3dsrenderer.cpp
index 591cc37a..2ebe88f9 100644
--- a/src/Runtime/api/studio3dqml/q3dsrenderer.cpp
+++ b/src/Runtime/api/studio3dqml/q3dsrenderer.cpp
@@ -215,6 +215,8 @@ bool Q3DSRenderer::initializeRuntime(QOpenGLFramebufferObject *inFbo)
this, &Q3DSRenderer::elementsCreated);
connect(m_runtime, &Q3DSViewer::Q3DSViewerApp::SigMaterialsCreated,
this, &Q3DSRenderer::materialsCreated);
+ connect(m_runtime, &Q3DSViewer::Q3DSViewerApp::SigDataOutputValueUpdated,
+ this, &Q3DSRenderer::dataOutputValueUpdated);
return true;
}
@@ -392,6 +394,18 @@ void Q3DSRenderer::processCommands()
Q_EMIT requestResponse(cmd.m_elementPath, cmd.m_commandType, requestData);
break;
}
+ case CommandType_RequestDataOutputs: {
+ QVariantList *requestData = new QVariantList();
+ if (m_presentation) {
+ const auto diList = m_presentation->dataOutputs();
+
+ for (const auto &it : diList)
+ requestData->append(QVariant::fromValue(it->name()));
+ }
+
+ Q_EMIT requestResponse(cmd.m_elementPath, cmd.m_commandType, requestData);
+ break;
+ }
case CommandType_PreloadSlide:
m_runtime->preloadSlide(cmd.m_elementPath);
break;
diff --git a/src/Runtime/api/studio3dqml/q3dsrenderer.h b/src/Runtime/api/studio3dqml/q3dsrenderer.h
index 757ec4af..333edcda 100644
--- a/src/Runtime/api/studio3dqml/q3dsrenderer.h
+++ b/src/Runtime/api/studio3dqml/q3dsrenderer.h
@@ -65,6 +65,7 @@ Q_SIGNALS:
void customSignalEmitted(const QString &elNmentPath, const QString &name);
void elementsCreated(const QStringList &elementPaths, const QString &error);
void materialsCreated(const QStringList &materialNames, const QString &error);
+ void dataOutputValueUpdated(const QString &name, const QVariant &newValue);
protected:
static void onInitHandler(void *userData);
diff --git a/src/Runtime/api/studio3dqml/q3dsstudio3d.cpp b/src/Runtime/api/studio3dqml/q3dsstudio3d.cpp
index 75633a09..769861fe 100644
--- a/src/Runtime/api/studio3dqml/q3dsstudio3d.cpp
+++ b/src/Runtime/api/studio3dqml/q3dsstudio3d.cpp
@@ -187,6 +187,16 @@ void Q3DSStudio3D::requestResponseHandler(const QString &elementPath, CommandTyp
}
break;
}
+ case CommandType_RequestDataOutputs: {
+ Q3DSPresentation *handler = qobject_cast<Q3DSPresentation *>(m_presentation);
+ if (handler) {
+ handler->d_ptr->requestResponseHandler(commandType, requestData);
+ } else {
+ qWarning() << __FUNCTION__
+ << "RequestDataOutputs response got for invalid presentation.";
+ }
+ break;
+ }
default:
qWarning() << __FUNCTION__ << "Unknown command type.";
break;
@@ -203,6 +213,8 @@ QQuickFramebufferObject::Renderer *Q3DSStudio3D::createRenderer() const
connect(renderer, &Q3DSRenderer::enterSlide,
m_presentation->d_ptr, &Q3DSPresentationPrivate::handleSlideEntered);
+ connect(renderer, &Q3DSRenderer::dataOutputValueUpdated,
+ m_presentation->d_ptr, &Q3DSPresentationPrivate::handleDataOutputValueUpdate);
connect(renderer, &Q3DSRenderer::exitSlide,
m_presentation, &Q3DSPresentation::slideExited);
connect(renderer, &Q3DSRenderer::customSignalEmitted,