From b6cb1fcfe723a5f84ca4854665c1d2eeb8231f6a Mon Sep 17 00:00:00 2001 From: HifiExperiments Date: Wed, 22 Apr 2020 16:36:51 -0700 Subject: [PATCH 001/174] fix procedural applying to other things --- .../src/RenderableModelEntityItem.cpp | 29 +++++++++++++++++++ .../src/RenderableModelEntityItem.h | 1 + .../src/RenderableShapeEntityItem.cpp | 6 ++-- .../render-utils/src/MeshPartPayload.cpp | 3 +- 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 5fbbdfa0b8..c70220506e 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1442,6 +1442,31 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce emit requestRenderUpdate(); } + if (!_allProceduralMaterialsLoaded) { + std::lock_guard lock(_materialsLock); + bool allProceduralMaterialsLoaded = true; + for (auto& shapeMaterialPair : _materials) { + auto material = shapeMaterialPair.second; + while (!material.empty()) { + auto mat = material.top(); + if (mat.material && mat.material->isProcedural() && !mat.material->isReady()) { + allProceduralMaterialsLoaded = false; + break; + } + material.pop(); + } + if (!allProceduralMaterialsLoaded) { + break; + } + } + if (!allProceduralMaterialsLoaded) { + emit requestRenderUpdate(); + } else { + _allProceduralMaterialsLoaded = true; + model->setRenderItemsNeedUpdate(); + } + } + // When the individual mesh parts of a model finish fading, they will mark their Model as needing updating // we will watch for that and ask the model to update it's render items if (model->getRenderItemsNeedUpdate()) { @@ -1559,6 +1584,10 @@ void ModelEntityRenderer::addMaterial(graphics::MaterialLayer material, const st if (_model && _model->fetchRenderItemIDs().size() > 0) { _model->addMaterial(material, parentMaterialName); } + if (material.material && material.material->isProcedural()) { + _allProceduralMaterialsLoaded = false; + emit requestRenderUpdate(); + } } void ModelEntityRenderer::removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 0119c7bc26..7c219422a6 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -198,6 +198,7 @@ private: bool _prevModelLoaded { false }; void processMaterials(); + bool _allProceduralMaterialsLoaded { false }; }; } } // namespace diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 222b833be2..38dd3b2160 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -32,7 +32,7 @@ bool ShapeEntityRenderer::needsRenderUpdate() const { if (resultWithReadLock([&] { auto mat = _materials.find("0"); if (mat != _materials.end() && mat->second.top().material && mat->second.top().material->isProcedural() && - mat->second.top().material->isEnabled()) { + mat->second.top().material->isReady()) { auto procedural = std::static_pointer_cast(mat->second.top().material); if (procedural->isFading()) { return true; @@ -88,7 +88,7 @@ void ShapeEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce void ShapeEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) { withReadLock([&] { auto mat = _materials.find("0"); - if (mat != _materials.end() && mat->second.top().material && mat->second.top().material->isProcedural() && mat->second.top().material->isEnabled()) { + if (mat != _materials.end() && mat->second.top().material && mat->second.top().material->isProcedural() && mat->second.top().material->isReady()) { auto procedural = std::static_pointer_cast(mat->second.top().material); if (procedural->isFading()) { procedural->setIsFading(Interpolate::calculateFadeRatio(procedural->getFadeStartTime()) < 1.0f); @@ -136,7 +136,7 @@ bool ShapeEntityRenderer::isTransparent() const { auto mat = _materials.find("0"); if (mat != _materials.end() && mat->second.top().material) { - if (mat->second.top().material->isProcedural() && mat->second.top().material->isEnabled()) { + if (mat->second.top().material->isProcedural() && mat->second.top().material->isReady()) { auto procedural = std::static_pointer_cast(mat->second.top().material); if (procedural->isFading()) { return true; diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index e82af5395f..544947ab59 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -493,8 +493,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) { batch.setDrawcallUniform(drawcallInfo); } - if (!_drawMaterials.empty() && _drawMaterials.top().material && _drawMaterials.top().material->isProcedural() && - _drawMaterials.top().material->isReady()) { + if (_shapeKey.hasOwnPipeline()) { if (!(enableMaterialProceduralShaders)) { return; } From 1ea78c6ab56272b3c94bca383fb4d7b1b2d29ee7 Mon Sep 17 00:00:00 2001 From: Kasen IO Date: Fri, 17 Jul 2020 05:22:59 -0400 Subject: [PATCH 002/174] Enable automatic avatar and audio mixer threading defaults --- domain-server/resources/describe-settings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index 3ae92651b8..2adca55554 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -1075,7 +1075,7 @@ "label": "Automatically determine thread count", "type": "checkbox", "help": "Allow system to determine number of threads (recommended)", - "default": false, + "default": true, "advanced": true }, { @@ -1365,7 +1365,7 @@ "label": "Automatically determine thread count", "type": "checkbox", "help": "Allow system to determine number of threads (recommended)", - "default": false, + "default": true, "advanced": true }, { From 61e6e8dfc9b39c85a4e2865012c1e47723717376 Mon Sep 17 00:00:00 2001 From: Heather Anderson Date: Wed, 22 Apr 2020 23:16:51 -0700 Subject: [PATCH 003/174] Restructure ContextAwareProfile from a (thread-blocking) polling to a threadsafe-notification --- .../ui/src/ui/types/ContextAwareProfile.cpp | 65 +++++++++++++++---- .../ui/src/ui/types/ContextAwareProfile.h | 40 ++++++++++-- 2 files changed, 89 insertions(+), 16 deletions(-) diff --git a/libraries/ui/src/ui/types/ContextAwareProfile.cpp b/libraries/ui/src/ui/types/ContextAwareProfile.cpp index f74e8754c9..d5b8d958b6 100644 --- a/libraries/ui/src/ui/types/ContextAwareProfile.cpp +++ b/libraries/ui/src/ui/types/ContextAwareProfile.cpp @@ -20,20 +20,62 @@ static const QString RESTRICTED_FLAG_PROPERTY = "RestrictFileAccess"; -ContextAwareProfile::ContextAwareProfile(QQmlContext* context) : - ContextAwareProfileParent(context), _context(context) { - assert(context); +QMutex RestrictedContextMonitor::gl_monitorMapProtect; +RestrictedContextMonitor::TMonitorMap RestrictedContextMonitor::gl_monitorMap; + +RestrictedContextMonitor::~RestrictedContextMonitor() { + gl_monitorMapProtect.lock(); + TMonitorMap::iterator lookup = gl_monitorMap.find(context); + if (lookup != gl_monitorMap.end()) { + gl_monitorMap.erase(lookup); + } + gl_monitorMapProtect.unlock(); } +RestrictedContextMonitor::TSharedPtr RestrictedContextMonitor::getMonitor(QQmlContext* context, bool createIfMissing) { + TSharedPtr monitor; + + gl_monitorMapProtect.lock(); + TMonitorMap::const_iterator lookup = gl_monitorMap.find(context); + if (lookup != gl_monitorMap.end()) { + monitor = lookup->second.lock(); + assert(monitor); + } else if(createIfMissing) { + monitor = std::make_shared(context); + monitor->selfPtr = monitor; + gl_monitorMap.insert(TMonitorMap::value_type(context, monitor)); + } + gl_monitorMapProtect.unlock(); + return monitor; +} + +ContextAwareProfile::ContextAwareProfile(QQmlContext* context) : ContextAwareProfileParent(context), _context(context) { + assert(context); + + _monitor = RestrictedContextMonitor::getMonitor(context, true); + assert(_monitor); + connect(_monitor.get(), &RestrictedContextMonitor::onIsRestrictedChanged, this, &ContextAwareProfile::onIsRestrictedChanged); + if (_monitor->isUninitialized) { + _monitor->isRestricted = isRestrictedGetProperty(); + _monitor->isUninitialized = false; + } + _isRestricted.store(_monitor->isRestricted ? 1 : 0); +} void ContextAwareProfile::restrictContext(QQmlContext* context, bool restrict) { + RestrictedContextMonitor::TSharedPtr monitor = RestrictedContextMonitor::getMonitor(context, false); + context->setContextProperty(RESTRICTED_FLAG_PROPERTY, restrict); + if (monitor && monitor->isRestricted != restrict) { + monitor->isRestricted = restrict; + monitor->onIsRestrictedChanged(restrict); + } } -bool ContextAwareProfile::isRestrictedInternal() { +bool ContextAwareProfile::isRestrictedGetProperty() { if (QThread::currentThread() != thread()) { bool restrictedResult = false; - BLOCKING_INVOKE_METHOD(this, "isRestrictedInternal", Q_RETURN_ARG(bool, restrictedResult)); + BLOCKING_INVOKE_METHOD(this, "isRestrictedGetProperty", Q_RETURN_ARG(bool, restrictedResult)); return restrictedResult; } @@ -47,11 +89,10 @@ bool ContextAwareProfile::isRestrictedInternal() { return true; } -bool ContextAwareProfile::isRestricted() { - auto now = usecTimestampNow(); - if (now > _cacheExpiry) { - _cachedValue = isRestrictedInternal(); - _cacheExpiry = now + MAX_CACHE_AGE; - } - return _cachedValue; +void ContextAwareProfile::onIsRestrictedChanged(bool newValue) { + _isRestricted.store(newValue ? 1 : 0); +} + +bool ContextAwareProfile::isRestricted() { + return _isRestricted.load() != 0; } diff --git a/libraries/ui/src/ui/types/ContextAwareProfile.h b/libraries/ui/src/ui/types/ContextAwareProfile.h index 3192d2be54..30bdad4b78 100644 --- a/libraries/ui/src/ui/types/ContextAwareProfile.h +++ b/libraries/ui/src/ui/types/ContextAwareProfile.h @@ -12,6 +12,7 @@ #define hifi_ContextAwareProfile_h #include +#include #if !defined(Q_OS_ANDROID) #include @@ -30,12 +31,39 @@ using RequestInterceptorParent = QObject; class QQmlContext; +class RestrictedContextMonitor : public QObject { + Q_OBJECT +public: + typedef std::shared_ptr TSharedPtr; + typedef std::weak_ptr TWeakPtr; + + inline RestrictedContextMonitor(QQmlContext* c) : context(c) {} + ~RestrictedContextMonitor(); + + static TSharedPtr getMonitor(QQmlContext* context, bool createIfMissing); + +signals: + void onIsRestrictedChanged(bool newValue); + +public: + TWeakPtr selfPtr; + QQmlContext* context{ nullptr }; + bool isRestricted{ true }; + bool isUninitialized{ true }; + +private: + typedef std::map TMonitorMap; + + static QMutex gl_monitorMapProtect; + static TMonitorMap gl_monitorMap; +}; + class ContextAwareProfile : public ContextAwareProfileParent { Q_OBJECT public: static void restrictContext(QQmlContext* context, bool restrict = true); bool isRestricted(); - Q_INVOKABLE bool isRestrictedInternal(); + Q_INVOKABLE bool isRestrictedGetProperty(); protected: class RequestInterceptor : public RequestInterceptorParent { @@ -48,9 +76,13 @@ protected: ContextAwareProfile(QQmlContext* parent); QQmlContext* _context{ nullptr }; - bool _cachedValue{ false }; - quint64 _cacheExpiry{ 0 }; - constexpr static quint64 MAX_CACHE_AGE = MSECS_PER_SECOND; + QAtomicInt _isRestricted{ 0 }; + +private slots: + void onIsRestrictedChanged(bool newValue); + +private: + RestrictedContextMonitor::TSharedPtr _monitor; }; #endif // hifi_FileTypeProfile_h From 84a8c640e4f68fe9a9d7104a3acabdb53b5a22d8 Mon Sep 17 00:00:00 2001 From: Heather Anderson Date: Fri, 15 May 2020 13:42:29 -0700 Subject: [PATCH 004/174] cleaned up variables names, replaced std::shared_ptr with QSharedPointer --- .../ui/src/ui/types/ContextAwareProfile.cpp | 24 +++++++++---------- .../ui/src/ui/types/ContextAwareProfile.h | 21 ++++++++-------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/libraries/ui/src/ui/types/ContextAwareProfile.cpp b/libraries/ui/src/ui/types/ContextAwareProfile.cpp index d5b8d958b6..0374de87ff 100644 --- a/libraries/ui/src/ui/types/ContextAwareProfile.cpp +++ b/libraries/ui/src/ui/types/ContextAwareProfile.cpp @@ -25,15 +25,15 @@ RestrictedContextMonitor::TMonitorMap RestrictedContextMonitor::gl_monitorMap; RestrictedContextMonitor::~RestrictedContextMonitor() { gl_monitorMapProtect.lock(); - TMonitorMap::iterator lookup = gl_monitorMap.find(context); + TMonitorMap::iterator lookup = gl_monitorMap.find(_context); if (lookup != gl_monitorMap.end()) { gl_monitorMap.erase(lookup); } gl_monitorMapProtect.unlock(); } -RestrictedContextMonitor::TSharedPtr RestrictedContextMonitor::getMonitor(QQmlContext* context, bool createIfMissing) { - TSharedPtr monitor; +RestrictedContextMonitor::TSharedPointer RestrictedContextMonitor::getMonitor(QQmlContext* context, bool createIfMissing) { + TSharedPointer monitor; gl_monitorMapProtect.lock(); TMonitorMap::const_iterator lookup = gl_monitorMap.find(context); @@ -41,8 +41,8 @@ RestrictedContextMonitor::TSharedPtr RestrictedContextMonitor::getMonitor(QQmlCo monitor = lookup->second.lock(); assert(monitor); } else if(createIfMissing) { - monitor = std::make_shared(context); - monitor->selfPtr = monitor; + monitor = TSharedPointer::create(context); + monitor->_selfPointer = monitor; gl_monitorMap.insert(TMonitorMap::value_type(context, monitor)); } gl_monitorMapProtect.unlock(); @@ -55,19 +55,19 @@ ContextAwareProfile::ContextAwareProfile(QQmlContext* context) : ContextAwarePro _monitor = RestrictedContextMonitor::getMonitor(context, true); assert(_monitor); connect(_monitor.get(), &RestrictedContextMonitor::onIsRestrictedChanged, this, &ContextAwareProfile::onIsRestrictedChanged); - if (_monitor->isUninitialized) { - _monitor->isRestricted = isRestrictedGetProperty(); - _monitor->isUninitialized = false; + if (_monitor->_isUninitialized) { + _monitor->_isRestricted = isRestrictedGetProperty(); + _monitor->_isUninitialized = false; } - _isRestricted.store(_monitor->isRestricted ? 1 : 0); + _isRestricted.store(_monitor->_isRestricted ? 1 : 0); } void ContextAwareProfile::restrictContext(QQmlContext* context, bool restrict) { - RestrictedContextMonitor::TSharedPtr monitor = RestrictedContextMonitor::getMonitor(context, false); + RestrictedContextMonitor::TSharedPointer monitor = RestrictedContextMonitor::getMonitor(context, false); context->setContextProperty(RESTRICTED_FLAG_PROPERTY, restrict); - if (monitor && monitor->isRestricted != restrict) { - monitor->isRestricted = restrict; + if (monitor && monitor->_isRestricted != restrict) { + monitor->_isRestricted = restrict; monitor->onIsRestrictedChanged(restrict); } } diff --git a/libraries/ui/src/ui/types/ContextAwareProfile.h b/libraries/ui/src/ui/types/ContextAwareProfile.h index 30bdad4b78..ec0590eda5 100644 --- a/libraries/ui/src/ui/types/ContextAwareProfile.h +++ b/libraries/ui/src/ui/types/ContextAwareProfile.h @@ -13,6 +13,7 @@ #include #include +#include #if !defined(Q_OS_ANDROID) #include @@ -34,25 +35,25 @@ class QQmlContext; class RestrictedContextMonitor : public QObject { Q_OBJECT public: - typedef std::shared_ptr TSharedPtr; - typedef std::weak_ptr TWeakPtr; + typedef QSharedPointer TSharedPointer; + typedef QWeakPointer TWeakPointer; - inline RestrictedContextMonitor(QQmlContext* c) : context(c) {} + inline RestrictedContextMonitor(QQmlContext* c) : _context(c) {} ~RestrictedContextMonitor(); - static TSharedPtr getMonitor(QQmlContext* context, bool createIfMissing); + static TSharedPointer getMonitor(QQmlContext* context, bool createIfMissing); signals: void onIsRestrictedChanged(bool newValue); public: - TWeakPtr selfPtr; - QQmlContext* context{ nullptr }; - bool isRestricted{ true }; - bool isUninitialized{ true }; + TWeakPointer _selfPointer; + QQmlContext* _context{ nullptr }; + bool _isRestricted{ true }; + bool _isUninitialized{ true }; private: - typedef std::map TMonitorMap; + typedef std::map TMonitorMap; static QMutex gl_monitorMapProtect; static TMonitorMap gl_monitorMap; @@ -82,7 +83,7 @@ private slots: void onIsRestrictedChanged(bool newValue); private: - RestrictedContextMonitor::TSharedPtr _monitor; + RestrictedContextMonitor::TSharedPointer _monitor; }; #endif // hifi_FileTypeProfile_h From 3cff3f8b5deaba040c519adb8e43b619f5b55906 Mon Sep 17 00:00:00 2001 From: Heather Anderson Date: Mon, 25 May 2020 15:51:48 -0700 Subject: [PATCH 005/174] found and removed another STL-ism --- libraries/ui/src/ui/types/ContextAwareProfile.cpp | 4 ++-- libraries/ui/src/ui/types/ContextAwareProfile.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/ui/src/ui/types/ContextAwareProfile.cpp b/libraries/ui/src/ui/types/ContextAwareProfile.cpp index 0374de87ff..ef17ad7229 100644 --- a/libraries/ui/src/ui/types/ContextAwareProfile.cpp +++ b/libraries/ui/src/ui/types/ContextAwareProfile.cpp @@ -38,12 +38,12 @@ RestrictedContextMonitor::TSharedPointer RestrictedContextMonitor::getMonitor(QQ gl_monitorMapProtect.lock(); TMonitorMap::const_iterator lookup = gl_monitorMap.find(context); if (lookup != gl_monitorMap.end()) { - monitor = lookup->second.lock(); + monitor = lookup.value().lock(); assert(monitor); } else if(createIfMissing) { monitor = TSharedPointer::create(context); monitor->_selfPointer = monitor; - gl_monitorMap.insert(TMonitorMap::value_type(context, monitor)); + gl_monitorMap.insert(context, monitor); } gl_monitorMapProtect.unlock(); return monitor; diff --git a/libraries/ui/src/ui/types/ContextAwareProfile.h b/libraries/ui/src/ui/types/ContextAwareProfile.h index ec0590eda5..99fee8112d 100644 --- a/libraries/ui/src/ui/types/ContextAwareProfile.h +++ b/libraries/ui/src/ui/types/ContextAwareProfile.h @@ -12,6 +12,7 @@ #define hifi_ContextAwareProfile_h #include +#include #include #include @@ -53,7 +54,7 @@ public: bool _isUninitialized{ true }; private: - typedef std::map TMonitorMap; + typedef QMap TMonitorMap; static QMutex gl_monitorMapProtect; static TMonitorMap gl_monitorMap; From 47c96fcff56218275cf9145fe962140200b14373 Mon Sep 17 00:00:00 2001 From: Heather Anderson Date: Thu, 28 May 2020 00:03:39 -0700 Subject: [PATCH 006/174] (re-)discovered QMutexLocker, good for tightening up the code a bit --- libraries/ui/src/ui/types/ContextAwareProfile.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/libraries/ui/src/ui/types/ContextAwareProfile.cpp b/libraries/ui/src/ui/types/ContextAwareProfile.cpp index ef17ad7229..2399471e29 100644 --- a/libraries/ui/src/ui/types/ContextAwareProfile.cpp +++ b/libraries/ui/src/ui/types/ContextAwareProfile.cpp @@ -12,6 +12,7 @@ #include "ContextAwareProfile.h" #include +#include #include #include @@ -24,18 +25,17 @@ QMutex RestrictedContextMonitor::gl_monitorMapProtect; RestrictedContextMonitor::TMonitorMap RestrictedContextMonitor::gl_monitorMap; RestrictedContextMonitor::~RestrictedContextMonitor() { - gl_monitorMapProtect.lock(); + QMutexLocker locker(&gl_monitorMapProtect); TMonitorMap::iterator lookup = gl_monitorMap.find(_context); if (lookup != gl_monitorMap.end()) { gl_monitorMap.erase(lookup); } - gl_monitorMapProtect.unlock(); } RestrictedContextMonitor::TSharedPointer RestrictedContextMonitor::getMonitor(QQmlContext* context, bool createIfMissing) { TSharedPointer monitor; - gl_monitorMapProtect.lock(); + QMutexLocker locker(&gl_monitorMapProtect); TMonitorMap::const_iterator lookup = gl_monitorMap.find(context); if (lookup != gl_monitorMap.end()) { monitor = lookup.value().lock(); @@ -45,7 +45,6 @@ RestrictedContextMonitor::TSharedPointer RestrictedContextMonitor::getMonitor(QQ monitor->_selfPointer = monitor; gl_monitorMap.insert(context, monitor); } - gl_monitorMapProtect.unlock(); return monitor; } From 4f5f46a6237a693e0c7eb81154b58f11f870e159 Mon Sep 17 00:00:00 2001 From: Heather Anderson Date: Thu, 30 Jul 2020 22:49:54 -0700 Subject: [PATCH 007/174] restructured the code to using a set of ContextAwareProfile objects rather than subscribing to an intermediate object changed _isRestricted from QAtomicInt to std::atomic --- .../ui/src/ui/types/ContextAwareProfile.cpp | 87 ++++++++++--------- .../ui/src/ui/types/ContextAwareProfile.h | 48 +++------- 2 files changed, 59 insertions(+), 76 deletions(-) diff --git a/libraries/ui/src/ui/types/ContextAwareProfile.cpp b/libraries/ui/src/ui/types/ContextAwareProfile.cpp index 2399471e29..5df2d34dfa 100644 --- a/libraries/ui/src/ui/types/ContextAwareProfile.cpp +++ b/libraries/ui/src/ui/types/ContextAwareProfile.cpp @@ -12,8 +12,9 @@ #include "ContextAwareProfile.h" #include -#include +#include #include +#include #include #include @@ -21,53 +22,59 @@ static const QString RESTRICTED_FLAG_PROPERTY = "RestrictFileAccess"; -QMutex RestrictedContextMonitor::gl_monitorMapProtect; -RestrictedContextMonitor::TMonitorMap RestrictedContextMonitor::gl_monitorMap; - -RestrictedContextMonitor::~RestrictedContextMonitor() { - QMutexLocker locker(&gl_monitorMapProtect); - TMonitorMap::iterator lookup = gl_monitorMap.find(_context); - if (lookup != gl_monitorMap.end()) { - gl_monitorMap.erase(lookup); - } -} - -RestrictedContextMonitor::TSharedPointer RestrictedContextMonitor::getMonitor(QQmlContext* context, bool createIfMissing) { - TSharedPointer monitor; - - QMutexLocker locker(&gl_monitorMapProtect); - TMonitorMap::const_iterator lookup = gl_monitorMap.find(context); - if (lookup != gl_monitorMap.end()) { - monitor = lookup.value().lock(); - assert(monitor); - } else if(createIfMissing) { - monitor = TSharedPointer::create(context); - monitor->_selfPointer = monitor; - gl_monitorMap.insert(context, monitor); - } - return monitor; -} +QReadWriteLock ContextAwareProfile::gl_contextMapProtect; +ContextAwareProfile::ContextMap ContextAwareProfile::gl_contextMap; ContextAwareProfile::ContextAwareProfile(QQmlContext* context) : ContextAwareProfileParent(context), _context(context) { assert(context); - _monitor = RestrictedContextMonitor::getMonitor(context, true); - assert(_monitor); - connect(_monitor.get(), &RestrictedContextMonitor::onIsRestrictedChanged, this, &ContextAwareProfile::onIsRestrictedChanged); - if (_monitor->_isUninitialized) { - _monitor->_isRestricted = isRestrictedGetProperty(); - _monitor->_isUninitialized = false; + { // register our object for future updates + QWriteLocker guard(&gl_contextMapProtect); + ContextMap::iterator setLookup = gl_contextMap.find(_context); + if (setLookup == gl_contextMap.end()) { + setLookup = gl_contextMap.insert(_context, ContextAwareProfileSet()); + } + assert(setLookup != gl_contextMap.end()); + ContextAwareProfileSet& profileSet = setLookup.value(); + assert(profileSet.find(this) == profileSet.end()); + profileSet.insert(this); + } + + _isRestricted.store(isRestrictedGetProperty()); +} + +ContextAwareProfile::~ContextAwareProfile() { + { // deregister our object + QWriteLocker guard(&gl_contextMapProtect); + ContextMap::iterator setLookup = gl_contextMap.find(_context); + assert(setLookup != gl_contextMap.end()); + if (setLookup != gl_contextMap.end()) { + ContextAwareProfileSet& profileSet = setLookup.value(); + assert(profileSet.find(this) != profileSet.end()); + profileSet.remove(this); + if (profileSet.isEmpty()) { + gl_contextMap.erase(setLookup); + } + } } - _isRestricted.store(_monitor->_isRestricted ? 1 : 0); } void ContextAwareProfile::restrictContext(QQmlContext* context, bool restrict) { - RestrictedContextMonitor::TSharedPointer monitor = RestrictedContextMonitor::getMonitor(context, false); + // set the QML property context->setContextProperty(RESTRICTED_FLAG_PROPERTY, restrict); - if (monitor && monitor->_isRestricted != restrict) { - monitor->_isRestricted = restrict; - monitor->onIsRestrictedChanged(restrict); + + // broadcast the new value to any registered ContextAwareProfile objects + { // deregister our object + QReadLocker guard(&gl_contextMapProtect); + ContextMap::const_iterator setLookup = gl_contextMap.find(context); + if (setLookup != gl_contextMap.end()) { + const ContextAwareProfileSet& profileSet = setLookup.value(); + for (ContextAwareProfileSet::const_iterator profileIterator = profileSet.begin(); + profileIterator != profileSet.end(); profileIterator++) { + (*profileIterator)->onIsRestrictedChanged(restrict); + } + } } } @@ -89,9 +96,9 @@ bool ContextAwareProfile::isRestrictedGetProperty() { } void ContextAwareProfile::onIsRestrictedChanged(bool newValue) { - _isRestricted.store(newValue ? 1 : 0); + _isRestricted.store(newValue); } bool ContextAwareProfile::isRestricted() { - return _isRestricted.load() != 0; + return _isRestricted.load(); } diff --git a/libraries/ui/src/ui/types/ContextAwareProfile.h b/libraries/ui/src/ui/types/ContextAwareProfile.h index 99fee8112d..d8ec762858 100644 --- a/libraries/ui/src/ui/types/ContextAwareProfile.h +++ b/libraries/ui/src/ui/types/ContextAwareProfile.h @@ -11,9 +11,10 @@ #ifndef hifi_ContextAwareProfile_h #define hifi_ContextAwareProfile_h -#include +#include #include -#include +#include +#include #include #if !defined(Q_OS_ANDROID) @@ -29,37 +30,8 @@ using ContextAwareProfileParent = QObject; using RequestInterceptorParent = QObject; #endif -#include - class QQmlContext; -class RestrictedContextMonitor : public QObject { - Q_OBJECT -public: - typedef QSharedPointer TSharedPointer; - typedef QWeakPointer TWeakPointer; - - inline RestrictedContextMonitor(QQmlContext* c) : _context(c) {} - ~RestrictedContextMonitor(); - - static TSharedPointer getMonitor(QQmlContext* context, bool createIfMissing); - -signals: - void onIsRestrictedChanged(bool newValue); - -public: - TWeakPointer _selfPointer; - QQmlContext* _context{ nullptr }; - bool _isRestricted{ true }; - bool _isUninitialized{ true }; - -private: - typedef QMap TMonitorMap; - - static QMutex gl_monitorMapProtect; - static TMonitorMap gl_monitorMap; -}; - class ContextAwareProfile : public ContextAwareProfileParent { Q_OBJECT public: @@ -77,14 +49,18 @@ protected: }; ContextAwareProfile(QQmlContext* parent); - QQmlContext* _context{ nullptr }; - QAtomicInt _isRestricted{ 0 }; - -private slots: + ~ContextAwareProfile(); void onIsRestrictedChanged(bool newValue); + QQmlContext* _context{ nullptr }; + std::atomic _isRestricted{ false }; + private: - RestrictedContextMonitor::TSharedPointer _monitor; + typedef QSet ContextAwareProfileSet; + typedef QMap ContextMap; + + static QReadWriteLock gl_contextMapProtect; + static ContextMap gl_contextMap; }; #endif // hifi_FileTypeProfile_h From 0c7aab1556e1e2a9186787fd251c23245e993a8f Mon Sep 17 00:00:00 2001 From: Heather Anderson Date: Sun, 2 Aug 2020 14:38:55 -0700 Subject: [PATCH 008/174] minor code review --- .../ui/src/ui/types/ContextAwareProfile.cpp | 40 +++++++++---------- .../ui/src/ui/types/ContextAwareProfile.h | 15 +++---- 2 files changed, 27 insertions(+), 28 deletions(-) diff --git a/libraries/ui/src/ui/types/ContextAwareProfile.cpp b/libraries/ui/src/ui/types/ContextAwareProfile.cpp index 5df2d34dfa..ee823ec784 100644 --- a/libraries/ui/src/ui/types/ContextAwareProfile.cpp +++ b/libraries/ui/src/ui/types/ContextAwareProfile.cpp @@ -22,19 +22,19 @@ static const QString RESTRICTED_FLAG_PROPERTY = "RestrictFileAccess"; -QReadWriteLock ContextAwareProfile::gl_contextMapProtect; -ContextAwareProfile::ContextMap ContextAwareProfile::gl_contextMap; +QReadWriteLock ContextAwareProfile::_global_contextMapProtect; +ContextAwareProfile::ContextMap ContextAwareProfile::_global_contextMap; ContextAwareProfile::ContextAwareProfile(QQmlContext* context) : ContextAwareProfileParent(context), _context(context) { assert(context); { // register our object for future updates - QWriteLocker guard(&gl_contextMapProtect); - ContextMap::iterator setLookup = gl_contextMap.find(_context); - if (setLookup == gl_contextMap.end()) { - setLookup = gl_contextMap.insert(_context, ContextAwareProfileSet()); + QWriteLocker guard(&_global_contextMapProtect); + ContextMap::iterator setLookup = _global_contextMap.find(_context); + if (setLookup == _global_contextMap.end()) { + setLookup = _global_contextMap.insert(_context, ContextAwareProfileSet()); } - assert(setLookup != gl_contextMap.end()); + assert(setLookup != _global_contextMap.end()); ContextAwareProfileSet& profileSet = setLookup.value(); assert(profileSet.find(this) == profileSet.end()); profileSet.insert(this); @@ -45,15 +45,15 @@ ContextAwareProfile::ContextAwareProfile(QQmlContext* context) : ContextAwarePro ContextAwareProfile::~ContextAwareProfile() { { // deregister our object - QWriteLocker guard(&gl_contextMapProtect); - ContextMap::iterator setLookup = gl_contextMap.find(_context); - assert(setLookup != gl_contextMap.end()); - if (setLookup != gl_contextMap.end()) { + QWriteLocker guard(&_global_contextMapProtect); + ContextMap::iterator setLookup = _global_contextMap.find(_context); + assert(setLookup != _global_contextMap.end()); + if (setLookup != _global_contextMap.end()) { ContextAwareProfileSet& profileSet = setLookup.value(); assert(profileSet.find(this) != profileSet.end()); profileSet.remove(this); if (profileSet.isEmpty()) { - gl_contextMap.erase(setLookup); + _global_contextMap.erase(setLookup); } } } @@ -65,15 +65,13 @@ void ContextAwareProfile::restrictContext(QQmlContext* context, bool restrict) { context->setContextProperty(RESTRICTED_FLAG_PROPERTY, restrict); // broadcast the new value to any registered ContextAwareProfile objects - { // deregister our object - QReadLocker guard(&gl_contextMapProtect); - ContextMap::const_iterator setLookup = gl_contextMap.find(context); - if (setLookup != gl_contextMap.end()) { - const ContextAwareProfileSet& profileSet = setLookup.value(); - for (ContextAwareProfileSet::const_iterator profileIterator = profileSet.begin(); - profileIterator != profileSet.end(); profileIterator++) { - (*profileIterator)->onIsRestrictedChanged(restrict); - } + QReadLocker guard(&_global_contextMapProtect); + ContextMap::const_iterator setLookup = _global_contextMap.find(context); + if (setLookup != _global_contextMap.end()) { + const ContextAwareProfileSet& profileSet = setLookup.value(); + for (ContextAwareProfileSet::const_iterator profileIterator = profileSet.begin(); + profileIterator != profileSet.end(); profileIterator++) { + (*profileIterator)->onIsRestrictedChanged(restrict); } } } diff --git a/libraries/ui/src/ui/types/ContextAwareProfile.h b/libraries/ui/src/ui/types/ContextAwareProfile.h index d8ec762858..c6f3020c4f 100644 --- a/libraries/ui/src/ui/types/ContextAwareProfile.h +++ b/libraries/ui/src/ui/types/ContextAwareProfile.h @@ -12,7 +12,7 @@ #define hifi_ContextAwareProfile_h #include -#include +#include #include #include #include @@ -50,17 +50,18 @@ protected: ContextAwareProfile(QQmlContext* parent); ~ContextAwareProfile(); - void onIsRestrictedChanged(bool newValue); + +private: + typedef QSet ContextAwareProfileSet; + typedef QHash ContextMap; QQmlContext* _context{ nullptr }; std::atomic _isRestricted{ false }; -private: - typedef QSet ContextAwareProfileSet; - typedef QMap ContextMap; + static QReadWriteLock _global_contextMapProtect; + static ContextMap _global_contextMap; - static QReadWriteLock gl_contextMapProtect; - static ContextMap gl_contextMap; + void onIsRestrictedChanged(bool newValue); }; #endif // hifi_FileTypeProfile_h From 37c613c3d440a7e2a80fa0b4133a35fc89661462 Mon Sep 17 00:00:00 2001 From: Heather Anderson Date: Mon, 3 Aug 2020 20:50:10 -0700 Subject: [PATCH 009/174] more changes from code review --- .../ui/src/ui/types/ContextAwareProfile.cpp | 47 +++++++++---------- .../ui/src/ui/types/ContextAwareProfile.h | 6 +-- 2 files changed, 23 insertions(+), 30 deletions(-) diff --git a/libraries/ui/src/ui/types/ContextAwareProfile.cpp b/libraries/ui/src/ui/types/ContextAwareProfile.cpp index ee823ec784..210e1f36b1 100644 --- a/libraries/ui/src/ui/types/ContextAwareProfile.cpp +++ b/libraries/ui/src/ui/types/ContextAwareProfile.cpp @@ -22,19 +22,19 @@ static const QString RESTRICTED_FLAG_PROPERTY = "RestrictFileAccess"; -QReadWriteLock ContextAwareProfile::_global_contextMapProtect; -ContextAwareProfile::ContextMap ContextAwareProfile::_global_contextMap; +QReadWriteLock ContextAwareProfile::_contextMapProtect; +ContextAwareProfile::ContextMap ContextAwareProfile::_contextMap; ContextAwareProfile::ContextAwareProfile(QQmlContext* context) : ContextAwareProfileParent(context), _context(context) { assert(context); { // register our object for future updates - QWriteLocker guard(&_global_contextMapProtect); - ContextMap::iterator setLookup = _global_contextMap.find(_context); - if (setLookup == _global_contextMap.end()) { - setLookup = _global_contextMap.insert(_context, ContextAwareProfileSet()); + QWriteLocker guard(&_contextMapProtect); + ContextMap::iterator setLookup = _contextMap.find(_context); + if (setLookup == _contextMap.end()) { + setLookup = _contextMap.insert(_context, ContextAwareProfileSet()); } - assert(setLookup != _global_contextMap.end()); + assert(setLookup != _contextMap.end()); ContextAwareProfileSet& profileSet = setLookup.value(); assert(profileSet.find(this) == profileSet.end()); profileSet.insert(this); @@ -44,17 +44,16 @@ ContextAwareProfile::ContextAwareProfile(QQmlContext* context) : ContextAwarePro } ContextAwareProfile::~ContextAwareProfile() { - { // deregister our object - QWriteLocker guard(&_global_contextMapProtect); - ContextMap::iterator setLookup = _global_contextMap.find(_context); - assert(setLookup != _global_contextMap.end()); - if (setLookup != _global_contextMap.end()) { - ContextAwareProfileSet& profileSet = setLookup.value(); - assert(profileSet.find(this) != profileSet.end()); - profileSet.remove(this); - if (profileSet.isEmpty()) { - _global_contextMap.erase(setLookup); - } + // deregister our object + QWriteLocker guard(&_contextMapProtect); + ContextMap::iterator setLookup = _contextMap.find(_context); + assert(setLookup != _contextMap.end()); + if (setLookup != _contextMap.end()) { + ContextAwareProfileSet& profileSet = setLookup.value(); + assert(profileSet.find(this) != profileSet.end()); + profileSet.remove(this); + if (profileSet.isEmpty()) { + _contextMap.erase(setLookup); } } } @@ -65,13 +64,13 @@ void ContextAwareProfile::restrictContext(QQmlContext* context, bool restrict) { context->setContextProperty(RESTRICTED_FLAG_PROPERTY, restrict); // broadcast the new value to any registered ContextAwareProfile objects - QReadLocker guard(&_global_contextMapProtect); - ContextMap::const_iterator setLookup = _global_contextMap.find(context); - if (setLookup != _global_contextMap.end()) { + QReadLocker guard(&_contextMapProtect); + ContextMap::const_iterator setLookup = _contextMap.find(context); + if (setLookup != _contextMap.end()) { const ContextAwareProfileSet& profileSet = setLookup.value(); for (ContextAwareProfileSet::const_iterator profileIterator = profileSet.begin(); profileIterator != profileSet.end(); profileIterator++) { - (*profileIterator)->onIsRestrictedChanged(restrict); + (*profileIterator)->_isRestricted.store(restrict); } } } @@ -93,10 +92,6 @@ bool ContextAwareProfile::isRestrictedGetProperty() { return true; } -void ContextAwareProfile::onIsRestrictedChanged(bool newValue) { - _isRestricted.store(newValue); -} - bool ContextAwareProfile::isRestricted() { return _isRestricted.load(); } diff --git a/libraries/ui/src/ui/types/ContextAwareProfile.h b/libraries/ui/src/ui/types/ContextAwareProfile.h index c6f3020c4f..486ac5481a 100644 --- a/libraries/ui/src/ui/types/ContextAwareProfile.h +++ b/libraries/ui/src/ui/types/ContextAwareProfile.h @@ -58,10 +58,8 @@ private: QQmlContext* _context{ nullptr }; std::atomic _isRestricted{ false }; - static QReadWriteLock _global_contextMapProtect; - static ContextMap _global_contextMap; - - void onIsRestrictedChanged(bool newValue); + static QReadWriteLock _contextMapProtect; + static ContextMap _contextMap; }; #endif // hifi_FileTypeProfile_h From 1601ea9231d19bc6cffe1b98df124e9c7c861154 Mon Sep 17 00:00:00 2001 From: Kasen IO Date: Fri, 7 Aug 2020 15:08:00 -0400 Subject: [PATCH 010/174] Just to prove it works... needs to be done better. --- .../resources/qml/+webengine/BrowserWebView.qml | 3 ++- .../resources/qml/+webengine/QmlWebWindowView.qml | 1 + interface/resources/qml/Web3DSurface.qml | 2 ++ .../controls/+webengine/FlickableWebViewCore.qml | 1 + interface/resources/qml/controls/WebView.qml | 1 + scripts/test.html | 15 +++++++++++++++ 6 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 scripts/test.html diff --git a/interface/resources/qml/+webengine/BrowserWebView.qml b/interface/resources/qml/+webengine/BrowserWebView.qml index fcf4b39a2e..ad837ad043 100644 --- a/interface/resources/qml/+webengine/BrowserWebView.qml +++ b/interface/resources/qml/+webengine/BrowserWebView.qml @@ -8,7 +8,8 @@ WebView { id: webview url: "https://vircadia.com/" profile: FileTypeProfile; - + // backgroundColor: "transparent" + property var parentRoot: null // Create a global EventBridge object for raiseAndLowerKeyboard. diff --git a/interface/resources/qml/+webengine/QmlWebWindowView.qml b/interface/resources/qml/+webengine/QmlWebWindowView.qml index 84ab61ad28..dfabc631ba 100644 --- a/interface/resources/qml/+webengine/QmlWebWindowView.qml +++ b/interface/resources/qml/+webengine/QmlWebWindowView.qml @@ -10,6 +10,7 @@ Controls.WebView { anchors.fill: parent focus: true profile: HFWebEngineProfile; + // backgroundColor: "transparent" property string userScriptUrl: "" diff --git a/interface/resources/qml/Web3DSurface.qml b/interface/resources/qml/Web3DSurface.qml index d58bcd2eba..39ee85444d 100644 --- a/interface/resources/qml/Web3DSurface.qml +++ b/interface/resources/qml/Web3DSurface.qml @@ -3,6 +3,7 @@ // // Created by David Rowe on 16 Dec 2016. // Copyright 2016 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -43,6 +44,7 @@ Item { root.item = newItem root.item.url = url root.item.scriptUrl = scriptUrl + root.item.transparentBackground = true }) } diff --git a/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml b/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml index e9546748c0..6fbfcfde1f 100644 --- a/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml +++ b/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml @@ -98,6 +98,7 @@ Item { width: parent.width height: parent.height + backgroundColor: "transparent" profile: HFWebEngineProfile; settings.pluginsEnabled: true diff --git a/interface/resources/qml/controls/WebView.qml b/interface/resources/qml/controls/WebView.qml index 24ea11a906..6b57ab85cd 100644 --- a/interface/resources/qml/controls/WebView.qml +++ b/interface/resources/qml/controls/WebView.qml @@ -42,6 +42,7 @@ Item { id: webroot width: parent.width height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height + // backgroundColor: "transparent" onLoadingChangedCallback: { keyboardRaised = false; diff --git a/scripts/test.html b/scripts/test.html new file mode 100644 index 0000000000..8fb6458eeb --- /dev/null +++ b/scripts/test.html @@ -0,0 +1,15 @@ + + + + + + + +

This is a test. (: (that doesn't work for some reason)

+ + + From 21167a0f6a6faf14c9948e0c6821313fb261402c Mon Sep 17 00:00:00 2001 From: Kasen IO Date: Fri, 14 Aug 2020 23:53:01 -0400 Subject: [PATCH 011/174] Maybe fix scriptURL on WebEntities bug --- libraries/entities-renderer/src/RenderableWebEntityItem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 1da4999bad..121752d6c0 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -207,7 +207,7 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene { auto scriptURL = entity->getScriptURL(); if (_scriptURL != scriptURL) { - _webSurface->getRootItem()->setProperty("scriptURL", _scriptURL); + _webSurface->getRootItem()->setProperty("scriptURL", scriptURL); _scriptURL = scriptURL; } } From cd5bdf6160ada2ff905226c9ccddaf1507bfc4c6 Mon Sep 17 00:00:00 2001 From: Kasen IO Date: Sat, 15 Aug 2020 00:56:19 -0400 Subject: [PATCH 012/174] Added transparent + colored background web entity capability dynamically. --- interface/resources/qml/Web3DSurface.qml | 19 +++++++++++----- .../+webengine/FlickableWebViewCore.qml | 6 ++++- .../qml/controls/FlickableWebViewCore.qml | 2 ++ interface/resources/qml/controls/WebView.qml | 1 + .../src/RenderableWebEntityItem.cpp | 9 ++++++++ .../src/RenderableWebEntityItem.h | 2 ++ .../entities/src/EntityItemProperties.cpp | 12 ++++++++++ libraries/entities/src/EntityItemProperties.h | 2 ++ libraries/entities/src/EntityPropertyFlags.h | 2 ++ libraries/entities/src/WebEntityItem.cpp | 22 +++++++++++++++++-- libraries/entities/src/WebEntityItem.h | 6 +++++ 11 files changed, 75 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/Web3DSurface.qml b/interface/resources/qml/Web3DSurface.qml index 39ee85444d..92ceba6a8d 100644 --- a/interface/resources/qml/Web3DSurface.qml +++ b/interface/resources/qml/Web3DSurface.qml @@ -18,22 +18,31 @@ Item { anchors.fill: parent property string url: "" property string scriptUrl: null + property string webBackgroundColor: "#FFFFFFFF" onUrlChanged: { - load(root.url, root.scriptUrl); + load(root.url, root.scriptUrl, root.webBackgroundColor); } onScriptUrlChanged: { if (root.item) { root.item.scriptUrl = root.scriptUrl; } else { - load(root.url, root.scriptUrl); + load(root.url, root.scriptUrl, root.webBackgroundColor); + } + } + + onWebBackgroundColorChanged: { + if (root.item) { + root.item.webBackgroundColor = root.webBackgroundColor; + } else { + load(root.url, root.scriptUrl, root.webBackgroundColor); } } property var item: null - function load(url, scriptUrl) { + function load(url, scriptUrl, webBackgroundColor) { // Ensure we reset any existing item to "about:blank" to ensure web audio stops: DEV-2375 if (root.item != null) { root.item.url = "about:blank" @@ -44,12 +53,12 @@ Item { root.item = newItem root.item.url = url root.item.scriptUrl = scriptUrl - root.item.transparentBackground = true + root.item.transparentBackground = webBackgroundColor.endsWith("FF") ? true : false }) } Component.onCompleted: { - load(root.url, root.scriptUrl); + load(root.url, root.scriptUrl, root.webBackgroundColor); } signal sendToScript(var message); diff --git a/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml b/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml index 6fbfcfde1f..93126152a2 100644 --- a/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml +++ b/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml @@ -15,6 +15,7 @@ Item { property alias webViewCore: webViewCore property alias webViewCoreProfile: webViewCore.profile property string webViewCoreUserAgent + property string webBackgroundColor: "#FFFFFFFF" // Fully opaque white. property string userScriptUrl: "" property string urlTag: "noDownload=false"; @@ -98,7 +99,10 @@ Item { width: parent.width height: parent.height - backgroundColor: "transparent" + //backgroundColor: "transparent" + //backgroundColor: "#FFFF00CC" + backgroundColor: flick.webBackgroundColor + //backgroundColor: Qt.rgba(0.502, 0.502, 0.502, 0.502) profile: HFWebEngineProfile; settings.pluginsEnabled: true diff --git a/interface/resources/qml/controls/FlickableWebViewCore.qml b/interface/resources/qml/controls/FlickableWebViewCore.qml index a844c8b624..cccd42457a 100644 --- a/interface/resources/qml/controls/FlickableWebViewCore.qml +++ b/interface/resources/qml/controls/FlickableWebViewCore.qml @@ -14,6 +14,8 @@ Item { property alias webViewCoreProfile: webViewCore.profile property string webViewCoreUserAgent + property string webBackgroundColor: "#FFFFFFFF" + property string userScriptUrl: "" property string urlTag: "noDownload=false"; diff --git a/interface/resources/qml/controls/WebView.qml b/interface/resources/qml/controls/WebView.qml index 6b57ab85cd..8b103c5ef2 100644 --- a/interface/resources/qml/controls/WebView.qml +++ b/interface/resources/qml/controls/WebView.qml @@ -23,6 +23,7 @@ Item { property bool passwordField: false property alias flickable: webroot.interactive property alias blurOnCtrlShift: webroot.blurOnCtrlShift + property alias webBackgroundColor: webroot.webBackgroundColor function stop() { webroot.stop(); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 121752d6c0..b3d1edcfb5 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -1,6 +1,7 @@ // // Created by Bradley Austin Davis on 2015/05/12 // Copyright 2013 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -226,6 +227,14 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene } } + { + auto webBackgroundColor = entity->getWebBackgroundColor(); + if (_webBackgroundColor != webBackgroundColor) { + _webSurface->getRootItem()->setProperty("webBackgroundColor", webBackgroundColor); + _webBackgroundColor = webBackgroundColor; + } + } + { auto contextPosition = entity->getWorldPosition(); if (_contextPosition != contextPosition) { diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 7118774d30..786df55ee5 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -1,6 +1,7 @@ // // Created by Bradley Austin Davis on 2015/05/12 // Copyright 2013 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -93,6 +94,7 @@ private: uint16_t _dpi; QString _scriptURL; uint8_t _maxFPS; + QString _webBackgroundColor; WebInputMode _inputMode; glm::vec3 _contextPosition; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index d671d46c22..294ac6228e 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -4,6 +4,7 @@ // // Created by Brad Hefta-Gaub on 12/4/13. // Copyright 2013 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -601,6 +602,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_MAX_FPS, maxFPS); CHECK_PROPERTY_CHANGE(PROP_INPUT_MODE, inputMode); CHECK_PROPERTY_CHANGE(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, showKeyboardFocusHighlight); + CHECK_PROPERTY_CHANGE(PROP_WEB_BACKGROUND_COLOR, webBackgroundColor); // Polyline CHECK_PROPERTY_CHANGE(PROP_LINE_POINTS, linePoints); @@ -1819,6 +1821,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MAX_FPS, maxFPS); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_INPUT_MODE, inputMode, getInputModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, showKeyboardFocusHighlight); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_WEB_BACKGROUND_COLOR, webBackgroundColor); } // PolyVoxel only @@ -2200,6 +2203,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(maxFPS, uint8_t, setMaxFPS); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(inputMode, InputMode); COPY_PROPERTY_FROM_QSCRIPTVALUE(showKeyboardFocusHighlight, bool, setShowKeyboardFocusHighlight); + COPY_PROPERTY_FROM_QSCRIPTVALUE(webBackgroundColor, QString, setWebBackgroundColor); // Polyline COPY_PROPERTY_FROM_QSCRIPTVALUE(linePoints, qVectorVec3, setLinePoints); @@ -2491,6 +2495,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(maxFPS); COPY_PROPERTY_IF_CHANGED(inputMode); COPY_PROPERTY_IF_CHANGED(showKeyboardFocusHighlight); + COPY_PROPERTY_IF_CHANGED(webBackgroundColor); // Polyline COPY_PROPERTY_IF_CHANGED(linePoints); @@ -2890,6 +2895,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_PROPERTY_TO_MAP(PROP_MAX_FPS, MaxFPS, maxFPS, uint8_t); ADD_PROPERTY_TO_MAP(PROP_INPUT_MODE, InputMode, inputMode, WebInputMode); ADD_PROPERTY_TO_MAP(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, ShowKeyboardFocusHighlight, showKeyboardFocusHighlight, bool); + ADD_PROPERTY_TO_MAP(PROP_WEB_BACKGROUND_COLOR, WebBackgroundColor, webBackgroundColor, QString); // Polyline ADD_PROPERTY_TO_MAP(PROP_LINE_POINTS, LinePoints, linePoints, QVector); @@ -3320,6 +3326,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_MAX_FPS, properties.getMaxFPS()); APPEND_ENTITY_PROPERTY(PROP_INPUT_MODE, (uint32_t)properties.getInputMode()); APPEND_ENTITY_PROPERTY(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, properties.getShowKeyboardFocusHighlight()); + APPEND_ENTITY_PROPERTY(PROP_WEB_BACKGROUND_COLOR, properties.getWebBackgroundColor()); } if (properties.getType() == EntityTypes::Line) { @@ -3795,6 +3802,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MAX_FPS, uint8_t, setMaxFPS); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_INPUT_MODE, WebInputMode, setInputMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, bool, setShowKeyboardFocusHighlight); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_WEB_BACKGROUND_COLOR, QString, setWebBackgroundColor); } if (properties.getType() == EntityTypes::Line) { @@ -4182,6 +4190,7 @@ void EntityItemProperties::markAllChanged() { _maxFPSChanged = true; _inputModeChanged = true; _showKeyboardFocusHighlightChanged = true; + _webBackgroundColor = true; // Polyline _linePointsChanged = true; @@ -4872,6 +4881,9 @@ QList EntityItemProperties::listChangedProperties() { if (showKeyboardFocusHighlightChanged()) { out += "showKeyboardFocusHighlight"; } + if (webBackgroundColorChanged()) { + out += "webBackgroundColor"; + } // Shape if (shapeChanged()) { diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index efc8b5dc33..6049391ba6 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -4,6 +4,7 @@ // // Created by Brad Hefta-Gaub on 12/4/13. // Copyright 2013 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -365,6 +366,7 @@ public: DEFINE_PROPERTY_REF(PROP_MAX_FPS, MaxFPS, maxFPS, uint8_t, WebEntityItem::DEFAULT_MAX_FPS); DEFINE_PROPERTY_REF_ENUM(PROP_INPUT_MODE, InputMode, inputMode, WebInputMode, WebInputMode::TOUCH); DEFINE_PROPERTY_REF(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, ShowKeyboardFocusHighlight, showKeyboardFocusHighlight, bool, true); + DEFINE_PROPERTY_REF(PROP_WEB_BACKGROUND_COLOR, WebBackgroundColor, webBackgroundColor, QString, WebEntityItem::DEFAULT_WEB_BACKGROUND_COLOR); // Polyline DEFINE_PROPERTY_REF(PROP_LINE_POINTS, LinePoints, linePoints, QVector, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index a7359c0bca..0e068544e3 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -4,6 +4,7 @@ // // Created by Brad Hefta-Gaub on 12/4/13. // Copyright 2013 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -317,6 +318,7 @@ enum EntityPropertyList { PROP_MAX_FPS = PROP_DERIVED_3, PROP_INPUT_MODE = PROP_DERIVED_4, PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT = PROP_DERIVED_5, + PROP_WEB_BACKGROUND_COLOR = PROP_DERIVED_6, // Polyline PROP_LINE_POINTS = PROP_DERIVED_0, diff --git a/libraries/entities/src/WebEntityItem.cpp b/libraries/entities/src/WebEntityItem.cpp index a62f599e4c..57b53bd911 100644 --- a/libraries/entities/src/WebEntityItem.cpp +++ b/libraries/entities/src/WebEntityItem.cpp @@ -1,6 +1,7 @@ // // Created by Bradley Austin Davis on 2015/05/12 // Copyright 2013 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -22,8 +23,9 @@ #include "EntityTree.h" #include "EntityTreeElement.h" -const QString WebEntityItem::DEFAULT_SOURCE_URL = "http://www.google.com"; +const QString WebEntityItem::DEFAULT_SOURCE_URL = "https://www.vircadia.com"; const uint8_t WebEntityItem::DEFAULT_MAX_FPS = 10; +const QString WebEntityItem::DEFAULT_WEB_BACKGROUND_COLOR = "#FFFFFFFF"; EntityItemPointer WebEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItemPointer entity(new WebEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); @@ -60,6 +62,7 @@ EntityItemProperties WebEntityItem::getProperties(const EntityPropertyFlags& des COPY_ENTITY_PROPERTY_TO_PROPERTIES(maxFPS, getMaxFPS); COPY_ENTITY_PROPERTY_TO_PROPERTIES(inputMode, getInputMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(showKeyboardFocusHighlight, getShowKeyboardFocusHighlight); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(webBackgroundColor, getWebBackgroundColor); return properties; } @@ -82,6 +85,7 @@ bool WebEntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(maxFPS, setMaxFPS); SET_ENTITY_PROPERTY_FROM_PROPERTIES(inputMode, setInputMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(showKeyboardFocusHighlight, setShowKeyboardFocusHighlight); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(webBackgroundColor, setWebBackgroundColor); if (somethingChanged) { bool wantDebug = false; @@ -122,6 +126,7 @@ int WebEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, i READ_ENTITY_PROPERTY(PROP_MAX_FPS, uint8_t, setMaxFPS); READ_ENTITY_PROPERTY(PROP_INPUT_MODE, WebInputMode, setInputMode); READ_ENTITY_PROPERTY(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, bool, setShowKeyboardFocusHighlight); + READ_ENTITY_PROPERTY(PROP_WEB_BACKGROUND_COLOR, QString, setWebBackgroundColor); return bytesRead; } @@ -139,6 +144,7 @@ EntityPropertyFlags WebEntityItem::getEntityProperties(EncodeBitstreamParams& pa requestedProperties += PROP_MAX_FPS; requestedProperties += PROP_INPUT_MODE; requestedProperties += PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT; + requestedProperties += PROP_WEB_BACKGROUND_COLOR; return requestedProperties; } @@ -165,6 +171,7 @@ void WebEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitst APPEND_ENTITY_PROPERTY(PROP_MAX_FPS, getMaxFPS()); APPEND_ENTITY_PROPERTY(PROP_INPUT_MODE, (uint32_t)getInputMode()); APPEND_ENTITY_PROPERTY(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, getShowKeyboardFocusHighlight()); + APPEND_ENTITY_PROPERTY(PROP_WEB_BACKGROUND_COLOR, getWebBackgroundColor()); } glm::vec3 WebEntityItem::getRaycastDimensions() const { @@ -307,7 +314,7 @@ void WebEntityItem::setScriptURL(const QString& value) { auto newURL = QUrl::fromUserInput(value); if (!newURL.isValid()) { - qCDebug(entities) << "Not setting web entity script URL since" << value << "cannot be parsed to a valid URL."; + qCDebug(entities) << "Not setting web entity script URL since" << value << "cannot be parsed to a valid URL."; return; } @@ -359,6 +366,17 @@ bool WebEntityItem::getShowKeyboardFocusHighlight() const { return _showKeyboardFocusHighlight; } +void WebEntityItem::setWebBackgroundColor(const QString& value) { + withWriteLock([&] { + _needsRenderUpdate |= _webBackgroundColor != value; + _webBackgroundColor = value; + }); +} + +QString WebEntityItem::getWebBackgroundColor() const { + return resultWithReadLock([&] { return _webBackgroundColor; }); +} + PulsePropertyGroup WebEntityItem::getPulseProperties() const { return resultWithReadLock([&] { return _pulseProperties; diff --git a/libraries/entities/src/WebEntityItem.h b/libraries/entities/src/WebEntityItem.h index b61e2b124f..25a0a162e5 100644 --- a/libraries/entities/src/WebEntityItem.h +++ b/libraries/entities/src/WebEntityItem.h @@ -1,6 +1,7 @@ // // Created by Bradley Austin Davis on 2015/05/12 // Copyright 2013 High Fidelity, Inc. +// Copyright 2020 Vircadia contributors. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -80,6 +81,10 @@ public: void setMaxFPS(uint8_t value); uint8_t getMaxFPS() const; + static const QString DEFAULT_WEB_BACKGROUND_COLOR; + void setWebBackgroundColor(const QString& value); + QString getWebBackgroundColor() const; + void setInputMode(const WebInputMode& value); WebInputMode getInputMode() const; @@ -98,6 +103,7 @@ protected: uint16_t _dpi; QString _scriptURL; uint8_t _maxFPS; + QString _webBackgroundColor; WebInputMode _inputMode; bool _showKeyboardFocusHighlight; bool _localSafeContext { false }; From 2590dfbaa72cdb6f6768db637cc9183082e51749 Mon Sep 17 00:00:00 2001 From: Kasen IO Date: Sat, 15 Aug 2020 00:57:10 -0400 Subject: [PATCH 013/174] Remove unused tests for backgroundColor. --- interface/resources/qml/+webengine/BrowserWebView.qml | 1 - interface/resources/qml/+webengine/QmlWebWindowView.qml | 1 - interface/resources/qml/controls/WebView.qml | 1 - 3 files changed, 3 deletions(-) diff --git a/interface/resources/qml/+webengine/BrowserWebView.qml b/interface/resources/qml/+webengine/BrowserWebView.qml index ad837ad043..a0525161f0 100644 --- a/interface/resources/qml/+webengine/BrowserWebView.qml +++ b/interface/resources/qml/+webengine/BrowserWebView.qml @@ -8,7 +8,6 @@ WebView { id: webview url: "https://vircadia.com/" profile: FileTypeProfile; - // backgroundColor: "transparent" property var parentRoot: null diff --git a/interface/resources/qml/+webengine/QmlWebWindowView.qml b/interface/resources/qml/+webengine/QmlWebWindowView.qml index dfabc631ba..84ab61ad28 100644 --- a/interface/resources/qml/+webengine/QmlWebWindowView.qml +++ b/interface/resources/qml/+webengine/QmlWebWindowView.qml @@ -10,7 +10,6 @@ Controls.WebView { anchors.fill: parent focus: true profile: HFWebEngineProfile; - // backgroundColor: "transparent" property string userScriptUrl: "" diff --git a/interface/resources/qml/controls/WebView.qml b/interface/resources/qml/controls/WebView.qml index 8b103c5ef2..663bbf7867 100644 --- a/interface/resources/qml/controls/WebView.qml +++ b/interface/resources/qml/controls/WebView.qml @@ -43,7 +43,6 @@ Item { id: webroot width: parent.width height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height : parent.height - // backgroundColor: "transparent" onLoadingChangedCallback: { keyboardRaised = false; From 1c457b766c5bd12c25e9428a0bc6b61f9bdb763c Mon Sep 17 00:00:00 2001 From: Kasen IO Date: Sat, 15 Aug 2020 01:05:52 -0400 Subject: [PATCH 014/174] Use https://google.com/ to test transparency. --- scripts/test.html | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 scripts/test.html diff --git a/scripts/test.html b/scripts/test.html deleted file mode 100644 index 8fb6458eeb..0000000000 --- a/scripts/test.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - -

This is a test. (: (that doesn't work for some reason)

- - - From 433629163575109f9b0f4b54bca89a51b1b291ee Mon Sep 17 00:00:00 2001 From: Kasen IO Date: Sat, 15 Aug 2020 01:33:18 -0400 Subject: [PATCH 015/174] Fix bool. --- interface/resources/qml/Web3DSurface.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/Web3DSurface.qml b/interface/resources/qml/Web3DSurface.qml index 92ceba6a8d..3a3a2ef8b1 100644 --- a/interface/resources/qml/Web3DSurface.qml +++ b/interface/resources/qml/Web3DSurface.qml @@ -53,7 +53,7 @@ Item { root.item = newItem root.item.url = url root.item.scriptUrl = scriptUrl - root.item.transparentBackground = webBackgroundColor.endsWith("FF") ? true : false + root.item.transparentBackground = webBackgroundColor.endsWith("FF") ? false : true }) } From ca974504644ea8169179bbeb7ceb7ce836df37fe Mon Sep 17 00:00:00 2001 From: Kasen IO Date: Mon, 17 Aug 2020 02:44:26 -0400 Subject: [PATCH 016/174] Centralizing some more networking constants. --- interface/src/avatar/MyAvatar.cpp | 3 ++- interface/src/ui/ModelsBrowser.cpp | 4 ++-- libraries/entities/src/WebEntityItem.cpp | 3 ++- libraries/networking/src/NetworkingConstants.h | 8 ++++++++ 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 570ac5fce2..27af9b3c14 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -50,6 +50,7 @@ #include #include #include +#include #include "MyHead.h" #include "MySkeletonModel.h" @@ -82,7 +83,7 @@ const int SCRIPTED_MOTOR_AVATAR_FRAME = 1; const int SCRIPTED_MOTOR_WORLD_FRAME = 2; const int SCRIPTED_MOTOR_SIMPLE_MODE = 0; const int SCRIPTED_MOTOR_DYNAMIC_MODE = 1; -const QString& DEFAULT_AVATAR_COLLISION_SOUND_URL = "https://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/Body_Hits_Impact.wav"; +const QString& DEFAULT_AVATAR_COLLISION_SOUND_URL = NetworkingConstants::DEFAULT_AVATAR_COLLISION_SOUND_URL; const float MyAvatar::ZOOM_MIN = 0.5f; const float MyAvatar::ZOOM_MAX = 25.0f; diff --git a/interface/src/ui/ModelsBrowser.cpp b/interface/src/ui/ModelsBrowser.cpp index 96c10be212..cccfb6e6d6 100644 --- a/interface/src/ui/ModelsBrowser.cpp +++ b/interface/src/ui/ModelsBrowser.cpp @@ -32,8 +32,8 @@ const char* MODEL_TYPE_NAMES[] = { "entities", "heads", "skeletons", "skeletons", "attachments" }; -static const QString S3_URL = "http://s3.amazonaws.com/hifi-public"; -static const QString PUBLIC_URL = "http://public.highfidelity.io"; +static const QString S3_URL = NetworkingConstants::S3_URL; +static const QString PUBLIC_URL = NetworkingConstants::PUBLIC_URL; static const QString MODELS_LOCATION = "models/"; static const QString PREFIX_PARAMETER_NAME = "prefix"; diff --git a/libraries/entities/src/WebEntityItem.cpp b/libraries/entities/src/WebEntityItem.cpp index 57b53bd911..5419dca53c 100644 --- a/libraries/entities/src/WebEntityItem.cpp +++ b/libraries/entities/src/WebEntityItem.cpp @@ -17,13 +17,14 @@ #include #include #include +#include #include "EntitiesLogging.h" #include "EntityItemProperties.h" #include "EntityTree.h" #include "EntityTreeElement.h" -const QString WebEntityItem::DEFAULT_SOURCE_URL = "https://www.vircadia.com"; +const QString WebEntityItem::DEFAULT_SOURCE_URL = NetworkingConstants::WEB_ENTITY_DEFAULT_SOURCE_URL; const uint8_t WebEntityItem::DEFAULT_MAX_FPS = 10; const QString WebEntityItem::DEFAULT_WEB_BACKGROUND_COLOR = "#FFFFFFFF"; diff --git a/libraries/networking/src/NetworkingConstants.h b/libraries/networking/src/NetworkingConstants.h index edc1c1a1ef..0e490885cd 100644 --- a/libraries/networking/src/NetworkingConstants.h +++ b/libraries/networking/src/NetworkingConstants.h @@ -41,7 +41,15 @@ namespace NetworkingConstants { const QUrl BUILDS_XML_URL("https://highfidelity.com/builds.xml"); const QUrl MASTER_BUILDS_XML_URL("https://highfidelity.com/dev-builds.xml"); + + // WebEntity Defaults + const QString WEB_ENTITY_DEFAULT_SOURCE_URL = "https://vircadia.com/"; + + const QString DEFAULT_AVATAR_COLLISION_SOUND_URL = "https://hifi-public.s3.amazonaws.com/sounds/Collisions-otherorganic/Body_Hits_Impact.wav"; + // CDN URLs + const QString S3_URL = "http://s3.amazonaws.com/hifi-public"; + const QString PUBLIC_URL = "http://public.highfidelity.io"; #if USE_STABLE_GLOBAL_SERVICES const QString ICE_SERVER_DEFAULT_HOSTNAME = "ice.highfidelity.com"; From 7e5e513dc1584e382f5d5e53857564f58f4b0ea6 Mon Sep 17 00:00:00 2001 From: Kasen IO Date: Mon, 17 Aug 2020 15:13:07 -0400 Subject: [PATCH 017/174] Update Web3DSurface.qml --- interface/resources/qml/Web3DSurface.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/Web3DSurface.qml b/interface/resources/qml/Web3DSurface.qml index 3a3a2ef8b1..7ac7c2593b 100644 --- a/interface/resources/qml/Web3DSurface.qml +++ b/interface/resources/qml/Web3DSurface.qml @@ -53,7 +53,7 @@ Item { root.item = newItem root.item.url = url root.item.scriptUrl = scriptUrl - root.item.transparentBackground = webBackgroundColor.endsWith("FF") ? false : true + root.item.transparentBackground = webBackgroundColor.startsWith("#FF") ? false : true }) } From b05cb8b6ac7ea95909c23478c425c250005266a0 Mon Sep 17 00:00:00 2001 From: Kasen IO Date: Tue, 18 Aug 2020 01:48:27 -0400 Subject: [PATCH 018/174] useBackground property added. --- interface/resources/qml/Web3DSurface.qml | 18 ++++++++-------- .../+webengine/FlickableWebViewCore.qml | 7 ++----- .../qml/controls/FlickableWebViewCore.qml | 3 +-- interface/resources/qml/controls/WebView.qml | 2 +- .../src/RenderableWebEntityItem.cpp | 14 +++++++------ .../src/RenderableWebEntityItem.h | 2 +- .../entities/src/EntityItemProperties.cpp | 20 +++++++++--------- libraries/entities/src/EntityItemProperties.h | 2 +- libraries/entities/src/EntityPropertyFlags.h | 2 +- libraries/entities/src/WebEntityItem.cpp | 21 +++++++++---------- libraries/entities/src/WebEntityItem.h | 9 ++++---- 11 files changed, 48 insertions(+), 52 deletions(-) diff --git a/interface/resources/qml/Web3DSurface.qml b/interface/resources/qml/Web3DSurface.qml index 7ac7c2593b..e9c8d119d6 100644 --- a/interface/resources/qml/Web3DSurface.qml +++ b/interface/resources/qml/Web3DSurface.qml @@ -18,31 +18,31 @@ Item { anchors.fill: parent property string url: "" property string scriptUrl: null - property string webBackgroundColor: "#FFFFFFFF" + property bool useBackground: true onUrlChanged: { - load(root.url, root.scriptUrl, root.webBackgroundColor); + load(root.url, root.scriptUrl, root.useBackground); } onScriptUrlChanged: { if (root.item) { root.item.scriptUrl = root.scriptUrl; } else { - load(root.url, root.scriptUrl, root.webBackgroundColor); + load(root.url, root.scriptUrl, root.useBackground); } } - onWebBackgroundColorChanged: { + onUseBackgroundChanged: { if (root.item) { - root.item.webBackgroundColor = root.webBackgroundColor; + root.item.useBackground = root.useBackground; } else { - load(root.url, root.scriptUrl, root.webBackgroundColor); + load(root.url, root.scriptUrl, root.useBackground); } } property var item: null - function load(url, scriptUrl, webBackgroundColor) { + function load(url, scriptUrl, useBackground) { // Ensure we reset any existing item to "about:blank" to ensure web audio stops: DEV-2375 if (root.item != null) { root.item.url = "about:blank" @@ -53,12 +53,12 @@ Item { root.item = newItem root.item.url = url root.item.scriptUrl = scriptUrl - root.item.transparentBackground = webBackgroundColor.startsWith("#FF") ? false : true + root.item.useBackground = useBackground }) } Component.onCompleted: { - load(root.url, root.scriptUrl, root.webBackgroundColor); + load(root.url, root.scriptUrl, root.useBackground); } signal sendToScript(var message); diff --git a/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml b/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml index 93126152a2..d7d84a0961 100644 --- a/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml +++ b/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml @@ -15,8 +15,8 @@ Item { property alias webViewCore: webViewCore property alias webViewCoreProfile: webViewCore.profile property string webViewCoreUserAgent - property string webBackgroundColor: "#FFFFFFFF" // Fully opaque white. + property bool useBackground: true property string userScriptUrl: "" property string urlTag: "noDownload=false"; @@ -99,10 +99,7 @@ Item { width: parent.width height: parent.height - //backgroundColor: "transparent" - //backgroundColor: "#FFFF00CC" - backgroundColor: flick.webBackgroundColor - //backgroundColor: Qt.rgba(0.502, 0.502, 0.502, 0.502) + backgroundColor: (flick.useBackground) ? "white" : "transparent" profile: HFWebEngineProfile; settings.pluginsEnabled: true diff --git a/interface/resources/qml/controls/FlickableWebViewCore.qml b/interface/resources/qml/controls/FlickableWebViewCore.qml index cccd42457a..4f611a4d9b 100644 --- a/interface/resources/qml/controls/FlickableWebViewCore.qml +++ b/interface/resources/qml/controls/FlickableWebViewCore.qml @@ -14,8 +14,7 @@ Item { property alias webViewCoreProfile: webViewCore.profile property string webViewCoreUserAgent - property string webBackgroundColor: "#FFFFFFFF" - + property bool useBackground: true property string userScriptUrl: "" property string urlTag: "noDownload=false"; diff --git a/interface/resources/qml/controls/WebView.qml b/interface/resources/qml/controls/WebView.qml index 663bbf7867..b46c8c904d 100644 --- a/interface/resources/qml/controls/WebView.qml +++ b/interface/resources/qml/controls/WebView.qml @@ -23,7 +23,7 @@ Item { property bool passwordField: false property alias flickable: webroot.interactive property alias blurOnCtrlShift: webroot.blurOnCtrlShift - property alias webBackgroundColor: webroot.webBackgroundColor + property alias useBackground: webroot.useBackground function stop() { webroot.stop(); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index b3d1edcfb5..dc48a575f1 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -101,7 +101,7 @@ WebEntityRenderer::~WebEntityRenderer() { bool WebEntityRenderer::isTransparent() const { float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; - return fadeRatio < OPAQUE_ALPHA_THRESHOLD || _alpha < 1.0f || _pulseProperties.getAlphaMode() != PulseMode::NONE; + return fadeRatio < OPAQUE_ALPHA_THRESHOLD || _alpha < 1.0f || _pulseProperties.getAlphaMode() != PulseMode::NONE || !_useBackground; } bool WebEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const { @@ -228,10 +228,10 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene } { - auto webBackgroundColor = entity->getWebBackgroundColor(); - if (_webBackgroundColor != webBackgroundColor) { - _webSurface->getRootItem()->setProperty("webBackgroundColor", webBackgroundColor); - _webBackgroundColor = webBackgroundColor; + auto useBackground = entity->getUseBackground(); + if (_useBackground != useBackground) { + _webSurface->getRootItem()->setProperty("useBackground", useBackground); + _useBackground = useBackground; } } @@ -300,12 +300,14 @@ void WebEntityRenderer::doRender(RenderArgs* args) { glm::vec4 color; Transform transform; bool forward; + bool isTransparent; withReadLock([&] { float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; color = glm::vec4(toGlm(_color), _alpha * fadeRatio); color = EntityRenderer::calculatePulseColor(color, _pulseProperties, _created); transform = _renderTransform; forward = _renderLayer != RenderLayer::WORLD || args->_renderMethod == render::Args::FORWARD; + isTransparent = isTransparent(); }); if (color.a == 0.0f) { @@ -319,7 +321,7 @@ void WebEntityRenderer::doRender(RenderArgs* args) { // Turn off jitter for these entities batch.pushProjectionJitter(); - DependencyManager::get()->bindWebBrowserProgram(batch, color.a < OPAQUE_ALPHA_THRESHOLD, forward); + DependencyManager::get()->bindWebBrowserProgram(batch, isTransparent, forward); DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, color, _geometryId); batch.popProjectionJitter(); batch.setResourceTexture(0, nullptr); diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 786df55ee5..b03ea93ef5 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -94,7 +94,7 @@ private: uint16_t _dpi; QString _scriptURL; uint8_t _maxFPS; - QString _webBackgroundColor; + bool _useBackground; WebInputMode _inputMode; glm::vec3 _contextPosition; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 294ac6228e..921904ed25 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -602,7 +602,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_MAX_FPS, maxFPS); CHECK_PROPERTY_CHANGE(PROP_INPUT_MODE, inputMode); CHECK_PROPERTY_CHANGE(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, showKeyboardFocusHighlight); - CHECK_PROPERTY_CHANGE(PROP_WEB_BACKGROUND_COLOR, webBackgroundColor); + CHECK_PROPERTY_CHANGE(PROP_WEB_USE_BACKGROUND, useBackground); // Polyline CHECK_PROPERTY_CHANGE(PROP_LINE_POINTS, linePoints); @@ -1821,7 +1821,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MAX_FPS, maxFPS); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_INPUT_MODE, inputMode, getInputModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, showKeyboardFocusHighlight); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_WEB_BACKGROUND_COLOR, webBackgroundColor); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_WEB_USE_BACKGROUND, useBackground); } // PolyVoxel only @@ -2203,7 +2203,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(maxFPS, uint8_t, setMaxFPS); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(inputMode, InputMode); COPY_PROPERTY_FROM_QSCRIPTVALUE(showKeyboardFocusHighlight, bool, setShowKeyboardFocusHighlight); - COPY_PROPERTY_FROM_QSCRIPTVALUE(webBackgroundColor, QString, setWebBackgroundColor); + COPY_PROPERTY_FROM_QSCRIPTVALUE(useBackground, bool, setUseBackground); // Polyline COPY_PROPERTY_FROM_QSCRIPTVALUE(linePoints, qVectorVec3, setLinePoints); @@ -2495,7 +2495,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(maxFPS); COPY_PROPERTY_IF_CHANGED(inputMode); COPY_PROPERTY_IF_CHANGED(showKeyboardFocusHighlight); - COPY_PROPERTY_IF_CHANGED(webBackgroundColor); + COPY_PROPERTY_IF_CHANGED(useBackground); // Polyline COPY_PROPERTY_IF_CHANGED(linePoints); @@ -2895,7 +2895,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ADD_PROPERTY_TO_MAP(PROP_MAX_FPS, MaxFPS, maxFPS, uint8_t); ADD_PROPERTY_TO_MAP(PROP_INPUT_MODE, InputMode, inputMode, WebInputMode); ADD_PROPERTY_TO_MAP(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, ShowKeyboardFocusHighlight, showKeyboardFocusHighlight, bool); - ADD_PROPERTY_TO_MAP(PROP_WEB_BACKGROUND_COLOR, WebBackgroundColor, webBackgroundColor, QString); + ADD_PROPERTY_TO_MAP(PROP_WEB_USE_BACKGROUND, useBackground, useBackground, bool); // Polyline ADD_PROPERTY_TO_MAP(PROP_LINE_POINTS, LinePoints, linePoints, QVector); @@ -3326,7 +3326,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_MAX_FPS, properties.getMaxFPS()); APPEND_ENTITY_PROPERTY(PROP_INPUT_MODE, (uint32_t)properties.getInputMode()); APPEND_ENTITY_PROPERTY(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, properties.getShowKeyboardFocusHighlight()); - APPEND_ENTITY_PROPERTY(PROP_WEB_BACKGROUND_COLOR, properties.getWebBackgroundColor()); + APPEND_ENTITY_PROPERTY(PROP_WEB_USE_BACKGROUND, properties.getUseBackground()); } if (properties.getType() == EntityTypes::Line) { @@ -3802,7 +3802,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MAX_FPS, uint8_t, setMaxFPS); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_INPUT_MODE, WebInputMode, setInputMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, bool, setShowKeyboardFocusHighlight); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_WEB_BACKGROUND_COLOR, QString, setWebBackgroundColor); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_WEB_USE_BACKGROUND, bool, setUseBackground); } if (properties.getType() == EntityTypes::Line) { @@ -4190,7 +4190,7 @@ void EntityItemProperties::markAllChanged() { _maxFPSChanged = true; _inputModeChanged = true; _showKeyboardFocusHighlightChanged = true; - _webBackgroundColor = true; + _useBackgroundChanged = true; // Polyline _linePointsChanged = true; @@ -4881,8 +4881,8 @@ QList EntityItemProperties::listChangedProperties() { if (showKeyboardFocusHighlightChanged()) { out += "showKeyboardFocusHighlight"; } - if (webBackgroundColorChanged()) { - out += "webBackgroundColor"; + if (useBackgroundChanged()) { + out += "useBackground"; } // Shape diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 6049391ba6..f7fde73430 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -366,7 +366,7 @@ public: DEFINE_PROPERTY_REF(PROP_MAX_FPS, MaxFPS, maxFPS, uint8_t, WebEntityItem::DEFAULT_MAX_FPS); DEFINE_PROPERTY_REF_ENUM(PROP_INPUT_MODE, InputMode, inputMode, WebInputMode, WebInputMode::TOUCH); DEFINE_PROPERTY_REF(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, ShowKeyboardFocusHighlight, showKeyboardFocusHighlight, bool, true); - DEFINE_PROPERTY_REF(PROP_WEB_BACKGROUND_COLOR, WebBackgroundColor, webBackgroundColor, QString, WebEntityItem::DEFAULT_WEB_BACKGROUND_COLOR); + DEFINE_PROPERTY_REF(PROP_WEB_USE_BACKGROUND, UseBackground, useBackground, bool, true); // Polyline DEFINE_PROPERTY_REF(PROP_LINE_POINTS, LinePoints, linePoints, QVector, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 0e068544e3..93bb8a89a7 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -318,7 +318,7 @@ enum EntityPropertyList { PROP_MAX_FPS = PROP_DERIVED_3, PROP_INPUT_MODE = PROP_DERIVED_4, PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT = PROP_DERIVED_5, - PROP_WEB_BACKGROUND_COLOR = PROP_DERIVED_6, + PROP_WEB_USE_BACKGROUND = PROP_DERIVED_6, // Polyline PROP_LINE_POINTS = PROP_DERIVED_0, diff --git a/libraries/entities/src/WebEntityItem.cpp b/libraries/entities/src/WebEntityItem.cpp index 5419dca53c..99daca47f4 100644 --- a/libraries/entities/src/WebEntityItem.cpp +++ b/libraries/entities/src/WebEntityItem.cpp @@ -26,7 +26,6 @@ const QString WebEntityItem::DEFAULT_SOURCE_URL = NetworkingConstants::WEB_ENTITY_DEFAULT_SOURCE_URL; const uint8_t WebEntityItem::DEFAULT_MAX_FPS = 10; -const QString WebEntityItem::DEFAULT_WEB_BACKGROUND_COLOR = "#FFFFFFFF"; EntityItemPointer WebEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItemPointer entity(new WebEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); @@ -63,7 +62,7 @@ EntityItemProperties WebEntityItem::getProperties(const EntityPropertyFlags& des COPY_ENTITY_PROPERTY_TO_PROPERTIES(maxFPS, getMaxFPS); COPY_ENTITY_PROPERTY_TO_PROPERTIES(inputMode, getInputMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(showKeyboardFocusHighlight, getShowKeyboardFocusHighlight); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(webBackgroundColor, getWebBackgroundColor); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(useBackground, getUseBackground); return properties; } @@ -86,7 +85,7 @@ bool WebEntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(maxFPS, setMaxFPS); SET_ENTITY_PROPERTY_FROM_PROPERTIES(inputMode, setInputMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(showKeyboardFocusHighlight, setShowKeyboardFocusHighlight); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(webBackgroundColor, setWebBackgroundColor); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(useBackground, setUseBackground); if (somethingChanged) { bool wantDebug = false; @@ -127,7 +126,7 @@ int WebEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, i READ_ENTITY_PROPERTY(PROP_MAX_FPS, uint8_t, setMaxFPS); READ_ENTITY_PROPERTY(PROP_INPUT_MODE, WebInputMode, setInputMode); READ_ENTITY_PROPERTY(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, bool, setShowKeyboardFocusHighlight); - READ_ENTITY_PROPERTY(PROP_WEB_BACKGROUND_COLOR, QString, setWebBackgroundColor); + READ_ENTITY_PROPERTY(PROP_WEB_USE_BACKGROUND, bool, setUseBackground); return bytesRead; } @@ -145,7 +144,7 @@ EntityPropertyFlags WebEntityItem::getEntityProperties(EncodeBitstreamParams& pa requestedProperties += PROP_MAX_FPS; requestedProperties += PROP_INPUT_MODE; requestedProperties += PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT; - requestedProperties += PROP_WEB_BACKGROUND_COLOR; + requestedProperties += PROP_WEB_USE_BACKGROUND; return requestedProperties; } @@ -172,7 +171,7 @@ void WebEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitst APPEND_ENTITY_PROPERTY(PROP_MAX_FPS, getMaxFPS()); APPEND_ENTITY_PROPERTY(PROP_INPUT_MODE, (uint32_t)getInputMode()); APPEND_ENTITY_PROPERTY(PROP_SHOW_KEYBOARD_FOCUS_HIGHLIGHT, getShowKeyboardFocusHighlight()); - APPEND_ENTITY_PROPERTY(PROP_WEB_BACKGROUND_COLOR, getWebBackgroundColor()); + APPEND_ENTITY_PROPERTY(PROP_WEB_USE_BACKGROUND, getUseBackground()); } glm::vec3 WebEntityItem::getRaycastDimensions() const { @@ -367,15 +366,15 @@ bool WebEntityItem::getShowKeyboardFocusHighlight() const { return _showKeyboardFocusHighlight; } -void WebEntityItem::setWebBackgroundColor(const QString& value) { +void WebEntityItem::setUseBackground(bool value) { withWriteLock([&] { - _needsRenderUpdate |= _webBackgroundColor != value; - _webBackgroundColor = value; + _needsRenderUpdate |= _useBackground != value; + _useBackground = value; }); } -QString WebEntityItem::getWebBackgroundColor() const { - return resultWithReadLock([&] { return _webBackgroundColor; }); +bool WebEntityItem::getUseBackground() const { + return resultWithReadLock([&] { return _useBackground; }); } PulsePropertyGroup WebEntityItem::getPulseProperties() const { diff --git a/libraries/entities/src/WebEntityItem.h b/libraries/entities/src/WebEntityItem.h index 25a0a162e5..8b92cf3bb6 100644 --- a/libraries/entities/src/WebEntityItem.h +++ b/libraries/entities/src/WebEntityItem.h @@ -81,15 +81,14 @@ public: void setMaxFPS(uint8_t value); uint8_t getMaxFPS() const; - static const QString DEFAULT_WEB_BACKGROUND_COLOR; - void setWebBackgroundColor(const QString& value); - QString getWebBackgroundColor() const; - void setInputMode(const WebInputMode& value); WebInputMode getInputMode() const; bool getShowKeyboardFocusHighlight() const; void setShowKeyboardFocusHighlight(bool value); + + bool getUseBackground() const; + void setUseBackground(bool value); PulsePropertyGroup getPulseProperties() const; @@ -103,9 +102,9 @@ protected: uint16_t _dpi; QString _scriptURL; uint8_t _maxFPS; - QString _webBackgroundColor; WebInputMode _inputMode; bool _showKeyboardFocusHighlight; + bool _useBackground; bool _localSafeContext { false }; }; From fe1ea6ca724eda56d8eb395f5c2fd4e24bbd9023 Mon Sep 17 00:00:00 2001 From: Kasen IO Date: Tue, 18 Aug 2020 02:22:56 -0400 Subject: [PATCH 019/174] Bump protocol. --- libraries/networking/src/udt/PacketHeaders.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index a34be09db7..9738e7e704 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -281,6 +281,7 @@ enum class EntityVersion : PacketVersion { ScreenshareZone, ZoneOcclusion, ModelBlendshapes, + TransparentWeb, // Add new versions above here NUM_PACKET_TYPE, From c379f6c92f3d84c171ff7dd33a6545946242df96 Mon Sep 17 00:00:00 2001 From: Kasen IO Date: Tue, 18 Aug 2020 03:21:34 -0400 Subject: [PATCH 020/174] Fix transparency check bug. Co-Authored-By: null --- .../entities-renderer/src/RenderableWebEntityItem.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index dc48a575f1..8606cbb049 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -175,6 +175,7 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene _alpha = entity->getAlpha(); _pulseProperties = entity->getPulseProperties(); _billboardMode = entity->getBillboardMode(); + _useBackground = entity->getUseBackground(); if (_contentType == ContentType::NoContent) { _tryingToBuildURL = newSourceURL; @@ -300,14 +301,14 @@ void WebEntityRenderer::doRender(RenderArgs* args) { glm::vec4 color; Transform transform; bool forward; - bool isTransparent; + bool isTransparentWeb; withReadLock([&] { float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; color = glm::vec4(toGlm(_color), _alpha * fadeRatio); color = EntityRenderer::calculatePulseColor(color, _pulseProperties, _created); transform = _renderTransform; forward = _renderLayer != RenderLayer::WORLD || args->_renderMethod == render::Args::FORWARD; - isTransparent = isTransparent(); + isTransparentWeb = isTransparent(); }); if (color.a == 0.0f) { @@ -321,7 +322,7 @@ void WebEntityRenderer::doRender(RenderArgs* args) { // Turn off jitter for these entities batch.pushProjectionJitter(); - DependencyManager::get()->bindWebBrowserProgram(batch, isTransparent, forward); + DependencyManager::get()->bindWebBrowserProgram(batch, isTransparentWeb, forward); DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, color, _geometryId); batch.popProjectionJitter(); batch.setResourceTexture(0, nullptr); From 8d904d39ce13f6e44f40badff1005582fdfa7a3b Mon Sep 17 00:00:00 2001 From: Kasen IO Date: Tue, 18 Aug 2020 04:17:29 -0400 Subject: [PATCH 021/174] Semi-working fixes for updates to web entities. Co-Authored-By: null --- .../src/RenderableWebEntityItem.cpp | 19 +++++++++++++++---- .../src/RenderableWebEntityItem.h | 4 ++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 8606cbb049..d441941175 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -36,6 +36,10 @@ using namespace render::entities; const QString WebEntityRenderer::QML = "Web3DSurface.qml"; const char* WebEntityRenderer::URL_PROPERTY = "url"; +const char* WebEntityRenderer::MAX_FPS_PROPERTY = "maxFPS"; +const char* WebEntityRenderer::SCRIPT_URL_PROPERTY = "scriptURL"; +const char* WebEntityRenderer::GLOBAL_POSITION_PROPERTY = "globalPosition"; +const char* WebEntityRenderer::USE_BACKGROUND_PROPERTY = "useBackground"; std::function&, bool&)> WebEntityRenderer::_acquireWebSurfaceOperator = nullptr; std::function&, bool&, std::vector&)> WebEntityRenderer::_releaseWebSurfaceOperator = nullptr; @@ -175,7 +179,10 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene _alpha = entity->getAlpha(); _pulseProperties = entity->getPulseProperties(); _billboardMode = entity->getBillboardMode(); - _useBackground = entity->getUseBackground(); + // _maxFPS = entity->getMaxFPS(); + // _scriptURL = entity->getScriptURL(); + // _contextPosition = entity->getWorldPosition(); + // _useBackground = entity->getUseBackground(); if (_contentType == ContentType::NoContent) { _tryingToBuildURL = newSourceURL; @@ -200,6 +207,10 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene ::hifi::scripting::setLocalAccessSafeThread(true); } _webSurface->getRootItem()->setProperty(URL_PROPERTY, newSourceURL); + _webSurface->getRootItem()->setProperty(MAX_FPS_PROPERTY, _maxFPS); + _webSurface->getRootItem()->setProperty(SCRIPT_URL_PROPERTY, _scriptURL); + _webSurface->getRootItem()->setProperty(USE_BACKGROUND_PROPERTY, _useBackground); + _webSurface->getSurfaceContext()->setContextProperty(GLOBAL_POSITION_PROPERTY, vec3toVariant(_contextPosition)); ::hifi::scripting::setLocalAccessSafeThread(false); _sourceURL = newSourceURL; } else if (_contentType != ContentType::HtmlContent) { @@ -209,7 +220,7 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene { auto scriptURL = entity->getScriptURL(); if (_scriptURL != scriptURL) { - _webSurface->getRootItem()->setProperty("scriptURL", scriptURL); + _webSurface->getRootItem()->setProperty(SCRIPT_URL_PROPERTY, scriptURL); _scriptURL = scriptURL; } } @@ -231,7 +242,7 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene { auto useBackground = entity->getUseBackground(); if (_useBackground != useBackground) { - _webSurface->getRootItem()->setProperty("useBackground", useBackground); + _webSurface->getRootItem()->setProperty(USE_BACKGROUND_PROPERTY, useBackground); _useBackground = useBackground; } } @@ -239,7 +250,7 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene { auto contextPosition = entity->getWorldPosition(); if (_contextPosition != contextPosition) { - _webSurface->getSurfaceContext()->setContextProperty("globalPosition", vec3toVariant(contextPosition)); + _webSurface->getSurfaceContext()->setContextProperty(GLOBAL_POSITION_PROPERTY, vec3toVariant(contextPosition)); _contextPosition = contextPosition; } } diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index b03ea93ef5..0db69851cc 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -33,6 +33,10 @@ public: static const QString QML; static const char* URL_PROPERTY; + static const char* MAX_FPS_PROPERTY; + static const char* SCRIPT_URL_PROPERTY; + static const char* GLOBAL_POSITION_PROPERTY; + static const char* USE_BACKGROUND_PROPERTY; static void setAcquireWebSurfaceOperator(std::function&, bool&)> acquireWebSurfaceOperator) { _acquireWebSurfaceOperator = acquireWebSurfaceOperator; } static void acquireWebSurface(const QString& url, bool htmlContent, QSharedPointer& webSurface, bool& cachedWebSurface) { From 0e609b677711ff2559bb7d3262678cd3246920b5 Mon Sep 17 00:00:00 2001 From: Kasen IO Date: Tue, 18 Aug 2020 18:49:28 -0400 Subject: [PATCH 022/174] Fixed web entities useBackground on reload. Co-Authored-By: null --- .../resources/qml/controls/+webengine/FlickableWebViewCore.qml | 2 +- interface/resources/qml/controls/FlickableWebViewCore.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml b/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml index d7d84a0961..a0585ae053 100644 --- a/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml +++ b/interface/resources/qml/controls/+webengine/FlickableWebViewCore.qml @@ -16,7 +16,7 @@ Item { property alias webViewCoreProfile: webViewCore.profile property string webViewCoreUserAgent - property bool useBackground: true + property bool useBackground: webViewCore.useBackground property string userScriptUrl: "" property string urlTag: "noDownload=false"; diff --git a/interface/resources/qml/controls/FlickableWebViewCore.qml b/interface/resources/qml/controls/FlickableWebViewCore.qml index 4f611a4d9b..35f3182f98 100644 --- a/interface/resources/qml/controls/FlickableWebViewCore.qml +++ b/interface/resources/qml/controls/FlickableWebViewCore.qml @@ -14,7 +14,7 @@ Item { property alias webViewCoreProfile: webViewCore.profile property string webViewCoreUserAgent - property bool useBackground: true + property bool useBackground: webViewCore.useBackground property string userScriptUrl: "" property string urlTag: "noDownload=false"; From 204789bd421c273d8b0748f7bb03aec114f8dfbc Mon Sep 17 00:00:00 2001 From: Kasen IO Date: Tue, 18 Aug 2020 19:22:54 -0400 Subject: [PATCH 023/174] Further fix up web entity property processing on reload. Co-Authored-By: null --- .../entities-renderer/src/RenderableWebEntityItem.cpp | 9 ++------- .../entities-renderer/src/RenderableWebEntityItem.h | 1 - 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index d441941175..4f6beb5e90 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -36,7 +36,6 @@ using namespace render::entities; const QString WebEntityRenderer::QML = "Web3DSurface.qml"; const char* WebEntityRenderer::URL_PROPERTY = "url"; -const char* WebEntityRenderer::MAX_FPS_PROPERTY = "maxFPS"; const char* WebEntityRenderer::SCRIPT_URL_PROPERTY = "scriptURL"; const char* WebEntityRenderer::GLOBAL_POSITION_PROPERTY = "globalPosition"; const char* WebEntityRenderer::USE_BACKGROUND_PROPERTY = "useBackground"; @@ -179,10 +178,6 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene _alpha = entity->getAlpha(); _pulseProperties = entity->getPulseProperties(); _billboardMode = entity->getBillboardMode(); - // _maxFPS = entity->getMaxFPS(); - // _scriptURL = entity->getScriptURL(); - // _contextPosition = entity->getWorldPosition(); - // _useBackground = entity->getUseBackground(); if (_contentType == ContentType::NoContent) { _tryingToBuildURL = newSourceURL; @@ -202,15 +197,15 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene if (_webSurface) { if (_webSurface->getRootItem()) { - if (_contentType == ContentType::HtmlContent && _sourceURL != newSourceURL) { + if (_contentType == ContentType::HtmlContent && _sourceURL != newSourceURL) { if (localSafeContext) { ::hifi::scripting::setLocalAccessSafeThread(true); } _webSurface->getRootItem()->setProperty(URL_PROPERTY, newSourceURL); - _webSurface->getRootItem()->setProperty(MAX_FPS_PROPERTY, _maxFPS); _webSurface->getRootItem()->setProperty(SCRIPT_URL_PROPERTY, _scriptURL); _webSurface->getRootItem()->setProperty(USE_BACKGROUND_PROPERTY, _useBackground); _webSurface->getSurfaceContext()->setContextProperty(GLOBAL_POSITION_PROPERTY, vec3toVariant(_contextPosition)); + _webSurface->setMaxFps((QUrl(newSourceURL).host().endsWith("youtube.com", Qt::CaseInsensitive)) ? YOUTUBE_MAX_FPS : _maxFPS); ::hifi::scripting::setLocalAccessSafeThread(false); _sourceURL = newSourceURL; } else if (_contentType != ContentType::HtmlContent) { diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.h b/libraries/entities-renderer/src/RenderableWebEntityItem.h index 0db69851cc..ffd5880c1e 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.h +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.h @@ -33,7 +33,6 @@ public: static const QString QML; static const char* URL_PROPERTY; - static const char* MAX_FPS_PROPERTY; static const char* SCRIPT_URL_PROPERTY; static const char* GLOBAL_POSITION_PROPERTY; static const char* USE_BACKGROUND_PROPERTY; From 103288b8de82b2049d60bec8ee4fb36a8de653eb Mon Sep 17 00:00:00 2001 From: Kasen IO Date: Tue, 18 Aug 2020 20:04:27 -0400 Subject: [PATCH 024/174] Update bool name "isTransparentWeb" -> "transparent" --- libraries/entities-renderer/src/RenderableWebEntityItem.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp index 4f6beb5e90..6117065df6 100644 --- a/libraries/entities-renderer/src/RenderableWebEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableWebEntityItem.cpp @@ -307,14 +307,14 @@ void WebEntityRenderer::doRender(RenderArgs* args) { glm::vec4 color; Transform transform; bool forward; - bool isTransparentWeb; + bool transparent; withReadLock([&] { float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f; color = glm::vec4(toGlm(_color), _alpha * fadeRatio); color = EntityRenderer::calculatePulseColor(color, _pulseProperties, _created); transform = _renderTransform; forward = _renderLayer != RenderLayer::WORLD || args->_renderMethod == render::Args::FORWARD; - isTransparentWeb = isTransparent(); + transparent = isTransparent(); }); if (color.a == 0.0f) { @@ -328,7 +328,7 @@ void WebEntityRenderer::doRender(RenderArgs* args) { // Turn off jitter for these entities batch.pushProjectionJitter(); - DependencyManager::get()->bindWebBrowserProgram(batch, isTransparentWeb, forward); + DependencyManager::get()->bindWebBrowserProgram(batch, transparent, forward); DependencyManager::get()->renderQuad(batch, topLeft, bottomRight, texMin, texMax, color, _geometryId); batch.popProjectionJitter(); batch.setResourceTexture(0, nullptr); From bda88828a0f907eed02cefe42b15442d1dcaf7c1 Mon Sep 17 00:00:00 2001 From: Alezia Kurdis <60075796+AleziaKurdis@users.noreply.github.com> Date: Tue, 18 Aug 2020 23:21:32 -0400 Subject: [PATCH 025/174] Tooltip for useBackground Tooltip for useBackground --- .../create/assets/data/createAppTooltips.json | 1331 +++++++++-------- 1 file changed, 667 insertions(+), 664 deletions(-) diff --git a/scripts/system/create/assets/data/createAppTooltips.json b/scripts/system/create/assets/data/createAppTooltips.json index 4efd0593fb..7c0ca4f6fa 100644 --- a/scripts/system/create/assets/data/createAppTooltips.json +++ b/scripts/system/create/assets/data/createAppTooltips.json @@ -1,664 +1,667 @@ -{ - "shape": { - "tooltip": "The shape of this entity's geometry." - }, - "color": { - "tooltip": "The color of this entity." - }, - "shapeAlpha": { - "tooltip": "The opacity of the entity between 0.0 fully transparent and 1.0 completely opaque." - }, - "text": { - "tooltip": "The text to display on the entity." - }, - "textColor": { - "tooltip": "The color of the text." - }, - "textAlpha": { - "tooltip": "The opacity of the text between 0.0 fully transparent and 1.0 completely opaque." - }, - "backgroundColor": { - "tooltip": "The color of the background." - }, - "backgroundAlpha": { - "tooltip": "The opacity of the background between 0.0 fully transparent and 1.0 completely opaque." - }, - "lineHeight": { - "tooltip": "The height of each line of text. This determines the size of the text." - }, - "font": { - "tooltip": "The font to render the text. Supported values: \"Courier\", \"Inconsolata\", \"Roboto\", \"Timeless\", or a URL to a .sdff file." - }, - "textEffect": { - "tooltip": "The effect that is applied to the text." - }, - "textEffectColor": { - "tooltip": "The color of the text effect." - }, - "textEffectThickness": { - "tooltip": "The magnitude of the text effect." - }, - "textBillboardMode": { - "tooltip": "If enabled, determines how the entity will face the camera.", - "jsPropertyName": "billboardMode" - }, - "topMargin": { - "tooltip": "The top margin, in meters." - }, - "rightMargin": { - "tooltip": "The right margin, in meters." - }, - "bottomMargin": { - "tooltip": "The bottom margin, in meters." - }, - "leftMargin": { - "tooltip": "The left margin, in meters." - }, - "unlit": { - "tooltip": "If enabled, the entity will not be lit by the keylight or local lights.", - "jsPropertyName": "unlit" - }, - "zoneShapeType": { - "tooltip": "The shape of the volume in which the zone's lighting effects and avatar permissions have effect.", - "jsPropertyName": "shapeType" - }, - "zoneCompoundShapeURL": { - "tooltip": "The model file to use for the compound shape if Shape Type is \"Use Compound Shape URL\".", - "jsPropertyName": "compoundShapeURL" - }, - "flyingAllowed": { - "tooltip": "If enabled, users can fly in the zone." - }, - "ghostingAllowed": { - "tooltip": "If enabled, users with avatar collisions turned off will not collide with content in the zone." - }, - "filterURL": { - "tooltip": "The URL of a JS file that checks for changes to entity properties within the zone. Runs periodically." - }, - "keyLightMode": { - "tooltip": "Configures the key light in the zone. This light is directional." - }, - "keyLight.color": { - "tooltip": "The color of the key light." - }, - "keyLight.intensity": { - "tooltip": "The intensity of the key light." - }, - "keyLight.direction.y": { - "tooltip": "The angle in deg at which light emits. Starts in the entity's -z direction, and rotates around its y axis." - }, - "keyLight.direction.x": { - "tooltip": "The angle in deg at which light emits. Starts in the entity's -z direction, and rotates around its x axis." - }, - "keyLight.castShadows": { - "tooltip": "If enabled, shadows are cast. The entity or avatar casting the shadow must also have Cast Shadows enabled. Note: Shadows are rendered only on high-profiled computers. This setting will have no effect on computers profiled to medium or low graphics." - }, - "keyLight.shadowBias": { - "tooltip": "The bias of the shadows cast by the light. Use this to fine-tune your shadows to your scene to prevent shadow acne and peter panning." - }, - "keyLight.shadowMaxDistance": { - "tooltip": "The max distance from your view at which shadows will be computed." - }, - "skyboxMode": { - "tooltip": "Configures the skybox in the zone. The skybox is a cube map image." - }, - "skybox.color": { - "tooltip": "If the URL is blank, this changes the color of the sky, otherwise it modifies the color of the skybox." - }, - "skybox.url": { - "tooltip": "A cube map image that is used to render the sky." - }, - "ambientLightMode": { - "tooltip": "Configures the ambient light in the zone. Use this if you want your skybox to reflect light on the content." - }, - "ambientLight.ambientIntensity": { - "tooltip": "The intensity of the ambient light." - }, - "ambientLight.ambientURL": { - "tooltip": "A cube map image that defines the color of the light coming from each direction." - }, - "hazeMode": { - "tooltip": "Configures the haze in the scene." - }, - "haze.hazeRange": { - "tooltip": "How far the haze extends out. This is measured in meters." - }, - "haze.hazeAltitudeEffect": { - "tooltip": "If enabled, this adjusts the haze intensity as it gets higher." - }, - "haze.hazeBaseRef": { - "tooltip": "The base of the altitude range. Measured in entity space." - }, - "haze.hazeCeiling": { - "tooltip": "The ceiling of the altitude range. Measured in entity space." - }, - "haze.hazeColor": { - "tooltip": "The color of the haze." - }, - "haze.hazeBackgroundBlend": { - "tooltip": "How much the skybox shows through the haze. The higher the value, the more it shows through." - }, - "haze.hazeEnableGlare": { - "tooltip": "If enabled, a glare is enabled on the skybox, based on the key light." - }, - "haze.hazeGlareColor": { - "tooltip": "The color of the glare based on the key light." - }, - "haze.hazeGlareAngle": { - "tooltip": "The angular size of the glare and how much it encompasses the skybox, based on the key light." - }, - "bloomMode": { - "tooltip": "Configures how much bright areas of the scene glow." - }, - "bloom.bloomIntensity": { - "tooltip": "The intensity, or brightness, of the bloom effect." - }, - "bloom.bloomThreshold": { - "tooltip": "The cutoff of the bloom. The higher the value, the more only bright areas of the scene will glow." - }, - "bloom.bloomSize": { - "tooltip": "The radius of bloom. The higher the value, the larger the bloom." - }, - "avatarPriority": { - "tooltip": "Alter Avatars' update priorities." - }, - "screenshare": { - "tooltip": "Enable screen-sharing within this zone" - }, - "modelURL": { - "tooltip": "A mesh model from an FBX or OBJ file." - }, - "shapeType": { - "tooltip": "The shape of the collision hull used if collisions are enabled. This affects how an entity collides." - }, - "compoundShapeURL": { - "tooltip": "The model file to use for the compound shape if Collision Shape is \"Compound\"." - }, - "animation.url": { - "tooltip": "An animation to play on the model." - }, - "animation.running": { - "tooltip": "If enabled, the animation on the model will play automatically." - }, - "animation.allowTranslation": { - "tooltip": "If enabled, this allows an entity to move in space during an animation." - }, - "animation.loop": { - "tooltip": "If enabled, then the animation will continuously repeat." - }, - "animation.hold": { - "tooltip": "If enabled, then rotations and translations of the last frame played are maintained when the animation stops." - }, - "animation.currentFrame": { - "tooltip": "The current frame being played in the animation." - }, - "animation.firstFrame": { - "tooltip": "The first frame to play in the animation." - }, - "animation.lastFrame": { - "tooltip": "The last frame to play in the animation." - }, - "animation.fps": { - "tooltip": "The speed of the animation." - }, - "textures": { - "tooltip": "A JSON string containing a texture. Use a name from the Original Texture property to override it." - }, - "originalTextures": { - "tooltip": "A JSON string containing the original texture used on the model." - }, - "imageURL": { - "tooltip": "The URL for the image source." - }, - "imageColor": { - "tooltip": "The tint to be applied to the image.", - "jsPropertyName": "color" - }, - "imageAlpha": { - "tooltip": "The opacity of the image between 0.0 fully transparent and 1.0 completely opaque." - }, - "emissive": { - "tooltip": "If enabled, the image will display at full brightness." - }, - "subImage": { - "tooltip": "The area of the image that is displayed." - }, - "imageBillboardMode": { - "tooltip": "If enabled, determines how the entity will face the camera.", - "jsPropertyName": "billboardMode" - }, - "keepAspectRatio": { - "tooltip": "If enabled, the image will maintain its original aspect ratio." - }, - "sourceUrl": { - "tooltip": "The URL for the web page source." - }, - "dpi": { - "tooltip": "The resolution to display the page at, in pixels per inch. Use this to resize your web source in the frame." - }, - "webBillboardMode": { - "tooltip": "If enabled, determines how the entity will face the camera.", - "jsPropertyName": "billboardMode" - }, - "inputMode": { - "tooltip": "The user input mode to use." - }, - "showKeyboardFocusHighlight": { - "tooltip": "If enabled, highlights when it has keyboard focus." - }, - "isEmitting": { - "tooltip": "If enabled, then particles are emitted." - }, - "lifespan": { - "tooltip": "How long each particle lives, measured in seconds." - }, - "maxParticles": { - "tooltip": "The maximum number of particles to render at one time. Older particles are swapped out for new ones." - }, - "particleTextures": { - "tooltip": "The URL of a JPG or PNG image file to display for each particle.", - "jsPropertyName": "textures" - }, - "emitRate": { - "tooltip": "The number of particles per second to emit." - }, - "emitSpeed": { - "tooltip": "The speed that each particle is emitted at, measured in m/s." - }, - "speedSpread": { - "tooltip": "The spread in speeds at which particles are emitted at, resulting in a variety of speeds." - }, - "particleShapeType": { - "tooltip": "The shape of the surface from which to emit particles.", - "jsPropertyName": "shapeType" - }, - "particleCompoundShapeURL": { - "tooltip": "The model file to use for the particle emitter if Shape Type is \"Use Compound Shape URL\".", - "jsPropertyName": "compoundShapeURL" - }, - "emitDimensions": { - "tooltip": "The outer limit radius in dimensions that the particles can be emitted from." - }, - "emitOrientation": { - "tooltip": "The orientation of particle emission relative to the entity's axes." - }, - "emitRadiusStart": { - "tooltip": "The inner limit radius in dimensions that the particles start emitting from." - }, - "emitterShouldTrail": { - "tooltip": "If enabled, then particles are \"left behind\" as the emitter moves, otherwise they are not." - }, - "particleRadiusTriple": { - "tooltip": "The size of each particle.", - "jsPropertyName": "particleRadius" - }, - "particleRadius": { - "tooltip": "The size of each particle." - }, - "radiusStart": { - "tooltip": "The start size of each particle." - }, - "radiusFinish": { - "tooltip": "The finish size of each particle." - }, - "radiusSpread": { - "tooltip": "The spread in size that each particle is given, resulting in a variety of sizes." - }, - "particleColorTriple": { - "tooltip": "The color of each particle.", - "jsPropertyName": "color" - }, - "particleColor": { - "tooltip": "The color of each particle.", - "jsPropertyName": "color" - }, - "colorStart": { - "tooltip": "The start color of each particle." - }, - "colorFinish": { - "tooltip": "The finish color of each particle." - }, - "colorSpread": { - "tooltip": "The spread in color that each particle is given, resulting in a variety of colors." - }, - "particleAlphaTriple": { - "tooltip": "The opacity of each particle between 0.0 fully transparent and 1.0 completely opaque.", - "jsPropertyName": "alpha" - }, - "alpha": { - "tooltip": "The opacity of each particle between 0.0 fully transparent and 1.0 completely opaque." - }, - "alphaStart": { - "tooltip": "The initial opacity level of each particle between 0.0 fully transparent and 1.0 completely opaque." - }, - "alphaFinish": { - "tooltip": "The final opacity level of each particle between 0.0 fully transparent and 1.0 completely opaque." - }, - "alphaSpread": { - "tooltip": "The spread in opacity that each particle is given, resulting in a variety of opacity levels." - }, - "emitAcceleration": { - "tooltip": "The acceleration that is applied to each particle during its lifetime." - }, - "accelerationSpread": { - "tooltip": "The spread in accelerations that each particle is given, resulting in a variety of accelerations." - }, - "particleSpinTriple": { - "tooltip": "The spin of each particle.", - "jsPropertyName": "particleSpin" - }, - "particleSpin": { - "tooltip": "The spin of each particle." - }, - "spinStart": { - "tooltip": "The start spin of each particle." - }, - "spinFinish": { - "tooltip": "The finish spin of each particle." - }, - "spinSpread": { - "tooltip": "The spread in spin that each particle is given, resulting in a variety of spins." - }, - "rotateWithEntity": { - "tooltip": "If enabled, each particle will spin relative to the rotation of the entity as a whole." - }, - "particlePolarTriple": { - "tooltip": "The angle range in deg at which particles are emitted. Starts in the entity's -z direction, and rotates around its y axis.", - "skipJSProperty": true - }, - "polarStart": { - "tooltip": "The start angle in deg at which particles are emitted. Starts in the entity's -z direction, and rotates around its y axis." - }, - "polarFinish": { - "tooltip": "The finish angle in deg at which particles are emitted. Starts in the entity's -z direction, and rotates around its y axis." - }, - "particleAzimuthTriple": { - "tooltip": "The angle range in deg at which particles are emitted. Starts in the entity's -x direction, and rotates around its z axis.", - "skipJSProperty": true - }, - "azimuthStart": { - "tooltip": "The start angle in deg at which particles are emitted. Starts in the entity's -x direction, and rotates around its z axis." - }, - "azimuthFinish": { - "tooltip": "The finish angle in deg at which particles are emitted. Starts in the entity's -x direction, and rotates around its z axis." - }, - "lightColor": { - "tooltip": "The color of the light emitted.", - "jsPropertyName": "color" - }, - "intensity": { - "tooltip": "The brightness of the light." - }, - "falloffRadius": { - "tooltip": "The distance from the light's center where the intensity is reduced." - }, - "isSpotlight": { - "tooltip": "If enabled, then the light is directional, otherwise the light is a point light which emits light in all directions." - }, - "exponent": { - "tooltip": "Affects the softness of the spotlight beam; the higher the value, the softer the beam." - }, - "cutoff": { - "tooltip": "Affects the size of the spotlight beam; the higher the value, the larger the beam." - }, - "materialURL": { - "tooltip": "The URL to an external JSON file or \"materialData\", \"materialData? to use Material Data." - }, - "materialData": { - "tooltip": "Can be used instead of a JSON file when material set to materialData." - }, - "parentMaterialName": { - "tooltip": "The target mesh indices or material names that this material entity should be assigned to on it's parent. This only supports parents that are Avatars as well as Shape or Model entity types." - }, - "priority": { - "tooltip": "The priority of the material, where a larger number means higher priority. Original materials = 0." - }, - "materialMappingMode": { - "tooltip": "How the material is mapped to the entity. If set to \"UV space\", then the material will be applied with the target entity's UV coordinates. If set to \"3D Projected\", then the 3D transform of the material entity will be used." - }, - "materialMappingPos": { - "tooltip": "The offset position of the bottom left of the material within the parent's UV space." - }, - "materialMappingScale": { - "tooltip": "How many times the material will repeat in each direction within the parent's UV space." - }, - "materialMappingRot": { - "tooltip": "How much to rotate the material within the parent's UV-space, in degrees." - }, - "materialRepeat": { - "tooltip": "If enabled, the material will repeat, otherwise it will clamp." - }, - "followCamera": { - "tooltip": "If enabled, the grid is always visible even as the camera moves to another position." - }, - "majorGridEvery": { - "tooltip": "The number of \"Minor Grid Every\" intervals at which to draw a thick grid line." - }, - "minorGridEvery": { - "tooltip": "The real number of meters at which to draw thin grid lines." - }, - "id": { - "tooltip": "The unique identifier of this entity." - }, - "name": { - "tooltip": "The name of this entity." - }, - "description": { - "tooltip": "Use this field to describe the entity." - }, - "position": { - "tooltip": "The global position of this entity." - }, - "localPosition": { - "tooltip": "The local position of this entity." - }, - "rotation": { - "tooltip": "The global rotation of this entity." - }, - "localRotation": { - "tooltip": "The local rotation of this entity." - }, - "dimensions": { - "tooltip": "The global dimensions of this entity." - }, - "localDimensions": { - "tooltip": "The local dimensions of this entity." - }, - "scale": { - "tooltip": "The global scaling of this entity.", - "skipJSProperty": true - }, - "registrationPoint": { - "tooltip": "The point in the entity at which the entity is rotated about." - }, - "visible": { - "tooltip": "If enabled, this entity will be visible." - }, - "locked": { - "tooltip": "If enabled, this entity will be locked." - }, - "collisionless": { - "tooltip": "If enabled, this entity will collide with other entities or avatars." - }, - "dynamic": { - "tooltip": "If enabled, this entity has collisions associated with it that can affect its movement." - }, - "collidesWithStatic": { - "tooltip": "If enabled, this entity will collide with other non-moving, static entities.", - "jsPropertyName": "collidesWith" - }, - "collidesWithDynamic": { - "tooltip": "If enabled, this entity will collide with other dynamic entities.", - "jsPropertyName": "collidesWith" - }, - "collidesWithKinematic": { - "tooltip": "If enabled, this entity will collide with other kinematic entities (they have velocity but are not dynamic).", - "jsPropertyName": "collidesWith" - }, - "collidesWithOtherAvatar": { - "tooltip": "If enabled, this entity will collide with other user's avatars.", - "jsPropertyName": "collidesWith" - }, - "collidesWithMyAvatar": { - "tooltip": "If enabled, this entity will collide with your own avatar.", - "jsPropertyName": "collidesWith" - }, - "collisionSoundURL": { - "tooltip": "The URL of a sound to play when the entity collides with something else." - }, - "grab.grabbable": { - "tooltip": "If enabled, this entity will allow grabbing input and will be movable." - }, - "grab.triggerable": { - "tooltip": "If enabled, the collider on this entity is used for triggering events." - }, - "cloneable": { - "tooltip": "If enabled, this entity can be duplicated." - }, - "cloneLifetime": { - "tooltip": "The lifetime for clones of this entity." - }, - "cloneLimit": { - "tooltip": "The total number of clones of this entity that can exist in the domain at any given time." - }, - "cloneDynamic": { - "tooltip": "If enabled, then clones created from this entity will be dynamic, allowing the clone to collide." - }, - "cloneAvatarEntity": { - "tooltip": "If enabled, then clones created from this entity will be created as avatar entities." - }, - "grab.grabFollowsController": { - "tooltip": "If enabled, grabbed entities will follow the movements of your hand controller instead of your avatar's hand." - }, - "canCastShadow": { - "tooltip": "If enabled, the geometry of this entity casts shadows when a shadow-casting light source shines on it. Note: Shadows are rendered only on high-profiled computers. This setting will have no effect on computers profiled to medium or low graphics.." - }, - "ignorePickIntersection": { - "tooltip": "If enabled, this entity will not be considered for ray picks, and will also not occlude other entities when picking." - }, - "parentID": { - "tooltip": "The ID of the entity or avatar that this entity is parented to." - }, - "parentJointIndex": { - "tooltip": "If the entity is parented to an avatar, this joint defines where on the avatar the entity is parented." - }, - "href": { - "tooltip": "The URL that will be opened when a user clicks on this entity. Useful for web pages and portals." - }, - "script": { - "tooltip": "The URL to an external JS file to add behaviors to the client." - }, - "serverScripts": { - "tooltip": "The URL to an external JS file to add behaviors to the server." - }, - "serverScriptsStatus": { - "tooltip": "The status of the server script, if provided. This shows if it's running or has an error.", - "skipJSProperty": true - }, - "hasLifetime": { - "tooltip": "If enabled, the entity will disappear after a certain amount of time specified by Lifetime.", - "jsPropertyName": "lifetime" - }, - "lifetime": { - "tooltip": "The time this entity will exist in the environment for." - }, - "userData": { - "tooltip": "Used to store extra data about the entity in JSON format." - }, - "localVelocity": { - "tooltip": "The linear velocity vector of the entity. The velocity at which this entity moves forward in space." - }, - "damping": { - "tooltip": "The linear damping to slow down the linear velocity of an entity over time." - }, - "localAngularVelocity": { - "tooltip": "The angular velocity of the entity in rad/s with respect to its axes, about its pivot point." - }, - "angularDamping": { - "tooltip": "The angular damping to slow down the angular velocity of an entity over time." - }, - "restitution": { - "tooltip": "If enabled, the entity can bounce against other objects that also have Bounciness." - }, - "friction": { - "tooltip": "The friction applied to slow down an entity when it's moving against another entity." - }, - "density": { - "tooltip": "The density of the entity. The higher the density, the harder the entity is to move." - }, - "gravity": { - "tooltip": "The acceleration due to gravity that the entity should move with, in world space." - }, - "renderLayer": { - "tooltip": "The layer on which this entity is rendered." - }, - "primitiveMode": { - "tooltip": "The mode in which to draw an entity, either \"Solid\" or \"Wireframe\"." - }, - "renderWithZones": { - "tooltip": "If set, this entity will only render when your avatar is inside of a zone in this list." - }, - "groupCulled": { - "tooltip": "If false, individual pieces of the entity may be culled by the render engine. If true, either the entire entity will be culled, or it won't at all." - }, - "webColor": { - "tooltip": "The tint of the web entity." - }, - "webAlpha": { - "tooltip": "The opacity of the web entity between 0.0 fully transparent and 1.0 completely opaque." - }, - "maxFPS": { - "tooltip": "The FPS at which to render the web entity. Higher values will have a performance impact." - }, - "scriptURL": { - "tooltip": "The URL of a script to inject into the web page." - }, - "alignToGrid": { - "tooltip": "Used to align entities to the grid, or floor of the environment.", - "skipJSProperty": true - }, - "createModel": { - "tooltip": "An entity that is based on a custom mesh created from an .OBJ or .FBX.", - "skipJSProperty": true - }, - "createShape": { - "tooltip": "An entity that has many different primitive shapes.", - "skipJSProperty": true - }, - "createLight": { - "tooltip": "An entity that emits light.", - "skipJSProperty": true - }, - "createText": { - "tooltip": "An entity that displays text on a panel.", - "skipJSProperty": true - }, - "createImage": { - "tooltip": "An entity that displays an image on a panel.", - "skipJSProperty": true - }, - "createWeb": { - "tooltip": "An entity that displays a web page on a panel.", - "skipJSProperty": true - }, - "createZone": { - "tooltip": "An entity that can be used for skyboxes, lighting, and can constrain or change avatar behaviors.", - "skipJSProperty": true - }, - "createParticle": { - "tooltip": "An entity that emits particles.", - "skipJSProperty": true - }, - "createMaterial": { - "tooltip": "An entity that creates a material that can be attached to a Shape or Model.", - "skipJSProperty": true - }, - "useAssetServer": { - "tooltip": "A server that hosts content and assets. You can't take items that are hosted here into other domains.", - "skipJSProperty": true - }, - "importNewEntity": { - "tooltip": "Import a local or hosted file that can be used across domains.", - "skipJSProperty": true - } -} +{ + "shape": { + "tooltip": "The shape of this entity's geometry." + }, + "color": { + "tooltip": "The color of this entity." + }, + "shapeAlpha": { + "tooltip": "The opacity of the entity between 0.0 fully transparent and 1.0 completely opaque." + }, + "text": { + "tooltip": "The text to display on the entity." + }, + "textColor": { + "tooltip": "The color of the text." + }, + "textAlpha": { + "tooltip": "The opacity of the text between 0.0 fully transparent and 1.0 completely opaque." + }, + "backgroundColor": { + "tooltip": "The color of the background." + }, + "backgroundAlpha": { + "tooltip": "The opacity of the background between 0.0 fully transparent and 1.0 completely opaque." + }, + "lineHeight": { + "tooltip": "The height of each line of text. This determines the size of the text." + }, + "font": { + "tooltip": "The font to render the text. Supported values: \"Courier\", \"Inconsolata\", \"Roboto\", \"Timeless\", or a URL to a .sdff file." + }, + "textEffect": { + "tooltip": "The effect that is applied to the text." + }, + "textEffectColor": { + "tooltip": "The color of the text effect." + }, + "textEffectThickness": { + "tooltip": "The magnitude of the text effect." + }, + "textBillboardMode": { + "tooltip": "If enabled, determines how the entity will face the camera.", + "jsPropertyName": "billboardMode" + }, + "topMargin": { + "tooltip": "The top margin, in meters." + }, + "rightMargin": { + "tooltip": "The right margin, in meters." + }, + "bottomMargin": { + "tooltip": "The bottom margin, in meters." + }, + "leftMargin": { + "tooltip": "The left margin, in meters." + }, + "unlit": { + "tooltip": "If enabled, the entity will not be lit by the keylight or local lights.", + "jsPropertyName": "unlit" + }, + "zoneShapeType": { + "tooltip": "The shape of the volume in which the zone's lighting effects and avatar permissions have effect.", + "jsPropertyName": "shapeType" + }, + "zoneCompoundShapeURL": { + "tooltip": "The model file to use for the compound shape if Shape Type is \"Use Compound Shape URL\".", + "jsPropertyName": "compoundShapeURL" + }, + "flyingAllowed": { + "tooltip": "If enabled, users can fly in the zone." + }, + "ghostingAllowed": { + "tooltip": "If enabled, users with avatar collisions turned off will not collide with content in the zone." + }, + "filterURL": { + "tooltip": "The URL of a JS file that checks for changes to entity properties within the zone. Runs periodically." + }, + "keyLightMode": { + "tooltip": "Configures the key light in the zone. This light is directional." + }, + "keyLight.color": { + "tooltip": "The color of the key light." + }, + "keyLight.intensity": { + "tooltip": "The intensity of the key light." + }, + "keyLight.direction.y": { + "tooltip": "The angle in deg at which light emits. Starts in the entity's -z direction, and rotates around its y axis." + }, + "keyLight.direction.x": { + "tooltip": "The angle in deg at which light emits. Starts in the entity's -z direction, and rotates around its x axis." + }, + "keyLight.castShadows": { + "tooltip": "If enabled, shadows are cast. The entity or avatar casting the shadow must also have Cast Shadows enabled. Note: Shadows are rendered only on high-profiled computers. This setting will have no effect on computers profiled to medium or low graphics." + }, + "keyLight.shadowBias": { + "tooltip": "The bias of the shadows cast by the light. Use this to fine-tune your shadows to your scene to prevent shadow acne and peter panning." + }, + "keyLight.shadowMaxDistance": { + "tooltip": "The max distance from your view at which shadows will be computed." + }, + "skyboxMode": { + "tooltip": "Configures the skybox in the zone. The skybox is a cube map image." + }, + "skybox.color": { + "tooltip": "If the URL is blank, this changes the color of the sky, otherwise it modifies the color of the skybox." + }, + "skybox.url": { + "tooltip": "A cube map image that is used to render the sky." + }, + "ambientLightMode": { + "tooltip": "Configures the ambient light in the zone. Use this if you want your skybox to reflect light on the content." + }, + "ambientLight.ambientIntensity": { + "tooltip": "The intensity of the ambient light." + }, + "ambientLight.ambientURL": { + "tooltip": "A cube map image that defines the color of the light coming from each direction." + }, + "hazeMode": { + "tooltip": "Configures the haze in the scene." + }, + "haze.hazeRange": { + "tooltip": "How far the haze extends out. This is measured in meters." + }, + "haze.hazeAltitudeEffect": { + "tooltip": "If enabled, this adjusts the haze intensity as it gets higher." + }, + "haze.hazeBaseRef": { + "tooltip": "The base of the altitude range. Measured in entity space." + }, + "haze.hazeCeiling": { + "tooltip": "The ceiling of the altitude range. Measured in entity space." + }, + "haze.hazeColor": { + "tooltip": "The color of the haze." + }, + "haze.hazeBackgroundBlend": { + "tooltip": "How much the skybox shows through the haze. The higher the value, the more it shows through." + }, + "haze.hazeEnableGlare": { + "tooltip": "If enabled, a glare is enabled on the skybox, based on the key light." + }, + "haze.hazeGlareColor": { + "tooltip": "The color of the glare based on the key light." + }, + "haze.hazeGlareAngle": { + "tooltip": "The angular size of the glare and how much it encompasses the skybox, based on the key light." + }, + "bloomMode": { + "tooltip": "Configures how much bright areas of the scene glow." + }, + "bloom.bloomIntensity": { + "tooltip": "The intensity, or brightness, of the bloom effect." + }, + "bloom.bloomThreshold": { + "tooltip": "The cutoff of the bloom. The higher the value, the more only bright areas of the scene will glow." + }, + "bloom.bloomSize": { + "tooltip": "The radius of bloom. The higher the value, the larger the bloom." + }, + "avatarPriority": { + "tooltip": "Alter Avatars' update priorities." + }, + "screenshare": { + "tooltip": "Enable screen-sharing within this zone" + }, + "modelURL": { + "tooltip": "A mesh model from an FBX or OBJ file." + }, + "shapeType": { + "tooltip": "The shape of the collision hull used if collisions are enabled. This affects how an entity collides." + }, + "compoundShapeURL": { + "tooltip": "The model file to use for the compound shape if Collision Shape is \"Compound\"." + }, + "animation.url": { + "tooltip": "An animation to play on the model." + }, + "animation.running": { + "tooltip": "If enabled, the animation on the model will play automatically." + }, + "animation.allowTranslation": { + "tooltip": "If enabled, this allows an entity to move in space during an animation." + }, + "animation.loop": { + "tooltip": "If enabled, then the animation will continuously repeat." + }, + "animation.hold": { + "tooltip": "If enabled, then rotations and translations of the last frame played are maintained when the animation stops." + }, + "animation.currentFrame": { + "tooltip": "The current frame being played in the animation." + }, + "animation.firstFrame": { + "tooltip": "The first frame to play in the animation." + }, + "animation.lastFrame": { + "tooltip": "The last frame to play in the animation." + }, + "animation.fps": { + "tooltip": "The speed of the animation." + }, + "textures": { + "tooltip": "A JSON string containing a texture. Use a name from the Original Texture property to override it." + }, + "originalTextures": { + "tooltip": "A JSON string containing the original texture used on the model." + }, + "imageURL": { + "tooltip": "The URL for the image source." + }, + "imageColor": { + "tooltip": "The tint to be applied to the image.", + "jsPropertyName": "color" + }, + "imageAlpha": { + "tooltip": "The opacity of the image between 0.0 fully transparent and 1.0 completely opaque." + }, + "emissive": { + "tooltip": "If enabled, the image will display at full brightness." + }, + "subImage": { + "tooltip": "The area of the image that is displayed." + }, + "imageBillboardMode": { + "tooltip": "If enabled, determines how the entity will face the camera.", + "jsPropertyName": "billboardMode" + }, + "keepAspectRatio": { + "tooltip": "If enabled, the image will maintain its original aspect ratio." + }, + "sourceUrl": { + "tooltip": "The URL for the web page source." + }, + "dpi": { + "tooltip": "The resolution to display the page at, in pixels per inch. Use this to resize your web source in the frame." + }, + "webBillboardMode": { + "tooltip": "If enabled, determines how the entity will face the camera.", + "jsPropertyName": "billboardMode" + }, + "inputMode": { + "tooltip": "The user input mode to use." + }, + "showKeyboardFocusHighlight": { + "tooltip": "If enabled, highlights when it has keyboard focus." + }, + "isEmitting": { + "tooltip": "If enabled, then particles are emitted." + }, + "lifespan": { + "tooltip": "How long each particle lives, measured in seconds." + }, + "maxParticles": { + "tooltip": "The maximum number of particles to render at one time. Older particles are swapped out for new ones." + }, + "particleTextures": { + "tooltip": "The URL of a JPG or PNG image file to display for each particle.", + "jsPropertyName": "textures" + }, + "emitRate": { + "tooltip": "The number of particles per second to emit." + }, + "emitSpeed": { + "tooltip": "The speed that each particle is emitted at, measured in m/s." + }, + "speedSpread": { + "tooltip": "The spread in speeds at which particles are emitted at, resulting in a variety of speeds." + }, + "particleShapeType": { + "tooltip": "The shape of the surface from which to emit particles.", + "jsPropertyName": "shapeType" + }, + "particleCompoundShapeURL": { + "tooltip": "The model file to use for the particle emitter if Shape Type is \"Use Compound Shape URL\".", + "jsPropertyName": "compoundShapeURL" + }, + "emitDimensions": { + "tooltip": "The outer limit radius in dimensions that the particles can be emitted from." + }, + "emitOrientation": { + "tooltip": "The orientation of particle emission relative to the entity's axes." + }, + "emitRadiusStart": { + "tooltip": "The inner limit radius in dimensions that the particles start emitting from." + }, + "emitterShouldTrail": { + "tooltip": "If enabled, then particles are \"left behind\" as the emitter moves, otherwise they are not." + }, + "particleRadiusTriple": { + "tooltip": "The size of each particle.", + "jsPropertyName": "particleRadius" + }, + "particleRadius": { + "tooltip": "The size of each particle." + }, + "radiusStart": { + "tooltip": "The start size of each particle." + }, + "radiusFinish": { + "tooltip": "The finish size of each particle." + }, + "radiusSpread": { + "tooltip": "The spread in size that each particle is given, resulting in a variety of sizes." + }, + "particleColorTriple": { + "tooltip": "The color of each particle.", + "jsPropertyName": "color" + }, + "particleColor": { + "tooltip": "The color of each particle.", + "jsPropertyName": "color" + }, + "colorStart": { + "tooltip": "The start color of each particle." + }, + "colorFinish": { + "tooltip": "The finish color of each particle." + }, + "colorSpread": { + "tooltip": "The spread in color that each particle is given, resulting in a variety of colors." + }, + "particleAlphaTriple": { + "tooltip": "The opacity of each particle between 0.0 fully transparent and 1.0 completely opaque.", + "jsPropertyName": "alpha" + }, + "alpha": { + "tooltip": "The opacity of each particle between 0.0 fully transparent and 1.0 completely opaque." + }, + "alphaStart": { + "tooltip": "The initial opacity level of each particle between 0.0 fully transparent and 1.0 completely opaque." + }, + "alphaFinish": { + "tooltip": "The final opacity level of each particle between 0.0 fully transparent and 1.0 completely opaque." + }, + "alphaSpread": { + "tooltip": "The spread in opacity that each particle is given, resulting in a variety of opacity levels." + }, + "emitAcceleration": { + "tooltip": "The acceleration that is applied to each particle during its lifetime." + }, + "accelerationSpread": { + "tooltip": "The spread in accelerations that each particle is given, resulting in a variety of accelerations." + }, + "particleSpinTriple": { + "tooltip": "The spin of each particle.", + "jsPropertyName": "particleSpin" + }, + "particleSpin": { + "tooltip": "The spin of each particle." + }, + "spinStart": { + "tooltip": "The start spin of each particle." + }, + "spinFinish": { + "tooltip": "The finish spin of each particle." + }, + "spinSpread": { + "tooltip": "The spread in spin that each particle is given, resulting in a variety of spins." + }, + "rotateWithEntity": { + "tooltip": "If enabled, each particle will spin relative to the rotation of the entity as a whole." + }, + "particlePolarTriple": { + "tooltip": "The angle range in deg at which particles are emitted. Starts in the entity's -z direction, and rotates around its y axis.", + "skipJSProperty": true + }, + "polarStart": { + "tooltip": "The start angle in deg at which particles are emitted. Starts in the entity's -z direction, and rotates around its y axis." + }, + "polarFinish": { + "tooltip": "The finish angle in deg at which particles are emitted. Starts in the entity's -z direction, and rotates around its y axis." + }, + "particleAzimuthTriple": { + "tooltip": "The angle range in deg at which particles are emitted. Starts in the entity's -x direction, and rotates around its z axis.", + "skipJSProperty": true + }, + "azimuthStart": { + "tooltip": "The start angle in deg at which particles are emitted. Starts in the entity's -x direction, and rotates around its z axis." + }, + "azimuthFinish": { + "tooltip": "The finish angle in deg at which particles are emitted. Starts in the entity's -x direction, and rotates around its z axis." + }, + "lightColor": { + "tooltip": "The color of the light emitted.", + "jsPropertyName": "color" + }, + "intensity": { + "tooltip": "The brightness of the light." + }, + "falloffRadius": { + "tooltip": "The distance from the light's center where the intensity is reduced." + }, + "isSpotlight": { + "tooltip": "If enabled, then the light is directional, otherwise the light is a point light which emits light in all directions." + }, + "exponent": { + "tooltip": "Affects the softness of the spotlight beam; the higher the value, the softer the beam." + }, + "cutoff": { + "tooltip": "Affects the size of the spotlight beam; the higher the value, the larger the beam." + }, + "materialURL": { + "tooltip": "The URL to an external JSON file or \"materialData\", \"materialData? to use Material Data." + }, + "materialData": { + "tooltip": "Can be used instead of a JSON file when material set to materialData." + }, + "parentMaterialName": { + "tooltip": "The target mesh indices or material names that this material entity should be assigned to on it's parent. This only supports parents that are Avatars as well as Shape or Model entity types." + }, + "priority": { + "tooltip": "The priority of the material, where a larger number means higher priority. Original materials = 0." + }, + "materialMappingMode": { + "tooltip": "How the material is mapped to the entity. If set to \"UV space\", then the material will be applied with the target entity's UV coordinates. If set to \"3D Projected\", then the 3D transform of the material entity will be used." + }, + "materialMappingPos": { + "tooltip": "The offset position of the bottom left of the material within the parent's UV space." + }, + "materialMappingScale": { + "tooltip": "How many times the material will repeat in each direction within the parent's UV space." + }, + "materialMappingRot": { + "tooltip": "How much to rotate the material within the parent's UV-space, in degrees." + }, + "materialRepeat": { + "tooltip": "If enabled, the material will repeat, otherwise it will clamp." + }, + "followCamera": { + "tooltip": "If enabled, the grid is always visible even as the camera moves to another position." + }, + "majorGridEvery": { + "tooltip": "The number of \"Minor Grid Every\" intervals at which to draw a thick grid line." + }, + "minorGridEvery": { + "tooltip": "The real number of meters at which to draw thin grid lines." + }, + "id": { + "tooltip": "The unique identifier of this entity." + }, + "name": { + "tooltip": "The name of this entity." + }, + "description": { + "tooltip": "Use this field to describe the entity." + }, + "position": { + "tooltip": "The global position of this entity." + }, + "localPosition": { + "tooltip": "The local position of this entity." + }, + "rotation": { + "tooltip": "The global rotation of this entity." + }, + "localRotation": { + "tooltip": "The local rotation of this entity." + }, + "dimensions": { + "tooltip": "The global dimensions of this entity." + }, + "localDimensions": { + "tooltip": "The local dimensions of this entity." + }, + "scale": { + "tooltip": "The global scaling of this entity.", + "skipJSProperty": true + }, + "registrationPoint": { + "tooltip": "The point in the entity at which the entity is rotated about." + }, + "visible": { + "tooltip": "If enabled, this entity will be visible." + }, + "locked": { + "tooltip": "If enabled, this entity will be locked." + }, + "collisionless": { + "tooltip": "If enabled, this entity will collide with other entities or avatars." + }, + "dynamic": { + "tooltip": "If enabled, this entity has collisions associated with it that can affect its movement." + }, + "collidesWithStatic": { + "tooltip": "If enabled, this entity will collide with other non-moving, static entities.", + "jsPropertyName": "collidesWith" + }, + "collidesWithDynamic": { + "tooltip": "If enabled, this entity will collide with other dynamic entities.", + "jsPropertyName": "collidesWith" + }, + "collidesWithKinematic": { + "tooltip": "If enabled, this entity will collide with other kinematic entities (they have velocity but are not dynamic).", + "jsPropertyName": "collidesWith" + }, + "collidesWithOtherAvatar": { + "tooltip": "If enabled, this entity will collide with other user's avatars.", + "jsPropertyName": "collidesWith" + }, + "collidesWithMyAvatar": { + "tooltip": "If enabled, this entity will collide with your own avatar.", + "jsPropertyName": "collidesWith" + }, + "collisionSoundURL": { + "tooltip": "The URL of a sound to play when the entity collides with something else." + }, + "grab.grabbable": { + "tooltip": "If enabled, this entity will allow grabbing input and will be movable." + }, + "grab.triggerable": { + "tooltip": "If enabled, the collider on this entity is used for triggering events." + }, + "cloneable": { + "tooltip": "If enabled, this entity can be duplicated." + }, + "cloneLifetime": { + "tooltip": "The lifetime for clones of this entity." + }, + "cloneLimit": { + "tooltip": "The total number of clones of this entity that can exist in the domain at any given time." + }, + "cloneDynamic": { + "tooltip": "If enabled, then clones created from this entity will be dynamic, allowing the clone to collide." + }, + "cloneAvatarEntity": { + "tooltip": "If enabled, then clones created from this entity will be created as avatar entities." + }, + "grab.grabFollowsController": { + "tooltip": "If enabled, grabbed entities will follow the movements of your hand controller instead of your avatar's hand." + }, + "canCastShadow": { + "tooltip": "If enabled, the geometry of this entity casts shadows when a shadow-casting light source shines on it. Note: Shadows are rendered only on high-profiled computers. This setting will have no effect on computers profiled to medium or low graphics.." + }, + "ignorePickIntersection": { + "tooltip": "If enabled, this entity will not be considered for ray picks, and will also not occlude other entities when picking." + }, + "parentID": { + "tooltip": "The ID of the entity or avatar that this entity is parented to." + }, + "parentJointIndex": { + "tooltip": "If the entity is parented to an avatar, this joint defines where on the avatar the entity is parented." + }, + "href": { + "tooltip": "The URL that will be opened when a user clicks on this entity. Useful for web pages and portals." + }, + "script": { + "tooltip": "The URL to an external JS file to add behaviors to the client." + }, + "serverScripts": { + "tooltip": "The URL to an external JS file to add behaviors to the server." + }, + "serverScriptsStatus": { + "tooltip": "The status of the server script, if provided. This shows if it's running or has an error.", + "skipJSProperty": true + }, + "hasLifetime": { + "tooltip": "If enabled, the entity will disappear after a certain amount of time specified by Lifetime.", + "jsPropertyName": "lifetime" + }, + "lifetime": { + "tooltip": "The time this entity will exist in the environment for." + }, + "userData": { + "tooltip": "Used to store extra data about the entity in JSON format." + }, + "localVelocity": { + "tooltip": "The linear velocity vector of the entity. The velocity at which this entity moves forward in space." + }, + "damping": { + "tooltip": "The linear damping to slow down the linear velocity of an entity over time." + }, + "localAngularVelocity": { + "tooltip": "The angular velocity of the entity in rad/s with respect to its axes, about its pivot point." + }, + "angularDamping": { + "tooltip": "The angular damping to slow down the angular velocity of an entity over time." + }, + "restitution": { + "tooltip": "If enabled, the entity can bounce against other objects that also have Bounciness." + }, + "friction": { + "tooltip": "The friction applied to slow down an entity when it's moving against another entity." + }, + "density": { + "tooltip": "The density of the entity. The higher the density, the harder the entity is to move." + }, + "gravity": { + "tooltip": "The acceleration due to gravity that the entity should move with, in world space." + }, + "renderLayer": { + "tooltip": "The layer on which this entity is rendered." + }, + "primitiveMode": { + "tooltip": "The mode in which to draw an entity, either \"Solid\" or \"Wireframe\"." + }, + "renderWithZones": { + "tooltip": "If set, this entity will only render when your avatar is inside of a zone in this list." + }, + "groupCulled": { + "tooltip": "If false, individual pieces of the entity may be culled by the render engine. If true, either the entire entity will be culled, or it won't at all." + }, + "webColor": { + "tooltip": "The tint of the web entity." + }, + "webAlpha": { + "tooltip": "The opacity of the web entity between 0.0 fully transparent and 1.0 completely opaque." + }, + "useBackground": { + "tooltip": "If disabled, this web entity will support a transparent background for the webpage if the CSS property of the 'body' is set with 'background-color: transparent;'" + }, + "maxFPS": { + "tooltip": "The FPS at which to render the web entity. Higher values will have a performance impact." + }, + "scriptURL": { + "tooltip": "The URL of a script to inject into the web page." + }, + "alignToGrid": { + "tooltip": "Used to align entities to the grid, or floor of the environment.", + "skipJSProperty": true + }, + "createModel": { + "tooltip": "An entity that is based on a custom mesh created from an .OBJ or .FBX.", + "skipJSProperty": true + }, + "createShape": { + "tooltip": "An entity that has many different primitive shapes.", + "skipJSProperty": true + }, + "createLight": { + "tooltip": "An entity that emits light.", + "skipJSProperty": true + }, + "createText": { + "tooltip": "An entity that displays text on a panel.", + "skipJSProperty": true + }, + "createImage": { + "tooltip": "An entity that displays an image on a panel.", + "skipJSProperty": true + }, + "createWeb": { + "tooltip": "An entity that displays a web page on a panel.", + "skipJSProperty": true + }, + "createZone": { + "tooltip": "An entity that can be used for skyboxes, lighting, and can constrain or change avatar behaviors.", + "skipJSProperty": true + }, + "createParticle": { + "tooltip": "An entity that emits particles.", + "skipJSProperty": true + }, + "createMaterial": { + "tooltip": "An entity that creates a material that can be attached to a Shape or Model.", + "skipJSProperty": true + }, + "useAssetServer": { + "tooltip": "A server that hosts content and assets. You can't take items that are hosted here into other domains.", + "skipJSProperty": true + }, + "importNewEntity": { + "tooltip": "Import a local or hosted file that can be used across domains.", + "skipJSProperty": true + } +} From c6cf0b8c33936554f83d7347c9d27b8e53b411bf Mon Sep 17 00:00:00 2001 From: Alezia Kurdis <60075796+AleziaKurdis@users.noreply.github.com> Date: Tue, 18 Aug 2020 23:22:39 -0400 Subject: [PATCH 026/174] New "useBackground" property on Web Entity New "useBackground" property on Web Entity. --- .../html/js/entityProperties.js | 9435 +++++++++-------- 1 file changed, 4720 insertions(+), 4715 deletions(-) diff --git a/scripts/system/create/entityProperties/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js index 182dddf817..6a6e4d6f66 100644 --- a/scripts/system/create/entityProperties/html/js/entityProperties.js +++ b/scripts/system/create/entityProperties/html/js/entityProperties.js @@ -1,4715 +1,4720 @@ -// entityProperties.js -// -// Created by Ryan Huffman on 13 Nov 2014 -// Copyright 2014 High Fidelity, Inc. -// Copyright 2020 Vircadia contributors. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html - -/* global alert, augmentSpinButtons, clearTimeout, console, document, Element, - EventBridge, JSONEditor, openEventBridge, setTimeout, window, _, $ */ - -var currentTab = "base"; - -const DEGREES_TO_RADIANS = Math.PI / 180.0; - -const NO_SELECTION = ","; - -const PROPERTY_SPACE_MODE = Object.freeze({ - ALL: 0, - LOCAL: 1, - WORLD: 2 -}); - -const PROPERTY_SELECTION_VISIBILITY = Object.freeze({ - SINGLE_SELECTION: 1, - MULTIPLE_SELECTIONS: 2, - MULTI_DIFF_SELECTIONS: 4, - ANY_SELECTIONS: 7 /* SINGLE_SELECTION | MULTIPLE_SELECTIONS | MULTI_DIFF_SELECTIONS */ -}); - -// Multiple-selection behavior -const PROPERTY_MULTI_DISPLAY_MODE = Object.freeze({ - DEFAULT: 0, - /** - * Comma separated values - * Limited for properties with type "string" or "textarea" and readOnly enabled - */ - COMMA_SEPARATED_VALUES: 1 -}); - -const GROUPS = [ - { - id: "base", - label: "ENTITY", - properties: [ - { - label: NO_SELECTION, - type: "icon", - icons: ENTITY_TYPE_ICON, - propertyID: "type", - replaceID: "placeholder-property-type", - }, - { - label: "Name", - type: "string", - propertyID: "name", - placeholder: "Name", - replaceID: "placeholder-property-name", - }, - { - label: "ID", - type: "string", - propertyID: "id", - placeholder: "ID", - readOnly: true, - replaceID: "placeholder-property-id", - multiDisplayMode: PROPERTY_MULTI_DISPLAY_MODE.COMMA_SEPARATED_VALUES, - }, - { - label: "Description", - type: "string", - propertyID: "description", - }, - { - label: "Parent", - type: "string", - propertyID: "parentID", - onChange: parentIDChanged, - }, - { - label: "Parent Joint Index", - type: "number", - propertyID: "parentJointIndex", - }, - { - label: "", - glyph: "", - type: "bool", - propertyID: "locked", - replaceID: "placeholder-property-locked", - }, - { - label: "", - glyph: "", - type: "bool", - propertyID: "visible", - replaceID: "placeholder-property-visible", - }, - { - label: "Render Layer", - type: "dropdown", - options: { - world: "World", - front: "Front", - hud: "HUD" - }, - propertyID: "renderLayer", - }, - { - label: "Primitive Mode", - type: "dropdown", - options: { - solid: "Solid", - lines: "Wireframe", - }, - propertyID: "primitiveMode", - }, - { - label: "Render With Zones", - type: "multipleZonesSelection", - propertyID: "renderWithZones", - } - ] - }, - { - id: "shape", - label: "SHAPE", - properties: [ - { - label: "Shape", - type: "dropdown", - options: { Cube: "Box", Sphere: "Sphere", Tetrahedron: "Tetrahedron", Octahedron: "Octahedron", - Icosahedron: "Icosahedron", Dodecahedron: "Dodecahedron", Hexagon: "Hexagon", - Triangle: "Triangle", Octagon: "Octagon", Cylinder: "Cylinder", Cone: "Cone", - Circle: "Circle", Quad: "Quad" }, - propertyID: "shape", - }, - { - label: "Color", - type: "color", - propertyID: "color", - }, - { - label: "Alpha", - type: "number-draggable", - min: 0, - max: 1, - step: 0.01, - decimals: 2, - propertyID: "shapeAlpha", - propertyName: "alpha", - }, - ] - }, - { - id: "text", - label: "TEXT", - properties: [ - { - label: "Text", - type: "string", - propertyID: "text", - }, - { - label: "Text Color", - type: "color", - propertyID: "textColor", - }, - { - label: "Text Alpha", - type: "number-draggable", - min: 0, - max: 1, - step: 0.01, - decimals: 2, - propertyID: "textAlpha", - }, - { - label: "Background Color", - type: "color", - propertyID: "backgroundColor", - }, - { - label: "Background Alpha", - type: "number-draggable", - min: 0, - max: 1, - step: 0.01, - decimals: 2, - propertyID: "backgroundAlpha", - }, - { - label: "Line Height", - type: "number-draggable", - min: 0, - step: 0.001, - decimals: 4, - unit: "m", - propertyID: "lineHeight", - }, - { - label: "Font", - type: "string", - propertyID: "font", - }, - { - label: "Effect", - type: "dropdown", - options: { - none: "None", - outline: "Outline", - "outline fill": "Outline with fill", - shadow: "Shadow" - }, - propertyID: "textEffect", - }, - { - label: "Effect Color", - type: "color", - propertyID: "textEffectColor", - }, - { - label: "Effect Thickness", - type: "number-draggable", - min: 0.0, - max: 0.5, - step: 0.01, - decimals: 2, - propertyID: "textEffectThickness", - }, - { - label: "Billboard Mode", - type: "dropdown", - options: { none: "None", yaw: "Yaw", full: "Full"}, - propertyID: "textBillboardMode", - propertyName: "billboardMode", // actual entity property name - }, - { - label: "Top Margin", - type: "number-draggable", - step: 0.01, - decimals: 2, - propertyID: "topMargin", - }, - { - label: "Right Margin", - type: "number-draggable", - step: 0.01, - decimals: 2, - propertyID: "rightMargin", - }, - { - label: "Bottom Margin", - type: "number-draggable", - step: 0.01, - decimals: 2, - propertyID: "bottomMargin", - }, - { - label: "Left Margin", - type: "number-draggable", - step: 0.01, - decimals: 2, - propertyID: "leftMargin", - }, - { - label: "Unlit", - type: "bool", - propertyID: "unlit", - } - ] - }, - { - id: "zone", - label: "ZONE", - properties: [ - { - label: "Shape Type", - type: "dropdown", - options: { "box": "Box", "sphere": "Sphere", "ellipsoid": "Ellipsoid", - "cylinder-y": "Cylinder", "compound": "Use Compound Shape URL" }, - propertyID: "zoneShapeType", - propertyName: "shapeType", // actual entity property name - }, - { - label: "Compound Shape URL", - type: "string", - propertyID: "zoneCompoundShapeURL", - propertyName: "compoundShapeURL", // actual entity property name - }, - { - label: "Flying Allowed", - type: "bool", - propertyID: "flyingAllowed", - }, - { - label: "Ghosting Allowed", - type: "bool", - propertyID: "ghostingAllowed", - }, - { - label: "Filter", - type: "string", - propertyID: "filterURL", - } - ] - }, - { - id: "zone_key_light", - label: "ZONE KEY LIGHT", - properties: [ - { - label: "Key Light", - type: "dropdown", - options: { inherit: "Inherit", disabled: "Off", enabled: "On" }, - propertyID: "keyLightMode", - - }, - { - label: "Key Light Color", - type: "color", - propertyID: "keyLight.color", - showPropertyRule: { "keyLightMode": "enabled" }, - }, - { - label: "Light Intensity", - type: "number-draggable", - min: -40, - max: 40, - step: 0.01, - decimals: 2, - propertyID: "keyLight.intensity", - showPropertyRule: { "keyLightMode": "enabled" }, - }, - { - label: "Light Horizontal Angle", - type: "number-draggable", - step: 0.1, - multiplier: DEGREES_TO_RADIANS, - decimals: 2, - unit: "deg", - propertyID: "keyLight.direction.y", - showPropertyRule: { "keyLightMode": "enabled" }, - }, - { - label: "Light Vertical Angle", - type: "number-draggable", - step: 0.1, - multiplier: DEGREES_TO_RADIANS, - decimals: 2, - unit: "deg", - propertyID: "keyLight.direction.x", - showPropertyRule: { "keyLightMode": "enabled" }, - }, - { - label: "Cast Shadows", - type: "bool", - propertyID: "keyLight.castShadows", - showPropertyRule: { "keyLightMode": "enabled" }, - }, - { - label: "Shadow Bias", - type: "number-draggable", - min: 0, - max: 1, - step: 0.01, - decimals: 2, - propertyID: "keyLight.shadowBias", - showPropertyRule: { "keyLightMode": "enabled" }, - }, - { - label: "Shadow Max Distance", - type: "number-draggable", - min: 0, - max: 250, - step: 0.1, - decimals: 2, - propertyID: "keyLight.shadowMaxDistance", - showPropertyRule: { "keyLightMode": "enabled" }, - } - ] - }, - { - id: "zone_skybox", - label: "ZONE SKYBOX", - properties: [ - { - label: "Skybox", - type: "dropdown", - options: { inherit: "Inherit", disabled: "Off", enabled: "On" }, - propertyID: "skyboxMode", - }, - { - label: "Skybox Color", - type: "color", - propertyID: "skybox.color", - showPropertyRule: { "skyboxMode": "enabled" }, - }, - { - label: "Skybox Source", - type: "string", - propertyID: "skybox.url", - showPropertyRule: { "skyboxMode": "enabled" }, - } - ] - }, - { - id: "zone_ambient_light", - label: "ZONE AMBIENT LIGHT", - properties: [ - { - label: "Ambient Light", - type: "dropdown", - options: { inherit: "Inherit", disabled: "Off", enabled: "On" }, - propertyID: "ambientLightMode", - }, - { - label: "Ambient Intensity", - type: "number-draggable", - min: -200, - max: 200, - step: 0.1, - decimals: 2, - propertyID: "ambientLight.ambientIntensity", - showPropertyRule: { "ambientLightMode": "enabled" }, - }, - { - label: "Ambient Source", - type: "string", - propertyID: "ambientLight.ambientURL", - showPropertyRule: { "ambientLightMode": "enabled" }, - }, - { - type: "buttons", - buttons: [ { id: "copy", label: "Copy from Skybox", - className: "black", onClick: copySkyboxURLToAmbientURL } ], - propertyID: "copyURLToAmbient", - showPropertyRule: { "ambientLightMode": "enabled" }, - } - ] - }, - { - id: "zone_haze", - label: "ZONE HAZE", - properties: [ - { - label: "Haze", - type: "dropdown", - options: { inherit: "Inherit", disabled: "Off", enabled: "On" }, - propertyID: "hazeMode", - }, - { - label: "Range", - type: "number-draggable", - min: 1, - max: 10000, - step: 1, - decimals: 0, - unit: "m", - propertyID: "haze.hazeRange", - showPropertyRule: { "hazeMode": "enabled" }, - }, - { - label: "Use Altitude", - type: "bool", - propertyID: "haze.hazeAltitudeEffect", - showPropertyRule: { "hazeMode": "enabled" }, - }, - { - label: "Base", - type: "number-draggable", - min: -16000, - max: 16000, - step: 1, - decimals: 0, - unit: "m", - propertyID: "haze.hazeBaseRef", - showPropertyRule: { "hazeMode": "enabled" }, - }, - { - label: "Ceiling", - type: "number-draggable", - min: -16000, - max: 16000, - step: 1, - decimals: 0, - unit: "m", - propertyID: "haze.hazeCeiling", - showPropertyRule: { "hazeMode": "enabled" }, - }, - { - label: "Haze Color", - type: "color", - propertyID: "haze.hazeColor", - showPropertyRule: { "hazeMode": "enabled" }, - }, - { - label: "Background Blend", - type: "number-draggable", - min: 0, - max: 1, - step: 0.001, - decimals: 3, - propertyID: "haze.hazeBackgroundBlend", - showPropertyRule: { "hazeMode": "enabled" }, - }, - { - label: "Enable Glare", - type: "bool", - propertyID: "haze.hazeEnableGlare", - showPropertyRule: { "hazeMode": "enabled" }, - }, - { - label: "Glare Color", - type: "color", - propertyID: "haze.hazeGlareColor", - showPropertyRule: { "hazeMode": "enabled" }, - }, - { - label: "Glare Angle", - type: "number-draggable", - min: 0, - max: 180, - step: 1, - decimals: 0, - propertyID: "haze.hazeGlareAngle", - showPropertyRule: { "hazeMode": "enabled" }, - } - ] - }, - { - id: "zone_bloom", - label: "ZONE BLOOM", - properties: [ - { - label: "Bloom", - type: "dropdown", - options: { inherit: "Inherit", disabled: "Off", enabled: "On" }, - propertyID: "bloomMode", - }, - { - label: "Bloom Intensity", - type: "number-draggable", - min: 0, - max: 1, - step: 0.001, - decimals: 3, - propertyID: "bloom.bloomIntensity", - showPropertyRule: { "bloomMode": "enabled" }, - }, - { - label: "Bloom Threshold", - type: "number-draggable", - min: 0, - max: 1, - step: 0.001, - decimals: 3, - propertyID: "bloom.bloomThreshold", - showPropertyRule: { "bloomMode": "enabled" }, - }, - { - label: "Bloom Size", - type: "number-draggable", - min: 0, - max: 2, - step: 0.001, - decimals: 3, - propertyID: "bloom.bloomSize", - showPropertyRule: { "bloomMode": "enabled" }, - } - ] - }, - { - id: "zone_avatar_priority", - label: "ZONE AVATAR PRIORITY", - properties: [ - { - label: "Avatar Priority", - type: "dropdown", - options: { inherit: "Inherit", crowd: "Crowd", hero: "Hero" }, - propertyID: "avatarPriority", - }, - { - label: "Screen-share", - type: "dropdown", - options: { inherit: "Inherit", disabled: "Off", enabled: "On" }, - propertyID: "screenshare", - } - ] - }, - { - id: "model", - label: "MODEL", - properties: [ - { - label: "Model", - type: "string", - placeholder: "URL", - propertyID: "modelURL", - hideIfCertified: true, - }, - { - label: "Collision Shape", - type: "dropdown", - options: { "none": "No Collision", "box": "Box", "sphere": "Sphere", "compound": "Compound" , - "simple-hull": "Basic - Whole model", "simple-compound": "Good - Sub-meshes" , - "static-mesh": "Exact - All polygons (non-dynamic only)" }, - propertyID: "shapeType", - }, - { - label: "Compound Shape", - type: "string", - propertyID: "compoundShapeURL", - hideIfCertified: true, - }, - { - label: "Animation", - type: "string", - propertyID: "animation.url", - hideIfCertified: true, - }, - { - label: "Play Automatically", - type: "bool", - propertyID: "animation.running", - }, - { - label: "Loop", - type: "bool", - propertyID: "animation.loop", - }, - { - label: "Allow Translation", - type: "bool", - propertyID: "animation.allowTranslation", - }, - { - label: "Hold", - type: "bool", - propertyID: "animation.hold", - }, - { - label: "Animation Frame", - type: "number-draggable", - propertyID: "animation.currentFrame", - }, - { - label: "First Frame", - type: "number-draggable", - propertyID: "animation.firstFrame", - }, - { - label: "Last Frame", - type: "number-draggable", - propertyID: "animation.lastFrame", - }, - { - label: "Animation FPS", - type: "number-draggable", - propertyID: "animation.fps", - }, - { - label: "Texture", - type: "textarea", - propertyID: "textures", - }, - { - label: "Original Texture", - type: "textarea", - propertyID: "originalTextures", - readOnly: true, - hideIfCertified: true, - }, - { - label: "Group Culled", - type: "bool", - propertyID: "groupCulled", - } - ] - }, - { - id: "image", - label: "IMAGE", - properties: [ - { - label: "Image", - type: "string", - placeholder: "URL", - propertyID: "imageURL", - }, - { - label: "Color", - type: "color", - propertyID: "imageColor", - propertyName: "color", // actual entity property name - }, - { - label: "Alpha", - type: "number-draggable", - min: 0, - max: 1, - step: 0.01, - decimals: 2, - propertyID: "imageAlpha", - propertyName: "alpha", - }, - { - label: "Emissive", - type: "bool", - propertyID: "emissive", - }, - { - label: "Sub Image", - type: "rect", - min: 0, - step: 1, - subLabels: [ "x", "y", "w", "h" ], - propertyID: "subImage", - }, - { - label: "Billboard Mode", - type: "dropdown", - options: { none: "None", yaw: "Yaw", full: "Full"}, - propertyID: "imageBillboardMode", - propertyName: "billboardMode", // actual entity property name - }, - { - label: "Keep Aspect Ratio", - type: "bool", - propertyID: "keepAspectRatio", - } - ] - }, - { - id: "web", - label: "WEB", - properties: [ - { - label: "Source", - type: "string", - propertyID: "sourceUrl", - }, - { - label: "Source Resolution", - type: "number-draggable", - propertyID: "dpi", - }, - { - label: "Web Color", - type: "color", - propertyID: "webColor", - propertyName: "color", // actual entity property name - }, - { - label: "Web Alpha", - type: "number-draggable", - step: 0.001, - decimals: 3, - propertyID: "webAlpha", - propertyName: "alpha", - min: 0, - max: 1, - }, - { - label: "Max FPS", - type: "number-draggable", - step: 1, - decimals: 0, - propertyID: "maxFPS", - }, - { - label: "Billboard Mode", - type: "dropdown", - options: { none: "None", yaw: "Yaw", full: "Full"}, - propertyID: "webBillboardMode", - propertyName: "billboardMode", // actual entity property name - }, - { - label: "Input Mode", - type: "dropdown", - options: { - touch: "Touch events", - mouse: "Mouse events" - }, - propertyID: "inputMode", - }, - { - label: "Focus Highlight", - type: "bool", - propertyID: "showKeyboardFocusHighlight", - }, - { - label: "Script URL", - type: "string", - propertyID: "scriptURL", - placeholder: "URL", - } - ] - }, - { - id: "light", - label: "LIGHT", - properties: [ - { - label: "Light Color", - type: "color", - propertyID: "lightColor", - propertyName: "color", // actual entity property name - }, - { - label: "Intensity", - type: "number-draggable", - min: -1000, - max: 10000, - step: 0.1, - decimals: 2, - propertyID: "intensity", - }, - { - label: "Fall-Off Radius", - type: "number-draggable", - min: 0, - max: 10000, - step: 0.1, - decimals: 2, - unit: "m", - propertyID: "falloffRadius", - }, - { - label: "Spotlight", - type: "bool", - propertyID: "isSpotlight", - }, - { - label: "Spotlight Exponent", - type: "number-draggable", - min: 0, - step: 0.01, - decimals: 2, - propertyID: "exponent", - }, - { - label: "Spotlight Cut-Off", - type: "number-draggable", - step: 0.01, - decimals: 2, - propertyID: "cutoff", - } - ] - }, - { - id: "material", - label: "MATERIAL", - properties: [ - { - label: "Material URL", - type: "string", - propertyID: "materialURL", - }, - { - label: "Material Data", - type: "textarea", - buttons: [ { id: "clear", label: "Clear Material Data", className: "red", onClick: clearMaterialData }, - { id: "edit", label: "Edit as JSON", className: "blue", onClick: newJSONMaterialEditor }, - { id: "save", label: "Save Material Data", className: "black", onClick: saveMaterialData } ], - propertyID: "materialData", - }, - { - label: "Material Target", - type: "dynamic-multiselect", - propertyUpdate: materialTargetPropertyUpdate, - propertyID: "parentMaterialName", - selectionVisibility: PROPERTY_SELECTION_VISIBILITY.SINGLE_SELECTION, - }, - { - label: "Priority", - type: "number-draggable", - min: 0, - propertyID: "priority", - }, - { - label: "Material Mapping Mode", - type: "dropdown", - options: { - uv: "UV space", projected: "3D projected" - }, - propertyID: "materialMappingMode", - }, - { - label: "Material Position", - type: "vec2", - vec2Type: "xyz", - min: 0, - max: 1, - step: 0.1, - decimals: 4, - subLabels: [ "x", "y" ], - propertyID: "materialMappingPos", - }, - { - label: "Material Scale", - type: "vec2", - vec2Type: "xyz", - min: 0, - step: 0.1, - decimals: 4, - subLabels: [ "x", "y" ], - propertyID: "materialMappingScale", - }, - { - label: "Material Rotation", - type: "number-draggable", - step: 0.1, - decimals: 2, - unit: "deg", - propertyID: "materialMappingRot", - }, - { - label: "Material Repeat", - type: "bool", - propertyID: "materialRepeat", - } - ] - }, - { - id: "grid", - label: "GRID", - properties: [ - { - label: "Color", - type: "color", - propertyID: "gridColor", - propertyName: "color", // actual entity property name - }, - { - label: "Follow Camera", - type: "bool", - propertyID: "followCamera", - }, - { - label: "Major Grid Every", - type: "number-draggable", - min: 0, - step: 1, - decimals: 0, - propertyID: "majorGridEvery", - }, - { - label: "Minor Grid Every", - type: "number-draggable", - min: 0, - step: 0.01, - decimals: 2, - propertyID: "minorGridEvery", - } - ] - }, - { - id: "particles", - label: "PARTICLES", - properties: [ - { - label: "Emit", - type: "bool", - propertyID: "isEmitting", - }, - { - label: "Lifespan", - type: "number-draggable", - unit: "s", - step: 0.01, - decimals: 2, - propertyID: "lifespan", - }, - { - label: "Max Particles", - type: "number-draggable", - step: 1, - propertyID: "maxParticles", - }, - { - label: "Texture", - type: "texture", - propertyID: "particleTextures", - propertyName: "textures", // actual entity property name - } - ] - }, - { - id: "particles_emit", - label: "PARTICLES EMIT", - properties: [ - { - label: "Emit Rate", - type: "number-draggable", - step: 1, - propertyID: "emitRate", - }, - { - label: "Emit Speed", - type: "number-draggable", - step: 0.1, - decimals: 2, - propertyID: "emitSpeed", - }, - { - label: "Speed Spread", - type: "number-draggable", - step: 0.1, - decimals: 2, - propertyID: "speedSpread", - }, - { - label: "Shape Type", - type: "dropdown", - options: { "box": "Box", "ellipsoid": "Ellipsoid", - "cylinder-y": "Cylinder", "circle": "Circle", "plane": "Plane", - "compound": "Use Compound Shape URL" }, - propertyID: "particleShapeType", - propertyName: "shapeType", - }, - { - label: "Compound Shape URL", - type: "string", - propertyID: "particleCompoundShapeURL", - propertyName: "compoundShapeURL", - }, - { - label: "Emit Dimensions", - type: "vec3", - vec3Type: "xyz", - step: 0.01, - round: 100, - subLabels: [ "x", "y", "z" ], - propertyID: "emitDimensions", - }, - { - label: "Emit Radius Start", - type: "number-draggable", - step: 0.001, - decimals: 3, - propertyID: "emitRadiusStart" - }, - { - label: "Emit Orientation", - type: "vec3", - vec3Type: "pyr", - step: 0.01, - round: 100, - subLabels: [ "x", "y", "z" ], - unit: "deg", - propertyID: "emitOrientation", - }, - { - label: "Trails", - type: "bool", - propertyID: "emitterShouldTrail", - } - ] - }, - { - id: "particles_size", - label: "PARTICLES SIZE", - properties: [ - { - type: "triple", - label: "Size", - propertyID: "particleRadiusTriple", - properties: [ - { - label: "Start", - type: "number-draggable", - step: 0.01, - decimals: 2, - propertyID: "radiusStart", - fallbackProperty: "particleRadius", - }, - { - label: "Middle", - type: "number-draggable", - step: 0.01, - decimals: 2, - propertyID: "particleRadius", - }, - { - label: "Finish", - type: "number-draggable", - step: 0.01, - decimals: 2, - propertyID: "radiusFinish", - fallbackProperty: "particleRadius", - } - ] - }, - { - label: "Size Spread", - type: "number-draggable", - step: 0.01, - decimals: 2, - propertyID: "radiusSpread", - } - ] - }, - { - id: "particles_color", - label: "PARTICLES COLOR", - properties: [ - { - type: "triple", - label: "Color", - propertyID: "particleColorTriple", - properties: [ - { - label: "Start", - type: "color", - propertyID: "colorStart", - fallbackProperty: "color", - }, - { - label: "Middle", - type: "color", - propertyID: "particleColor", - propertyName: "color", // actual entity property name - }, - { - label: "Finish", - type: "color", - propertyID: "colorFinish", - fallbackProperty: "color", - } - ] - }, - { - label: "Color Spread", - type: "color", - propertyID: "colorSpread", - }, - { - type: "triple", - label: "Alpha", - propertyID: "particleAlphaTriple", - properties: [ - { - label: "Start", - type: "number-draggable", - step: 0.001, - decimals: 3, - propertyID: "alphaStart", - fallbackProperty: "alpha", - }, - { - label: "Middle", - type: "number-draggable", - step: 0.001, - decimals: 3, - propertyID: "alpha", - }, - { - label: "Finish", - type: "number-draggable", - step: 0.001, - decimals: 3, - propertyID: "alphaFinish", - fallbackProperty: "alpha", - } - ] - }, - { - label: "Alpha Spread", - type: "number-draggable", - step: 0.001, - decimals: 3, - propertyID: "alphaSpread", - } - ] - }, - { - id: "particles_behavior", - label: "PARTICLES BEHAVIOR", - properties: [ - { - label: "Emit Acceleration", - type: "vec3", - vec3Type: "xyz", - step: 0.01, - round: 100, - subLabels: [ "x", "y", "z" ], - propertyID: "emitAcceleration", - }, - { - label: "Acceleration Spread", - type: "vec3", - vec3Type: "xyz", - step: 0.01, - round: 100, - subLabels: [ "x", "y", "z" ], - propertyID: "accelerationSpread", - }, - { - type: "triple", - label: "Spin", - propertyID: "particleSpinTriple", - properties: [ - { - label: "Start", - type: "number-draggable", - step: 0.1, - decimals: 2, - multiplier: DEGREES_TO_RADIANS, - unit: "deg", - propertyID: "spinStart", - fallbackProperty: "particleSpin", - }, - { - label: "Middle", - type: "number-draggable", - step: 0.1, - decimals: 2, - multiplier: DEGREES_TO_RADIANS, - unit: "deg", - propertyID: "particleSpin", - }, - { - label: "Finish", - type: "number-draggable", - step: 0.1, - decimals: 2, - multiplier: DEGREES_TO_RADIANS, - unit: "deg", - propertyID: "spinFinish", - fallbackProperty: "particleSpin", - } - ] - }, - { - label: "Spin Spread", - type: "number-draggable", - step: 0.1, - decimals: 2, - multiplier: DEGREES_TO_RADIANS, - unit: "deg", - propertyID: "spinSpread", - }, - { - label: "Rotate with Entity", - type: "bool", - propertyID: "rotateWithEntity", - } - ] - }, - { - id: "particles_constraints", - label: "PARTICLES CONSTRAINTS", - properties: [ - { - type: "triple", - label: "Horizontal Angle", - propertyID: "particlePolarTriple", - properties: [ - { - label: "Start", - type: "number-draggable", - step: 0.1, - decimals: 2, - multiplier: DEGREES_TO_RADIANS, - unit: "deg", - propertyID: "polarStart", - }, - { - label: "Finish", - type: "number-draggable", - step: 0.1, - decimals: 2, - multiplier: DEGREES_TO_RADIANS, - unit: "deg", - propertyID: "polarFinish", - } - ], - }, - { - type: "triple", - label: "Vertical Angle", - propertyID: "particleAzimuthTriple", - properties: [ - { - label: "Start", - type: "number-draggable", - step: 0.1, - decimals: 2, - multiplier: DEGREES_TO_RADIANS, - unit: "deg", - propertyID: "azimuthStart", - }, - { - label: "Finish", - type: "number-draggable", - step: 0.1, - decimals: 2, - multiplier: DEGREES_TO_RADIANS, - unit: "deg", - propertyID: "azimuthFinish", - } - ] - } - ] - }, - { - id: "spatial", - label: "SPATIAL", - properties: [ - { - label: "Position", - type: "vec3", - vec3Type: "xyz", - step: 0.1, - decimals: 4, - subLabels: [ "x", "y", "z" ], - unit: "m", - propertyID: "position", - spaceMode: PROPERTY_SPACE_MODE.WORLD, - }, - { - label: "Local Position", - type: "vec3", - vec3Type: "xyz", - step: 0.1, - decimals: 4, - subLabels: [ "x", "y", "z" ], - unit: "m", - propertyID: "localPosition", - spaceMode: PROPERTY_SPACE_MODE.LOCAL, - }, - { - label: "Rotation", - type: "vec3", - vec3Type: "pyr", - step: 0.1, - decimals: 4, - subLabels: [ "x", "y", "z" ], - unit: "deg", - propertyID: "rotation", - spaceMode: PROPERTY_SPACE_MODE.WORLD, - }, - { - label: "Local Rotation", - type: "vec3", - vec3Type: "pyr", - step: 0.1, - decimals: 4, - subLabels: [ "x", "y", "z" ], - unit: "deg", - propertyID: "localRotation", - spaceMode: PROPERTY_SPACE_MODE.LOCAL, - }, - { - label: "Dimensions", - type: "vec3", - vec3Type: "xyz", - step: 0.01, - decimals: 4, - subLabels: [ "x", "y", "z" ], - unit: "m", - propertyID: "dimensions", - spaceMode: PROPERTY_SPACE_MODE.WORLD, - }, - { - label: "Local Dimensions", - type: "vec3", - vec3Type: "xyz", - step: 0.01, - decimals: 4, - subLabels: [ "x", "y", "z" ], - unit: "m", - propertyID: "localDimensions", - spaceMode: PROPERTY_SPACE_MODE.LOCAL, - }, - { - label: "Scale", - type: "number-draggable", - defaultValue: 100, - unit: "%", - buttons: [ { id: "rescale", label: "Rescale", className: "blue", onClick: rescaleDimensions }, - { id: "reset", label: "Reset Dimensions", className: "red", onClick: resetToNaturalDimensions } ], - propertyID: "scale", - }, - { - label: "Pivot", - type: "vec3", - vec3Type: "xyz", - step: 0.001, - decimals: 4, - subLabels: [ "x", "y", "z" ], - unit: "(ratio of dimension)", - propertyID: "registrationPoint", - }, - { - label: "Align", - type: "buttons", - buttons: [ { id: "selection", label: "Selection to Grid", className: "black", onClick: moveSelectionToGrid }, - { id: "all", label: "All to Grid", className: "black", onClick: moveAllToGrid } ], - propertyID: "alignToGrid", - } - ] - }, - { - id: "behavior", - label: "BEHAVIOR", - properties: [ - { - label: "Grabbable", - type: "bool", - propertyID: "grab.grabbable", - }, - { - label: "Cloneable", - type: "bool", - propertyID: "cloneable", - }, - { - label: "Clone Lifetime", - type: "number-draggable", - min: -1, - unit: "s", - propertyID: "cloneLifetime", - showPropertyRule: { "cloneable": "true" }, - }, - { - label: "Clone Limit", - type: "number-draggable", - min: 0, - propertyID: "cloneLimit", - showPropertyRule: { "cloneable": "true" }, - }, - { - label: "Clone Dynamic", - type: "bool", - propertyID: "cloneDynamic", - showPropertyRule: { "cloneable": "true" }, - }, - { - label: "Clone Avatar Entity", - type: "bool", - propertyID: "cloneAvatarEntity", - showPropertyRule: { "cloneable": "true" }, - }, - { - label: "Triggerable", - type: "bool", - propertyID: "grab.triggerable", - }, - { - label: "Follow Controller", - type: "bool", - propertyID: "grab.grabFollowsController", - }, - { - label: "Cast Shadows", - type: "bool", - propertyID: "canCastShadow", - }, - { - label: "Link", - type: "string", - propertyID: "href", - placeholder: "URL", - }, - { - label: "Ignore Pick Intersection", - type: "bool", - propertyID: "ignorePickIntersection", - }, - { - label: "Lifetime", - type: "number", - unit: "s", - propertyID: "lifetime", - } - ] - }, - { - id: "scripts", - label: "SCRIPTS", - properties: [ - { - label: "Script", - type: "string", - buttons: [ { id: "reload", label: "F", className: "glyph", onClick: reloadScripts } ], - propertyID: "script", - placeholder: "URL", - hideIfCertified: true, - }, - { - label: "Server Script", - type: "string", - buttons: [ { id: "reload", label: "F", className: "glyph", onClick: reloadServerScripts } ], - propertyID: "serverScripts", - placeholder: "URL", - }, - { - label: "Server Script Status", - type: "placeholder", - indentedLabel: true, - propertyID: "serverScriptStatus", - selectionVisibility: PROPERTY_SELECTION_VISIBILITY.SINGLE_SELECTION, - }, - { - label: "User Data", - type: "textarea", - buttons: [ { id: "clear", label: "Clear User Data", className: "red", onClick: clearUserData }, - { id: "edit", label: "Edit as JSON", className: "blue", onClick: newJSONEditor }, - { id: "save", label: "Save User Data", className: "black", onClick: saveUserData } ], - propertyID: "userData", - } - ] - }, - { - id: "collision", - label: "COLLISION", - properties: [ - { - label: "Collides", - type: "bool", - inverse: true, - propertyID: "collisionless", - }, - { - label: "Static Entities", - type: "bool", - propertyID: "collidesWithStatic", - propertyName: "static", // actual subProperty name - subPropertyOf: "collidesWith", - showPropertyRule: { "collisionless": "false" }, - }, - { - label: "Kinematic Entities", - type: "bool", - propertyID: "collidesWithKinematic", - propertyName: "kinematic", // actual subProperty name - subPropertyOf: "collidesWith", - showPropertyRule: { "collisionless": "false" }, - }, - { - label: "Dynamic Entities", - type: "bool", - propertyID: "collidesWithDynamic", - propertyName: "dynamic", // actual subProperty name - subPropertyOf: "collidesWith", - showPropertyRule: { "collisionless": "false" }, - }, - { - label: "My Avatar", - type: "bool", - propertyID: "collidesWithMyAvatar", - propertyName: "myAvatar", // actual subProperty name - subPropertyOf: "collidesWith", - showPropertyRule: { "collisionless": "false" }, - }, - { - label: "Other Avatars", - type: "bool", - propertyID: "collidesWithOtherAvatar", - propertyName: "otherAvatar", // actual subProperty name - subPropertyOf: "collidesWith", - showPropertyRule: { "collisionless": "false" }, - }, - { - label: "Collision Sound", - type: "string", - placeholder: "URL", - propertyID: "collisionSoundURL", - showPropertyRule: { "collisionless": "false" }, - hideIfCertified: true, - }, - { - label: "Dynamic", - type: "bool", - propertyID: "dynamic", - } - ] - }, - { - id: "physics", - label: "PHYSICS", - properties: [ - { - label: "Linear Velocity", - type: "vec3", - vec3Type: "xyz", - step: 0.01, - decimals: 4, - subLabels: [ "x", "y", "z" ], - unit: "m/s", - propertyID: "localVelocity", - }, - { - label: "Linear Damping", - type: "number-draggable", - min: 0, - max: 1, - step: 0.001, - decimals: 4, - propertyID: "damping", - }, - { - label: "Angular Velocity", - type: "vec3", - vec3Type: "pyr", - multiplier: DEGREES_TO_RADIANS, - decimals: 4, - subLabels: [ "x", "y", "z" ], - unit: "deg/s", - propertyID: "localAngularVelocity", - }, - { - label: "Angular Damping", - type: "number-draggable", - min: 0, - max: 1, - step: 0.001, - decimals: 4, - propertyID: "angularDamping", - }, - { - label: "Bounciness", - type: "number-draggable", - step: 0.001, - decimals: 4, - propertyID: "restitution", - }, - { - label: "Friction", - type: "number-draggable", - step: 0.01, - decimals: 4, - propertyID: "friction", - }, - { - label: "Density", - type: "number-draggable", - step: 1, - decimals: 4, - propertyID: "density", - }, - { - label: "Gravity", - type: "vec3", - vec3Type: "xyz", - subLabels: [ "x", "y", "z" ], - step: 0.1, - decimals: 4, - unit: "m/s2", - propertyID: "gravity", - }, - { - label: "Acceleration", - type: "vec3", - vec3Type: "xyz", - subLabels: [ "x", "y", "z" ], - step: 0.1, - decimals: 4, - unit: "m/s2", - propertyID: "acceleration", - } - ] - }, -]; - -const GROUPS_PER_TYPE = { - None: [ 'base', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], - Shape: [ 'base', 'shape', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], - Text: [ 'base', 'text', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], - Zone: [ 'base', 'zone', 'zone_key_light', 'zone_skybox', 'zone_ambient_light', 'zone_haze', - 'zone_bloom', 'zone_avatar_priority', 'spatial', 'behavior', 'scripts', 'physics' ], - Model: [ 'base', 'model', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], - Image: [ 'base', 'image', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], - Web: [ 'base', 'web', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], - Light: [ 'base', 'light', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], - Material: [ 'base', 'material', 'spatial', 'behavior', 'scripts', 'physics' ], - ParticleEffect: [ 'base', 'particles', 'particles_emit', 'particles_size', 'particles_color', - 'particles_behavior', 'particles_constraints', 'spatial', 'behavior', 'scripts', 'physics' ], - PolyLine: [ 'base', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], - PolyVox: [ 'base', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], - Grid: [ 'base', 'grid', 'spatial', 'behavior', 'scripts', 'physics' ], - Multiple: [ 'base', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ], -}; - -const EDITOR_TIMEOUT_DURATION = 1500; -const DEBOUNCE_TIMEOUT = 125; - -const COLOR_MIN = 0; -const COLOR_MAX = 255; -const COLOR_STEP = 1; - -const MATERIAL_PREFIX_STRING = "mat::"; - -const PENDING_SCRIPT_STATUS = "[ Fetching status ]"; -const NOT_RUNNING_SCRIPT_STATUS = "Not running"; -const ENTITY_SCRIPT_STATUS = { - pending: "Pending", - loading: "Loading", - error_loading_script: "Error loading script", // eslint-disable-line camelcase - error_running_script: "Error running script", // eslint-disable-line camelcase - running: "Running", - unloaded: "Unloaded" -}; - -const ENABLE_DISABLE_SELECTOR = "input, textarea, span, .dropdown dl, .color-picker"; - -const PROPERTY_NAME_DIVISION = { - GROUP: 0, - PROPERTY: 1, - SUB_PROPERTY: 2, -}; - -const RECT_ELEMENTS = { - X_NUMBER: 0, - Y_NUMBER: 1, - WIDTH_NUMBER: 2, - HEIGHT_NUMBER: 3, -}; - -const VECTOR_ELEMENTS = { - X_NUMBER: 0, - Y_NUMBER: 1, - Z_NUMBER: 2, -}; - -const COLOR_ELEMENTS = { - COLOR_PICKER: 0, - RED_NUMBER: 1, - GREEN_NUMBER: 2, - BLUE_NUMBER: 3, -}; - -const TEXTURE_ELEMENTS = { - IMAGE: 0, - TEXT_INPUT: 1, -}; - -const JSON_EDITOR_ROW_DIV_INDEX = 2; - -let elGroups = {}; -let properties = {}; -let propertyRangeRequests = []; -let colorPickers = {}; -let particlePropertyUpdates = {}; -let selectedEntityIDs = new Set(); -let currentSelections = []; -let createAppTooltip = new CreateAppTooltip(); -let currentSpaceMode = PROPERTY_SPACE_MODE.LOCAL; -let zonesList = []; - -function createElementFromHTML(htmlString) { - let elTemplate = document.createElement('template'); - elTemplate.innerHTML = htmlString.trim(); - return elTemplate.content.firstChild; -} - -function isFlagSet(value, flag) { - return (value & flag) === flag; -} - -/** - * GENERAL PROPERTY/GROUP FUNCTIONS - */ - -function getPropertyInputElement(propertyID) { - let property = properties[propertyID]; - switch (property.data.type) { - case 'string': - case 'number': - case 'bool': - case 'dropdown': - case 'textarea': - case 'texture': - return property.elInput; - case 'multipleZonesSelection': - return property.elInput; - case 'number-draggable': - return property.elNumber.elInput; - case 'rect': - return { - x: property.elNumberX.elInput, - y: property.elNumberY.elInput, - width: property.elNumberWidth.elInput, - height: property.elNumberHeight.elInput - }; - case 'vec3': - case 'vec2': - return { x: property.elNumberX.elInput, y: property.elNumberY.elInput, z: property.elNumberZ.elInput }; - case 'color': - return { red: property.elNumberR.elInput, green: property.elNumberG.elInput, blue: property.elNumberB.elInput }; - case 'icon': - return property.elLabel; - case 'dynamic-multiselect': - return property.elDivOptions; - default: - return undefined; - } -} - -function enableChildren(el, selector) { - let elSelectors = el.querySelectorAll(selector); - for (let selectorIndex = 0; selectorIndex < elSelectors.length; ++selectorIndex) { - elSelectors[selectorIndex].removeAttribute('disabled'); - } -} - -function disableChildren(el, selector) { - let elSelectors = el.querySelectorAll(selector); - for (let selectorIndex = 0; selectorIndex < elSelectors.length; ++selectorIndex) { - elSelectors[selectorIndex].setAttribute('disabled', 'disabled'); - } -} - -function enableProperties() { - enableChildren(document.getElementById("properties-list"), ENABLE_DISABLE_SELECTOR); - enableChildren(document, ".colpick"); - enableAllMultipleZoneSelector(); -} - -function disableProperties() { - disableChildren(document.getElementById("properties-list"), ENABLE_DISABLE_SELECTOR); - disableChildren(document, ".colpick"); - for (let pickKey in colorPickers) { - colorPickers[pickKey].colpickHide(); - } - disableAllMultipleZoneSelector(); -} - -function showPropertyElement(propertyID, show) { - setPropertyVisibility(properties[propertyID], show); -} - -function setPropertyVisibility(property, visible) { - property.elContainer.style.display = visible ? null : "none"; -} - -function resetProperties() { - for (let propertyID in properties) { - let property = properties[propertyID]; - let propertyData = property.data; - - switch (propertyData.type) { - case 'number': - case 'string': { - property.elInput.classList.remove('multi-diff'); - if (propertyData.defaultValue !== undefined) { - property.elInput.value = propertyData.defaultValue; - } else { - property.elInput.value = ""; - } - break; - } - case 'bool': { - property.elInput.classList.remove('multi-diff'); - property.elInput.checked = false; - break; - } - case 'number-draggable': { - if (propertyData.defaultValue !== undefined) { - property.elNumber.setValue(propertyData.defaultValue, false); - } else { - property.elNumber.setValue("", false); - } - break; - } - case 'rect': { - property.elNumberX.setValue("", false); - property.elNumberY.setValue("", false); - property.elNumberWidth.setValue("", false); - property.elNumberHeight.setValue("", false); - break; - } - case 'vec3': - case 'vec2': { - property.elNumberX.setValue("", false); - property.elNumberY.setValue("", false); - if (property.elNumberZ !== undefined) { - property.elNumberZ.setValue("", false); - } - break; - } - case 'color': { - property.elColorPicker.style.backgroundColor = "rgb(" + 0 + "," + 0 + "," + 0 + ")"; - property.elNumberR.setValue("", false); - property.elNumberG.setValue("", false); - property.elNumberB.setValue("", false); - break; - } - case 'dropdown': { - property.elInput.classList.remove('multi-diff'); - property.elInput.value = ""; - setDropdownText(property.elInput); - break; - } - case 'textarea': { - property.elInput.classList.remove('multi-diff'); - property.elInput.value = ""; - setTextareaScrolling(property.elInput); - break; - } - case 'multipleZonesSelection': { - property.elInput.classList.remove('multi-diff'); - property.elInput.value = "[]"; - setZonesSelectionData(property.elInput, false); - break; - } - case 'icon': { - property.elSpan.style.display = "none"; - break; - } - case 'texture': { - property.elInput.classList.remove('multi-diff'); - property.elInput.value = ""; - property.elInput.imageLoad(property.elInput.value); - break; - } - case 'dynamic-multiselect': { - resetDynamicMultiselectProperty(property.elDivOptions); - break; - } - } - - let showPropertyRules = properties[propertyID].showPropertyRules; - if (showPropertyRules !== undefined) { - for (let propertyToHide in showPropertyRules) { - showPropertyElement(propertyToHide, false); - } - } - } - - resetServerScriptStatus(); -} - -function resetServerScriptStatus() { - let elServerScriptError = document.getElementById("property-serverScripts-error"); - let elServerScriptStatus = document.getElementById("property-serverScripts-status"); - elServerScriptError.parentElement.style.display = "none"; - elServerScriptStatus.innerText = NOT_RUNNING_SCRIPT_STATUS; -} - -function showGroupsForType(type) { - if (type === "Box" || type === "Sphere") { - showGroupsForTypes(["Shape"]); - showOnTheSamePage(["Shape"]); - return; - } - if (type === "None") { - showGroupsForTypes(["None"]); - return; - } - showGroupsForTypes([type]); - showOnTheSamePage([type]); -} - -function getGroupsForTypes(types) { - return Object.keys(elGroups).filter((groupKey) => { - return types.map(type => GROUPS_PER_TYPE[type].includes(groupKey)).every(function (hasGroup) { - return hasGroup; - }); - }); -} - -function showGroupsForTypes(types) { - Object.entries(elGroups).forEach(([groupKey, elGroup]) => { - if (types.map(type => GROUPS_PER_TYPE[type].includes(groupKey)).every(function (hasGroup) { return hasGroup; })) { - elGroup.style.display = "none"; - if (types !== "None") { - document.getElementById("tab-" + groupKey).style.display = "block"; - } else { - document.getElementById("tab-" + groupKey).style.display = "none"; - } - } else { - elGroup.style.display = "none"; - document.getElementById("tab-" + groupKey).style.display = "none"; - } - }); -} - -function getFirstSelectedID() { - if (selectedEntityIDs.size === 0) { - return null; - } - return selectedEntityIDs.values().next().value; -} - -/** - * Returns true when the user is currently dragging the numeric slider control of the property - * @param propertyName - name of property - * @returns {boolean} currentlyDragging - */ -function isCurrentlyDraggingProperty(propertyName) { - return properties[propertyName] && properties[propertyName].dragging === true; -} - -const SUPPORTED_FALLBACK_TYPES = ['number', 'number-draggable', 'rect', 'vec3', 'vec2', 'color']; - -function getMultiplePropertyValue(originalPropertyName) { - // if this is a compound property name (i.e. animation.running) - // then split it by . up to 3 times to find property value - - let propertyData = null; - if (properties[originalPropertyName] !== undefined) { - propertyData = properties[originalPropertyName].data; - } - - let propertyValues = []; - let splitPropertyName = originalPropertyName.split('.'); - if (splitPropertyName.length > 1) { - let propertyGroupName = splitPropertyName[PROPERTY_NAME_DIVISION.GROUP]; - let propertyName = splitPropertyName[PROPERTY_NAME_DIVISION.PROPERTY]; - propertyValues = currentSelections.map(selection => { - let groupProperties = selection.properties[propertyGroupName]; - if (groupProperties === undefined || groupProperties[propertyName] === undefined) { - return undefined; - } - if (splitPropertyName.length === PROPERTY_NAME_DIVISION.SUB_PROPERTY + 1) { - let subPropertyName = splitPropertyName[PROPERTY_NAME_DIVISION.SUB_PROPERTY]; - return groupProperties[propertyName][subPropertyName]; - } else { - return groupProperties[propertyName]; - } - }); - } else { - propertyValues = currentSelections.map(selection => selection.properties[originalPropertyName]); - } - - if (propertyData !== null && propertyData.fallbackProperty !== undefined && - SUPPORTED_FALLBACK_TYPES.includes(propertyData.type)) { - - let fallbackMultiValue = null; - - for (let i = 0; i < propertyValues.length; ++i) { - let isPropertyNotNumber = false; - let propertyValue = propertyValues[i]; - if (propertyValue === undefined) { - continue; - } - switch (propertyData.type) { - case 'number': - case 'number-draggable': - isPropertyNotNumber = isNaN(propertyValue) || propertyValue === null; - break; - case 'rect': - case 'vec3': - case 'vec2': - isPropertyNotNumber = isNaN(propertyValue.x) || propertyValue.x === null; - break; - case 'color': - isPropertyNotNumber = isNaN(propertyValue.red) || propertyValue.red === null; - break; - } - if (isPropertyNotNumber) { - if (fallbackMultiValue === null) { - fallbackMultiValue = getMultiplePropertyValue(propertyData.fallbackProperty); - } - propertyValues[i] = fallbackMultiValue.values[i]; - } - } - } - - const firstValue = propertyValues[0]; - const isMultiDiffValue = !propertyValues.every((x) => deepEqual(firstValue, x)); - - if (isMultiDiffValue) { - return { - value: undefined, - values: propertyValues, - isMultiDiffValue: true - } - } - - return { - value: propertyValues[0], - values: propertyValues, - isMultiDiffValue: false - }; -} - -/** - * Retrieve more detailed info for differing Numeric MultiplePropertyValue - * @param multiplePropertyValue - input multiplePropertyValue - * @param propertyData - * @returns {{keys: *[], propertyComponentDiff, averagePerPropertyComponent}} - */ -function getDetailedNumberMPVDiff(multiplePropertyValue, propertyData) { - let detailedValues = {}; - // Fixed numbers can't be easily averaged since they're strings, so lets keep an array of unmodified numbers - let unmodifiedValues = {}; - const DEFAULT_KEY = 0; - let uniqueKeys = new Set([]); - multiplePropertyValue.values.forEach(function(propertyValue) { - if (typeof propertyValue === "object") { - Object.entries(propertyValue).forEach(function([key, value]) { - if (!uniqueKeys.has(key)) { - uniqueKeys.add(key); - detailedValues[key] = []; - unmodifiedValues[key] = []; - } - detailedValues[key].push(applyInputNumberPropertyModifiers(value, propertyData)); - unmodifiedValues[key].push(value); - }); - } else { - if (!uniqueKeys.has(DEFAULT_KEY)) { - uniqueKeys.add(DEFAULT_KEY); - detailedValues[DEFAULT_KEY] = []; - unmodifiedValues[DEFAULT_KEY] = []; - } - detailedValues[DEFAULT_KEY].push(applyInputNumberPropertyModifiers(propertyValue, propertyData)); - unmodifiedValues[DEFAULT_KEY].push(propertyValue); - } - }); - let keys = [...uniqueKeys]; - - let propertyComponentDiff = {}; - Object.entries(detailedValues).forEach(function([key, value]) { - propertyComponentDiff[key] = [...new Set(value)].length > 1; - }); - - let averagePerPropertyComponent = {}; - Object.entries(unmodifiedValues).forEach(function([key, value]) { - let average = value.reduce((a, b) => a + b) / value.length; - averagePerPropertyComponent[key] = applyInputNumberPropertyModifiers(average, propertyData); - }); - - return { - keys, - propertyComponentDiff, - averagePerPropertyComponent, - }; -} - -function getDetailedSubPropertyMPVDiff(multiplePropertyValue, subPropertyName) { - let isChecked = false; - let checkedValues = multiplePropertyValue.values.map((value) => value.split(",").includes(subPropertyName)); - let isMultiDiff = !checkedValues.every(value => value === checkedValues[0]); - if (!isMultiDiff) { - isChecked = checkedValues[0]; - } - return { - isChecked, - isMultiDiff - } -} - -function updateVisibleSpaceModeProperties() { - for (let propertyID in properties) { - if (properties.hasOwnProperty(propertyID)) { - let property = properties[propertyID]; - let propertySpaceMode = property.spaceMode; - let elProperty = properties[propertyID].elContainer; - if (propertySpaceMode !== PROPERTY_SPACE_MODE.ALL && propertySpaceMode !== currentSpaceMode) { - elProperty.classList.add('spacemode-hidden'); - } else { - elProperty.classList.remove('spacemode-hidden'); - } - } - } -} - -/** - * PROPERTY UPDATE FUNCTIONS - */ - -function createPropertyUpdateObject(originalPropertyName, propertyValue) { - let propertyUpdate = {}; - // if this is a compound property name (i.e. animation.running) then split it by . up to 3 times - let splitPropertyName = originalPropertyName.split('.'); - if (splitPropertyName.length > 1) { - let propertyGroupName = splitPropertyName[PROPERTY_NAME_DIVISION.GROUP]; - let propertyName = splitPropertyName[PROPERTY_NAME_DIVISION.PROPERTY]; - propertyUpdate[propertyGroupName] = {}; - if (splitPropertyName.length === PROPERTY_NAME_DIVISION.SUB_PROPERTY + 1) { - let subPropertyName = splitPropertyName[PROPERTY_NAME_DIVISION.SUB_PROPERTY]; - propertyUpdate[propertyGroupName][propertyName] = {}; - propertyUpdate[propertyGroupName][propertyName][subPropertyName] = propertyValue; - } else { - propertyUpdate[propertyGroupName][propertyName] = propertyValue; - } - } else { - propertyUpdate[originalPropertyName] = propertyValue; - } - return propertyUpdate; -} - -function updateProperty(originalPropertyName, propertyValue, isParticleProperty) { - let propertyUpdate = createPropertyUpdateObject(originalPropertyName, propertyValue); - - // queue up particle property changes with the debounced sync to avoid - // causing particle emitting to reset excessively with each value change - if (isParticleProperty) { - Object.keys(propertyUpdate).forEach(function (propertyUpdateKey) { - particlePropertyUpdates[propertyUpdateKey] = propertyUpdate[propertyUpdateKey]; - }); - particleSyncDebounce(); - } else { - // only update the entity property value itself if in the middle of dragging - // prevent undo command push, saving new property values, and property update - // callback until drag is complete (additional update sent via dragEnd callback) - let onlyUpdateEntity = isCurrentlyDraggingProperty(originalPropertyName); - updateProperties(propertyUpdate, onlyUpdateEntity); - } -} - -let particleSyncDebounce = _.debounce(function () { - updateProperties(particlePropertyUpdates); - particlePropertyUpdates = {}; -}, DEBOUNCE_TIMEOUT); - -function updateProperties(propertiesToUpdate, onlyUpdateEntity) { - if (onlyUpdateEntity === undefined) { - onlyUpdateEntity = false; - } - EventBridge.emitWebEvent(JSON.stringify({ - ids: [...selectedEntityIDs], - type: "update", - properties: propertiesToUpdate, - onlyUpdateEntities: onlyUpdateEntity - })); -} - -function updateMultiDiffProperties(propertiesMapToUpdate, onlyUpdateEntity) { - if (onlyUpdateEntity === undefined) { - onlyUpdateEntity = false; - } - EventBridge.emitWebEvent(JSON.stringify({ - type: "update", - propertiesMap: propertiesMapToUpdate, - onlyUpdateEntities: onlyUpdateEntity - })); -} - -function createEmitTextPropertyUpdateFunction(property) { - return function() { - property.elInput.classList.remove('multi-diff'); - updateProperty(property.name, this.value, property.isParticleProperty); - }; -} - -function createEmitCheckedPropertyUpdateFunction(property) { - return function() { - updateProperty(property.name, property.data.inverse ? !this.checked : this.checked, property.isParticleProperty); - }; -} - -function createDragStartFunction(property) { - return function() { - property.dragging = true; - }; -} - -function createDragEndFunction(property) { - return function() { - property.dragging = false; - - if (this.multiDiffModeEnabled) { - let propertyMultiValue = getMultiplePropertyValue(property.name); - let updateObjects = []; - const selectedEntityIDsArray = [...selectedEntityIDs]; - - for (let i = 0; i < selectedEntityIDsArray.length; ++i) { - let entityID = selectedEntityIDsArray[i]; - updateObjects.push({ - entityIDs: [entityID], - properties: createPropertyUpdateObject(property.name, propertyMultiValue.values[i]), - }); - } - - // send a full updateMultiDiff post-dragging to count as an action in the undo stack - updateMultiDiffProperties(updateObjects); - } else { - // send an additional update post-dragging to consider whole property change from dragStart to dragEnd to be 1 action - this.valueChangeFunction(); - } - }; -} - -function createEmitNumberPropertyUpdateFunction(property) { - return function() { - let value = parseFloat(applyOutputNumberPropertyModifiers(parseFloat(this.value), property.data)); - updateProperty(property.name, value, property.isParticleProperty); - }; -} - -function createEmitNumberPropertyComponentUpdateFunction(property, propertyComponent) { - return function() { - let propertyMultiValue = getMultiplePropertyValue(property.name); - let value = parseFloat(applyOutputNumberPropertyModifiers(parseFloat(this.value), property.data)); - - if (propertyMultiValue.isMultiDiffValue) { - let updateObjects = []; - const selectedEntityIDsArray = [...selectedEntityIDs]; - - for (let i = 0; i < selectedEntityIDsArray.length; ++i) { - let entityID = selectedEntityIDsArray[i]; - - let propertyObject = propertyMultiValue.values[i]; - propertyObject[propertyComponent] = value; - - let updateObject = createPropertyUpdateObject(property.name, propertyObject); - updateObjects.push({ - entityIDs: [entityID], - properties: updateObject, - }); - - mergeDeep(currentSelections[i].properties, updateObject); - } - - // only update the entity property value itself if in the middle of dragging - // prevent undo command push, saving new property values, and property update - // callback until drag is complete (additional update sent via dragEnd callback) - let onlyUpdateEntity = isCurrentlyDraggingProperty(property.name); - updateMultiDiffProperties(updateObjects, onlyUpdateEntity); - } else { - let propertyValue = propertyMultiValue.value; - propertyValue[propertyComponent] = value; - updateProperty(property.name, propertyValue, property.isParticleProperty); - } - }; -} - -function createEmitColorPropertyUpdateFunction(property) { - return function() { - emitColorPropertyUpdate(property.name, property.elNumberR.elInput.value, property.elNumberG.elInput.value, - property.elNumberB.elInput.value, property.isParticleProperty); - }; -} - -function emitColorPropertyUpdate(propertyName, red, green, blue, isParticleProperty) { - let newValue = { - red: red, - green: green, - blue: blue - }; - updateProperty(propertyName, newValue, isParticleProperty); -} - -function toggleBooleanCSV(inputCSV, property, enable) { - let values = inputCSV.split(","); - if (enable && !values.includes(property)) { - values.push(property); - } else if (!enable && values.includes(property)) { - values = values.filter(value => value !== property); - } - return values.join(","); -} - -function updateCheckedSubProperty(propertyName, propertyMultiValue, subPropertyElement, subPropertyString, isParticleProperty) { - if (propertyMultiValue.isMultiDiffValue) { - let updateObjects = []; - const selectedEntityIDsArray = [...selectedEntityIDs]; - - for (let i = 0; i < selectedEntityIDsArray.length; ++i) { - let newValue = toggleBooleanCSV(propertyMultiValue.values[i], subPropertyString, subPropertyElement.checked); - updateObjects.push({ - entityIDs: [selectedEntityIDsArray[i]], - properties: createPropertyUpdateObject(propertyName, newValue), - }); - } - - updateMultiDiffProperties(updateObjects); - } else { - updateProperty(propertyName, toggleBooleanCSV(propertyMultiValue.value, subPropertyString, subPropertyElement.checked), - isParticleProperty); - } -} - -/** - * PROPERTY ELEMENT CREATION FUNCTIONS - */ - -function createStringProperty(property, elProperty) { - let elementID = property.elementID; - let propertyData = property.data; - - elProperty.className = "text"; - - let elInput = createElementFromHTML(` - - `); - - - elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(property)); - if (propertyData.onChange !== undefined) { - elInput.addEventListener('change', propertyData.onChange); - } - - - let elMultiDiff = document.createElement('span'); - elMultiDiff.className = "multi-diff"; - - elProperty.appendChild(elInput); - elProperty.appendChild(elMultiDiff); - - if (propertyData.buttons !== undefined) { - addButtons(elProperty, elementID, propertyData.buttons, false); - } - - return elInput; -} - -function createBoolProperty(property, elProperty) { - let propertyName = property.name; - let elementID = property.elementID; - let propertyData = property.data; - - elProperty.className = "checkbox"; - - if (propertyData.glyph !== undefined) { - let elSpan = document.createElement('span'); - elSpan.innerHTML = propertyData.glyph; - elSpan.className = 'icon'; - elProperty.appendChild(elSpan); - } - - let elInput = document.createElement('input'); - elInput.setAttribute("id", elementID); - elInput.setAttribute("type", "checkbox"); - - elProperty.appendChild(elInput); - elProperty.appendChild(createElementFromHTML(``)); - - let subPropertyOf = propertyData.subPropertyOf; - if (subPropertyOf !== undefined) { - elInput.addEventListener('change', function() { - let subPropertyMultiValue = getMultiplePropertyValue(subPropertyOf); - - updateCheckedSubProperty(subPropertyOf, - subPropertyMultiValue, - elInput, propertyName, property.isParticleProperty); - }); - } else { - elInput.addEventListener('change', createEmitCheckedPropertyUpdateFunction(property)); - } - - return elInput; -} - -function createNumberProperty(property, elProperty) { - let elementID = property.elementID; - let propertyData = property.data; - - elProperty.className = "text"; - - let elInput = createElementFromHTML(` - - `); - - if (propertyData.min !== undefined) { - elInput.setAttribute("min", propertyData.min); - } - if (propertyData.max !== undefined) { - elInput.setAttribute("max", propertyData.max); - } - if (propertyData.step !== undefined) { - elInput.setAttribute("step", propertyData.step); - } - if (propertyData.defaultValue !== undefined) { - elInput.value = propertyData.defaultValue; - } - - elInput.addEventListener('change', createEmitNumberPropertyUpdateFunction(property)); - - let elMultiDiff = document.createElement('span'); - elMultiDiff.className = "multi-diff"; - - elProperty.appendChild(elInput); - elProperty.appendChild(elMultiDiff); - - if (propertyData.buttons !== undefined) { - addButtons(elProperty, elementID, propertyData.buttons, false); - } - - return elInput; -} - -function updateNumberMinMax(property) { - let elInput = property.elInput; - let min = property.data.min; - let max = property.data.max; - if (min !== undefined) { - elInput.setAttribute("min", min); - } - if (max !== undefined) { - elInput.setAttribute("max", max); - } -} - -/** - * - * @param {object} property - property update on step - * @param {string} [propertyComponent] - propertyComponent to update on step (e.g. enter 'x' to just update position.x) - * @returns {Function} - */ -function createMultiDiffStepFunction(property, propertyComponent) { - return function(step, shouldAddToUndoHistory) { - if (shouldAddToUndoHistory === undefined) { - shouldAddToUndoHistory = false; - } - - let propertyMultiValue = getMultiplePropertyValue(property.name); - if (!propertyMultiValue.isMultiDiffValue) { - console.log("setMultiDiffStepFunction is only supposed to be called in MultiDiff mode."); - return; - } - - let multiplier = property.data.multiplier !== undefined ? property.data.multiplier : 1; - - let applyDelta = step * multiplier; - - if (selectedEntityIDs.size !== propertyMultiValue.values.length) { - console.log("selectedEntityIDs and propertyMultiValue got out of sync."); - return; - } - let updateObjects = []; - const selectedEntityIDsArray = [...selectedEntityIDs]; - - for (let i = 0; i < selectedEntityIDsArray.length; ++i) { - let entityID = selectedEntityIDsArray[i]; - - let updatedValue; - if (propertyComponent !== undefined) { - let objectToUpdate = propertyMultiValue.values[i]; - objectToUpdate[propertyComponent] += applyDelta; - updatedValue = objectToUpdate; - } else { - updatedValue = propertyMultiValue.values[i] + applyDelta; - } - let propertiesUpdate = createPropertyUpdateObject(property.name, updatedValue); - updateObjects.push({ - entityIDs: [entityID], - properties: propertiesUpdate - }); - // We need to store these so that we can send a full update on the dragEnd - mergeDeep(currentSelections[i].properties, propertiesUpdate); - } - - updateMultiDiffProperties(updateObjects, !shouldAddToUndoHistory); - } -} - -function createNumberDraggableProperty(property, elProperty) { - let elementID = property.elementID; - let propertyData = property.data; - - elProperty.className += " draggable-number-container"; - - let dragStartFunction = createDragStartFunction(property); - let dragEndFunction = createDragEndFunction(property); - let elDraggableNumber = new DraggableNumber(propertyData.min, propertyData.max, propertyData.step, - propertyData.decimals, dragStartFunction, dragEndFunction); - - let defaultValue = propertyData.defaultValue; - if (defaultValue !== undefined) { - elDraggableNumber.elInput.value = defaultValue; - } - - let valueChangeFunction = createEmitNumberPropertyUpdateFunction(property); - elDraggableNumber.setValueChangeFunction(valueChangeFunction); - - elDraggableNumber.setMultiDiffStepFunction(createMultiDiffStepFunction(property)); - - elDraggableNumber.elInput.setAttribute("id", elementID); - elProperty.appendChild(elDraggableNumber.elDiv); - - if (propertyData.buttons !== undefined) { - addButtons(elDraggableNumber.elDiv, elementID, propertyData.buttons, false); - } - - return elDraggableNumber; -} - -function updateNumberDraggableMinMax(property) { - let propertyData = property.data; - property.elNumber.updateMinMax(propertyData.min, propertyData.max); -} - -function createRectProperty(property, elProperty) { - let propertyData = property.data; - - elProperty.className = "rect"; - - let elXYRow = document.createElement('div'); - elXYRow.className = "rect-row fstuple"; - elProperty.appendChild(elXYRow); - - let elWidthHeightRow = document.createElement('div'); - elWidthHeightRow.className = "rect-row fstuple"; - elProperty.appendChild(elWidthHeightRow); - - - let elNumberX = createTupleNumberInput(property, propertyData.subLabels[RECT_ELEMENTS.X_NUMBER]); - let elNumberY = createTupleNumberInput(property, propertyData.subLabels[RECT_ELEMENTS.Y_NUMBER]); - let elNumberWidth = createTupleNumberInput(property, propertyData.subLabels[RECT_ELEMENTS.WIDTH_NUMBER]); - let elNumberHeight = createTupleNumberInput(property, propertyData.subLabels[RECT_ELEMENTS.HEIGHT_NUMBER]); - - elXYRow.appendChild(elNumberX.elDiv); - elXYRow.appendChild(elNumberY.elDiv); - elWidthHeightRow.appendChild(elNumberWidth.elDiv); - elWidthHeightRow.appendChild(elNumberHeight.elDiv); - - elNumberX.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'x')); - elNumberY.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'y')); - elNumberWidth.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'width')); - elNumberHeight.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'height')); - - elNumberX.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'x')); - elNumberY.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'y')); - elNumberX.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'width')); - elNumberY.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'height')); - - let elResult = []; - elResult[RECT_ELEMENTS.X_NUMBER] = elNumberX; - elResult[RECT_ELEMENTS.Y_NUMBER] = elNumberY; - elResult[RECT_ELEMENTS.WIDTH_NUMBER] = elNumberWidth; - elResult[RECT_ELEMENTS.HEIGHT_NUMBER] = elNumberHeight; - return elResult; -} - -function updateRectMinMax(property) { - let min = property.data.min; - let max = property.data.max; - property.elNumberX.updateMinMax(min, max); - property.elNumberY.updateMinMax(min, max); - property.elNumberWidth.updateMinMax(min, max); - property.elNumberHeight.updateMinMax(min, max); -} - -function createVec3Property(property, elProperty) { - let propertyData = property.data; - - elProperty.className = propertyData.vec3Type + " fstuple"; - - let elNumberX = createTupleNumberInput(property, propertyData.subLabels[VECTOR_ELEMENTS.X_NUMBER]); - let elNumberY = createTupleNumberInput(property, propertyData.subLabels[VECTOR_ELEMENTS.Y_NUMBER]); - let elNumberZ = createTupleNumberInput(property, propertyData.subLabels[VECTOR_ELEMENTS.Z_NUMBER]); - elProperty.appendChild(elNumberX.elDiv); - elProperty.appendChild(elNumberY.elDiv); - elProperty.appendChild(elNumberZ.elDiv); - - elNumberX.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'x')); - elNumberY.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'y')); - elNumberZ.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'z')); - - elNumberX.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'x')); - elNumberY.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'y')); - elNumberZ.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'z')); - - let elResult = []; - elResult[VECTOR_ELEMENTS.X_NUMBER] = elNumberX; - elResult[VECTOR_ELEMENTS.Y_NUMBER] = elNumberY; - elResult[VECTOR_ELEMENTS.Z_NUMBER] = elNumberZ; - return elResult; -} - -function createVec2Property(property, elProperty) { - let propertyData = property.data; - - elProperty.className = propertyData.vec2Type + " fstuple"; - - let elTuple = document.createElement('div'); - elTuple.className = "tuple"; - - elProperty.appendChild(elTuple); - - let elNumberX = createTupleNumberInput(property, propertyData.subLabels[VECTOR_ELEMENTS.X_NUMBER]); - let elNumberY = createTupleNumberInput(property, propertyData.subLabels[VECTOR_ELEMENTS.Y_NUMBER]); - elProperty.appendChild(elNumberX.elDiv); - elProperty.appendChild(elNumberY.elDiv); - - elNumberX.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'x')); - elNumberY.setValueChangeFunction(createEmitNumberPropertyComponentUpdateFunction(property, 'y')); - - elNumberX.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'x')); - elNumberY.setMultiDiffStepFunction(createMultiDiffStepFunction(property, 'y')); - - let elResult = []; - elResult[VECTOR_ELEMENTS.X_NUMBER] = elNumberX; - elResult[VECTOR_ELEMENTS.Y_NUMBER] = elNumberY; - return elResult; -} - -function updateVectorMinMax(property) { - let min = property.data.min; - let max = property.data.max; - property.elNumberX.updateMinMax(min, max); - property.elNumberY.updateMinMax(min, max); - if (property.elNumberZ) { - property.elNumberZ.updateMinMax(min, max); - } -} - -function createColorProperty(property, elProperty) { - let propertyName = property.name; - let elementID = property.elementID; - let propertyData = property.data; - - elProperty.className += " rgb fstuple"; - - let elColorPicker = document.createElement('div'); - elColorPicker.className = "color-picker"; - elColorPicker.setAttribute("id", elementID); - - let elTuple = document.createElement('div'); - elTuple.className = "tuple"; - - elProperty.appendChild(elColorPicker); - elProperty.appendChild(elTuple); - - if (propertyData.min === undefined) { - propertyData.min = COLOR_MIN; - } - if (propertyData.max === undefined) { - propertyData.max = COLOR_MAX; - } - if (propertyData.step === undefined) { - propertyData.step = COLOR_STEP; - } - - let elNumberR = createTupleNumberInput(property, "red"); - let elNumberG = createTupleNumberInput(property, "green"); - let elNumberB = createTupleNumberInput(property, "blue"); - elTuple.appendChild(elNumberR.elDiv); - elTuple.appendChild(elNumberG.elDiv); - elTuple.appendChild(elNumberB.elDiv); - - let valueChangeFunction = createEmitColorPropertyUpdateFunction(property); - elNumberR.setValueChangeFunction(valueChangeFunction); - elNumberG.setValueChangeFunction(valueChangeFunction); - elNumberB.setValueChangeFunction(valueChangeFunction); - - let colorPickerID = "#" + elementID; - colorPickers[colorPickerID] = $(colorPickerID).colpick({ - colorScheme: 'dark', - layout: 'rgbhex', - color: '000000', - submit: false, // We don't want to have a submission button - onShow: function(colpick) { - // The original color preview within the picker needs to be updated on show because - // prior to the picker being shown we don't have access to the selections' starting color. - colorPickers[colorPickerID].colpickSetColor({ - "r": elNumberR.elInput.value, - "g": elNumberG.elInput.value, - "b": elNumberB.elInput.value - }); - - // Set the color picker active after setting the color, otherwise an update will be sent on open. - $(colorPickerID).attr('active', 'true'); - }, - onHide: function(colpick) { - $(colorPickerID).attr('active', 'false'); - }, - onChange: function(hsb, hex, rgb, el) { - $(el).css('background-color', '#' + hex); - if ($(colorPickerID).attr('active') === 'true') { - emitColorPropertyUpdate(propertyName, rgb.r, rgb.g, rgb.b); - } - } - }); - - let elResult = []; - elResult[COLOR_ELEMENTS.COLOR_PICKER] = elColorPicker; - elResult[COLOR_ELEMENTS.RED_NUMBER] = elNumberR; - elResult[COLOR_ELEMENTS.GREEN_NUMBER] = elNumberG; - elResult[COLOR_ELEMENTS.BLUE_NUMBER] = elNumberB; - return elResult; -} - -function createDropdownProperty(property, propertyID, elProperty) { - let elementID = property.elementID; - let propertyData = property.data; - - elProperty.className = "dropdown"; - - let elInput = document.createElement('select'); - elInput.setAttribute("id", elementID); - elInput.setAttribute("propertyID", propertyID); - - for (let optionKey in propertyData.options) { - let option = document.createElement('option'); - option.value = optionKey; - option.text = propertyData.options[optionKey]; - elInput.add(option); - } - - elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(property)); - - elProperty.appendChild(elInput); - - return elInput; -} - -function createTextareaProperty(property, elProperty) { - let elementID = property.elementID; - let propertyData = property.data; - - elProperty.className = "textarea"; - - let elInput = document.createElement('textarea'); - elInput.setAttribute("id", elementID); - if (propertyData.readOnly) { - elInput.readOnly = true; - } - - elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(property)); - - let elMultiDiff = document.createElement('span'); - elMultiDiff.className = "multi-diff"; - - elProperty.appendChild(elInput); - elProperty.appendChild(elMultiDiff); - - if (propertyData.buttons !== undefined) { - addButtons(elProperty, elementID, propertyData.buttons, true); - } - - return elInput; -} - -function createIconProperty(property, elProperty) { - let elementID = property.elementID; - - elProperty.className = "value"; - - let elSpan = document.createElement('span'); - elSpan.setAttribute("id", elementID + "-icon"); - elSpan.className = 'icon'; - - elProperty.appendChild(elSpan); - - return elSpan; -} - -function createTextureProperty(property, elProperty) { - let elementID = property.elementID; - - elProperty.className = "texture"; - - let elDiv = document.createElement("div"); - let elImage = document.createElement("img"); - elDiv.className = "texture-image no-texture"; - elDiv.appendChild(elImage); - - let elInput = document.createElement('input'); - elInput.setAttribute("id", elementID); - elInput.setAttribute("type", "text"); - - let imageLoad = function(url) { - elDiv.style.display = null; - if (url.slice(0, 5).toLowerCase() === "atp:/") { - elImage.src = ""; - elImage.style.display = "none"; - elDiv.classList.remove("with-texture"); - elDiv.classList.remove("no-texture"); - elDiv.classList.add("no-preview"); - } else if (url.length > 0) { - elDiv.classList.remove("no-texture"); - elDiv.classList.remove("no-preview"); - elDiv.classList.add("with-texture"); - elImage.src = url; - elImage.style.display = "block"; - } else { - elImage.src = ""; - elImage.style.display = "none"; - elDiv.classList.remove("with-texture"); - elDiv.classList.remove("no-preview"); - elDiv.classList.add("no-texture"); - } - }; - elInput.imageLoad = imageLoad; - elInput.setMultipleValues = function() { - elDiv.style.display = "none"; - }; - elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(property)); - elInput.addEventListener('change', function(ev) { - imageLoad(ev.target.value); - }); - - elProperty.appendChild(elInput); - let elMultiDiff = document.createElement('span'); - elMultiDiff.className = "multi-diff"; - elProperty.appendChild(elMultiDiff); - elProperty.appendChild(elDiv); - - let elResult = []; - elResult[TEXTURE_ELEMENTS.IMAGE] = elImage; - elResult[TEXTURE_ELEMENTS.TEXT_INPUT] = elInput; - return elResult; -} - -function createButtonsProperty(property, elProperty) { - let elementID = property.elementID; - let propertyData = property.data; - - elProperty.className = "text"; - - if (propertyData.buttons !== undefined) { - addButtons(elProperty, elementID, propertyData.buttons, false); - } - - return elProperty; -} - -function createDynamicMultiselectProperty(property, elProperty) { - let elementID = property.elementID; - let propertyData = property.data; - - elProperty.className = "dynamic-multiselect"; - - let elDivOptions = document.createElement('div'); - elDivOptions.setAttribute("id", elementID + "-options"); - elDivOptions.style = "overflow-y:scroll;max-height:160px;"; - - let elDivButtons = document.createElement('div'); - elDivButtons.setAttribute("id", elDivOptions.getAttribute("id") + "-buttons"); - - let elLabel = document.createElement('label'); - elLabel.innerText = "No Options"; - elDivOptions.appendChild(elLabel); - - let buttons = [ { id: "selectAll", label: "Select All", className: "black", onClick: selectAllMaterialTarget }, - { id: "clearAll", label: "Clear All", className: "black", onClick: clearAllMaterialTarget } ]; - addButtons(elDivButtons, elementID, buttons, false); - - elProperty.appendChild(elDivOptions); - elProperty.appendChild(elDivButtons); - - return elDivOptions; -} - -function resetDynamicMultiselectProperty(elDivOptions) { - let elInputs = elDivOptions.getElementsByTagName("input"); - while (elInputs.length > 0) { - let elDivOption = elInputs[0].parentNode; - elDivOption.parentNode.removeChild(elDivOption); - } - elDivOptions.firstChild.style.display = null; // show "No Options" text - elDivOptions.parentNode.lastChild.style.display = "none"; // hide Select/Clear all buttons -} - -function createTupleNumberInput(property, subLabel) { - let propertyElementID = property.elementID; - let propertyData = property.data; - let elementID = propertyElementID + "-" + subLabel.toLowerCase(); - - let elLabel = document.createElement('label'); - elLabel.className = "sublabel " + subLabel; - elLabel.innerText = subLabel[0].toUpperCase() + subLabel.slice(1); - elLabel.setAttribute("for", elementID); - elLabel.style.visibility = "visible"; - - let dragStartFunction = createDragStartFunction(property); - let dragEndFunction = createDragEndFunction(property); - let elDraggableNumber = new DraggableNumber(propertyData.min, propertyData.max, propertyData.step, - propertyData.decimals, dragStartFunction, dragEndFunction); - elDraggableNumber.elInput.setAttribute("id", elementID); - elDraggableNumber.elDiv.className += " fstuple"; - elDraggableNumber.elDiv.insertBefore(elLabel, elDraggableNumber.elLeftArrow); - - return elDraggableNumber; -} - -function addButtons(elProperty, propertyID, buttons, newRow) { - let elDiv = document.createElement('div'); - elDiv.className = "row"; - - buttons.forEach(function(button) { - let elButton = document.createElement('input'); - elButton.className = button.className; - elButton.setAttribute("type", "button"); - elButton.setAttribute("id", propertyID + "-button-" + button.id); - elButton.setAttribute("value", button.label); - elButton.addEventListener("click", button.onClick); - if (newRow) { - elDiv.appendChild(elButton); - } else { - elProperty.appendChild(elButton); - } - }); - - if (newRow) { - elProperty.appendChild(document.createElement('br')); - elProperty.appendChild(elDiv); - } -} - -function createProperty(propertyData, propertyElementID, propertyName, propertyID, elProperty) { - let property = { - data: propertyData, - elementID: propertyElementID, - name: propertyName, - elProperty: elProperty, - }; - let propertyType = propertyData.type; - - switch (propertyType) { - case 'string': { - property.elInput = createStringProperty(property, elProperty); - break; - } - case 'bool': { - property.elInput = createBoolProperty(property, elProperty); - break; - } - case 'number': { - property.elInput = createNumberProperty(property, elProperty); - break; - } - case 'number-draggable': { - property.elNumber = createNumberDraggableProperty(property, elProperty); - break; - } - case 'rect': { - let elRect = createRectProperty(property, elProperty); - property.elNumberX = elRect[RECT_ELEMENTS.X_NUMBER]; - property.elNumberY = elRect[RECT_ELEMENTS.Y_NUMBER]; - property.elNumberWidth = elRect[RECT_ELEMENTS.WIDTH_NUMBER]; - property.elNumberHeight = elRect[RECT_ELEMENTS.HEIGHT_NUMBER]; - break; - } - case 'vec3': { - let elVec3 = createVec3Property(property, elProperty); - property.elNumberX = elVec3[VECTOR_ELEMENTS.X_NUMBER]; - property.elNumberY = elVec3[VECTOR_ELEMENTS.Y_NUMBER]; - property.elNumberZ = elVec3[VECTOR_ELEMENTS.Z_NUMBER]; - break; - } - case 'vec2': { - let elVec2 = createVec2Property(property, elProperty); - property.elNumberX = elVec2[VECTOR_ELEMENTS.X_NUMBER]; - property.elNumberY = elVec2[VECTOR_ELEMENTS.Y_NUMBER]; - break; - } - case 'color': { - let elColor = createColorProperty(property, elProperty); - property.elColorPicker = elColor[COLOR_ELEMENTS.COLOR_PICKER]; - property.elNumberR = elColor[COLOR_ELEMENTS.RED_NUMBER]; - property.elNumberG = elColor[COLOR_ELEMENTS.GREEN_NUMBER]; - property.elNumberB = elColor[COLOR_ELEMENTS.BLUE_NUMBER]; - break; - } - case 'dropdown': { - property.elInput = createDropdownProperty(property, propertyID, elProperty); - break; - } - case 'textarea': { - property.elInput = createTextareaProperty(property, elProperty); - break; - } - case 'multipleZonesSelection': { - property.elInput = createZonesSelection(property, elProperty); - break; - } - case 'icon': { - property.elSpan = createIconProperty(property, elProperty); - break; - } - case 'texture': { - let elTexture = createTextureProperty(property, elProperty); - property.elImage = elTexture[TEXTURE_ELEMENTS.IMAGE]; - property.elInput = elTexture[TEXTURE_ELEMENTS.TEXT_INPUT]; - break; - } - case 'buttons': { - property.elProperty = createButtonsProperty(property, elProperty); - break; - } - case 'dynamic-multiselect': { - property.elDivOptions = createDynamicMultiselectProperty(property, elProperty); - break; - } - case 'placeholder': - case 'sub-header': { - break; - } - default: { - console.log("EntityProperties - Unknown property type " + - propertyType + " set to property " + propertyID); - break; - } - } - - return property; -} - - -/** - * PROPERTY-SPECIFIC CALLBACKS - */ - -function parentIDChanged() { - if (currentSelections.length === 1 && currentSelections[0].properties.type === "Material") { - requestMaterialTarget(); - } -} - - -/** - * BUTTON CALLBACKS - */ - -function rescaleDimensions() { - EventBridge.emitWebEvent(JSON.stringify({ - type: "action", - action: "rescaleDimensions", - percentage: parseFloat(document.getElementById("property-scale").value) - })); -} - -function moveSelectionToGrid() { - EventBridge.emitWebEvent(JSON.stringify({ - type: "action", - action: "moveSelectionToGrid" - })); -} - -function moveAllToGrid() { - EventBridge.emitWebEvent(JSON.stringify({ - type: "action", - action: "moveAllToGrid" - })); -} - -function resetToNaturalDimensions() { - EventBridge.emitWebEvent(JSON.stringify({ - type: "action", - action: "resetToNaturalDimensions" - })); -} - -function reloadScripts() { - EventBridge.emitWebEvent(JSON.stringify({ - type: "action", - action: "reloadClientScripts" - })); -} - -function reloadServerScripts() { - // invalidate the current status (so that same-same updates can still be observed visually) - document.getElementById("property-serverScripts-status").innerText = PENDING_SCRIPT_STATUS; - EventBridge.emitWebEvent(JSON.stringify({ - type: "action", - action: "reloadServerScripts" - })); -} - -function copySkyboxURLToAmbientURL() { - let skyboxURL = getPropertyInputElement("skybox.url").value; - getPropertyInputElement("ambientLight.ambientURL").value = skyboxURL; - updateProperty("ambientLight.ambientURL", skyboxURL, false); -} - - -/** - * USER DATA FUNCTIONS - */ - -function clearUserData() { - let elUserData = getPropertyInputElement("userData"); - deleteJSONEditor(); - elUserData.value = ""; - showUserDataTextArea(); - showNewJSONEditorButton(); - hideSaveUserDataButton(); - updateProperty('userData', elUserData.value, false); -} - -function newJSONEditor() { - getPropertyInputElement("userData").classList.remove('multi-diff'); - deleteJSONEditor(); - createJSONEditor(); - let data = {}; - setEditorJSON(data); - hideUserDataTextArea(); - hideNewJSONEditorButton(); - showSaveUserDataButton(); -} - -/** - * @param {Set.} [entityIDsToUpdate] Entity IDs to update userData for. - */ -function saveUserData(entityIDsToUpdate) { - saveJSONUserData(true, entityIDsToUpdate); -} - -function setJSONError(property, isError) { - $("#property-"+ property + "-editor").toggleClass('error', isError); - let $propertyUserDataEditorStatus = $("#property-"+ property + "-editorStatus"); - $propertyUserDataEditorStatus.css('display', isError ? 'block' : 'none'); - $propertyUserDataEditorStatus.text(isError ? 'Invalid JSON code - look for red X in your code' : ''); -} - -/** - * @param {boolean} noUpdate - don't update the UI, but do send a property update. - * @param {Set.} [entityIDsToUpdate] - Entity IDs to update userData for. - */ -function setUserDataFromEditor(noUpdate, entityIDsToUpdate) { - let errorFound = false; - try { - editor.get(); - } catch (e) { - errorFound = true; - } - - setJSONError('userData', errorFound); - - if (errorFound) { - return; - } - - let text = editor.getText(); - if (noUpdate) { - EventBridge.emitWebEvent( - JSON.stringify({ - ids: [...entityIDsToUpdate], - type: "saveUserData", - properties: { - userData: text - } - }) - ); - } else { - updateProperty('userData', text, false); - } -} - -let editor = null; - -function createJSONEditor() { - let container = document.getElementById("property-userData-editor"); - let options = { - search: false, - mode: 'tree', - modes: ['code', 'tree'], - name: 'userData', - onError: function(e) { - alert('JSON editor:' + e); - }, - onChange: function() { - let currentJSONString = editor.getText(); - - if (currentJSONString === '{"":""}') { - return; - } - $('#property-userData-button-save').attr('disabled', false); - } - }; - editor = new JSONEditor(container, options); -} - -function showSaveUserDataButton() { - $('#property-userData-button-save').show(); -} - -function hideSaveUserDataButton() { - $('#property-userData-button-save').hide(); -} - -function disableSaveUserDataButton() { - $('#property-userData-button-save').attr('disabled', true); -} - -function showNewJSONEditorButton() { - $('#property-userData-button-edit').show(); -} - -function hideNewJSONEditorButton() { - $('#property-userData-button-edit').hide(); -} - -function showUserDataTextArea() { - $('#property-userData').show(); -} - -function hideUserDataTextArea() { - $('#property-userData').hide(); -} - -function hideUserDataSaved() { - $('#property-userData-saved').hide(); -} - -function setEditorJSON(json) { - editor.set(json); - if (editor.hasOwnProperty('expandAll')) { - editor.expandAll(); - } -} - -function deleteJSONEditor() { - if (editor !== null) { - setJSONError('userData', false); - editor.destroy(); - editor = null; - } -} - -let savedJSONTimer = null; - -/** - * @param {boolean} noUpdate - don't update the UI, but do send a property update. - * @param {Set.} [entityIDsToUpdate] Entity IDs to update userData for - */ -function saveJSONUserData(noUpdate, entityIDsToUpdate) { - setUserDataFromEditor(noUpdate, entityIDsToUpdate ? entityIDsToUpdate : selectedEntityIDs); - $('#property-userData-saved').show(); - $('#property-userData-button-save').attr('disabled', true); - if (savedJSONTimer !== null) { - clearTimeout(savedJSONTimer); - } - savedJSONTimer = setTimeout(function() { - hideUserDataSaved(); - }, EDITOR_TIMEOUT_DURATION); -} - - -/** - * MATERIAL DATA FUNCTIONS - */ - -function clearMaterialData() { - let elMaterialData = getPropertyInputElement("materialData"); - deleteJSONMaterialEditor(); - elMaterialData.value = ""; - showMaterialDataTextArea(); - showNewJSONMaterialEditorButton(); - hideSaveMaterialDataButton(); - updateProperty('materialData', elMaterialData.value, false); -} - -function newJSONMaterialEditor() { - getPropertyInputElement("materialData").classList.remove('multi-diff'); - deleteJSONMaterialEditor(); - createJSONMaterialEditor(); - let data = {}; - setMaterialEditorJSON(data); - hideMaterialDataTextArea(); - hideNewJSONMaterialEditorButton(); - showSaveMaterialDataButton(); -} - -function saveMaterialData() { - saveJSONMaterialData(true); -} - -/** - * @param {boolean} noUpdate - don't update the UI, but do send a property update. - * @param {Set.} [entityIDsToUpdate] - Entity IDs to update materialData for. - */ -function setMaterialDataFromEditor(noUpdate, entityIDsToUpdate) { - let errorFound = false; - try { - materialEditor.get(); - } catch (e) { - errorFound = true; - } - - setJSONError('materialData', errorFound); - - if (errorFound) { - return; - } - let text = materialEditor.getText(); - if (noUpdate) { - EventBridge.emitWebEvent( - JSON.stringify({ - ids: [...entityIDsToUpdate], - type: "saveMaterialData", - properties: { - materialData: text - } - }) - ); - } else { - updateProperty('materialData', text, false); - } -} - -let materialEditor = null; - -function createJSONMaterialEditor() { - let container = document.getElementById("property-materialData-editor"); - let options = { - search: false, - mode: 'tree', - modes: ['code', 'tree'], - name: 'materialData', - onError: function(e) { - alert('JSON editor:' + e); - }, - onChange: function() { - let currentJSONString = materialEditor.getText(); - - if (currentJSONString === '{"":""}') { - return; - } - $('#property-materialData-button-save').attr('disabled', false); - } - }; - materialEditor = new JSONEditor(container, options); -} - -function showSaveMaterialDataButton() { - $('#property-materialData-button-save').show(); -} - -function hideSaveMaterialDataButton() { - $('#property-materialData-button-save').hide(); -} - -function disableSaveMaterialDataButton() { - $('#property-materialData-button-save').attr('disabled', true); -} - -function showNewJSONMaterialEditorButton() { - $('#property-materialData-button-edit').show(); -} - -function hideNewJSONMaterialEditorButton() { - $('#property-materialData-button-edit').hide(); -} - -function showMaterialDataTextArea() { - $('#property-materialData').show(); -} - -function hideMaterialDataTextArea() { - $('#property-materialData').hide(); -} - -function hideMaterialDataSaved() { - $('#property-materialData-saved').hide(); -} - -function setMaterialEditorJSON(json) { - materialEditor.set(json); - if (materialEditor.hasOwnProperty('expandAll')) { - materialEditor.expandAll(); - } -} - -function deleteJSONMaterialEditor() { - if (materialEditor !== null) { - setJSONError('materialData', false); - materialEditor.destroy(); - materialEditor = null; - } -} - -let savedMaterialJSONTimer = null; - -/** - * @param {boolean} noUpdate - don't update the UI, but do send a property update. - * @param {Set.} [entityIDsToUpdate] - Entity IDs to update materialData for. - */ -function saveJSONMaterialData(noUpdate, entityIDsToUpdate) { - setMaterialDataFromEditor(noUpdate, entityIDsToUpdate ? entityIDsToUpdate : selectedEntityIDs); - $('#property-materialData-saved').show(); - $('#property-materialData-button-save').attr('disabled', true); - if (savedMaterialJSONTimer !== null) { - clearTimeout(savedMaterialJSONTimer); - } - savedMaterialJSONTimer = setTimeout(function() { - hideMaterialDataSaved(); - }, EDITOR_TIMEOUT_DURATION); -} - -function bindAllNonJSONEditorElements() { - let inputs = $('input'); - let i; - for (i = 0; i < inputs.length; ++i) { - let input = inputs[i]; - let field = $(input); - // TODO FIXME: (JSHint) Functions declared within loops referencing - // an outer scoped variable may lead to confusing semantics. - field.on('focus', function(e) { - if (e.target.id === "property-userData-button-edit" || e.target.id === "property-userData-button-clear" || - e.target.id === "property-materialData-button-edit" || e.target.id === "property-materialData-button-clear") { - return; - } - if ($('#property-userData-editor').css('height') !== "0px") { - saveUserData(); - } - if ($('#property-materialData-editor').css('height') !== "0px") { - saveMaterialData(); - } - }); - } -} - - -/** - * DROPDOWN FUNCTIONS - */ - -function setDropdownText(dropdown) { - let lis = dropdown.parentNode.getElementsByTagName("li"); - let text = ""; - for (let i = 0; i < lis.length; ++i) { - if (String(lis[i].getAttribute("value")) === String(dropdown.value)) { - text = lis[i].textContent; - } - } - dropdown.firstChild.textContent = text; -} - -function toggleDropdown(event) { - let element = event.target; - if (element.nodeName !== "DT") { - element = element.parentNode; - } - element = element.parentNode; - let isDropped = element.getAttribute("dropped"); - element.setAttribute("dropped", isDropped !== "true" ? "true" : "false"); -} - -function closeAllDropdowns() { - let elDropdowns = document.querySelectorAll("div.dropdown > dl"); - for (let i = 0; i < elDropdowns.length; ++i) { - elDropdowns[i].setAttribute('dropped', 'false'); - } -} - -function setDropdownValue(event) { - let dt = event.target.parentNode.parentNode.previousSibling.previousSibling; - dt.value = event.target.getAttribute("value"); - dt.firstChild.textContent = event.target.textContent; - - dt.parentNode.setAttribute("dropped", "false"); - - let evt = document.createEvent("HTMLEvents"); - evt.initEvent("change", true, true); - dt.dispatchEvent(evt); -} - - -/** - * TEXTAREA FUNCTIONS - */ - -function setTextareaScrolling(element) { - let isScrolling = element.scrollHeight > element.offsetHeight; - element.setAttribute("scrolling", isScrolling ? "true" : "false"); -} - -/** - * ZONE SELECTOR FUNCTIONS - */ - -function enableAllMultipleZoneSelector() { - let allMultiZoneSelectors = document.querySelectorAll(".hiddenMultiZonesSelection"); - let i, propId; - for (i = 0; i < allMultiZoneSelectors.length; i++) { - propId = allMultiZoneSelectors[i].id; - displaySelectedZones(propId, true); - } -} - -function disableAllMultipleZoneSelector() { - let allMultiZoneSelectors = document.querySelectorAll(".hiddenMultiZonesSelection"); - let i, propId; - for (i = 0; i < allMultiZoneSelectors.length; i++) { - propId = allMultiZoneSelectors[i].id; - displaySelectedZones(propId, false); - } -} - -function requestZoneList() { - EventBridge.emitWebEvent(JSON.stringify({ - type: "zoneListRequest" - })); -} - -function addZoneToZonesSelection(propertyId) { - let hiddenField = document.getElementById(propertyId); - if (JSON.stringify(hiddenField.value) === '"undefined"') { - hiddenField.value = "[]"; - } - let selectedZones = JSON.parse(hiddenField.value); - let zoneToAdd = document.getElementById("zones-select-" + propertyId).value; - if (!selectedZones.includes(zoneToAdd)) { - selectedZones.push(zoneToAdd); - } - hiddenField.value = JSON.stringify(selectedZones); - displaySelectedZones(propertyId, true); - let propertyName = propertyId.replace("property-", ""); - updateProperty(propertyName, selectedZones, false); -} - -function removeZoneFromZonesSelection(propertyId, zoneId) { - let hiddenField = document.getElementById(propertyId); - if (JSON.stringify(hiddenField.value) === '"undefined"') { - hiddenField.value = "[]"; - } - let selectedZones = JSON.parse(hiddenField.value); - let index = selectedZones.indexOf(zoneId); - if (index > -1) { - selectedZones.splice(index, 1); - } - hiddenField.value = JSON.stringify(selectedZones); - displaySelectedZones(propertyId, true); - let propertyName = propertyId.replace("property-", ""); - updateProperty(propertyName, selectedZones, false); -} - -function displaySelectedZones(propertyId, isEditable) { - let i,j, name, listedZoneInner, hiddenData, isMultiple; - hiddenData = document.getElementById(propertyId).value; - if (JSON.stringify(hiddenData) === '"undefined"') { - isMultiple = true; - hiddenData = "[]"; - } else { - isMultiple = false; - } - let selectedZones = JSON.parse(hiddenData); - listedZoneInner = ""; - if (selectedZones.length === 0) { - if (!isMultiple) { - listedZoneInner += ""; - } else { - listedZoneInner += ""; - } - } else { - for (i = 0; i < selectedZones.length; i++) { - name = "{ERROR: NOT FOUND}"; - for (j = 0; j < zonesList.length; j++) { - if (selectedZones[i] === zonesList[j].id) { - if (zonesList[j].name !== "") { - name = zonesList[j].name; - } else { - name = zonesList[j].id; - } - break; - } - } - if (isEditable) { - listedZoneInner += ""; - } else { - listedZoneInner += ""; - } - } - } - listedZoneInner += "
  
[ WARNING: Any changes will apply to all. ] 
" + name + ""; - listedZoneInner += "
" + name + " 
"; - document.getElementById("selected-zones-" + propertyId).innerHTML = listedZoneInner; - if (isEditable) { - document.getElementById("multiZoneSelTools-" + propertyId).style.display = "block"; - } else { - document.getElementById("multiZoneSelTools-" + propertyId).style.display = "none"; - } -} - -function createZonesSelection(property, elProperty) { - let elementID = property.elementID; - requestZoneList(); - elProperty.className = "multipleZonesSelection"; - let elInput = document.createElement('input'); - elInput.setAttribute("id", elementID); - elInput.setAttribute("type", "hidden"); - elInput.className = "hiddenMultiZonesSelection"; - - let elZonesSelector = document.createElement('div'); - elZonesSelector.setAttribute("id", "zones-selector-" + elementID); - - let elMultiDiff = document.createElement('span'); - elMultiDiff.className = "multi-diff"; - - elProperty.appendChild(elInput); - elProperty.appendChild(elZonesSelector); - elProperty.appendChild(elMultiDiff); - - return elInput; -} - -function setZonesSelectionData(element, isEditable) { - let zoneSelectorContainer = document.getElementById("zones-selector-" + element.id); - let zoneSelector = "
 "; - zoneSelector += "
"; - zoneSelector += "
"; - zoneSelectorContainer.innerHTML = zoneSelector; - displaySelectedZones(element.id, isEditable); -} - -function updateAllZoneSelect() { - let allZoneSelects = document.querySelectorAll(".zoneSelect"); - let i, j, name, propId; - for (i = 0; i < allZoneSelects.length; i++) { - allZoneSelects[i].options.length = 0; - for (j = 0; j < zonesList.length; j++) { - if (zonesList[j].name === "") { - name = zonesList[j].id; - } else { - name = zonesList[j].name; - } - allZoneSelects[i].options[j] = new Option(name, zonesList[j].id, false , false); - } - propId = allZoneSelects[i].id.replace("zones-select-", ""); - if (document.getElementById("multiZoneSelTools-" + propId).style.display === "block") { - displaySelectedZones(propId, true); - } else { - displaySelectedZones(propId, false); - } - } -} - -/** - * MATERIAL TARGET FUNCTIONS - */ - -function requestMaterialTarget() { - EventBridge.emitWebEvent(JSON.stringify({ - type: 'materialTargetRequest', - entityID: getFirstSelectedID(), - })); -} - -function setMaterialTargetData(materialTargetData) { - let elDivOptions = getPropertyInputElement("parentMaterialName"); - resetDynamicMultiselectProperty(elDivOptions); - - if (materialTargetData === undefined) { - return; - } - - elDivOptions.firstChild.style.display = "none"; // hide "No Options" text - elDivOptions.parentNode.lastChild.style.display = null; // show Select/Clear all buttons - - let numMeshes = materialTargetData.numMeshes; - for (let i = 0; i < numMeshes; ++i) { - addMaterialTarget(elDivOptions, i, false); - } - - let materialNames = materialTargetData.materialNames; - let materialNamesAdded = []; - for (let i = 0; i < materialNames.length; ++i) { - let materialName = materialNames[i]; - if (materialNamesAdded.indexOf(materialName) === -1) { - addMaterialTarget(elDivOptions, materialName, true); - materialNamesAdded.push(materialName); - } - } - - materialTargetPropertyUpdate(elDivOptions.propertyValue); -} - -function addMaterialTarget(elDivOptions, targetID, isMaterialName) { - let elementID = elDivOptions.getAttribute("id"); - elementID += isMaterialName ? "-material-" : "-mesh-"; - elementID += targetID; - - let elDiv = document.createElement('div'); - elDiv.className = "materialTargetDiv"; - elDiv.onclick = onToggleMaterialTarget; - elDivOptions.appendChild(elDiv); - - let elInput = document.createElement('input'); - elInput.className = "materialTargetInput"; - elInput.setAttribute("type", "checkbox"); - elInput.setAttribute("id", elementID); - elInput.setAttribute("targetID", targetID); - elInput.setAttribute("isMaterialName", isMaterialName); - elDiv.appendChild(elInput); - - let elLabel = document.createElement('label'); - elLabel.setAttribute("for", elementID); - elLabel.innerText = isMaterialName ? "Material " + targetID : "Mesh Index " + targetID; - elDiv.appendChild(elLabel); - - return elDiv; -} - -function onToggleMaterialTarget(event) { - let elTarget = event.target; - if (elTarget instanceof HTMLInputElement) { - sendMaterialTargetProperty(); - } - event.stopPropagation(); -} - -function setAllMaterialTargetInputs(checked) { - let elDivOptions = getPropertyInputElement("parentMaterialName"); - let elInputs = elDivOptions.getElementsByClassName("materialTargetInput"); - for (let i = 0; i < elInputs.length; ++i) { - elInputs[i].checked = checked; - } -} - -function selectAllMaterialTarget() { - setAllMaterialTargetInputs(true); - sendMaterialTargetProperty(); -} - -function clearAllMaterialTarget() { - setAllMaterialTargetInputs(false); - sendMaterialTargetProperty(); -} - -function sendMaterialTargetProperty() { - let elDivOptions = getPropertyInputElement("parentMaterialName"); - let elInputs = elDivOptions.getElementsByClassName("materialTargetInput"); - - let materialTargetList = []; - for (let i = 0; i < elInputs.length; ++i) { - let elInput = elInputs[i]; - if (elInput.checked) { - let targetID = elInput.getAttribute("targetID"); - if (elInput.getAttribute("isMaterialName") === "true") { - materialTargetList.push(MATERIAL_PREFIX_STRING + targetID); - } else { - materialTargetList.push(targetID); - } - } - } - - let propertyValue = materialTargetList.join(","); - if (propertyValue.length > 1) { - propertyValue = "[" + propertyValue + "]"; - } - - updateProperty("parentMaterialName", propertyValue, false); -} - -function materialTargetPropertyUpdate(propertyValue) { - let elDivOptions = getPropertyInputElement("parentMaterialName"); - let elInputs = elDivOptions.getElementsByClassName("materialTargetInput"); - - if (propertyValue.startsWith('[')) { - propertyValue = propertyValue.substring(1, propertyValue.length); - } - if (propertyValue.endsWith(']')) { - propertyValue = propertyValue.substring(0, propertyValue.length - 1); - } - - let materialTargets = propertyValue.split(","); - for (let i = 0; i < elInputs.length; ++i) { - let elInput = elInputs[i]; - let targetID = elInput.getAttribute("targetID"); - let materialTargetName = targetID; - if (elInput.getAttribute("isMaterialName") === "true") { - materialTargetName = MATERIAL_PREFIX_STRING + targetID; - } - elInput.checked = materialTargets.indexOf(materialTargetName) >= 0; - } - - elDivOptions.propertyValue = propertyValue; -} - -function roundAndFixNumber(number, propertyData) { - let result = number; - if (propertyData.round !== undefined) { - result = Math.round(result * propertyData.round) / propertyData.round; - } - if (propertyData.decimals !== undefined) { - return result.toFixed(propertyData.decimals) - } - return result; -} - -function applyInputNumberPropertyModifiers(number, propertyData) { - const multiplier = propertyData.multiplier !== undefined ? propertyData.multiplier : 1; - return roundAndFixNumber(number / multiplier, propertyData); -} - -function applyOutputNumberPropertyModifiers(number, propertyData) { - const multiplier = propertyData.multiplier !== undefined ? propertyData.multiplier : 1; - return roundAndFixNumber(number * multiplier, propertyData); -} - -const areSetsEqual = (a, b) => a.size === b.size && [...a].every(value => b.has(value)); - - -function handleEntitySelectionUpdate(selections, isPropertiesToolUpdate) { - const previouslySelectedEntityIDs = selectedEntityIDs; - currentSelections = selections; - selectedEntityIDs = new Set(selections.map(selection => selection.id)); - const multipleSelections = currentSelections.length > 1; - const hasSelectedEntityChanged = !areSetsEqual(selectedEntityIDs, previouslySelectedEntityIDs); - - requestZoneList(); - - if (selections.length === 0) { - deleteJSONEditor(); - deleteJSONMaterialEditor(); - - resetProperties(); - showGroupsForType("None"); - - let elIcon = properties.type.elSpan; - elIcon.innerText = NO_SELECTION; - elIcon.style.display = 'inline-block'; - - getPropertyInputElement("userData").value = ""; - showUserDataTextArea(); - showSaveUserDataButton(); - showNewJSONEditorButton(); - - getPropertyInputElement("materialData").value = ""; - showMaterialDataTextArea(); - showSaveMaterialDataButton(); - showNewJSONMaterialEditorButton(); - - disableProperties(); - } else { - if (!isPropertiesToolUpdate && !hasSelectedEntityChanged && document.hasFocus()) { - // in case the selection has not changed and we still have focus on the properties page, - // we will ignore the event. - return; - } - - if (hasSelectedEntityChanged) { - if (!multipleSelections) { - resetServerScriptStatus(); - } - } - - const doSelectElement = !hasSelectedEntityChanged; - - // Get unique entity types, and convert the types Sphere and Box to Shape - const shapeTypes = ["Sphere", "Box"]; - const entityTypes = [...new Set(currentSelections.map(a => - shapeTypes.includes(a.properties.type) ? "Shape" : a.properties.type))]; - - const shownGroups = getGroupsForTypes(entityTypes); - showGroupsForTypes(entityTypes); - showOnTheSamePage(entityTypes); - - const lockedMultiValue = getMultiplePropertyValue('locked'); - - if (lockedMultiValue.isMultiDiffValue || lockedMultiValue.value) { - disableProperties(); - getPropertyInputElement('locked').removeAttribute('disabled'); - } else { - enableProperties(); - disableSaveUserDataButton(); - disableSaveMaterialDataButton(); - } - - const certificateIDMultiValue = getMultiplePropertyValue('certificateID'); - const hasCertifiedInSelection = certificateIDMultiValue.isMultiDiffValue || certificateIDMultiValue.value !== ""; - - Object.entries(properties).forEach(function([propertyID, property]) { - const propertyData = property.data; - const propertyName = property.name; - let propertyMultiValue = getMultiplePropertyValue(propertyName); - let isMultiDiffValue = propertyMultiValue.isMultiDiffValue; - let propertyValue = propertyMultiValue.value; - - if (propertyData.selectionVisibility !== undefined) { - let visibility = propertyData.selectionVisibility; - let propertyVisible = true; - if (!multipleSelections) { - propertyVisible = isFlagSet(visibility, PROPERTY_SELECTION_VISIBILITY.SINGLE_SELECTION); - } else if (isMultiDiffValue) { - propertyVisible = isFlagSet(visibility, PROPERTY_SELECTION_VISIBILITY.MULTI_DIFF_SELECTIONS); - } else { - propertyVisible = isFlagSet(visibility, PROPERTY_SELECTION_VISIBILITY.MULTIPLE_SELECTIONS); - } - setPropertyVisibility(property, propertyVisible); - } - - const isSubProperty = propertyData.subPropertyOf !== undefined; - if (propertyValue === undefined && !isMultiDiffValue && !isSubProperty) { - return; - } - - if (!shownGroups.includes(property.group_id)) { - const WANT_DEBUG_SHOW_HIDDEN_FROM_GROUPS = false; - if (WANT_DEBUG_SHOW_HIDDEN_FROM_GROUPS) { - console.log("Skipping property " + property.data.label + " [" + property.name + - "] from hidden group " + property.group_id); - } - return; - } - - if (propertyData.hideIfCertified && hasCertifiedInSelection) { - propertyValue = "** Certified **"; - property.elInput.disabled = true; - } - - if (propertyName === "type") { - propertyValue = entityTypes.length > 1 ? "Multiple" : propertyMultiValue.values[0]; - } - - switch (propertyData.type) { - case 'string': { - if (isMultiDiffValue) { - if (propertyData.readOnly && propertyData.multiDisplayMode - && propertyData.multiDisplayMode === PROPERTY_MULTI_DISPLAY_MODE.COMMA_SEPARATED_VALUES) { - property.elInput.value = propertyMultiValue.values.join(", "); - } else { - property.elInput.classList.add('multi-diff'); - property.elInput.value = ""; - } - } else { - property.elInput.classList.remove('multi-diff'); - property.elInput.value = propertyValue; - } - break; - } - case 'bool': { - const inverse = propertyData.inverse !== undefined ? propertyData.inverse : false; - if (isSubProperty) { - let subPropertyMultiValue = getMultiplePropertyValue(propertyData.subPropertyOf); - let propertyValue = subPropertyMultiValue.value; - isMultiDiffValue = subPropertyMultiValue.isMultiDiffValue; - if (isMultiDiffValue) { - let detailedSubProperty = getDetailedSubPropertyMPVDiff(subPropertyMultiValue, propertyName); - property.elInput.checked = detailedSubProperty.isChecked; - property.elInput.classList.toggle('multi-diff', detailedSubProperty.isMultiDiff); - } else { - let subProperties = propertyValue.split(","); - let subPropertyValue = subProperties.indexOf(propertyName) > -1; - property.elInput.checked = inverse ? !subPropertyValue : subPropertyValue; - property.elInput.classList.remove('multi-diff'); - } - - } else { - if (isMultiDiffValue) { - property.elInput.checked = false; - } else { - property.elInput.checked = inverse ? !propertyValue : propertyValue; - } - property.elInput.classList.toggle('multi-diff', isMultiDiffValue); - } - - break; - } - case 'number': { - property.elInput.value = isMultiDiffValue ? "" : propertyValue; - property.elInput.classList.toggle('multi-diff', isMultiDiffValue); - break; - } - case 'number-draggable': { - let detailedNumberDiff = getDetailedNumberMPVDiff(propertyMultiValue, propertyData); - property.elNumber.setValue(detailedNumberDiff.averagePerPropertyComponent[0], detailedNumberDiff.propertyComponentDiff[0]); - break; - } - case 'rect': { - let detailedNumberDiff = getDetailedNumberMPVDiff(propertyMultiValue, propertyData); - property.elNumberX.setValue(detailedNumberDiff.averagePerPropertyComponent.x, detailedNumberDiff.propertyComponentDiff.x); - property.elNumberY.setValue(detailedNumberDiff.averagePerPropertyComponent.y, detailedNumberDiff.propertyComponentDiff.y); - property.elNumberWidth.setValue(detailedNumberDiff.averagePerPropertyComponent.width, detailedNumberDiff.propertyComponentDiff.width); - property.elNumberHeight.setValue(detailedNumberDiff.averagePerPropertyComponent.height, detailedNumberDiff.propertyComponentDiff.height); - break; - } - case 'vec3': - case 'vec2': { - let detailedNumberDiff = getDetailedNumberMPVDiff(propertyMultiValue, propertyData); - property.elNumberX.setValue(detailedNumberDiff.averagePerPropertyComponent.x, detailedNumberDiff.propertyComponentDiff.x); - property.elNumberY.setValue(detailedNumberDiff.averagePerPropertyComponent.y, detailedNumberDiff.propertyComponentDiff.y); - if (property.elNumberZ !== undefined) { - property.elNumberZ.setValue(detailedNumberDiff.averagePerPropertyComponent.z, detailedNumberDiff.propertyComponentDiff.z); - } - break; - } - case 'color': { - let displayColor = propertyMultiValue.isMultiDiffValue ? propertyMultiValue.values[0] : propertyValue; - property.elColorPicker.style.backgroundColor = "rgb(" + displayColor.red + "," + - displayColor.green + "," + - displayColor.blue + ")"; - property.elColorPicker.classList.toggle('multi-diff', propertyMultiValue.isMultiDiffValue); - - if (hasSelectedEntityChanged && $(property.elColorPicker).attr('active') === 'true') { - // Set the color picker inactive before setting the color, - // otherwise an update will be sent directly after setting it here. - $(property.elColorPicker).attr('active', 'false'); - colorPickers['#' + property.elementID].colpickSetColor({ - "r": displayColor.red, - "g": displayColor.green, - "b": displayColor.blue - }); - $(property.elColorPicker).attr('active', 'true'); - } - - property.elNumberR.setValue(displayColor.red); - property.elNumberG.setValue(displayColor.green); - property.elNumberB.setValue(displayColor.blue); - break; - } - case 'dropdown': { - property.elInput.classList.toggle('multi-diff', isMultiDiffValue); - property.elInput.value = isMultiDiffValue ? "" : propertyValue; - setDropdownText(property.elInput); - break; - } - case 'textarea': { - property.elInput.value = propertyValue; - setTextareaScrolling(property.elInput); - break; - } - case 'multipleZonesSelection': { - property.elInput.value = JSON.stringify(propertyValue); - if (lockedMultiValue.isMultiDiffValue || lockedMultiValue.value) { - setZonesSelectionData(property.elInput, false); - } else { - setZonesSelectionData(property.elInput, true); - } - break; - } - case 'icon': { - property.elSpan.innerHTML = propertyData.icons[propertyValue]; - property.elSpan.style.display = "inline-block"; - break; - } - case 'texture': { - property.elInput.value = isMultiDiffValue ? "" : propertyValue; - property.elInput.classList.toggle('multi-diff', isMultiDiffValue); - if (isMultiDiffValue) { - property.elInput.setMultipleValues(); - } else { - property.elInput.imageLoad(property.elInput.value); - } - break; - } - case 'dynamic-multiselect': { - if (!isMultiDiffValue && property.data.propertyUpdate) { - property.data.propertyUpdate(propertyValue); - } - break; - } - } - - let showPropertyRules = property.showPropertyRules; - if (showPropertyRules !== undefined) { - for (let propertyToShow in showPropertyRules) { - let showIfThisPropertyValue = showPropertyRules[propertyToShow]; - let show = String(propertyValue) === String(showIfThisPropertyValue); - showPropertyElement(propertyToShow, show); - } - } - }); - - updateVisibleSpaceModeProperties(); - - let userDataMultiValue = getMultiplePropertyValue("userData"); - let userDataTextArea = getPropertyInputElement("userData"); - let json = null; - if (!userDataMultiValue.isMultiDiffValue) { - try { - json = JSON.parse(userDataMultiValue.value); - } catch (e) { - - } - } - if (json !== null && !lockedMultiValue.isMultiDiffValue && !lockedMultiValue.value) { - if (editor === null) { - createJSONEditor(); - } - userDataTextArea.classList.remove('multi-diff'); - setEditorJSON(json); - showSaveUserDataButton(); - hideUserDataTextArea(); - hideNewJSONEditorButton(); - hideUserDataSaved(); - } else { - // normal text - deleteJSONEditor(); - userDataTextArea.classList.toggle('multi-diff', userDataMultiValue.isMultiDiffValue); - userDataTextArea.value = userDataMultiValue.isMultiDiffValue ? "" : userDataMultiValue.value; - - showUserDataTextArea(); - showNewJSONEditorButton(); - hideSaveUserDataButton(); - hideUserDataSaved(); - } - - let materialDataMultiValue = getMultiplePropertyValue("materialData"); - let materialDataTextArea = getPropertyInputElement("materialData"); - let materialJson = null; - if (!materialDataMultiValue.isMultiDiffValue) { - try { - materialJson = JSON.parse(materialDataMultiValue.value); - } catch (e) { - - } - } - if (materialJson !== null && !lockedMultiValue.isMultiDiffValue && !lockedMultiValue.value) { - if (materialEditor === null) { - createJSONMaterialEditor(); - } - materialDataTextArea.classList.remove('multi-diff'); - setMaterialEditorJSON(materialJson); - showSaveMaterialDataButton(); - hideMaterialDataTextArea(); - hideNewJSONMaterialEditorButton(); - hideMaterialDataSaved(); - } else { - // normal text - deleteJSONMaterialEditor(); - materialDataTextArea.classList.toggle('multi-diff', materialDataMultiValue.isMultiDiffValue); - materialDataTextArea.value = materialDataMultiValue.isMultiDiffValue ? "" : materialDataMultiValue.value; - showMaterialDataTextArea(); - showNewJSONMaterialEditorButton(); - hideSaveMaterialDataButton(); - hideMaterialDataSaved(); - } - - if (hasSelectedEntityChanged && selections.length === 1 && entityTypes[0] === "Material") { - requestMaterialTarget(); - } - - let activeElement = document.activeElement; - if (doSelectElement && typeof activeElement.select !== "undefined") { - activeElement.select(); - } - } -} - -function loaded() { - openEventBridge(function() { - let elPropertiesList = document.getElementById("properties-pages"); - let elTabs = document.getElementById("tabs"); - - GROUPS.forEach(function(group) { - let elGroup; - - elGroup = document.createElement('div'); - elGroup.className = 'section ' + "major"; - elGroup.setAttribute("id", "properties-" + group.id); - elPropertiesList.appendChild(elGroup); - - if (group.label !== undefined) { - let elLegend = document.createElement('div'); - elLegend.className = "tab-section-header"; - elLegend.appendChild(createElementFromHTML(`
${group.label}
`)); - elGroup.appendChild(elLegend); - elTabs.appendChild(createElementFromHTML('')); - } - - group.properties.forEach(function(propertyData) { - let propertyType = propertyData.type; - let propertyID = propertyData.propertyID; - let propertyName = propertyData.propertyName !== undefined ? propertyData.propertyName : propertyID; - let propertySpaceMode = propertyData.spaceMode !== undefined ? propertyData.spaceMode : PROPERTY_SPACE_MODE.ALL; - let propertyElementID = "property-" + propertyID; - propertyElementID = propertyElementID.replace('.', '-'); - - let elContainer, elLabel; - - if (propertyData.replaceID === undefined) { - // Create subheader, or create new property and append it. - if (propertyType === "sub-header") { - elContainer = createElementFromHTML( - `
${propertyData.label}
`); - } else { - elContainer = document.createElement('div'); - elContainer.setAttribute("id", "div-" + propertyElementID); - elContainer.className = 'property container'; - } - - if (group.twoColumn && propertyData.column !== undefined && propertyData.column !== -1) { - let columnName = group.id + "column" + propertyData.column; - let elColumn = document.getElementById(columnName); - if (!elColumn) { - let columnDivName = group.id + "columnDiv"; - let elColumnDiv = document.getElementById(columnDivName); - if (!elColumnDiv) { - elColumnDiv = document.createElement('div'); - elColumnDiv.className = "two-column"; - elColumnDiv.setAttribute("id", group.id + "columnDiv"); - elGroup.appendChild(elColumnDiv); - } - elColumn = document.createElement('fieldset'); - elColumn.className = "column"; - elColumn.setAttribute("id", columnName); - elColumnDiv.appendChild(elColumn); - } - elColumn.appendChild(elContainer); - } else { - elGroup.appendChild(elContainer); - } - - let labelText = propertyData.label !== undefined ? propertyData.label : ""; - let className = ''; - if (propertyData.indentedLabel || propertyData.showPropertyRule !== undefined) { - className = 'indented'; - } - elLabel = createElementFromHTML( - ``); - elContainer.appendChild(elLabel); - } else { - elContainer = document.getElementById(propertyData.replaceID); - } - - if (elLabel) { - createAppTooltip.registerTooltipElement(elLabel.childNodes[0], propertyID, propertyName); - } - - let elProperty = createElementFromHTML('
'); - elContainer.appendChild(elProperty); - - if (propertyType === 'triple') { - elProperty.className = 'flex-row'; - for (let i = 0; i < propertyData.properties.length; ++i) { - let innerPropertyData = propertyData.properties[i]; - - let elWrapper = createElementFromHTML('
'); - elProperty.appendChild(elWrapper); - - let propertyID = innerPropertyData.propertyID; - let propertyName = innerPropertyData.propertyName !== undefined ? innerPropertyData.propertyName : propertyID; - let propertyElementID = "property-" + propertyID; - propertyElementID = propertyElementID.replace('.', '-'); - - let property = createProperty(innerPropertyData, propertyElementID, propertyName, propertyID, elWrapper); - property.isParticleProperty = group.id.includes("particles"); - property.elContainer = elContainer; - property.spaceMode = propertySpaceMode; - property.group_id = group.id; - - let elLabel = createElementFromHTML(`
${innerPropertyData.label}
`); - createAppTooltip.registerTooltipElement(elLabel, propertyID, propertyName); - - elWrapper.appendChild(elLabel); - - if (property.type !== 'placeholder') { - properties[propertyID] = property; - } - if (innerPropertyData.type === 'number' || innerPropertyData.type === 'number-draggable') { - propertyRangeRequests.push(propertyID); - } - } - } else { - let property = createProperty(propertyData, propertyElementID, propertyName, propertyID, elProperty); - property.isParticleProperty = group.id.includes("particles"); - property.elContainer = elContainer; - property.spaceMode = propertySpaceMode; - property.group_id = group.id; - - if (property.type !== 'placeholder') { - properties[propertyID] = property; - } - if (propertyData.type === 'number' || propertyData.type === 'number-draggable' || - propertyData.type === 'vec2' || propertyData.type === 'vec3' || propertyData.type === 'rect') { - propertyRangeRequests.push(propertyID); - } - - let showPropertyRule = propertyData.showPropertyRule; - if (showPropertyRule !== undefined) { - let dependentProperty = Object.keys(showPropertyRule)[0]; - let dependentPropertyValue = showPropertyRule[dependentProperty]; - if (properties[dependentProperty] === undefined) { - properties[dependentProperty] = {}; - } - if (properties[dependentProperty].showPropertyRules === undefined) { - properties[dependentProperty].showPropertyRules = {}; - } - properties[dependentProperty].showPropertyRules[propertyID] = dependentPropertyValue; - } - } - }); - - elGroups[group.id] = elGroup; - }); - - updateVisibleSpaceModeProperties(); - - if (window.EventBridge !== undefined) { - EventBridge.scriptEventReceived.connect(function(data) { - data = JSON.parse(data); - if (data.type === "server_script_status" && selectedEntityIDs.size === 1) { - let elServerScriptError = document.getElementById("property-serverScripts-error"); - let elServerScriptStatus = document.getElementById("property-serverScripts-status"); - elServerScriptError.value = data.errorInfo; - // If we just set elServerScriptError's display to block or none, we still end up with - // it's parent contributing 21px bottom padding even when elServerScriptError is display:none. - // So set it's parent to block or none - elServerScriptError.parentElement.style.display = data.errorInfo ? "block" : "none"; - if (data.statusRetrieved === false) { - elServerScriptStatus.innerText = "Failed to retrieve status"; - } else if (data.isRunning) { - elServerScriptStatus.innerText = ENTITY_SCRIPT_STATUS[data.status] || data.status; - } else { - elServerScriptStatus.innerText = NOT_RUNNING_SCRIPT_STATUS; - } - } else if (data.type === "update" && data.selections) { - if (data.spaceMode !== undefined) { - currentSpaceMode = data.spaceMode === "local" ? PROPERTY_SPACE_MODE.LOCAL : PROPERTY_SPACE_MODE.WORLD; - } - handleEntitySelectionUpdate(data.selections, data.isPropertiesToolUpdate); - } else if (data.type === 'tooltipsReply') { - createAppTooltip.setIsEnabled(!data.hmdActive); - createAppTooltip.setTooltipData(data.tooltips); - } else if (data.type === 'hmdActiveChanged') { - createAppTooltip.setIsEnabled(!data.hmdActive); - } else if (data.type === 'setSpaceMode') { - currentSpaceMode = data.spaceMode === "local" ? PROPERTY_SPACE_MODE.LOCAL : PROPERTY_SPACE_MODE.WORLD; - updateVisibleSpaceModeProperties(); - } else if (data.type === 'propertyRangeReply') { - let propertyRanges = data.propertyRanges; - for (let property in propertyRanges) { - let propertyRange = propertyRanges[property]; - if (propertyRange !== undefined) { - let propertyData = properties[property].data; - let multiplier = propertyData.multiplier; - if (propertyData.min === undefined && propertyRange.minimum !== "") { - propertyData.min = propertyRange.minimum; - if (multiplier !== undefined) { - propertyData.min /= multiplier; - } - } - if (propertyData.max === undefined && propertyRange.maximum !== "") { - propertyData.max = propertyRange.maximum; - if (multiplier !== undefined) { - propertyData.max /= multiplier; - } - } - switch (propertyData.type) { - case 'number': - updateNumberMinMax(properties[property]); - break; - case 'number-draggable': - updateNumberDraggableMinMax(properties[property]); - break; - case 'vec3': - case 'vec2': - updateVectorMinMax(properties[property]); - break; - case 'rect': - updateRectMinMax(properties[property]); - break; - } - } - } - } else if (data.type === 'materialTargetReply') { - if (data.entityID === getFirstSelectedID()) { - setMaterialTargetData(data.materialTargetData); - } - } else if (data.type === 'zoneListRequest') { - zonesList = data.zones; - updateAllZoneSelect(); - } - }); - - // Request tooltips and property ranges as soon as we can process a reply: - EventBridge.emitWebEvent(JSON.stringify({ type: 'tooltipsRequest' })); - EventBridge.emitWebEvent(JSON.stringify({ type: 'propertyRangeRequest', properties: propertyRangeRequests })); - } - - // Server Script Status - let elServerScriptStatusOuter = document.getElementById('div-property-serverScriptStatus'); - let elServerScriptStatusContainer = document.getElementById('div-property-serverScriptStatus').childNodes[1]; - let serverScriptStatusElementID = 'property-serverScripts-status'; - createAppTooltip.registerTooltipElement(elServerScriptStatusOuter.childNodes[0], "serverScriptsStatus"); - let elServerScriptStatus = document.createElement('div'); - elServerScriptStatus.setAttribute("id", serverScriptStatusElementID); - elServerScriptStatusContainer.appendChild(elServerScriptStatus); - - // Server Script Error - let elServerScripts = getPropertyInputElement("serverScripts"); - let elDiv = document.createElement('div'); - elDiv.className = "property"; - let elServerScriptError = document.createElement('textarea'); - let serverScriptErrorElementID = 'property-serverScripts-error'; - elServerScriptError.setAttribute("id", serverScriptErrorElementID); - elDiv.appendChild(elServerScriptError); - elServerScriptStatusContainer.appendChild(elDiv); - - let elScript = getPropertyInputElement("script"); - elScript.parentNode.className = "url refresh"; - elServerScripts.parentNode.className = "url refresh"; - - // User Data - let userDataProperty = properties["userData"]; - let elUserData = userDataProperty.elInput; - let userDataElementID = userDataProperty.elementID; - elDiv = elUserData.parentNode; - let elUserDataEditor = document.createElement('div'); - elUserDataEditor.setAttribute("id", userDataElementID + "-editor"); - let elUserDataEditorStatus = document.createElement('div'); - elUserDataEditorStatus.setAttribute("id", userDataElementID + "-editorStatus"); - let elUserDataSaved = document.createElement('span'); - elUserDataSaved.setAttribute("id", userDataElementID + "-saved"); - elUserDataSaved.innerText = "Saved!"; - elDiv.childNodes[JSON_EDITOR_ROW_DIV_INDEX].appendChild(elUserDataSaved); - elDiv.insertBefore(elUserDataEditor, elUserData); - elDiv.insertBefore(elUserDataEditorStatus, elUserData); - - // Material Data - let materialDataProperty = properties["materialData"]; - let elMaterialData = materialDataProperty.elInput; - let materialDataElementID = materialDataProperty.elementID; - elDiv = elMaterialData.parentNode; - let elMaterialDataEditor = document.createElement('div'); - elMaterialDataEditor.setAttribute("id", materialDataElementID + "-editor"); - let elMaterialDataEditorStatus = document.createElement('div'); - elMaterialDataEditorStatus.setAttribute("id", materialDataElementID + "-editorStatus"); - let elMaterialDataSaved = document.createElement('span'); - elMaterialDataSaved.setAttribute("id", materialDataElementID + "-saved"); - elMaterialDataSaved.innerText = "Saved!"; - elDiv.childNodes[JSON_EDITOR_ROW_DIV_INDEX].appendChild(elMaterialDataSaved); - elDiv.insertBefore(elMaterialDataEditor, elMaterialData); - elDiv.insertBefore(elMaterialDataEditorStatus, elMaterialData); - - // Textarea scrollbars - let elTextareas = document.getElementsByTagName("TEXTAREA"); - - let textareaOnChangeEvent = function(event) { - setTextareaScrolling(event.target); - }; - - for (let textAreaIndex = 0, numTextAreas = elTextareas.length; textAreaIndex < numTextAreas; ++textAreaIndex) { - let curTextAreaElement = elTextareas[textAreaIndex]; - setTextareaScrolling(curTextAreaElement); - curTextAreaElement.addEventListener("input", textareaOnChangeEvent, false); - curTextAreaElement.addEventListener("change", textareaOnChangeEvent, false); - /* FIXME: Detect and update textarea scrolling attribute on resize. Unfortunately textarea doesn't have a resize - event; mouseup is a partial stand-in but doesn't handle resizing if mouse moves outside textarea rectangle. */ - curTextAreaElement.addEventListener("mouseup", textareaOnChangeEvent, false); - } - - // Dropdowns - // For each dropdown the following replacement is created in place of the original dropdown... - // Structure created: - //
- //
display textcarat
- //
- //