summaryrefslogtreecommitdiffstats
path: root/src/runtime/q3dsuippresentation.cpp
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2018-08-01 12:15:42 +0200
committerLaszlo Agocs <laszlo.agocs@qt.io>2018-08-02 09:48:49 +0000
commit48149c7af40dc1e87d2caaa91526269a752a9107 (patch)
tree1032577006feff959209242a4c8ac849478f4c38 /src/runtime/q3dsuippresentation.cpp
parent2040e207f56ac9246240938a43c6f5c2b6d912c4 (diff)
Make effects dynamically spawnable, destroyable, and changeable
Also makes it possible to use filenames in effects', custom materials' and behaviors' "class" property. Previously this could only contain an id referring to an entry in the Classes section of the uip document. With programatically constructed scenes the pre-loaded list of effect/material/behavior descriptions is not used. Instead the instances refer directly to filenames (the loaded data is cached like with meshes). Custom materials and behaviors do not yet support changing their "class" at runtime, although they get many of the enablers. Change-Id: I560ab00b9dc447bd19d5ebeb749972873b89ec2c Reviewed-by: Andy Nichols <andy.nichols@qt.io>
Diffstat (limited to 'src/runtime/q3dsuippresentation.cpp')
-rw-r--r--src/runtime/q3dsuippresentation.cpp177
1 files changed, 116 insertions, 61 deletions
diff --git a/src/runtime/q3dsuippresentation.cpp b/src/runtime/q3dsuippresentation.cpp
index 1cba87c..3ac137a 100644
--- a/src/runtime/q3dsuippresentation.cpp
+++ b/src/runtime/q3dsuippresentation.cpp
@@ -849,6 +849,16 @@ QVariantMap Q3DSGraphObject::dynamicProperties() const
return dynProps;
}
+void Q3DSGraphObject::clearDynamicProperties()
+{
+ Q3DSObjectExtraMetaData::Data *d = extraMetaData()->data.data();
+ if (!d)
+ return;
+
+ d->propertyNames.clear();
+ d->propertyValues.clear();
+}
+
Q3DSPropertyChangeList Q3DSGraphObject::applyDynamicProperties(const QVariantMap &v)
{
Q3DSPropertyChangeList changeList;
@@ -2176,8 +2186,11 @@ void mapCustomPropertyFileNames(QVariantMap *propTab, const T &propMeta, const Q
void Q3DSCustomMaterialInstance::resolveReferences(Q3DSUipPresentation &pres)
{
// changing the material class dynamically is not supported. do it only once.
- if (!m_materialIsResolved && m_material_unresolved.startsWith('#')) {
- m_material = pres.customMaterial(m_material_unresolved.mid(1).toUtf8());
+ if (!m_materialIsResolved && !m_material_unresolved.isEmpty()) {
+ QString idOrFilename = m_material_unresolved;
+ if (!idOrFilename.startsWith(QLatin1Char('#')))
+ idOrFilename = pres.assetFileName(idOrFilename, nullptr);
+ m_material = pres.customMaterial(idOrFilename);
m_materialIsResolved = true;
if (!m_material.isNull()) {
// Now add the (dynamic) properties with the default values first.
@@ -2242,7 +2255,8 @@ template<typename V>
void Q3DSEffectInstance::setProps(const V &attrs, PropSetFlags flags)
{
const QString typeName = QStringLiteral("Effect");
- parseProperty(attrs, flags, typeName, QStringLiteral("class"), &m_effect_unresolved);
+ if (parseProperty(attrs, flags, typeName, QStringLiteral("class"), &m_effect_unresolved))
+ m_effectIsResolved = false;
parseProperty(attrs, flags, typeName, QStringLiteral("eyeball"), &m_eyeballEnabled);
@@ -2281,35 +2295,57 @@ void Q3DSEffectInstance::applyPropertyChanges(const Q3DSPropertyChangeList &chan
}
if (!propChanges.isEmpty())
- applyDynamicProperties(propChanges); // Only updates dynamic properties, if they already exists!
+ applyDynamicProperties(propChanges);
}
void Q3DSEffectInstance::resolveReferences(Q3DSUipPresentation &pres)
{
- // changing the effect class dynamically is not supported. do it only once.
- if (!m_effectIsResolved && m_effect_unresolved.startsWith('#')) {
- m_effect = pres.effect(m_effect_unresolved.mid(1).toUtf8());
- m_effectIsResolved = true;
- if (!m_effect.isNull()) {
- // Now add the (dynamic) properties with the default values first.
- const auto props = m_effect.properties();
- for (auto it = props.cbegin(); it != props.cend(); ++it) {
- // Fix up the filenames to that no further adjustment is necessary from this point on.
- QVariant value = Q3DS::convertToVariant(it->defaultValue, *it);
- mapCustomPropertyFileNames(it.key(), &value, props, pres);
- setProperty(it.key().toLatin1(), value);
- }
+ // Only do the rest if sourcepath actually changed because the custom
+ // property processing must only be done when there was a real change.
+ if (m_effectIsResolved)
+ return;
- // Now go update any pending property values
- QVariantMap propChanges;
- for (const Q3DSPropertyChange &change : m_pendingCustomProperties) {
- const Q3DS::PropertyType type = props.value(change.nameStr()).type;
- propChanges[change.nameStr()] = Q3DS::convertToVariant(change.valueStr(), type);
- }
- mapCustomPropertyFileNames(&propChanges, props, pres);
- applyDynamicProperties(propChanges);
- m_pendingCustomProperties.clear();
+ m_effectIsResolved = true;
+
+ if (m_effect_unresolved.isEmpty())
+ return;
+
+ // keep the # if it's an id since it is otherwise treated as a filename
+ // (that needs resolving when relative)
+ QString idOrFilename = m_effect_unresolved;
+ if (!idOrFilename.startsWith(QLatin1Char('#')))
+ idOrFilename = pres.assetFileName(idOrFilename, nullptr);
+
+ m_effect = pres.effect(idOrFilename);
+ if (m_effect.isNull())
+ return;
+
+ // Remove all existing dynamic properties. This may seem unnecessary but
+ // unfortunately it is needed since changing the source (class property) to
+ // a different effect brings in a different set of custom properties, and
+ // having the old ones lingering around causes trouble (or is inefficient
+ // at best).
+ clearDynamicProperties();
+
+ // Add the effect's dynamic properties with the default values first.
+ const auto props = m_effect.properties();
+ for (auto it = props.cbegin(); it != props.cend(); ++it) {
+ // Fix up the filenames to that no further adjustment is necessary from this point on.
+ QVariant value = Q3DS::convertToVariant(it->defaultValue, *it);
+ mapCustomPropertyFileNames(it.key(), &value, props, pres);
+ setProperty(it.key().toLatin1(), value);
+ }
+
+ // Now go update any pending property values (from the .uip parsing).
+ if (!m_pendingCustomProperties.isEmpty()) {
+ QVariantMap propChanges;
+ for (const Q3DSPropertyChange &change : m_pendingCustomProperties) {
+ const Q3DS::PropertyType type = props.value(change.nameStr()).type;
+ propChanges[change.nameStr()] = Q3DS::convertToVariant(change.valueStr(), type);
}
+ mapCustomPropertyFileNames(&propChanges, props, pres);
+ applyDynamicProperties(propChanges);
+ m_pendingCustomProperties.clear(); // clear, we won't apply these values again in case the source changes
}
}
@@ -2319,10 +2355,21 @@ int Q3DSEffectInstance::mapChangeFlags(const Q3DSPropertyChangeList &changeList)
for (auto it = changeList.cbegin(), itEnd = changeList.cend(); it != itEnd; ++it) {
if (it->nameStr() == QStringLiteral("eyeball"))
changeFlags |= EyeBallChanges;
+ else if (it->nameStr() == QStringLiteral("class"))
+ changeFlags |= SourceChanges;
}
return changeFlags;
}
+Q3DSPropertyChange Q3DSEffectInstance::setSourcePath(const QString &v)
+{
+ Q3DSPropertyChange r = createPropSetter(m_effect_unresolved, v, "sourcepath");
+ const bool valueChanged = !r.nameStr().isEmpty();
+ if (valueChanged)
+ m_effectIsResolved = false;
+ return r;
+}
+
Q3DSPropertyChange Q3DSEffectInstance::setEyeballEnabled(bool v)
{
return createPropSetter(m_eyeballEnabled, v, "eyeball");
@@ -2375,14 +2422,17 @@ void Q3DSBehaviorInstance::applyPropertyChanges(const Q3DSPropertyChangeList &ch
}
if (!propChanges.isEmpty())
- applyDynamicProperties(propChanges); // Only updates dynamic properties, if they already exists!
+ applyDynamicProperties(propChanges);
}
void Q3DSBehaviorInstance::resolveReferences(Q3DSUipPresentation &pres)
{
// changing the behavior class dynamically is not supported. do it only once.
- if (!m_behaviorIsResolved && m_behavior_unresolved.startsWith('#')) {
- m_behavior = pres.behavior(m_behavior_unresolved.mid(1).toUtf8());
+ if (!m_behaviorIsResolved && !m_behavior_unresolved.isEmpty()) {
+ QString idOrFilename = m_behavior_unresolved;
+ if (!idOrFilename.startsWith(QLatin1Char('#')))
+ idOrFilename = pres.assetFileName(idOrFilename, nullptr);
+ m_behavior = pres.behavior(idOrFilename);
m_behaviorIsResolved = true;
if (!m_behavior.isNull()) {
// Now add the (dynamic) properties with the default values first.
@@ -3130,7 +3180,7 @@ void Q3DSModelNode::resolveReferences(Q3DSUipPresentation &pres)
if (m_mesh_unresolved != QStringLiteral("#Custom")) {
int part = 1;
const QString fn = pres.assetFileName(m_mesh_unresolved, &part);
- m_mesh = pres.mesh(fn, part);
+ m_mesh = pres.mesh(fn, part); // this caches; cheap if already used the same mesh somewhere
} else {
// ### should this be cached? (would need a cache key then)
m_mesh = Q3DSMeshLoader::loadMesh(*m_customMesh, &m_customMeshMapping);
@@ -3897,58 +3947,63 @@ void Q3DSUipPresentation::unregisterObject(const QByteArray &id)
d->objects.remove(id);
}
-bool Q3DSUipPresentation::loadCustomMaterial(const QStringRef &id, const QStringRef &, const QString &assetFilename)
+template<typename T, typename ParserT>
+bool loadMeta(const QByteArray &id, const QString &assetFilename, QHash<QByteArray, T> *dst)
{
- Q3DSCustomMaterialParser p;
+ ParserT p;
bool ok = false;
- Q3DSCustomMaterial mat = p.parse(assetFilename, &ok);
+ T eff = p.parse(assetFilename, &ok);
if (!ok) {
- qWarning("Failed to parse custom material %s", qPrintable(assetFilename));
+ qWarning("Failed to parse metadata %s", qPrintable(assetFilename));
return false;
}
- d->customMaterials.insert(id.toUtf8(), mat);
+ dst->insert(id, eff);
return true;
}
-Q3DSCustomMaterial Q3DSUipPresentation::customMaterial(const QByteArray &id) const
+bool Q3DSUipPresentation::loadCustomMaterial(const QByteArray &id, const QString &assetFilename)
{
- return d->customMaterials.value(id);
+ return loadMeta<Q3DSCustomMaterial, Q3DSCustomMaterialParser>(id, assetFilename, &d->customMaterials);
}
-bool Q3DSUipPresentation::loadEffect(const QStringRef &id, const QStringRef &, const QString &assetFilename)
+Q3DSCustomMaterial Q3DSUipPresentation::customMaterial(const QString &idOrFilename)
{
- Q3DSEffectParser p;
- bool ok = false;
- Q3DSEffect eff = p.parse(assetFilename, &ok);
- if (!ok) {
- qWarning("Failed to parse effect %s", qPrintable(assetFilename));
- return false;
- }
- d->effects.insert(id.toUtf8(), eff);
- return true;
+ const QByteArray key = idOrFilename.toUtf8();
+ if (!key.startsWith('#') && !d->customMaterials.contains(key))
+ loadCustomMaterial(key, idOrFilename);
+
+ return d->customMaterials.value(key);
}
-Q3DSEffect Q3DSUipPresentation::effect(const QByteArray &id) const
+bool Q3DSUipPresentation::loadEffect(const QByteArray &id, const QString &assetFilename)
{
- return d->effects.value(id);
+ return loadMeta<Q3DSEffect, Q3DSEffectParser>(id, assetFilename, &d->effects);
}
-bool Q3DSUipPresentation::loadBehavior(const QStringRef &id, const QStringRef &, const QString &assetFilename)
+Q3DSEffect Q3DSUipPresentation::effect(const QString &idOrFilename)
{
- Q3DSBehaviorParser p;
- bool ok = false;
- Q3DSBehavior eff = p.parse(assetFilename, &ok);
- if (!ok) {
- qWarning("Failed to parse behavior %s", qPrintable(assetFilename));
- return false;
- }
- d->behaviors.insert(id.toUtf8(), eff);
- return true;
+ // We either have an id (starting with #) for an already loaded effect (when
+ // defined in .uip), or a filename which may or may not be already parsed.
+
+ const QByteArray key = idOrFilename.toUtf8();
+ if (!key.startsWith('#') && !d->effects.contains(key))
+ loadEffect(key, idOrFilename);
+
+ return d->effects.value(key);
+}
+
+bool Q3DSUipPresentation::loadBehavior(const QByteArray &id, const QString &assetFilename)
+{
+ return loadMeta<Q3DSBehavior, Q3DSBehaviorParser>(id, assetFilename, &d->behaviors);
}
-Q3DSBehavior Q3DSUipPresentation::behavior(const QByteArray &id) const
+Q3DSBehavior Q3DSUipPresentation::behavior(const QString &idOrFilename)
{
- return d->behaviors.value(id);
+ const QByteArray key = idOrFilename.toUtf8();
+ if (!key.startsWith('#') && !d->behaviors.contains(key))
+ loadBehavior(key, idOrFilename);
+
+ return d->behaviors.value(key);
}
MeshList Q3DSUipPresentation::mesh(const QString &assetFilename, int part)