From 36cecef1667f0897f802eaf2fdd45ac0c4399e01 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 26 Mar 2019 13:31:55 -0700 Subject: [PATCH 01/49] Support explicit timestamps in tracing, conditional event output based on duration --- libraries/shared/src/Profile.cpp | 27 +++++++++++++++++++++++++-- libraries/shared/src/Profile.h | 21 ++++++++++++++++++--- libraries/shared/src/Trace.cpp | 16 ++++++++++++++-- libraries/shared/src/Trace.h | 17 +++++++++++++++++ 4 files changed, 74 insertions(+), 7 deletions(-) diff --git a/libraries/shared/src/Profile.cpp b/libraries/shared/src/Profile.cpp index 272538e26d..018636ad5a 100644 --- a/libraries/shared/src/Profile.cpp +++ b/libraries/shared/src/Profile.cpp @@ -7,6 +7,7 @@ // #include "Profile.h" +#include Q_LOGGING_CATEGORY(trace_app, "trace.app") Q_LOGGING_CATEGORY(trace_app_detail, "trace.app.detail") @@ -41,14 +42,22 @@ static bool tracingEnabled() { return DependencyManager::isSet() && DependencyManager::get()->isEnabled(); } -Duration::Duration(const QLoggingCategory& category, const QString& name, uint32_t argbColor, uint64_t payload, const QVariantMap& baseArgs) : _name(name), _category(category) { +DurationBase::DurationBase(const QLoggingCategory& category, const QString& name) : _name(name), _category(category) { +} + +Duration::Duration(const QLoggingCategory& category, + const QString& name, + uint32_t argbColor, + uint64_t payload, + const QVariantMap& baseArgs) : + DurationBase(category, name) { if (tracingEnabled() && category.isDebugEnabled()) { QVariantMap args = baseArgs; args["nv_payload"] = QVariant::fromValue(payload); tracing::traceEvent(_category, _name, tracing::DurationBegin, "", args); #if defined(NSIGHT_TRACING) - nvtxEventAttributes_t eventAttrib { 0 }; + nvtxEventAttributes_t eventAttrib{ 0 }; eventAttrib.version = NVTX_VERSION; eventAttrib.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE; eventAttrib.colorType = NVTX_COLOR_ARGB; @@ -98,3 +107,17 @@ void Duration::endRange(const QLoggingCategory& category, uint64_t rangeId) { #endif } +ConditionalDuration::ConditionalDuration(const QLoggingCategory& category, const QString& name, uint32_t minTime) : + DurationBase(category, name), _startTime(tracing::Tracer::now()), _minTime(minTime * USECS_PER_MSEC) { +} + +ConditionalDuration::~ConditionalDuration() { + if (tracingEnabled() && _category.isDebugEnabled()) { + auto endTime = tracing::Tracer::now(); + auto duration = endTime - _startTime; + if (duration >= _minTime) { + tracing::traceEvent(_category, _startTime, _name, tracing::DurationBegin); + tracing::traceEvent(_category, endTime, _name, tracing::DurationEnd); + } + } +} diff --git a/libraries/shared/src/Profile.h b/libraries/shared/src/Profile.h index dc2ed6e754..e7084b4f79 100644 --- a/libraries/shared/src/Profile.h +++ b/libraries/shared/src/Profile.h @@ -37,17 +37,31 @@ Q_DECLARE_LOGGING_CATEGORY(trace_startup) Q_DECLARE_LOGGING_CATEGORY(trace_workload) Q_DECLARE_LOGGING_CATEGORY(trace_baker) -class Duration { +class DurationBase { + +protected: + DurationBase(const QLoggingCategory& category, const QString& name); + const QString _name; + const QLoggingCategory& _category; +}; + +class Duration : public DurationBase { public: Duration(const QLoggingCategory& category, const QString& name, uint32_t argbColor = 0xff0000ff, uint64_t payload = 0, const QVariantMap& args = QVariantMap()); ~Duration(); static uint64_t beginRange(const QLoggingCategory& category, const char* name, uint32_t argbColor); static void endRange(const QLoggingCategory& category, uint64_t rangeId); +}; + +class ConditionalDuration : public DurationBase { +public: + ConditionalDuration(const QLoggingCategory& category, const QString& name, uint32_t minTime); + ~ConditionalDuration(); private: - QString _name; - const QLoggingCategory& _category; + const int64_t _startTime; + const int64_t _minTime; }; @@ -95,6 +109,7 @@ inline void metadata(const QString& metadataType, const QVariantMap& args) { } #define PROFILE_RANGE(category, name) Duration profileRangeThis(trace_##category(), name); +#define PROFILE_RANGE_IF_LONGER(category, name, ms) ConditionalDuration profileRangeThis(trace_##category(), name, ms); #define PROFILE_RANGE_EX(category, name, argbColor, payload, ...) Duration profileRangeThis(trace_##category(), name, argbColor, (uint64_t)payload, ##__VA_ARGS__); #define PROFILE_RANGE_BEGIN(category, rangeId, name, argbColor) rangeId = Duration::beginRange(trace_##category(), name, argbColor) #define PROFILE_RANGE_END(category, rangeId) Duration::endRange(trace_##category(), rangeId) diff --git a/libraries/shared/src/Trace.cpp b/libraries/shared/src/Trace.cpp index 3f6a2dd643..e9e77b55ae 100644 --- a/libraries/shared/src/Trace.cpp +++ b/libraries/shared/src/Trace.cpp @@ -176,6 +176,10 @@ void Tracer::serialize(const QString& filename) { #endif } +int64_t Tracer::now() { + return std::chrono::duration_cast(p_high_resolution_clock::now().time_since_epoch()).count(); +} + void Tracer::traceEvent(const QLoggingCategory& category, const QString& name, EventType type, qint64 timestamp, qint64 processID, qint64 threadID, @@ -226,9 +230,17 @@ void Tracer::traceEvent(const QLoggingCategory& category, return; } - auto timestamp = std::chrono::duration_cast(p_high_resolution_clock::now().time_since_epoch()).count(); + traceEvent(category, name, type, now(), id, args, extra); +} + +void Tracer::traceEvent(const QLoggingCategory& category, + const QString& name, EventType type, int64_t timestamp, const QString& id, + const QVariantMap& args, const QVariantMap& extra) { + if (!_enabled && type != Metadata) { + return; + } + auto processID = QCoreApplication::applicationPid(); auto threadID = int64_t(QThread::currentThreadId()); - traceEvent(category, name, type, timestamp, processID, threadID, id, args, extra); } diff --git a/libraries/shared/src/Trace.h b/libraries/shared/src/Trace.h index 1e1326968f..917530d6ca 100644 --- a/libraries/shared/src/Trace.h +++ b/libraries/shared/src/Trace.h @@ -78,11 +78,18 @@ struct TraceEvent { class Tracer : public Dependency { public: + static int64_t now(); void traceEvent(const QLoggingCategory& category, const QString& name, EventType type, const QString& id = "", const QVariantMap& args = QVariantMap(), const QVariantMap& extra = QVariantMap()); + void traceEvent(const QLoggingCategory& category, + const QString& name, EventType type, + int64_t timestamp, + const QString& id = "", + const QVariantMap& args = QVariantMap(), const QVariantMap& extra = QVariantMap()); + void startTracing(); void stopTracing(); void serialize(const QString& file); @@ -101,6 +108,16 @@ private: std::mutex _eventsMutex; }; +inline void traceEvent(const QLoggingCategory& category, int64_t timestamp, const QString& name, EventType type, const QString& id = "", const QVariantMap& args = {}, const QVariantMap& extra = {}) { + if (!DependencyManager::isSet()) { + return; + } + const auto& tracer = DependencyManager::get(); + if (tracer) { + tracer->traceEvent(category, name, type, timestamp, id, args, extra); + } +} + inline void traceEvent(const QLoggingCategory& category, const QString& name, EventType type, const QString& id = "", const QVariantMap& args = {}, const QVariantMap& extra = {}) { if (!DependencyManager::isSet()) { return; From 413091fed3d0c72f773087fc43d2c90fb0c1c3f0 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 26 Mar 2019 13:32:29 -0700 Subject: [PATCH 02/49] Trace application notification handler events if longer than 2 ms --- interface/src/Application.cpp | 9 +++++++++ interface/src/Application.h | 1 + 2 files changed, 10 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 635932ea1c..afe109daf3 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3961,6 +3961,15 @@ static void dumpEventQueue(QThread* thread) { } #endif // DEBUG_EVENT_QUEUE +bool Application::notify(QObject * object, QEvent * event) { + if (thread() == QThread::currentThread()) { + PROFILE_RANGE_IF_LONGER(app, "notify", 2) + return QApplication::notify(object, event); + } + + return QApplication::notify(object, event); +} + bool Application::event(QEvent* event) { if (_aboutToQuit) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 762ac9585a..545c223837 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -156,6 +156,7 @@ public: void updateCamera(RenderArgs& renderArgs, float deltaTime); void resizeGL(); + bool notify(QObject *, QEvent *) override; bool event(QEvent* event) override; bool eventFilter(QObject* object, QEvent* event) override; From 0c78c8fd879f9f03b0ede0429202d0e9f387d57f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 26 Mar 2019 13:38:52 -0700 Subject: [PATCH 03/49] Disable per-frame context switch on present thread --- .../src/display-plugins/OpenGLDisplayPlugin.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 5bc84acc6a..2a29325581 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -109,7 +109,6 @@ public: Q_ASSERT(_context); _context->makeCurrent(); CHECK_GL_ERROR(); - _context->doneCurrent(); while (!_shutdown) { if (_pendingOtherThreadOperation) { PROFILE_RANGE(render, "MainThreadOp") @@ -129,6 +128,7 @@ public: Lock lock(_mutex); _condition.wait(lock, [&] { return _finishedOtherThreadOperation; }); } + _context->makeCurrent(); } // Check for a new display plugin @@ -140,18 +140,16 @@ public: if (newPlugin != currentPlugin) { // Deactivate the old plugin if (currentPlugin != nullptr) { - _context->makeCurrent(); currentPlugin->uncustomizeContext(); CHECK_GL_ERROR(); - _context->doneCurrent(); + // Force completion of all pending GL commands + glFinish(); } if (newPlugin) { bool hasVsync = true; QThread::setPriority(newPlugin->getPresentPriority()); bool wantVsync = newPlugin->wantVsync(); - _context->makeCurrent(); - CHECK_GL_ERROR(); #if defined(Q_OS_MAC) newPlugin->swapBuffers(); #endif @@ -163,7 +161,8 @@ public: newPlugin->setVsyncEnabled(hasVsync); newPlugin->customizeContext(); CHECK_GL_ERROR(); - _context->doneCurrent(); + // Force completion of all pending GL commands + glFinish(); } currentPlugin = newPlugin; _newPluginQueue.pop(); @@ -180,7 +179,6 @@ public: } // Execute the frame and present it to the display device. - _context->makeCurrent(); { PROFILE_RANGE(render, "PluginPresent") gl::globalLock(); @@ -188,9 +186,9 @@ public: gl::globalRelease(false); CHECK_GL_ERROR(); } - _context->doneCurrent(); } + _context->doneCurrent(); Lock lock(_mutex); _context->moveToThread(qApp->thread()); _shutdown = false; From 81f28b3b2cdf6ba81b6ccc5f66fc6afc5278e9a9 Mon Sep 17 00:00:00 2001 From: Oren Hurvitz Date: Thu, 28 Mar 2019 09:38:28 +0200 Subject: [PATCH 04/49] Fixed ray intersections with Local entities. Previously, rays *always* intersected with Local entities, regardless of whether the pick filter requested to hit on collidable or noncollidable entities. But Local entities are always collisionless, so a pick filter for collidable entities shouldn't intersect with Local entities. --- libraries/entities/src/EntityTreeElement.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index aab98adb52..51e3da0dca 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -148,13 +148,20 @@ bool EntityTreeElement::checkFilterSettings(const EntityItemPointer& entity, Pic (!searchFilter.doesPickLocalEntities() && hostType == entity::HostType::LOCAL)) { return false; } - // We only check the collidable filters for non-local entities, because local entities are always collisionless - bool collidable = !entity->getCollisionless() && (entity->getShapeType() != SHAPE_TYPE_NONE); - if (hostType != entity::HostType::LOCAL) { - if ((collidable && !searchFilter.doesPickCollidable()) || (!collidable && !searchFilter.doesPickNonCollidable())) { - return false; - } + + bool collidable; + if (hostType == entity::HostType::LOCAL) { + // Local entities are always collisionless + collidable = false; } + else { + collidable = !entity->getCollisionless() && (entity->getShapeType() != SHAPE_TYPE_NONE); + } + + if ((collidable && !searchFilter.doesPickCollidable()) || (!collidable && !searchFilter.doesPickNonCollidable())) { + return false; + } + return true; } From 8e201f4801dbfdd95486d1e28dce02a8fbb85acb Mon Sep 17 00:00:00 2001 From: Oren Hurvitz Date: Wed, 27 Feb 2019 10:23:57 +0200 Subject: [PATCH 05/49] Fixed race condition during initialization of the Desktop. The Application constructor must setup event handlers for events emitted when the Desktop is constructed *before* calling initializeUi(). Otherwise, sometimes these event handlers won't be called. When this problem happened (usually on slow machines), Interface would start but neither the Desktop nor the Login Screen would appear. --- interface/src/Application.cpp | 56 +++++++++++++++++------------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 635932ea1c..1efd9cc461 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1446,6 +1446,34 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _overlays.init(); // do this before scripts load DependencyManager::set(); + auto offscreenUi = getOffscreenUI(); + connect(offscreenUi.data(), &OffscreenUi::desktopReady, []() { + // Now that we've loaded the menu and thus switched to the previous display plugin + // we can unlock the desktop repositioning code, since all the positions will be + // relative to the desktop size for this plugin + auto offscreenUi = getOffscreenUI(); + auto desktop = offscreenUi->getDesktop(); + if (desktop) { + desktop->setProperty("repositionLocked", false); + } + }); + + connect(offscreenUi.data(), &OffscreenUi::keyboardFocusActive, [this]() { +#if !defined(Q_OS_ANDROID) && !defined(DISABLE_QML) + // Do not show login dialog if requested not to on the command line + QString hifiNoLoginCommandLineKey = QString("--").append(HIFI_NO_LOGIN_COMMAND_LINE_KEY); + int index = arguments().indexOf(hifiNoLoginCommandLineKey); + if (index != -1) { + resumeAfterLoginDialogActionTaken(); + return; + } + + showLoginScreen(); +#else + resumeAfterLoginDialogActionTaken(); +#endif + }); + // Initialize the user interface and menu system // Needs to happen AFTER the render engine initialization to access its configuration initializeUi(); @@ -1791,34 +1819,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo updateVerboseLogging(); - // Now that we've loaded the menu and thus switched to the previous display plugin - // we can unlock the desktop repositioning code, since all the positions will be - // relative to the desktop size for this plugin - auto offscreenUi = getOffscreenUI(); - connect(offscreenUi.data(), &OffscreenUi::desktopReady, []() { - auto offscreenUi = getOffscreenUI(); - auto desktop = offscreenUi->getDesktop(); - if (desktop) { - desktop->setProperty("repositionLocked", false); - } - }); - - connect(offscreenUi.data(), &OffscreenUi::keyboardFocusActive, [this]() { -#if !defined(Q_OS_ANDROID) && !defined(DISABLE_QML) - // Do not show login dialog if requested not to on the command line - QString hifiNoLoginCommandLineKey = QString("--").append(HIFI_NO_LOGIN_COMMAND_LINE_KEY); - int index = arguments().indexOf(hifiNoLoginCommandLineKey); - if (index != -1) { - resumeAfterLoginDialogActionTaken(); - return; - } - - showLoginScreen(); -#else - resumeAfterLoginDialogActionTaken(); -#endif - }); - // Make sure we don't time out during slow operations at startup updateHeartbeat(); QTimer* settingsTimer = new QTimer(); From 49165056c9e7de4dbb7b394ac5ad9918b4f4b880 Mon Sep 17 00:00:00 2001 From: Oren Hurvitz Date: Fri, 1 Mar 2019 15:39:06 +0200 Subject: [PATCH 06/49] Fixed a race condition that sometimes caused the main menus not to appear. When Interface starts, it first calls pauseUntilLoginDetermined(), and later resumeAfterLoginDialogActionTaken(). But on rare occasions these functions are called in the reverse order, and this caused Interface to remain in the "paused" state. Now we check for this case, and abort pauseUntilLoginDetermined() if it happens. This has only happened to me when running Interface immediately after rebuilding it (in Release mode). --- interface/src/Application.cpp | 9 +++++++++ interface/src/Application.h | 3 +++ 2 files changed, 12 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1efd9cc461..a6ab9c9782 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5414,6 +5414,13 @@ void Application::pauseUntilLoginDetermined() { return; } + if (_resumeAfterLoginDialogActionTakenWasCalled) { + // This happens occasionally (though not often): resumeAfterLoginDialogActionTaken() has already been called. + // We must abort this method, otherwise Interface will remain in the "Paused" state permanently. + // E.g., the menus "Edit", "View", etc. will not appear. + return; + } + auto myAvatar = getMyAvatar(); _previousAvatarTargetScale = myAvatar->getTargetScale(); _previousAvatarSkeletonModel = myAvatar->getSkeletonModelURL().toString(); @@ -5528,6 +5535,8 @@ void Application::resumeAfterLoginDialogActionTaken() { menu->getMenu("Developer")->setVisible(_developerMenuVisible); _myCamera.setMode(_previousCameraMode); cameraModeChanged(); + + _resumeAfterLoginDialogActionTakenWasCalled = true; } void Application::loadAvatarScripts(const QVector& urls) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 762ac9585a..421797d3a7 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -802,5 +802,8 @@ private: bool _showTrackedObjects { false }; bool _prevShowTrackedObjects { false }; + + // Whether resumeAfterLoginDialogActionTaken() has been called + bool _resumeAfterLoginDialogActionTakenWasCalled { false }; }; #endif // hifi_Application_h From 7a8b7c095bd139b413f2f23259a9c9759413d585 Mon Sep 17 00:00:00 2001 From: Oren Hurvitz Date: Sun, 10 Mar 2019 15:20:59 +0200 Subject: [PATCH 07/49] Fixed a race condition related to the keyboard when starting Interface. These two things need to happen in the following order: 1. Initialize the input plugins (including the keyboard) 2. Start the scripts That's because the scripts try to use the keyboard (e.g., in edit.js), and if it's not ready yet then they fail, and then Interface doesn't work right (e.g., the Create button doesn't work). In order to ensure that these things happen in the correct order, we must make sure that resumeAfterLoginDialogActionTaken() (which starts the scripts) is called only after everything else in the Interface constructor has finished. Usually this happens correctly, but occasionally resumeAfterLoginDialogActionTaken() is called too soon. This commit makes sure that even in that case, we'll postpone calling resumeAfterLoginDialogActionTaken() until the Interface constructor has finished. --- interface/src/Application.cpp | 21 ++++++++++++--------- interface/src/Application.h | 6 +++--- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index a6ab9c9782..f6af20e180 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5414,13 +5414,6 @@ void Application::pauseUntilLoginDetermined() { return; } - if (_resumeAfterLoginDialogActionTakenWasCalled) { - // This happens occasionally (though not often): resumeAfterLoginDialogActionTaken() has already been called. - // We must abort this method, otherwise Interface will remain in the "Paused" state permanently. - // E.g., the menus "Edit", "View", etc. will not appear. - return; - } - auto myAvatar = getMyAvatar(); _previousAvatarTargetScale = myAvatar->getTargetScale(); _previousAvatarSkeletonModel = myAvatar->getSkeletonModelURL().toString(); @@ -5459,6 +5452,13 @@ void Application::pauseUntilLoginDetermined() { // disconnect domain handler. nodeList->getDomainHandler().disconnect(); + // From now on, it's permissible to call resumeAfterLoginDialogActionTaken() + _resumeAfterLoginDialogActionTaken_SafeToRun = true; + + if (_resumeAfterLoginDialogActionTaken_WasPostponed) { + // resumeAfterLoginDialogActionTaken() was already called, but it aborted. Now it's safe to call it again. + resumeAfterLoginDialogActionTaken(); + } } void Application::resumeAfterLoginDialogActionTaken() { @@ -5467,6 +5467,11 @@ void Application::resumeAfterLoginDialogActionTaken() { return; } + if (!_resumeAfterLoginDialogActionTaken_SafeToRun) { + _resumeAfterLoginDialogActionTaken_WasPostponed = true; + return; + } + if (!isHMDMode() && getDesktopTabletBecomesToolbarSetting()) { auto toolbar = DependencyManager::get()->getToolbar("com.highfidelity.interface.toolbar.system"); toolbar->writeProperty("visible", true); @@ -5535,8 +5540,6 @@ void Application::resumeAfterLoginDialogActionTaken() { menu->getMenu("Developer")->setVisible(_developerMenuVisible); _myCamera.setMode(_previousCameraMode); cameraModeChanged(); - - _resumeAfterLoginDialogActionTakenWasCalled = true; } void Application::loadAvatarScripts(const QVector& urls) { diff --git a/interface/src/Application.h b/interface/src/Application.h index 421797d3a7..a674e313a9 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -1,4 +1,4 @@ -// +// // Application.h // interface/src // @@ -803,7 +803,7 @@ private: bool _showTrackedObjects { false }; bool _prevShowTrackedObjects { false }; - // Whether resumeAfterLoginDialogActionTaken() has been called - bool _resumeAfterLoginDialogActionTakenWasCalled { false }; + bool _resumeAfterLoginDialogActionTaken_WasPostponed { false }; + bool _resumeAfterLoginDialogActionTaken_SafeToRun { false }; }; #endif // hifi_Application_h From a00c9893a1f11895b66732bfa865efb65f864e2c Mon Sep 17 00:00:00 2001 From: Oren Hurvitz Date: Thu, 28 Mar 2019 16:06:31 +0200 Subject: [PATCH 08/49] Eliminated some JavaScript warnings due to missing commas --- scripts/tutorials/makeBlocks.js | 2 +- .../DomainContent/Toybox/basketball/createHoop.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/tutorials/makeBlocks.js b/scripts/tutorials/makeBlocks.js index c7d0425863..d750689f4d 100644 --- a/scripts/tutorials/makeBlocks.js +++ b/scripts/tutorials/makeBlocks.js @@ -56,7 +56,7 @@ grab: { grabbable: true }, cloneable: true, cloneLifetime: LIFETIME, - cloneLimit: 9999 + cloneLimit: 9999, position: Vec3.sum(MyAvatar.position, Vec3.sum(forwardOffset, forwardVector)), color: newColor(), script: SCRIPT_URL diff --git a/unpublishedScripts/DomainContent/Toybox/basketball/createHoop.js b/unpublishedScripts/DomainContent/Toybox/basketball/createHoop.js index cc67a4d479..8a3f9bb6d7 100644 --- a/unpublishedScripts/DomainContent/Toybox/basketball/createHoop.js +++ b/unpublishedScripts/DomainContent/Toybox/basketball/createHoop.js @@ -38,6 +38,6 @@ var hoop = Entities.addEntity({ grabbableKey: { grabbable: false } - }) + }), compoundShapeURL: hoopCollisionHullURL }); \ No newline at end of file From 32406b839926d5b12ad1e3a3b942ee88161947ae Mon Sep 17 00:00:00 2001 From: Oren Hurvitz Date: Thu, 28 Mar 2019 19:46:51 +0200 Subject: [PATCH 09/49] Fixed Safe Landing interaction with Local entities. Local entities are collisionless, so they shouldn't affect Safe Landing since it specifies that it only wants to find COLLIDABLE entities. However, due to the requirement to support legacy behavior, picks for COLLIDABLE entities *do* intersect Local entities. In order to prevent this, we have to explicitly request only intersections with Domain or Avatar entities. For more information about this, see this Pull Request: https://github.com/highfidelity/hifi/pull/15282 --- interface/src/avatar/MyAvatar.cpp | 3 ++- libraries/entities/src/EntityTreeElement.cpp | 23 +++++++++----------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index f2d8730ebb..42f9923652 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -3742,7 +3742,8 @@ bool MyAvatar::requiresSafeLanding(const glm::vec3& positionIn, glm::vec3& bette // See https://highfidelity.fogbugz.com/f/cases/5003/findRayIntersection-has-option-to-use-collidableOnly-but-doesn-t-actually-use-colliders QVariantMap extraInfo; EntityItemID entityID = entityTree->evalRayIntersection(startPointIn, directionIn, include, ignore, - PickFilter(PickFilter::getBitMask(PickFilter::FlagBit::COLLIDABLE) | PickFilter::getBitMask(PickFilter::FlagBit::PRECISE)), + PickFilter(PickFilter::getBitMask(PickFilter::FlagBit::COLLIDABLE) | PickFilter::getBitMask(PickFilter::FlagBit::PRECISE) + | PickFilter::getBitMask(PickFilter::FlagBit::DOMAIN_ENTITIES) | PickFilter::getBitMask(PickFilter::FlagBit::AVATAR_ENTITIES)), // exclude Local entities element, distance, face, normalOut, extraInfo, lockType, accurateResult); if (entityID.isNull()) { return false; diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 51e3da0dca..60eaafc0dd 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -148,20 +148,17 @@ bool EntityTreeElement::checkFilterSettings(const EntityItemPointer& entity, Pic (!searchFilter.doesPickLocalEntities() && hostType == entity::HostType::LOCAL)) { return false; } - - bool collidable; - if (hostType == entity::HostType::LOCAL) { - // Local entities are always collisionless - collidable = false; + // We only check the collidable filters for non-local entities, because local entities are always collisionless, + // but picks always include COLLIDABLE (see PickScriptingInterface::getPickFilter()), so if we were to respect + // the getCollisionless() property of Local entities then we would *never* intersect them in a pick. + // An unfortunate side effect of the following code is that Local entities are intersected even if the + // pick explicitly requested only COLLIDABLE entities (but, again, Local entities are always collisionless). + if (hostType != entity::HostType::LOCAL) { + bool collidable = !entity->getCollisionless() && (entity->getShapeType() != SHAPE_TYPE_NONE); + if ((collidable && !searchFilter.doesPickCollidable()) || (!collidable && !searchFilter.doesPickNonCollidable())) { + return false; + } } - else { - collidable = !entity->getCollisionless() && (entity->getShapeType() != SHAPE_TYPE_NONE); - } - - if ((collidable && !searchFilter.doesPickCollidable()) || (!collidable && !searchFilter.doesPickNonCollidable())) { - return false; - } - return true; } From d963ec30053d708c2e64a1137eb0549360f24770 Mon Sep 17 00:00:00 2001 From: Angus Antley Date: Thu, 28 Mar 2019 11:27:26 -0700 Subject: [PATCH 10/49] can now override the eye rotation when procedural eye movement is turned off --- interface/src/avatar/MySkeletonModel.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 55c29b66c1..df46b428e7 100755 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -334,7 +334,9 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { eyeParams.leftEyeJointIndex = _rig.indexOfJoint("LeftEye"); eyeParams.rightEyeJointIndex = _rig.indexOfJoint("RightEye"); - _rig.updateFromEyeParameters(eyeParams); + if (_owningAvatar->getHasProceduralEyeFaceMovement()) { + _rig.updateFromEyeParameters(eyeParams); + } updateFingers(); } From ebc73bebc5bcb23623cca787656de0c587f06740 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Sat, 30 Mar 2019 15:39:33 -0700 Subject: [PATCH 11/49] Ambisonic renderer using 4x4 rotate/scale matrix to implement gain crossfade --- libraries/audio/src/AudioFOA.cpp | 96 ++++++++++++++-------- libraries/audio/src/AudioFOA.h | 11 +-- libraries/audio/src/avx2/AudioFOA_avx2.cpp | 47 ++++++----- 3 files changed, 91 insertions(+), 63 deletions(-) diff --git a/libraries/audio/src/AudioFOA.cpp b/libraries/audio/src/AudioFOA.cpp index 30d29b72b7..ddcdf7f250 100644 --- a/libraries/audio/src/AudioFOA.cpp +++ b/libraries/audio/src/AudioFOA.cpp @@ -882,14 +882,16 @@ static void convertInput_ref(int16_t* src, float *dst[4], float gain, int numFra #endif -// in-place rotation of the soundfield -// crossfade between old and new rotation, to prevent artifacts -static void rotate_3x3_ref(float* buf[4], const float m0[3][3], const float m1[3][3], const float* win, int numFrames) { +// in-place rotation and scaling of the soundfield +// crossfade between old and new matrix, to prevent artifacts +static void rotate_4x4_ref(float* buf[4], const float m0[4][4], const float m1[4][4], const float* win, int numFrames) { - const float md[3][3] = { - { m0[0][0] - m1[0][0], m0[0][1] - m1[0][1], m0[0][2] - m1[0][2] }, - { m0[1][0] - m1[1][0], m0[1][1] - m1[1][1], m0[1][2] - m1[1][2] }, - { m0[2][0] - m1[2][0], m0[2][1] - m1[2][1], m0[2][2] - m1[2][2] }, + // matrix difference + const float md[4][4] = { + { m0[0][0] - m1[0][0], m0[0][1] - m1[0][1], m0[0][2] - m1[0][2], m0[0][3] - m1[0][3] }, + { m0[1][0] - m1[1][0], m0[1][1] - m1[1][1], m0[1][2] - m1[1][2], m0[1][3] - m1[1][3] }, + { m0[2][0] - m1[2][0], m0[2][1] - m1[2][1], m0[2][2] - m1[2][2], m0[2][3] - m1[2][3] }, + { m0[3][0] - m1[3][0], m0[3][1] - m1[3][1], m0[3][2] - m1[3][2], m0[3][3] - m1[3][3] }, }; for (int i = 0; i < numFrames; i++) { @@ -898,22 +900,27 @@ static void rotate_3x3_ref(float* buf[4], const float m0[3][3], const float m1[3 // interpolate the matrix float m00 = m1[0][0] + frac * md[0][0]; - float m10 = m1[1][0] + frac * md[1][0]; - float m20 = m1[2][0] + frac * md[2][0]; - float m01 = m1[0][1] + frac * md[0][1]; float m11 = m1[1][1] + frac * md[1][1]; float m21 = m1[2][1] + frac * md[2][1]; + float m31 = m1[3][1] + frac * md[3][1]; - float m02 = m1[0][2] + frac * md[0][2]; float m12 = m1[1][2] + frac * md[1][2]; float m22 = m1[2][2] + frac * md[2][2]; + float m32 = m1[3][2] + frac * md[3][2]; + + float m13 = m1[1][3] + frac * md[1][3]; + float m23 = m1[2][3] + frac * md[2][3]; + float m33 = m1[3][3] + frac * md[3][3]; // matrix multiply - float x = m00 * buf[1][i] + m01 * buf[2][i] + m02 * buf[3][i]; - float y = m10 * buf[1][i] + m11 * buf[2][i] + m12 * buf[3][i]; - float z = m20 * buf[1][i] + m21 * buf[2][i] + m22 * buf[3][i]; + float w = m00 * buf[0][i]; + float x = m11 * buf[1][i] + m12 * buf[2][i] + m13 * buf[3][i]; + float y = m21 * buf[1][i] + m22 * buf[2][i] + m23 * buf[3][i]; + float z = m31 * buf[1][i] + m32 * buf[2][i] + m33 * buf[3][i]; + + buf[0][i] = w; buf[1][i] = x; buf[2][i] = y; buf[3][i] = z; @@ -932,7 +939,7 @@ void rfft512_AVX2(float buf[512]); void rifft512_AVX2(float buf[512]); void rfft512_cmadd_1X2_AVX2(const float src[512], const float coef0[512], const float coef1[512], float dst0[512], float dst1[512]); void convertInput_AVX2(int16_t* src, float *dst[4], float gain, int numFrames); -void rotate_3x3_AVX2(float* buf[4], const float m0[3][3], const float m1[3][3], const float* win, int numFrames); +void rotate_4x4_AVX2(float* buf[4], const float m0[4][4], const float m1[4][4], const float* win, int numFrames); static void rfft512(float buf[512]) { static auto f = cpuSupportsAVX2() ? rfft512_AVX2 : rfft512_ref; @@ -954,8 +961,8 @@ static void convertInput(int16_t* src, float *dst[4], float gain, int numFrames) (*f)(src, dst, gain, numFrames); // dispatch } -static void rotate_3x3(float* buf[4], const float m0[3][3], const float m1[3][3], const float* win, int numFrames) { - static auto f = cpuSupportsAVX2() ? rotate_3x3_AVX2 : rotate_3x3_ref; +static void rotate_4x4(float* buf[4], const float m0[4][4], const float m1[4][4], const float* win, int numFrames) { + static auto f = cpuSupportsAVX2() ? rotate_4x4_AVX2 : rotate_4x4_ref; (*f)(buf, m0, m1, win, numFrames); // dispatch } @@ -965,7 +972,7 @@ static auto& rfft512 = rfft512_ref; static auto& rifft512 = rifft512_ref; static auto& rfft512_cmadd_1X2 = rfft512_cmadd_1X2_ref; static auto& convertInput = convertInput_ref; -static auto& rotate_3x3 = rotate_3x3_ref; +static auto& rotate_4x4 = rotate_4x4_ref; #endif @@ -1007,8 +1014,8 @@ ALIGN32 static const float crossfadeTable[FOA_BLOCK] = { 0.0020975362f, 0.0015413331f, 0.0010705384f, 0.0006852326f, 0.0003854819f, 0.0001713375f, 0.0000428362f, 0.0000000000f, }; -// convert quaternion to a column-major 3x3 rotation matrix -static void quatToMatrix_3x3(float w, float x, float y, float z, float m[3][3]) { +// convert quaternion to a column-major 4x4 rotation matrix +static void quatToMatrix_4x4(float w, float x, float y, float z, float m[4][4]) { float xx = x * (x + x); float xy = x * (y + y); @@ -1022,17 +1029,33 @@ static void quatToMatrix_3x3(float w, float x, float y, float z, float m[3][3]) float wy = w * (y + y); float wz = w * (z + z); - m[0][0] = 1.0f - (yy + zz); - m[0][1] = xy - wz; - m[0][2] = xz + wy; + m[0][0] = 1.0f; + m[0][1] = 0.0f; + m[0][2] = 0.0f; + m[0][3] = 0.0f; - m[1][0] = xy + wz; - m[1][1] = 1.0f - (xx + zz); - m[1][2] = yz - wx; + m[1][0] = 0.0f; + m[1][1] = 1.0f - (yy + zz); + m[1][2] = xy - wz; + m[1][3] = xz + wy; - m[2][0] = xz - wy; - m[2][1] = yz + wx; - m[2][2] = 1.0f - (xx + yy); + m[2][0] = 0.0f; + m[2][1] = xy + wz; + m[2][2] = 1.0f - (xx + zz); + m[2][3] = yz - wx; + + m[3][0] = 0.0f; + m[3][1] = xz - wy; + m[3][2] = yz + wx; + m[3][3] = 1.0f - (xx + yy); +} + +static void scaleMatrix_4x4(float scale, float m[4][4]) { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + m[i][j] *= scale; + } + } } // Ambisonic to binaural render @@ -1047,16 +1070,19 @@ void AudioFOA::render(int16_t* input, float* output, int index, float qw, float ALIGN32 float inBuffer[4][FOA_BLOCK]; // deinterleaved input buffers float* in[4] = { inBuffer[0], inBuffer[1], inBuffer[2], inBuffer[3] }; - float rotation[3][3]; + float rotation[4][4]; // convert input to deinterleaved float - convertInput(input, in, FOA_GAIN * gain, FOA_BLOCK); + convertInput(input, in, FOA_GAIN, FOA_BLOCK); - // convert quaternion to 3x3 rotation - quatToMatrix_3x3(qw, qx, qy, qz, rotation); + // convert quaternion to 4x4 rotation + quatToMatrix_4x4(qw, qx, qy, qz, rotation); - // rotate the soundfield - rotate_3x3(in, _rotationState, rotation, crossfadeTable, FOA_BLOCK); + // apply gain as uniform scale + scaleMatrix_4x4(gain, rotation); + + // rotate and scale the soundfield + rotate_4x4(in, _rotationState, rotation, crossfadeTable, FOA_BLOCK); // rotation history update memcpy(_rotationState, rotation, sizeof(_rotationState)); diff --git a/libraries/audio/src/AudioFOA.h b/libraries/audio/src/AudioFOA.h index 9eccc35bce..b8257e5190 100644 --- a/libraries/audio/src/AudioFOA.h +++ b/libraries/audio/src/AudioFOA.h @@ -28,12 +28,7 @@ static_assert((FOA_BLOCK + FOA_OVERLAP) == FOA_NFFT, "FFT convolution requires L class AudioFOA { public: - AudioFOA() { - // identity matrix - _rotationState[0][0] = 1.0f; - _rotationState[1][1] = 1.0f; - _rotationState[2][2] = 1.0f; - }; + AudioFOA() {}; // // input: interleaved First-Order Ambisonic source @@ -55,8 +50,8 @@ private: // input history, for overlap-save float _fftState[4][FOA_OVERLAP] = {}; - // orientation history - float _rotationState[3][3] = {}; + // orientation and gain history + float _rotationState[4][4] = {}; }; #endif // AudioFOA_h diff --git a/libraries/audio/src/avx2/AudioFOA_avx2.cpp b/libraries/audio/src/avx2/AudioFOA_avx2.cpp index 70f9b0e5f6..15d37fcc3a 100644 --- a/libraries/audio/src/avx2/AudioFOA_avx2.cpp +++ b/libraries/audio/src/avx2/AudioFOA_avx2.cpp @@ -1289,14 +1289,16 @@ void convertInput_AVX2(int16_t* src, float *dst[4], float gain, int numFrames) { #endif -// in-place rotation of the soundfield -// crossfade between old and new rotation, to prevent artifacts -void rotate_3x3_AVX2(float* buf[4], const float m0[3][3], const float m1[3][3], const float* win, int numFrames) { +// in-place rotation and scaling of the soundfield +// crossfade between old and new matrix, to prevent artifacts +void rotate_4x4_AVX2(float* buf[4], const float m0[4][4], const float m1[4][4], const float* win, int numFrames) { - const float md[3][3] = { - { m0[0][0] - m1[0][0], m0[0][1] - m1[0][1], m0[0][2] - m1[0][2] }, - { m0[1][0] - m1[1][0], m0[1][1] - m1[1][1], m0[1][2] - m1[1][2] }, - { m0[2][0] - m1[2][0], m0[2][1] - m1[2][1], m0[2][2] - m1[2][2] }, + // matrix difference + const float md[4][4] = { + { m0[0][0] - m1[0][0], m0[0][1] - m1[0][1], m0[0][2] - m1[0][2], m0[0][3] - m1[0][3] }, + { m0[1][0] - m1[1][0], m0[1][1] - m1[1][1], m0[1][2] - m1[1][2], m0[1][3] - m1[1][3] }, + { m0[2][0] - m1[2][0], m0[2][1] - m1[2][1], m0[2][2] - m1[2][2], m0[2][3] - m1[2][3] }, + { m0[3][0] - m1[3][0], m0[3][1] - m1[3][1], m0[3][2] - m1[3][2], m0[3][3] - m1[3][3] }, }; assert(numFrames % 8 == 0); @@ -1307,30 +1309,35 @@ void rotate_3x3_AVX2(float* buf[4], const float m0[3][3], const float m1[3][3], // interpolate the matrix __m256 m00 = _mm256_fmadd_ps(frac, _mm256_broadcast_ss(&md[0][0]), _mm256_broadcast_ss(&m1[0][0])); - __m256 m10 = _mm256_fmadd_ps(frac, _mm256_broadcast_ss(&md[1][0]), _mm256_broadcast_ss(&m1[1][0])); - __m256 m20 = _mm256_fmadd_ps(frac, _mm256_broadcast_ss(&md[2][0]), _mm256_broadcast_ss(&m1[2][0])); - __m256 m01 = _mm256_fmadd_ps(frac, _mm256_broadcast_ss(&md[0][1]), _mm256_broadcast_ss(&m1[0][1])); __m256 m11 = _mm256_fmadd_ps(frac, _mm256_broadcast_ss(&md[1][1]), _mm256_broadcast_ss(&m1[1][1])); __m256 m21 = _mm256_fmadd_ps(frac, _mm256_broadcast_ss(&md[2][1]), _mm256_broadcast_ss(&m1[2][1])); + __m256 m31 = _mm256_fmadd_ps(frac, _mm256_broadcast_ss(&md[3][1]), _mm256_broadcast_ss(&m1[3][1])); - __m256 m02 = _mm256_fmadd_ps(frac, _mm256_broadcast_ss(&md[0][2]), _mm256_broadcast_ss(&m1[0][2])); __m256 m12 = _mm256_fmadd_ps(frac, _mm256_broadcast_ss(&md[1][2]), _mm256_broadcast_ss(&m1[1][2])); __m256 m22 = _mm256_fmadd_ps(frac, _mm256_broadcast_ss(&md[2][2]), _mm256_broadcast_ss(&m1[2][2])); + __m256 m32 = _mm256_fmadd_ps(frac, _mm256_broadcast_ss(&md[3][2]), _mm256_broadcast_ss(&m1[3][2])); + + __m256 m13 = _mm256_fmadd_ps(frac, _mm256_broadcast_ss(&md[1][3]), _mm256_broadcast_ss(&m1[1][3])); + __m256 m23 = _mm256_fmadd_ps(frac, _mm256_broadcast_ss(&md[2][3]), _mm256_broadcast_ss(&m1[2][3])); + __m256 m33 = _mm256_fmadd_ps(frac, _mm256_broadcast_ss(&md[3][3]), _mm256_broadcast_ss(&m1[3][3])); // matrix multiply - __m256 x = _mm256_mul_ps(m00, _mm256_loadu_ps(&buf[1][i])); - __m256 y = _mm256_mul_ps(m10, _mm256_loadu_ps(&buf[1][i])); - __m256 z = _mm256_mul_ps(m20, _mm256_loadu_ps(&buf[1][i])); + __m256 w = _mm256_mul_ps(m00, _mm256_loadu_ps(&buf[0][i])); - x = _mm256_fmadd_ps(m01, _mm256_loadu_ps(&buf[2][i]), x); - y = _mm256_fmadd_ps(m11, _mm256_loadu_ps(&buf[2][i]), y); - z = _mm256_fmadd_ps(m21, _mm256_loadu_ps(&buf[2][i]), z); + __m256 x = _mm256_mul_ps(m11, _mm256_loadu_ps(&buf[1][i])); + __m256 y = _mm256_mul_ps(m21, _mm256_loadu_ps(&buf[1][i])); + __m256 z = _mm256_mul_ps(m31, _mm256_loadu_ps(&buf[1][i])); - x = _mm256_fmadd_ps(m02, _mm256_loadu_ps(&buf[3][i]), x); - y = _mm256_fmadd_ps(m12, _mm256_loadu_ps(&buf[3][i]), y); - z = _mm256_fmadd_ps(m22, _mm256_loadu_ps(&buf[3][i]), z); + x = _mm256_fmadd_ps(m12, _mm256_loadu_ps(&buf[2][i]), x); + y = _mm256_fmadd_ps(m22, _mm256_loadu_ps(&buf[2][i]), y); + z = _mm256_fmadd_ps(m32, _mm256_loadu_ps(&buf[2][i]), z); + x = _mm256_fmadd_ps(m13, _mm256_loadu_ps(&buf[3][i]), x); + y = _mm256_fmadd_ps(m23, _mm256_loadu_ps(&buf[3][i]), y); + z = _mm256_fmadd_ps(m33, _mm256_loadu_ps(&buf[3][i]), z); + + _mm256_storeu_ps(&buf[0][i], w); _mm256_storeu_ps(&buf[1][i], x); _mm256_storeu_ps(&buf[2][i], y); _mm256_storeu_ps(&buf[3][i], z); From 423da7d7378f9862e3458fd1196d11f703087674 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Sat, 30 Mar 2019 16:39:35 -0700 Subject: [PATCH 12/49] Add direct mix (non-spatialized) audio renderers with gain crossfade --- libraries/audio/src/AudioHRTF.cpp | 65 +++++++++++++++++++++++++++++++ libraries/audio/src/AudioHRTF.h | 6 +++ 2 files changed, 71 insertions(+) diff --git a/libraries/audio/src/AudioHRTF.cpp b/libraries/audio/src/AudioHRTF.cpp index 9de6440d67..6f7d01d3f9 100644 --- a/libraries/audio/src/AudioHRTF.cpp +++ b/libraries/audio/src/AudioHRTF.cpp @@ -750,6 +750,43 @@ static void interpolate(const float* src0, const float* src1, float* dst, float #endif +// apply gain crossfade with accumulation (interleaved) +static void gainfade_1x2(int16_t* src, float* dst, const float* win, float gain0, float gain1, int numFrames) { + + gain0 *= (1/32768.0f); // int16_t to float + gain1 *= (1/32768.0f); + + for (int i = 0; i < numFrames; i++) { + + float frac = win[i]; + float gain = gain1 + frac * (gain0 - gain1); + + float x0 = (float)src[i] * gain; + + dst[2*i+0] += x0; + dst[2*i+1] += x0; + } +} + +// apply gain crossfade with accumulation (interleaved) +static void gainfade_2x2(int16_t* src, float* dst, const float* win, float gain0, float gain1, int numFrames) { + + gain0 *= (1/32768.0f); // int16_t to float + gain1 *= (1/32768.0f); + + for (int i = 0; i < numFrames; i++) { + + float frac = win[i]; + float gain = gain1 + frac * (gain0 - gain1); + + float x0 = (float)src[2*i+0] * gain; + float x1 = (float)src[2*i+1] * gain; + + dst[2*i+0] += x0; + dst[2*i+1] += x1; + } +} + // design a 2nd order Thiran allpass static void ThiranBiquad(float f, float& b0, float& b1, float& b2, float& a1, float& a2) { @@ -1175,3 +1212,31 @@ void AudioHRTF::render(int16_t* input, float* output, int index, float azimuth, _resetState = false; } + +void AudioHRTF::mixMono(int16_t* input, float* output, float gain, int numFrames) { + + assert(numFrames == HRTF_BLOCK); + + // apply global and local gain adjustment + gain *= _gainAdjust; + + // crossfade gain and accumulate + gainfade_1x2(input, output, crossfadeTable, _gainState, gain, HRTF_BLOCK); + + // new parameters become old + _gainState = gain; +} + +void AudioHRTF::mixStereo(int16_t* input, float* output, float gain, int numFrames) { + + assert(numFrames == HRTF_BLOCK); + + // apply global and local gain adjustment + gain *= _gainAdjust; + + // crossfade gain and accumulate + gainfade_2x2(input, output, crossfadeTable, _gainState, gain, HRTF_BLOCK); + + // new parameters become old + _gainState = gain; +} diff --git a/libraries/audio/src/AudioHRTF.h b/libraries/audio/src/AudioHRTF.h index 7d23f4825a..436d6318a5 100644 --- a/libraries/audio/src/AudioHRTF.h +++ b/libraries/audio/src/AudioHRTF.h @@ -50,6 +50,12 @@ public: // void render(int16_t* input, float* output, int index, float azimuth, float distance, float gain, int numFrames); + // + // Non-spatialized direct mix (accumulates into existing output) + // + void mixMono(int16_t* input, float* output, float gain, int numFrames); + void mixStereo(int16_t* input, float* output, float gain, int numFrames); + // // Fast path when input is known to be silent and state as been flushed // From 58605161e8bf076d7460159f98bdf3a8ec2da6d4 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 31 Mar 2019 11:17:11 -0700 Subject: [PATCH 13/49] cooperate, rather than fight with, Linux window managers --- interface/src/main.cpp | 7 +++++-- interface/src/ui/DialogsManager.cpp | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 5af0a9371d..b2be010544 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -302,8 +302,11 @@ int main(int argc, const char* argv[]) { PROFILE_SYNC_BEGIN(startup, "app full ctor", ""); Application app(argcExtended, const_cast(argvExtended.data()), startupTime, runningMarkerExisted); PROFILE_SYNC_END(startup, "app full ctor", ""); - - + +#if defined(Q_OS_LINUX) + app.setWindowIcon(QIcon(PathUtils::resourcesPath() + "images/hifi-logo.svg")); +#endif + QTimer exitTimer; if (traceDuration > 0.0f) { exitTimer.setSingleShot(true); diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index a3a875ac40..e34b82e0a1 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -156,10 +156,10 @@ void DialogsManager::hmdTools(bool showTools) { } _hmdToolsDialog->show(); _hmdToolsDialog->raise(); + qApp->getWindow()->activateWindow(); } else { hmdToolsClosed(); } - qApp->getWindow()->activateWindow(); } void DialogsManager::hmdToolsClosed() { @@ -207,4 +207,4 @@ void DialogsManager::showDomainConnectionDialog() { _domainConnectionDialog->show(); _domainConnectionDialog->raise(); -} \ No newline at end of file +} From 181a4e9bdcd4598e8d10ffeb8df6c169d4ad0376 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Sun, 31 Mar 2019 11:32:29 -0700 Subject: [PATCH 14/49] Disable parameter interpolation from reset state Restores original behavior of constant-volume streams (no initial fade-in) --- libraries/audio/src/AudioFOA.cpp | 9 ++++++++- libraries/audio/src/AudioFOA.h | 2 ++ libraries/audio/src/AudioHRTF.cpp | 21 +++++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/libraries/audio/src/AudioFOA.cpp b/libraries/audio/src/AudioFOA.cpp index ddcdf7f250..0dd61fbd02 100644 --- a/libraries/audio/src/AudioFOA.cpp +++ b/libraries/audio/src/AudioFOA.cpp @@ -1081,10 +1081,15 @@ void AudioFOA::render(int16_t* input, float* output, int index, float qw, float // apply gain as uniform scale scaleMatrix_4x4(gain, rotation); + // disable interpolation from reset state + if (_resetState) { + memcpy(_rotationState, rotation, sizeof(_rotationState)); + } + // rotate and scale the soundfield rotate_4x4(in, _rotationState, rotation, crossfadeTable, FOA_BLOCK); - // rotation history update + // new parameters become old memcpy(_rotationState, rotation, sizeof(_rotationState)); // @@ -1119,4 +1124,6 @@ void AudioFOA::render(int16_t* input, float* output, int index, float qw, float output[2*i+0] += accBuffer[0][i + FOA_OVERLAP]; output[2*i+1] += accBuffer[1][i + FOA_OVERLAP]; } + + _resetState = false; } diff --git a/libraries/audio/src/AudioFOA.h b/libraries/audio/src/AudioFOA.h index b8257e5190..e8cacc22ab 100644 --- a/libraries/audio/src/AudioFOA.h +++ b/libraries/audio/src/AudioFOA.h @@ -52,6 +52,8 @@ private: // orientation and gain history float _rotationState[4][4] = {}; + + bool _resetState = true; }; #endif // AudioFOA_h diff --git a/libraries/audio/src/AudioHRTF.cpp b/libraries/audio/src/AudioHRTF.cpp index 6f7d01d3f9..e5e32781b0 100644 --- a/libraries/audio/src/AudioHRTF.cpp +++ b/libraries/audio/src/AudioHRTF.cpp @@ -1141,6 +1141,13 @@ void AudioHRTF::render(int16_t* input, float* output, int index, float azimuth, // apply global and local gain adjustment gain *= _gainAdjust; + // disable interpolation from reset state + if (_resetState) { + _azimuthState = azimuth; + _distanceState = distance; + _gainState = gain; + } + // to avoid polluting the cache, old filters are recomputed instead of stored setFilters(firCoef, bqCoef, delay, index, _azimuthState, _distanceState, _gainState, L0); @@ -1220,11 +1227,18 @@ void AudioHRTF::mixMono(int16_t* input, float* output, float gain, int numFrames // apply global and local gain adjustment gain *= _gainAdjust; + // disable interpolation from reset state + if (_resetState) { + _gainState = gain; + } + // crossfade gain and accumulate gainfade_1x2(input, output, crossfadeTable, _gainState, gain, HRTF_BLOCK); // new parameters become old _gainState = gain; + + _resetState = false; } void AudioHRTF::mixStereo(int16_t* input, float* output, float gain, int numFrames) { @@ -1234,9 +1248,16 @@ void AudioHRTF::mixStereo(int16_t* input, float* output, float gain, int numFram // apply global and local gain adjustment gain *= _gainAdjust; + // disable interpolation from reset state + if (_resetState) { + _gainState = gain; + } + // crossfade gain and accumulate gainfade_2x2(input, output, crossfadeTable, _gainState, gain, HRTF_BLOCK); // new parameters become old _gainState = gain; + + _resetState = false; } From 85368e6836393843152d7a414b8e0afff446420a Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Mon, 1 Apr 2019 12:24:37 -0700 Subject: [PATCH 15/49] Replace inline audio mixing with functions that do gain interpolation --- .../src/audio/AudioMixerSlave.cpp | 26 ++++++------------- libraries/audio-client/src/AudioClient.cpp | 15 +++-------- 2 files changed, 12 insertions(+), 29 deletions(-) diff --git a/assignment-client/src/audio/AudioMixerSlave.cpp b/assignment-client/src/audio/AudioMixerSlave.cpp index cb90df58e5..e5e9f89984 100644 --- a/assignment-client/src/audio/AudioMixerSlave.cpp +++ b/assignment-client/src/audio/AudioMixerSlave.cpp @@ -549,38 +549,28 @@ void AudioMixerSlave::addStream(AudioMixerClientData::MixableStream& mixableStre // grab the stream from the ring buffer AudioRingBuffer::ConstIterator streamPopOutput = streamToAdd->getLastPopOutput(); - // stereo sources are not passed through HRTF if (streamToAdd->isStereo()) { - // apply the avatar gain adjustment - gain *= mixableStream.hrtf->getGainAdjustment(); + streamPopOutput.readSamples(_bufferSamples, AudioConstants::NETWORK_FRAME_SAMPLES_STEREO); - const float scale = 1 / 32768.0f; // int16_t to float - - for (int i = 0; i < AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL; i++) { - _mixSamples[2*i+0] += (float)streamPopOutput[2*i+0] * gain * scale; - _mixSamples[2*i+1] += (float)streamPopOutput[2*i+1] * gain * scale; - } + // stereo sources are not passed through HRTF + mixableStream.hrtf->mixStereo(_bufferSamples, _mixSamples, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); ++stats.manualStereoMixes; } else if (isEcho) { + + streamPopOutput.readSamples(_bufferSamples, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); + // echo sources are not passed through HRTF - - const float scale = 1/32768.0f; // int16_t to float - - for (int i = 0; i < AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL; i++) { - float sample = (float)streamPopOutput[i] * gain * scale; - _mixSamples[2*i+0] += sample; - _mixSamples[2*i+1] += sample; - } + mixableStream.hrtf->mixMono(_bufferSamples, _mixSamples, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); ++stats.manualEchoMixes; } else { + streamPopOutput.readSamples(_bufferSamples, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); mixableStream.hrtf->render(_bufferSamples, _mixSamples, HRTF_DATASET_INDEX, azimuth, distance, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); - ++stats.hrtfRenders; } } diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index c537fea646..4d3311b065 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1397,7 +1397,6 @@ bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) { // spatialize into mixBuffer injector->getLocalFOA().render(_localScratchBuffer, mixBuffer, HRTF_DATASET_INDEX, qw, qx, qy, qz, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); - } else if (options.stereo) { if (options.positionSet) { @@ -1409,11 +1408,8 @@ bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) { } // direct mix into mixBuffer - for (int i = 0; i < AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL; i++) { - mixBuffer[2*i+0] += convertToFloat(_localScratchBuffer[2*i+0]) * gain; - mixBuffer[2*i+1] += convertToFloat(_localScratchBuffer[2*i+1]) * gain; - } - + injector->getLocalHRTF().mixStereo(_localScratchBuffer, mixBuffer, gain, + AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); } else { // injector is mono if (options.positionSet) { @@ -1431,11 +1427,8 @@ bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) { } else { // direct mix into mixBuffer - for (int i = 0; i < AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL; i++) { - float sample = convertToFloat(_localScratchBuffer[i]) * gain; - mixBuffer[2*i+0] += sample; - mixBuffer[2*i+1] += sample; - } + injector->getLocalHRTF().mixMono(_localScratchBuffer, mixBuffer, gain, + AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); } } From 857a4ab739bb335a420f9d87779b9a5e57dba87a Mon Sep 17 00:00:00 2001 From: Angus Antley Date: Wed, 3 Apr 2019 13:04:55 -0700 Subject: [PATCH 16/49] enabled MyAvatar.get and .set rotation --- libraries/avatars/src/AvatarData.cpp | 5 ++--- libraries/avatars/src/AvatarData.h | 19 +++++++++++-------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index a2b0b808ba..08f65b6d6a 100755 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1721,7 +1721,6 @@ glm::vec3 AvatarData::getJointTranslation(const QString& name) const { // on another thread in between the call to getJointIndex and getJointTranslation // return getJointTranslation(getJointIndex(name)); return readLockWithNamedJointIndex(name, [this](int index) { - return _jointData.at(index).translation; return getJointTranslation(index); }); } @@ -1809,8 +1808,8 @@ glm::quat AvatarData::getJointRotation(const QString& name) const { // Can't do this, not thread safe // return getJointRotation(getJointIndex(name)); - return readLockWithNamedJointIndex(name, [&](int index) { - return _jointData.at(index).rotation; + return readLockWithNamedJointIndex(name, [this](int index) { + return getJointRotation(index); }); } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index caa1f9f892..2eb5e28eea 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -1750,14 +1750,14 @@ protected: template T readLockWithNamedJointIndex(const QString& name, const T& defaultValue, F f) const { - int index = getFauxJointIndex(name); - QReadLocker readLock(&_jointDataLock); - - // The first conditional is superfluous, but illustrative - if (index == -1 || index < _jointData.size()) { + int index = getJointIndex(name); + if (index == -1) { + index = getFauxJointIndex(name); + } + if (index == -1) { return defaultValue; } - + QReadLocker readLock(&_jointDataLock); return f(index); } @@ -1768,11 +1768,14 @@ protected: template void writeLockWithNamedJointIndex(const QString& name, F f) { - int index = getFauxJointIndex(name); - QWriteLocker writeLock(&_jointDataLock); + int index = getJointIndex(name); + if (index == -1) { + index = getFauxJointIndex(name); + } if (index == -1) { return; } + QWriteLocker writeLock(&_jointDataLock); if (_jointData.size() <= index) { _jointData.resize(index + 1); } From 2a17ad3da5120b6688108cdf937d086f46537975 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 4 Apr 2019 10:11:54 +1300 Subject: [PATCH 17/49] AvatarList and AvatarManager JSDoc --- interface/src/avatar/AvatarManager.cpp | 12 ++ interface/src/avatar/AvatarManager.h | 133 ++++++++++++------ .../src/avatars-renderer/Avatar.cpp | 20 +++ .../src/avatars-renderer/Avatar.h | 4 +- libraries/avatars/src/AvatarData.cpp | 16 ++- libraries/avatars/src/AvatarData.h | 3 +- libraries/avatars/src/AvatarHashMap.h | 94 +++++++++---- libraries/avatars/src/ScriptAvatarData.h | 46 ++++++ libraries/render-utils/src/Model.cpp | 13 ++ libraries/shared/src/GeometryUtil.h | 7 + tools/jsdoc/plugins/hifi.js | 1 + 11 files changed, 277 insertions(+), 72 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 69f7054953..35022c882f 100755 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -946,6 +946,18 @@ void AvatarManager::setAvatarSortCoefficient(const QString& name, const QScriptV } } +/**jsdoc + * PAL (People Access List) data for an avatar. + * @typedef {object} AvatarManager.PalData + * @property {Uuid} sessionUUID - The avatar's session ID. "" if the avatar is your own. + * @property {string} sessionDisplayName - The avatar's display name, sanitized and versioned, as defined by the avatar mixer. + * It is unique among all avatars present in the domain at the time. + * @property {number} audioLoudness - The instantaneous loudness of the audio input that the avatar is injecting into the + * domain. + * @property {boolean} isReplicated - Deprecated. + * @property {Vec3} position - The position of the avatar. + * @property {number} palOrbOffset - The vertical offset from the avatar's position that an overlay orb should be displayed at. + */ QVariantMap AvatarManager::getPalData(const QStringList& specificAvatarIdentifiers) { QJsonArray palData; diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 0468fbd809..64bcb8dceb 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -37,10 +37,11 @@ using SortedAvatar = std::pair>; /**jsdoc - * The AvatarManager API has properties and methods which manage Avatars within the same domain. + * The AvatarManager API provides information about avatars within the domain. The avatars available are those + * that Interface has displayed and so knows about. * - *

Note: This API is also provided to Interface and client entity scripts as the synonym, - * AvatarList. For assignment client scripts, see the separate {@link AvatarList} API. + *

Warning: This API is also provided to Interface, client entity, and avatar scripts as the synonym, + * "AvatarList". For assignment client scripts, see the separate {@link AvatarList} API.

* * @namespace AvatarManager * @@ -48,8 +49,9 @@ using SortedAvatar = std::pair>; * @hifi-client-entity * @hifi-avatar * - * @borrows AvatarList.getAvatarIdentifiers as getAvatarIdentifiers - * @borrows AvatarList.getAvatarsInRange as getAvatarsInRange + * @borrows AvatarList.getAvatar as getAvatar + * @comment AvatarList.getAvatarIdentifiers as getAvatarIdentifiers - Don't borrow because behavior is slightly different. + * @comment AvatarList.getAvatarsInRange as getAvatarsInRange - Don't borrow because behavior is slightly different. * @borrows AvatarList.avatarAddedEvent as avatarAddedEvent * @borrows AvatarList.avatarRemovedEvent as avatarRemovedEvent * @borrows AvatarList.avatarSessionChangedEvent as avatarSessionChangedEvent @@ -67,6 +69,31 @@ class AvatarManager : public AvatarHashMap { public: + /**jsdoc + * Gets the IDs of all avatars known about in the domain. + * Your own avatar is included in the list as a null value. + * @function AvatarManager.getAvatarIdentifiers + * @returns {Uuid[]} The IDs of all known avatars in the domain. + * @example Report the IDS of all avatars within the domain. + * var avatars = AvatarManager.getAvatarIdentifiers(); + * print("Avatars in the domain: " + JSON.stringify(avatars)); + * // A null item is included for your avatar. + */ + + /**jsdoc + * Gets the IDs of all avatars known about within a specified distance from a point. + * Your own avatar's ID is included in the list if it is in range. + * @function AvatarManager.getAvatarsInRange + * @param {Vec3} position - The point about which the search is performed. + * @param {number} range - The search radius. + * @returns {Uuid[]} The IDs of all known avatars within the search distance from the position. + * @example Report the IDs of all avatars within 10m of your avatar. + * var RANGE = 10; + * var avatars = AvatarManager.getAvatarsInRange(MyAvatar.position, RANGE); + * print("Nearby avatars: " + JSON.stringify(avatars)); + * print("Own avatar: " + MyAvatar.sessionUUID); + */ + /// Registers the script types associated with the avatar manager. static void registerMetaTypes(QScriptEngine* engine); @@ -79,9 +106,7 @@ public: glm::vec3 getMyAvatarPosition() const { return _myAvatar->getWorldPosition(); } /**jsdoc - * @function AvatarManager.getAvatar - * @param {Uuid} avatarID - * @returns {AvatarData} + * @comment Uses the base class's JSDoc. */ // Null/Default-constructed QUuids will return MyAvatar Q_INVOKABLE virtual ScriptAvatarData* getAvatar(QUuid avatarID) override { return new ScriptAvatar(getAvatarBySessionID(avatarID)); } @@ -112,36 +137,53 @@ public: void handleCollisionEvents(const CollisionEvents& collisionEvents); /**jsdoc + * Gets the amount of avatar mixer data being generated by an avatar other than your own. * @function AvatarManager.getAvatarDataRate - * @param {Uuid} sessionID - * @param {string} [rateName=""] - * @returns {number} + * @param {Uuid} sessionID - The ID of the avatar to get the data rate for. + * @param {AvatarDataRate} [rateName=""] - The type of avatar mixer data to get the data rate of. + * @returns {number} The data rate in kbps; 0 if the avatar is your own. */ Q_INVOKABLE float getAvatarDataRate(const QUuid& sessionID, const QString& rateName = QString("")) const; /**jsdoc + * Gets the update rate of avatar mixer data being generated by an avatar other than your own. * @function AvatarManager.getAvatarUpdateRate - * @param {Uuid} sessionID - * @param {string} [rateName=""] - * @returns {number} + * @param {Uuid} sessionID - The ID of the avatar to get the update rate for. + * @param {AvatarUpdateRate} [rateName=""] - The type of avatar mixer data to get the update rate of. + * @returns {number} The update rate in Hz; 0 if the avatar is your own. */ Q_INVOKABLE float getAvatarUpdateRate(const QUuid& sessionID, const QString& rateName = QString("")) const; /**jsdoc + * Gets the simulation rate of an avatar other than your own. * @function AvatarManager.getAvatarSimulationRate - * @param {Uuid} sessionID - * @param {string} [rateName=""] - * @returns {number} + * @param {Uuid} sessionID - The ID of the avatar to get the simulation rate for. + * @param {AvatarSimulationRate} [rateName=""] - The type of avatar data to get the simulation rate of. + * @returns {number} The simulation rate in Hz; 0 if the avatar is your own. */ Q_INVOKABLE float getAvatarSimulationRate(const QUuid& sessionID, const QString& rateName = QString("")) const; /**jsdoc + * Find the first avatar intersected by a {@link PickRay}. * @function AvatarManager.findRayIntersection - * @param {PickRay} ray - * @param {Uuid[]} [avatarsToInclude=[]] - * @param {Uuid[]} [avatarsToDiscard=[]] - * @param {boolean} pickAgainstMesh - * @returns {RayToAvatarIntersectionResult} + * @param {PickRay} ray - The ray to use for finding avatars. + * @param {Uuid[]} [avatarsToInclude=[]] - If not empty then search is restricted to these avatars. + * @param {Uuid[]} [avatarsToDiscard=[]] - Avatars to ignore in the search. + * @param {boolean} [pickAgainstMesh=true] - If true then the exact intersection with the avatar mesh is + * calculated, if false then the intersection is approximate. + * @returns {RayToAvatarIntersectionResult} The result of the search for the first intersected avatar. + * @example Find the first avatar directly in front of you. + * var pickRay = { + * origin: MyAvatar.position, + * direction: Quat.getFront(MyAvatar.orientation) + * }; + * + * var intersection = AvatarManager.findRayIntersection(pickRay); + * if (intersection.intersects) { + * print("Avatar found: " + JSON.stringify(intersection)); + * } else { + * print("No avatar found."); + * } */ Q_INVOKABLE RayToAvatarIntersectionResult findRayIntersection(const PickRay& ray, const QScriptValue& avatarIdsToInclude = QScriptValue(), @@ -149,11 +191,12 @@ public: bool pickAgainstMesh = true); /**jsdoc * @function AvatarManager.findRayIntersectionVector - * @param {PickRay} ray - * @param {Uuid[]} avatarsToInclude - * @param {Uuid[]} avatarsToDiscard - * @param {boolean} pickAgainstMesh - * @returns {RayToAvatarIntersectionResult} + * @param {PickRay} ray - Ray. + * @param {Uuid[]} avatarsToInclude - Avatars to include. + * @param {Uuid[]} avatarsToDiscard - Avatars to discard. + * @param {boolean} pickAgainstMesh - Pick against mesh. + * @returns {RayToAvatarIntersectionResult} Intersection result. + * @deprecated This function is deprecated and will be removed. */ Q_INVOKABLE RayToAvatarIntersectionResult findRayIntersectionVector(const PickRay& ray, const QVector& avatarsToInclude, @@ -162,10 +205,11 @@ public: /**jsdoc * @function AvatarManager.findParabolaIntersectionVector - * @param {PickParabola} pick - * @param {Uuid[]} avatarsToInclude - * @param {Uuid[]} avatarsToDiscard - * @returns {ParabolaToAvatarIntersectionResult} + * @param {PickParabola} pick - Pick. + * @param {Uuid[]} avatarsToInclude - Avatars to include. + * @param {Uuid[]} avatarsToDiscard - Avatars to discard. + * @returns {ParabolaToAvatarIntersectionResult} Intersection result. + * @deprecated This function is deprecated and will be removed. */ Q_INVOKABLE ParabolaToAvatarIntersectionResult findParabolaIntersectionVector(const PickParabola& pick, const QVector& avatarsToInclude, @@ -173,27 +217,31 @@ public: /**jsdoc * @function AvatarManager.getAvatarSortCoefficient - * @param {string} name - * @returns {number} + * @param {string} name - Name. + * @returns {number} Value. + * @deprecated This function is deprecated and will be removed. */ // TODO: remove this HACK once we settle on optimal default sort coefficients Q_INVOKABLE float getAvatarSortCoefficient(const QString& name); /**jsdoc * @function AvatarManager.setAvatarSortCoefficient - * @param {string} name - * @param {number} value + * @param {string} name - Name + * @param {number} value - Value. + * @deprecated This function is deprecated and will be removed. */ Q_INVOKABLE void setAvatarSortCoefficient(const QString& name, const QScriptValue& value); /**jsdoc - * Used in the PAL for getting PAL-related data about avatars nearby. Using this method is faster - * than iterating over each avatar and obtaining data about them in JavaScript, as that method - * locks and unlocks each avatar's data structure potentially hundreds of times per update tick. + * Gets PAL (People Access List) data for one or more avatars. Using this method is faster than iterating over each avatar + * and obtaining data about each individually. * @function AvatarManager.getPalData - * @param {string[]} [specificAvatarIdentifiers=[]] - The list of IDs of the avatars you want the PAL data for. - * If an empty list, the PAL data for all nearby avatars is returned. - * @returns {object[]} An array of objects, each object being the PAL data for an avatar. + * @param {string[]} [avatarIDs=[]] - The IDs of the avatars to get the PAL data for. If empty then PAL + * data is obtained for all avatars. + * @returns {object<"data", AvatarManager.PalData[]>} An array of objects, each object being the PAL data for an avatar. + * @example Report the PAL data for one nearby avatar. + * var palData = AvatarManager.getPalData(); + * print("PAL data for one avatar: " + JSON.stringify(palData.data[0])); */ Q_INVOKABLE QVariantMap getPalData(const QStringList& specificAvatarIdentifiers = QStringList()); @@ -209,7 +257,8 @@ public: public slots: /**jsdoc * @function AvatarManager.updateAvatarRenderStatus - * @param {boolean} shouldRenderAvatars + * @param {boolean} shouldRenderAvatars - Should render avatars. + * @deprecated This function is deprecated and will be removed. */ void updateAvatarRenderStatus(bool shouldRenderAvatars); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 839c4ed1d9..d083dfb41b 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -509,6 +509,26 @@ void Avatar::relayJointDataToChildren() { _reconstructSoftEntitiesJointMap = false; } +/**jsdoc + * An avatar has different types of data simulated at different rates, in Hz. + * + * + * + * + * + * + * + * + * + * + * + * + *
Rate NameDescription
"avatar" or ""The rate at which the avatar is updated even if not in view.
"avatarInView"The rate at which the avatar is updated if in view.
"skeletonModel"The rate at which the skeleton model is being updated, even if there are no + * joint data available.
"jointData"The rate at which joint data are being updated.
""When no rate name is specified, the "avatar" update rate is + * provided.
+ * + * @typedef {string} AvatarSimulationRate + */ float Avatar::getSimulationRate(const QString& rateName) const { if (rateName == "") { return _simulationRate.rate(); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 974fae2034..7c4cde1f4d 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -501,8 +501,8 @@ public: /**jsdoc * @function MyAvatar.getSimulationRate - * @param {string} [rateName=""] - Rate name. - * @returns {number} Simulation rate. + * @param {AvatarSimulationRate} [rateName=""] - Rate name. + * @returns {number} Simulation rate in Hz. * @deprecated This function is deprecated and will be removed. */ Q_INVOKABLE float getSimulationRate(const QString& rateName = QString("")) const; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index a2b0b808ba..404d5c57af 100755 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1545,7 +1545,6 @@ float AvatarData::getDataRate(const QString& rateName) const { * Rate NameDescription * * - * "globalPosition"Global position. * "localPosition"Local position. * "avatarBoundingBox"Avatar bounding box. @@ -1559,7 +1558,6 @@ float AvatarData::getDataRate(const QString& rateName) const { * "faceTracker"Face tracker data. * "jointData"Joint data. * "farGrabJointData"Far grab joint data. - * ""When no rate name is specified, the overall update rate is provided. * * @@ -2905,6 +2903,20 @@ glm::mat4 AvatarData::getControllerRightHandMatrix() const { return _controllerRightHandMatrixCache.get(); } +/**jsdoc + * Information about a ray-to-avatar intersection. + * @typedef {object} RayToAvatarIntersectionResult + * @property {boolean} intersects - true if an avatar is intersected, false if it isn't. + * @property {string} avatarID - The ID of the avatar that is intersected. + * @property {number} distance - The distance from the ray origin to the intersection. + * @property {string} face - The name of the box face that is intersected; "UNKNOWN_FACE" if mesh was picked + * against. + * @property {Vec3} intersection - The ray intersection point in world coordinates. + * @property {Vec3} surfaceNormal - The surface normal at the intersection point. + * @property {number} jointIndex - The index of the joint intersected. + * @property {SubmeshIntersection} extraInfo - Extra information on the mesh intersected if mesh was picked against, + * {} if it wasn't. + */ QScriptValue RayToAvatarIntersectionResultToScriptValue(QScriptEngine* engine, const RayToAvatarIntersectionResult& value) { QScriptValue obj = engine->newObject(); obj.setProperty("intersects", value.intersects); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index caa1f9f892..802e5c953e 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -479,7 +479,8 @@ class AvatarData : public QObject, public SpatiallyNestable { * avatar. Read-only. * @property {number} sensorToWorldScale - The scale that transforms dimensions in the user's real world to the avatar's * size in the virtual world. Read-only. - * @property {boolean} hasPriority - is the avatar in a Hero zone? Read-only. + * @property {boolean} hasPriority - true if the avatar is in a "hero" zone, false if it isn't. + * Read-only. */ Q_PROPERTY(glm::vec3 position READ getWorldPosition WRITE setPositionViaScript) Q_PROPERTY(float scale READ getDomainLimitedScale WRITE setTargetScale) diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index 8395651d6b..3340635c82 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -36,8 +36,10 @@ const int CLIENT_TO_AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 50; const quint64 MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS = USECS_PER_SECOND / CLIENT_TO_AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND; /**jsdoc - * Note: An AvatarList API is also provided for Interface and client entity scripts: it is a - * synonym for the {@link AvatarManager} API. + * The AvatarList API provides information about avatars within the domain. + * + *

Warning: An API named "AvatarList" is also provided for Interface, client entity, and avatar + * scripts, however, it is a synonym for the {@link AvatarManager} API.

* * @namespace AvatarList * @@ -78,23 +80,37 @@ public: // Currently, your own avatar will be included as the null avatar id. /**jsdoc + * Gets the IDs of all avatars in the domain. + *

Warning: If the AC script is acting as an avatar (i.e., Agent.isAvatar == true) the + * avatar's ID is NOT included in results.

* @function AvatarList.getAvatarIdentifiers - * @returns {Uuid[]} + * @returns {Uuid[]} The IDs of all avatars in the domain (excluding AC script's avatar). + * @example Report the IDS of all avatars within the domain. + * var avatars = AvatarList.getAvatarIdentifiers(); + * print("Avatars in the domain: " + JSON.stringify(avatars)); */ Q_INVOKABLE QVector getAvatarIdentifiers(); /**jsdoc + * Gets the IDs of all avatars within a specified distance from a point. + *

Warning: If the AC script is acting as an avatar (i.e., Agent.isAvatar == true) the + * avatar's ID is NOT included in results.

* @function AvatarList.getAvatarsInRange - * @param {Vec3} position - * @param {number} range - * @returns {Uuid[]} + * @param {Vec3} position - The point about which the search is performed. + * @param {number} range - The search radius. + * @returns {Uuid[]} The IDs of all avatars within the search distance from the position (excluding AC script's avatar). + * @example Report the IDs of all avatars within 10m of the origin. + * var RANGE = 10; + * var avatars = AvatarList.getAvatarsInRange(Vec3.ZERO, RANGE); + * print("Avatars near the origin: " + JSON.stringify(avatars)); */ Q_INVOKABLE QVector getAvatarsInRange(const glm::vec3& position, float rangeMeters) const; /**jsdoc + * Gets information about an avatar. * @function AvatarList.getAvatar - * @param {Uuid} avatarID - * @returns {AvatarData} + * @param {Uuid} avatarID - The ID of the avatar. + * @returns {AvatarData} Information about the avatar. */ // Null/Default-constructed QUuids will return MyAvatar Q_INVOKABLE virtual ScriptAvatarData* getAvatar(QUuid avatarID) { return new ScriptAvatarData(getAvatarBySessionID(avatarID)); } @@ -110,34 +126,57 @@ public: signals: /**jsdoc + * Triggered when an avatar arrives in the domain. * @function AvatarList.avatarAddedEvent - * @param {Uuid} sessionUUID + * @param {Uuid} sessionUUID - The ID of the avatar that arrived in the domain. * @returns {Signal} + * @example Report when an avatar arrives in the domain. + * AvatarManager.avatarAddedEvent.connect(function (sessionID) { + * print("Avatar arrived: " + sessionID); + * }); + * + * // Note: If using from the AvatarList API, replace "AvatarManager" with "AvatarList". */ void avatarAddedEvent(const QUuid& sessionUUID); /**jsdoc + * Triggered when an avatar leaves the domain. * @function AvatarList.avatarRemovedEvent - * @param {Uuid} sessionUUID + * @param {Uuid} sessionUUID - The ID of the avatar that left the domain. * @returns {Signal} + * @example Report when an avatar leaves the domain. + * AvatarManager.avatarRemovedEvent.connect(function (sessionID) { + * print("Avatar left: " + sessionID); + * }); + * + * // Note: If using from the AvatarList API, replace "AvatarManager" with "AvatarList". */ void avatarRemovedEvent(const QUuid& sessionUUID); /**jsdoc + * Triggered when an avatar's session ID changes. * @function AvatarList.avatarSessionChangedEvent - * @param {Uuid} sessionUUID - * @param {Uuid} oldSessionUUID + * @param {Uuid} newSessionUUID - The new session ID. + * @param {Uuid} oldSessionUUID - The old session ID. * @returns {Signal} + * @example Report when an avatar's session ID changes. + * AvatarManager.avatarSessionChangedEvent.connect(function (newSessionID, oldSessionID) { + * print("Avatar session ID changed from " + oldSessionID + " to " + newSessionID); + * }); + * + * // Note: If using from the AvatarList API, replace "AvatarManager" with "AvatarList". */ void avatarSessionChangedEvent(const QUuid& sessionUUID,const QUuid& oldUUID); public slots: /**jsdoc + * Checks whether there is an avatar within a specified distance from a point. * @function AvatarList.isAvatarInRange - * @param {string} position - * @param {string} range - * @returns {boolean} + * @param {string} position - The test position. + * @param {string} range - The test distance. + * @returns {boolean} true if there's an avatar within the specified distance of the point, false + * if not. */ bool isAvatarInRange(const glm::vec3 & position, const float range); @@ -145,36 +184,41 @@ protected slots: /**jsdoc * @function AvatarList.sessionUUIDChanged - * @param {Uuid} sessionUUID - * @param {Uuid} oldSessionUUID + * @param {Uuid} sessionUUID - New session ID. + * @param {Uuid} oldSessionUUID - Old session ID. + * @deprecated This function is deprecated and will be removed. */ void sessionUUIDChanged(const QUuid& sessionUUID, const QUuid& oldUUID); /**jsdoc * @function AvatarList.processAvatarDataPacket - * @param {} message - * @param {} sendingNode + * @param {object} message - Message. + * @param {object} sendingNode - Sending node. + * @deprecated This function is deprecated and will be removed. */ void processAvatarDataPacket(QSharedPointer message, SharedNodePointer sendingNode); /**jsdoc * @function AvatarList.processAvatarIdentityPacket - * @param {} message - * @param {} sendingNode + * @param {object} message - Message. + * @param {object} sendingNode - Sending node. + * @deprecated This function is deprecated and will be removed. */ void processAvatarIdentityPacket(QSharedPointer message, SharedNodePointer sendingNode); /**jsdoc * @function AvatarList.processBulkAvatarTraits - * @param {} message - * @param {} sendingNode + * @param {object} message - Message. + * @param {object} sendingNode - Sending node. + * @deprecated This function is deprecated and will be removed. */ void processBulkAvatarTraits(QSharedPointer message, SharedNodePointer sendingNode); /**jsdoc * @function AvatarList.processKillAvatar - * @param {} message - * @param {} sendingNode + * @param {object} message - Message. + * @param {object} sendingNode - Sending node. + * @deprecated This function is deprecated and will be removed. */ void processKillAvatar(QSharedPointer message, SharedNodePointer sendingNode); diff --git a/libraries/avatars/src/ScriptAvatarData.h b/libraries/avatars/src/ScriptAvatarData.h index 01f7ff360a..61ceb88480 100644 --- a/libraries/avatars/src/ScriptAvatarData.h +++ b/libraries/avatars/src/ScriptAvatarData.h @@ -16,6 +16,52 @@ #include "AvatarData.h" +/**jsdoc + * Information about an avatar. + * @typedef {object} AvatarData + * @property {Vec3} position - The avatar's position. + * @property {number} scale - The target scale of the avatar without any restrictions on permissible values imposed by the + * domain. + * @property {Vec3} handPosition - A user-defined hand position, in world coordinates. The position moves with the avatar but + * is otherwise not used or changed by Interface. + * @property {number} bodyPitch - The pitch of the avatar's body, in degrees. + * @property {number} bodyYaw - The yaw of the avatar's body, in degrees. + * @property {number} bodyRoll - The roll of the avatar's body, in degrees. + * @property {Quat} orientation - The orientation of the avatar's body. + * @property {Quat} headOrientation - The orientation of the avatar's head. + * @property {number} headPitch - The pitch of the avatar's head relative to the body, in degrees. + * @property {number} headYaw - The yaw of the avatar's head relative to the body, in degrees. + * @property {number} headRoll - The roll of the avatar's head relative to the body, in degrees. + * + * @property {Vec3} velocity - The linear velocity of the avatar. + * @property {Vec3} angularVelocity - The angular velocity of the avatar. + * + * @property {Uuid} sessionUUID - The avatar's session ID. + * @property {string} displayName - The avatar's display name. + * @property {string} sessionDisplayName - The avatar's display name, sanitized and versioned, as defined by the avatar mixer. + * It is unique among all avatars present in the domain at the time. + * @property {boolean} isReplicated - Deprecated. + * @property {boolean} lookAtSnappingEnabled - true if the avatar's eyes snap to look at another avatar's eyes + * when the other avatar is in the line of sight and also has lookAtSnappingEnabled == true. + * + * @property {string} skeletonModelURL - The avatar's FST file. + * @property {AttachmentData[]} attachmentData - Information on the avatar's attachments.
+ * Deprecated: Use avatar entities insteada. + * @property {string[]} jointNames - The list of joints in the current avatar model. + * + * @property {number} audioLoudness - The instantaneous loudness of the audio input that the avatar is injecting into the + * domain. + * @property {number} audioAverageLoudness - The rolling average loudness of the audio input that the avatar is injecting into + * the domain. + * + * @property {Mat4} sensorToWorldMatrix - The scale, rotation, and translation transform from the user's real world to the + * avatar's size, orientation, and position in the virtual world. + * @property {Mat4} controllerLeftHandMatrix - The rotation and translation of the left hand controller relative to the avatar. + * @property {Mat4} controllerRightHandMatrix - The rotation and translation of the right hand controller relative to the + * avatar. + * + * @property {boolean} hasPriority - true if the avatar is in a "hero" zone, false if it isn't. + */ class ScriptAvatarData : public QObject { Q_OBJECT diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 89a9c7cf47..9c7df54cda 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -442,6 +442,19 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g } } + /**jsdoc + * Information about a submesh intersection point. + * @typedef {object} SubmeshIntersection + * @property {Vec3} worldIntersectionPoint - The intersection point in world coordinates. + * @property {Vec3} meshIntersectionPoint - The intersection point in model coordinates. + * @property {number} partIndex - The index of the intersected mesh part within the submesh. + * @property {number} shapeID - The index of the mesh part within the model. + * @property {number} subMeshIndex - The index of the intersected submesh within the model. + * @property {string} subMeshName - The name of the intersected submesh. + * @property {Triangle} subMeshTriangleWorld - The vertexes of the intersected mesh part triangle in world coordinates. + * @property {Vec3} subMeshNormal - The normal of the intersected mesh part triangle in model coordinates. + * @property {Triangle} subMeshTriangle - The vertexes of the intersected mesh part triangle in model coordinates. + */ if (intersectedSomething) { distance = bestDistance; face = bestFace; diff --git a/libraries/shared/src/GeometryUtil.h b/libraries/shared/src/GeometryUtil.h index 8ec75f71bd..ce25a4f559 100644 --- a/libraries/shared/src/GeometryUtil.h +++ b/libraries/shared/src/GeometryUtil.h @@ -119,6 +119,13 @@ void swingTwistDecomposition(const glm::quat& rotation, glm::quat& swing, glm::quat& twist); +/**jsdoc + * A triangle in a mesh. + * @typedef {object} Triangle + * @property {Vec3} v0 - The position of vertex 0 in the triangle. + * @property {Vec3} v1 - The position of vertex 1 in the triangle. + * @property {Vec3} v2 - The position of vertex 2 in the triangle. + */ class Triangle { public: glm::vec3 v0; diff --git a/tools/jsdoc/plugins/hifi.js b/tools/jsdoc/plugins/hifi.js index 5ec94b46aa..6624301cf3 100644 --- a/tools/jsdoc/plugins/hifi.js +++ b/tools/jsdoc/plugins/hifi.js @@ -56,6 +56,7 @@ exports.handlers = { '../../libraries/physics/src', '../../libraries/plugins/src/plugins', '../../libraries/pointers/src', + '../../libraries/render-utils/src', '../../libraries/script-engine/src', '../../libraries/shared/src', '../../libraries/shared/src/shared', From 1bc38cab28b62e004d60d1f721154ec5c52f54df Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 4 Apr 2019 18:03:14 +1300 Subject: [PATCH 18/49] Camera JSDoc polish --- interface/src/FancyCamera.h | 56 +++++++++++-------- libraries/shared/src/shared/Camera.h | 84 +++++++++++----------------- 2 files changed, 65 insertions(+), 75 deletions(-) diff --git a/interface/src/FancyCamera.h b/interface/src/FancyCamera.h index 4ca073fb4f..cd587279e1 100644 --- a/interface/src/FancyCamera.h +++ b/interface/src/FancyCamera.h @@ -19,14 +19,22 @@ class FancyCamera : public Camera { Q_OBJECT /**jsdoc - * @namespace - * @augments Camera - */ - - // FIXME: JSDoc 3.5.5 doesn't augment @property definitions. The following definition is repeated in Camera.h. - /**jsdoc - * @property {Uuid} cameraEntity The ID of the entity that the camera position and orientation follow when the camera is in - * entity mode. + * The Camera API provides access to the "camera" that defines your view in desktop and HMD display modes. + * + * @namespace Camera + * + * @hifi-interface + * @hifi-client-entity + * @hifi-avatar + * + * @property {Vec3} position - The position of the camera. You can set this value only when the camera is in independent + * mode. + * @property {Quat} orientation - The orientation of the camera. You can set this value only when the camera is in + * independent mode. + * @property {Camera.Mode} mode - The camera mode. + * @property {ViewFrustum} frustum - The camera frustum. + * @property {Uuid} cameraEntity - The ID of the entity that is used for the camera position and orientation when the + * camera is in entity mode. */ Q_PROPERTY(QUuid cameraEntity READ getCameraEntity WRITE setCameraEntity) @@ -38,25 +46,25 @@ public: public slots: - /**jsdoc - * Get the ID of the entity that the camera is set to use the position and orientation from when it's in entity mode. You can - * also get the entity ID using the Camera.cameraEntity property. - * @function Camera.getCameraEntity - * @returns {Uuid} The ID of the entity that the camera is set to follow when in entity mode; null if no camera - * entity has been set. - */ + /**jsdoc + * Gets the ID of the entity that the camera is set to use the position and orientation from when it's in entity mode. You + * can also get the entity ID using the {@link Camera|Camera.cameraEntity} property. + * @function Camera.getCameraEntity + * @returns {Uuid} The ID of the entity that the camera is set to follow when in entity mode; null if no + * camera entity has been set. + */ QUuid getCameraEntity() const; /**jsdoc - * Set the entity that the camera should use the position and orientation from when it's in entity mode. You can also set the - * entity using the Camera.cameraEntity property. - * @function Camera.setCameraEntity - * @param {Uuid} entityID The entity that the camera should follow when it's in entity mode. - * @example Move your camera to the position and orientation of the closest entity. - * Camera.setModeString("entity"); - * var entity = Entities.findClosestEntity(MyAvatar.position, 100.0); - * Camera.setCameraEntity(entity); - */ + * Sets the entity that the camera should use the position and orientation from when it's in entity mode. You can also set + * the entity using the {@link Camera|Camera.cameraEntity} property. + * @function Camera.setCameraEntity + * @param {Uuid} entityID - The entity that the camera should follow when it's in entity mode. + * @example Move your camera to the position and orientation of the closest entity. + * Camera.setModeString("entity"); + * var entity = Entities.findClosestEntity(MyAvatar.position, 100.0); + * Camera.setCameraEntity(entity); + */ void setCameraEntity(QUuid entityID); private: diff --git a/libraries/shared/src/shared/Camera.h b/libraries/shared/src/shared/Camera.h index 0132e58d18..f41183479c 100644 --- a/libraries/shared/src/shared/Camera.h +++ b/libraries/shared/src/shared/Camera.h @@ -36,25 +36,6 @@ static int cameraModeId = qRegisterMetaType(); class Camera : public QObject { Q_OBJECT - /**jsdoc - * The Camera API provides access to the "camera" that defines your view in desktop and HMD display modes. - * - * @namespace Camera - * - * @hifi-interface - * @hifi-client-entity - * @hifi-avatar - * - * @property {Vec3} position - The position of the camera. You can set this value only when the camera is in independent - * mode. - * @property {Quat} orientation - The orientation of the camera. You can set this value only when the camera is in - * independent mode. - * @property {Camera.Mode} mode - The camera mode. - * @property {ViewFrustum} frustum - The camera frustum. - * @property {Uuid} cameraEntity - The ID of the entity that is used for the camera position and orientation when the - * camera is in entity mode. - */ - // FIXME: The cameraEntity property definition is copied from FancyCamera.h. Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition) Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation) Q_PROPERTY(QString mode READ getModeString WRITE setModeString) @@ -82,53 +63,54 @@ public: public slots: /**jsdoc - * Get the current camera mode. You can also get the mode using the Camera.mode property. + * Gets the current camera mode. You can also get the mode using the {@link Camera|Camera.mode} property. * @function Camera.getModeString * @returns {Camera.Mode} The current camera mode. */ QString getModeString() const; /**jsdoc - * Set the camera mode. You can also set the mode using the Camera.mode property. - * @function Camera.setModeString - * @param {Camera.Mode} mode - The mode to set the camera to. - */ + * Sets the camera mode. You can also set the mode using the {@link Camera|Camera.mode} property. + * @function Camera.setModeString + * @param {Camera.Mode} mode - The mode to set the camera to. + */ void setModeString(const QString& mode); /**jsdoc - * Get the current camera position. You can also get the position using the Camera.position property. - * @function Camera.getPosition - * @returns {Vec3} The current camera position. - */ + * Gets the current camera position. You can also get the position using the {@link Camera|Camera.position} property. + * @function Camera.getPosition + * @returns {Vec3} The current camera position. + */ glm::vec3 getPosition() const { return _position; } /**jsdoc - * Set the camera position. You can also set the position using the Camera.position property. Only works if the - * camera is in independent mode. - * @function Camera.setPosition - * @param {Vec3} position - The position to set the camera at. - */ + * Sets the camera position. You can also set the position using the {@link Camera|Camera.position} property. Only works if + * the camera is in independent mode. + * @function Camera.setPosition + * @param {Vec3} position - The position to set the camera at. + */ void setPosition(const glm::vec3& position); /**jsdoc - * Get the current camera orientation. You can also get the orientation using the Camera.orientation property. - * @function Camera.getOrientation - * @returns {Quat} The current camera orientation. - */ + * Gets the current camera orientation. You can also get the orientation using the {@link Camera|Camera.orientation} + * property. + * @function Camera.getOrientation + * @returns {Quat} The current camera orientation. + */ glm::quat getOrientation() const { return _orientation; } /**jsdoc - * Set the camera orientation. You can also set the orientation using the Camera.orientation property. Only - * works if the camera is in independent mode. - * @function Camera.setOrientation - * @param {Quat} orientation - The orientation to set the camera to. - */ + * Sets the camera orientation. You can also set the orientation using the {@link Camera|Camera.orientation} property. Only + * works if the camera is in independent mode. + * @function Camera.setOrientation + * @param {Quat} orientation - The orientation to set the camera to. + */ void setOrientation(const glm::quat& orientation); /**jsdoc - * Compute a {@link PickRay} based on the current camera configuration and the specified x, y position on the - * screen. The {@link PickRay} can be used in functions such as {@link Entities.findRayIntersection} and - * {@link Overlays.findRayIntersection}. + * Computes a {@link PickRay} based on the current camera configuration and the specified x, y position on the + * screen. The {@link PickRay} can be used in functions such as {@link Entities.findRayIntersection} and + * {@link Overlays.findRayIntersection}. * @function Camera.computePickRay * @param {number} x - X-coordinate on screen. * @param {number} y - Y-coordinate on screen. @@ -147,9 +129,9 @@ public slots: virtual PickRay computePickRay(float x, float y) const = 0; /**jsdoc - * Rotate the camera to look at the specified position. Only works if the camera is in independent mode. + * Rotates the camera to look at the specified position. Only works if the camera is in independent mode. * @function Camera.lookAt - * @param {Vec3} position - Position to look at. + * @param {Vec3} position - The position to look at. * @example Rotate your camera to look at entities as you click on them with your mouse. * function onMousePressEvent(event) { * var pickRay = Camera.computePickRay(event.x, event.y); @@ -168,15 +150,15 @@ public slots: void lookAt(const glm::vec3& position); /**jsdoc - * Set the camera to continue looking at the specified position even while the camera moves. Only works if the - * camera is in independent mode. + * Sets the camera to continue looking at the specified position even while the camera moves. Only works if + * the camera is in independent mode. * @function Camera.keepLookingAt - * @param {Vec3} position - Position to keep looking at. + * @param {Vec3} position - The position to keep looking at. */ void keepLookingAt(const glm::vec3& position); /**jsdoc - * Stops the camera from continually looking at the position that was set with Camera.keepLookingAt. + * Stops the camera from continually looking at the position that was set with {@link Camera.keepLookingAt}. * @function Camera.stopLookingAt */ void stopLooking() { _isKeepLookingAt = false; } From 72264203dbca1fba700a3a0d0a46fb4e15aa48a0 Mon Sep 17 00:00:00 2001 From: Angus Antley Date: Thu, 4 Apr 2019 10:08:50 -0700 Subject: [PATCH 19/49] corrected the lock positions to cover the getJointIndex calls --- libraries/avatars/src/AvatarData.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 2eb5e28eea..6dbb8dc7c8 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -1750,6 +1750,7 @@ protected: template T readLockWithNamedJointIndex(const QString& name, const T& defaultValue, F f) const { + QReadLocker readLock(&_jointDataLock); int index = getJointIndex(name); if (index == -1) { index = getFauxJointIndex(name); @@ -1757,7 +1758,6 @@ protected: if (index == -1) { return defaultValue; } - QReadLocker readLock(&_jointDataLock); return f(index); } @@ -1768,6 +1768,7 @@ protected: template void writeLockWithNamedJointIndex(const QString& name, F f) { + QWriteLocker writeLock(&_jointDataLock); int index = getJointIndex(name); if (index == -1) { index = getFauxJointIndex(name); @@ -1775,7 +1776,6 @@ protected: if (index == -1) { return; } - QWriteLocker writeLock(&_jointDataLock); if (_jointData.size() <= index) { _jointData.resize(index + 1); } From 9afbf76ec19ab6c6d818726660995a52eed0be07 Mon Sep 17 00:00:00 2001 From: Angus Antley Date: Thu, 4 Apr 2019 10:22:24 -0700 Subject: [PATCH 20/49] removed unnecessary getFauxJointIndex call, this is handled in getJointIndex --- libraries/avatars/src/AvatarData.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 6dbb8dc7c8..7604a93ac4 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -1752,9 +1752,6 @@ protected: T readLockWithNamedJointIndex(const QString& name, const T& defaultValue, F f) const { QReadLocker readLock(&_jointDataLock); int index = getJointIndex(name); - if (index == -1) { - index = getFauxJointIndex(name); - } if (index == -1) { return defaultValue; } @@ -1770,9 +1767,6 @@ protected: void writeLockWithNamedJointIndex(const QString& name, F f) { QWriteLocker writeLock(&_jointDataLock); int index = getJointIndex(name); - if (index == -1) { - index = getFauxJointIndex(name); - } if (index == -1) { return; } From 7cbda006a088ec5613dce484514466666ac73b1f Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 8 Apr 2019 11:03:21 -0700 Subject: [PATCH 21/49] disable jsbaker --- tools/oven/src/BakerCLI.cpp | 5 +++-- tools/oven/src/DomainBaker.cpp | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/oven/src/BakerCLI.cpp b/tools/oven/src/BakerCLI.cpp index f09db3ed26..64462a4e37 100644 --- a/tools/oven/src/BakerCLI.cpp +++ b/tools/oven/src/BakerCLI.cpp @@ -55,8 +55,9 @@ void BakerCLI::bakeFile(QUrl inputUrl, const QString& outputPath, const QString& } } } else if (type == SCRIPT_EXTENSION) { - _baker = std::unique_ptr { new JSBaker(inputUrl, outputPath) }; - _baker->moveToThread(Oven::instance().getNextWorkerThread()); + // FIXME: disabled for now because it breaks some scripts + //_baker = std::unique_ptr { new JSBaker(inputUrl, outputPath) }; + //_baker->moveToThread(Oven::instance().getNextWorkerThread()); } else if (type == MATERIAL_EXTENSION) { _baker = std::unique_ptr { new MaterialBaker(inputUrl.toDisplayString(), true, outputPath) }; _baker->moveToThread(Oven::instance().getNextWorkerThread()); diff --git a/tools/oven/src/DomainBaker.cpp b/tools/oven/src/DomainBaker.cpp index b92a310f5d..8a4489fcac 100644 --- a/tools/oven/src/DomainBaker.cpp +++ b/tools/oven/src/DomainBaker.cpp @@ -397,6 +397,8 @@ void DomainBaker::enumerateEntities() { } } + // FIXME: disabled for now because it breaks some scripts + /* // Scripts if (entity.contains(SCRIPT_KEY)) { addScriptBaker(SCRIPT_KEY, entity[SCRIPT_KEY].toString(), *it); @@ -404,6 +406,7 @@ void DomainBaker::enumerateEntities() { if (entity.contains(SERVER_SCRIPTS_KEY)) { // TODO: serverScripts can be multiple scripts, need to handle that } + */ // Materials if (entity.contains(MATERIAL_URL_KEY)) { From b8b9035f9d4530f50bd46d3033c88d05bf83895b Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 8 Apr 2019 13:29:43 -0700 Subject: [PATCH 22/49] fix avatar/shape material targets, refresh material target when parent changes --- scripts/system/edit.js | 29 +++++++++++++--------- scripts/system/html/js/entityProperties.js | 25 +++++++++++++++++-- 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 11fc88dcbd..4f90c17e94 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -2523,18 +2523,23 @@ var PropertiesTool = function (opts) { propertyRanges: propertyRanges, }); } else if (data.type === "materialTargetRequest") { - var properties = Entities.getEntityProperties(data.entityID, ["type", "parentID"]); - var parentModel = properties.parentID !== Uuid.NULL && - Entities.getEntityProperties(properties.parentID, ["type"]).type === "Model"; - var parentModelData; - if (properties.type === "Material" && parentModel) { - parentModelData = Graphics.getModel(properties.parentID); - } - emitScriptEvent({ - type: 'materialTargetReply', - materialTargetData: parentModelData, - }); - } + var parentModelData; + var properties = Entities.getEntityProperties(data.entityID, ["type", "parentID"]); + if (properties.type === "Material" && properties.parentID !== Uuid.NULL) { + var parentType = Entities.getEntityProperties(properties.parentID, ["type"]).type; + if (parentType === "Model" || Entities.getNestableType(properties.parentID) === "avatar") { + parentModelData = Graphics.getModel(properties.parentID); + } else if (parentType === "Shape" || parentType === "Box" || parentType === "Sphere") { + parentModelData = {}; + parentModelData.numMeshes = 1; + parentModelData.materialNames = []; + } + } + emitScriptEvent({ + type: 'materialTargetReply', + materialTargetData: parentModelData, + }); + } }; HMD.displayModeChanged.connect(function() { diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index ca04f36dc9..1fe29cc579 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -55,6 +55,7 @@ const GROUPS = [ label: "Parent", type: "string", propertyID: "parentID", + onChange: parentIDChanged, }, { label: "Parent Joint Index", @@ -2007,6 +2008,9 @@ function createStringProperty(property, elProperty) { elInput.addEventListener('change', createEmitTextPropertyUpdateFunction(property)); + if (propertyData.onChange !== undefined) { + elInput.addEventListener('change', propertyData.onChange); + } elProperty.appendChild(elInput); @@ -2622,6 +2626,17 @@ function createProperty(propertyData, propertyElementID, propertyName, propertyI } +/** + * PROPERTY-SPECIFIC CALLBACKS + */ + +function parentIDChanged() { + if (selectedEntityProperties.type === "Material") { + requestMaterialTarget(); + } +} + + /** * BUTTON CALLBACKS */ @@ -3156,6 +3171,10 @@ function setTextareaScrolling(element) { * MATERIAL TARGET FUNCTIONS */ +function requestMaterialTarget() { + EventBridge.emitWebEvent(JSON.stringify({ type: 'materialTargetRequest', entityID: selectedEntityProperties.id })); +} + function setMaterialTargetData(materialTargetData) { let elDivOptions = getPropertyInputElement("parentMaterialName"); resetDynamicMultiselectProperty(elDivOptions); @@ -3256,7 +3275,9 @@ function sendMaterialTargetProperty() { if (materialTargetList !== "") { materialTargetList = materialTargetList.substring(0, materialTargetList.length - 1); - materialTargetList = "[" + materialTargetList + "]"; + if (materialTargetList.length > 1) { + materialTargetList = "[" + materialTargetList + "]"; + } } updateProperty("parentMaterialName", materialTargetList, false); @@ -3780,7 +3801,7 @@ function loaded() { } if (hasSelectedEntityChanged && selectedEntityProperties.type === "Material") { - EventBridge.emitWebEvent(JSON.stringify({ type: 'materialTargetRequest', entityID: selectedEntityProperties.id })); + requestMaterialTarget(); } let activeElement = document.activeElement; From 1537d4351d16a7bccf322553cc328448ef76e994 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 8 Apr 2019 14:07:25 -0700 Subject: [PATCH 23/49] CR changes from PR 15325 --- scripts/system/edit.js | 1 + scripts/system/html/js/entityProperties.js | 28 +++++++++++----------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 4f90c17e94..0346e1c7a1 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -2537,6 +2537,7 @@ var PropertiesTool = function (opts) { } emitScriptEvent({ type: 'materialTargetReply', + entityID: data.entityID, materialTargetData: parentModelData, }); } diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 1fe29cc579..bc8212ca1c 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -2480,7 +2480,7 @@ function resetDynamicMultiselectProperty(elDivOptions) { let elDivOption = elInputs[0].parentNode; elDivOption.parentNode.removeChild(elDivOption); } - elDivOptions.firstChild.style.display = "block"; // show "No Options" text + elDivOptions.firstChild.style.display = null; // show "No Options" text elDivOptions.parentNode.lastChild.style.display = "none"; // hide Select/Clear all buttons } @@ -3184,7 +3184,7 @@ function setMaterialTargetData(materialTargetData) { } elDivOptions.firstChild.style.display = "none"; // hide "No Options" text - elDivOptions.parentNode.lastChild.style.display = "block"; // show Select/Clear all buttons + elDivOptions.parentNode.lastChild.style.display = null; // show Select/Clear all buttons let numMeshes = materialTargetData.numMeshes; for (let i = 0; i < numMeshes; ++i) { @@ -3260,37 +3260,35 @@ function sendMaterialTargetProperty() { let elDivOptions = getPropertyInputElement("parentMaterialName"); let elInputs = elDivOptions.getElementsByClassName("materialTargetInput"); - let materialTargetList = ""; + 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 += "mat::" + targetID + ","; + materialTargetList.push("mat::" + targetID); } else { - materialTargetList += targetID + ","; + materialTargetList.push(targetID); } } } - if (materialTargetList !== "") { - materialTargetList = materialTargetList.substring(0, materialTargetList.length - 1); - if (materialTargetList.length > 1) { - materialTargetList = "[" + materialTargetList + "]"; - } + let propertyValue = materialTargetList.join(","); + if (propertyValue.length > 1) { + propertyValue = "[" + propertyValue + "]"; } - updateProperty("parentMaterialName", materialTargetList, false); + updateProperty("parentMaterialName", propertyValue, false); } function materialTargetPropertyUpdate(propertyValue) { let elDivOptions = getPropertyInputElement("parentMaterialName"); let elInputs = elDivOptions.getElementsByClassName("materialTargetInput"); - if (propertyValue.charAt(0) === '[') { + if (propertyValue.startsWith('[')) { propertyValue = propertyValue.substring(1, propertyValue.length); } - if (propertyValue.charAt(propertyValue.length - 1) === ']') { + if (propertyValue.endsWith(']')) { propertyValue = propertyValue.substring(0, propertyValue.length - 1); } @@ -3854,7 +3852,9 @@ function loaded() { } } } else if (data.type === 'materialTargetReply') { - setMaterialTargetData(data.materialTargetData); + if (data.entityID === selectedEntityProperties.id) { + setMaterialTargetData(data.materialTargetData); + } } }); From cb6f7a45f88ad9283aed1495804fd620d8dd3dd6 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Mon, 8 Apr 2019 15:30:05 -0700 Subject: [PATCH 24/49] Case 819 - Add min-listen-port command line parameter to assignment monitor This will allow users to specify a port range for assignment client UDP ports, allowing easier port forwarding, etc. --- assignment-client/src/AssignmentClientApp.cpp | 13 ++++++- assignment-client/src/AssignmentClientApp.h | 1 + .../src/AssignmentClientMonitor.cpp | 38 ++++++++++++++++--- .../src/AssignmentClientMonitor.h | 10 +++-- 4 files changed, 52 insertions(+), 10 deletions(-) diff --git a/assignment-client/src/AssignmentClientApp.cpp b/assignment-client/src/AssignmentClientApp.cpp index acfbb8571c..0b2f4c9295 100644 --- a/assignment-client/src/AssignmentClientApp.cpp +++ b/assignment-client/src/AssignmentClientApp.cpp @@ -64,6 +64,10 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) : "UDP port for this assignment client (or monitor)", "port"); parser.addOption(portOption); + const QCommandLineOption minChildListenPort(ASSIGNMENT_MONITOR_MIN_CHILDREN_LISTEN_PORT_OPTION, + "Minimum UDP listen port", "port"); + parser.addOption(minChildListenPort); + const QCommandLineOption walletDestinationOption(ASSIGNMENT_WALLET_DESTINATION_ID_OPTION, "set wallet destination", "wallet-uuid"); parser.addOption(walletDestinationOption); @@ -195,6 +199,11 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) : assignmentServerPort = parser.value(assignmentServerPortOption).toInt(); } + quint16 childMinListenPort = INVALID_PORT; + if (argumentVariantMap.contains(ASSIGNMENT_MONITOR_MIN_CHILDREN_LISTEN_PORT_OPTION)) { + childMinListenPort = argumentVariantMap.value(ASSIGNMENT_MONITOR_MIN_CHILDREN_LISTEN_PORT_OPTION).toUInt(); + } + // check for an overidden listen port quint16 listenPort = 0; if (argumentVariantMap.contains(ASSIGNMENT_CLIENT_LISTEN_PORT_OPTION)) { @@ -234,8 +243,8 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) : if (numForks || minForks || maxForks) { AssignmentClientMonitor* monitor = new AssignmentClientMonitor(numForks, minForks, maxForks, - requestAssignmentType, assignmentPool, - listenPort, walletUUID, assignmentServerHostname, + requestAssignmentType, assignmentPool, listenPort, + childMinListenPort, walletUUID, assignmentServerHostname, assignmentServerPort, httpStatusPort, logDirectory); monitor->setParent(this); connect(this, &QCoreApplication::aboutToQuit, monitor, &AssignmentClientMonitor::aboutToQuit); diff --git a/assignment-client/src/AssignmentClientApp.h b/assignment-client/src/AssignmentClientApp.h index 37d3b9cc1d..1b50922980 100644 --- a/assignment-client/src/AssignmentClientApp.h +++ b/assignment-client/src/AssignmentClientApp.h @@ -20,6 +20,7 @@ const QString ASSIGNMENT_POOL_OPTION = "pool"; const QString ASSIGNMENT_CLIENT_LISTEN_PORT_OPTION = "p"; const QString ASSIGNMENT_WALLET_DESTINATION_ID_OPTION = "wallet"; const QString CUSTOM_ASSIGNMENT_SERVER_HOSTNAME_OPTION = "a"; +const QString ASSIGNMENT_MONITOR_MIN_CHILDREN_LISTEN_PORT_OPTION = "min-listen-port"; const QString CUSTOM_ASSIGNMENT_SERVER_PORT_OPTION = "server-port"; const QString ASSIGNMENT_NUM_FORKS_OPTION = "n"; const QString ASSIGNMENT_MIN_FORKS_OPTION = "min"; diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp index fefed6e143..c662ffeb03 100644 --- a/assignment-client/src/AssignmentClientMonitor.cpp +++ b/assignment-client/src/AssignmentClientMonitor.cpp @@ -40,7 +40,7 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen const unsigned int minAssignmentClientForks, const unsigned int maxAssignmentClientForks, Assignment::Type requestAssignmentType, QString assignmentPool, - quint16 listenPort, QUuid walletUUID, QString assignmentServerHostname, + quint16 listenPort, quint16 childMinListenPort, QUuid walletUUID, QString assignmentServerHostname, quint16 assignmentServerPort, quint16 httpStatusServerPort, QString logDirectory) : _httpManager(QHostAddress::LocalHost, httpStatusServerPort, "", this), _numAssignmentClientForks(numAssignmentClientForks), @@ -48,6 +48,7 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen _maxAssignmentClientForks(maxAssignmentClientForks), _requestAssignmentType(requestAssignmentType), _assignmentPool(assignmentPool), + _childMinListenPort(childMinListenPort), _walletUUID(walletUUID), _assignmentServerHostname(assignmentServerHostname), _assignmentServerPort(assignmentServerPort) @@ -100,8 +101,13 @@ void AssignmentClientMonitor::simultaneousWaitOnChildren(int waitMsecs) { } } -void AssignmentClientMonitor::childProcessFinished(qint64 pid, int exitCode, QProcess::ExitStatus exitStatus) { - auto message = "Child process " + QString::number(pid) + " has %1 with exit code " + QString::number(exitCode) + "."; +void AssignmentClientMonitor::childProcessFinished(qint64 pid, quint16 listenPort, int exitCode, QProcess::ExitStatus exitStatus) { + auto message = "Child process " + QString::number(pid) + " on port " + QString::number(listenPort) + + "has %1 with exit code " + QString::number(exitCode) + "."; + + if (listenPort != INVALID_PORT) { + _childListenPorts.remove(listenPort); + } if (_childProcesses.remove(pid)) { message.append(" Removed from internal map."); @@ -153,6 +159,23 @@ void AssignmentClientMonitor::aboutToQuit() { void AssignmentClientMonitor::spawnChildClient() { QProcess* assignmentClient = new QProcess(this); + quint16 listenPort = INVALID_PORT; + // allocate a port + + if (_childMinListenPort != INVALID_PORT) { + for (listenPort = _childMinListenPort; _childListenPorts.contains(listenPort); listenPort++) { + if (_maxAssignmentClientForks && + (listenPort >= _maxAssignmentClientForks + _childMinListenPort)) { + listenPort = INVALID_PORT; + qDebug() << "Insufficient listen ports"; + break; + } + } + } + if (listenPort != INVALID_PORT) { + _childListenPorts.insert(listenPort); + } + // unparse the parts of the command-line that the child cares about QStringList _childArguments; if (_assignmentPool != "") { @@ -176,6 +199,11 @@ void AssignmentClientMonitor::spawnChildClient() { _childArguments.append(QString::number(_requestAssignmentType)); } + if (listenPort != INVALID_PORT) { + _childArguments.append("-" + ASSIGNMENT_CLIENT_LISTEN_PORT_OPTION); + _childArguments.append(QString::number(listenPort)); + } + // tell children which assignment monitor port to use // for now they simply talk to us on localhost _childArguments.append("--" + ASSIGNMENT_CLIENT_MONITOR_PORT_OPTION); @@ -247,8 +275,8 @@ void AssignmentClientMonitor::spawnChildClient() { auto pid = assignmentClient->processId(); // make sure we hear that this process has finished when it does connect(assignmentClient, static_cast(&QProcess::finished), - this, [this, pid](int exitCode, QProcess::ExitStatus exitStatus) { - childProcessFinished(pid, exitCode, exitStatus); + this, [this, listenPort, pid](int exitCode, QProcess::ExitStatus exitStatus) { + childProcessFinished(pid, listenPort, exitCode, exitStatus); }); qDebug() << "Spawned a child client with PID" << assignmentClient->processId(); diff --git a/assignment-client/src/AssignmentClientMonitor.h b/assignment-client/src/AssignmentClientMonitor.h index 5e32c50e0d..f5355476b7 100644 --- a/assignment-client/src/AssignmentClientMonitor.h +++ b/assignment-client/src/AssignmentClientMonitor.h @@ -37,14 +37,15 @@ class AssignmentClientMonitor : public QObject, public HTTPRequestHandler { public: AssignmentClientMonitor(const unsigned int numAssignmentClientForks, const unsigned int minAssignmentClientForks, const unsigned int maxAssignmentClientForks, Assignment::Type requestAssignmentType, - QString assignmentPool, quint16 listenPort, QUuid walletUUID, QString assignmentServerHostname, - quint16 assignmentServerPort, quint16 httpStatusServerPort, QString logDirectory); + QString assignmentPool, quint16 listenPort, quint16 childMinListenPort, QUuid walletUUID, + QString assignmentServerHostname, quint16 assignmentServerPort, quint16 httpStatusServerPort, + QString logDirectory); ~AssignmentClientMonitor(); void stopChildProcesses(); private slots: void checkSpares(); - void childProcessFinished(qint64 pid, int exitCode, QProcess::ExitStatus exitStatus); + void childProcessFinished(qint64 pid, quint16 port, int exitCode, QProcess::ExitStatus exitStatus); void handleChildStatusPacket(QSharedPointer message); bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler = false) override; @@ -75,6 +76,9 @@ private: QMap _childProcesses; + quint16 _childMinListenPort; + QSet _childListenPorts; + bool _wantsChildFileLogging { false }; }; From 377d412264e6422c80e525bfbc6555c54ba7c93c Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Mon, 8 Apr 2019 16:29:42 -0700 Subject: [PATCH 25/49] Fix mac/ubuntu compiler warning --- assignment-client/src/AssignmentClientApp.cpp | 2 +- assignment-client/src/AssignmentClientMonitor.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/assignment-client/src/AssignmentClientApp.cpp b/assignment-client/src/AssignmentClientApp.cpp index 0b2f4c9295..a87200dc5b 100644 --- a/assignment-client/src/AssignmentClientApp.cpp +++ b/assignment-client/src/AssignmentClientApp.cpp @@ -199,7 +199,7 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) : assignmentServerPort = parser.value(assignmentServerPortOption).toInt(); } - quint16 childMinListenPort = INVALID_PORT; + quint16 childMinListenPort = 0; if (argumentVariantMap.contains(ASSIGNMENT_MONITOR_MIN_CHILDREN_LISTEN_PORT_OPTION)) { childMinListenPort = argumentVariantMap.value(ASSIGNMENT_MONITOR_MIN_CHILDREN_LISTEN_PORT_OPTION).toUInt(); } diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp index c662ffeb03..918c89527f 100644 --- a/assignment-client/src/AssignmentClientMonitor.cpp +++ b/assignment-client/src/AssignmentClientMonitor.cpp @@ -105,7 +105,7 @@ void AssignmentClientMonitor::childProcessFinished(qint64 pid, quint16 listenPor auto message = "Child process " + QString::number(pid) + " on port " + QString::number(listenPort) + "has %1 with exit code " + QString::number(exitCode) + "."; - if (listenPort != INVALID_PORT) { + if (listenPort) { _childListenPorts.remove(listenPort); } @@ -159,20 +159,20 @@ void AssignmentClientMonitor::aboutToQuit() { void AssignmentClientMonitor::spawnChildClient() { QProcess* assignmentClient = new QProcess(this); - quint16 listenPort = INVALID_PORT; + quint16 listenPort = 0; // allocate a port - if (_childMinListenPort != INVALID_PORT) { + if (_childMinListenPort) { for (listenPort = _childMinListenPort; _childListenPorts.contains(listenPort); listenPort++) { if (_maxAssignmentClientForks && (listenPort >= _maxAssignmentClientForks + _childMinListenPort)) { - listenPort = INVALID_PORT; + listenPort = 0; qDebug() << "Insufficient listen ports"; break; } } } - if (listenPort != INVALID_PORT) { + if (listenPort) { _childListenPorts.insert(listenPort); } @@ -199,7 +199,7 @@ void AssignmentClientMonitor::spawnChildClient() { _childArguments.append(QString::number(_requestAssignmentType)); } - if (listenPort != INVALID_PORT) { + if (listenPort) { _childArguments.append("-" + ASSIGNMENT_CLIENT_LISTEN_PORT_OPTION); _childArguments.append(QString::number(listenPort)); } From 68cf18e2a0383e92a110bcbc584e70051515be93 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Mon, 8 Apr 2019 16:35:15 -0700 Subject: [PATCH 26/49] Fix another compiler warning. --- assignment-client/src/AssignmentClientMonitor.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/AssignmentClientMonitor.cpp b/assignment-client/src/AssignmentClientMonitor.cpp index 918c89527f..6601be849f 100644 --- a/assignment-client/src/AssignmentClientMonitor.cpp +++ b/assignment-client/src/AssignmentClientMonitor.cpp @@ -48,11 +48,10 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen _maxAssignmentClientForks(maxAssignmentClientForks), _requestAssignmentType(requestAssignmentType), _assignmentPool(assignmentPool), - _childMinListenPort(childMinListenPort), _walletUUID(walletUUID), _assignmentServerHostname(assignmentServerHostname), - _assignmentServerPort(assignmentServerPort) - + _assignmentServerPort(assignmentServerPort), + _childMinListenPort(childMinListenPort) { qDebug() << "_requestAssignmentType =" << _requestAssignmentType; From 27338c363917e21e0d91d82e4133ecabcd59c154 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 8 Apr 2019 19:48:11 -0700 Subject: [PATCH 27/49] fix cauterization of entities that are children of MyAvatar's head --- interface/src/avatar/MyAvatar.cpp | 30 +++++++------------ .../src/RenderableEntityItem.cpp | 12 ++++---- .../src/RenderableModelEntityItem.cpp | 11 +++---- .../src/RenderableModelEntityItem.h | 2 -- libraries/entities/src/EntityItem.cpp | 20 +++++++++++++ libraries/entities/src/EntityItem.h | 6 ++-- .../render-utils/src/MeshPartPayload.cpp | 2 +- libraries/render-utils/src/MeshPartPayload.h | 3 ++ libraries/render-utils/src/Model.cpp | 21 ++++++++++++- libraries/render-utils/src/Model.h | 4 +++ libraries/render-utils/src/RenderHifi.h | 6 ++-- .../render-utils/src/RenderShadowTask.cpp | 14 +++------ libraries/render-utils/src/RenderShadowTask.h | 6 ++-- libraries/render/src/render/Args.h | 2 +- .../src/render/RenderFetchCullSortTask.h | 2 +- libraries/shared/src/SpatiallyNestable.cpp | 18 ++++++++--- 16 files changed, 96 insertions(+), 63 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 568b492b46..d6b753c734 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -818,20 +818,8 @@ void MyAvatar::simulate(float deltaTime, bool inView) { if (_cauterizationNeedsUpdate) { _cauterizationNeedsUpdate = false; - // Redisplay cauterized entities that are no longer children of the avatar. - auto cauterizedChild = _cauterizedChildrenOfHead.begin(); - if (cauterizedChild != _cauterizedChildrenOfHead.end()) { - auto children = getChildren(); - while (cauterizedChild != _cauterizedChildrenOfHead.end()) { - if (!children.contains(*cauterizedChild)) { - updateChildCauterization(*cauterizedChild, false); - cauterizedChild = _cauterizedChildrenOfHead.erase(cauterizedChild); - } else { - ++cauterizedChild; - } - } - } - + auto objectsToUncauterize = _cauterizedChildrenOfHead; + _cauterizedChildrenOfHead.clear(); // Update cauterization of entities that are children of the avatar. auto headBoneSet = _skeletonModel->getCauterizeBoneSet(); forEachChild([&](SpatiallyNestablePointer object) { @@ -843,15 +831,19 @@ void MyAvatar::simulate(float deltaTime, bool inView) { updateChildCauterization(descendant, !_prevShouldDrawHead); }); _cauterizedChildrenOfHead.insert(object); - } else if (_cauterizedChildrenOfHead.find(object) != _cauterizedChildrenOfHead.end()) { - // Redisplay cauterized children that are not longer children of the head. - updateChildCauterization(object, false); + objectsToUncauterize.erase(object); + } else if (objectsToUncauterize.find(object) == objectsToUncauterize.end()) { + objectsToUncauterize.insert(object); object->forEachDescendant([&](SpatiallyNestablePointer descendant) { - updateChildCauterization(descendant, false); + objectsToUncauterize.insert(descendant); }); - _cauterizedChildrenOfHead.erase(object); } }); + + // Redisplay cauterized entities that are no longer children of the avatar. + for (auto cauterizedChild = objectsToUncauterize.begin(); cauterizedChild != objectsToUncauterize.end(); cauterizedChild++) { + updateChildCauterization(*cauterizedChild, false); + } } { diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index 3a56521702..fb6fbad2ac 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -166,7 +166,10 @@ ShapeKey EntityRenderer::getShapeKey() { } render::hifi::Tag EntityRenderer::getTagMask() const { - return _isVisibleInSecondaryCamera ? render::hifi::TAG_ALL_VIEWS : render::hifi::TAG_MAIN_VIEW; + render::hifi::Tag mask = render::hifi::TAG_NONE; + mask = (render::hifi::Tag)(mask | (!_cauterized * render::hifi::TAG_MAIN_VIEW)); + mask = (render::hifi::Tag)(mask | (_isVisibleInSecondaryCamera * render::hifi::TAG_SECONDARY_VIEW)); + return mask; } render::hifi::Layer EntityRenderer::getHifiRenderLayer() const { @@ -215,12 +218,7 @@ void EntityRenderer::render(RenderArgs* args) { emit requestRenderUpdate(); } - auto& renderMode = args->_renderMode; - bool cauterized = (renderMode != RenderArgs::RenderMode::SHADOW_RENDER_MODE && - renderMode != RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE && - _cauterized); - - if (_visible && !cauterized) { + if (_visible && (args->_renderMode != RenderArgs::RenderMode::DEFAULT_RENDER_MODE || !_cauterized)) { doRender(args); } } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index f921f6eca6..bfbbe12ea6 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1066,13 +1066,6 @@ ItemKey ModelEntityRenderer::getKey() { return _itemKey; } -render::hifi::Tag ModelEntityRenderer::getTagMask() const { - // Default behavior for model is to not be visible in main view if cauterized (aka parented to the avatar's neck joint) - return _cauterized ? - (_isVisibleInSecondaryCamera ? render::hifi::TAG_SECONDARY_VIEW : render::hifi::TAG_NONE) : - Parent::getTagMask(); // calculate which views to be shown in -} - uint32_t ModelEntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) { if (_model) { auto metaSubItems = _model->fetchRenderItemIDs(); @@ -1409,6 +1402,10 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce model->setVisibleInScene(_visible, scene); } + if (model->isCauterized() != _cauterized) { + model->setCauterized(_cauterized, scene); + } + render::hifi::Tag tagMask = getTagMask(); if (model->getTagMask() != tagMask) { model->setTagMask(tagMask, scene); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 2fd1041c5f..ee6e7d0b04 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -161,8 +161,6 @@ protected: virtual void doRender(RenderArgs* args) override; virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override; - render::hifi::Tag getTagMask() const override; - void setIsVisibleInSecondaryCamera(bool value) override; void setRenderLayer(RenderLayer value) override; void setPrimitiveMode(PrimitiveMode value) override; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 8a50c39da9..a532064b6c 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -3007,6 +3007,26 @@ void EntityItem::setPrimitiveMode(PrimitiveMode value) { } } +bool EntityItem::getCauterized() const { + return resultWithReadLock([&] { + return _cauterized; + }); +} + +void EntityItem::setCauterized(bool value) { + bool changed = false; + withWriteLock([&] { + if (_cauterized != value) { + changed = true; + _cauterized = value; + } + }); + + if (changed) { + emit requestRenderUpdate(); + } +} + bool EntityItem::getIgnorePickIntersection() const { return resultWithReadLock([&] { return _ignorePickIntersection; diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index c57fd16a2e..29a1a8d73c 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -303,6 +303,9 @@ public: bool getCanCastShadow() const; void setCanCastShadow(bool value); + void setCauterized(bool value); + bool getCauterized() const; + inline bool isVisible() const { return getVisible(); } inline bool isInvisible() const { return !getVisible(); } @@ -530,9 +533,6 @@ public: static QString _marketplacePublicKey; static void retrieveMarketplacePublicKey(); - void setCauterized(bool value) { _cauterized = value; } - bool getCauterized() const { return _cauterized; } - float getBoundingRadius() const { return _boundingRadius; } void setSpaceIndex(int32_t index); int32_t getSpaceIndex() const { return _spaceIndex; } diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index b1104b8aad..2634784f3a 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -416,7 +416,7 @@ void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, RenderArgs::RenderMo void ModelMeshPartPayload::render(RenderArgs* args) { PerformanceTimer perfTimer("ModelMeshPartPayload::render"); - if (!args) { + if (!args || (args->_renderMode == RenderArgs::RenderMode::DEFAULT_RENDER_MODE && _cauterized)) { return; } diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index deae91dda9..641fc81487 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -107,6 +107,7 @@ public: void render(RenderArgs* args) override; void setShapeKey(bool invalidateShapeKey, PrimitiveMode primitiveMode, bool useDualQuaternionSkinning); + void setCauterized(bool cauterized) { _cauterized = cauterized; } // ModelMeshPartPayload functions to perform render void bindMesh(gpu::Batch& batch) override; @@ -138,6 +139,8 @@ private: gpu::BufferPointer _meshBlendshapeBuffer; int _meshNumVertices; render::ShapeKey _shapeKey { render::ShapeKey::Builder::invalid() }; + bool _cauterized { false }; + }; namespace render { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 6b33012adf..ac47b48c5f 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -224,6 +224,7 @@ void Model::updateRenderItems() { PrimitiveMode primitiveMode = self->getPrimitiveMode(); auto renderItemKeyGlobalFlags = self->getRenderItemKeyGlobalFlags(); + bool cauterized = self->isCauterized(); render::Transaction transaction; for (int i = 0; i < (int) self->_modelMeshRenderItemIDs.size(); i++) { @@ -237,7 +238,7 @@ void Model::updateRenderItems() { bool useDualQuaternionSkinning = self->getUseDualQuaternionSkinning(); transaction.updateItem(itemID, [modelTransform, meshState, useDualQuaternionSkinning, - invalidatePayloadShapeKey, primitiveMode, renderItemKeyGlobalFlags](ModelMeshPartPayload& data) { + invalidatePayloadShapeKey, primitiveMode, renderItemKeyGlobalFlags, cauterized](ModelMeshPartPayload& data) { if (useDualQuaternionSkinning) { data.updateClusterBuffer(meshState.clusterDualQuaternions); } else { @@ -261,6 +262,7 @@ void Model::updateRenderItems() { } data.updateTransformForSkinnedMesh(renderTransform, modelTransform); + data.setCauterized(cauterized); data.updateKey(renderItemKeyGlobalFlags); data.setShapeKey(invalidatePayloadShapeKey, primitiveMode, useDualQuaternionSkinning); }); @@ -922,6 +924,23 @@ bool Model::isGroupCulled() const { return _renderItemKeyGlobalFlags.isSubMetaCulled(); } +void Model::setCauterized(bool cauterized, const render::ScenePointer& scene) { + if (Model::isCauterized() != cauterized) { + _cauterized = cauterized; + if (!scene) { + _needsFixupInScene = true; + return; + } + render::Transaction transaction; + foreach (auto item, _modelMeshRenderItemsMap.keys()) { + transaction.updateItem(item, [cauterized](ModelMeshPartPayload& data) { + data.setCauterized(cauterized); + }); + } + scene->enqueueTransaction(transaction); + } +} + const render::ItemKey Model::getRenderItemKeyGlobalFlags() const { return _renderItemKeyGlobalFlags; } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 3c207c982c..1431b5e3f9 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -126,6 +126,9 @@ public: void setHifiRenderLayer(render::hifi::Layer layer, const render::ScenePointer& scene = nullptr); + bool isCauterized() const { return _cauterized; } + void setCauterized(bool value, const render::ScenePointer& scene); + // Access the current RenderItemKey Global Flags used by the model and applied to the render items representing the parts of the model. const render::ItemKey getRenderItemKeyGlobalFlags() const; @@ -502,6 +505,7 @@ protected: // For this to work, a Meta RI must exists and knows about the RIs of this Model. // render::ItemKey _renderItemKeyGlobalFlags; + bool _cauterized { false }; bool shouldInvalidatePayloadShapeKey(int meshIndex); diff --git a/libraries/render-utils/src/RenderHifi.h b/libraries/render-utils/src/RenderHifi.h index c489a0ad1d..8d3fe7e4d1 100644 --- a/libraries/render-utils/src/RenderHifi.h +++ b/libraries/render-utils/src/RenderHifi.h @@ -23,9 +23,9 @@ namespace render { // Tag is the alias names of render::ItemKey::Tag combinations used in the Hifi Render Engine enum Tag : uint8_t { - TAG_NONE = render::ItemKey::TAG_BITS_NONE, // No Tags at all - TAG_MAIN_VIEW = render::ItemKey::TAG_BITS_0, // Main view - TAG_SECONDARY_VIEW = render::ItemKey::TAG_BITS_1, // Secondary View + TAG_NONE = render::ItemKey::TAG_BITS_NONE, // No Tags at all + TAG_MAIN_VIEW = render::ItemKey::TAG_BITS_0, // Main view + TAG_SECONDARY_VIEW = render::ItemKey::TAG_BITS_1, // Secondary View TAG_ALL_VIEWS = TAG_MAIN_VIEW | TAG_SECONDARY_VIEW, // All views }; diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 5456453d8a..a261deefb8 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -58,10 +58,6 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende initZPassPipelines(*shapePlumber, state, fadeEffect->getBatchSetter(), fadeEffect->getItemUniformSetter()); } - // FIXME: calling this here before the zones/lights are drawn during the deferred/forward passes means we're actually using the frames from the previous draw - // Fetch the current frame stacks from all the stages - // Starting with the Light Frame genreated in previous tasks - const auto setupOutput = task.addJob("ShadowSetup", input); const auto queryResolution = setupOutput.getN(1); const auto shadowFrame = setupOutput.getN(3); @@ -99,7 +95,7 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) { char jobName[64]; sprintf(jobName, "ShadowCascadeSetup%d", i); - const auto cascadeSetupOutput = task.addJob(jobName, shadowFrame, i, tagBits, tagMask); + const auto cascadeSetupOutput = task.addJob(jobName, shadowFrame, i, shadowCasterReceiverFilter); const auto shadowFilter = cascadeSetupOutput.getN(0); auto antiFrustum = render::Varying(ViewFrustumPointer()); cascadeFrustums[i] = cascadeSetupOutput.getN(1); @@ -452,8 +448,7 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon const auto globalShadow = shadowFrame->_objects[0]; if (globalShadow && _cascadeIndex < globalShadow->getCascadeCount()) { - // Second item filter is to filter items to keep in shadow frustum computation (here we need to keep shadow receivers) - output.edit0() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(_tagBits, _tagMask); + output.edit0() = _filter; // Set the keylight render args auto& cascade = globalShadow->getCascade(_cascadeIndex); @@ -551,7 +546,6 @@ void CullShadowBounds::run(const render::RenderContextPointer& renderContext, co assert(lightStage); const auto globalLightDir = currentKeyLight->getDirection(); auto castersFilter = render::ItemFilter::Builder(filter).withShadowCaster().build(); - const auto& receiversFilter = filter; for (auto& inItems : inShapes) { auto key = inItems.first; @@ -570,7 +564,7 @@ void CullShadowBounds::run(const render::RenderContextPointer& renderContext, co if (castersFilter.test(shapeKey)) { outItems->second.emplace_back(item); outBounds += item.bound; - } else if (receiversFilter.test(shapeKey)) { + } else { // Receivers are not rendered but they still increase the bounds of the shadow scene // although only in the direction of the light direction so as to have a correct far // distance without decreasing the near distance. @@ -585,7 +579,7 @@ void CullShadowBounds::run(const render::RenderContextPointer& renderContext, co if (castersFilter.test(shapeKey)) { outItems->second.emplace_back(item); outBounds += item.bound; - } else if (receiversFilter.test(shapeKey)) { + } else { // Receivers are not rendered but they still increase the bounds of the shadow scene // although only in the direction of the light direction so as to have a correct far // distance without decreasing the near distance. diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index 7e7d59763e..4dc6f3073f 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -134,15 +134,13 @@ public: using Outputs = render::VaryingSet3; using JobModel = render::Job::ModelIO; - RenderShadowCascadeSetup(unsigned int cascadeIndex, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00) : - _cascadeIndex(cascadeIndex), _tagBits(tagBits), _tagMask(tagMask) {} + RenderShadowCascadeSetup(unsigned int cascadeIndex, render::ItemFilter filter) : _cascadeIndex(cascadeIndex), _filter(filter) {} void run(const render::RenderContextPointer& renderContext, const Inputs& input, Outputs& output); private: unsigned int _cascadeIndex; - uint8_t _tagBits { 0x00 }; - uint8_t _tagMask { 0x00 }; + render::ItemFilter _filter; }; class RenderShadowCascadeTeardown { diff --git a/libraries/render/src/render/Args.h b/libraries/render/src/render/Args.h index b5c98e3428..78e9909222 100644 --- a/libraries/render/src/render/Args.h +++ b/libraries/render/src/render/Args.h @@ -61,7 +61,7 @@ namespace render { class Args { public: - enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE, MIRROR_RENDER_MODE, SECONDARY_CAMERA_RENDER_MODE }; + enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, MIRROR_RENDER_MODE, SECONDARY_CAMERA_RENDER_MODE }; enum DisplayMode { MONO, STEREO_MONITOR, STEREO_HMD }; enum DebugFlags { RENDER_DEBUG_NONE = 0, diff --git a/libraries/render/src/render/RenderFetchCullSortTask.h b/libraries/render/src/render/RenderFetchCullSortTask.h index 1b1e4a5d8f..0b475614a1 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.h +++ b/libraries/render/src/render/RenderFetchCullSortTask.h @@ -39,7 +39,7 @@ public: RenderFetchCullSortTask() {} - void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00); + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask); }; #endif // hifi_RenderFetchCullSortTask_h diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 4d97f43e6a..866698adeb 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -68,19 +68,28 @@ const QUuid SpatiallyNestable::getParentID() const { void SpatiallyNestable::setParentID(const QUuid& parentID) { bumpAncestorChainRenderableVersion(); + bool success = false; + auto parent = getParentPointer(success); + bool parentChanged = false; _idLock.withWriteLock([&] { if (_parentID != parentID) { + parentChanged = true; _parentID = parentID; _parentKnowsMe = false; } }); + if (parentChanged && success && parent) { + parent->recalculateChildCauterization(); + } + if (!_parentKnowsMe) { - bool success = false; - auto parent = getParentPointer(success); + success = false; + parent = getParentPointer(success); if (success && parent) { bumpAncestorChainRenderableVersion(); parent->updateQueryAACube(); + parent->recalculateChildCauterization(); } } } @@ -175,8 +184,9 @@ void SpatiallyNestable::forgetChild(SpatiallyNestablePointer newChild) const { void SpatiallyNestable::setParentJointIndex(quint16 parentJointIndex) { _parentJointIndex = parentJointIndex; - auto parent = _parent.lock(); - if (parent) { + bool success = false; + auto parent = getParentPointer(success); + if (success && parent) { parent->recalculateChildCauterization(); } } From a50cca006e5d1be145e03d851663937a640652cd Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 8 Apr 2019 23:45:47 -0700 Subject: [PATCH 28/49] use # instead of ? for material URL name selection --- libraries/baking/src/ModelBaker.cpp | 2 +- .../entities-renderer/src/RenderableMaterialEntityItem.cpp | 6 +++++- libraries/entities/src/EntityItemProperties.cpp | 2 +- libraries/render-utils/src/Model.cpp | 4 ++-- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/libraries/baking/src/ModelBaker.cpp b/libraries/baking/src/ModelBaker.cpp index 82af2f94e9..e58ec00afa 100644 --- a/libraries/baking/src/ModelBaker.cpp +++ b/libraries/baking/src/ModelBaker.cpp @@ -285,7 +285,7 @@ void ModelBaker::handleFinishedMaterialBaker() { QJsonArray materialMapping; for (auto material : _hfmModel->materials) { QJsonObject json; - json["mat::" + material.name] = relativeBakedMaterialURL + "?" + material.name; + json["mat::" + material.name] = relativeBakedMaterialURL + "#" + material.name; materialMapping.push_back(json); } diff --git a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp index da8baca95a..01d1098daa 100644 --- a/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableMaterialEntityItem.cpp @@ -121,7 +121,11 @@ void MaterialEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPo QString materialURL = entity->getMaterialURL(); if (materialURL != _materialURL) { _materialURL = materialURL; - if (_materialURL.contains("?")) { + if (_materialURL.contains("#")) { + auto split = _materialURL.split("#"); + newCurrentMaterialName = split.last().toStdString(); + } else if (_materialURL.contains("?")) { + qDebug() << "DEPRECATED: Use # instead of ? for material URLS:" << _materialURL; auto split = _materialURL.split("?"); newCurrentMaterialName = split.last().toStdString(); } diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 5958af66dd..44e317696c 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -976,7 +976,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * by setting the entityHostType parameter in {@link Entities.addEntity} to "avatar". * Material entities render as non-scalable spheres if they don't have their parent set. * @typedef {object} Entities.EntityProperties-Material - * @property {string} materialURL="" - URL to a {@link MaterialResource}. If you append ?name to the URL, the + * @property {string} materialURL="" - URL to a {@link MaterialResource}. If you append #name to the URL, the * material with that name in the {@link MaterialResource} will be applied to the entity.
* Alternatively, set the property value to "materialData" to use the materialData property * for the {@link MaterialResource} values. diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 6b33012adf..fbe0af11f2 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1561,8 +1561,8 @@ void Model::applyMaterialMapping() { { QString url = networkMaterialResource->getURL().toString(); bool foundMaterialName = false; - if (url.contains("?")) { - auto split = url.split("?"); + if (url.contains("#")) { + auto split = url.split("#"); std::string materialName = split.last().toStdString(); auto networkMaterialIter = networkMaterialResource->parsedMaterials.networkMaterials.find(materialName); if (networkMaterialIter != networkMaterialResource->parsedMaterials.networkMaterials.end()) { From 02d57699916c60fe3aa816b192c170bd2cb1592e Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 9 Apr 2019 12:57:03 -0700 Subject: [PATCH 29/49] Better head vs camera checks for avatar head cauterization * cameraInsideHead() check now uses detailed avatar collision when possible. * head is now more constantly hidden in first person camera mode * getEyeModelPositions() uses a better estimate when avatar eye joints are missing. * moved findPointKDopDisplacement from Rig.cpp into AnimUtil.cpp * added isPlayingOverrideAnimation() method to Rig class --- interface/src/avatar/MyAvatar.cpp | 29 +++++++- libraries/animation/src/AnimUtil.cpp | 69 +++++++++++++++++++ libraries/animation/src/AnimUtil.h | 6 ++ libraries/animation/src/Rig.cpp | 68 ------------------ libraries/animation/src/Rig.h | 5 +- .../src/avatars-renderer/SkeletonModel.cpp | 29 ++------ libraries/shared/src/AvatarConstants.h | 1 + 7 files changed, 112 insertions(+), 95 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 568b492b46..dcd768f2ae 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -3180,17 +3180,40 @@ int MyAvatar::sendAvatarDataPacket(bool sendAll) { return bytesSent; } -const float RENDER_HEAD_CUTOFF_DISTANCE = 0.47f; - bool MyAvatar::cameraInsideHead(const glm::vec3& cameraPosition) const { + if (!_skeletonModel) { + return false; + } + + // transform cameraPosition into rig coordinates + AnimPose rigToWorld = AnimPose(getWorldOrientation() * Quaternions::Y_180, getWorldPosition()); + AnimPose worldToRig = rigToWorld.inverse(); + glm::vec3 rigCameraPosition = worldToRig * cameraPosition; + + // use head k-dop shape to determine if camera is inside head. + const Rig& rig = _skeletonModel->getRig(); + int headJointIndex = rig.indexOfJoint("Head"); + if (headJointIndex >= 0) { + const HFMModel& hfmModel = _skeletonModel->getHFMModel(); + AnimPose headPose; + if (rig.getAbsoluteJointPoseInRigFrame(headJointIndex, headPose)) { + glm::vec3 displacement; + const HFMJointShapeInfo& headShapeInfo = hfmModel.joints[headJointIndex].shapeInfo; + return findPointKDopDisplacement(rigCameraPosition, headPose, headShapeInfo, displacement); + } + } + + // fall back to simple distance check. + const float RENDER_HEAD_CUTOFF_DISTANCE = 0.47f; return glm::length(cameraPosition - getHeadPosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getModelScale()); } bool MyAvatar::shouldRenderHead(const RenderArgs* renderArgs) const { bool defaultMode = renderArgs->_renderMode == RenderArgs::DEFAULT_RENDER_MODE; bool firstPerson = qApp->getCamera().getMode() == CAMERA_MODE_FIRST_PERSON; + bool overrideAnim = _skeletonModel ? _skeletonModel->getRig().isPlayingOverrideAnimation() : false; bool insideHead = cameraInsideHead(renderArgs->getViewFrustum().getPosition()); - return !defaultMode || !firstPerson || !insideHead; + return !defaultMode || (!firstPerson && !insideHead) || (overrideAnim && !insideHead); } void MyAvatar::setHasScriptedBlendshapes(bool hasScriptedBlendshapes) { diff --git a/libraries/animation/src/AnimUtil.cpp b/libraries/animation/src/AnimUtil.cpp index c23e228556..5fca2b4f88 100644 --- a/libraries/animation/src/AnimUtil.cpp +++ b/libraries/animation/src/AnimUtil.cpp @@ -142,3 +142,72 @@ glm::quat computeBodyFacingFromHead(const glm::quat& headRot, const glm::vec3& u return glmExtractRotation(bodyMat); } + + +const float INV_SQRT_3 = 1.0f / sqrtf(3.0f); +const int DOP14_COUNT = 14; +const glm::vec3 DOP14_NORMALS[DOP14_COUNT] = { + Vectors::UNIT_X, + -Vectors::UNIT_X, + Vectors::UNIT_Y, + -Vectors::UNIT_Y, + Vectors::UNIT_Z, + -Vectors::UNIT_Z, + glm::vec3(INV_SQRT_3, INV_SQRT_3, INV_SQRT_3), + -glm::vec3(INV_SQRT_3, INV_SQRT_3, INV_SQRT_3), + glm::vec3(INV_SQRT_3, -INV_SQRT_3, INV_SQRT_3), + -glm::vec3(INV_SQRT_3, -INV_SQRT_3, INV_SQRT_3), + glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3), + -glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3), + glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3), + -glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3) +}; + +// returns true if the given point lies inside of the k-dop, specified by shapeInfo & shapePose. +// if the given point does lie within the k-dop, it also returns the amount of displacement necessary to push that point outward +// such that it lies on the surface of the kdop. +bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& shapePose, const HFMJointShapeInfo& shapeInfo, glm::vec3& displacementOut) { + + // transform point into local space of jointShape. + glm::vec3 localPoint = shapePose.inverse().xformPoint(point); + + // Only works for 14-dop shape infos. + if (shapeInfo.dots.size() != DOP14_COUNT) { + return false; + } + + glm::vec3 minDisplacement(FLT_MAX); + float minDisplacementLen = FLT_MAX; + glm::vec3 p = localPoint - shapeInfo.avgPoint; + float pLen = glm::length(p); + if (pLen > 0.0f) { + int slabCount = 0; + for (int i = 0; i < DOP14_COUNT; i++) { + float dot = glm::dot(p, DOP14_NORMALS[i]); + if (dot > 0.0f && dot < shapeInfo.dots[i]) { + slabCount++; + float distToPlane = pLen * (shapeInfo.dots[i] / dot); + float displacementLen = distToPlane - pLen; + + // keep track of the smallest displacement + if (displacementLen < minDisplacementLen) { + minDisplacementLen = displacementLen; + minDisplacement = (p / pLen) * displacementLen; + } + } + } + if (slabCount == (DOP14_COUNT / 2) && minDisplacementLen != FLT_MAX) { + // we are within the k-dop so push the point along the minimum displacement found + displacementOut = shapePose.xformVectorFast(minDisplacement); + return true; + } else { + // point is outside of kdop + return false; + } + } else { + // point is directly on top of shapeInfo.avgPoint. + // push the point out along the x axis. + displacementOut = shapePose.xformVectorFast(shapeInfo.points[0]); + return true; + } +} diff --git a/libraries/animation/src/AnimUtil.h b/libraries/animation/src/AnimUtil.h index cf190e8dbf..c2925e31e8 100644 --- a/libraries/animation/src/AnimUtil.h +++ b/libraries/animation/src/AnimUtil.h @@ -128,4 +128,10 @@ protected: bool _snapshotValid { false }; }; + +// returns true if the given point lies inside of the k-dop, specified by shapeInfo & shapePose. +// if the given point does lie within the k-dop, it also returns the amount of displacement necessary to push that point outward +// such that it lies on the surface of the kdop. +bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& shapePose, const HFMJointShapeInfo& shapeInfo, glm::vec3& displacementOut); + #endif diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 43e94d23e8..6e434eb68d 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1521,74 +1521,6 @@ void Rig::updateHead(bool headEnabled, bool hipsEnabled, const AnimPose& headPos } } -const float INV_SQRT_3 = 1.0f / sqrtf(3.0f); -const int DOP14_COUNT = 14; -const glm::vec3 DOP14_NORMALS[DOP14_COUNT] = { - Vectors::UNIT_X, - -Vectors::UNIT_X, - Vectors::UNIT_Y, - -Vectors::UNIT_Y, - Vectors::UNIT_Z, - -Vectors::UNIT_Z, - glm::vec3(INV_SQRT_3, INV_SQRT_3, INV_SQRT_3), - -glm::vec3(INV_SQRT_3, INV_SQRT_3, INV_SQRT_3), - glm::vec3(INV_SQRT_3, -INV_SQRT_3, INV_SQRT_3), - -glm::vec3(INV_SQRT_3, -INV_SQRT_3, INV_SQRT_3), - glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3), - -glm::vec3(INV_SQRT_3, INV_SQRT_3, -INV_SQRT_3), - glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3), - -glm::vec3(INV_SQRT_3, -INV_SQRT_3, -INV_SQRT_3) -}; - -// returns true if the given point lies inside of the k-dop, specified by shapeInfo & shapePose. -// if the given point does lie within the k-dop, it also returns the amount of displacement necessary to push that point outward -// such that it lies on the surface of the kdop. -static bool findPointKDopDisplacement(const glm::vec3& point, const AnimPose& shapePose, const HFMJointShapeInfo& shapeInfo, glm::vec3& displacementOut) { - - // transform point into local space of jointShape. - glm::vec3 localPoint = shapePose.inverse().xformPoint(point); - - // Only works for 14-dop shape infos. - if (shapeInfo.dots.size() != DOP14_COUNT) { - return false; - } - - glm::vec3 minDisplacement(FLT_MAX); - float minDisplacementLen = FLT_MAX; - glm::vec3 p = localPoint - shapeInfo.avgPoint; - float pLen = glm::length(p); - if (pLen > 0.0f) { - int slabCount = 0; - for (int i = 0; i < DOP14_COUNT; i++) { - float dot = glm::dot(p, DOP14_NORMALS[i]); - if (dot > 0.0f && dot < shapeInfo.dots[i]) { - slabCount++; - float distToPlane = pLen * (shapeInfo.dots[i] / dot); - float displacementLen = distToPlane - pLen; - - // keep track of the smallest displacement - if (displacementLen < minDisplacementLen) { - minDisplacementLen = displacementLen; - minDisplacement = (p / pLen) * displacementLen; - } - } - } - if (slabCount == (DOP14_COUNT / 2) && minDisplacementLen != FLT_MAX) { - // we are within the k-dop so push the point along the minimum displacement found - displacementOut = shapePose.xformVectorFast(minDisplacement); - return true; - } else { - // point is outside of kdop - return false; - } - } else { - // point is directly on top of shapeInfo.avgPoint. - // push the point out along the x axis. - displacementOut = shapePose.xformVectorFast(shapeInfo.points[0]); - return true; - } -} - glm::vec3 Rig::deflectHandFromTorso(const glm::vec3& handPosition, const HFMJointShapeInfo& hipsShapeInfo, const HFMJointShapeInfo& spineShapeInfo, const HFMJointShapeInfo& spine1ShapeInfo, const HFMJointShapeInfo& spine2ShapeInfo) const { glm::vec3 position = handPosition; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index df13ff5c2b..1a511b2487 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -116,8 +116,9 @@ public: void destroyAnimGraph(); void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); + bool isPlayingOverrideAnimation() const { return _userAnimState.clipNodeEnum != UserAnimState::None; }; void restoreAnimation(); - + void overrideNetworkAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); void triggerNetworkRole(const QString& role); void restoreNetworkAnimation(); @@ -333,7 +334,7 @@ protected: RigRole _state { RigRole::Idle }; RigRole _desiredState { RigRole::Idle }; float _desiredStateAge { 0.0f }; - + struct NetworkAnimState { enum ClipNodeEnum { None = 0, diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index fbcf36a8c9..6276b049a1 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -270,28 +270,13 @@ bool SkeletonModel::getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& getJointPosition(_rig.indexOfJoint("RightEye"), secondEyePosition)) { return true; } - // no eye joints; try to estimate based on head/neck joints - glm::vec3 neckPosition, headPosition; - if (getJointPosition(_rig.indexOfJoint("Neck"), neckPosition) && - getJointPosition(_rig.indexOfJoint("Head"), headPosition)) { - const float EYE_PROPORTION = 0.6f; - glm::vec3 baseEyePosition = glm::mix(neckPosition, headPosition, EYE_PROPORTION); - glm::quat headRotation; - getJointRotation(_rig.indexOfJoint("Head"), headRotation); - const float EYES_FORWARD = 0.25f; - const float EYE_SEPARATION = 0.1f; - float headHeight = glm::distance(neckPosition, headPosition); - firstEyePosition = baseEyePosition + headRotation * glm::vec3(EYE_SEPARATION, 0.0f, EYES_FORWARD) * headHeight; - secondEyePosition = baseEyePosition + headRotation * glm::vec3(-EYE_SEPARATION, 0.0f, EYES_FORWARD) * headHeight; - return true; - } else if (getJointPosition(_rig.indexOfJoint("Head"), headPosition)) { - glm::vec3 baseEyePosition = headPosition; - glm::quat headRotation; - getJointRotation(_rig.indexOfJoint("Head"), headRotation); - const float EYES_FORWARD_HEAD_ONLY = 0.30f; - const float EYE_SEPARATION = 0.1f; - firstEyePosition = baseEyePosition + headRotation * glm::vec3(EYE_SEPARATION, 0.0f, EYES_FORWARD_HEAD_ONLY); - secondEyePosition = baseEyePosition + headRotation * glm::vec3(-EYE_SEPARATION, 0.0f, EYES_FORWARD_HEAD_ONLY); + + glm::vec3 headPosition; + if (getJointPosition(_rig.indexOfJoint("Head"), headPosition)) { + float heightRatio = _rig.getUnscaledEyeHeight() / DEFAULT_AVATAR_EYE_HEIGHT; + glm::vec3 ipdOffset = glm::vec3(DEFAULT_AVATAR_IPD / 2.0f, 0.0f, 0.0f); + firstEyePosition = headPosition + heightRatio * (DEFAULT_AVATAR_HEAD_TO_MIDDLE_EYE_OFFSET + ipdOffset); + secondEyePosition = headPosition + heightRatio * (DEFAULT_AVATAR_HEAD_TO_MIDDLE_EYE_OFFSET - ipdOffset); return true; } return false; diff --git a/libraries/shared/src/AvatarConstants.h b/libraries/shared/src/AvatarConstants.h index 5166cb7a0b..9338249e99 100644 --- a/libraries/shared/src/AvatarConstants.h +++ b/libraries/shared/src/AvatarConstants.h @@ -42,6 +42,7 @@ const float DEFAULT_AVATAR_HIPS_MASS = 40.0f; const float DEFAULT_AVATAR_HEAD_MASS = 20.0f; const float DEFAULT_AVATAR_LEFTHAND_MASS = 2.0f; const float DEFAULT_AVATAR_RIGHTHAND_MASS = 2.0f; +const float DEFAULT_AVATAR_IPD = 0.064f; // Used when avatar is missing joints... (avatar space) const glm::quat DEFAULT_AVATAR_MIDDLE_EYE_ROT { Quaternions::Y_180 }; From de3f07cea42b3231f609e3cfbcf497e1d6022dbf Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 9 Apr 2019 16:39:46 -0700 Subject: [PATCH 30/49] remove js option from parser --- tools/oven/src/OvenCLIApplication.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/oven/src/OvenCLIApplication.cpp b/tools/oven/src/OvenCLIApplication.cpp index 640bfda64a..eab8647e71 100644 --- a/tools/oven/src/OvenCLIApplication.cpp +++ b/tools/oven/src/OvenCLIApplication.cpp @@ -33,7 +33,7 @@ OvenCLIApplication::OvenCLIApplication(int argc, char* argv[]) : parser.addOptions({ { CLI_INPUT_PARAMETER, "Path to file that you would like to bake.", "input" }, { CLI_OUTPUT_PARAMETER, "Path to folder that will be used as output.", "output" }, - { CLI_TYPE_PARAMETER, "Type of asset. [model|material|js]", "type" }, + { CLI_TYPE_PARAMETER, "Type of asset. [model|material]"/*|js]"*/, "type" }, { CLI_DISABLE_TEXTURE_COMPRESSION_PARAMETER, "Disable texture compression." } }); From ca7cfc0240c3a3a0ae32e8463987e9ac833fcf2b Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 10 Apr 2019 09:44:56 -0700 Subject: [PATCH 31/49] only call onMove on lastMouseEvent change --- scripts/system/libraries/entitySelectionTool.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index d2f3ece5f1..3fdc1d6652 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -1347,12 +1347,16 @@ SelectionDisplay = (function() { }; that.updateLastMouseEvent = function(event) { - if (activeTool && lastMouseEvent !== null) { + if (activeTool && lastMouseEvent !== null) { + var change = lastMouseEvent.isShifted !== event.isShifted || lastMouseEvent.isMeta !== event.isMeta || + lastMouseEvent.isControl !== event.isControl || lastMouseEvent.isAlt !== event.isAlt; lastMouseEvent.isShifted = event.isShifted; lastMouseEvent.isMeta = event.isMeta; lastMouseEvent.isControl = event.isControl; - lastMouseEvent.isAlt = event.isAlt; - activeTool.onMove(lastMouseEvent); + lastMouseEvent.isAlt = event.isAlt; + if (change) { + activeTool.onMove(lastMouseEvent); + } } }; From 1f838c681c1c9bf7fa2610889599377f7d4183ad Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Wed, 10 Apr 2019 13:07:50 -0700 Subject: [PATCH 32/49] Case 22140 - Dynamic Jitter Buffer switch on admin UI disables dynamic buffers when it's set to enable them. The dynamic jitter buffer switch is inverted in that it disables dynamic buffers when it set to enable them, and vice versa. As it defaults to 'enable,' dynamic buffers are basically disabled for everyone which will affect audio quality negatively. --- assignment-client/src/audio/AudioMixer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index fd3a8f7c0c..eb487df850 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -588,8 +588,8 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) { // check the payload to see if we have asked for dynamicJitterBuffer support const QString DYNAMIC_JITTER_BUFFER_JSON_KEY = "dynamic_jitter_buffer"; bool enableDynamicJitterBuffer = audioBufferGroupObject[DYNAMIC_JITTER_BUFFER_JSON_KEY].toBool(); - if (enableDynamicJitterBuffer) { - qCDebug(audio) << "Enabling dynamic jitter buffers."; + if (!enableDynamicJitterBuffer) { + qCDebug(audio) << "Disabling dynamic jitter buffers."; bool ok; const QString DESIRED_JITTER_BUFFER_FRAMES_KEY = "static_desired_jitter_buffer_frames"; @@ -599,7 +599,7 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) { } qCDebug(audio) << "Static desired jitter buffer frames:" << _numStaticJitterFrames; } else { - qCDebug(audio) << "Disabling dynamic jitter buffers."; + qCDebug(audio) << "Enabling dynamic jitter buffers."; _numStaticJitterFrames = DISABLE_STATIC_JITTER_FRAMES; } From a7b1d613d33295a1f57035dd37d32a8dd0c64eae Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 11 Apr 2019 10:27:17 +1200 Subject: [PATCH 33/49] Doc review updates --- interface/src/FancyCamera.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/FancyCamera.h b/interface/src/FancyCamera.h index cd587279e1..aead54d0fd 100644 --- a/interface/src/FancyCamera.h +++ b/interface/src/FancyCamera.h @@ -47,8 +47,8 @@ public: public slots: /**jsdoc - * Gets the ID of the entity that the camera is set to use the position and orientation from when it's in entity mode. You - * can also get the entity ID using the {@link Camera|Camera.cameraEntity} property. + * Gets the ID of the entity that the camera is set to follow (i.e., use the position and orientation from) when it's in + * entity mode. You can also get the entity ID using the {@link Camera|Camera.cameraEntity} property. * @function Camera.getCameraEntity * @returns {Uuid} The ID of the entity that the camera is set to follow when in entity mode; null if no * camera entity has been set. @@ -56,8 +56,8 @@ public slots: QUuid getCameraEntity() const; /**jsdoc - * Sets the entity that the camera should use the position and orientation from when it's in entity mode. You can also set - * the entity using the {@link Camera|Camera.cameraEntity} property. + * Sets the entity that the camera should follow (i.e., use the position and orientation from) when it's in entity mode. + * You can also set the entity using the {@link Camera|Camera.cameraEntity} property. * @function Camera.setCameraEntity * @param {Uuid} entityID - The entity that the camera should follow when it's in entity mode. * @example Move your camera to the position and orientation of the closest entity. From 38e21dcaa20b08819911d8d424bd8fa0733e28b5 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 11 Apr 2019 11:04:02 +1200 Subject: [PATCH 34/49] Doc review --- interface/src/avatar/AvatarManager.h | 16 ++++++++-------- libraries/avatars/src/AvatarHashMap.h | 2 +- libraries/avatars/src/ScriptAvatarData.h | 2 +- libraries/render-utils/src/Model.cpp | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 678dc5a3e2..1bddaedc42 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -37,8 +37,8 @@ using SortedAvatar = std::pair>; /**jsdoc - * The AvatarManager API provides information about avatars within the domain. The avatars available are those - * that Interface has displayed and so knows about. + * The AvatarManager API provides information about avatars within the current domain. The avatars available are + * those that Interface has displayed and therefore knows about. * *

Warning: This API is also provided to Interface, client entity, and avatar scripts as the synonym, * "AvatarList". For assignment client scripts, see the separate {@link AvatarList} API.

@@ -139,7 +139,7 @@ public: /**jsdoc * Gets the amount of avatar mixer data being generated by an avatar other than your own. * @function AvatarManager.getAvatarDataRate - * @param {Uuid} sessionID - The ID of the avatar to get the data rate for. + * @param {Uuid} sessionID - The ID of the avatar whose data rate you're retrieving. * @param {AvatarDataRate} [rateName=""] - The type of avatar mixer data to get the data rate of. * @returns {number} The data rate in kbps; 0 if the avatar is your own. */ @@ -148,7 +148,7 @@ public: /**jsdoc * Gets the update rate of avatar mixer data being generated by an avatar other than your own. * @function AvatarManager.getAvatarUpdateRate - * @param {Uuid} sessionID - The ID of the avatar to get the update rate for. + * @param {Uuid} sessionID - The ID of the avatar whose update rate you're retrieving. * @param {AvatarUpdateRate} [rateName=""] - The type of avatar mixer data to get the update rate of. * @returns {number} The update rate in Hz; 0 if the avatar is your own. */ @@ -157,7 +157,7 @@ public: /**jsdoc * Gets the simulation rate of an avatar other than your own. * @function AvatarManager.getAvatarSimulationRate - * @param {Uuid} sessionID - The ID of the avatar to get the simulation rate for. + * @param {Uuid} sessionID - The ID of the avatar whose simulation you're retrieving. * @param {AvatarSimulationRate} [rateName=""] - The type of avatar data to get the simulation rate of. * @returns {number} The simulation rate in Hz; 0 if the avatar is your own. */ @@ -236,10 +236,10 @@ public: * Gets PAL (People Access List) data for one or more avatars. Using this method is faster than iterating over each avatar * and obtaining data about each individually. * @function AvatarManager.getPalData - * @param {string[]} [avatarIDs=[]] - The IDs of the avatars to get the PAL data for. If empty then PAL - * data is obtained for all avatars. + * @param {string[]} [avatarIDs=[]] - The IDs of the avatars to get the PAL data for. If empty, then PAL data is obtained + * for all avatars. * @returns {object<"data", AvatarManager.PalData[]>} An array of objects, each object being the PAL data for an avatar. - * @example Report the PAL data for one nearby avatar. + * @example Report the PAL data for an avatar nearby. * var palData = AvatarManager.getPalData(); * print("PAL data for one avatar: " + JSON.stringify(palData.data[0])); */ diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index 3340635c82..17a3d28eb0 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -36,7 +36,7 @@ const int CLIENT_TO_AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 50; const quint64 MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS = USECS_PER_SECOND / CLIENT_TO_AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND; /**jsdoc - * The AvatarList API provides information about avatars within the domain. + * The AvatarList API provides information about avatars within the current domain. * *

Warning: An API named "AvatarList" is also provided for Interface, client entity, and avatar * scripts, however, it is a synonym for the {@link AvatarManager} API.

diff --git a/libraries/avatars/src/ScriptAvatarData.h b/libraries/avatars/src/ScriptAvatarData.h index 61ceb88480..7e33618ba9 100644 --- a/libraries/avatars/src/ScriptAvatarData.h +++ b/libraries/avatars/src/ScriptAvatarData.h @@ -46,7 +46,7 @@ * * @property {string} skeletonModelURL - The avatar's FST file. * @property {AttachmentData[]} attachmentData - Information on the avatar's attachments.
- * Deprecated: Use avatar entities insteada. + * Deprecated: Use avatar entities instead. * @property {string[]} jointNames - The list of joints in the current avatar model. * * @property {number} audioLoudness - The instantaneous loudness of the audio input that the avatar is injecting into the diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index ff0fa4ac3b..2a35b0d161 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -453,9 +453,9 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g * @property {number} shapeID - The index of the mesh part within the model. * @property {number} subMeshIndex - The index of the intersected submesh within the model. * @property {string} subMeshName - The name of the intersected submesh. - * @property {Triangle} subMeshTriangleWorld - The vertexes of the intersected mesh part triangle in world coordinates. + * @property {Triangle} subMeshTriangleWorld - The vertices of the intersected mesh part triangle in world coordinates. * @property {Vec3} subMeshNormal - The normal of the intersected mesh part triangle in model coordinates. - * @property {Triangle} subMeshTriangle - The vertexes of the intersected mesh part triangle in model coordinates. + * @property {Triangle} subMeshTriangle - The vertices of the intersected mesh part triangle in model coordinates. */ if (intersectedSomething) { distance = bestDistance; From 39cf3c3d83fd09f88160a32cc1fa4c13578a3095 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Wed, 3 Apr 2019 12:27:32 -0700 Subject: [PATCH 35/49] Fix TextureBaker failures and heap corruption in MaterialBaker --- libraries/baking/src/MaterialBaker.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/baking/src/MaterialBaker.cpp b/libraries/baking/src/MaterialBaker.cpp index 9fcd7d0354..7fc2573d7b 100644 --- a/libraries/baking/src/MaterialBaker.cpp +++ b/libraries/baking/src/MaterialBaker.cpp @@ -144,7 +144,12 @@ void MaterialBaker::processMaterial() { connect(textureBaker.data(), &TextureBaker::finished, this, &MaterialBaker::handleFinishedTextureBaker); _textureBakers.insert(textureKey, textureBaker); textureBaker->moveToThread(_getNextOvenWorkerThreadOperator ? _getNextOvenWorkerThreadOperator() : thread()); - QMetaObject::invokeMethod(textureBaker.data(), "bake"); + // By default, Qt will invoke this bake immediately if the TextureBaker is on the same worker thread as this MaterialBaker. + // We don't want that, because threads may be waiting for work while this thread is stuck processing a TextureBaker. + // On top of that, _textureBakers isn't fully populated. + // So, use Qt::QueuedConnection. + // TODO: Better thread utilization at the top level, not just the MaterialBaker level + QMetaObject::invokeMethod(textureBaker.data(), "bake", Qt::QueuedConnection); } _materialsNeedingRewrite.insert(textureKey, networkMaterial.second); } else { From 7aeb4bca87d3625f46f2babd829e903187cba6c4 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 11 Apr 2019 14:05:15 -0700 Subject: [PATCH 36/49] Include head rotation in getEyeModelPositions --- .../src/avatars-renderer/SkeletonModel.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index 6276b049a1..295a0e9f52 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -271,12 +271,18 @@ bool SkeletonModel::getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& return true; } + int headJointIndex = _rig.indexOfJoint("Head"); glm::vec3 headPosition; - if (getJointPosition(_rig.indexOfJoint("Head"), headPosition)) { + if (getJointPosition(headJointIndex, headPosition)) { + + // get head joint rotation. + glm::quat headRotation; + getJointRotation(headJointIndex, headRotation); + float heightRatio = _rig.getUnscaledEyeHeight() / DEFAULT_AVATAR_EYE_HEIGHT; glm::vec3 ipdOffset = glm::vec3(DEFAULT_AVATAR_IPD / 2.0f, 0.0f, 0.0f); - firstEyePosition = headPosition + heightRatio * (DEFAULT_AVATAR_HEAD_TO_MIDDLE_EYE_OFFSET + ipdOffset); - secondEyePosition = headPosition + heightRatio * (DEFAULT_AVATAR_HEAD_TO_MIDDLE_EYE_OFFSET - ipdOffset); + firstEyePosition = headPosition + headRotation * heightRatio * (DEFAULT_AVATAR_HEAD_TO_MIDDLE_EYE_OFFSET + ipdOffset); + secondEyePosition = headPosition + headRotation * heightRatio * (DEFAULT_AVATAR_HEAD_TO_MIDDLE_EYE_OFFSET - ipdOffset); return true; } return false; From f2474b4d2b4906cef1dae4367d103b47037dd0ed Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 11 Apr 2019 14:20:52 -0700 Subject: [PATCH 37/49] Fix lack of ranges on numerical types in Create --- libraries/entities/src/EntityItemProperties.cpp | 10 +++++----- libraries/entities/src/EntityItemProperties.h | 13 +++++++++++++ libraries/entities/src/EntityItemPropertiesMacros.h | 3 ++- scripts/system/html/js/entityProperties.js | 2 ++ 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 44e317696c..2b738bc4e7 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -2630,11 +2630,11 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr ENTITY_ITEM_MIN_FRICTION, ENTITY_ITEM_MAX_FRICTION); ADD_PROPERTY_TO_MAP(PROP_LIFETIME, Lifetime, lifetime, float); ADD_PROPERTY_TO_MAP(PROP_COLLISIONLESS, Collisionless, collisionless, bool); - ADD_PROPERTY_TO_MAP(PROP_COLLISIONLESS, unused, ignoreForCollisions, unused); // legacy support - ADD_PROPERTY_TO_MAP(PROP_COLLISION_MASK, unused, collisionMask, unused); - ADD_PROPERTY_TO_MAP(PROP_COLLISION_MASK, unused, collidesWith, unused); - ADD_PROPERTY_TO_MAP(PROP_DYNAMIC, unused, collisionsWillMove, unused); // legacy support - ADD_PROPERTY_TO_MAP(PROP_DYNAMIC, unused, dynamic, unused); + ADD_PROPERTY_TO_MAP(PROP_COLLISIONLESS, unused, ignoreForCollisions, bool); // legacy support + ADD_PROPERTY_TO_MAP(PROP_COLLISION_MASK, unused, collisionMask, uint16_t); + ADD_PROPERTY_TO_MAP(PROP_COLLISION_MASK, unused, collidesWith, uint16_t); + ADD_PROPERTY_TO_MAP(PROP_DYNAMIC, unused, collisionsWillMove, bool); // legacy support + ADD_PROPERTY_TO_MAP(PROP_DYNAMIC, unused, dynamic, bool); ADD_PROPERTY_TO_MAP(PROP_COLLISION_SOUND_URL, CollisionSoundURL, collisionSoundURL, QString); ADD_PROPERTY_TO_MAP(PROP_ACTION_DATA, ActionData, actionData, QByteArray); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index d030f4f2e4..0142f42536 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -14,6 +14,9 @@ #include +#include +#include + #include #include @@ -85,6 +88,16 @@ struct EntityPropertyInfo { QVariant maximum; }; +template +EntityPropertyInfo makePropertyInfo(EntityPropertyList p, typename std::enable_if::value>::type* = 0) { + return EntityPropertyInfo(p); +} + +template +EntityPropertyInfo makePropertyInfo(EntityPropertyList p, typename std::enable_if::value>::type* = 0) { + return EntityPropertyInfo(p, std::numeric_limits::min(), std::numeric_limits::max()); +} + /// A collection of properties of an entity item used in the scripting API. Translates between the actual properties of an /// entity and a JavaScript style hash/QScriptValue storing a set of properties. Used in scripting to set/get the complete /// set of entity item properties via JavaScript hashes/QScriptValues diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index 7064f3e62e..4c989ef74e 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -416,9 +416,10 @@ inline QRect QRect_convertFromScriptValue(const QScriptValue& v, bool& isValid) T _##n; \ static T _static##N; + #define ADD_PROPERTY_TO_MAP(P, N, n, T) \ { \ - EntityPropertyInfo propertyInfo = EntityPropertyInfo(P); \ + EntityPropertyInfo propertyInfo { makePropertyInfo(P) }; \ _propertyInfos[#n] = propertyInfo; \ _enumsToPropertyStrings[P] = #n; \ } diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index ca04f36dc9..3283e6c266 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -616,6 +616,8 @@ const GROUPS = [ decimals: 3, propertyID: "webAlpha", propertyName: "alpha", + min: 0, + max: 1, }, { label: "Max FPS", From a5e94eea755e2347a9b033a1e12994a7ce4d8a88 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Thu, 11 Apr 2019 17:36:09 -0700 Subject: [PATCH 38/49] Increase ping interval for unconnected Nodes --- libraries/networking/src/NetworkPeer.h | 2 +- libraries/networking/src/NodeList.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/networking/src/NetworkPeer.h b/libraries/networking/src/NetworkPeer.h index b75d2f8b86..43fbc753eb 100644 --- a/libraries/networking/src/NetworkPeer.h +++ b/libraries/networking/src/NetworkPeer.h @@ -26,7 +26,7 @@ const quint16 ICE_SERVER_DEFAULT_PORT = 7337; const int ICE_HEARBEAT_INTERVAL_MSECS = 2 * 1000; const int MAX_ICE_CONNECTION_ATTEMPTS = 5; -const int UDP_PUNCH_PING_INTERVAL_MS = 25; +const int UDP_PUNCH_PING_INTERVAL_MS = 250; class NetworkPeer : public QObject { Q_OBJECT diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 0021a594bc..0a4c63d712 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -752,11 +752,11 @@ void NodeList::pingPunchForInactiveNode(const SharedNodePointer& node) { flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendAudioPing); } - // every second we're trying to ping this node and we're not getting anywhere - debug that out - const int NUM_DEBUG_CONNECTION_ATTEMPTS = 1000 / (UDP_PUNCH_PING_INTERVAL_MS); + // every two seconds we're trying to ping this node and we're not getting anywhere - debug that out + const int NUM_DEBUG_CONNECTION_ATTEMPTS = 2000 / (UDP_PUNCH_PING_INTERVAL_MS); if (node->getConnectionAttempts() > 0 && node->getConnectionAttempts() % NUM_DEBUG_CONNECTION_ATTEMPTS == 0) { - qCDebug(networking) << "No response to UDP hole punch pings for node" << node->getUUID() << "in last second."; + qCDebug(networking) << "No response to UDP hole punch pings for node" << node->getUUID() << "in last 2 s."; } auto nodeID = node->getUUID(); From 7180d8b99b68bbf983f79438770b6aacb0acaf77 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Thu, 11 Apr 2019 17:56:27 -0700 Subject: [PATCH 39/49] Clear delayed Node-adds upon restart --- libraries/networking/src/LimitedNodeList.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 18a180ad79..82f3459c15 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -588,6 +588,8 @@ void LimitedNodeList::eraseAllNodes() { foreach(const SharedNodePointer& killedNode, killedNodes) { handleNodeKill(killedNode); } + + _delayedNodeAdds.clear(); } void LimitedNodeList::reset() { @@ -755,7 +757,7 @@ void LimitedNodeList::delayNodeAdd(NewNodeInfo info) { } void LimitedNodeList::removeDelayedAdd(QUuid nodeUUID) { - auto it = std::find_if(_delayedNodeAdds.begin(), _delayedNodeAdds.end(), [&](auto info) { + auto it = std::find_if(_delayedNodeAdds.begin(), _delayedNodeAdds.end(), [&](const auto& info) { return info.uuid == nodeUUID; }); if (it != _delayedNodeAdds.end()) { @@ -764,7 +766,7 @@ void LimitedNodeList::removeDelayedAdd(QUuid nodeUUID) { } bool LimitedNodeList::isDelayedNode(QUuid nodeUUID) { - auto it = std::find_if(_delayedNodeAdds.begin(), _delayedNodeAdds.end(), [&](auto info) { + auto it = std::find_if(_delayedNodeAdds.begin(), _delayedNodeAdds.end(), [&](const auto& info) { return info.uuid == nodeUUID; }); return it != _delayedNodeAdds.end(); From 87deb348a8fce5ddb31fdcbaccff6163ac569d37 Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Fri, 12 Apr 2019 14:07:06 -0700 Subject: [PATCH 40/49] Adjust run speed constants. --- libraries/shared/src/AvatarConstants.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/AvatarConstants.h b/libraries/shared/src/AvatarConstants.h index 5166cb7a0b..5b65390620 100644 --- a/libraries/shared/src/AvatarConstants.h +++ b/libraries/shared/src/AvatarConstants.h @@ -102,6 +102,6 @@ static const float MAX_AVATAR_HEIGHT = 1000.0f * DEFAULT_AVATAR_HEIGHT; // meter static const float MIN_AVATAR_HEIGHT = 0.005f * DEFAULT_AVATAR_HEIGHT; // meters static const float MIN_AVATAR_RADIUS = 0.5f * MIN_AVATAR_HEIGHT; static const float AVATAR_WALK_SPEED_SCALAR = 1.0f; -static const float AVATAR_SPRINT_SPEED_SCALAR = 2.0f; +static const float AVATAR_SPRINT_SPEED_SCALAR = 3.0f; #endif // hifi_AvatarConstants_h From d342d93ec9191b5ba7d232e7f989be845dc6c6cc Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 12 Apr 2019 14:47:48 -0700 Subject: [PATCH 41/49] fix material targets when re-opening properties to same entity --- scripts/system/html/js/entityProperties.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index 78a6e26ff0..021a27152a 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -3503,6 +3503,7 @@ function loaded() { deleteJSONMaterialEditor(); } } + lastEntityID = null; resetProperties(); showGroupsForType("None"); From 3bf8fb5d954d3562e5d57e8935478acd58adaa56 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Fri, 12 Apr 2019 16:40:41 -0700 Subject: [PATCH 42/49] Case21707 - Wallet doesn't work if user has unicode characters in windows username --- interface/src/commerce/Wallet.cpp | 134 ++++++++++++++++-------------- 1 file changed, 72 insertions(+), 62 deletions(-) diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 37f28960e5..e00cd44fa9 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -96,28 +96,32 @@ int passwordCallback(char* password, int maxPasswordSize, int rwFlag, void* u) { } } -EC_KEY* readKeys(const char* filename) { - FILE* fp; - EC_KEY *key = NULL; - if ((fp = fopen(filename, "rt"))) { +EC_KEY* readKeys(QString filename) { + QFile file(filename); + EC_KEY* key = NULL; + if (file.open(QFile::ReadOnly)) { // file opened successfully qCDebug(commerce) << "opened key file" << filename; - if ((key = PEM_read_EC_PUBKEY(fp, NULL, NULL, NULL))) { + QByteArray pemKeyBytes = file.readAll(); + BIO* bufio = BIO_new_mem_buf((void*)pemKeyBytes.constData(), pemKeyBytes.length()); + if ((key = PEM_read_bio_EC_PUBKEY(bufio, NULL, NULL, NULL))) { // now read private key qCDebug(commerce) << "read public key"; - if ((key = PEM_read_ECPrivateKey(fp, &key, passwordCallback, NULL))) { + if (key = PEM_read_bio_ECPrivateKey(bufio, &key, passwordCallback, NULL)) { qCDebug(commerce) << "read private key"; - fclose(fp); - return key; + BIO_free(bufio); + file.close(); + } else { + qCDebug(commerce) << "failed to read private key"; } - qCDebug(commerce) << "failed to read private key"; } else { qCDebug(commerce) << "failed to read public key"; } - fclose(fp); + BIO_free(bufio); + file.close(); } else { qCDebug(commerce) << "failed to open key file" << filename; } @@ -131,8 +135,7 @@ bool Wallet::writeBackupInstructions() { QFile outputFile(outputFilename); bool retval = false; - if (getKeyFilePath().isEmpty()) - { + if (getKeyFilePath().isEmpty()) { return false; } @@ -152,7 +155,7 @@ bool Wallet::writeBackupInstructions() { outputFile.write(text.toUtf8()); // Close the output file - outputFile.close(); + outputFile.close(); retval = true; qCDebug(commerce) << "wrote html file successfully"; @@ -165,28 +168,35 @@ bool Wallet::writeBackupInstructions() { return retval; } -bool writeKeys(const char* filename, EC_KEY* keys) { - FILE* fp; +bool writeKeys(QString filename, EC_KEY* keys) { + BIO* bio = BIO_new(BIO_s_mem()); bool retval = false; - if ((fp = fopen(filename, "wt"))) { - if (!PEM_write_EC_PUBKEY(fp, keys)) { - fclose(fp); - qCCritical(commerce) << "failed to write public key"; - return retval; - } + if (!PEM_write_bio_EC_PUBKEY(bio, keys)) { + BIO_free(bio); + qCCritical(commerce) << "failed to write public key"; + return retval; + } - if (!PEM_write_ECPrivateKey(fp, keys, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) { - fclose(fp); - qCCritical(commerce) << "failed to write private key"; - return retval; - } + if (!PEM_write_bio_ECPrivateKey(bio, keys, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) { + BIO_free(bio); + qCCritical(commerce) << "failed to write private key"; + return retval; + } + QFile file(filename); + if (!file.open(QIODevice::WriteOnly)) { + const char* bio_data; + long bio_size = BIO_get_mem_data(bio, &bio_data); + + QByteArray keyBytes(bio_data, bio_size); + file.write(keyBytes); retval = true; qCDebug(commerce) << "wrote keys successfully"; - fclose(fp); + file.close(); } else { qCDebug(commerce) << "failed to open key file" << filename; } + BIO_free(bio); return retval; } @@ -215,7 +225,6 @@ QByteArray Wallet::getWallet() { } QPair generateECKeypair() { - EC_KEY* keyPair = EC_KEY_new_by_curve_name(NID_secp256k1); QPair retval{}; @@ -235,7 +244,6 @@ QPair generateECKeypair() { if (publicKeyLength <= 0 || privateKeyLength <= 0) { qCDebug(commerce) << "Error getting DER public or private key from EC struct -" << ERR_get_error(); - // cleanup the EC struct EC_KEY_free(keyPair); @@ -251,8 +259,7 @@ QPair generateECKeypair() { return retval; } - - if (!writeKeys(keyFilePath().toStdString().c_str(), keyPair)) { + if (!writeKeys(keyFilePath(), keyPair)) { qCDebug(commerce) << "couldn't save keys!"; return retval; } @@ -273,13 +280,18 @@ QPair generateECKeypair() { // END copied code (which will soon change) // the public key can just go into a byte array -QByteArray readPublicKey(const char* filename) { - FILE* fp; - EC_KEY* key = NULL; - if ((fp = fopen(filename, "r"))) { +QByteArray readPublicKey(QString filename) { + QByteArray retval; + QFile file(filename); + if (file.open(QIODevice::ReadOnly)) { // file opened successfully qCDebug(commerce) << "opened key file" << filename; - if ((key = PEM_read_EC_PUBKEY(fp, NULL, NULL, NULL))) { + + QByteArray pemKeyBytes = file.readAll(); + BIO* bufio = BIO_new_mem_buf((void*)pemKeyBytes.constData(), pemKeyBytes.length()); + + EC_KEY* key = PEM_read_bio_EC_PUBKEY(bufio, NULL, NULL, NULL); + if (key) { // file read successfully unsigned char* publicKeyDER = NULL; int publicKeyLength = i2d_EC_PUBKEY(key, &publicKeyDER); @@ -287,32 +299,35 @@ QByteArray readPublicKey(const char* filename) { // cleanup EC_KEY_free(key); - fclose(fp); qCDebug(commerce) << "parsed public key file successfully"; - QByteArray retval((char*)publicKeyDER, publicKeyLength); + retval.setRawData((char*)publicKeyDER, publicKeyLength); OPENSSL_free(publicKeyDER); - return retval; } else { qCDebug(commerce) << "couldn't parse" << filename; } - fclose(fp); + BIO_free(bufio); + file.close(); } else { qCDebug(commerce) << "couldn't open" << filename; } - return QByteArray(); + return retval; } // the private key should be read/copied into heap memory. For now, we need the EC_KEY struct // so I'll return that. -EC_KEY* readPrivateKey(const char* filename) { - FILE* fp; +EC_KEY* readPrivateKey(QString filename) { + QFile file(filename); EC_KEY* key = NULL; - if ((fp = fopen(filename, "r"))) { + if (file.open(QIODevice::ReadOnly)) { // file opened successfully qCDebug(commerce) << "opened key file" << filename; - if ((key = PEM_read_ECPrivateKey(fp, &key, passwordCallback, NULL))) { + + QByteArray pemKeyBytes = file.readAll(); + BIO* bufio = BIO_new_mem_buf((void*)pemKeyBytes.constData(), pemKeyBytes.length()); + + if ((key = PEM_read_bio_ECPrivateKey(bufio, &key, passwordCallback, NULL))) { qCDebug(commerce) << "parsed private key file successfully"; } else { @@ -320,7 +335,8 @@ EC_KEY* readPrivateKey(const char* filename) { // if the passphrase is wrong, then let's not cache it DependencyManager::get()->setPassphrase(""); } - fclose(fp); + BIO_free(bufio); + file.close(); } else { qCDebug(commerce) << "couldn't open" << filename; } @@ -361,7 +377,7 @@ Wallet::Wallet() { if (wallet->getKeyFilePath().isEmpty() || !wallet->getSecurityImage()) { if (keyStatus == "preexisting") { status = (uint) WalletStatus::WALLET_STATUS_PREEXISTING; - } else{ + } else { status = (uint) WalletStatus::WALLET_STATUS_NOT_SET_UP; } } else if (!wallet->walletIsAuthenticatedWithPassphrase()) { @@ -569,10 +585,10 @@ bool Wallet::walletIsAuthenticatedWithPassphrase() { } // otherwise, we have a passphrase but no keys, so we have to check - auto publicKey = readPublicKey(keyFilePath().toStdString().c_str()); + auto publicKey = readPublicKey(keyFilePath()); if (publicKey.size() > 0) { - if (auto key = readPrivateKey(keyFilePath().toStdString().c_str())) { + if (auto key = readPrivateKey(keyFilePath())) { EC_KEY_free(key); // be sure to add the public key so we don't do this over and over @@ -631,8 +647,7 @@ QStringList Wallet::listPublicKeys() { QString Wallet::signWithKey(const QByteArray& text, const QString& key) { EC_KEY* ecPrivateKey = NULL; - auto keyFilePathString = keyFilePath().toStdString(); - if ((ecPrivateKey = readPrivateKey(keyFilePath().toStdString().c_str()))) { + if (ecPrivateKey = readPrivateKey(keyFilePath())) { unsigned char* sig = new unsigned char[ECDSA_size(ecPrivateKey)]; unsigned int signatureBytes = 0; @@ -641,12 +656,8 @@ QString Wallet::signWithKey(const QByteArray& text, const QString& key) { QByteArray hashedPlaintext = QCryptographicHash::hash(text, QCryptographicHash::Sha256); - - int retrn = ECDSA_sign(0, - reinterpret_cast(hashedPlaintext.constData()), - hashedPlaintext.size(), - sig, - &signatureBytes, ecPrivateKey); + int retrn = ECDSA_sign(0, reinterpret_cast(hashedPlaintext.constData()), hashedPlaintext.size(), + sig, &signatureBytes, ecPrivateKey); EC_KEY_free(ecPrivateKey); QByteArray signature(reinterpret_cast(sig), signatureBytes); @@ -682,7 +693,6 @@ void Wallet::updateImageProvider() { } void Wallet::chooseSecurityImage(const QString& filename) { - if (_securityImage) { delete _securityImage; } @@ -754,7 +764,7 @@ QString Wallet::getKeyFilePath() { } bool Wallet::writeWallet(const QString& newPassphrase) { - EC_KEY* keys = readKeys(keyFilePath().toStdString().c_str()); + EC_KEY* keys = readKeys(keyFilePath()); auto ledger = DependencyManager::get(); // Remove any existing locker, because it will be out of date. if (!_publicKeys.isEmpty() && !ledger->receiveAt(_publicKeys.first(), _publicKeys.first(), QByteArray())) { @@ -768,7 +778,7 @@ bool Wallet::writeWallet(const QString& newPassphrase) { setPassphrase(newPassphrase); } - if (writeKeys(tempFileName.toStdString().c_str(), keys)) { + if (writeKeys(tempFileName, keys)) { if (writeSecurityImage(_securityImage, tempFileName)) { // ok, now move the temp file to the correct spot QFile(QString(keyFilePath())).remove(); @@ -834,10 +844,10 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack challengingNodeUUID = packet->read(challengingNodeUUIDByteArraySize); } - EC_KEY* ec = readKeys(keyFilePath().toStdString().c_str()); + EC_KEY* ec = readKeys(keyFilePath()); QString sig; - if (ec) { + if (ec) { ERR_clear_error(); sig = signWithKey(text, ""); // base64 signature, QByteArray cast (on return) to QString FIXME should pass ec as string so we can tell which key to sign with status = 1; From 246f11ba02ddce4d8aa0787f9b4bb9d5c09fc641 Mon Sep 17 00:00:00 2001 From: danteruiz Date: Fri, 12 Apr 2019 17:11:35 -0700 Subject: [PATCH 43/49] fixing bubble --- interface/src/avatar/AvatarManager.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 575d87dfb7..6c0eb4a371 100755 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -498,8 +498,10 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar // on the creation of entities for that avatar instance and the deletion of entities for this instance avatar->removeAvatarEntitiesFromTree(); if (removalReason != KillAvatarReason::AvatarDisconnected) { - emit AvatarInputs::getInstance()->avatarEnteredIgnoreRadius(avatar->getSessionUUID()); - emit DependencyManager::get()->enteredIgnoreRadius(); + if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble) { + emit AvatarInputs::getInstance()->avatarEnteredIgnoreRadius(avatar->getSessionUUID()); + emit DependencyManager::get()->enteredIgnoreRadius(); + } workload::Transaction workloadTransaction; workloadTransaction.remove(avatar->getSpaceIndex()); From e36a8fc6a2a4a6221e970a6947a1b56847f4d398 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Fri, 12 Apr 2019 18:36:14 -0700 Subject: [PATCH 44/49] Fix osx/ubuntu build error --- interface/src/commerce/Wallet.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index e00cd44fa9..e6f8491053 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -110,7 +110,7 @@ EC_KEY* readKeys(QString filename) { qCDebug(commerce) << "read public key"; - if (key = PEM_read_bio_ECPrivateKey(bufio, &key, passwordCallback, NULL)) { + if ((key = PEM_read_bio_ECPrivateKey(bufio, &key, passwordCallback, NULL))) { qCDebug(commerce) << "read private key"; BIO_free(bufio); file.close(); @@ -647,7 +647,7 @@ QStringList Wallet::listPublicKeys() { QString Wallet::signWithKey(const QByteArray& text, const QString& key) { EC_KEY* ecPrivateKey = NULL; - if (ecPrivateKey = readPrivateKey(keyFilePath())) { + if ((ecPrivateKey = readPrivateKey(keyFilePath()))) { unsigned char* sig = new unsigned char[ECDSA_size(ecPrivateKey)]; unsigned int signatureBytes = 0; From 8f9d8372d8ea9e85afdae870a497d00b2fab91c0 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Fri, 12 Apr 2019 20:30:03 -0700 Subject: [PATCH 45/49] Public Key was getting corrupted. --- interface/src/commerce/Wallet.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index e6f8491053..2054206f87 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -302,8 +302,11 @@ QByteArray readPublicKey(QString filename) { qCDebug(commerce) << "parsed public key file successfully"; - retval.setRawData((char*)publicKeyDER, publicKeyLength); + QByteArray retval((char*)publicKeyDER, publicKeyLength); OPENSSL_free(publicKeyDER); + BIO_free(bufio); + file.close(); + return retval; } else { qCDebug(commerce) << "couldn't parse" << filename; } @@ -312,7 +315,7 @@ QByteArray readPublicKey(QString filename) { } else { qCDebug(commerce) << "couldn't open" << filename; } - return retval; + return QByteArray(); } // the private key should be read/copied into heap memory. For now, we need the EC_KEY struct @@ -387,7 +390,7 @@ Wallet::Wallet() { } else { status = (uint) WalletStatus::WALLET_STATUS_READY; } - + qCDebug(commerce) << "WALLET STATUS:" + keyStatus + " " + status; walletScriptingInterface->setWalletStatus(status); }); From 12d7e357227777cf04780c95288aa3845fff6906 Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Mon, 15 Apr 2019 10:56:07 -0700 Subject: [PATCH 46/49] Add separate constant for desktop run. HMD run affected negatively by 3x multiplier. This will need to be cleaned up in a future release. --- interface/src/avatar/MyAvatar.cpp | 7 ++++++- libraries/shared/src/AvatarConstants.h | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c0cf63d7e4..ee0e9ebc4d 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -4798,7 +4798,12 @@ bool MyAvatar::isReadyForPhysics() const { } void MyAvatar::setSprintMode(bool sprint) { - _walkSpeedScalar = sprint ? AVATAR_SPRINT_SPEED_SCALAR : AVATAR_WALK_SPEED_SCALAR; + if (qApp->isHMDMode()) { + _walkSpeedScalar = sprint ? AVATAR_DESKTOP_SPRINT_SPEED_SCALAR : AVATAR_WALK_SPEED_SCALAR; + } + else { + _walkSpeedScalar = sprint ? AVATAR_HMD_SPRINT_SPEED_SCALAR : AVATAR_WALK_SPEED_SCALAR; + } } void MyAvatar::setIsInWalkingState(bool isWalking) { diff --git a/libraries/shared/src/AvatarConstants.h b/libraries/shared/src/AvatarConstants.h index 5b65390620..b2ee4caa7c 100644 --- a/libraries/shared/src/AvatarConstants.h +++ b/libraries/shared/src/AvatarConstants.h @@ -102,6 +102,7 @@ static const float MAX_AVATAR_HEIGHT = 1000.0f * DEFAULT_AVATAR_HEIGHT; // meter static const float MIN_AVATAR_HEIGHT = 0.005f * DEFAULT_AVATAR_HEIGHT; // meters static const float MIN_AVATAR_RADIUS = 0.5f * MIN_AVATAR_HEIGHT; static const float AVATAR_WALK_SPEED_SCALAR = 1.0f; -static const float AVATAR_SPRINT_SPEED_SCALAR = 3.0f; +static const float AVATAR_DESKTOP_SPRINT_SPEED_SCALAR = 2.0f; +static const float AVATAR_HMD_SPRINT_SPEED_SCALAR = 3.0f; #endif // hifi_AvatarConstants_h From 3949ca26385a2803fd9a9a8fa1b30f682c9cd5fc Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Mon, 15 Apr 2019 12:35:36 -0700 Subject: [PATCH 47/49] Remove logging and bump build. --- interface/src/commerce/Wallet.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 2054206f87..5644f9ea4c 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -390,7 +390,6 @@ Wallet::Wallet() { } else { status = (uint) WalletStatus::WALLET_STATUS_READY; } - qCDebug(commerce) << "WALLET STATUS:" + keyStatus + " " + status; walletScriptingInterface->setWalletStatus(status); }); From 8f87afd7db64d6eb5a067c7733f31530e8c72a91 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Mon, 15 Apr 2019 10:37:28 -0700 Subject: [PATCH 48/49] Disable baking of embedded material textures in material entities --- tools/oven/src/DomainBaker.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/oven/src/DomainBaker.cpp b/tools/oven/src/DomainBaker.cpp index 8a4489fcac..50a3d212c0 100644 --- a/tools/oven/src/DomainBaker.cpp +++ b/tools/oven/src/DomainBaker.cpp @@ -412,9 +412,13 @@ void DomainBaker::enumerateEntities() { if (entity.contains(MATERIAL_URL_KEY)) { addMaterialBaker(MATERIAL_URL_KEY, entity[MATERIAL_URL_KEY].toString(), true, *it); } + // FIXME: Disabled for now because relative texture URLs are not supported for embedded materials in material entities + // We need to make texture URLs absolute in this particular case only, keeping in mind that FSTBaker also uses embedded materials + /* if (entity.contains(MATERIAL_DATA_KEY)) { addMaterialBaker(MATERIAL_DATA_KEY, entity[MATERIAL_DATA_KEY].toString(), false, *it); } + */ } } From d0abb863661997220a077aa9c1173df305b4bb59 Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Mon, 15 Apr 2019 17:26:22 -0700 Subject: [PATCH 49/49] Reversed costants because no sleep. >_< --- libraries/shared/src/AvatarConstants.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/shared/src/AvatarConstants.h b/libraries/shared/src/AvatarConstants.h index b2ee4caa7c..b4545cedf0 100644 --- a/libraries/shared/src/AvatarConstants.h +++ b/libraries/shared/src/AvatarConstants.h @@ -102,7 +102,7 @@ static const float MAX_AVATAR_HEIGHT = 1000.0f * DEFAULT_AVATAR_HEIGHT; // meter static const float MIN_AVATAR_HEIGHT = 0.005f * DEFAULT_AVATAR_HEIGHT; // meters static const float MIN_AVATAR_RADIUS = 0.5f * MIN_AVATAR_HEIGHT; static const float AVATAR_WALK_SPEED_SCALAR = 1.0f; -static const float AVATAR_DESKTOP_SPRINT_SPEED_SCALAR = 2.0f; -static const float AVATAR_HMD_SPRINT_SPEED_SCALAR = 3.0f; +static const float AVATAR_DESKTOP_SPRINT_SPEED_SCALAR = 3.0f; +static const float AVATAR_HMD_SPRINT_SPEED_SCALAR = 2.0f; #endif // hifi_AvatarConstants_h